;Test program for SD_CARD interface of S100Computers.com Dual SD Card S100 board. ; ; John Monahan S100Computers.com 7/25/2024 ; ; ;V0.1 10/10/2024 ; First version. It is "rough & ready" with little menu etc. error checking etc. ;V0.2 12/19/2024 ; Second version. ;V0.3 12/25/2024 ;Added FPGA_FDC Board like Commands ;V0.4 1/22/2025 ;New SD card board ; ; ; PORT ASSIGNMENTS ; FALSE EQU 0 TRUE EQU NOT FALSE DEBUG EQU TRUE ; If TRUE all sector reads will display the actual 512 bytes of data read SD_CARD_STATUS EQU 80H ; We will assume the Dual SD Card board uses ports 80H-81H SD_CARD_DATA EQU 81H ; CMD and Data port CONSTAT EQU 0H ; Console Status Port CONIN EQU 1H ; Console IN Port CONOUT EQU 1H ; Console OUT Port MONITOR EQU 0F000H ; Location of Z80 ROM monitor when done. DEFAULT_BUFFER EQU 4000H ; Default Sector buffer location ("DMA address") SPACE EQU 20H BELL EQU 07H ESC EQU 1BH CR EQU 0DH LF EQU 0AH CMD$INIT$DRIVE_A EQU 80H ;Initilized drive A: CMD$INIT$DRIVE_B EQU 81H ;Initilized drive B: CMD$SEL$DRIVE_A EQU 82H ;**** (Re)select an already initilized drive A: CMD$SEL$DRIVE_B EQU 83H ;**** (Re)select an already initilized drive B: CMD$SET$TRK$SEC EQU 84H ;Set new current TRACK+SECTOR on current drive (new) CMD$READ$SECTOR EQU 85H ;**** Read data from the CURRENT sector (on current track,drive). CMD$WRITE$SECTOR EQU 86H ;**** Write data to the CURRENT sector (on current track,drive). CMD$FORMAT$SECTOR EQU 87H ;Format the CURRENT sector with E5's. CMD$RESET$ESP32 EQU 88H ;Reset the ESP32 CPU ; SD CARD Interface Status:- ORG 100H ;<<<<<<<< LOAD AT 100H START: LD SP,STACK LD HL,SIGNON CALL PMSG LD HL,DEFAULT_BUFFER ; Default to 2000H LD (DMA_ADDRESS),HL XOR A,A LD (CURRENT_CARD),A ; Startup with No Drive selected START1: LD HL,SIGNON1 ; '-------- Main Menu ------Buffer = ' CALL PMSG LD HL,(DMA_ADDRESS) CALL PRINT_HL LD HL,SIGNON2 ; 'H' CALL PMSG LD A,00H OUT (SD_CARD_STATUS),A ;Tell ESP32 no data ready to read IN A,(SD_CARD_DATA) ;Reset U6A START2: CALL CRLF LD C,'>' CALL GETCMD CP A,ESC JP Z,MONITOR ; ESC drop back to Z80 monitor CP A,'A' ; Check range (0-6) JP C,DATA_ERROR CP A,'[' ; Check range (0-6) JP NC,DATA_ERROR JP START3 START3: CP A,'A' JP Z,INITILIZE_A CP A,'B' JP Z,INITILIZE_B CP A,'C' JP Z,SEL_A CP A,'D' JP Z,SEL_B CP A,'S' JP Z,SET_SECTOR CP A,'R' JP Z,READ_SEC CP A,'W' JP Z,WRITE_SEC CP A,'D' JP Z,SET_DMA CP A,'F' JP Z,CPM_FORMAT ;Format sectors for CPM CP A,'Z' JP Z,RESET_ESP32 JP START1 DATA_ERROR: LD HL,DATA_ERROR_MSG ; "Data error" CALL PMSG JP START1 ;-------------------------------------------------------- INITILIZE_A: LD A,00H OUT (SD_CARD_STATUS),A ;Tell ESP32 no data ready to read LD C,CMD$INIT$DRIVE_A ;A, Initilize A: for raw sector read/writes CALL SEND_CMD LD A,1 ;Select card 1: LD (SD_A_INITILIZED),A ;Flag A as itilized LD (CURRENT_CARD),A ;A: == 1 CALL GET_DATA ;0 returned means all info recieved OK OR A,A JP Z,START1 JP DATA_ERROR ;-------------------------------------------------------- INITILIZE_B: LD A,00H OUT (SD_CARD_STATUS),A ;Tell ESP32 no data ready to read LD C,CMD$INIT$DRIVE_B ;B, Initilize B: for raw sector read/writes CALL SEND_CMD LD A,2 ;Select card 2: LD (SD_B_INITILIZED),A ;Flag A as itilized LD (CURRENT_CARD),A ;B: == 2 CALL GET_DATA ;0 returned means all info recieved OR A,A JP Z,START1 JP DATA_ERROR ;-------------------------------------------------------- SEL_A: LD A,(SD_A_INITILIZED) ;Is SD Card A itilized OR A,A JP Z,NO_CARD_SEL LD C,CMD$SEL$DRIVE_A ;A, Select SD card A: for raw sector read/writes CALL SEND_CMD LD A,1 ;Select card 1: LD (CURRENT_CARD),A ;Flag A as current card CALL GET_DATA ;0 returned means all info recieved OK OR A,A JP Z,START1 JP DATA_ERROR ;-------------------------------------------------------- SEL_B: LD A,(SD_B_INITILIZED) ;Is SD Card A itilized OR A,A JP Z,NO_CARD_SEL LD C,CMD$SEL$DRIVE_B ;A, Select SD card A: for raw sector read/writes CALL SEND_CMD LD A,2 ;Select card 2: LD (CURRENT_CARD),A ;Flag B as current card CALL GET_DATA ;0 returned means all info recieved OK OR A,A JP Z,START1 JP DATA_ERROR ;--------------------------------------------------------- SET_SECTOR: LD HL,GET_TRK_MSG CALL PMSG CALL GET_HEX2 ;Get 2 digits in L JP C,DATA_ERROR LD A,L LD (CURRENT_TRACK),A ;Store for later LD HL,GET_SEC_MSG CALL PMSG CALL GET_HEX2 ;Get 2 digits in L JP C,DATA_ERROR LD A,L LD (CURRENT_SECTOR),A ;Store for later LD C,CMD$SET$TRK$SEC ;Set Track/Sec CALL SEND_CMD LD A,(CURRENT_TRACK) ;Send track LD C,A CALL SEND_DATA LD A,(CURRENT_SECTOR) ;Send Sector LD C,A CALL SEND_DATA CALL GET_DATA ;0 returned means all info recieved OR A,A JP Z,START1 JP DATA_ERROR ;--------------------------------------------------------- READ_SEC: ; LD A,(CURRENT_CARD) ; Check for no selected card ; OR A,A ; JP Z,NO_CARD_SEL READ_SEC2: LD C,CMD$READ$SECTOR ;R, Read a sector CALL SEND_CMD LD C,'@' CALL CO LD HL,DEFAULT_BUFFER LD B,0 MORE_DATA4: CALL GET_DATA ;0 returned means all directory info done LD (HL),A INC HL DJNZ MORE_DATA4 MORE_DATA5: CALL GET_DATA ;0 returned means all directory info done LD (HL),A INC HL DJNZ MORE_DATA5 CALL GET_DATA ;0 returned means all info recieved OR A,A JP NZ,SEC_RD_ERROR LD HL,TRK_NUM_MSG CALL PMSG LD A,(CURRENT_TRACK) ;Show Sector number CALL PRINT_A LD HL,SPACE_MSG CALL PMSG LD HL,SEC_NUM_MSG CALL PMSG LD A,(CURRENT_SECTOR) ;Show Sector number CALL PRINT_A LD HL,SPACE_MSG CALL PMSG LD HL,DEFAULT_BUFFER ;Location where Sec data is stored CALL HEXDUMP LD HL,NEXT_SEC_MSG ;Show next sector? CALL PMSG CALL CI CP A,' ' ;Space for next sector JP NZ,START1 LD A,(CURRENT_SECTOR) LD L,A LD A,(CURRENT_TRACK) LD H,A INC HL LD A,H LD (CURRENT_TRACK),A LD A,L LD (CURRENT_SECTOR),A LD C,CMD$SET$TRK$SEC ;Set Track/Sec CALL SEND_CMD LD A,(CURRENT_TRACK) ;Send track LD C,A CALL SEND_DATA LD A,(CURRENT_SECTOR) ;Send Sector LD C,A CALL SEND_DATA CALL GET_DATA ;0 returned means all info recieved OR A,A JP Z,READ_SEC2 SEC_RD_ERROR: LD HL,RD_ERROR ;Sector read error CALL PMSG JP START1 NO_CARD_SEL: LD HL,NO_CARD_MSG ;NO CARD Initilizwed CALL PMSG JP START1 ;----------------------------------------------------- WRITE_SEC: LD A,(CURRENT_CARD) ; Check for no selected card OR A,A JP Z,NO_CARD_SEL LD C,CMD$WRITE$SECTOR ;W, Write a sector CALL SEND_CMD LD HL,DEFAULT_BUFFER LD B,0 MORE_DATA6: LD C,(HL) ;Send 256 Bytes CALL SEND_DATA INC HL DJNZ MORE_DATA6 MORE_DATA7: LD C,(HL) ;Send 256 Bytes CALL SEND_DATA INC HL DJNZ MORE_DATA7 CALL GET_DATA ;Wait for error flag check. OR A,A JP NZ,SEC_WR_ERROR LD HL,SEC_WR_OK ;Sector write OK CALL PMSG JP START1 SEC_WR_ERROR: LD HL,WR_ERROR ;Sector write error CALL PMSG JP START1 ;----------------------------------------------------- CPM_FORMAT: LD A,(CURRENT_CARD) ; Check for no selected card OR A,A JP Z,NO_CARD_SEL LD C,CMD$FORMAT$SECTOR ;W, Format the current sector CALL SEND_CMD LD HL,FORMAT_SEC_MSG ;Enter number of sectors to format CALL PMSG CALL GET_HEX2 ;Get 4 digits in HL LD A,L LD (SECTOR_COUNT),A ;Store it CALL CRLF LD C,L ;Low byte CALL SEND_DATA LD C,0 ;High Byte always 0 CALL SEND_DATA LD HL,FORMAT_WAIT_MSG ;Please wait while sectors are being formatted CALL PMSG CALL GET_DATA ;Wait for end/error flag check. OR A,A JP NZ,SEC_WR_ERROR LD HL,FORMAT_DONE_MSG ;Please wait while sectors are being formatted CALL PMSG JP START1 ;----------------------------------------------------- RESET_ESP32: LD C,CMD$RESET$ESP32 ;W, Format the current sector CALL SEND_CMD IN A,(SD_CARD_STATUS) ;Send GPIO_10 LOW pin to ESP32 XOR A,A LD (SD_A_INITILIZED),A ;Flag A as unitilized LD (SD_B_INITILIZED),A ;Flag A as unitilized LD HL,RESET_DONE_MSG CALL PMSG JP START1 ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< SUPPORT ROUTINES >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> GET_DATA: IN A,(SD_CARD_STATUS) ;Wait for character (GPIO_3 and GPIO_21) BIT 7,A JR Z,GET_DATA IN A,(SD_CARD_DATA) ;S100 read enable will reset U6B GPIO_21 LD C,A GET_DATA1: IN A,(SD_CARD_STATUS) ;Wait for character (GPIO_3 and GPIO_21) BIT 7,A JR NZ,GET_DATA1 LD A,C RET GET_STRING: CALL GET_DATA OR A RET Z CALL CO JR GET_STRING SEND_DATA: IN A,(SD_CARD_STATUS) ;Wait until tany previous character has been read BIT 0,A JR NZ,SEND_DATA LD A,C ;Send Data, this will raise GPIO_20 OUT (SD_CARD_DATA),A SEND_DATA1: IN A,(SD_CARD_STATUS) ;Wait until the ESP32 has reset U6A with GPIO_2 BIT 0,A JR NZ,SEND_DATA1 RET SEND_CMD: PUSH BC LD C,33H ;All CMD's begin with 33H CALL SEND_DATA POP BC ;CMD is in C CALL SEND_DATA RET ;=================================================================================================== SET_DMA: LD HL,ENTER_RAM_LOC ; 'Set Sector RAM Buffer.' CALL PMSG LD HL,ENTER_RAM_LOC1 ; 'Please enter RAM buffer location (XXXXH): CALL PMSG CALL GET_HEX4 JP C,DATA_ERROR LD (DMA_ADDRESS),HL ; Store for later XOR A,A RET ;<<<<<<<<<<<<<<<<<<<<<<<<< SUPPORT ROUTINES >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PMSG: PUSH BC ; Only [A] and [HL] is changed PMSG1: LD A,(HL) ; A ROUTINE TO PRINT OUT A STRING @ [HL] INC HL ; UP TO THE FIRST 0. OR A,A JP Z,PMSG_DONE LD C,A CALL CO JP PMSG1 PMSG_DONE: POP BC RET CRLF: PUSH AF PUSH BC LD C,CR CALL CO LD C,LF CALL CO POP BC POP AF RET BLANK: PUSH AF ; Send one space to console PUSH BC LD C,' ' CALL CO POP BC POP AF RET ; GET A CHARACTER, convert to UC, ECHO it GETCMD: CALL CI CALL UCASE CP A,ESC RET Z ;Don't echo an ESC PUSH AF ;Save it PUSH BC LD C,A CALL CO ;Echo it POP BC POP AF ;get it back RET ; ;Convert LC to UC UCASE: CP A,'a' ;must be >= lowercase a RET C ; else go back... CP A,'z'+1 ;must be <= lowercase z RET NC ; else go back... SUB A,'a'-'A' ;subtract lowercase bias RET ; Check if next character is a ESC CHECK_ESC: CALL CI CP A,ESC RET ; Return Z if ESC character. ; Check if next character is a CR CHECK_CR: CALL CI CP A,CR RET ; Return Z if ESC character. ; Return with 2 HEX digits in [A]. If abort, Carry flag set + ESC in [A] GET_HEX: PUSH BC CALL GETCMD ;Get a character from keyboard & ECHO CP A,ESC JP Z,HEX_ABORT CP '/' ;check 0-9, A-F JP C,HEX_ABORT CP 'F'+1 JP NC,HEX_ABORT CALL ASBIN ;Convert to binary SLA A SLA A SLA A SLA A ;Shift to high nibble LD B,A ;Store it CALL GETCMD ;Get 2nd character from keyboard & ECHO CP A,ESC JP Z,HEX_ABORT CP '/' ;check 0-9, A-F JP C,HEX_ABORT CP 'F'+1 JP NC,HEX_ABORT CALL ASBIN ;Convert to binary OR A,B ;add in the first digit OR A,A ;To return NC POP BC RET HEX_ABORT: SCF ;Set Carry flag LD A,ESC POP BC RET ; ; ; Put 4 HEX characters in [HL] GET_HEX4: LD H,0000H CALL GET_HEX ;get 2 HEX digits JP C,SCAN_ABORT LD H,A CALL GET_HEX ;get 2 more HEX digits JP C,SCAN_ABORT LD L,A OR A,A ;To return NC RET GET_HEX2: LD L,0 CALL GET_HEX ;get 2 HEX digits JP C,SCAN_ABORT LD L,A OR A,A ;To return NC RET SCAN_ABORT: SCF ;Set Carry flag RET ; ASCII TO BINARY CONVERSION ROUTINE ASBIN: SUB 30H CP 0AH RET M SUB 07H RET ; ; ; PRINT [HL] ON CONSOL PRINT_HL: LD A,H CALL PRINT_A LD A,L PRINT_A: PUSH AF RRCA RRCA RRCA RRCA CALL SF598B POP AF SF598B: CALL CONV JP CO ; CONVERT HEX TO ASCII CONV: AND 0FH ADD A,90H DAA ADC A,40H DAA LD C,A RET ; DISPLAY 8 BITS OF [A] (No registers changed) ; DISPLAY BIT PATTERN IN [A] ZBITS: PUSH AF PUSH BC PUSH DE LD E,A LD B,8 BQ2: SLA E LD A,18H ADC A,A LD C,A CALL CO DJNZ BQ2 POP DE POP BC POP AF RET HEXDUMP: ;Print a hexdump of the data in the 512 byte buffer @[HL] LD D,32 ;Print 32 lines total LD B,16 ;16 characters across LD (@StartLineHex),HL ;Save the buffer location for ASCII display below LD HL,0 LD (@BYTE$COUNT),HL SF172: CALL CRLF LD HL,(@BYTE$COUNT) LD A,H CALL PRINT_A ;Print byte count in sector LD A,L CALL PRINT_A PUSH DE LD DE,16 ADD HL,DE POP DE LD (@BYTE$COUNT),HL ;store for next time CALL BLANK LD HL,(@StartLineHex) LD (@StartLineASCII),HL ;Store for ASCII display below SF175: LD A,(HL) CALL PRINT_A ;Display [A] on CRT/LCD INC HL DJNZ SF175 LD (@StartLineHex),HL ;Save for next line later CALL ShowAscii ;Now translate to ASCII and display LD B,16 ;16 characters across for next line DEC D JP NZ,SF172 ;Have we done all 32 lines CALL CRLF ret ShowAscii: ;Now show as ascii info LD HL,(@StartLineASCII) LD B,16 ;16 ASCII characters across XF172: CALL BLANK ;send a space character CALL BLANK XF175: LD A,(hl) AND 7FH CP ' ' ;FILTER OUT CONTROL CHARACTERS JP NC,XT33 XT22: LD A,'.' XT33: CP 07CH JP NC,XT22 LD C,A ;SET UP TO SEND push bc CALL CO pop bc inc hl ;Next position in buffer DJNZ XF175 ret ;<<<<<<<<<<<<<<<<<<<<<< MAIN CONSOL OUTPUT ROUTINE >>>>>>>>>>>>>>>>>>>>>>>>> ; CO: IN A,(CONSTAT) ; SD SYSTEMS or PROPELLER VIDIO BOARD PORT AND 4H JP Z,CO LD A,C OUT (CONOUT),A RET ; RETURN CHARACTER SENT IN [A] ;<<<<<<<<<<<<<<<<<<< MAIN CONSOL STATUS ROUTINE >>>>>>>>>>>>>>>>>>>>>> ; CSTS: IN A,(CONSTAT) AND 02H RET Z XOR A DEC A ; RETURN WITH 0FFH IN [A] IF SOMETHING RET ;<<<<<<<<<<<<<<<<<<<< MAIN CONSOL INPUT ROUTINE >>>>>>>>>>>>>>>>>>>> ; CI: IN A,(CONSTAT) ; NEED CONSTAT TO CLEAN UP SHIFT KEYS ETC AND 02H JP Z,CI IN A,(CONIN) AND 7FH RET ORG 800H ;--------------------------------------------------------------------------------------- SIGNON: DB CR,LF DB CR,LF,'Test program for SD CARD interface of S100Computers.com Dual SD Card Board' DB CR,LF,'Note: The test is for Type 2 SD Cards only. (e.g. Transcend 2GB Type 2)' DB CR,LF,'By John Monahan S100Computers.COM (V0.5) 1/22/2025',0 SIGNON1: DB CR,LF,LF,'----- Main Menu ---- RAM Buffer = ',0 SIGNON2: DB 'H' SIGNON3: DB CR,LF,'A. Initilize Drive A: for Sec R/W' DB CR,LF,'B. Initilize Drive B: for Sec R/W' DB CR,LF,'C. Reselect Drive A: for Ser R/W' DB CR,LF,'D. Reselect Drive B: for Ser R/W' DB CR,LF,'S Set Track & Sector. (CPM Track 0-FFH, Sector 0-FFH)' DB CR,LF,'R. Read the current selected SD card sector.' DB CR,LF,'W. Write the current selected SD card sector.' DB CR,LF,'F Quick CPM Format sectors for card (0-FFH Sectors)' DB CR,LF,'Z Reset the ESP32' DB CR,LF,'ESC To abort program.',0 DATA_ERROR_MSG: DB BELL,CR,LF,'Menu or Data entry error.',0 GET_TRK_MSG: DB CR,LF,'Enter CPM Track number. (XXH) '0 GET_SEC_MSG: DB CR,LF,'Enter CPM Sector number. (XXH) '0 ENTER_RAM_LOC: DB CR,LF,'Set Sector RAM buffer location.',CR,LF,0H ENTER_RAM_LOC1: DB CR,LF,'Please enter RAM buffer location (XXXXH): ',0 RD_ERROR DB CR,LF,BELL,'Sector read error',0 WR_ERROR DB CR,LF,BELL,'Sector write error',0 NEXT_SEC_MSG DB CR,LF,'Hit space bar for next sector. Anything else for main menu',0 SEC_NUM_MSG DB CR,LF,'SD Card CPM Sector Number = ',0 TRK_NUM_MSG DB CR,LF,'SD Card CPM Track Number = ',0 FORMAT_SEC_MSG DB CR,LF,'Enter number of 512 byte sectors to format (XX): ',0 FORMAT_WAIT_MSG DB CR,LF,'Please wait while sectors are being formatted',0 FORMAT_DONE_MSG DB CR,LF,'Formatting complete!',0 NO_CARD_MSG DB CR,LF,BELL,'You must first Initilize the SD card',0 SPACE_MSG DB 'H. ',0 SEC_WR_OK DB CR,LF,'Sector written OK',0 RESET_DONE_MSG DB CR,LF,'Reset ESP32',0 DB '>>>>>>>>>>>> DATA STORE >>>>>>>>>>>>>' SD_A_INITILIZED DB 0H SD_B_INITILIZED DB 0H CURRENT_CARD DB 0H DMA_ADDRESS DW 4000H CURRENT_SECTOR DB 00H CURRENT_TRACK DB 00H SECTOR_COUNT DW 0000H @StartLineHex DW 0000H @BYTE$COUNT DW 0000H @StartLineASCII DW 0000H DS 40H STACK: DB 0H ; END