initial commit

This commit is contained in:
slederer 2024-09-19 14:12:22 +02:00
commit 60db522e87
107 changed files with 36924 additions and 0 deletions

1501
lib/corelib.s Normal file

File diff suppressed because it is too large Load diff

279
lib/coreloader.s Normal file
View file

@ -0,0 +1,279 @@
; Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
.ORG 4096
CORELOADER:
; initialize program stack and
; return stack pointers
LOADCP 24060
STOREREG FP
LOADCP 24064
STOREREG RP
LOADCP SYSBOOTTICKS
LOADCP GETTICKS
CALL
STOREI
DROP
LOADCP INITSDCARD
CALL
;LOADCP FIND_SYSPART ; no need to call, it never
;CALL ; returns, so just fall through
.EQU PART_START 0
.EQU EXTENT_SIZE 4
.EQU DIR_SIZE 8
.EQU SLOT_NO 12
.EQU SIZE_BYTES 16
.EQU PRG_START_BLK 20
.EQU FIND_FS 24
.EQU PARTENTRY_SIZE 64
.EQU DIRENTRY_SIZE 64
FIND_SYSPART:
FPADJ -FIND_FS
; load block 0
LOADC 0
LOADCP CARDREADBLK
CALL
DUP ; non-zero return code means error
CBRANCH.Z FIND_1
LOADCP PRINTHEXW
CALL
LOADCP NEWLINE
CALL
LOADC 0
JUMP
FIND_1:
DROP ; remove return code
;LOADC 512
;LOADCP CARD_BUF
;LOADCP HEXDUMP
;CALL
; address of the first partition entry
LOADCP CARD_BUF
FIND_L:
DUP ; dup addr for comparison
LOADCP SYSPART_NAME
LOADC SYSNAME_WORDS
LOADCP _CMPWORDS
CALL
CBRANCH.NZ FIND_FOUND
; go to next entry
LOADC PARTENTRY_SIZE
ADD
; check if address is still
; within the data block
DUP
LOADCP CARD_BUF,512
CMP LT
CBRANCH FIND_L
; remove address
DROP
; not found, complain and
; go back to ROM monitor
LOADCP SYSPART_ERR
LOADCP PRINTLINE
CALL
LOADC 0
JUMP
FIND_FOUND:
; address of the part entry is on stack
; check if partition is enabled
DUP ; duplicate address
LOADC 40 ; add PartFlags field offset
ADD
LOADI
LOADC 1
AND ; check bit 0 (PartEnabled)
CBRANCH.Z FIND_L ; if not set, continue loop
; address of part entry is still on stack
DUP
LOADC 44 ; add startBlock field offset
ADD
LOADI ; get start block number
STORE PART_START
; address of part entry is still on stack
DUP
LOADC 52 ; move to extentSize field
ADD
LOADI ; get value
STORE EXTENT_SIZE
; address of part entry is still on stack
LOADC 56 ; move to dirSize field
ADD
LOADI ; get value
STORE DIR_SIZE
LOADC 0
STORE SLOT_NO ; start with dirslot 0
LOAD PART_START ; start with first block of the partition
FIND_FILE:
DUP ; duplicate block number
LOADCP CARDREADBLK ; read that block
CALL
DROP ; ignore error
; scan directory entries for shell file name
LOADCP CARD_BUF
FIND_FILE_L:
DUP
LOADCP SHELL_NAME
LOADC SHELLNAME_WORDS
LOADCP _CMPWORDS ; compare names
CALL
CBRANCH.NZ FIND_F_FOUND ; exit loop if names match
; check if current dirslot no
; is below maximum number of slots
LOAD SLOT_NO
LOAD DIR_SIZE
CMP GE
CBRANCH FIND_F_NOTFOUND ; max slots reached, exit
; add 1 to SLOT_NO
LOAD SLOT_NO
INC 1
STORE SLOT_NO
; address is still on stack
LOADC DIRENTRY_SIZE
ADD ; go to next dir entry
; check if address is still
; below end of data block
DUP
LOADCP CARD_BUF,512
CMP LT
CBRANCH FIND_FILE_L ; if it is below, loop
DROP ; remove dir entry addr
; block no is still on stack
INC 1
BRANCH FIND_FILE ; read next block
FIND_F_NOTFOUND:
LOADCP SHELL_ERR
LOADCP PRINTLINE
CALL
; remove entry addr and block number
DROP
DROP
LOADC 0
JUMP
FIND_F_FOUND:
; found the file name, now check if it has the right flags
; address of dir entry is still on stack
DUP
LOADC 40 ; add flags field offset
ADD
LOADI ; load flags
LOADC 16 ; test for SlotFirst flag
AND
CBRANCH.Z FIND_FILE_L ; if not set, continue loop
;LOADCP FOUND_MSG
;LOADCP PRINTLINE
;CALL
; we got the right file, now calculate start block
; and get file size from dir entry
; address of dir entry is still on stack
; phys start block = part start + slot_no * (extent_size/512)
LOAD EXTENT_SIZE
LOADC 9
LOADCP _SHRM
CALL
LOAD SLOT_NO
LOADCP _MUL
CALL
LOAD PART_START
ADD
;DUP
;LOADCP PRINTHEXW
;CALL
;LOADC ' '
;LOADCP CONOUT
;CALL
STORE PRG_START_BLK
; address of dir entry is still on stack
LOADC 44
ADD ; add sizeBytes field offset
LOADI ; get size in bytes
;DUP
;LOADCP PRINTHEXW
;CALL
;LOADCP NEWLINE
;CALL
STORE SIZE_BYTES
; remove block number
DROP
; set argument count to 0
; in case this gets called
; by a terminating program
LOADCP PARGCOUNT
LOADC 0
STOREI
DROP
LOADC 0 ; device id is always 0
LOAD PRG_START_BLK
LOAD SIZE_BYTES
; release our stack frame
FPADJ FIND_FS
; load program
LOADCP CORELOAD
CALL
LOADC 0
JUMP
.CPOOL
.EQU SYSNAME_WORDS 4
SYSPART_NAME:
.WORD 6, 32
.BYTE "SYSTEM"
SYSPART_ERR:
.BYTE "No ""SYSTEM"" partition.",13,10,10,0
.EQU SHELLNAME_WORDS 5
SHELL_NAME:
.WORD 10,32
.BYTE "shell.prog"
SHELL_ERR:
.BYTE "No shell on ""SYSTEM"" partition.",13,10,10,0
FOUND_MSG:
.BYTE " shell.prog ",0
%include corelib.s

1053
lib/float32.s Normal file

File diff suppressed because it is too large Load diff

805
lib/rommon.s Normal file
View file

