;-------------------------------------------------------------------------------------------------------- ;- ;- Name: rfs_bank2.asm ;- Created: July 2019 ;- Author(s): Philip Smart ;- Description: Sharp MZ series Rom Filing System. ;- This assembly language program is written to utilise the banked flashroms added with ;- the MZ-80A RFS hardware upgrade. ;- ;- Credits: ;- Copyright: (c) 2018-2023 Philip Smart ;- ;- History: July 2019 - Merged 2 utilities to create this compilation. ;- May 2020 - Bank switch changes with release of v2 pcb with coded latch. The coded ;- latch adds additional instruction overhead as the control latches share ;- the same address space as the Flash RAMS thus the extra hardware to ;- only enable the control registers if a fixed number of reads is made ;- into the upper 8 bytes which normally wouldnt occur. Caveat - ensure ;- that no loop instruction is ever placed into EFF8H - EFFFH. ;- July 2020 - Bug fixes and additions. ;- July 2020 - Updated for the v2.1 hardware. RFS can run with a tranZPUter board with ;- or without the K64 I/O processor. RFS wont use the K64 processor all ;- operations are done by the Z80 under RFS. ;- Aug 2023 - Updates to make RFS run under the SFD700 Floppy Disk Interface board. ;- UROM remains the same, a 2K paged ROM, MROM is located at F000 when ;- RFS is built for the SFD700. ;- ;-------------------------------------------------------------------------------------------------------- ;- This source file is free software: you can redistribute it and-or modify ;- it under the terms of the GNU General Public License as published ;- by the Free Software Foundation, either version 3 of the License, or ;- (at your option) any later version. ;- ;- This source file is distributed in the hope that it will be useful, ;- but WITHOUT ANY WARRANTY; without even the implied warranty of ;- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;- GNU General Public License for more details. ;- ;- You should have received a copy of the GNU General Public License ;- along with this program. If not, see . ;-------------------------------------------------------------------------------------------------------- IF BUILD_SFD700 = 1 ORG 0E000H ALIGN 0E300H DB "BANK2" ALIGN UROMADDR ENDIF ;=========================================================== ; ; USER ROM BANK 2 - SD Card Controller functions. ; ;=========================================================== ORG UROMADDR ;-------------------------------- ; Common code spanning all banks. ;-------------------------------- NOP HWSELROM2 ; Select the first ROM page. ; ; No mans land... this should have switched to Bank 0 and at this point there is a jump to 00000H. JP 00000H ; This is for safety!! ;------------------------------------------------------------------------------------------ ; Bank switching code, allows a call to code in another bank. ; This code is duplicated in each bank such that a bank switch doesnt affect logic flow. ;------------------------------------------------------------------------------------------ ALIGN_NOPS UROMBSTBL ; BKSW2to0: PUSH AF LD A, ROMBANK2 ; Calling bank (ie. us). PUSH AF LD A, ROMBANK0 ; Required bank to call. JR BKSW2_0 BKSW2to1: PUSH AF LD A, ROMBANK2 ; Calling bank (ie. us). PUSH AF LD A, ROMBANK1 ; Required bank to call. JR BKSW2_0 BKSW2to2: PUSH AF LD A, ROMBANK2 ; Calling bank (ie. us). PUSH AF LD A, ROMBANK2 ; Required bank to call. JR BKSW2_0 BKSW2to3: PUSH AF LD A, ROMBANK2 ; Calling bank (ie. us). PUSH AF LD A, ROMBANK3 ; Required bank to call. JR BKSW2_0 BKSW2to4: PUSH AF LD A, ROMBANK2 ; Calling bank (ie. us). PUSH AF LD A, ROMBANK4 ; Required bank to call. JR BKSW2_0 BKSW2to5: PUSH AF LD A, ROMBANK2 ; Calling bank (ie. us). PUSH AF LD A, ROMBANK5 ; Required bank to call. JR BKSW2_0 BKSW2to6: PUSH AF LD A, ROMBANK2 ; Calling bank (ie. us). PUSH AF LD A, ROMBANK6 ; Required bank to call. JR BKSW2_0 BKSW2to7: PUSH AF LD A, ROMBANK2 ; Calling bank (ie. us). PUSH AF LD A, ROMBANK7 ; Required bank to call. ; BKSW2_0: PUSH HL ; Place function to call on stack LD HL, BKSWRET2 ; Place bank switchers return address on stack. EX (SP),HL LD (TMPSTACKP),SP ; Save the stack pointer as some old code corrupts it. BNKSWSEL JP (HL) ; Jump to required function. BKSWRET2: POP AF ; Get bank which called us. BNKSWSELRET POP AF RET ;------------------------------------------------------------------------------- ; GENERAL PURPOSE FUNCTIONS. ;------------------------------------------------------------------------------- ; Method to multiply a 16bit number by another 16 bit number to arrive at a 32bit result. ; Input: DE = Factor 1 ; BC = Factor 2 ; Output:DEHL = 32bit Product ; MULT16X16: LD HL,0 LD A,16 MULT16X1: ADD HL,HL RL E RL D JR NC,$+6 ADD HL,BC JR NC,$+3 INC DE DEC A JR NZ,MULT16X1 RET ; Method to add a 16bit number to a 32bit number to obtain a 32bit product. ; Input: DEHL = 32bit Addend ; BC = 16bit Addend ; Output:DEHL = 32bit sum. ; ADD3216: ADD HL,BC EX DE,HL LD BC,0 ADC HL,BC EX DE,HL RET ; Method to add two 32bit numbers whilst calculating the SD Start Sector. ; Input: DEHL = 32bit Addend ; (SDSTARTSEC) = 32bit Addend ; Output: (SDSTARTSEC) = 32bit Sum. ; Output; DEHL = 32bit Sum. ; ADD32: LD BC,(SDSTARTSEC+2) ADD HL,BC LD (SDSTARTSEC+2),HL LD BC,(SDSTARTSEC) EX DE,HL ADC HL,BC LD (SDSTARTSEC),HL EX DE,HL RET ; A function from the z88dk stdlib, a delay loop with T state accuracy. ; ; enter : hl = tstates >= 141 ; uses : af, bc, hl T_DELAY: LD BC,-141 ADD HL,BC LD BC,-23 TDELAYLOOP: ADD HL,BC JR C, TDELAYLOOP LD A,L ADD A,15 JR NC, TDELAYG0 CP 8 JR C, TDELAYG1 OR 0 TDELAYG0: INC HL TDELAYG1: RRA JR C, TDELAYB0 NOP TDELAYB0: RRA JR NC, TDELAYB1 OR 0 TDELAYB1: RRA ;RET NC RET ;------------------------------------------------------------------------------- ; START OF SD CONTROLLER FUNCTIONALITY ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; Hardware SPI SD Controller (HW_SPI_ENA = 1) ; This logic uses the RFS PCB v2+ hardware shift registers to communicate with ; an SD Card. It is the fastest solution available but has a high IC count. ; ; Software SPI SD Controller (SW_SPI_ENA = 1) ; This logic uses the RFS PCB v2+ logic to simulate the SPI interface with ; bitbanging techniques. It is similar to the Parallel Port SD Controller ; but uses logic on the RFS board rather than the parallel port interface. ; ; Parallel Port SD Controller (PP_SPI_ENA = 1) ; This logic uses the standard Sharp MZ-80A Parallel Port for simulating the ; SPI interface with bitbanging techniques. This interface is then used to ; communicate with an SD Card. ;------------------------------------------------------------------------------- ; Method to initialise the SD card. Assume that the RFS control registers are enabled, the default state within the RFS environment. ; SDINIT: LD A,0FFH ; CS to inactive (high) CALL SPICS ; CALL SPIINIT ; Train SD with our clock. ; LD A,0 ; CS to active (low) CALL SPICS ; LD BC,SD_RETRIES ; Number of retries before giving up, card not responding. SDINIT1: LD A,CMD0 ; Command 0 LD HL,00000H ; NB. Important, HL should be coded as LH due to little endian and the way it is used in SDCMD. LD DE,00000H ; NB. Important, DE should be coded as ED due to little endian and the way it is used in SDCMD. PUSH BC ; CALL SDCMD ; LD A,(SDBUF+6) ; Get response code. DEC A ; Set Z flag to test if response is 0x01 POP BC JP Z,SDINIT2 ; Command response 0x01? Exit if match. DEC BC LD A,B OR C JR NZ,SDINIT1 ; Retry for BC times. LD A,1 JP SD_EXIT ; Error, card is not responding to CMD0 SDINIT2: ; Now send CMD8 to get card details. This command can only be sent ; when the card is idle. LD A,CMD8 ; CMD8 has 0x00001AA as parameter, load up registers and call command routine. LD HL,00000H ; NB. Important, HL should be coded as LH due to little endian and the way it is used in SDCMD. LD DE,0AA01H ; NB. Important, DE should be coded as ED due to little endian and the way it is used in SDCMD. CALL SDCMD ; Version 2 card, check its voltage range. IF not in the 2.7-3.6V dont try the ACMD41 to get capabilities. SDINIT3: LD A,1 ; Check that we receive 0x0001AA in response. LD (SDVER),A ; Indicate this is not a version 2 card. LD A,(SDBUF+9) CP 1 JP NZ,SDINIT8 LD A,(SDBUF+10) CP 0AAH JP NZ,SDINIT8 SDINIT4: LD A,2 ; This is a version 2 card. SDINIT5: LD (SDVER),A ; Indicate this is not a version 2 card. CALL SDACMD41 JR Z,SDINIT6 LD A,2 ; Error, card is not responding to ACMD41 JP SD_EXIT SDINIT6: LD A,CMD58 ; CMD58 has 0x00000000 as parameter, load up registers and call command routine. LD HL,00000H ; NB. Important, HL should be coded as LH due to little endian and the way it is used in SDCMD. LD DE,00000H ; NB. Important, DE should be coded as ED due to little endian and the way it is used in SDCMD. CALL SDCMD LD A,(SDBUF+6) CP 040H LD A,CT_SD2 JR Z,SDINIT7 LD A,CT_SD2 | CT_BLOCK SDINIT7: LD (SDCAP),A ; Set the capabilities according to the returned flag. JR SDINIT14 ; Version 1 card or MMC v3. SDINIT8: CALL SDACMD41 LD A, CT_SD1 LD E,ACMD41 ; SD1 cards we use the ACMD41 command. JR Z,SDINIT9 LD A,CT_MMC LD E,CMD1 ; MMC cards we use the CMD1 command. SDINIT9: LD (SDCAP),A LD A,E CP ACMD41 JR NZ,SDINIT10 CALL SDACMD41 JR Z,SDINIT14 LD A,3 ; Exit code, failed to initialise v1 card. JP SD_EXIT SDINIT10: LD BC,10 ; ACMD41/CMD55 may take some cards time to process or respond, so give a large number of retries. SDINIT11: PUSH BC LD A,CMD1 ; CMD1 has 0x00000000 as parameter, load up registers and call command routine. LD HL,00000H ; NB. Important, HL should be coded as LH due to little endian and the way it is used in SDCMD. LD DE,00000H ; NB. Important, DE should be coded as ED due to little endian and the way it is used in SDCMD. CALL SDCMD LD A,(SDBUF+6) ; Should be a response of 0 whereby the card has left idle. OR A JR Z,SDINIT13 LD BC,0FFFFH ; Delay for at least 200mS for the card to recover and be ready. SDINIT12: DEC BC ; 6T LD A,B ; 9T OR C ; 4T JR NZ,SDINIT12 ; 12T = 31T x 500ns = 15.5uS x 12903 = 200mS ; POP BC DEC BC LD A,B OR C JR NZ,SDINIT11 LD A,4 ; Exit code, failed to initialise v1 MMC card. JP SD_EXIT SDINIT13: POP BC LD A,CMD16 ; No response from the card for an ACMD41/CMD1 so try CMD16 with parameter 0x00000200 LD HL,00000H ; NB. Important, HL should be coded as LH due to little endian and the way it is used in SDCMD. LD DE,00002H ; NB. Important, DE should be coded as ED due to little endian and the way it is used in SDCMD. CALL SDCMD LD A,(SDBUF+6) OR A JR Z,SDINIT14 LD A,0 LD (SDCAP),A ; No capabilities on this unknown card. SDINIT14: XOR A JR SD_EXIT SD_EXIT: LD L,A ; Return value goes into HL. LD H,0 RET ; Method to send a command to the card and receive back a response. ; ; A = CMD to send ; LHED = Argument, ie. CMD = A, L, H, E, D, CRC ; SDCMD: LD (SDBUF),A LD (SDBUF+1),HL EX DE,HL LD (SDBUF+3),HL ; ; Send command but with parameters preloaded by caller. SDCMDNP: LD B,5 ; R1 + 32bit argument for CMD8, CMD58 CP CMD8 LD C,135 JP Z,SDCMD0 LD C,1 ; CMD58 is not CRC checked so just set to 0x01. CP CMD58 LD B,5 ; R1 + 32bit argument JP Z,SDCMD0 ; LD B,1 ; Default, expect R1 which is 1 byte. CP CMD0 ; Work out the CRC based on the command. CRC checking is LD C,149 ; not implemented but certain commands require a fixed argument and CRC. JP Z,SDCMD0 LD C,1 ; Remaining commands are not CRC checked so just set to 0x01. SDCMD0: PUSH BC ; Save the CRC and the number of bytes to be returned, LD A,C ; Store the CRC LD (SDBUF+5),A LD A,255 ; Preamble byte CALL SPIOUT LD HL,SDBUF LD B,6 SDCMD1: PUSH BC LD A,(HL) INC HL CALL SPIOUT ; Send the command and parameters. POP BC DJNZ SDCMD1 PUSH HL LD HL,SD_RETRIES SDCMD2: PUSH HL CALL SPIIN POP HL CP 0FFH JR NZ,SDCMD4 ; If != 0xFF then byte received is response R1. DEC HL LD A,H OR L JR NZ,SDCMD2 ; ; Error as we are not receiving data. ; POP HL POP BC LD A,1 ; Force return code to be error. LD (SDBUF+6),A RET SDCMD3: PUSH BC PUSH HL CALL SPIIN ; SDCMD4: POP HL LD (HL),A INC HL POP BC ; Get back number of expected bytes. HL = place in buffer to store response. DJNZ SDCMD3 IF HW_SPI_ENA = 0 LD A,DOUT_HIGH | CLOCK_LOW | CS_HIGH OUT (SPI_OUT),A ENDIF RET ; Method to send an Application Command to the SD Card. This involves sending CMD55 followed by the required command. ; ; A = ACMD to send ; LHED = Argument, ie. ACMD = A, L, H, E, D, CRC ; SDACMD: PUSH AF PUSH DE PUSH HL LD A,CMD55 ; CMD55 has 0x00000000 as parameter, load up registers and call command routine. LD HL,00000H ; NB. Important, HL should be coded as LH due to little endian and the way it is used in SDCMD. LD DE,00000H ; NB. Important, DE should be coded as ED due to little endian and the way it is used in SDCMD. CALL SDCMD POP HL POP DE LD A,(SDBUF+6) ; Should be a response of 0 or 1. CP 2 JR NC,SDACMDE POP AF CALL SDCMD SDACMD0: LD A,(SDBUF+6) ; Should be a response of 0 whereby the card has left idle. OR A RET SDACMDE: POP AF JR SDACMD0 ; Method to send Application Command 41 to the SD card. This command involves retries and delays ; hence coded seperately. ; ; Returns Z set if successful, else NZ. ; SDACMD41: LD BC,10 ; ACMD41/CMD55 may take some cards time to process or respond, so give a large number of retries. SDACMD1: PUSH BC LD A,ACMD41 ; ACMD41 has 0x40000000 as parameter, load up registers and call command routine. LD HL,00040H ; NB. Important, HL should be coded as LH due to little endian and the way it is used in SDCMD. LD DE,00000H ; NB. Important, DE should be coded as ED due to little endian and the way it is used in SDCMD. CALL SDACMD JR Z,SDACMD3 LD BC,12903 ; Delay for at least 200mS for the card to recover and be ready. SDACMD2: DEC BC ; 6T LD A,B ; 9T OR C ; 4T JR NZ,SDACMD2 ; 12T = 31T x 500ns = 15.5uS x 12903 = 200mS ; POP BC DEC BC LD A,B OR C JR NZ,SDACMD1 LD A,1 OR A ; Retries exceeded, return error. RET SDACMD3: POP BC ; Success, tidy up stack and exit with Z set. XOR A RET ; Method to initialise communications with the SD card. We basically train it to our clock characteristics. ; This is important, as is maintaining the same clock for read or write otherwise the card may not respond. SPIINIT: IF HW_SPI_ENA = 1 ; Hardware SPI on the RFS v2+ PCB. LD B,10 LD A, 0FFH ; We need to send 80 '1's, so preload the data register with all 1's, future transmits dont require this as it self loads with 1's. LD (HWSPIDATA),A SPIINIT1: LD (HWSPISTART),A ; Commence transmission of an 8bit byte. Runs 1 8MHz, so 1 byte in 1uS, it takes the Z80 2uS for its quickest instruction at 2MHz clock. DJNZ SPIINIT1 RET ELSE ; Software SPI on the RFS v2+ PCB. IF SW_SPI_ENA = 1 ELSE ; Software SPI on the centronics parallel port. LD B,80 SPIINIT1: LD A,DOUT_HIGH | CLOCK_HIGH | CS_HIGH ; Output a 1 OUT (SPI_OUT),A LD A,DOUT_HIGH | CLOCK_LOW | CS_HIGH ; Output a 1 OUT (SPI_OUT),A DJNZ SPIINIT1 RET ENDIF ENDIF ; Method to set the Chip Select level on the SD card. The Chip Select is active LOW. ; ; A = 0 - Set CS LOW (active) ; A = 0xFF - Set CS HIGH (active) SPICS: IF HW_SPI_ENA = 1 ; Hardware SPI on the RFS v2+ PCB. OR A LD A,(ROMCTL) SET 1,A ; If we are inactivating CS then set CS high and disable clock by setting BBCLK to low. RES 0,A JR NZ, SPICS0 RES 1,A ; If we are activating CS then set CS low and enable clock by setting BBCLK to high. SET 0,A SPICS0: LD (BNKCTRL),A LD (ROMCTL),A RET ELSE ; Software SPI on the RFS v2+ PCB. IF SW_SPI_ENA = 1 ELSE ; Software SPI on the centronics parallel port. OR A LD A,DOUT_HIGH | CLOCK_LOW | CS_LOW ; Set CS Low if parameter = 0 (ie. enable) JR Z, SPICS0 LD A,DOUT_HIGH | CLOCK_LOW | CS_HIGH ; Set CS High if parameter != 0 (ie. disable) SPICS0: OUT (SPI_OUT),A RET ENDIF ENDIF ; Method to send a byte to the SD card via the SPI protocol. ; This method uses the hardware shift registers. ; ; Input A = Byte to send. ; SPIOUT: IF HW_SPI_ENA = 1 ; Hardware SPI on the RFS v2+ PCB. LD (HWSPIDATA),A LD (HWSPISTART),A RET ELSE ; Software SPI on the RFS v2+ PCB. IF SW_SPI_ENA = 1 ELSE ; Software SPI on the centronics parallel port. RLCA ; 65432107 RLCA ; 54321076 RLCA ; 43210765 - Adjust so that starting bit is same position as Data line. LD E,A ; E = Character to send. LD B,8 ; B = Bit count SPIOUT0: LD A,E AND DOUT_MASK ; Data bit to data line, clock and cs low. RLC E SPIOUT1: OUT (SPI_OUT),A OR CLOCK_HIGH ; Clock high OUT (SPI_OUT),A AND CLOCK_MASK ; Clock low OUT (SPI_OUT),A DJNZ SPIOUT0 ; Perform actions for the full 8 bits. RET ENDIF ENDIF ; Method to receive a byte from the SD card via the SPI protocol. ; This method uses the hardware shift registers. ; NB. Timing must be very similar in SPIOUT and SPIIN. ; ; Output: A = received byte. ; SPIIN: IF HW_SPI_ENA = 1 ; Hardware SPI on the RFS v2+ PCB. LD (HWSPISTART),A ; Commence transmission to receive back data from the SD card, we just send 1's. LD A,(HWSPIDATA) ; Get the data byte. RET ELSE ; Software SPI on the RFS v2+ PCB. IF SW_SPI_ENA = 1 ELSE ; Software SPI on the centronics parallel port. LD BC,00800H | SPI_OUT ; B = Bit count, C = clock port LD L,0 ; L = Character being read. LD D,DOUT_HIGH | CLOCK_LOW | CS_LOW ; Output a 0 OUT (C),D ; To start ensure clock is low and CS is low. LD E,DOUT_HIGH | CLOCK_HIGH | CS_LOW ; Output a 0 SPIIN1: OUT (C),E ; Clock to high. IN A,(SPI_IN) ; Input the received bit OUT (C),D ; Clock to low. SRL A RL L DJNZ SPIIN1 ; Perform actions for the full 8 bits. LD A,L ; return value RET ENDIF ENDIF ;------------------------------------------------------------------------------- ; End of SPI SD Controller ;------------------------------------------------------------------------------- ; Method to skip over an SD card input stream to arrive at the required bytes, ; ; Input: BC = Number of bytes to skip. ; SPISKIP: PUSH BC CALL SPIIN POP BC DEC BC LD A,B OR C JR NZ,SPISKIP RET ; Method to convert an LBA value into a physical byte address. This is achieved by multiplying the block x 512. ; We take the big endian sector value, shift left 9 times then store the result back onto the stack. ; This is done as follows: <2> <1> <0> => <2> <1> <0> 0 (ie. 8 bit shift): Shift left <0> with carry, shift left <1> shift left <2>, 0 to ; ; Input: HL = Stack offset. ; LBATOADDR: LD HL,(SDSTARTSEC+1) LD A,(HL) ; Start ny retrieving bytes as HED0 INC HL LD E,(HL) INC HL LD D,(HL) LD L,A SLA D ; Shift the long left by 9 to effect a x512 RL E RL H LD BC,(SDSTARTSEC) LD A,H ; Now save the results as LHED, big endian format as used by the SD Card argument LD (BC),A INC BC LD A,E LD (BC),A INC BC LD A,D LD (BC),A INC BC XOR A LD (BC),A RET ; ; This method was originally a C routine I was using for FatFS but optimised it (still more can be done). The C->ASM is not so optimal. ; ; Input: Memory variables: SDSTARTSEC= unsigned long sector. - The sector number or direct byte address for older cards. This is big endian as per card. ; HL: Address where to store data read from sector. ; BC: Size of data to read, upto a sector size. ; Output: A: 0 - All ok. A > 0 - error occurred. ; HL: End address of data loaded, ; SD_READ: PUSH HL ; Store the load address. PUSH BC ; Store the read size. XOR A CALL SPICS ; Set CS low (active). LD HL,(SDCAP) ; Test to see if CT_BLOCK is available. LD H,0 LD A,CT_BLOCK AND L JP NZ,SD_READ1 ; If it has CT_BLOCK then use sector numbers otherwise multiply up to bytes. CALL LBATOADDR ; Multiply the sector by 512 for byte addressing on older cards. SD_READ1: ; A = ACMD to send ; LHED = Argument, ie. ACMD = A, L, H, E, D, CRC LD A,CMD17 ; Send CMD17 to read a sector. LD (SDBUF),A LD HL,(SDSTARTSEC) LD (SDBUF+1),HL LD HL,(SDSTARTSEC+2) LD (SDBUF+3),HL CALL SDCMDNP ; Execute SD Command, parameters already loaded into command buffer. LD A,(SDBUF+6) ; Fetch result and store. AND A JP NZ,SD_READ6 ; If R1 is positive then an error occurred, get out. LD HL,1000 ; Sit in a tight loop waiting for the data packet arrival (ie. not 0xFF). SD_READ2: PUSH HL LD HL,200 CALL T_DELAY CALL SPIIN POP HL CP 255 JP NZ,SD_READ3 DEC HL LD A,H OR L JR NZ,SD_READ2 SD_READ3: CP 254 ; Data? If not exit with error code. JP NZ,SD_READ6 LD HL,SD_SECSIZE ; Size of full sector + 2 bytes CRC. POP BC OR A SBC HL,BC ; Calculate the bytes to skip once we have read the required bytes. EX DE,HL POP HL ; Get the store address into HL. SD_READ4: PUSH HL ; Start reading bytes into the buffer. PUSH DE PUSH BC CALL SPIIN POP BC POP DE POP HL LD (HL),A INC HL ; Update buffer pointer. DEC BC LD A,B OR C JP NZ,SD_READ4 ; Not zero, keep reading. PUSH HL PUSH DE POP BC INC BC ; Were not interested in the CRC so skip it. INC BC CALL SPISKIP ; Skip unread bytes + CRC. POP HL XOR A ; And exit with success. SD_READ5: PUSH AF LD A,0FFH ; De-activate CS. CALL SPICS POP AF RET SD_READ6: POP BC POP HL LD A,1 JR SD_READ5 ; Method to write a 512byte sector to an SD Card. ; ; Input: Memory variables: SDSTARTSEC= unsigned long sector. - The sector number or direct byte address for older cards. This is big endian as per card. ; HL: Address of buffer to read data from. ; BC: Size of data to write, upto a sector size. ; Output: A: 0 - All ok. A > 0 - error occurred. ; HL: Address of end of buffer. SD_WRITE: PUSH HL PUSH BC XOR A ; Activate CS (set low). CALL SPICS ; Open transaction. LD HL,(SDCAP) ; Check to see if the card has block addressing. LD H,0 LD A,CT_BLOCK AND L JP NZ,SD_WRITE1 ; If it hasnt then we need to multiply up to the correct byte. CALL LBATOADDR ; Multiply the sector by 512 for byte addressing. ; A = ACMD to send ; LHED = Argument, ie. ACMD = A, L, H, E, D, CRC SD_WRITE1: LD A,CMD24 ; Send CMD24 to write a sector. LD (SDBUF),A LD HL,(SDSTARTSEC) ; Place long endian sector into command buffer. LD (SDBUF+1),HL LD HL,(SDSTARTSEC+2) LD (SDBUF+3),HL CALL SDCMDNP ; Send command using No Parameters version as we loaded them into the command buffer already. LD A,(SDBUF+6) ; Fetch result and store. AND A JP NZ,SD_WRITE10 LD A,255 ; Ok, so command sent successfully, mark the write by sending an 0xFF followed by 0xFE CALL SPIOUT LD A,254 CALL SPIOUT ; Write buffer. POP BC LD HL,SD_SECSIZE OR A SBC HL,BC ; Calculate number of bytes to pad with 0. EX DE,HL POP HL ; Address to read data from. ; SD_WRITE2: LD A,B ; So long as we have bytes in the buffer, send to the card for writing. OR C JP Z,SD_WRITE3 PUSH DE PUSH BC LD A,(HL) ; Get the byte to transmit. INC HL ; And update the pointer. CALL SPIOUT ; Transmit value in A. POP BC POP DE DEC BC JR SD_WRITE2 ; Close transaction. SD_WRITE3: PUSH HL ; Save the end of buffer address for return to caller. PUSH DE ; DE contains number of padding bytes to send POP BC INC BC ; Add 2 bytes for CRC INC BC SD_WRITE4: LD A,B ; Test to see if we are already at zero, ie. all bytes sent. Exit if so. OR C JP Z,SD_WRITE5 DEC BC PUSH BC XOR A ; Send 0's as padding bytes and CRC. CALL SPIOUT POP BC JP SD_WRITE4 SD_WRITE5: CALL SPIIN ; Check received response, if 0x05 which indicates write under way inside SD Card. AND 01FH CP 5 JP NZ,SD_WRITE11 LD HL,10000 ; Now wait for the write to complete allowing 1000mS before timing out. PUSH HL JR SD_WRITE7 SD_WRITE6: DEC HL PUSH HL LD HL,200 ; 200T state delay = 200 x 1/2000000 = 100uS CALL T_DELAY SD_WRITE7: CALL SPIIN ; Get a byte, if it is not 0xFF then we have our response so exit. POP HL CP 255 JP Z,SD_WRITE8 LD A,H OR L JR NZ,SD_WRITE6 SD_WRITE8: LD A,H ; End of timeout? If so we exit with the preset fail code. OR L JP Z,SD_WRITE11 XOR A ; Success code. POP HL ; Get the updated return address to pass back to caller. SD_WRITE9: PUSH AF LD A,0FFH ; Disable SD Card Chip Select to finish. CALL SPICS POP AF RET SD_WRITE10: POP HL ; Waste the byte count and .. SD_WRITE11: POP HL ; ..load address. LD A,1 ; Error exit. JR SD_WRITE9 ; Method to print out an SDC directory entry name along with an incremental file number. The file number can be ; used as a quick reference to a file rather than the filename. ; ; Input: HL = Address of filename. ; D = File number. ; A = File type. ; PRTDIR: PUSH BC PUSH DE PUSH HL LD C,A ; Preseve file type. ; LD A,(SCRNMODE) CP 0 LD H,47 JR Z,PRTDIR0 LD H,93 PRTDIR0: LD A,(TMPLINECNT) ; Pause if we fill the screen. LD E,A INC E CP H JR NZ,PRTNOWAIT LD E, 0 PRTDIRWAIT: CALL GETKY CP ' ' JR Z,PRTNOWAIT CP 'X' ; Exit from listing. LD A,001H JR Z,PRTDIR4 JR PRTDIRWAIT PRTNOWAIT: LD A,E LD (TMPLINECNT),A ; LD A, D ; Print out file number and increment. CALL PRTHX LD A,C LD C,02AH CP BTX1CD ; '*' File type is 80K/80A BASIC. JR Z,PRTDIR0A INC C CP BTX2CD ; '+' File type is 700/800 BASIC. JR Z,PRTDIR0A INC C CP BTX3CD ; ',' File type is a NASCOM Cassette BASIC program. JR Z,PRTDIR0A INC C CP BTX4CD ; '-' File type is a NASCOM ASCII TEXT BASIC program. JR Z,PRTDIR0A INC C CP OBJCD ; '.' File type is MACHINE CODE program. JR Z,PRTDIR0A INC C PRTDIR0A: LD A,C CALL PRNT POP DE PUSH DE ; Get pointer to the file name and print. LD HL,PRTFN CALL BKSW2to6 ; Print out the filename. ; LD HL, (DSPXY) ; LD A,L CP 20 LD A,20 JR C, PRTDIR2 ; LD A,(SCRNMODE) ; 40 Char mode? 2 columns of filenames displayed so NL. CP 0 JR Z,PRTDIR1 ; LD A,L ; 80 Char mode we print 4 columns of filenames. CP 40 LD A,40 JR C, PRTDIR2 ; LD A,L CP 60 LD A,60 JR C, PRTDIR2 ; PRTDIR1: CALL NL JR PRTDIR3 PRTDIR2: LD L,A LD (DSPXY),HL PRTDIR3: XOR A PRTDIR4: OR A POP HL POP DE POP BC RET ; Method to get the LBA sector for an RFS SDCFS image location according to drive. ; Inputs: ; A = Starting sector in a single image. ; or ; DEHL = starting sector. ; Outputs: ; DEHL = Sector number. RFSGETSECT: LD DE,0 ; Load DEHL with a 32bit start sector. DE=0 as SDCFS starts from sector 0. LD H,0 ; Set HL to sector number. LD L,A RFSGETSECTR:LD A,(SDDRIVENO) ; Entry point when DEHL is defined. GETDIRDRV: OR A ; Multiply up according to drive number. RET Z CP 'C' ; If SDDRIVENO is set to 'C" - CMT, then default to SD Drive 0. CMT is valid in certain applications but not for SD access. JR NZ,GETDIRDRV1 LD A,1 GETDIRDRV1: DEC A LD BC,08010H ; Number of sectors in an SDCFS image, 2000H directory + (64 * 65536) file blocks. CALL ADD3216 ; Add drive sector multiple to move to next drive. JR GETDIRDRV ; Method to set the SD Card 32bit LBA sector address. ; Inputs: ; DEHL = 32bit sector number. SETLBAADDR: PUSH HL POP BC LD HL,SDSTARTSEC ; Store the starting sector in the SD card buffer ready for retrieval. LD (HL), D INC HL LD (HL), E INC HL LD (HL), B INC HL LD (HL), C INC HL RET ; Method to get an SD Directory entry. ; The SD Card works in 512byte sectors so a sector is cached and each call evaluates if the required request is in cache, if it is not, a new sector ; is read. ; ; Input: E = Directory entry number to retrieve. ; Output: HL = Address of directory entry. ; A = 0, no errors, A > 1 error. GETSDDIRENT:PUSH BC PUSH DE; ; LD A,E SRL A SRL A SRL A SRL A ; Divide by 16 to get sector number. LD C,A LD A,(DIRSECBUF) ; Do we have this sector in the buffer? If we do, use it. CP C JR Z,GETDIRSD0 LD A,C LD (DIRSECBUF),A ; Save the sector number we will load into the buffer. ; LD HL,SECTORBUF LD (SDLOADADDR),HL ; CALL RFSGETSECT ; Get the directory sector, offset by drive number. DEHL is returned as the full 32bit LBA sector address. CALL SETLBAADDR ; Store the sector address into the SD command buffer. ; LD BC,SD_SECSIZE ; Set retrieval size to 1 sector. LD (SDLOADSIZE),BC LD HL,(SDLOADADDR) ; ;DI CALL SD_READ ; Read the sector. ;EI ; OR A JR NZ,DIRSDERR LD (SDLOADADDR),HL ; Save the updated address. GETDIRSD0: POP DE PUSH DE LD A,E ; Retrieve the directory entry number required. AND 00FH LD HL,SECTORBUF JR Z,GETDIRSD2 LD B,A LD DE,SDDIR_ENTSZ ; Directory entry size GETDIRSD1: ADD HL,DE ; Directory entry address into HL. DJNZ GETDIRSD1 GETDIRSD2: POP DE POP BC XOR A GETDIRSD3: OR A RET ; DIRSDERR: EX DE,HL ; Print error message, show HL as it contains sector number where error occurred. PUSH HL POP BC ; HL to BC as the call requires the value to be displayed in BC. LD DE,MSGSDRERR CALL SDPRINT ; Print out the filename. POP DE POP BC LD A,1 JR GETDIRSD3 ; Method to find a free slot in the directory. ; ; Input: None. ; Output: E = Free directory entry. ; HL = Address of directory entry. ; C = 1 if no free directory slot available. ; Unless other directory entry functions called, DIRSECBUF = directory sector in cache which contains the free entry found. GETDIRFREE: LD A,0FFH LD (DIRSECBUF),A ; Reset the sector buffer in memory indicator. ; LD E,0 ; Directory entry number LD B,0 ; Scanning 256 directory entries for a free slot. GETDIRFREE1:CALL GETSDDIRENT ; Get SD Directory entry details for directory entry number stored in D. RET NZ ; Error reading sector so abort. LD A,(HL) BIT 7,A ; Look for an inactive directory entry slot, Bit 7 of lower flag = 0? JR NZ,GETDIRFREE2 OR A RET ; Found an entry that is free. GETDIRFREE2:INC E ; Onto next directory entry number. DJNZ GETDIRFREE1 SCF RET ; Method to write the cached SD Directory entry sector back to SD card. ; Normal use would be to call GERSDDIRENT to locate a required entry or slot, update it then call ; this method to flush it back to SD disk. WRSDDIRENT: LD A,(DIRSECBUF) ; Get the directory sector number of the cached directory sector. ; CALL RFSGETSECT ; Get the directory sector, offset by drive number. DEHL is returned as the full 32bit LBA sector address. CALL SETLBAADDR ; Store the sector address into the SD command buffer. ; LD HL,SECTORBUF ; Address of the sector. LD BC,SD_SECSIZE ; Set the size as one full sector. ; ;DI CALL SD_WRITE ;EI ; OR A JR NZ,DIRSDWERR XOR A WRSDDIRENT1:OR A RET DIRSDWERR: LD DE,MSGSDWERR PUSH HL POP BC ; HL to BC as the call requires the value to be displayed in BC. CALL SDPRINT ; Print out the filename. LD A,1 JR WRSDDIRENT1 ; Method to list the directory of the SD Card. ; ; This method creates a unique sequenced number starting at 0 and attaches to each successive valid directory entry ; The file number and file name are then printed out in tabular format. The file number can be used in Load/Save commands ; instead of the filename. ; ; No inputs or outputs. ; DIRSDCARD: LD A,1 ; Setup screen for printing, account for the title line. TMPLINECNT is used for page pause. LD (TMPLINECNT),A LD A,0FFH LD (DIRSECBUF),A ; Reset the sector buffer in memory indicator. ; LD A,(SDDRIVENO) CP 'C' JR Z,DIRSDP ADD A,'0' DIRSDP: LD C,A ; C is printed as a character embedded in the message. LD DE,MSGCDIRLST ; Print out header. CALL SDPRINT ; Print out the header. ; DIRSD0: LD E,0 ; Directory entry number LD D,0 ; Directory file number (incr when a valid dirent is found). LD B,0 DIRSD1: CALL GETSDDIRENT ; Get SD Directory entry details for directory entry number stored in D. RET NZ DIRSD2: LD A,(HL) BIT 7,A ; Is this entry active, ie. Bit 7 of lower flag = 1? JR Z,DIRSD3 INC HL LD A,(HL) ; Get file attribute for print identification. INC HL ; Hop over flags. CALL PRTDIR ; Valid entry so print directory number and name pointed to by HL. JR NZ,DIRSD4 INC D DIRSD3: INC E ; Onto next directory entry number. DJNZ DIRSD1 DIRSD4: RET ; ; Method to locate an entry in the SD directory based on a filenumber or a filename. ; This method is called with the raw parameter string in DE which is then parsed to determine if a filenumber ; or filename has been given. ; Input: DE = String containing a filenumber of filename. ; Output: (TMPCNT) = 0xFFFF - DE contains a filename. ; (TMPCNT) != 0xFFFF - DE contains a filenumber and (TMPCNT) is set to that number. FINDSDX: PUSH DE LD HL,0FFFFh ; Tag the filenumber as invalid. LD (TMPCNT), HL CALL _2HEX JR C, FINDSDX1 ; LD L,A LD H,0 LD A,(DE) ; Before comitting the file number verify it is actually a 2 digit hex string. CP 00DH JR Z,FINDSDX0 OR A JR NZ,FINDSDX1 FINDSDX0: LD (TMPCNT), HL ; Store filenumber making load by filenumber valid. FINDSDX1: POP DE ; Method to locate an entry in the SD directory based on a filenumber or a filename. ; Input: DE = String containing a filenumber or filename. ; (TMPCNT) = 0xFFFF - DE contains a filename. ; (TMPCNT) != 0xFFFF - Filenumber. FINDSD: PUSH DE LD A,0FFH LD (DIRSECBUF),A ; Reset the sector buffer in memory indicator. LD E,0 ; Directory entry number start LD D,0 ; Directory file number (incr when a valid dirent is found). LD B,0 FINDSD1: CALL GETSDDIRENT ; Get SD Directory entry details for directory entry number stored in D. FINDSD2: BIT 7,(HL) ; Is this entry active, ie. Bit 7 of lower flag = 1? JR Z,FINDSD3 PUSH HL LD HL,(TMPCNT) ;LD H,(HL) LD A,H CP 0FFH LD A,L POP HL JR Z,FINDSD4 ; A filenumber wasnt given so will need to name match. CP D ; Match on filenumber, does the given number match with the current? JR Z,FINDSD9 INC D FINDSD3: INC E DJNZ FINDSD1 POP DE ; No match, waste pointer to input string and exit with 1. LD A,1 JP FINDSD10 ; We didnt find a match so exit with code 1. ; FINDSD4: LD (TMPADR), DE ; Save current directory position and file number. POP DE ; Get back pointer to given filename. HL contains directory filename. PUSH DE ; Save DE as the pointer will be needed on next loop. PUSH BC ; Save directory count. ; LD B,SDDIR_FNSZ INC HL INC HL ; Hop over flags. FINDSD5: LD A,(DE) OR A JR Z, FINDSD9A CP 00Dh ; If we find a terminator then this is a valid name. JR Z, FINDSD9A CP (HL) JR NZ,FINDSD8 ; Mot a match. FINDSD6: INC DE INC HL DJNZ FINDSD5 ; Loop for all the filename characters until terminator or difference found. JR FINDSD9 ; If we matched all FNSIZE characters then this is a valid name. ; No match. FINDSD8: POP BC ; Retrieve the directory count for next entry, LD DE,(TMPADR) ; Retrieve the directory position and file number. JR FINDSD3 ; Match FINDSD9A: POP BC ; Waste the directory count. LD DE,(TMPADR) ; Retrieve the directory position and file number. FINDSD9: POP BC ; Waste the pointer to the input string. XOR A ; D contains the filenumber. FINDSD10: OR A RET ; Method to erase a filCOSMIC_CRUISER1MC.256.bine in the SD RFS. This is a simple matter of resetting the valid entry flag (bit 7 of FLAG1) in the directory entry for ; the required file. ; Input: DE = String containing filenumber or filename to erase. ; Output: A = 0 Success, 1 = Fail. ERASESD: CALL FINDSDX JR NZ,LOADSD3 ; Not found? Print message and exit. ; Directory entry in cache, HL points to FLAG1 of entry. LD A,(HL) ; Clear the valid entry flag, bit 7 of FLAG1 AND 07FH LD (HL),A PUSH DE CALL WRSDDIRENT ; Flush the directory entry to disk to effect the delete. OR A JR NZ,ERASESD1 ; Failure, the report and exit. POP BC ; Get the directory entry number. LD B,0 LD DE,MSGERASEDIR XOR A ; Success. JR SDPRINTRES ERASESD1: LD A,1 LD DE,MSGERAFAIL ; Fail, print out message. SDPRINTRES: LD (RESULT),A SDPRINT: LD HL,PRINTMSG CALL BKSW2to6 ; Print out the filename. RET ; Entry point when copying the SD file. Setup flags to indicate copying to effect any special processing. ; The idea is to load the file into memory, dont execute and pass back the parameters within the CMT header. ; LOADSDCP: LD A,0FFH LD (SDAUTOEXEC),A JR LOADSD2 ; Load a program from the SD Card into RAM and/or execute it. ; ; DE points to a number or filename to load. LOADSDCARDX:LD A,0FFH JR LOADSD1 LOADSDCARD: XOR A LOADSD1: LD (SDAUTOEXEC),A XOR A ; Clear copying flag. LOADSD2: LD (SDCOPY),A LD A,0FFH ; For interbank calls, save result in a memory variable. LD (RESULT),A CALL FINDSDX JP Z,LOADSD10 LOADSD3: LD DE,MSGNOTFND JR SDPRINT ; Helper method for the CMT replacement functions3. This method is inter bank called to locate a file pointed to by DE and set the header information. LOADSDINF: CALL FINDSDX JR Z,LOADSD9 ; Same as section above difference is we want to return after the header information has been extracted. JP LOADSDXERR ; Helper method for CMT replacement functions. This method is called to load data with details already set in the CMT header and SD command buffer. LOADSDDATA: LD DE,(DTADR) ; Update the load address in case caller changed it after reading the header. LD (SDLOADADDR),DE CALL LOADSD11 LD A,0 JR C,LOADSDDAT1 JR Z,LOADSDDAT1 INC A LOADSDDAT1: LD (RESULT),A RET ; We have found the directory entry, so use it to load the program into memory. ; HL points to end of filename so requires an update to point to start of the directory entry. ; Copy all relevant information into the CMT header (for reference) and working variables. ; LOADSD9: LD A,L AND 0E0H INC A LD L,A LD A,(HL) LD (ATRB),A ; Type of file, store in the tape header memory. INC HL LD DE,NAME LD B,SDDIR_FNSZ LOADSD90: LD A,(HL) OR A JR NZ,LOADSD91 LD A,CR ; Map NULL's to CR - applications use CR. LOADSD91: LD (DE),A ; Copy the filename into the CMT area. INC HL INC DE DJNZ LOADSD90 ; LD D,(HL) INC HL LD E,(HL) ; Start sector upper 16 bits, big endian. SDCFS under RFS this should always be zero. INC HL PUSH DE LD D,(HL) INC HL LD E,(HL) ; Start sector lower 16 bits, big endian. INC HL ; EX (SP),HL EX DE,HL CALL RFSGETSECTR ; Get the directory sector, offset by drive number. DEHL is returned as the full 32bit LBA sector address. CALL SETLBAADDR ; Store the sector address into the SD command buffer. POP HL ; LD E,(HL) INC HL LD D,(HL) ; Size of file. INC HL LD (SIZE),DE LD (SDLOADSIZE),DE LD E,(HL) INC HL LD D,(HL) ; Load address. INC HL LD A,D LD (DTADRSTORE),DE ; Save the original load address, use this to fill DTADR when complete if it has changed.. CP 001H JR NC,LOADSD9A LD DE,01200H ; If the file specifies a load address below 1000H then shift to 1200H as it is not valid. LOADSD9A: LD (DTADR),DE LD (SDLOADADDR),DE LD E,(HL) INC HL LD D,(HL) ; Execution address, store in tape header memory. LD (EXADR),DE JR LOADSDX ; Exit with ok. ; LOADSD10: CALL LOADSD9 ; Modularised file find as the CMT replacement functions need it. LD DE,MSGLOAD+1 ; Skip initial CR. LD BC,NAME CALL SDPRINT ; Print out the filename. CALL LOADSD11 JR C,LOADSD14 JR NZ,LOADSDERR JR LOADSD14 ; LOADSD11: LD A,(SDLOADSIZE+1) CP 002H LD BC,SD_SECSIZE ; A full sector read if remaining bytes >=512 JR NC,LOADSD12 LD BC,(SDLOADSIZE) ; Get remaining bytes size. LOADSD12: LD HL,(SDLOADADDR) ;DI CALL SD_READ ; Read the sector. ;EI OR A RET NZ ; Failure to read a sector, abandon with error message. LD (SDLOADADDR),HL ; Save the updated address. ; LD HL,SDSTARTSEC+3 INC (HL) JR NZ,LOADSD12A DEC HL INC (HL) LOADSD12A: LD HL,(SDLOADSIZE) LD DE,SD_SECSIZE OR A SBC HL,DE RET C RET Z LD (SDLOADSIZE),HL JR LOADSD11 ; ; Execute code loaded. LOADSD14: LD A,(SDAUTOEXEC) ; Autoexecute turned off? CP 0FFh JP Z,LOADSD15 ; Go back to monitor if it has been, else execute. LD A,(ATRB) CP OBJCD JR NZ,LOADSD17 LD A,0FEH ; Indicate that the program needs executing, must occur from ROM Bank 0. JR LOADSDEXIT ; ; Load only. LOADSD15: LD DE,MSGCMTDATA ; Indicate where the program was loaded and the execution address. LD HL,(DTADR) PUSH HL LD HL,(EXADR) PUSH HL LD BC,(SIZE) LOADSD16: LD HL,PRINTMSG CALL BKSW2to6 ; Print out the filename with stack parameters. POP BC POP BC ; Remove parameters off stack. LOADSDX: XOR A ; Non error exit. LOADSDEXIT: LD (RESULT),A ; 0 = No error, 2 = Error, 0xFE = Execute code, address at EXADR RET ; Not a binary file so cannot execute. LOADSD17: LD DE,MSGNOTBIN JR LOADSDERR2 LOADSDERR: LD DE,MSGSDRERR ; Print out the filename. LD BC,(TMPCNT) LOADSDERR2: CALL SDPRINT LOADSDXERR: LD A,2 JR LOADSDEXIT ; Setup for saving an application to SD Card but using the CMT header. Also set the copy flag because the values in the header ; may not reflect where the image is stored (ie. CTM LOAD=0x0000 -> data is at 0x1200). ; SAVESDCARDX:LD A,0FFH JR SAVESD1 ; Method to save a block of memory to the SD card as a program. ; The parameters which should be given are: ; XXXXYYYYZZZZ - where XXXX = Start Address, YYYY = End Address, ZZZZ = Execution Address. ; Prompt for a filename which will be written into the CMT header. ; All the values are stored in the CMT header and copied as needed into the SD directory. ; SAVESDCARD: LD HL,GETCMTPARM ; Get the CMT parameters. CALL BKSW2to3 LD A,C OR A RET NZ ; Exit if an error occurred. ; LD A,OBJCD ; Set attribute: OBJ LD (ATRB),A ; SAVESDDATA: XOR A ; Disable the copy flag. SAVESD1: LD (SDCOPY),A LD A,0FFH ; Interbank calls, pass result via a memory variable. Assume failure unless updated. LD (RESULT),A ; Find free slot. CALL GETDIRFREE JR NC,SAVESD4 LD DE,MSGDIRFULL ; Directory is full so abort command. LD A,1 ; Directory full code. JP SDPRINTRES ; SAVESD4: PUSH DE ; Save the directory entry number. PUSH HL LD B,0 LD C,E LD DE,MSGSVDIRENT CALL SDPRINT ; Print out the filename. POP HL ; HL points to the directory entry buffer. LD A,080H LD (HL),A ; Set directory entry flag indicating that the entry is in use. INC HL LD A,(ATRB) LD (HL),A ; Copy attribute. INC HL LD BC,SDDIR_FNSZ LD DE,NAME EX DE,HL LDIR ; Copy filename across to directory. EX DE,HL POP DE ; Get directory entry number in E. ; PUSH HL LD D,0 LD BC,SDDIR_BLOCKSZ / SD_SECSIZE ; Put the blocksize as sectors into BC and multiply with E to get the block address. CALL MULT16X16 ; DEHL to contain LBA address for given block. LD BC,SDDIR_DIRSIZE / SD_SECSIZE CALL ADD3216 ; Add in size of directory to arrive at correct LBA sector. PUSH HL POP BC ; 32bit LBA now in DEBC POP HL ; Store the Big Endian LBA Sector into the directory entry. LD (HL),D ; Now save the 32bit LBA sector number into the directory entry. INC HL LD (HL),E INC HL LD A,B LD (HL),A INC HL LD A,C LD (HL),A INC HL ; PUSH BC ; Save BC and swap for HL so sector now in DEHL. EX (SP),HL CALL RFSGETSECTR ; Get the directory sector, offset by drive number. DEHL is returned as the full 32bit LBA sector address. CALL SETLBAADDR ; Store the sector address into the SD command buffer. POP HL ; LD DE,(SIZE) ; Add in the size of the program. LD (SDLOADSIZE),DE LD (HL),E INC HL LD (HL),D INC HL ; LD DE,(DTADR) ; Add in the load address of the program. LD (HL),E INC HL LD (HL),D INC HL ; LD A,(SDCOPY) OR A JR Z,SAVEDS4A LD DE,01200H ; Copy mode -> data is always at 0x1200. SAVEDS4A: LD (SDLOADADDR),DE ; LD DE,(EXADR) ; Add in the execution address of the program. LD (HL),E INC HL LD (HL),D INC HL ; ; Header has been written into the cached directory sector, write out data and if successful, write out ; the directory entry to complete. ; SAVESD5: LD A,(SDLOADSIZE+1) CP 002H LD BC,SD_SECSIZE ; A full sector read if remaining bytes >=512 JR NC,SAVESD6 LD BC,(SDLOADSIZE) ; Get remaining bytes size. SAVESD6: LD HL,(SDLOADADDR) ;DI CALL SD_WRITE ; Write the sector. ;EI OR A JR NZ,SAVESD9 ; Failure to read a sector, abandon with error message. LD (SDLOADADDR),HL ; Save the updated address. ; LD HL,SDSTARTSEC+3 INC (HL) JR NZ,SAVESD7 DEC HL INC (HL) SAVESD7: LD A,(SDLOADSIZE+1) CP 002H JR C,SAVESD8 ; If carry then the last read obtained the remaining bytes. LD HL,(SDLOADSIZE) LD DE,SD_SECSIZE OR A SBC HL,DE LD (SDLOADSIZE),HL JR SAVESD5 SAVESD8: ; Data written, now write out the directory entry. CALL WRSDDIRENT ; Flush the directory entry to disk. ;PUSH HL ;PUSH DE ;PUSH BC ;LD BC,01000H ;LD DE,0D000H ;LD HL,DUMPBC ;CALL BKSW2to3 ;POP BC ;POP DE ;POP HL OR A JR NZ,SAVESD9 LD A,0 ; Success. LD (RESULT),A RET SAVESD9: LD DE,MSGSVFAIL ; Fail, print out message. LD A,1 ; Write failed code. JP SDPRINTRES ; Print out the filename. ;------------------------------------------------------------------------------- ; END OF SD CONTROLLER FUNCTIONALITY ;------------------------------------------------------------------------------- ; RomDisk, top 8 bytes are used by the control registers when enabled so dont use the space. IF BUILD_ROMDISK = 1 ALIGN 0EFF8h ORG 0EFF8h DB 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh ENDIF IF BUILD_SFD700 = 1 ALIGN 0F000H ENDIF