added missing assembly files, extended .gitignore

This commit is contained in:
slederer 2024-09-19 01:04:17 +02:00
parent e3ff5106cf
commit 3c6c119254
10 changed files with 15811 additions and 2924 deletions

11
.gitignore vendored
View file

@ -1,4 +1,7 @@
*.s pcomp/*.s
progs/*.s
tests/*.s
examples/*.s
*.o *.o
*.exe *.exe
*.bin *.bin
@ -20,8 +23,10 @@ graph2.pas
chase.pas chase.pas
!runtime.s !runtime.s
**/tridoracpu.*/ **/tridoracpu.*/
rtl/arty-a7/mig_dram_0/_tmp rtl/arty-a7/mig_dram_0/_tmp/*
rtl/arty-a7/mig_dram_0/mig_dram_0 rtl/arty-a7/mig_dram_0/doc/*
rtl/arty-a7/mig_dram_0/mig_dram_0*
rtl/arty-a7/mig_dram_0/xil_txt.* rtl/arty-a7/mig_dram_0/xil_txt.*
rtl/arty-a7/mig_dram_0/*.veo rtl/arty-a7/mig_dram_0/*.veo
rtl/arty-a7/mig_dram_0/*.tcl rtl/arty-a7/mig_dram_0/*.tcl
rtl/arty-a7/mig_dram_0/*.xml

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:

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

9181
lib/stdlib.s Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -15,56 +15,15 @@
# run results please launch the synthesis/implementation runs as needed. # run results please launch the synthesis/implementation runs as needed.
# #
#***************************************************************************************** #*****************************************************************************************
# NOTE: In order to use this script for source control purposes, please make sure that the
# following files are added to the source control system:-
#
# 1. This project restoration tcl script (tridoracpu.tcl) that was generated.
#
# 2. The following source(s) files that were local or imported into the original project.
# (Please see the '$orig_proj_dir' and '$origin_dir' variable setting below at the start of the script)
#
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/cpuclk.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/display_clock.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/mem.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/stack.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/stackcpu.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/vgafb.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/top.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/testbench.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/rom.mem"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/ip/mig_dram_0/mig_a.prj"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/ip/mig_dram_0/mig_b.prj"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/dram_bridge.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/sdspi.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/bram_tdp.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/palette.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/irqctrl.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/fifo.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/fifo_testbench.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/sdspi_testbench.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/ip/mig_dram_0/mig_dram_0.xci"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sim_1/new/uart_tb.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/testbench_behav1.wcfg"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/fifo.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/new/fifo_testbench.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/sdspi_testbench_behav.wcfg"
#
# 3. The following remote source files that were added to the original project:-
#
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/sources_1/imports/verilog/uart.v"
# "C:/Users/sebastian/develop/fpga/tridoracpu/tridoracpu.srcs/constrs_1/imports/fpga/Arty-A7-35-Master.xdc"
#
#*****************************************************************************************
#xhub::refresh_catalog [xhub::get_xstores xilinx_board_store] # uncomment next two statements if you have never initialized the Xilinx Board Store
# this will take quite some time # this will take quite some time
#xhub::refresh_catalog [xhub::get_xstores xilinx_board_store]
#xhub::install [xhub::get_xitems] #xhub::install [xhub::get_xitems]
# Set the reference directory for source file relative paths (by default the value is script directory path) # Set the reference directory for source file relative paths
set origin_dir "C:/Users/sebastian/develop/Tridora/rtl" set origin_dir "change_this_to_your_rtl_directory"
# Oh come on, Xilinx, why?
#set xilinx_board_store_dir "../../../../AppData/Roaming/Xilinx/Vivado/2020.1/xhub/board_store/xilinx_board_store"
set xilinx_board_store_dir [get_property LOCAL_ROOT_DIR [xhub::get_xstores xilinx_board_store]] set xilinx_board_store_dir [get_property LOCAL_ROOT_DIR [xhub::get_xstores xilinx_board_store]]
set_param board.repoPaths [get_property LOCAL_ROOT_DIR [xhub::get_xstores xilinx_board_store]] set_param board.repoPaths [get_property LOCAL_ROOT_DIR [xhub::get_xstores xilinx_board_store]]