@ -0,0 +1,805 @@
.EQU CR 13
.EQU LF 10
.EQU EOT 4
.EQU ACK 6
.EQU NAK 21
.EQU STX 2
.EQU UART_REG 2048
.EQU MON_ADDR 64512
BRANCH 2 ; the very first instruction is not
; executed correctly
LOADCP 65020 ; initialise FP and RP registers
STOREREG FP
LOADCP 65024
STOREREG RP
LOADCP MON_ADDR
LOADCP 4096
STOREI
DROP
CMDLOOP0:
LOADC MESSAGE
LOADC PRINTLINE
CALL
CMDLOOP:
LOADC NEWLINE
CALL
LOADC PROMPT
CALL
CMDLOOP1:
LOADC CONIN
CALL
LOADC TOUPPER
CALL
DUP
LOADC CONOUT
CALL
LOADC 'A'
CMP.S0 EQ
CBRANCH.Z CMD1
LOADC CMD_A
CALL
BRANCH CMDLOOP2
CMD1:
LOADC 'X'
CMP.S0 EQ
CBRANCH.Z CMD2
LOADC CMD_X
CALL
BRANCH CMDLOOP2
CMD2:
LOADC 'D'
CMP.S0 EQ
CBRANCH.Z CMD3
LOADC CMD_D
CALL
BRANCH CMDLOOP2
CMD3:
LOADC 'G'
CMP.S0 EQ
CBRANCH.Z CMD4
LOADC CMD_G
CALL
BRANCH CMDLOOP2
CMD4:
LOADC 'L'
CMP.S0 EQ
CBRANCH.Z CMD5
LOADC CMD_L
CALL
BRANCH CMDLOOP2
CMD5:
LOADC 'B'
CMP.S0 EQ
CBRANCH.Z CMD6
LOADC CMD_B
CALL
BRANCH CMDLOOP2
CMD6:
DROP
BRANCH CMDLOOP0
CMDLOOP2:
DROP
BRANCH CMDLOOP
; ---- Command 'A': set current address
CMD_A:
LOADC 32
LOADC CONOUT
CALL
LOADC READHEX
CALL
CBRANCH.Z CMD_A_INVALID ; 0 if not valid input
LOADCP MON_ADDR
SWAP
STOREI
DROP ; drop STOREI address
RET
CMD_A_INVALID:
DROP
LOADC '.'
LOADC CONOUT
CALL
RET
; ---- Command 'X': examine current address
CMD_X:
FPADJ -8 ; reserve space for 4 bytes of local variables
LOADCP MON_ADDR
LOADI
STORE 0 ; current memory address
LOADC 4 ; print 8 words
STORE 4 ; Loop counter
CMD_X_LOOP:
LOADC 32 ; print a a space
LOADC CONOUT
CALL
LOAD 0 ; load word via current address
LOADI
LOADC PRINTHEXW ; print it
CALL
LOAD 0
INC 4 ; increment current address
STORE 0
LOAD 4
DEC 1
DUP
STORE 4
CBRANCH.NZ CMD_X_LOOP
LOADCP MON_ADDR
LOAD 0
STOREI
DROP
FPADJ 8
RET
; ---- Command 'D': deposit words at current address
CMD_D:
FPADJ -4
LOADC 4 ; max number of words
STORE 0
CMD_D_LOOP:
LOADC 32 ; print a space
LOADC CONOUT
CALL
LOADC READHEX
CALL
DUP
CBRANCH.Z CMD_D_EXIT ; check for invalid input
SWAP ; swap return code and value
LOADCP MON_ADDR
LOADI ; get current address
SWAP ; swap address and value for STOREI
STOREI 4 ; store the value with post-increment of address
LOADCP MON_ADDR
SWAP ; swap destination address and value for STOREI
STOREI ; store the new address
DROP
LOADC 2 ; compare return code (swapped above) to 2
CMP EQ ; check for valid input and return key
CBRANCH CMD_D_EXIT
LOAD 0
DEC 1
DUP
STORE 0
CBRANCH.NZ CMD_D_LOOP
CMD_D_EXIT:
FPADJ 4
RET
CMD_G:
DROP ; remove input char
LOADCP NEWLINE
CALL
LOADCP MON_ADDR
LOADI
JUMP
CMD_L:
LOADCP NEWLINE
CALL
LOADCP RCVBLOCKS
CALL
LOADCP NEWLINE
CALL
RET
PROMPT:
LOADC '['
LOADC CONOUT
CALL
LOADCP MON_ADDR
LOADI
LOADC PRINTHEXW
CALL
LOADC PROMPT2
LOADC PRINTLINE
CALL
RET
NEWLINE:
LOADC CR
LOADC CONOUT
CALL
LOADC LF
LOADC CONOUT
CALL
RET
; print string of byte characters
; takes pointer to string on eval stack
PRINTLINE:
DUP ; duplicate address as arg to printchar
LOADC PRINTCHAR
CALL
CBRANCH.Z PRINTLINE_EXIT ; if char is zero, exit
INC 1 ; increment address
BRANCH PRINTLINE
PRINTLINE_EXIT:
DROP ; remove address from stack
RET
; print a single character
; takes a byte pointer on eval stack
; returns character on eval stack
PRINTCHAR:
LOADI.S1.X2Y ; load word, keep address on stack
BSEL ; select byte of a word via address
DUP ; check for null byte
CBRANCH.Z PRINTCHAR_XT
DUP
LOADC CONOUT
CALL
PRINTCHAR_XT:
RET
; print a 32-bit hexadecimal number
; takes the value on the stack
PRINTHEXW:
BROT
DUP
LOADC PRINTHEXB
CALL
BROT
DUP
LOADC PRINTHEXB
CALL
BROT
DUP
LOADC PRINTHEXB
CALL
BROT
LOADC PRINTHEXB
CALL
RET
PRINTHEXB:
DUP
SHR
SHR
SHR
SHR
LOADC PRINTNIBBLE
CALL
LOADC PRINTNIBBLE
CALL
RET
PRINTNIBBLE:
LOADC 15
AND ; isolate nibble
LOADC 10
CMPU.S0 GE ; nibble >= 10 ?
CBRANCH.NZ PRINTNIBBLE_1 ; then print a-f
LOADC '0' ; else print 0-9
BRANCH PRINTNIBBLE_2
PRINTNIBBLE_1:
LOADC 55 ; 55 + 10 == 'A'
PRINTNIBBLE_2:
ADD
LOADC CONOUT
CALL
RET
; ------ read a 8-digit hexadecimal number from the console
; stores variables on the user stack, so the FP register must be
; inizialized.
; returns two values on the eval stack:
; - return code (topmost)
; 0 - no valid number
; 1 - valid number
; 2 - valid number and enter was pressed
; - result value
READHEX:
FPADJ -8
LOADC 0 ; current value
STORE 0
LOADC 8 ; max number of digits
STORE 4 ; remaining digits counter
READHEX_1:
LOADC CONIN
CALL
LOADC CR ; RETURN pressed?
CMP.S0 EQ
CBRANCH READHEX_RT
DUP
LOADC CONOUT ; echo character
CALL
LOADC CONVHEXDIGIT
CALL
LOADC -1
CMP.S0 EQ ; invalid character?
CBRANCH.NZ READHEX_XT
LOAD 0
SHL 2 ; shift previous nibble
SHL 2
OR ; combine with last digit
STORE 0
LOAD 4
DEC 1
DUP
STORE 4
CBRANCH.NZ READHEX_1
BRANCH READHEX_XT1
READHEX_RT: ; if no digits were entered, set return code
DROP ; drop read character
LOAD 4 ;remaining digits counter
LOADC 8
CMP NE
CBRANCH READHEX_RT2
LOADC 0 ; no valid input
BRANCH READHEX_XT3
READHEX_RT2:
LOADC 2 ; valid input and return pressed
BRANCH READHEX_XT3
READHEX_XT:
DROP
LOAD 4
LOADC 8
CMP EQ ; if no digits were entered
CBRANCH READHEX_XT0
READHEX_XT1:
LOADC 1 ; valid input flag
BRANCH READHEX_XT3
READHEX_XT0:
LOADC 0
READHEX_XT3:
LOAD 0
SWAP
FPADJ 8
RET
; ------ convert character on the eval stack to upper case
TOUPPER:
LOADC 'a'
CMP.S0 LT
CBRANCH TOUPPER_XT
LOADC 'z'
CMP.S0 GT
CBRANCH TOUPPER_XT
LOADC 32
SUB
TOUPPER_XT:
RET
; ------ convert hexadecimal digit to integer
; ------ takes an ascii character as parameter on the eval stack
; ------ returns an integer value from 0-15 on the eval stack,
; ------ or -1 if the character was not a valid hexadecimal digit
CONVHEXDIGIT:
LOADC TOUPPER
CALL
LOADC '0'
CMP.S0 LT ; character < '0'?
CBRANCH.NZ CONVHEXDIGIT_ERR
LOADC '9'
CMP.S0 GT ; character > '9'?
CBRANCH.NZ CONVHEXDIGIT_ISALPHA
LOADC '0' ; character is between '0' and '9', subtract '0'
SUB
BRANCH CONVHEXDIGIT_NBL
CONVHEXDIGIT_ISALPHA:
LOADC 'A'
CMP.S0 LT ; character < 'A'?
CBRANCH.NZ CONVHEXDIGIT_ERR
LOADC 'F'
CMP.S0 GT ; character > 'F'?
CBRANCH.NZ CONVHEXDIGIT_ERR
LOADC 55 ; character is between 'A' and 'F', subtract ('A' - 10)
SUB
CONVHEXDIGIT_NBL:
RET
CONVHEXDIGIT_ERR:
DROP ; remove character from stack
LOADC -1 ; error
RET
; --------- output a character on serial console
; --------- takes a character (padded to a word) on the eval stack
CONOUT:
LOADC UART_REG ; address of UART register
LOADI ; load status
LOADC 256 ; check bit 8 (tx_busy)
AND
CBRANCH.NZ CONOUT ; loop if bit 8 is not zero
; transmitter is idle now, write character
LOADC UART_REG ; address of UART register
SWAP ; swap character and address for STOREI
LOADC 1024 ; TX enable bit
OR ; OR in the character
STOREI
DROP
RET
; ---- wait until a character is received and return it on eval stack
CONIN:
LOADC WAITFORBYTE
CALL
LOADC -1 ; -1 means timeout
CMP.S0 NE
CBRANCH CONIN_XT ; exit if no timeout
DROP ; remove last result
BRANCH CONIN
CONIN_XT:
RET
.EQU L_BLOCKSIZE 32
.EQU L_WORDSIZE 4
.EQU CKSUM_PATTERN $AFFECAFE
RCVBLOCKS:
LOADCP MON_ADDR ; pointer to current write position,
LOADI ; kept on stack
RCVBLOCKS_L:
LOADC WAITFORBYTE ; read header byte
CALL
LOADC -1
CMP.S0 EQ
CBRANCH RCVBLOCKS_XT ; exit on timeout
; check for EOT -> end
LOADC EOT
CMP.S0 EQ
CBRANCH RCVBLOCKS_XT
; check for STX -> read block
LOADC STX
CMP.S0 EQ
CBRANCH RCVBLOCKS_CONT
; anything else -> send NAK
BRANCH RCVBLOCKS_RETRY
RCVBLOCKS_CONT:
DROP ; remove header byte
DUP ; duplicate pointer
LOADC READBLOCK
CALL
LOADC -1
CMP.S0 EQ ; check for timeout
CBRANCH RCVBLOCKS_XT ; exit on timeout
LOADC -2
CMP.S0 EQ ; check for checksum error
CBRANCH RCVBLOCKS_RETRY
DROP ; remove return code
LOADC L_BLOCKSIZE ; advance pointer
ADD
LOADC ACK ; send ACK
LOADC CONOUT
CALL
; next block
BRANCH RCVBLOCKS_L
RCVBLOCKS_RETRY:
DROP ; remove read byte
; send NAK
LOADC NAK
LOADC CONOUT
CALL
; next block
BRANCH RCVBLOCKS_L
RCVBLOCKS_XT:
DROP ; remove pointer
DROP ; remove read byte
RET
; ---- read a sequence of binary words and store into memory
; - arguments: pointer to memory area
READBLOCK:
FPADJ -12
STORE 0 ; buffer pointer
LOADCP L_BLOCKSIZE
STORE 4 ; remaining bytes
LOADC 0
STORE 8 ; checksum
READBLOCK_L:
LOADCP READWORD ; read a word
CALL
LOADC -1 ; check for timeout
CMP EQ
CBRANCH.NZ READBLOCK_ERR
; DUP ; debug
; LOADC PRINTHEXW
; CALL
DUP ; duplicate read word
LOAD 8 ; load checksum
ADD ; checksum = ((checksum + data) ^ pattern) << 1
LOADCP CKSUM_PATTERN
XOR
SHL
STORE 8 ; store new checkcsum
LOAD 0 ; load buffer pointer
SWAP ; swap value and pointer for STOREI
STOREI 4 ; store word and increment pointer
STORE 0 ; store pointer
LOAD 4 ; load remaining bytes
DEC L_WORDSIZE ; decrement by word size
DUP
STORE 4 ; store
CBRANCH.NZ READBLOCK_L ; loop if remaining words not zero
LOADCP READWORD ; read checksum
CALL
LOADC -1 ; check for timeout
CMP EQ
CBRANCH READBLOCK_ERR
LOAD 8 ; load checksum
CMP EQ
CBRANCH READBLOCK_OK
LOADC -2 ; return code for checksum error
BRANCH READBLOCK_XT
READBLOCK_OK:
LOADC 0 ; return 0
READBLOCK_XT:
FPADJ 12
RET
READBLOCK_ERR:
DROP ; remove result
LOAD 4 ; return number of missing bytes
FPADJ 8
RET
; --- read four bytes (msb to lsb) and return as word
; returns: word, error code (-1 for error, 0 otherwise)
READWORD:
LOADCP WAITFORBYTE
CALL
DUP
LOADC -1 ; check for error
CMP EQ
CBRANCH.NZ READWORD_ERR
; first byte is now on stack
BROT ; rotate byte left
LOADCP WAITFORBYTE
CALL
DUP
LOADC -1 ; check for error
CMP EQ
CBRANCH.NZ READWORD_ERR
; second byte is now on stack
OR ; OR last byte with this byte
BROT ; rotate bytes left
LOADCP WAITFORBYTE
CALL
DUP
LOADC -1 ; check for error
CMP EQ
CBRANCH.NZ READWORD_ERR
; third byte is now on stack
OR ; OR last byte with this byte
BROT
LOADCP WAITFORBYTE
CALL
DUP
LOADC -1 ; check for error
CMP EQ
CBRANCH.NZ READWORD_ERR
; fourth byte is now on stack
OR ; OR last byte with this byte
LOADC 0 ; error code (0: no error)
RET
READWORD_ERR:
LOADC -1 ; error code
RET
;---- wait a fixed amount of cycles for a character to be
; received on the UART.
; returns character or -1 on timeout
.EQU MAX_WAIT 20000000
WAITFORBYTE:
LOADCP MAX_WAIT ; maximum wait loops
WAITFORBYTE_L:
LOADC UART_REG ; address of UART register
LOADI ; load status
LOADC 512 ; check bit 9 (rx_avail)
AND
CBRANCH WAITFORBYTE_RX ; if bit 9 is one, a character is available
DEC 1
DUP
CBRANCH.NZ WAITFORBYTE_L
DROP ; remove wait counter from stack
LOADC -1 ; error code
RET
WAITFORBYTE_RX:
DROP ; remove wait counter from stack
LOADC UART_REG
LOADI ; read register again
LOADC 255 ; mask status bits
AND
LOADC UART_REG ; I/O address
LOADC 512 ; set bit 9 (rx_clear)
STOREI ; write register
DROP ; remove address left by STOREI
RET
.CPOOL
;---- boot from SD-card
; declare buffer addresses used by sdcardlib.s
.EQU CSD_BUF 63984
.EQU CARD_BUF 64000
CMD_B:
DROP ; remove input char
LOADCP NEWLINE
CALL
FPADJ -4
; initialize card
LOADC INITSDCARD
CALL
; read partition block
LOADC 0
LOADC CARDREADBLK
CALL
DUP ; non-zero return code means error
CBRANCH.Z CMD_B_1
CMD_B_ERR:
LOADC PRINTHEXW ; print error code
CALL
LOADC NEWLINE
CALL
LOADC 0 ; if we return, we need to
; put a fake input char back on the
; stack because the main loop will
; try to remove it
FPADJ 4
RET
CMD_B_1:
DROP ; remove error code
; check boot partition slot (boot flag)
LOADCP CARD_BUF,104 ; offset partition flags second part slot
LOADI
LOADC 2 ; PartFlags [PartBoot]
CMP EQ
CBRANCH CMD_B_C
; no boot partition
LOADC $B0
BRANCH CMD_B_ERR
CMD_B_C:
; get start block
LOADCP CARD_BUF,108 ; offset startBlock
LOADI
; get block count
LOADCP CARD_BUF,124 ; offset bootBlocks
LOADI
FPADJ -4 ; allocate space for address var
LOADCP MON_ADDR
LOADI
STORE 0 ; initialize dest addr
CMD_B_L:
; read block
OVER ; duplicate block no
LOADC CARDREADBLK
CALL
DUP ; check for error
CBRANCH.Z CMD_B_C2 ; continue if zero (no error)
NIP ; remove start and count, keep error code
NIP
BRANCH CMD_B_ERR
CMD_B_C2: DROP ; remove error code
CMD_B_2:
; copy to destination
LOAD 0 ; dest addr
LOADC COPY_BLK
CALL
; decrement count and loop
LOAD 0 ; increment dest addr
LOADC 512
ADD
STORE 0
SWAP ; swap block no/count, blockno is now ToS
INC 1
SWAP ; count is now ToS
DEC 1
DUP
CBRANCH.NZ CMD_B_L ; if not zero, loop
; jump to coreloader
DROP
DROP
FPADJ 4
LOADCP MON_ADDR
LOADI
JUMP
CMD_B_XT:
FPADJ 4
RET
; copy a sdcard block to destination address
; block size is always 512 byte, source
; is always CARD_BUF
; parameters: dest addr
COPY_BLK:
FPADJ -4
LOADC 128 ; word count
STORE 0
LOADCP CARD_BUF ; src addr
COPY_BLK1:
SWAP
; [ src addr, dest addr ]
OVER ; [ saddr, daddr, saddr ]
LOADI ; [ saddr, daddr, sword ]
STOREI 4 ; [ saddr, daddr + 4 ]
SWAP ; [ daddr + 4, saddr ]
INC 4 ; [ daddr + 4, saddr + 4]
LOAD 0 ; load and decrement counter
DEC 1
DUP
STORE 0 ; store it again
CBRANCH.NZ COPY_BLK1 ; if not zero, loop
DROP ; remove saddr and daddr
DROP
FPADJ 4
RET
.CPOOL
; wait approx. 1 millisecond
;
; 83.333 MHz Clock, three instructions a 4 cycles
; 83333 / 12 = 6944.4166
; works only if executed without wait states (i.e.
; from BRAM/SRAM)
WAIT1MSEC:
LOADCP 6944
WAIT1LOOP:
DEC 1
DUP
CBRANCH.NZ WAIT1LOOP
DROP
RET
%include "sdcardboot.s"
.CPOOL
MESSAGE:
.BYTE 13,10,"ROM Monitor v3.0.3", 13, 10,
"Set A)ddress D)eposit eX)amine L)oad G)o B)oot",13,10,0
PROMPT2:
.BYTE "]> ",0
END:

1939
lib/runtime.s Normal file

File diff suppressed because it is too large Load diff

613
lib/sdcardboot.s Normal file
View file

@ -0,0 +1,613 @@
.EQU SPIREG $880
.EQU SPI_CTRL_WRITE %100000000000000
.EQU SPI_RX_FILTER_EN %010000000000000
.EQU SPI_TXRX_EN %001000000000000
.EQU SPI_CLK_F_EN %000100000000000
.EQU SPI_CLK_DIV_WR %000010000000000
.EQU SPI_RX_RD %000001000000000
.EQU SPI_TX_WR %000000100000000
.EQU SPI_C_D %100000000000000
.EQU SPI_C_CHG %010000000000000
.EQU SPDI_C_BUSY %001000000000000
.EQU SPI_TX_RDY %000100000000000
.EQU SPI_TX_EMPTY %000010000000000
.EQU SPI_RX_AVAIL %000001000000000
.EQU SPI_RX_OVR %000000100000000
.EQU SPI_TXRX_EN_MASK ~SPI_TXRX_EN
_WAIT:
LOADC 10
_WAITL:
LOADC WAIT1MSEC
CALL
DEC 1
DUP
CBRANCH.NZ _WAITL
DROP
RET
INITSDCARD:
LOADC WAIT1MSEC
CALL
LOADC _SPIINIT1
CALL
;LOADC 'I'
;LOADCP CONOUT
;CALL
LOADC _WAITSPITXRDY
CALL
;LOADC 'W'
;LOADCP CONOUT
;CALL
; send RESET CARD command
LOADC $95 ; send cmd0 with arg 0 and checksum $95
LOADC $0
LOADC $0
LOADC SENDCMD_R1
CALL
DROP ; TODO: handle errors
;LOADCP PRINTHEXW ; print status returned by the card
;CALL
;LOADCP NEWLINE
;CALL
;LOADC '9'
;LOADCP CONOUT
;CALL
LOADC _WAITSPITXRDY
CALL
LOADC _WAIT
CALL
;LOADC '1'
;LOADCP CONOUT
;CALL
LOADC $87
LOADC $01AA
LOADC $8
LOADC SENDCMD_R7
CALL
DROP
LOADC _WAITSPITXRDY
CALL
;LOADC '2'
;LOADCP CONOUT
;CALL
;LOADCP _WAIT
;CALL
;LOADC '.'
;LOADCP CONOUT
;CALL
LOADC CARDINITV2
CALL
;LOADC '+'
;LOADCP CONOUT
;CALL
LOADC CARDFASTCLK
CALL
;LOADC '3'
;LOADCP CONOUT
;CALL
; CMD16: set block size to 512 byte
LOADC 0
LOADC 512
LOADC 16
LOADCP SENDCMD_R1
CALL
DROP
;LOADCP _WAIT
;CALL
;LOADC '4'
;LOADCP CONOUT
;CALL
RET
; read a 512-byte-block from the card
; args: block number
; returns: 0 on success
CARDREADBLK:
LOADC 128 ; number of words in a block
SWAP ; move block number up the stack
LOADCP CARD_BUF
SWAP
LOADC 0
SWAP
LOADC 17 ; CMD17: read block
LOADC SENDCMD_PKT
CALL
RET
; send the card initialization command
; wait until the card responds
CARDINITV2:
LOADC 100 ; try up to 100 times
CARD_LOOP1:
LOADC 50 ; wait 50 msec
CARD_LOOP2:
LOADC WAIT1MSEC
CALL
DEC 1
DUP
CBRANCH.NZ CARD_LOOP2
DROP ; remove loop count value
LOADC $0
LOADC $0
LOADC 58
LOADC SENDCMD_R7 ; send CMD58
CALL
DROP ; ignore result (why?)
LOADC $0
LOADCP $40000000
LOADC 41
LOADC SENDACMD_R1 ; send ACMD41
CALL
CBRANCH.Z CARD_OK ; if result is zero, the command succeded
; and the card initialization is finished
DEC 1
DUP
CBRANCH.NZ CARD_LOOP1
DROP ; remove outer loop count value
RET
CARD_OK:
DROP ; remove outer loop count value
; CMD16: set block size to 512 byte
LOADC 0
LOADC 512
LOADC 16
LOADC SENDCMD_R1
CALL
DROP ; ignore return value
RET
; set fast transfer rate
CARDFASTCLK:
LOADC SPIREG
; set clock divider to ~2,6MHz
LOADC SPI_CLK_DIV_WR+10
STOREI
DROP
RET
; perform first phase of card initialization
; which is to enable clock and wait a bit
; leaves the clock running
_SPIINIT1:
LOADC SPIREG
; set clock divider to ~325KHz
LOADC SPI_CLK_DIV_WR+64
STOREI
DROP
; clear all flags + enable clock
; /CS and MOSI are default high
LOADC SPIREG
LOADCP SPI_CTRL_WRITE,SPI_CLK_F_EN
STOREI
DROP
; we should wait at least for 74 clock cycles now
LOADC 2 ; wait 2 msec, that should be ~300 cycles
_SPIINIT1L:
LOADC WAIT1MSEC
CALL
DEC 1
DUP
CBRANCH.NZ _SPIINIT1L
DROP
LOADC SPIREG
LOADCP SPI_CTRL_WRITE ; disable clock
STOREI
DROP
LOADCP WAIT1MSEC
CALL
RET
; wait for transmission to finish
; (wait for TX_EMPTY bit)
_SPIWAITTX:
LOADC SPIREG
LOADI
LOADCP SPI_TX_EMPTY
AND
CBRANCH.Z _SPIWAITTX
RET
; finalize a command that has been sent:
; wait until the transmitter is idle
; then disable clock and set MOSI high
_SPIENDCMD:
LOADC $FF
LOADC _SENDBYTE
CALL
LOADC $FF
LOADC _SENDBYTE
CALL
;LOADC 'E'
;LOADCP CONOUT
;CALL
LOADC _SPIWAITTX
CALL
;LOADC 'w'
;LOADCP CONOUT
;CALL
LOADC _WAIT_S ; wait a short time
CALL
LOADC SPIREG
LOADCP SPI_IDLE_FLAGS ; turn off transceiver
LOADI
STOREI
DROP
; wait for a few instructions
LOADC 100
SPIEND_LP: DEC 1
DUP
CBRANCH.NZ SPIEND_LP
DROP
RET
_WAIT_S:
LOADC 100
_WAIT_S_L:
DEC 1
DUP
CBRANCH.NZ _WAIT_S_L
DROP
RET
; clear RX fifo
CLEAR_RX_FIFO:
CLEAR_RX_L1:
LOADC SPIREG
LOADI
;DUP
;LOADCP PRINTHEXW
;CALL
;LOADCP NEWLINE
;CALL
LOADC SPI_RX_AVAIL
AND
CBRANCH.Z CLEAR_RX_X
LOADC SPIREG
LOADC SPI_RX_RD
STOREI
DROP
; FIXME: it seems that this
; does not remove a byte from the fifo,
; rx_avail stays on, but only after the first
; byte has been received and read
;LOADC 'x'
;LOADCP CONOUT
;CALL
BRANCH CLEAR_RX_L1
CLEAR_RX_X:
RET
_WAITSPITXRDY:
LOADC SPIREG
LOADI
LOADCP SPI_TX_RDY
AND
CBRANCH.Z _WAITSPITXRDY
RET
; send a command and receive a data packet response
; args: packet size in words, buffer pointer
; checksum byte, 32-bit cmd arg, cmd number
; returns: 0 on success
SENDCMD_PKT:
; first send the command
LOADC SENDCMD_0
CALL
LOADC _RCVBYTE ; receive R1 response
CALL
CBRANCH.NZ SENDCMD_PKT_E ; on success we get 0
; now wait for data token
SENDCMD_PKT_L:
LOADC _RCVBYTE
CALL
LOADC $FF
CMP EQ
CBRANCH SENDCMD_PKT_L
; parameters for _RCVWORDS are on the stack now
LOADC _RCVWORDS
CALL
; receive 2 crc bytes
LOADC _RCVBYTE
CALL
BROT
LOADC _RCVBYTE
CALL
OR
; terminate command
LOADC _SPIENDCMD
CALL
DROP ; we ignore the checksum for now
LOADC 0
RET
SENDCMD_PKT_E:
DROP ; remove remaining args
DROP
LOADC -1 ; return code for error
RET
; send a command and receive a 1-byte-response (R1)
; args: checksum byte, 32-bit cmd arg, cmd number
; returns: received byte
SENDCMD_R1:
LOADC SENDCMD_0
CALL
LOADC _RCVBYTE
CALL
;LOADC 'R'
;LOADCP CONOUT
;CALL
;terminate command (/cs high, disable clock)
LOADC _SPIENDCMD
CALL
RET
; send a command
; args: checksum byte, 32-bit cmd arg, cmd number
SENDCMD_0:
; clear RX FIFO first
LOADC CLEAR_RX_FIFO
CALL
;LOADC '>'
;LOADCP CONOUT
;CALL
; cmd byte is at TOS at this point
LOADC $40 ; or in start of frame bit
OR
LOADC _SENDBYTE
CALL
; cmd arg is at TOS now
LOADC _SENDWORD
CALL
; checksum byte is at TOS now
LOADC _SENDBYTE
CALL
LOADC _XCVR_ENABLE ; enable transceiver last,
CALL ; a complete command should
RET ; fit into the tx fifo
; send ACMD and receive a 1-byte-response (R1)
; args: checksum byte, 32-bit cmd arg, ACMD number
; returns: received byte or -1 if first response byte
; indicated an error
SENDACMD_R1:
LOADC $0
LOADC $0
LOADC 55 ; send CMD55
LOADC SENDCMD_R1
CALL
LOADC 1 ; 1 = idle state, no errors
CMP NE
CBRANCH.NZ SENDACMD_ERR
; pass our args to SENDCMD_R1
LOADC SENDCMD_R1
CALL
RET
SENDACMD_ERR:
LOADCP -1
RET
; send a command and receive a 4+1-byte-response (R7)
; args: checksum byte, 32-bit cmd arg, cmd number
; returns: received word or -1 if first response byte
; indicated an error
SENDCMD_R7:
; send the command
LOADC SENDCMD_0
CALL
;LOADC '7'
;LOADCP CONOUT
;CALL
LOADC _RCVBYTE
CALL
LOADC _RCVWORD
CALL
;terminate command (/cs high, disable clock)
LOADC _SPIENDCMD
CALL
SWAP ; swap 1st response byte with received word
LOADC %011111110 ; check for any error flags
AND
CBRANCH.Z SENDCMD_R7_NOERR
DROP
LOADC -1
SENDCMD_R7_NOERR:
RET
; send a word as 4 bytes, msb first
_SENDWORD:
DUP ; remember original value for later
BROT ; rotate msb to lsb (byte 0)
LOADC 255
AND.S0 ; isolate byte, keep previous value
LOADC _SENDBYTE
CALL
BROT ; byte 1
LOADC 255
AND.S0
LOADC _SENDBYTE
CALL
BROT ; byte 2
LOADC 255
AND
LOADC _SENDBYTE
CALL
; byte 3 is already on the stack
LOADC 255
AND
LOADC _SENDBYTE
CALL
RET
; receive multiple 4-byte-words and store into
; memory buffer
; args: number of words, pointer to buffer
_RCVWORDS:
FPADJ -4
STORE 0 ; store pointer arg into local variable
; keep counter on stack
_RCVWORDS_LP:
LOAD 0 ; load buf pointer for STOREI
LOADC _RCVWORD
CALL ; receive a word
STOREI 4 ; store to buf with postincrement
STORE 0 ; store pointer variable
DEC 1 ; decrement word counter
DUP
CBRANCH.NZ _RCVWORDS_LP ; if not null, loop
DROP ; remove counter value
FPADJ 4
RET
; receive 4 bytes, return as word
_RCVWORD:
LOADC _RCVBYTE ; receive first byte
CALL
BROT ; rotate byte to left
LOADC _RCVBYTE ; receive second byte
CALL
OR ; or first and second byte together
BROT ; rotate 1st + 2nd to left
LOADC _RCVBYTE ; receive third byte
CALL
OR
BROT
LOADCP _RCVBYTE ; receive fourth byte
CALL
OR
RET
_XCVR_ENABLE:
LOADC SPIREG
LOADC SPI_TX_FLAGS
LOADI
STOREI
DROP
RET
; send a byte
; args: byte to be sent
_SENDBYTE:
LOADC SPIREG
LOADI ; load spi io register
LOADCP SPI_TX_RDY
AND ; check tx_rdy bit
CBRANCH.Z _SENDBYTE ; if not set, loop
LOADC SPI_TX_WR ; TX_WR bit
OR ; OR in byte to be send
LOADC SPIREG
SWAP ; swap value and addr for STOREI
STOREI ; store word (flags + data) to io register
DROP ; remove STOREI result
RET
; receive a byte. receiver must be enabled.
; returns: received byte
_RCVBYTE:
LOADC SPIREG
LOADI ; load spi io register
LOADC SPI_RX_AVAIL
AND.S0 ; check rx_avail bit, keep original value
CBRANCH.NZ RECVGOTIT
DROP ; rx_avail not set, remove register value and loop
BRANCH _RCVBYTE
RECVGOTIT:
LOADC SPIREG
LOADC SPI_RX_RD ; remove one byte from rx fifo
STOREI
DROP
LOADC 255
AND ; keep bits 7-0
RET
SPI_TX_FLAGS: .WORD SPI_CTRL_WRITE + SPI_TXRX_EN + SPI_RX_FILTER_EN
SPI_IDLE_FLAGS: .WORD SPI_CTRL_WRITE

