3-Chip Z80 Design

Combining a Z80 retro design with a modern PSoC CPU.

Similar projects worth following
A Z80 CPU, 512K SRAM and a Programmable System on a Chip make a functional retro-future CPU card

Using a Z80, 512KB SRAM and a Cypress Semiconductor Programmable System on a Chip (PSoC) to make a 3-chip Z80 retro-future computer All of the I/O is performed in the PSoC by emulation of Z80 peripherals.

The PSoC has the following advantages:

  • 5V I/O which lets the PSoC interface directly with the Z80
  • Programmable Logic array to implement Z80 glue logic and peripherals
  • 32-bit ARM core
  • PSoC can download the program to the SRAM so there's no need for EPROM

A three chip Z80 design schematic is something like:

This has three parts - a 512KB SRAM, the Z80 and the PSoC.

The oscillator is optional unless precise serial connections are required. This is not necessary in most applications since the PSoC has a USB interface.

The PSoC could easily emulate the control/status registers of the DART/SIO and CTC and other common Z80 peripherals. Additional parallel I/O could be easily added with an optional MCP23017 chip which would only need to connect to I2C pins on the PSoC (this is slower than the PIO, but probably still OK for most applications).

This is similar to the Z80-MBC2 board without requiring the complications of the ATMEGA interface. In that design, the ATMEGA interfaces to the CPU force feeding it instructions with boot sequences. And this is one less IC.

Booting Software Builds

In this design, the PSoC downloads the software to the SRAM and takes the Z80 out of reset. Hardware/software implementations which have both EPROM and RAM could be "emulated" by banking the SRAM. Control of upper RAM address bits allows a bank of the SRAM to work as an EEPROM. The program can be downloaded from the emulated SRAM into the SRAM itself.

Z80 Peripheral Emulation

The Z80 peripherals can be emulated - mostly in hardware. For instance, the DART/SIO would consist of data and control/status registers which are I/O mapped to the Z80 and memory mapped to the PSoC. An 8-bit mailbox interface can be used for all peripherals.

Counter/Timers could also easily be simulated in PSoC hardware.

