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 ; Write 0 to SDCONTROL to issue read command ; Loop 512 times: ; Wait until SDSTATUS=0xE0 (read byte ready, block busy) ; Read byte from SDDATA
Grant must be doing something similar for this code. Grant's code starts with the label readhst:. and reads the SD_STATUS and SD_DATA locations. It calls setLBAaddr which does three writes to the addresses SD_LBA2..SD_LBA0. These are defined along with the other Z80 I/O addresses as:
0060 0000 SD_DATA .EQU 088H 0061 0000 SD_CONTROL .EQU 089H 0062 0000 SD_STATUS .EQU 089H 0063 0000 SD_LBA0 .EQU 08AH 0064 0000 SD_LBA1 .EQU 08BH 0065 0000 SD_LBA2 .EQU 08CH
There are six separate Z80 I/O address registers for these values.
- SD_DATA is the input/output buffer
- SD_CONTROL is a write address for the control of the SD card
- SD_STATUS is the same address which tells the status of the interface.
- The next three SD_LBAx are the block addresses.
This seems straightforward enough.
SD vs SDHC Cards
The code above doesn't deal with LBA3 which it would need to if SDHC cards are used. We'll take note of that and figure out if we can do something about that in the future. If I recall correctly the card type can be interrogated and the right code inserted for the right type of card. It might be as simple as just using the lower area of a really big card and setting LBA3 to be 0x00 always.
The code does partly anticipate SDHC cards in the memory storage since it reserves a spot for lba3. Grant initializes the lba3 value to 0x0 (in CPMLOAD2) but the code to talk to the SD card (setLBAaddr) doesn't use it as Grant notes in a comment.
Here's the LBA storage space in Grant's code.
0073 3004 00 lba0 .DB 00h 0074 3005 00 lba1 .DB 00h 0075 3006 00 lba2 .DB 00h 0076 3007 00 lba3 .DB 00h
For the moment we'll just use an SD card that is 2 GB and already has CP/M loaded onto it. This is not quite cheating since Grant details how to make the card and we did that for the Multicomp FPGA build. Ultimately, it would be preferable to use an SDHC card since it's getting increasingly harder to find low capacity SD cards.
The PSoC SD Card Driver
We have enough information now to start to design the PSoC low level software for the SD card interface between the Z80 and the PSoC. We need six IO space addresses. Three of them are write-only from the Z80 (lba0..2). I'm considering the Compact Flash (CF) start earlier to be a dead end since we now have code that runs and should be able to boot CP/M. This way we won't have to make the CF card code to SD card code.
The PSoC definitions for the SD card are:
#define USING_SDCARD
#ifdef USING_SDCARD
#define SD_DATA 0x88
#define SD_CONTROL 0x89
#define SD_STATUS 0x89
#define SD_LBA0 0x8A
#define SD_LBA1 0x8B
#define SD_LBA2 0x8C
#endif
I changed all references to CF into SD and set up the appropriate functions. Here's the function names:
void SDReadData(void);
void SDWriteData(void);
void SDReadStatus(void);
void SDWriteCommand(void);
void SDWriteLBA0(void);
void SDWriteLBA1(void);
void SDWriteLBA2(void);
void SdWriteLBA3(void);
The PSoC code compiled without error. This should be all that is needed for the Z80 side access to the SD Card interface. We still need to work out the PSoC to SD Card side in the next log(s).
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.