795
lib/sdcardlib.s Normal file
View file

@ -0,0 +1,795 @@
; Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
.EQU SPIREG $880
.EQU SPI_CTRL_WRITE %100000000000000
.EQU SPI_RX_FILTER_EN %010000000000000
.EQU SPI_TXRX_EN %001000000000000
.EQU SPI_CLK_F_EN %000100000000000
.EQU SPI_CLK_DIV_WR %000010000000000
.EQU SPI_RX_RD %000001000000000
.EQU SPI_TX_WR %000000100000000
.EQU SPI_C_D %100000000000000
.EQU SPI_C_CHG %010000000000000
.EQU SPDI_C_BUSY %001000000000000
.EQU SPI_TX_RDY %000100000000000
.EQU SPI_TX_EMPTY %000010000000000
.EQU SPI_RX_AVAIL %000001000000000
.EQU SPI_RX_OVR %000000100000000
.EQU SPI_TXRX_EN_MASK ~SPI_TXRX_EN
_WAIT:
LOADC 10
_WAITL:
LOADCP WAIT1MSEC
CALL
DEC 1
DUP
CBRANCH.NZ _WAITL
DROP
RET
INITSDCARD:
LOADCP WAIT1MSEC
CALL
LOADCP _SPIINIT1
CALL
;LOADC 'I'
;LOADCP CONOUT
;CALL
LOADCP _WAITSPITXRDY
CALL
;LOADC 'W'
;LOADCP CONOUT
;CALL
; send RESET CARD command
LOADC $95 ; send cmd0 with arg 0 and checksum $95
LOADC $0
LOADC $0
LOADCP SENDCMD_R1
CALL
DROP ; TODO: handle errors
;LOADCP PRINTHEXW ; print status returned by the card
;CALL
;LOADCP NEWLINE
;CALL
;LOADC '9'
;LOADCP CONOUT
;CALL
LOADCP _WAITSPITXRDY
CALL
LOADCP _WAIT
CALL
;LOADC '1'
;LOADCP CONOUT
;CALL
LOADC $87
LOADC $01AA
LOADC $8
LOADCP SENDCMD_R7
CALL
DROP
LOADCP _WAITSPITXRDY
CALL
;LOADC '2'
;LOADCP CONOUT
;CALL
;LOADCP _WAIT
;CALL
;LOADC '.'
;LOADCP CONOUT
;CALL
LOADCP CARDINITV2
CALL
;LOADC '+'
;LOADCP CONOUT
;CALL
LOADCP CARDFASTCLK
CALL
;LOADC '3'
;LOADCP CONOUT
;CALL
; CMD16: set block size to 512 byte
LOADC 0
LOADC 512
LOADC 16
LOADCP SENDCMD_R1
CALL
DROP
;LOADCP _WAIT
;CALL
;LOADC '4'
;LOADCP CONOUT
;CALL
RET
; read a 512-byte-block from the card
; args: block number
; returns: 0 on success
CARDREADBLK:
LOADC 128 ; number of words in a block
SWAP ; move block number up the stack
LOADCP CARD_BUF
SWAP
LOADC 0
SWAP
LOADC 17 ; CMD17: read block
LOADCP SENDCMD_PKT
CALL
RET
; determine number of blocks
; returns: number of blocks or -1 on error
CARDSIZE:
LOADC 4
LOADCP CSD_BUF
LOADC 0
LOADC 0
LOADC 9
LOADCP SENDCMD_PKT ; send CMD9
CALL
CBRANCH.NZ CARDSIZE_ERR ; if response is zero, an error occurred
; take bytes 7, 8 and 9 from CSD data
; and add 1 to get card size in ksectors
LOADCP CSD_BUF
INC 4
LOADI
LOADC $3F ; get byte 7 (bits 22-16)
AND
BROT
BROT
LOADCP CSD_BUF ; get bytes 8 and 9 (bits 15-0)
INC 8
LOADI
BROT
BROT
LOADCP $FFFF
AND
OR
INC 1
BROT
SHL 2; multiply by 1024 to get size in sectors
RET
CARDSIZE_ERR:
LOADC -1
RET
; returns 1 if the card was changed, 0 otherwise
CARDCHANGED:
LOADCP SPIREG
LOADI
LOADCP SPI_C_CHG
AND
LOADC 0
CMPU NE
RET
; write a 512-byte-block to the card
; args: block number
; returns: 0 on success
CARDWRITEBLK:
LOADC 128 ; number of words in a block
SWAP ; move block number up the stack
LOADCP CARD_BUF
SWAP
LOADC 0
SWAP
LOADC 24 ; CMD24: write block
LOADCP SENDCMD_TXPKT
CALL
RET
; send the card initialization command
; wait until the card responds
CARDINITV2:
LOADC 100 ; try up to 100 times
CARD_LOOP1:
LOADC 50 ; wait 50 msec
CARD_LOOP2:
LOADCP WAIT1MSEC
CALL
DEC 1
DUP
CBRANCH.NZ CARD_LOOP2
DROP ; remove loop count value
LOADC $0
LOADC $0
LOADC 58
LOADCP SENDCMD_R7 ; send CMD58
CALL
DROP ; ignore result (why?)
LOADC $0
LOADCP $40000000
LOADC 41
LOADCP SENDACMD_R1 ; send ACMD41
CALL
CBRANCH.Z CARD_OK ; if result is zero, the command succeded
; and the card initialization is finished
DEC 1
DUP
CBRANCH.NZ CARD_LOOP1
DROP ; remove outer loop count value
RET
CARD_OK:
DROP ; remove outer loop count value
; CMD16: set block size to 512 byte
LOADC 0
LOADC 512
LOADC 16
LOADCP SENDCMD_R1
CALL
DROP ; ignore return value
RET
; set fast transfer rate
CARDFASTCLK:
LOADC SPIREG
; set clock divider to ~2,6MHz
LOADCP SPI_CLK_DIV_WR,10 ; using the LOADCP with offset syntax here
STOREI
DROP
RET
; perform first phase of card initialization
; which is to enable clock and wait a bit
; leaves the clock running
_SPIINIT1:
LOADC SPIREG
; set clock divider to ~325KHz
LOADCP SPI_CLK_DIV_WR,64 ; LOADCP with offset
STOREI
DROP
; clear all flags + enable clock
; /CS and MOSI are default high
LOADC SPIREG
LOADCP SPI_CTRL_WRITE,SPI_CLK_F_EN
STOREI
DROP
; we should wait at least for 74 clock cycles now
LOADC 2 ; wait 2 msec, that should be ~300 cycles
_SPIINIT1L:
LOADCP WAIT1MSEC
CALL
DEC 1
DUP
CBRANCH.NZ _SPIINIT1L
DROP
LOADC SPIREG
LOADCP SPI_CTRL_WRITE ; disable clock
STOREI
DROP
LOADCP WAIT1MSEC
CALL
RET
; wait for transmission to finish
; (wait for TX_EMPTY bit)
_SPIWAITTX:
LOADC SPIREG
LOADI
LOADCP SPI_TX_EMPTY
AND
CBRANCH.Z _SPIWAITTX
RET
; finalize a command that has been sent:
; wait until the transmitter is idle
; then disable clock and set MOSI high
_SPIENDCMD:
LOADCP $FF
LOADCP _SENDBYTE
CALL
LOADCP $FF
LOADCP _SENDBYTE
CALL
;LOADC 'E'
;LOADCP CONOUT
;CALL
LOADCP _SPIWAITTX
CALL
;LOADC 'w'
;LOADCP CONOUT
;CALL
LOADCP _WAIT_S ; wait a short time
CALL
LOADC SPIREG
LOADCP SPI_IDLE_FLAGS ; turn off transceiver
LOADI
STOREI
DROP
; wait for a few instructions
LOADC 100
SPIEND_LP: DEC 1
DUP
CBRANCH.NZ SPIEND_LP
DROP
RET
_WAIT_S:
LOADC 100
_WAIT_S_L:
DEC 1
DUP
CBRANCH.NZ _WAIT_S_L
DROP
RET
; clear RX fifo
CLEAR_RX_FIFO:
CLEAR_RX_L1:
LOADC SPIREG
LOADI
;DUP
;LOADCP PRINTHEXW
;CALL
;LOADCP NEWLINE
;CALL
LOADC SPI_RX_AVAIL
AND
CBRANCH.Z CLEAR_RX_X
LOADC SPIREG
LOADC SPI_RX_RD
STOREI
DROP
; FIXME: it seems that this
; does not remove a byte from the fifo,
; rx_avail stays on, but only after the first
; byte has been received and read
;LOADC 'x'
;LOADCP CONOUT
;CALL
BRANCH CLEAR_RX_L1
CLEAR_RX_X:
RET
_WAITSPITXRDY:
LOADC SPIREG
LOADI
LOADCP SPI_TX_RDY
AND
CBRANCH.Z _WAITSPITXRDY
RET
; send a command and receive a data packet response
; args: packet size in words, buffer pointer
; checksum byte, 32-bit cmd arg, cmd number
; returns: 0 on success
SENDCMD_PKT:
; first send the command
LOADCP SENDCMD_0
CALL
LOADCP _RCVBYTE ; receive R1 response
CALL
CBRANCH.NZ SENDCMD_PKT_E ; on success we get 0
; now wait for data token
SENDCMD_PKT_L:
LOADCP _RCVBYTE
CALL
LOADC $FF
CMP EQ
CBRANCH SENDCMD_PKT_L
; parameters for _RCVWORDS are on the stack now
LOADCP _RCVWORDS
CALL
; receive 2 crc bytes
LOADCP _RCVBYTE
CALL
BROT
LOADCP _RCVBYTE
CALL
OR
; terminate command
LOADCP _SPIENDCMD
CALL
DROP ; we ignore the checksum for now
LOADC 0
RET
SENDCMD_PKT_E:
DROP ; remove remaining args
DROP
LOADC -1 ; return code for error
RET
; send a command and send a data packet
; args: packet size in words, buffer pointer
; checksum byte, 32-bit cmd arg, cmd number
; returns: 0 on success
SENDCMD_TXPKT:
; first send the command
LOADCP SENDCMD_0
CALL
;LOADCP _RCVBYTE
;CALL
;DROP ; remove byte received during transmit
LOADCP _RCVBYTE ; receive R1 response
CALL
CBRANCH.NZ SENDCMD_TXPKT_E ; on error we get nonzero
; send stuff byte
LOADC $FF
LOADCP _SENDBYTE
CALL
; now send data token
LOADCP %11111110
LOADCP _SENDBYTE
CALL
; send data block
; parameters for _SENDWORDS are on the stack now
LOADCP _SENDWORDS
CALL
; send 2 dummy crc bytes
LOADC 0
LOADCP _SENDBYTE
CALL
LOADC 0
LOADCP _SENDBYTE
CALL
;receive data response byte
SENDCMD_TXPKT_LR:
LOADCP _RCVBYTE
CALL
LOADC $FF ; discard $FF bytes
CMP.S0 NE
CBRANCH SENDCMD_TXPKT_CT
DROP
BRANCH SENDCMD_TXPKT_LR
SENDCMD_TXPKT_CT:
LOADC $1F
AND ; isolate status bits
LOADC $5 ; command accepted bit set?
CMP NE ; if not, exit with error
CBRANCH SENDCMD_TXPKT_E2
; wait until card is busy
SENDCMD_TXPKT_LB:
LOADCP _RCVBYTE ; receive byte
CALL
CBRANCH.NZ SENDCMD_TXPKT_LB ; loop until byte is 0
; wait until card is not busy
SENDCMD_TXPKT_L2:
LOADCP _RCVBYTE ;receive byte
CALL
CBRANCH.Z SENDCMD_TXPKT_L2 ; loop if byte is 0 (i.e. MISO is held low)
LOADC 0
BRANCH SENDCMD_TXPKT_X
SENDCMD_TXPKT_E:
DROP ; remove remaining args
DROP
SENDCMD_TXPKT_E2:
LOADC -1 ; return code for error
SENDCMD_TXPKT_X:
; terminate command
LOADCP _SPIENDCMD
CALL
RET
; send a command and receive a 1-byte-response (R1)
; args: checksum byte, 32-bit cmd arg, cmd number
; returns: received byte
SENDCMD_R1:
LOADCP SENDCMD_0
CALL
LOADCP _RCVBYTE
CALL
;LOADC 'R'
;LOADCP CONOUT
;CALL
;terminate command (/cs high, disable clock)
LOADCP _SPIENDCMD
CALL
RET
; send a command
; args: checksum byte, 32-bit cmd arg, cmd number
SENDCMD_0:
; clear RX FIFO first
LOADCP CLEAR_RX_FIFO
CALL
;LOADC '>'
;LOADCP CONOUT
;CALL
; cmd byte is at TOS at this point
LOADC $40 ; or in start of frame bit
OR
LOADCP _SENDBYTE
CALL
; cmd arg is at TOS now
LOADCP _SENDWORD
CALL
; checksum byte is at TOS now
LOADCP _SENDBYTE
CALL
LOADCP _XCVR_ENABLE ; enable transceiver last,
CALL ; a complete command should
RET ; fit into the tx fifo
; send ACMD and receive a 1-byte-response (R1)
; args: checksum byte, 32-bit cmd arg, ACMD number
; returns: received byte or -1 if first response byte
; indicated an error
SENDACMD_R1:
LOADC $0
LOADC $0
LOADC 55 ; send CMD55
LOADCP SENDCMD_R1
CALL
LOADC 1 ; 1 = idle state, no errors
CMP NE
CBRANCH.NZ SENDACMD_ERR
; pass our args to SENDCMD_R1
LOADCP SENDCMD_R1
CALL
RET
SENDACMD_ERR:
LOADCP -1
RET
; send a command and receive a 4+1-byte-response (R7)
; args: checksum byte, 32-bit cmd arg, cmd number
; returns: received word or -1 if first response byte
; indicated an error
SENDCMD_R7:
; send the command
LOADCP SENDCMD_0
CALL
;LOADC '7'
;LOADCP CONOUT
;CALL
LOADCP _RCVBYTE
CALL
LOADCP _RCVWORD
CALL
;terminate command (/cs high, disable clock)
LOADCP _SPIENDCMD
CALL
SWAP ; swap 1st response byte with received word
LOADC %011111110 ; check for any error flags
AND
CBRANCH.Z SENDCMD_R7_NOERR
DROP
LOADC -1
SENDCMD_R7_NOERR:
RET
; send a word as 4 bytes, msb first
_SENDWORD:
DUP ; remember original value for later
BROT ; rotate msb to lsb (byte 0)
LOADC 255
AND.S0 ; isolate byte, keep previous value
LOADCP _SENDBYTE
CALL
BROT ; byte 1
LOADC 255
AND.S0
LOADCP _SENDBYTE
CALL
BROT ; byte 2
LOADC 255
AND
LOADCP _SENDBYTE
CALL
; byte 3 is already on the stack
LOADC 255
AND
LOADCP _SENDBYTE
CALL
RET
; send multiple 4-byte-words
; args: number of words, pointer to buffer
_SENDWORDS:
FPADJ -4
STORE 0 ; store pointer arg into local variable
; keep counter on stack
_SENDWORDS_LP:
LOAD 0 ; load buf pointer
DUP ; duplicate it
INC 4 ; increment pointer
STORE 0 ; and store it back
LOADI ; load from previously duped pointer
LOADCP _SENDWORD
CALL ; send a word
DEC 1 ; decrement word counter
DUP
CBRANCH.NZ _SENDWORDS_LP ; if not null, loop
DROP ; remove counter value
FPADJ 4
RET
; receive multiple 4-byte-words and store into
; memory buffer
; args: number of words, pointer to buffer
_RCVWORDS:
FPADJ -4
STORE 0 ; store pointer arg into local variable
; keep counter on stack
_RCVWORDS_LP:
LOAD 0 ; load buf pointer for STOREI
LOADCP _RCVWORD
CALL ; receive a word
STOREI 4 ; store to buf with postincrement
STORE 0 ; store pointer variable
DEC 1 ; decrement word counter
DUP
CBRANCH.NZ _RCVWORDS_LP ; if not null, loop
DROP ; remove counter value
FPADJ 4
RET
; receive 4 bytes, return as word
_RCVWORD:
LOADCP _RCVBYTE ; receive first byte
CALL
BROT ; rotate byte to left
LOADCP _RCVBYTE ; receive second byte
CALL
OR ; or first and second byte together
BROT ; rotate 1st + 2nd to left
LOADCP _RCVBYTE ; receive third byte
CALL
OR
BROT
LOADCP _RCVBYTE ; receive fourth byte
CALL
OR
RET
_XCVR_ENABLE:
LOADC SPIREG
LOADCP SPI_TX_FLAGS
LOADI
STOREI
DROP
RET
; send a byte
; args: byte to be sent
_SENDBYTE:
LOADC SPIREG
LOADI ; load spi io register
LOADCP SPI_TX_RDY
AND ; check tx_rdy bit
CBRANCH.Z _SENDBYTE ; if not set, loop
LOADC SPI_TX_WR ; TX_WR bit
OR ; OR in byte to be send
LOADC SPIREG
SWAP ; swap value and addr for STOREI
STOREI ; store word (flags + data) to io register
DROP ; remove STOREI result
RET
; receive a byte. receiver must be enabled.
; returns: received byte
_RCVBYTE:
LOADC SPIREG
LOADI ; load spi io register
LOADC SPI_RX_AVAIL
AND.S0 ; check rx_avail bit, keep original value
CBRANCH.NZ RECV_GOTIT
DROP ; rx_avail not set, remove register value and loop
BRANCH _RCVBYTE
RECV_GOTIT:
LOADC SPIREG
LOADC SPI_RX_RD ; remove one byte from rx fifo
STOREI
DROP
LOADC 255
AND ; keep bits 7-0
RET
SPI_TX_FLAGS: .WORD SPI_CTRL_WRITE + SPI_TXRX_EN + SPI_RX_FILTER_EN
SPI_IDLE_FLAGS: .WORD SPI_CTRL_WRITE
.CPOOL
CSD_BUF: .BLOCK 4
CARD_BUF: .BLOCK 128

285
lib/stdlib.inc Normal file
View file

@ -0,0 +1,285 @@
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
{ const pi = 3.14169253; }
const MaxInt = 2147483647;
const MaxVolumes = 32;
DefaultBufSize = 4096;
DefaultBufBlocks = 8;
DirSlotSize = 64;
const IONoError = 0;
IOFileNotFound = 1;
IOVolNotFound = 2;
IOPathInvalid = 3;
IOFileExists = 4;
IOFileClosed = 5;
IOSeekInvalid = 6;
IONoSpace = 7;
IOReadOnly = 8;
IOInvalidOp = 9;
IOInvalidFormat = 10;
IOUserIntr = 11;
IOMaxErr = 11;
const PArgMax = 7;
type IOBlock = array [0..127] of integer;
type IOBuffer = array [0..7] of IOBlock;
type filetype = (IOChannel, IODiskFile);
type filemode = (ModeReadonly, ModeCreate, ModeModify, ModeOverwrite, ModeAppend);
type file = record
mode: filemode;
lastError: integer;
errorAck: boolean;
ateoln:boolean;
case typ:filetype of
IOChannel: (channelid:integer;
bufchar:char; buflen:integer;
ateof:boolean;
noecho:boolean; (* read chars are not echoed *)
raw:boolean; (* turn off backspace processing on input, CR processing on output *)
nointr: boolean); (* turn off keyboard interrupt character processing *)
IODiskFile: (volumeid: integer;fileno: integer; filpos:integer; bufStart:integer;
size:integer; sizeExtents:integer;
bufBlocks, extentBlocks:integer;
changed: boolean;
buffer: ^IOBuffer;
bufpos: integer;
bufsize: integer;
needsflush: boolean;
);
end;
type text = file;
type fscanmode = (ScanInteger, ScanReal, ScanString);
type filenamestr = string[32];
type pathnamestr = string[68];
type volumenamestr = string[32];
type PartFlags = set of (PartEnabled, PartBoot, PartLast, PartPhysical, PartDefault);
type Partition = record
name: volumenamestr;
flags: PartFlags;
startBlock: integer;
blocks: integer;
extentSize: integer; (* size of an extent in bytes, power of two > 512 *)
dirSize: integer; (* number of directory slots *)
bootBlocks: integer;
end;
type PartitionTableBlock = array[0..7] of Partition;
type Volume = record
part: Partition;
deviceId: integer;
partitionId: integer;
startSlot: integer; (* first directory slot known to be in use *)
freeSlot: integer; (* a directory slot that is probably free *)
(* dirFile: ^file; (* pseudo-file for accessing the directory *)
dirCache: ^DirBlock;
cachedBlock: integer; (* cached volume block number in dirCache *)
cacheDirty: boolean;
openFilesCount: integer;
end;
type DirSlotFlags = set of (SlotFree, SlotReserved, SlotDeleted, SlotEndScan, SlotFirst, SlotExtent, SlotReadonly);
type Timestamp = integer;
type DirectorySlot = record
name: filenamestr; (* the name of the file *)
flags: DirSlotFlags; (* see above *)
sizeBytes: integer; (* the size of the file in bytes *)
createTime: Timestamp; (* creation time of the file *)
modTime: Timestamp; (* time of last file modification *)
generation: integer; (* increased each time a file is overwritten *)
owner: integer; (* unused *)
end;
DirBlock = array [0..7] of DirectorySlot;
type PArgVec = array[0..PArgMax] of string;
type DateTime = record
year:integer;
month: 1..12;
day: 1..31;
hours: 0..23;
minutes: 0..59;
seconds: 0..59;
end;
var input,output:file external;
var DefaultVolumeId:integer external; (* do we need this here? *)
VolumeTable: array [1..MaxVolumes] of Volume external; (* and this *)
VolumeCount: integer external; (* and this *)
DefaultVolume: volumenamestr external;
SysBootTicks, SysLastTicks:integer external;
SysClock:DateTime external;
(* from graphics.s *)
PROCEDURE DRAWLINE(x1,y1,x2,y2, color:INTEGER); EXTERNAL;
PROCEDURE PUTPIXEL(x,y, color:INTEGER); EXTERNAL;
PROCEDURE CLEARGRAPHICS; EXTERNAL;
PROCEDURE INITGRAPHICS; EXTERNAL;
PROCEDURE SETPALETTE(slot, color:INTEGER);EXTERNAL;
PROCEDURE PUTSCREEN(VAR pixeldata: ARRAY [0..31999] OF INTEGER); EXTERNAL;
function conin():char; external;
procedure conout(c:char); external;
FUNCTION CONAVAIL:BOOLEAN; EXTERNAL;
PROCEDURE WAIT1MSEC; EXTERNAL;
function upcase(aChar:char):char; external;
function getticks():integer; external;
(* from float32.s *)
function shiftfloat32(aReal:real; shiftCount:integer):real; external;
function getfloat32exp(aReal:real):integer; external;
(* from runtime.s *)
FUNCTION LENGTH(s:STRING):INTEGER; EXTERNAL;
FUNCTION MAXLENGTH(s:STRING):INTEGER; EXTERNAL;
procedure appendchar(var s:string; aChar:char); external;
procedure strmoveup(var s:string;index,length,delta:integer); external;
procedure strmovedown(var s:string;index,length,delta:integer); external;
procedure RuntimeError(var s:string); external;
(* from stdlib *)
function copy(s:string;index,count:integer):string; external;
procedure insert(ins: string; var dest: string; position:integer); external;
procedure delete(var s:string; from:integer; count:integer); external;
function pos(substr:string;var s:string):integer; external;
function pwroften(exp:integer):real; external;
function exp(exponent:real):real; external;
function ln(power:real):real; external;
function sqrt(n:real):real; external;
function floor(x:real):integer; external;
function round(x:real):integer; external;
function sin(x:real):real; external;
function cos(x:real):real; external;
function arctan(x:real):real; external;
function tan(x:real):real; external;
function cotan(x:real):real; external;
procedure fillchar(var s:string; startpos,count:integer; theChar:char); external;
procedure cardinitv2; external;
function cardsize:integer; external;
function cardchanged:boolean; external;
procedure readpartblk(blkno:integer;var partblk:PartitionTableBlock;
var error:integer;devid: integer); external;
procedure readdirblk(blkno:integer;var dirblk:DirBlock;
var error:integer;devid: integer); external;
procedure readblock(blkno:integer;var buf:IOBlock;
var error:integer; devid: integer); external;
procedure writedirblk(blkno:integer;var dirblk:DirBlock;
var error:integer;devid: integer); external;
procedure writepartblk(blkno:integer;var partblk:PartitionTableBlock;
var error:integer;devid: integer); external;
procedure writeblock(blkno:integer;var buf:IOBlock;
var error:integer; devid: integer); external;
procedure copybuf(dest:^IOBuffer;destOffset:integer; src:^IOBuffer; srcOffset:integer; length: integer); external;
function readfschar(var f:file):char; external;
procedure writefschar(var f:file; aChar:char); external;
procedure writefsstring(var f:file; var s:string); external;
procedure conoutw(w:integer); external;
function coninw():integer; external;
procedure SetDefaultVolume(volname:volumenamestr); external;
procedure addPartitions(devid:integer; var partblk:PartitionTableBlock; var isLast:boolean); external;
procedure readPartitions(devid: integer); external;
procedure initDevices; external;
procedure readdevice(deviceId:integer;blockNo:integer;var buf:IOBlock; var error:integer); external;
procedure writedevice(deviceId:integer;blockNo:integer;var buf:IOBlock; var error:integer); external;
procedure readvolumeblks(volumeid:integer; destbuf:^iobuffer; blkno:integer; blkCount: integer; var error:integer);
external;
procedure writevolumeblks(volumeid:integer; srcbuf:^iobuffer; blkno:integer; blkCount: integer; var error:integer);
external;
function findvolume(name:string):integer; external;
procedure openvolumeid(volid:integer); external;
procedure closevolumeid(volid:integer); external;
function IOResult(var fil:file):integer; external;
function ErrorStr(err:integer):string; external;
function eof(var fil:file):boolean; external;
function eoln(var fil:file):boolean; external;
procedure readfs(var fil:file; destbuf:^IOBuffer; len:integer); external;
procedure flushfile(var fil:file); external;
procedure seek(var fil:file; position:integer); external;
function filepos(var fil:file):integer; external;
function filesize(var fil:file):integer; external;
procedure extendfile(var fil:file; newSize:integer); external;
procedure writefs(var fil:file; srcbuf:^IOBuffer; len:integer); external;
procedure close(var aFile:file); external;
procedure readdirnext(volid:integer; var index:integer; var dirslot:DirectorySlot; var error:integer); external;
procedure readdirfirst(volid:integer; var index:integer; var dirslot:DirectorySlot; var error:integer); external;
function charpos(searchChar:char; var s:string):integer; external;
procedure rename(oldname:filenamestr; newname:filenamestr; var error:integer); external;
procedure erase(name:pathnamestr; var error:integer); external;
function readchannel(var f:file):char; external;
procedure writechannel(var f:file; aChar:char); external;
function freadchar(var f:file):char; external;
procedure fwritechar(aChar:char; var f:file); external;
procedure fwritestring(var aString:string; var f:file; w:integer); external;
procedure fwriteint(v:integer; var f:file; w:integer); external;
procedure fwritereal(v:real; var f:file; w,d:integer); external;
procedure pushback(var aFile:file; aChar:char); external;
procedure skipeoln(var aFile:file); external;
procedure fscanbuf(var aFile:file; mode: fscanmode; var buf:string); external;
procedure freadint(var v:integer;var f:file); external;
procedure freadreal(var v:real;var f:file); external;
procedure openchannel(name:filenamestr; var f:file; mode:filemode; var error:integer); external;
procedure open(var f:file; name:pathnamestr; mode: filemode); external;
procedure noecho(var f:file; noecho:boolean; var old:boolean); external;
procedure intstr(v:integer; fieldWith:integer; var rbuf:string);
external;
procedure realstr(x:real; w, d: integer; var s: string[30]); external;
procedure intval(s:string; var value,code:integer); external;
procedure realval(s:string; var value:real;var code:integer); external;
function isdigit(aChar:char):boolean; external;
function iswhite(aChar:char):boolean; external;
procedure halt; external;
function random:integer; external;
procedure randomize; external;
(* from stdterm.inc *)
procedure ClrScr; external;
procedure ClrEol; external;
procedure CrtInit; external;
procedure GotoXY(x,y:integer); external;
procedure InsLine; external;
procedure DelLine; external;
procedure GetCursorPos(var x,y:integer); external;
procedure GetTermSize(var maxx,maxy:integer); external;
procedure TextColor(col:integer); external;
procedure TextBackground(bgcol:integer); external;
procedure TextDefault; external;
procedure PTerm; external; (* from runtime.s *)
procedure PExec(prgfile:pathnamestr; var args:PArgVec; argCount:integer;var error:integer); external;
procedure PExec2(prgfile:pathnamestr; arg1:string; var error:integer); external;
procedure PExec3(prgfile:pathnamestr; arg1, arg2:string; var error:integer); external;
function ParamStr(i:integer):string; external;
function ParamCount():integer; external;
procedure SetShellCmd(cmd:string[40];arg:integer); external;
function GetTime:DateTime; external;
function TimeStr(d:DateTime;showSeconds:boolean):string; external;
function DateStr(d:DateTime):string; external;
function GetTimestamp(var d:DateTime):integer; external;
function GetDateTime(ts:Timestamp):DateTime; external;
procedure delay(ms:integer); external;

2710
lib/stdlib.pas Normal file

File diff suppressed because it is too large Load diff

57
lib/stdterm.inc Normal file
View file

@ -0,0 +1,57 @@
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
procedure ClrScr;
begin
write(#27, '[2J');
write(#27, '[H');
end;
procedure ClrEol;
begin
write(#27, '[K');
end;
procedure CrtInit;
begin
write(#27, 'c');
end;
procedure GotoXY(x,y:integer);
begin
write(#27,'[', y, ';', x, 'H');
end;
procedure InsLine;
begin
write(#27,'[L');
end;
procedure DelLine;
begin
write(#27,'[M');
end;
procedure GetCursorPos(var x,y:integer); external; (* from corelib.s *)
procedure GetTermSize(var maxx,maxy:integer);
var x,y:integer;
begin
GetCursorPos(x,y);
GotoXY(9999,9999);
GetCursorPos(maxx,maxy);
GotoXY(x,y);
end;
procedure TextColor(col:integer);
begin
write(#27,'[38;5;',col,'m');
end;
procedure TextBackground(bgcol:integer);
begin
write(#27,'[48;5;',bgcol,'m');
end;
procedure TextDefault;
begin
write(#27,'[0m');
end;