The PCB would be less expensive (than my previous design) since it could easily be built in a board that is smaller (less than 100x100mm which is the sweet spot for PCB costs. PCBWay wants $14 for 10 boards that are larger than 100x100mm but only $5 for 10 boards which are less than 100x100mm or less.

  • 1 × Z80 - pick your own speed
  • 1 × PSOC5LP - Programamble System on a Chip - 100 pin TQFP
  • 1 × AS6C4008-55PCN 512KB SRAM

  • Changes to Bank Switch Logic

    land-boards.com2 hours ago 0 comments

    Made some changes to the address mapper/bank switch hardware to support Grant's bank switching.

    Signals are:

    • DRV_RAM - asserted when the PSoC was testing the SRAM at power up and when Z80 code is loaded into the SRAM to be run.
    • MSK[4..0] determines whether the address is used when the bank SRAM is accessed.
    • Z80A[15..11] is the address directly from the Z80. 
    • BankMask MSK[]4..0] is the number of bits that are replaced is determined by this.
    • SRAMA[18..A16] are all zero's when the access is to spaces other than the bank. 
    • SRAMA[18..16] are set from the SAR[7..5] when the BANK is selected.

    Set the Base Address for comparison to the first 8KB of SRAM which asserts the BANKED line.

    Needed to add in the MSK bits into each individual address select mux. The BankMask is set to 0x1C for 8KB banks. That means that the Z80A11 and A10 bits are passed through directly and only when the address is in the range of the first 8KB is the selection from the AdrHighOut.

    The Bank Base Address comparison logic looks like this. 

    With the previous logic the board rebooted when I did a cold boot.

    After making these changes the board booted correctly. The Bank logic initialization code looks like this:

    #define BANK_SIZE_32K   0x10
    #define BANK_SIZE_16K   0x18
    #define BANK_SIZE_8K    0x1c
    #define BANK_SIZE_4K    0x1E
    #define BANK_SIZE_2K    0x1F
    // Memory Mapper version 1 sets bank size to 8KB
    // Points to the first SRAM bank
    void init_mem_map_1(void)

    This logic is pretty general purpose and could support multiple pages if the Z80 wrote to a bank select I/O address. Grant's code only goes a single swap of the spaces.

  • SD Card Physical Connections

    land-boards.com14 hours ago 0 comments

    Created a SPI_Master in the PSoC TopDesign.cysch file:

    PCB Schematic

    PSoC pin list

    Signal NamePSoC PinH3 pin

    SD Card Adapter

    These adapters are dirt cheap on Ebay. The extra row of pins allows debugging to be done easier. 

    The Z80 runs at 5V and SD Cards run at 3.3V. The adapter card has a voltage regulator to make 3.3V from the 5V. They don't typically have level shifters for the digital lines but they have 10K resistor in series with the 4 control lines so that should be OK.

    Cable to connect adapter to Z80_PSoC connector.

    Modified the schematic to make SS an output pin. That's because It needs to be held high for at least 74 clocks during initialization. Could be better to use a mux so that after initialization it could be switched to the in-built SPI interface.

  • SD Cards and CP/M

    land-boards.coma day ago 0 comments

    I'm not totally sure which source code is running on my card. That's because I grabbed the file out of my FPGA design sources and there's no matching source file name. Funny that I can run code that I don't know what the source is but it wasn't all that hard to run. I ran it until the I/O access hung and I could see that the Z80 was accessing a 2nd ACIA. My PSoC code didn't have a 2nd ACIA and the source code had no access code for a 2nd ACIA. So I know the .asm file I was using didn't match the .HEX file.

    Digging deeper into Grant's page I found what I think may be the the original source code - basMon.asm. It also has listing file BASMON.LST. The code looks right. It's 8KB long, it needs two ACIAs.

    It should be pretty easy to examine the program that is downloaded by using the Front Panel and see if it matches BASMON.LST.

    The PSOC Creator menu options Tool "Debug without programming" lets me restart the code. The compile options are set to include the Front Panel support. After testing the SRAM, the front panel displays the contents of address 0x0 which are 0xF3 which is a DI instruction. Not a surprise since probably all Z80 code starts this way.

    Let's see if the Monitor is present in this code. After all the MON in BASMON would imply that it is present. The monitor code starts at address offset 0x00EC.

    0294   00EC             ;------------------------------------------------------------------------------
    0295   00EC             ; Monitor command loop
    0296   00EC             ;------------------------------------------------------------------------------
    0297   00EC 21 EC 00    MAIN          LD   HL,MAIN    ; Save entry point for Monitor    
    0298   00EF E5                  PUSH HL        ; This is the return address
    0299   00F0 CD 22 01    MAIN0        CALL TXCRLF    ; Entry point for Monitor, Normal    
    0300   00F3 3E 3E               LD   A,'>'    ; Get a ">"    
    0301   00F5 CF                  RST 08H        ; print it

    Setting 0xEC on the second row up and pressing the LDADR switch displays the contents of the address which is 0x21. That matches the source code so I think I'm on the right track. The function I am most interested in is CPMLOAD. It looks like this:

    0505   01F1             ;------------------------------------------------------------------------------
    0506   01F1             ; CP/M load command
    0507   01F1             ;------------------------------------------------------------------------------
    0508   01F1             CPMLOAD
    0509   01F1             
    0510   01F1 21 03 02            LD HL,CPMTXT
    0511   01F4 CD 1B 01            CALL M_PRINT
    0512   01F7 CD 29 01            CALL M_GETCHR
    0513   01FA C8                  RET Z    ; Cancel if CTRL-C
    0514   01FB E6 5F               AND  $5F ; uppercase
    0515   01FD FE 59               CP 'Y'

    Checking address 0x1F1 and following address confirms this is very likely the correct listing for the code that is running on the card. Odd since Grant's documentation doesn't mention BASMON.asm at all. I think BASMON is a combination of BASIC.ASM and MONITOR.ASM which Grant does mention. It looks to me like he put the output of that file in with the FPGA VHD files since the .HEX output file is what the FPGA compiler loads.

    This gives us a shot at figuring out what is wrong when this has problems - and it will have problems since there's a fair amount of heavy lifting to do getting the SPI to work with an external SD card and CP/M. Given this, we should also be able to correlate the FPGA design used for the SD card interface with Grant's software.

    SD Controller in the FPGA

    I've got some recent experience with the SD controller that Grant uses in the FPGA. I designed a 32-bit RISC CPU (R32V2020) and got the controller working with my code. The organization of the control registers on that design was:

    ; SD Card base address is 0x1000
    ; Register Addresses
    ;    0x1000    SDDATA        read/write data
    ;    0x1001    SDSTATUS      read
    ;    0x1001    SDCONTROL     write
    ;    0x1002    SDLBA0        write-only
    ;    0x1003    SDLBA1        write-only
    ;    0x1004    SDLBA2        write-only (only bits 6:0 are valid)

     Here's some more of the details from that project.

    ; To read a 512-byte block from the SDHC card:
    ; Wait until SDSTATUS=0x80 (ensures previous cmd has completed)
    ; Write SDLBA0, SDLBA1 SDLBA2 to select block index to read from
    Read more »

  • Third Software Build Running

    land-boards.coma day ago 0 comments

    Grant Searle has a third software build that might prove handy towards having external flash card storage. This is the software that he uses with his Z80 on an inexpensive FPGA project. This is named the Multicomp project.

    I got this third build working in minutes.

    It only took a bit of messing around since Grant's stock software assumes two ACIA (M6850 parts). I copied the M6580 code that I made for the previous logs and deleted most of the guts of the lower level routines. The read status routine returns saying that there are no characters in the second ACIA. That is how Grant chooses between the two serial ports - whichever port returns the space is selected as the code default - handy solution to the problem. Without this added code Grant's software hangs on the access to the missing 2nd ACIA.

    Why Yet Another Build of Grant's software?

    This is another 56KB SRAM (in BASIC) build but with critical differences. Grant's 48K build supports an assembly language monitor and CP/M. Grant's other 56K build only supports NASCOM BASIC (no CP/M). This build supports both NASCOM BASIC and CP/M support all packed into an 8K PROM space. That's cool because it's getting 8KB more of program space for BASIC with the cost of losing the machine code monitor. Not a real big cost but I could always load the old build if I wanted to mess around with the monitor.

    What I really wanted from this build is Grant's hardware/software support for CP/M that he has in the FPGA. In Grant's discrete (non-FPGA designs) he uses an external Compact Flash (CF) card which requires a bunch of connections from the CPU bus and these are expensive I/O on an FPGA. For the FPGA version, Grant uses an SD card which only needs a 4-pin SPI bus to control it. And the PSoC also has an SPI bus and 4 spare connectorized pins so it should be possible to get an SD card working with this software build. I say possible since it's yet to be done but this is where we are headed next.

    Grant's Documentation for the SD Card Interface

    Grant has some instructions on his page about the SD card on this page and the other support that is needed to do this (at least how he did it with his FPGA solution).

    Trading Spaces

    The first thing is that the memory mapper needs to swap out the EPROM for SRAM when the program writes to a particular memory location.

    When CP/M starts, the ROM is to be disabled. 
    The CP/M images are set up so that a write to $38 will 
    turn off the ROM, resulting in ALL of the 64K address space 
    being the SRAM instead.

    This is almost unneeded with this board design. The reason is there's no actual EPROM on the card - all memory is already SRAM. And CP/M uses SRAM. I say almost because Warm Boot complicates that. If CP/M overwrote the data in the first 8K then the board can't boot back into the initial prompt and run BASIC without reloading the code. That really would be no big deal but supporting page swapping shouldn't be difficult since the hardware is present already to do the swap.

    Although there is hardware support in the PSoC for page mapping and swapping but it hasn't been used yet. The PSoC code does test all 512KB of SRAM when it powers up so we know the SRAM is working. What is missing is the I/O routine to deal with the swapping.

    Adding the I/O handler for the swap write should be similar to the other handlers. To start with a #define for this memory mapper would look like in Hardware_Config.h:

    #ifdef GRANT_FPGA_CPM   // Swap out first 8KB

    I named it _1 since there will likely be any number of memory mapping schemes employed with this hardware.

    It is set to undefined at the top of the file and then defined within the particular build section of the file that corresponds to this build - noted by:

    #ifdef GRANT_FPGA_CPM

     This then gets added to this section as:

        #define USING_MEM_MAP_1
        #ifdef USING_MEM_MAP_1
            #define MEM_MAP_SWAP        0x38

    The code to deal with this gets added to Z80_IO_Handle.c as a new...

    Read more »

  • Compact Flash Emulation?

    land-boards.com3 days ago 0 comments

    I'd like to get something very difficult going next. Maybe Grant's Compact Flash code? I'd like to get it working with Grant's stock 9-Chip Z80 code. I'd really like to map it to an SDHC card instead of the more ancient Compact Flash. An SDHC card only needs a SPI bus connection for the physical connection.

    Physical Connections in Grant's Design

    Grant has the following connections in his 9-Chip Z80 design to connect to the CF from the Z80 bus.  

    Grant uses I/O addresses 0x10-0x1F and connects the Z80 lines A[2..0] directly to the CF. Makes me wish I had put the Z80 lines onto a connector - I'd only need a Chip Select now and that would be easy.  Grant generates a single Chip Select line when he is accessing the CF card that covers the 0x10-0x17 range. (The Front Panel address range (0x18-0x1F) is already out of the way so I have no conflicts with his code.)

    Grant hooks up other Z80 lines:

    • Power (5V) and Ground
    • A[0..2] - already mentioned
    • D[0..7]
    • IORD*
    • IOWR*
    • CS* - Chip select - asserted for the address range 0x10-0x17
    • RESET* 
    • BUSY_LED - Status LED (from the CF card) - Nice to have but, don't need it

    CF Mode?

    Grant's page describes the method of connection as:

    Additionally, the CF is set up as "IDE" mode so it only requires 3 address lines 
    and one chip select.

    This is why Compact Flash Drives can be connected to IDE connectors with purely passive connections.

    This page has some hints on the IDE mode. There's more information here.

    Grant has the following in his monitor.asm code:

    ; CF registers
    CF_DATA        .EQU    $10
    CF_FEATURES    .EQU    $11
    CF_ERROR    .EQU    $11
    CF_SECCOUNT    .EQU    $12
    CF_SECTOR    .EQU    $13
    CF_CYL_LOW    .EQU    $14
    CF_CYL_HI    .EQU    $15
    CF_HEAD        .EQU    $16
    CF_STATUS    .EQU    $17
    CF_COMMAND    .EQU    $17
    CF_LBA0        .EQU    $13
    CF_LBA1        .EQU    $14
    CF_LBA2        .EQU    $15
    CF_LBA3        .EQU    $16

    This seems pretty straightforward. Where there are two EQUs at the same address the first one is the write value and the second is the read value. I'm going to add support for this to the 9-Chip build. I will spare the reader the details - at least for the present moment - but I want to set it up to step through the code and watch what the Z80 tries to do when booting CP/M. This will be educational - for me at least.

    This will be very similar in steps to the M6850 emulation I completed in the most recent logs. If this looks promising I may pick it up here in the logs for this project.

    Added: A couple of quick observations.

    Grant's code in monitor.asm only does reads of particular low sectors. That's because it is loading CP/M. Once CP/M loads it must have it's own software for read/writing SD card sectors. That may complicate emulation because I may need to dump the code that is running . Fortunately Grant includes the CP/M code.

    The file cbios128 looks like it's the low level IO routines and it uses the same $EQU values as Grant's monitor.asm code. So it should be easy to pick out what it does from there.

  • 6850 Emulation with a PSoC (Part 4)

    land-boards.com3 days ago 0 comments

    In the last 3 logs we created an M6850 emulator based on the SIO that we already have. At the last step the program compiled correct. Let's try and get it to work. I'm sure I made mistakes when I did the previous changes. First I'm going to check the current build into GitHub with a note that it's a WIP.

    First, I've turned the Front Panel back on so that I can check the downloaded code. That is now as easy as changing the #undef to a #define for the Front Panel. Stepping through the first part of the code shows it loaded properly into the SRAM.

    However, pressing RUN on the Front Panel doesn't show any output on the USB port. So much for just up and running. I am probably missing some subtle part of Grant's implementation. Just to rule out breaking something fundamental I switched back to the 9-build code and it ran fine. So it's apparently something related to the M6850 emulator.

    Debugging I/O with the PSoC Debugger

    Debugging is really nice with the PSoc. You can set breakpoints and run the code. The Z80 waits around for the PSoC and since it's a CMOS part there's no problem with asserting WAIT* indefinitely. Assuming the problem relates to the I/O code let's set a breakpoint at the beginning of the handler - function HandleZ80IO( ). The debugger stops at the beginning of main and has to be told to Resume Execution (F5 is the shortcut). 

    Since I have the Front Panel code still emulated I have time to restart PuTTY before hitting the RUN button on the Front Panel. This probably isn't strictly required since the PSoC waits for the USB to come up before it does I/O anyway.

    F11 single steps through the PSoC code. The first transfer has ioCrtlRegVal with the value 0x1A. As you might expect that's a REGULAR_WRITE_CYCLE. The register ioZ80Addr shows that the Z80 is writing to address 0x80. Grant's INIT code shows this access first.

                   LD        HL,TEMPSTACK    ; Temp stack
                   LD        SP,HL           ; Set up a temporary stack
                   LD        HL,serBuf
                   LD        (serInPtr),HL
                   LD        (serRdPtr),HL
                   XOR       A               ;0 to accumulator
                   LD        (serBufUsed),A
                   LD        A,RTS_LOW
                   OUT       ($80),A         ; Initialise ACIA

     Earlier we saw 

    RTS_LOW         .EQU     096H

    Stepping through the code shows it calling M6850WriteCtrl( ) as it should and the variable M6850_Ctrl is being set to 0x96 (as it also should). We can also see a TBD we left there which says there may be more to do when the control register is written. After stepping through this code i hit F5 to run until the next I/O request.

    The next I/O transfer has ioCrtlRegVal with the value 0x1C which is a REGULAR_READ_CYCLE of the same location which should be a read of the status register.  This could be where the problem is since that is uninitialized at startup unless there's code added to set the initial values. The SIO code didn't need any initialization of the values. The value is 0x00 and may or may not be initialized by the CPU. That would indicate that there is no receiver character but it also indicates that the transmitter is busy sending a character and it will never get set to anything else. 

    My guess is that Grant's software will spin on reading this until it gets set to indicate it is empty. Let's add code to initialize the M6850 status register before the code runs. We are missing any handler at all for this bit so it will also need to be fixed in the transmit routine.  It looks as if this is the only bit that needs to be set since the other bits are all 0's when running.

    We handle handshake in sending to the PC by holding off deasserting WAIT* until the PSoC code is called to send the data out the serial port.

    We have to hit Stop Debugging to edit the program in PSOC Creator. The initialization code looks like:

    // void initM6850StatusRegister(void) - Set the initial value of the status reg
    void initM6850StatusRegister(void)
        M6850_Status = 0x2;

    We need to call this code...

    Read more »

  • 6850 Emulation with a PSoC (Part 3)

    land-boards.com3 days ago 0 comments

    In this log we will look at interrupts for the M6850. [Edit: this is a helpful page to understand the three interrupt modes supported by the Z80. Grant's code uses Interrupt Mode 1]. We left the compilation broken for interrupts and we need to fix those by creating the code and handing the differences vs the SIO. We also don't know exactly how Grant is handling interrupts. We do know from Grant's source code that he's apparently using them for receive and not for transmit. One difference between the Z80 peripheral chips and M6800 family parts like the M6850 is that there is no interrupt vector produced for the M6850 type of part.

    The Z80 handles the lack of interrupt vector by having a special vector of 0xFF for devices which are not present. This seems to rely on the data bus floating high. We can probably do better and send out a 0xFF with a proper interrupt acknowledge cycle. Let's make this assumption and see if it works.

    The errors that the compiler is producing now are both related to missing functions/values since they are not yet implemented for the M6850.

    The SIO reads the value of the Interrupt vector from one of the control registers. As noted there's no such register in the M6850 so let's fix that. The error is coming out of the Z80IOHandle code:

        if ((ioCrtlRegVal & IACK_MASK) == IN_IACK_CYCLE)

    Set the #define to conditionally remove the SIO code when the SIO is not used. 

    The SIO code for the routine is:

    // void SioReadIntRegB(void) - Read the Interrupt vector
    void SioReadIntRegB(void)
        IO_Ctrl_Reg_Write(IO_Ctrl_Reg_Read() & 0xFB);   // Clear IRQ* line

    Z80_Data_In_Write(SIO_B_WR2) - returns the interrupt vector to the SIO. The next line clears the interrupt source. We still haven't set the interrupt anywhere so we will need to deal with that, but let's create a flag after we make the equivalent function for the M6850.

     is not defined but we need to replace it with an the proper bit for the M6850. According to the data sheet this is bit 7 of the status register.

    For the M6850 the code looks like:

    // void M6850ReadIntReg(void) - Read the Interrupt vector
    void M6850ReadIntReg(void)
        IO_Ctrl_Reg_Write(IO_Ctrl_Reg_Read() & 0x7F);   // Clear IRQ* line

    After adding the function prototype to the Z80_Emul.h file the only compiler error is:

    So we are still missing an interrupt #define. That is used in a function in Z80_IO_Handle.c:

    void ackIO(void)
        IO_Ctrl_Reg_Write(IO_Ctrl_Reg_Read() | CLR_IO_INT_BIT);

    That is one of the #define values in Z80_SIO_Emul.c. It controls the interrupt hardware from the PSoC to the Z80, so it would be better added to the Z80_IO_Handle.h file. After doing that the PSoC compiles without error.

    As a rough measure the M6850 emulator file is 114 lines long as compared to the SIO emulator file which is 444 lines. This shows just how easy it is to create relatively simple peripherals in the PSoC. It's made even easier in this instance since we have a Serial UART emulator already in the SIO software.

    In the next log we will see what we missed and fix it so the code runs.

  • 6850 Emulation with a PSoC (Part 2)

    land-boards.com3 days ago 0 comments

    In the last log we got the basic functions in place to emulate a M6850 ACIA UART with the PSoC controller used on this card. We left a few details open but the code compiled without error so we've got a good place to fill in the details here. Hopefully, the serial transmit and receive functions will work as-is. 

    The status and control register handler needs some work. The receive part of the status register might just work since it's in the same bit and has the same meaning as the SIO.

    Hardware Handshake Handling

    In particular, we've been ignoring the differences between the SIO and M6850 where it comes to dealing with RTS. The SCC had a single bit and the M6850 has two bits. Grant's intmini.asm file has the answer for RTS values. These are possibly the hard coded values that Grant writes to the control register to toggle RTS on and off.

    RTS_HIGH        .EQU     0D6H
    RTS_LOW         .EQU     096H

     In fact, these are only different in the two relevant bits D6 and D5.

    RTS_HIGH sets D6..D5 to b'10.
    RTS_LOW sets D6..D5 to b'00.

    This correlates to RTS high and RTS low for the two cases . He sets Transmit Interrupt Disabled in both cases.  This means we don't probably need to generate interrupts for transmits. That's consistent with Grant's monitor code in his 9-chip design.

    This also means we don't need to have all that many side effects in the control register write routine. In fact the routine doesn't need to do anything other than set the value in the status register since it's being set in another routine. The RTS bit is in SIO_A_Ctrl2 so lLet's look at the routine which uses this value. That function is inside the Z80_SIO_emul.c file and for the SIO has:

    // uint8 checkSIOReceiverBusy(void) - Check the SIO port A receiver status
    // Returns: 
    //  0 if the port can take another character
    //  1 if the port is busy and can't take another character
    uint8 checkSIOReceiverBusy(void)
        if ((SIO_A_Ctrl2 & SIO_RTS) != SIO_RTS)
        return (SIO_A_RD0 & SIOA_CHAR_RDY);

    That is called from main( ) as a function so we can replace that function directly with one for the M6850. Here's what it looks like for the SIO.

                    USB_To_Z80_RxBytes_count = USBUART_GetAll(buffer);
                    if ((USB_To_Z80_RxBytes_count == 1) & (checkSIOReceiverBusy() == 0))     // Input 1 character immediately
                        USB_To_Z80_RxBytes_count = 0;

    I think it would be smart to replace the specific name of checkSIOReceiverBusy with a more generic value specific to this function. Also, we didn't get a compile error so the compiler must be including the SIO code and we want that removed. The find-replace in all uses is easy and produces this as the prototype:

    uint8 checkSerialReceiverBusy(void);

    Adding #ifdef  USING_SIO around the SIO code will remove it from conditional compilation and we should get a list of functions we need to create for the M6850 when we compile the code. After that we get a list of missing functions:

    Some of these are real but some are missing #includes. We expected the checkSIOReceiverBusy call to be missing but we need to do something with the other 3 functions. They are SIO specific and may or may not need to be replicated for the M6850. First, let's make sure that the M6850 is also conditionally included by bracketing the code with the appropriate #define. Once we do that the compiler warnings/error list surprisingly drops. This implies that there are implicit declarations from the SIO although they are indicated from the Z80_IO_Handle file. I will ignore them for now and look at them later. For the moment we need to deal with the function at hand. From the SIO we see that the function is like this:

    uint8 checkSerialReceiverBusy(void);

    Adding this as a stub function to the 6850 emulator .c and .h files should remove the compilation error.  The header for the function from the SIO emulator has the following:

    Read more »

  • Emulating the 6850 With a PSoC

    land-boards.com3 days ago 0 comments

    This should be the final piece of work to get Grant's 7-chip Z80 design to run on this hardware.

    This is arguably the "hardest" part of the port since the 6850 emulator does not yet exist. I've gone through the front end details of creating Z80 peripheral chips in a lot of details for the PIO (although the series is not yet complete) so I will spare the details here. Here's the first log of that series (Z80 Peripheral Emulation Guide for the PSoC). Since the 6850 is more or less a very cut down version (not really a version) of the SIO we will start from that design, clone it, and change the functions that need changes to match. 

    The significant differences between the SIO and the 8650 include:

    Command/Status InterfaceComplicated requiring a write then read or write cycleSimple with single control/status location
    InterruptsUses Interrupt AcknowledgeInterrupts but no Interrupt Acknowledge hardware

    Modify Z80_IO_Handle.c

    We've already put #define conditional selection in this file to remove the SIO so we just need to clone that for the 6850 so let's start by copying that code and deleting what we don't need. Also, we need to use the proper #define USING_6850 to select the code when there's a 6850 being emulated. That should all happen inside the HandleZ80IO() function. That looks like this:

    #ifdef USING_6850
            case M6850_D:
                if (ioCrtlRegVal == REGULAR_READ_CYCLE)             // regular read cycle
                else if (ioCrtlRegVal == REGULAR_WRITE_CYCLE)      // regular write cycle
            case  M6850_C:    // Control register
                if (ioCrtlRegVal == REGULAR_READ_CYCLE)             // regular read cycle
                if (ioCrtlRegVal == REGULAR_WRITE_CYCLE)      // regular write cycle

    Of course, as seen by four yellow exclamation boxes, we now broke the compilation since we've created a bunch of function calls to handle the Z80 access which don't yet exist. 

    Let's go create the four routines we need to handle these conditions. If we are careful enough the functions in main which call these handlers will still work. If we need to we can add some additional conditional compilations to handle them. In all likelihood we will have to modify the calling routines given that the SIO is interrupt driven for receive and this one may be different (yet to look at the details enough to see - it may be nearly the same). At the very least, interrupt acknowledge cycles will need to be different.

    Low Level 6850 Handler

    To do this, create two new source files in PSOC Creator (Z80_6850_Emul.c and .h). Liberally copy the pieces from the SIO equivalent and change what is necessary. Leave as much as possible the same to minimize changes to calling routines. Generally speaking, this means to replace the SIO_A with M6850_ in most places.

    There are two lines we have to pay particular attention to. These determine which bits are used for the calling routines to test receive character ready and set RTS. For the SIO, these lines are:

    #define SIOA_CHAR_RDY   0x1
    #define SIO_RTS 0x2

     It's a bit hard to read in the datasheet but here's the values of the registers in the 6850:

    The status register bit 0 is receiver data full. That's convenient since that's exactly what we have for the SIO so we won't change that #define value. But there's no bit which directly sets the RTS and we do need hardware handshake for flow control since the Z80 is much slower than a USB block transfer. The control register in the 6850 handles the RTS in a slightly different manner:

    We'll have to look closer at Grant's assembly code INTMINI for the answer on how these bits need to be set but for the moment let's just make a mask and define the bits for the four patterns. This will replace the RTS code above and may necessitate some changes in the main( ) calling code.

    The top of the .c file now looks like:

    #include <project.h>
    #include "Z80_6850_emul.h"
    #include "Z80_IO_Handle.h"...
    Read more »

  • Building the 7-Chip Z80 Software

    land-boards.com3 days ago 0 comments

    In the last log we added conditional compilation flags that allow us to use other software builds. We were left with two undone tasks:

    • Build the software
    • Add 6850 emulation

    Let's tackle building the software. Grant's folder is a bit confusing since there are 3 .HEX files and we only need one file to create the image. 

    BASIC.HEX is the BASIC interpreter. INTMINI is a short interrupt handler for serial I/Ot that replaces the monitor code from the 9-chip design (which had both interrupt handler and monitor plus CP/M loader). ROM.HEX is the file that would be loaded into a ROM in the "real" design. 

    There's a couple of ways to get what we need out of this. One would be to compile the code. I was able to use TASM with my Windows 10 machine in an earlier log by running TASM under DOSBOX. There's also a newer TASM version which allows it to be assembled under Windows 10. 

    An easier way would be to use srec_cat to convert the .hex file to a C-Array. That is described in detail in this log. The command line is:

    path_to_srecord\srec_cat ROM.hex -intel -o GS_7_CHIP_ROM.c -C-Array

     That worked and created a C file with the following at the top:

    /* */
    const unsigned char eprom[] =
    0xF3, 0xC3, 0xB8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x9F, 0x00, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x74, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

     Chedking this code from the relevant Z80 Opcode chart:

    Opcode 0xF3 is DIS INT (Disable Interrupts) and 0xC3 is JMP (jump). Grant's INTMINI has this code at the top.

    ; Reset
    RST00           DI                       ;Disable interrupts
                    JP       INIT            ;Initialize Hardware and go
    ; TX a character over RS232 
                    .ORG     0008H
    RST08            JP      TXA

    So, it looks like the code matches just fine. Next, add a new file to the PSOC project: calling it GS_7_BASIC_ROM.c which we will place the newly generated code into.

    After dropping the file and making a few changes, the top of this file looks like:

    #include "Hardware_Config.h"
    #ifdef GRANT_7_CHIP_Z80
    const unsigned char gs7chip_basic_eeprom[] =
    0xF3, 0xC3, 0xB8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x9F, 0x00, 0xFF,

    We have the #define conditional compile that gets set by the Hardware_Config.h file. The fact that the new code is not greyed out indicates that it will be included in the next PSOC build that is done. The inserted #define requires a #endif at the end of the file to close it.

    #define EPROM_FINISH      0x00002000
    #define EPROM_LENGTH      0x00002000
    /* [] END OF FILE */

    The array is renamed to be a name specific to this build. We could name all of the builds the same thing but it might be possible in the future to include multiple builds and select the build to run in some manner, so by pressing different front panel switches, so we are making this build specific to allow that possibility. This does mean that we need to add to the SRAM loading software for each specific build.

    That is done inside the function that loads the software in the ExtSRAM.c file.

    // void loadSRAM(void) - Load the SRAM on the card with the ROM code
    // Initial code build is monitor plus BASIC code
    void loadSRAM(void)
        uint32 charCount;
        uint32 SRAMAddr = MONITOR_START;
        volatile uint8 dataVal;
        for (charCount = 0; charCount < MONITOR_LENGTH; charCount++)
    #ifdef GRANT_9_CHIP_Z80
            dataVal = monitor_basic_eprom[charCount];
    #ifdef GRANT_7_CHIP_Z80
            dataVal = gs7chip_basic_eeprom[charCount];

    We also need to add the definition to the top of the ExtSRAM.c file:

    #ifdef GRANT_9_CHIP_Z80
    extern unsigned char monitor_basic_eprom[];
    #ifdef GRANT_7_CHIP_Z80
    extern unsigned char gs7chip_basic_eeprom[];

    At this point the software builds in PSOC Creator but if we run it there will be no...

    Read more »

View all 65 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates