1507 lines
28 KiB
ArmAsm
1507 lines
28 KiB
ArmAsm
; Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details
|
|
|
|
.EQU CR 13
|
|
.EQU LF 10
|
|
.EQU UART_REG 2048
|
|
.EQU IRQC_REG 2432
|
|
|
|
.EQU PROG_START 24576
|
|
.EQU FP_START 24060
|
|
.EQU RP_START 24064
|
|
NEWLINE:
|
|
LOADC CR
|
|
LOADCP CONOUT
|
|
CALL
|
|
LOADC LF
|
|
LOADCP CONOUT
|
|
CALL
|
|
RET
|
|
|
|
; print string of byte characters
|
|
; takes pointer to string on eval stack
|
|
PRINTLINE:
|
|
DUP ; duplicate address as arg to printchar
|
|
LOADCP _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
|
|
LOADCP CONOUT
|
|
CALL
|
|
_PRINTCHAR_XT:
|
|
RET
|
|
|
|
; print a 32-bit hexadecimal number
|
|
; takes the value on the stack
|
|
PRINTHEXW:
|
|
BROT
|
|
DUP
|
|
LOADCP _PRINTHEXB
|
|
CALL
|
|
BROT
|
|
DUP
|
|
LOADCP _PRINTHEXB
|
|
CALL
|
|
BROT
|
|
DUP
|
|
LOADCP _PRINTHEXB
|
|
CALL
|
|
BROT
|
|
LOADCP _PRINTHEXB
|
|
CALL
|
|
RET
|
|
|
|
_PRINTHEXB:
|
|
DUP
|
|
SHR
|
|
SHR
|
|
SHR
|
|
SHR
|
|
LOADCP _PRINTNIBBLE
|
|
CALL
|
|
LOADCP _PRINTNIBBLE
|
|
CALL
|
|
RET
|
|
|
|
_PRINTNIBBLE:
|
|
LOADC 15 ; isolate nibble
|
|
AND
|
|
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
|
|
LOADCP CONOUT
|
|
CALL
|
|
RET
|
|
|
|
_MUL10:
|
|
SHL ; x * 2
|
|
DUP
|
|
SHL 2 ; x * 8
|
|
ADD ; x * 2 + x * 8 = x * 10
|
|
RET
|
|
|
|
; shift left multiple times
|
|
; parameters: value, count
|
|
; returns: shifted value
|
|
_SHLM:
|
|
DUP
|
|
CBRANCH.Z _SHLM_X ; if count is zero, exit
|
|
LOADC 8
|
|
CMPU.S0 LT ; count is less than 8?
|
|
CBRANCH _SHLM_1
|
|
; if >= 8, continue here
|
|
DEC 8 ; decrement counter by 8
|
|
SWAP ; swap counter and value
|
|
BROT ; byte rotate value
|
|
LOADCP $FFFFFF00 ; and mask the lowest 8 bits
|
|
AND ; to get a shift left by 8
|
|
SWAP ; swap back counter and value
|
|
BRANCH _SHLM
|
|
_SHLM_1:
|
|
DEC 1
|
|
SWAP
|
|
SHL
|
|
SWAP
|
|
BRANCH _SHLM
|
|
_SHLM_X:
|
|
DROP ; drop counter
|
|
RET
|
|
|
|
; shift right multiple times
|
|
; parameters: value, count
|
|
; returns: shifted value
|
|
_SHRM:
|
|
DUP
|
|
CBRANCH.Z _SHRM_X ; if count is zero, exit
|
|
DEC 1
|
|
SWAP
|
|
SHR
|
|
SWAP
|
|
BRANCH _SHRM
|
|
_SHRM_X:
|
|
DROP ; remove counter
|
|
RET
|
|
|
|
|
|
; --- print signed integer as decimal
|
|
; parameter: value
|
|
PRINTDEC:
|
|
DUP
|
|
LOADC 0
|
|
CMP GE
|
|
CBRANCH PRINTDECU
|
|
LOADC '-'
|
|
LOADCP CONOUT
|
|
CALL
|
|
NOT
|
|
INC 1 ; negate value
|
|
; fallthrough to PRINTDECU
|
|
|
|
; print unsigned integer as decimal
|
|
; parameter: value
|
|
; local vars:
|
|
; 0: current value
|
|
; 4: pointer to digit value table
|
|
; 8: digit counter
|
|
; 12: flag for leading zeroes
|
|
PRINTDECU:
|
|
FPADJ -16
|
|
STORE 0
|
|
LOADCP CONVDEC_TAB
|
|
STORE 4
|
|
LOADC 0
|
|
STORE 12
|
|
PRDEC_NEXTDIGIT:
|
|
LOADC 0
|
|
STORE 8
|
|
LOAD 4
|
|
LOADI ; load via pointer
|
|
CBRANCH.Z PRDECU_DONE ; if digit value is 0, we are done
|
|
DIGIT_LOOP:
|
|
LOAD 4
|
|
LOADI
|
|
LOAD 0
|
|
CMPU GT
|
|
CBRANCH DIGIT_DONE
|
|
LOAD 0
|
|
LOAD 4
|
|
LOADI
|
|
SUB
|
|
STORE 0
|
|
LOAD 8
|
|
INC 1
|
|
STORE 8
|
|
BRANCH DIGIT_LOOP
|
|
DIGIT_DONE:
|
|
LOAD 8
|
|
CBRANCH.NZ DIGIT_OUT ; if digit is not 0, print it
|
|
LOAD 12 ; is it a leading zero, ignore it
|
|
CBRANCH.Z DIGIT_NEXT
|
|
DIGIT_OUT:
|
|
LOADC 1 ; set leading zero flag
|
|
STORE 12
|
|
LOAD 8
|
|
LOADCP _PRINTDIGIT
|
|
CALL
|
|
DIGIT_NEXT:
|
|
LOAD 4 ; increment digit value pointer
|
|
INC 4
|
|
STORE 4
|
|
BRANCH PRDEC_NEXTDIGIT
|
|
PRDECU_DONE:
|
|
LOAD 0
|
|
LOADCP _PRINTDIGIT
|
|
CALL
|
|
FPADJ 16
|
|
RET
|
|
|
|
_PRINTDIGIT:
|
|
LOADC '0'
|
|
ADD
|
|
LOADCP CONOUT
|
|
CALL
|
|
RET
|
|
CONVDEC_TAB:
|
|
.WORD 1000000000,100000000,10000000,1000000,100000,10000,1000,100,10,0
|
|
|
|
; ------ 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:
|
|
LOADCP CONIN
|
|
CALL
|
|
LOADC CR ; RETURN pressed?
|
|
CMP.S0 EQ
|
|
CBRANCH _READHEX_RT
|
|
DUP
|
|
LOADCP CONOUT ; echo character
|
|
CALL
|
|
LOADCP _CONVHEXDIGIT
|
|
CALL
|
|
LOADC -1
|
|
CMP.S0 EQ ; invalid character?
|
|
CBRANCH.NZ _READHEX_XT
|
|
LOAD 0
|
|
SHL 1 ; shift previous nibble
|
|
SHL 1
|
|
SHL 1
|
|
SHL 1
|
|
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
|
|
UPCASE:
|
|
LOADC 'a'
|
|
CMP.S0 LT
|
|
CBRANCH UPCASE_XT
|
|
LOADC 'z'
|
|
CMP.S0 GT
|
|
CBRANCH UPCASE_XT
|
|
LOADC 32
|
|
SUB
|
|
UPCASE_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:
|
|
LOADCP UPCASE
|
|
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 (as the lsb of 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 1024 ; TX enable bit
|
|
OR
|
|
LOADC UART_REG ; I/O address
|
|
SWAP ; swap addr and value as args for STOREI
|
|
STOREI
|
|
DROP
|
|
RET
|
|
|
|
; ---------- check if a character has been received
|
|
; returns: 1 if a character has been received, 0 otherwise
|
|
CONAVAIL:
|
|
LOADC 0 ; preliminary result
|
|
LOADC UART_REG ; address of UART register
|
|
LOADI ; load status
|
|
LOADC 512 ; check bit 9 (rx_avail)
|
|
AND
|
|
CBRANCH.Z CONAVAIL_0 ; if bit is zero, we are done
|
|
INC 1 ; add 1 to preliminary result
|
|
CONAVAIL_0:
|
|
RET
|
|
|
|
; ---------- wait until a character is received and return it on eval stack
|
|
CONIN:
|
|
LOADC UART_REG ; address of UART register
|
|
LOADI ; load status
|
|
LOADC 512 ; check bit 9 (rx_avail)
|
|
AND
|
|
CBRANCH.Z CONIN ; loop if bit 9 is zero
|
|
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
|
|
RET
|
|
|
|
; return absolute value
|
|
; parameters: value
|
|
; returns: abs(value)
|
|
ABS:
|
|
LOADC 0
|
|
CMP.S0 GE
|
|
CBRANCH ABS_XT
|
|
DEC 1 ; negate
|
|
NOT
|
|
ABS_XT:
|
|
RET
|
|
|
|
; signed multiplication
|
|
_MUL:
|
|
; fallthrough to MULU
|
|
|
|
; unsigned multiplication: x * y
|
|
; parameters: [x, y]
|
|
; returns: x * y
|
|
_MULU:
|
|
FPADJ -16
|
|
STORE 0 ; x
|
|
STORE 4 ; y
|
|
LOADC 32
|
|
STORE 8 ; bit count
|
|
LOADC 0
|
|
STORE 12 ; result
|
|
MULU_LOOP:
|
|
LOAD 8
|
|
CBRANCH.Z MULU_XT ; if count is zero, exit
|
|
LOAD 0
|
|
LOADC 1
|
|
AND.S0 ; get bit 0
|
|
CBRANCH.Z MULU_1 ; if bit 0 is zero, next binary digit
|
|
LOAD 12
|
|
LOAD 4
|
|
ADD ; result = result + y
|
|
STORE 12
|
|
MULU_1:
|
|
SHR ; x = x >> 1
|
|
STORE 0
|
|
LOAD 4
|
|
SHL ; y = y << 1
|
|
STORE 4
|
|
LOAD 8
|
|
DEC 1 ; count = count -1
|
|
STORE 8
|
|
BRANCH MULU_LOOP
|
|
MULU_XT:
|
|
LOAD 12
|
|
FPADJ 16
|
|
RET
|
|
|
|
; signed integer division
|
|
; parameters: [x,y]
|
|
; result: x/y
|
|
_DIV:
|
|
FPADJ -4
|
|
LOADC 0
|
|
STORE 0 ; clear negate flag
|
|
DUP
|
|
LOADC 0
|
|
CMP GE ; is y positive?
|
|
CBRANCH DIV_ISPOS
|
|
; y is negative
|
|
NOT
|
|
INC 1 ; negate y
|
|
LOADC -1
|
|
STORE 0 ; set negate flag
|
|
DIV_ISPOS:
|
|
SWAP ; swap x and y
|
|
DUP
|
|
LOADC 0
|
|
CMP GE ; is x positive?
|
|
CBRANCH DIV_ISPOS2
|
|
; x is negative
|
|
NOT
|
|
INC 1 ; negate x
|
|
LOAD 0
|
|
NOT ; invert negate flag
|
|
STORE 0
|
|
DIV_ISPOS2:
|
|
SWAP ; swap back y and x
|
|
LOADCP _DIVMODU
|
|
CALL
|
|
DROP ; throw away remainder
|
|
LOAD 0
|
|
CBRANCH.Z DIV_XT ; negate flag set?
|
|
NOT
|
|
INC 1 ; negate value
|
|
DIV_XT:
|
|
FPADJ 4
|
|
RET
|
|
|
|
; signed integer modulo
|
|
; the result is negative if x is negative.
|
|
; the sign of y is ignored.
|
|
; parameters: [x,y]
|
|
; returns: remainder of x/y
|
|
_MOD:
|
|
FPADJ -4
|
|
LOADC 0
|
|
STORE 0 ; clear negate flag
|
|
DUP
|
|
LOADC 0
|
|
CMP GE ; is y positive?
|
|
CBRANCH MOD_ISPOS
|
|
; y is negative
|
|
NOT
|
|
INC 1 ; negate y
|
|
MOD_ISPOS:
|
|
SWAP ; swap x and y
|
|
DUP
|
|
LOADC 0
|
|
CMP GE ; is x positive?
|
|
CBRANCH MOD_ISPOS2
|
|
; x is negative
|
|
NOT
|
|
INC 1 ; negate x
|
|
LOAD 0
|
|
NOT ; invert negate flag
|
|
STORE 0
|
|
MOD_ISPOS2:
|
|
SWAP ; swap back y and x
|
|
LOADCP _DIVMODU
|
|
CALL
|
|
NIP ; throw away quotient
|
|
LOAD 0
|
|
CBRANCH.Z MOD_XT ; negate flag set?
|
|
NOT
|
|
INC 1 ; negate value
|
|
MOD_XT:
|
|
|
|
FPADJ 4
|
|
RET
|
|
|
|
; unsigned integer division
|
|
; parameters: [x,y]
|
|
; result: x/y
|
|
_DIVU:
|
|
LOADCP _DIVMODU ; just call DIVMODU and throw away the remainder
|
|
CALL
|
|
DROP
|
|
RET
|
|
|
|
; unsigned integer division with remainder
|
|
; parameters: [x,y]
|
|
; result: [ quotient, remainder ]
|
|
_DIVMODU:
|
|
FPADJ -20
|
|
STORE 0 ; y
|
|
STORE 4 ; x
|
|
LOADC 32
|
|
STORE 8 ; bit count
|
|
LOADC 0
|
|
STORE 12 ; tmp value
|
|
LOADC 0
|
|
STORE 16 ; result
|
|
DIVU_LOOP:
|
|
LOAD 8
|
|
CBRANCH.Z DIVU_END ; if count is zero, exit
|
|
LOAD 16
|
|
SHL ; result = result << 1
|
|
STORE 16
|
|
LOAD 12
|
|
SHL ; tmp << 1
|
|
LOAD 4
|
|
LOADCP $80000000 ; msb of x
|
|
AND
|
|
CBRANCH.Z DIVU_1
|
|
INC 1 ; tmp[0] is 0, so +1 means tmp[0] = 1
|
|
DIVU_1:
|
|
DUP
|
|
STORE 12 ; tmp = tmp << 1 | msb
|
|
LOAD 0
|
|
CMPU GE ; tmp >= y?
|
|
CBRANCH.Z DIVU_2
|
|
LOAD 16
|
|
INC 1 ; result = result | 1
|
|
STORE 16
|
|
LOAD 12
|
|
LOAD 0
|
|
SUB
|
|
STORE 12 ; tmp = tmp - y
|
|
DIVU_2:
|
|
LOAD 4
|
|
SHL
|
|
STORE 4
|
|
LOAD 8
|
|
DEC 1
|
|
STORE 8
|
|
BRANCH DIVU_LOOP
|
|
DIVU_END:
|
|
LOAD 16 ; result (quotient)
|
|
LOAD 12 ; remainder
|
|
FPADJ 20
|
|
RET
|
|
|
|
.CPOOL
|
|
|
|
; wait approx. 1 millisecond
|
|
;
|
|
; the ROM at address 4
|
|
; contains the cpu clock freq in KHz
|
|
.EQU CLK_KHZ_ADDR 4
|
|
WAIT1MSEC:
|
|
LOADC CLK_KHZ_ADDR
|
|
LOADI
|
|
; divide by 16
|
|
SHR
|
|
SHR
|
|
SHR
|
|
SHR
|
|
WAIT1LOOP:
|
|
INC 0 ; NOP to make the loop 16 cycles long
|
|
DEC 1
|
|
DUP
|
|
CBRANCH.NZ WAIT1LOOP
|
|
DROP
|
|
RET
|
|
|
|
; clear a memory block
|
|
; parameters: addr, length in bytes
|
|
; length must be multiple of wordsize.
|
|
; if it is not, the last (partial) word is not cleared.
|
|
_CLEARMEM:
|
|
SHR
|
|
SHR ; calculate length in words
|
|
|
|
CLEARMEM_L:
|
|
DUP
|
|
CBRANCH.Z CLEARMEM_X ; if zero words to do, exit
|
|
SWAP ; swap counter and addr
|
|
LOADC 0
|
|
STOREI 4 ; store with post-increment
|
|
SWAP ; swap counter and addr back
|
|
DEC 1 ; decrement counter
|
|
BRANCH CLEARMEM_L
|
|
CLEARMEM_X:
|
|
DROP
|
|
DROP
|
|
RET
|
|
|
|
; copy a number of words from source to destination
|
|
; parameters: [ dest, source, count ]
|
|
; source and destination may not overlap
|
|
.EQU COPYWORDS_COUNT 0
|
|
.EQU COPYWORDS_SRC 4
|
|
.EQU COPYWORDS_DEST 8
|
|
_COPYWORDS:
|
|
FPADJ -12
|
|
STORE COPYWORDS_COUNT ; store args to local vars
|
|
STORE COPYWORDS_SRC
|
|
STORE COPYWORDS_DEST
|
|
LOAD COPYWORDS_COUNT ; we will keep count on the stack inside the loop
|
|
COPYWORDS_L0:
|
|
DUP ; count is on tos, duplicate it
|
|
CBRANCH.Z COPYWORDS_XT ; check if count is zero
|
|
DEC 1 ; if not, decrement
|
|
LOAD COPYWORDS_DEST ; load dest addr for STOREI below
|
|
LOAD COPYWORDS_SRC ; load src addr
|
|
INC.S1.X2Y 4 ; increment by 4 and put as new value on tos
|
|
STORE COPYWORDS_SRC ; store again, old addr is now on tos
|
|
LOADI ; load value by old addr
|
|
STOREI 4 ; store value and post-increment
|
|
STORE COPYWORDS_DEST ; store post-incremented addr
|
|
BRANCH COPYWORDS_L0
|
|
COPYWORDS_XT:
|
|
DROP ; drop count value
|
|
FPADJ 12
|
|
RET
|
|
|
|
; compare a number of words
|
|
; parameters: [ dest, source, count ]
|
|
; returns: 1 if all words are equal, 0 otherwise
|
|
.EQU CMPWORDS_COUNT 0
|
|
.EQU CMPWORDS_SRC 4
|
|
.EQU CMPWORDS_DEST 8
|
|
_CMPWORDS:
|
|
FPADJ -12
|
|
STORE CMPWORDS_COUNT ; store args to local vars
|
|
STORE CMPWORDS_SRC
|
|
STORE CMPWORDS_DEST
|
|
LOAD CMPWORDS_COUNT
|
|
CMPWORDS_L0:
|
|
DUP ; count is on tos, duplicate it
|
|
CBRANCH.Z CMPWORDS_XT ; check if count is zero
|
|
DEC 1 ; if not, decrement
|
|
LOAD CMPWORDS_SRC ; load src addr
|
|
INC.S1.X2Y 4 ; increment by 4 and put as new value on tos
|
|
STORE CMPWORDS_SRC ; store again, old addr is now on tos
|
|
LOADI ; load src value
|
|
LOAD CMPWORDS_DEST ; load dest addr
|
|
INC.S1.X2Y 4 ; increment by 4 and put as new value on tos
|
|
STORE CMPWORDS_DEST ; store again, old addr is now on tos
|
|
LOADI ; load dest value
|
|
CMPU EQ
|
|
CBRANCH CMPWORDS_L0 ; if words are equal, continue loop
|
|
DROP ; drop count value
|
|
LOADC 0 ; load exit code 0
|
|
BRANCH CMPWORDS_XT2
|
|
CMPWORDS_XT:
|
|
DROP ; drop count value
|
|
LOADC 1 ; load exit code 1
|
|
CMPWORDS_XT2:
|
|
FPADJ 12
|
|
RET
|
|
|
|
.CPOOL
|
|
; --------- Graphics Library ---------------
|
|
; vga controller registers
|
|
.EQU FB_RA $900
|
|
.EQU FB_WA $901
|
|
.EQU FB_IO $902
|
|
.EQU FB_PS $903
|
|
.EQU FB_PD $904
|
|
.EQU FB_CTL $905
|
|
; set a pixel in fb memory
|
|
; parameters: x,y - coordinates
|
|
PUTPIXEL_1BPP:
|
|
; calculate vmem address:
|
|
OVER ; duplicate x
|
|
; divide x by 32
|
|
SHR
|
|
SHR
|
|
SHR
|
|
SHR
|
|
SHR
|
|
SWAP
|
|
; multiply y by words per line
|
|
SHL 2
|
|
SHL 2
|
|
SHL
|
|
|
|
ADD ; add results together for vmem addr
|
|
|
|
DUP
|
|
LOADCP FB_WA
|
|
SWAP
|
|
STOREI ; store to framebuffer write addr register
|
|
DROP
|
|
LOADCP FB_RA ; and to framebuffer read addr register
|
|
SWAP
|
|
STOREI
|
|
DROP
|
|
|
|
; x is now at top of stack
|
|
; get bit value from x modulo 32
|
|
LOADC 31
|
|
AND
|
|
SHL 2 ; (x & 31) * 4 = offset into table
|
|
LOADCP INT_TO_PIX_TABLE
|
|
ADD
|
|
LOADI
|
|
|
|
LOADCP FB_IO
|
|
; read old vmem value
|
|
LOADCP FB_IO
|
|
LOADI
|
|
; or in new bit
|
|
OR
|
|
; write new value
|
|
STOREI
|
|
DROP
|
|
|
|
RET
|
|
|
|
INT_TO_PIX_TABLE:
|
|
.WORD %10000000_00000000_00000000_00000000
|
|
.WORD %01000000_00000000_00000000_00000000
|
|
.WORD %00100000_00000000_00000000_00000000
|
|
.WORD %00010000_00000000_00000000_00000000
|
|
.WORD %00001000_00000000_00000000_00000000
|
|
.WORD %00000100_00000000_00000000_00000000
|
|
.WORD %00000010_00000000_00000000_00000000
|
|
.WORD %00000001_00000000_00000000_00000000
|
|
.WORD %00000000_10000000_00000000_00000000
|
|
.WORD %00000000_01000000_00000000_00000000
|
|
.WORD %00000000_00100000_00000000_00000000
|
|
.WORD %00000000_00010000_00000000_00000000
|
|
.WORD %00000000_00001000_00000000_00000000
|
|
.WORD %00000000_00000100_00000000_00000000
|
|
.WORD %00000000_00000010_00000000_00000000
|
|
.WORD %00000000_00000001_00000000_00000000
|
|
.WORD %00000000_00000000_10000000_00000000
|
|
.WORD %00000000_00000000_01000000_00000000
|
|
.WORD %00000000_00000000_00100000_00000000
|
|
.WORD %00000000_00000000_00010000_00000000
|
|
.WORD %00000000_00000000_00001000_00000000
|
|
.WORD %00000000_00000000_00000100_00000000
|
|
.WORD %00000000_00000000_00000010_00000000
|
|
.WORD %00000000_00000000_00000001_00000000
|
|
.WORD %00000000_00000000_00000000_10000000
|
|
.WORD %00000000_00000000_00000000_01000000
|
|
.WORD %00000000_00000000_00000000_00100000
|
|
.WORD %00000000_00000000_00000000_00010000
|
|
.WORD %00000000_00000000_00000000_00001000
|
|
.WORD %00000000_00000000_00000000_00000100
|
|
.WORD %00000000_00000000_00000000_00000010
|
|
.WORD %00000000_00000000_00000000_00000001
|
|
|
|
PUTMPIXEL:
|
|
LOADC 1
|
|
; set a pixel in fb memory
|
|
; parameters: x,y,color - coordinates, color value (0-15)
|
|
PUTPIXEL:
|
|
PUTPIXEL_4BPP:
|
|
.EQU PUTPIXEL_X 0
|
|
.EQU PUTPIXEL_Y 4
|
|
.EQU PUTPIXEL_COLOR 8
|
|
.EQU PUTPIXEL_PIXPOS 12
|
|
.EQU PUTPIXEL_FS 16
|
|
|
|
FPADJ -PUTPIXEL_FS
|
|
|
|
STORE PUTPIXEL_COLOR
|
|
STORE PUTPIXEL_Y
|
|
STORE PUTPIXEL_X
|
|
|
|
|
|
; calculate vmem address: (x / 8) + (y * 80)
|
|
LOAD PUTPIXEL_X
|
|
; divide x by 8
|
|
SHR
|
|
SHR
|
|
SHR
|
|
|
|
LOAD PUTPIXEL_Y
|
|
; multiply y by words per line
|
|
SHL 2
|
|
SHL 2 ; * 16
|
|
DUP
|
|
SHL 2; * 64
|
|
ADD ; x*16 + x*64
|
|
|
|
ADD ; add results together for vmem addr
|
|
|
|
LOADCP FB_WA
|
|
OVER
|
|
STOREI ; store to framebuffer write addr register
|
|
DROP
|
|
LOADCP FB_RA ; and to framebuffer read addr register
|
|
SWAP ; swap addr and value for STOREI
|
|
STOREI
|
|
DROP
|
|
|
|
LOAD PUTPIXEL_X
|
|
; |0000.0000|0000.0000|0000.0000|0000.1111|
|
|
LOADC 7
|
|
AND ; calculate pixel position in word
|
|
LOADC 7
|
|
SWAP
|
|
SUB ; pixpos = 7 - (x & 7)
|
|
STORE PUTPIXEL_PIXPOS
|
|
|
|
LOAD PUTPIXEL_COLOR
|
|
LOAD PUTPIXEL_PIXPOS
|
|
SHR ; rcount = pixpos / 2
|
|
ROTLOOP_:
|
|
DUP ; exit loop if rcount is 0
|
|
CBRANCH.Z ROTLOOP_END
|
|
SWAP ; pixel value is now on top of stack
|
|
BROT ; value = value << 8
|
|
SWAP ; rcount is now on top of stack
|
|
DEC 1 ; rcount = rcount - 1
|
|
BRANCH ROTLOOP_
|
|
ROTLOOP_END:
|
|
DROP ; drop rcount
|
|
; shifted pixel value is now at top of stack
|
|
LOAD PUTPIXEL_PIXPOS
|
|
LOADC 1
|
|
AND
|
|
CBRANCH.Z EVEN_PIXPOS
|
|
SHL 2 ; if pixpos is odd, shift by 4 bits
|
|
SHL 2
|
|
EVEN_PIXPOS:
|
|
LOAD PUTPIXEL_X
|
|
; get bit value from x modulo 8
|
|
LOADC 7
|
|
AND
|
|
SHL 2 ; (x & 7) * 4 = offset into table
|
|
LOADCP INT_TO_MASK_TABLE
|
|
ADD
|
|
LOADI
|
|
|
|
; read old vmem value
|
|
LOADCP FB_IO
|
|
LOADI
|
|
; mask bits
|
|
AND
|
|
; or in shifted pixel value
|
|
OR
|
|
|
|
; write new value
|
|
LOADCP FB_IO
|
|
SWAP
|
|
STOREI
|
|
DROP
|
|
|
|
FPADJ PUTPIXEL_FS
|
|
RET
|
|
|
|
.CPOOL
|
|
|
|
INT_TO_MASK_TABLE:
|
|
.WORD %00001111_11111111_11111111_11111111
|
|
.WORD %11110000_11111111_11111111_11111111
|
|
.WORD %11111111_00001111_11111111_11111111
|
|
.WORD %11111111_11110000_11111111_11111111
|
|
.WORD %11111111_11111111_00001111_11111111
|
|
.WORD %11111111_11111111_11110000_11111111
|
|
.WORD %11111111_11111111_11111111_00001111
|
|
.WORD %11111111_11111111_11111111_11110000
|
|
|
|
; draw a line between two points
|
|
; parameters: x0, y0, x1, y1, color
|
|
.EQU DL_X0 0
|
|
.EQU DL_Y0 4
|
|
.EQU DL_X1 8
|
|
.EQU DL_Y1 12
|
|
.EQU DL_DX 16
|
|
.EQU DL_DY 20
|
|
.EQU DL_ERR 24
|
|
.EQU DL_E2 28
|
|
.EQU DL_SX 32
|
|
.EQU DL_SY 36
|
|
.EQU DL_COL 40
|
|
.EQU STACKFRAME_SIZE 44
|
|
|
|
DRAWLINE_M:
|
|
LOADC 1
|
|
DRAWLINE:
|
|
FPADJ -STACKFRAME_SIZE
|
|
|
|
STORE DL_COL ; store args
|
|
STORE DL_Y1
|
|
STORE DL_X1
|
|
STORE DL_Y0
|
|
STORE DL_X0
|
|
|
|
LOAD DL_X1 ; dx = abs(x1-x0)
|
|
LOAD DL_X0
|
|
SUB
|
|
LOADCP ABS
|
|
CALL
|
|
STORE DL_DX
|
|
|
|
LOAD DL_Y1 ; dy = -abs(y1-y0)
|
|
LOAD DL_Y0
|
|
SUB
|
|
LOADCP ABS
|
|
CALL
|
|
DEC 1
|
|
NOT
|
|
STORE DL_DY
|
|
|
|
LOAD DL_X0 ; sx = (x0<x1) ? 1 : -1
|
|
LOAD DL_X1
|
|
CMP LT
|
|
CBRANCH DL_SX0
|
|
LOADC -1
|
|
BRANCH DL_SX1
|
|
DL_SX0: LOADC 1
|
|
DL_SX1: STORE DL_SX
|
|
|
|
LOAD DL_Y0 ; sy = (y0<y1) ? 1 : -1
|
|
LOAD DL_Y1
|
|
CMP LT
|
|
CBRANCH DL_SY0
|
|
LOADC -1
|
|
BRANCH DL_SY1
|
|
DL_SY0: LOADC 1
|
|
DL_SY1: STORE DL_SY
|
|
|
|
LOAD DL_DX ; err = dx + dy
|
|
LOAD DL_DY
|
|
ADD
|
|
STORE DL_ERR
|
|
|
|
DL_LOOP:
|
|
LOAD DL_X0
|
|
LOAD DL_Y0
|
|
LOAD DL_COL
|
|
LOADCP PUTPIXEL
|
|
CALL
|
|
|
|
LOAD DL_X0 ; if x0 == x1 and y0 == y1, exit
|
|
LOAD DL_X1
|
|
CMP NE
|
|
CBRANCH DL_CONT
|
|
|
|
LOAD DL_Y0
|
|
LOAD DL_Y1
|
|
CMP EQ
|
|
CBRANCH DL_END
|
|
|
|
DL_CONT:
|
|
LOAD DL_ERR ; e2 = 2 * err
|
|
SHL
|
|
STORE DL_E2
|
|
|
|
LOAD DL_E2 ; if e2 > dy
|
|
LOAD DL_DY
|
|
CMP GT
|
|
CBRANCH.Z DL_SKIP1
|
|
|
|
LOAD DL_ERR ; err += dy
|
|
LOAD DL_DY
|
|
ADD
|
|
STORE DL_ERR
|
|
|
|
LOAD DL_X0 ; x0 += sx
|
|
LOAD DL_SX
|
|
ADD
|
|
STORE DL_X0
|
|
|
|
DL_SKIP1:
|
|
LOAD DL_E2 ; if e2 < dx
|
|
LOAD DL_DX
|
|
CMP LT
|
|
CBRANCH.Z DL_SKIP2
|
|
|
|
LOAD DL_ERR ; err += dx
|
|
LOAD DL_DX
|
|
ADD
|
|
STORE DL_ERR
|
|
|
|
LOAD DL_Y0 ; y0 += sy
|
|
LOAD DL_SY
|
|
ADD
|
|
STORE DL_Y0
|
|
|
|
DL_SKIP2:
|
|
BRANCH DL_LOOP
|
|
DL_END:
|
|
FPADJ STACKFRAME_SIZE
|
|
RET
|
|
|
|
; initialize the palette registers
|
|
INITPALETTE:
|
|
LOADCP DEFAULT_PALETTE ; load pointer to color table
|
|
LOADC 0 ; load counter
|
|
INITPAL_0:
|
|
DUP
|
|
LOADC FB_PS ; store counter to palette select register
|
|
SWAP ; swap addr and value for STOREI
|
|
STOREI
|
|
DROP
|
|
|
|
SWAP ; pointer on top of stack
|
|
DUP
|
|
LOADI ; load color value
|
|
LOADC FB_PD
|
|
SWAP ; swap addr and value for STOREI
|
|
STOREI ; store to palette data register
|
|
DROP
|
|
INC 4 ; increment pointer
|
|
|
|
SWAP ; counter on top of stack
|
|
DUP
|
|
LOADC 15
|
|
CMPU EQ
|
|
CBRANCH INITPAL_X ; exit if counter is 15
|
|
|
|
INC 1 ; increment counter
|
|
BRANCH INITPAL_0
|
|
|
|
INITPAL_X:
|
|
DROP ; remove counter and pointer
|
|
DROP
|
|
RET
|
|
|
|
; set a palette register
|
|
; parameters [ palette slot nr, color value ]
|
|
SETPALETTE:
|
|
SWAP ; slot nr to top
|
|
LOADC FB_PS ; load address of palette select register
|
|
SWAP ; swap addr and slot nr for STOREI
|
|
STOREI
|
|
DROP ; remove addr from STOREI
|
|
; left on stack now: color value
|
|
LOADC FB_PD ; load address of palette data register
|
|
SWAP ; swap addr and color value for STOREI
|
|
STOREI
|
|
DROP ; remove addr
|
|
|
|
RET
|
|
|
|
DEFAULT_PALETTE:
|
|
.WORD 0, $FFF, $F00, $0F0, $00F, $0FF, $F0F, $FF0
|
|
.WORD $777, $777, $700, $070, $007, $077, $707, $770
|
|
|
|
; set whole video memory to zero
|
|
CLEARGRAPHICS:
|
|
LOADC 0
|
|
CL_LOOP:
|
|
LOADC FB_WA
|
|
OVER ; duplicate value
|
|
STOREI
|
|
DROP
|
|
|
|
LOADC FB_IO
|
|
LOADC 0
|
|
STOREI
|
|
DROP
|
|
|
|
INC 1
|
|
|
|
LOADCP 32768
|
|
CMP.S0 NE
|
|
CBRANCH CL_LOOP
|
|
|
|
DROP
|
|
|
|
RET
|
|
|
|
INITGRAPHICS:
|
|
LOADCP CLEARGRAPHICS
|
|
CALL
|
|
LOADCP INITPALETTE
|
|
CALL
|
|
RET
|
|
|
|
; wait for vertical blank
|
|
; we first wait for the VBLANK bit
|
|
; to become zero to make sure we
|
|
; catch the beginning of the vertical blank
|
|
WAITVSYNC:
|
|
; wait for VBLANK to become zero
|
|
LOADC FB_CTL
|
|
LOADI ; read control register
|
|
LOADC 1 ; check bit 0 (VBLANK)
|
|
AND
|
|
CBRANCH.NZ WAITVSYNC ; if set, loop
|
|
VSYNC_WAIT1:
|
|
; wait for VBLANK to become one
|
|
LOADC FB_CTL
|
|
LOADI ; read control register
|
|
LOADC 1 ; check bit 0 (VBLANK)
|
|
AND
|
|
CBRANCH.Z VSYNC_WAIT1 ; if not set, loop
|
|
RET
|
|
|
|
; args: number of bytes, pointer to buf,
|
|
HEXDUMP:
|
|
DUP
|
|
LOADI
|
|
LOADCP PRINTHEXW
|
|
CALL
|
|
LOADC ' '
|
|
LOADCP CONOUT
|
|
CALL
|
|
INC 4
|
|
|
|
DUP
|
|
LOADI
|
|
LOADCP PRINTHEXW
|
|
CALL
|
|
LOADC ' '
|
|
LOADCP CONOUT
|
|
CALL
|
|
INC 4
|
|
|
|
DUP
|
|
LOADI
|
|
LOADCP PRINTHEXW
|
|
CALL
|
|
LOADC ' '
|
|
LOADCP CONOUT
|
|
CALL
|
|
INC 4
|
|
|
|
DUP
|
|
LOADI
|
|
LOADCP PRINTHEXW
|
|
CALL
|
|
LOADC ' '
|
|
LOADCP CONOUT
|
|
CALL
|
|
INC 4
|
|
|
|
LOADCP NEWLINE
|
|
CALL
|
|
|
|
SWAP ; swap pointer and counter
|
|
LOADC 16
|
|
SUB
|
|
DUP
|
|
CBRANCH.Z HEXDUMP_END ; end if counter is zero
|
|
SWAP ; swap back counter and pointer
|
|
BRANCH HEXDUMP
|
|
|
|
HEXDUMP_END:
|
|
DROP
|
|
DROP
|
|
RET
|
|
|
|
; inquire cursor position
|
|
; args: pointer to columns variable, pointer to rows variable
|
|
GETCURSORPOS:
|
|
LOADCP TERM_CPR_STR
|
|
LOADCP PRINTLINE
|
|
CALL
|
|
|
|
LOADCP CONIN ; skip ESC
|
|
CALL
|
|
DROP
|
|
LOADCP CONIN ; and '['
|
|
CALL
|
|
DROP
|
|
|
|
LOADC ';'
|
|
LOADCP _TERMRCVINT
|
|
CALL
|
|
STOREI
|
|
DROP
|
|
|
|
LOADC 'R'
|
|
LOADCP _TERMRCVINT
|
|
CALL
|
|
STOREI
|
|
DROP
|
|
|
|
RET
|
|
|
|
; receive digits and compose an integer value
|
|
; up to a termination character or an ';'
|
|
; args: termination character
|
|
; returns: -1 on error (invalid digit)
|
|
|
|
_TERMRCVINT:
|
|
FPADJ -4
|
|
STORE 0
|
|
|
|
LOADC 0 ; start with 0 value
|
|
RCVINT_L:
|
|
LOADCP CONIN
|
|
CALL
|
|
|
|
DUP ; duplicate received char
|
|
LOAD 0 ; compare with terminator
|
|
CMP EQ ; if equal,
|
|
CBRANCH RCVINT_XT ; exit
|
|
|
|
LOADC '0' ; subtract ascii value to get
|
|
SUB ; numerical value
|
|
|
|
DUP
|
|
LOADC 0 ; check if less than zero
|
|
CMP LT
|
|
CBRANCH RCVINT_ERR ; if yes, error
|
|
|
|
DUP
|
|
LOADC 9
|
|
CMP GT ; check if > 9
|
|
CBRANCH RCVINT_ERR ; if yes, error
|
|
|
|
SWAP
|
|
LOADCP _MUL10 ; old value * 10 (shift digits to the left)
|
|
CALL
|
|
|
|
ADD ; add to value
|
|
BRANCH RCVINT_L ; next digit
|
|
RCVINT_ERR:
|
|
DROP
|
|
DROP
|
|
LOADC -1
|
|
BRANCH RCVINT_XT2
|
|
RCVINT_XT:
|
|
DROP
|
|
RCVINT_XT2:
|
|
FPADJ 4
|
|
RET
|
|
|
|
TERM_CPR_STR: .BYTE 27, "[6n", 0 ; ANSI Cursor Position Report
|
|
|
|
.CPOOL
|
|
|
|
GETTICKS:
|
|
LOADC IRQC_REG
|
|
LOADI
|
|
LOADC -256
|
|
AND
|
|
BROT
|
|
BROT
|
|
BROT
|
|
RET
|
|
|
|
.EQU CRLD_BLOCK 0
|
|
.EQU CRLD_BYTES 4
|
|
.EQU CRLD_ADDR 8
|
|
.EQU CRLD_FS 12
|
|
|
|
; load a program image from sd card
|
|
; args: device id, block no, size in bytes
|
|
CORELOAD:
|
|
; We need to set the FP and RP registers,
|
|
; because we might overwrite that
|
|
; memory area where the calling program
|
|
; has its user and return stack
|
|
|
|
LOADCP FP_START
|
|
STOREREG FP
|
|
LOADCP RP_START
|
|
STOREREG RP
|
|
|
|
FPADJ -CRLD_FS
|
|
STORE CRLD_BYTES
|
|
STORE CRLD_BLOCK
|
|
DROP ; ignore device ID,
|
|
; we support only one sd card
|
|
|
|
; divide bytes by 512 and add one
|
|
; to get block count
|
|
LOAD CRLD_BYTES
|
|
LOADC 9
|
|
LOADCP _SHRM
|
|
CALL
|
|
INC 1
|
|
; keep block count on stack
|
|
|
|
; start at address 24576
|
|
LOADCP PROG_START
|
|
STORE CRLD_ADDR
|
|
|
|
CRLD_LP:
|
|
; read a block
|
|
LOAD CRLD_BLOCK
|
|
LOADCP CARDREADBLK
|
|
CALL
|
|
DROP ; ignore error for now
|
|
|
|
LOAD CRLD_ADDR
|
|
LOADCP CARD_BUF
|
|
LOADC 128
|
|
LOADCP _COPYWORDS
|
|
CALL
|
|
|
|
; advance dest pointer
|
|
LOAD CRLD_ADDR
|
|
LOADC 512
|
|
ADD
|
|
STORE CRLD_ADDR
|
|
|
|
; increment block number
|
|
LOAD CRLD_BLOCK
|
|
INC 1
|
|
STORE CRLD_BLOCK
|
|
|
|
; decrement block count on stack
|
|
DEC 1
|
|
DUP
|
|
CBRANCH.NZ CRLD_LP ; if block count not zero, loop
|
|
DROP ; remove block count
|
|
CRLD_CLEAN:
|
|
; clean up partial block
|
|
LOADC 512
|
|
LOAD CRLD_BYTES
|
|
INC 3 ; round up to next word
|
|
LOADC -4
|
|
AND
|
|
LOADC 511 ; get remainder by 512
|
|
AND
|
|
SUB ; subtract to get number of remaining bytes
|
|
; in last block
|
|
|
|
LOAD CRLD_ADDR ; this is now the addr of the last
|
|
; byte in the last block + 1
|
|
OVER ; duplicate number of remaining bytes
|
|
SUB ; and subtract from addr
|
|
|
|
SWAP ; swap addr and number of remaining bytes
|
|
LOADCP _CLEARMEM
|
|
CALL
|
|
|
|
; clear estack
|
|
|
|
; release our stack frame
|
|
FPADJ CRLD_FS
|
|
|
|
; jump to program start address
|
|
LOADCP PROG_START
|
|
JUMP
|
|
|
|
; no RET
|
|
|
|
.CPOOL
|
|
|
|
READDIRBLK:
|
|
; parameters: [ blkno, ptr to DirBlock, ptr to error return value, device id ]
|
|
; same routine as READBLOCK, is referenced by two names to convert buffer types
|
|
|
|
; parameters: [ blkno, ptr to PartitionTableBlock, ptr to error return value, device id ]
|
|
READPARTBLK:
|
|
; same routine as READBLOCK, is referenced by two names to convert buffer types
|
|
|
|
READBLOCK:
|
|
; parameters: [ blkno, ptr to IOBlock, ptr to error return value, device id ]
|
|
DROP ; ignore device id
|
|
|
|
LOADC 0
|
|
STOREI ; set return value to zero
|
|
DROP
|
|
SWAP ; swap blkno and ptr
|
|
LOADCP CARDREADBLK ; read that block number
|
|
CALL
|
|
DROP ; ignore error for now
|
|
; TODO: store it via error ptr
|
|
; ptr to PartitionTableBlock is now on ToS
|
|
LOADCP CARD_BUF
|
|
LOADC 128
|
|
LOADCP _COPYWORDS ; copy block to destination buffer
|
|
CALL
|
|
|
|
RET
|
|
|
|
WRITEPARTBLK:
|
|
WRITEDIRBLK:
|
|
; parameters: [ blkno, ptr to IOBlock, ptr to error return value, device id ]
|
|
WRITEBLOCK:
|
|
DROP ; ignore device id
|
|
|
|
LOADC 0
|
|
STOREI ; set return value to zero
|
|
DROP
|
|
|
|
LOADCP CARD_BUF
|
|
SWAP
|
|
LOADC 128
|
|
LOADCP _COPYWORDS ; copy block to card_buf
|
|
CALL
|
|
|
|
LOADCP CARDWRITEBLK ; write that block number
|
|
CALL
|
|
DROP ; ignore error for now
|
|
; TODO: store it via error ptr
|
|
; ptr to PartitionTableBlock is now on ToS
|
|
RET
|
|
|
|
%include "sdcardlib.s"
|
|
|
|
.CPOOL
|
|
|
|
SHELLWORKFILE: .WORD 0,68
|
|
.BLOCK 17
|
|
SHELLCMD: .WORD 0,40
|
|
.BLOCK 8
|
|
SHELLARG: .WORD 0
|
|
PARGCOUNT: .WORD 0
|
|
PARGS: .WORD 0,80
|
|
.BLOCK 20
|
|
.WORD 0,80
|
|
.BLOCK 20
|
|
.WORD 0,80
|
|
.BLOCK 20
|
|
.WORD 0,80
|
|
.BLOCK 20
|
|
.WORD 0,80
|
|
.BLOCK 20
|
|
.WORD 0,80
|
|
.BLOCK 20
|
|
.WORD 0,80
|
|
.BLOCK 20
|
|
.WORD 0,80
|
|
.BLOCK 20
|
|
|
|
DEFAULTVOLUME: .WORD 0,32
|
|
.BLOCK 8
|
|
|
|
SYSCLOCK:
|
|
.BLOCK 6
|
|
SYSBOOTTICKS:
|
|
.WORD 0
|
|
SYSLASTTICKS:
|
|
.WORD 0
|
|
|
|
; copy words to screen memory
|
|
; args: pointer to 32000 words of pixel data
|
|
PUTSCREEN:
|
|
LOADC FB_WA
|
|
LOADC 0 ; initialize write address register
|
|
STOREI
|
|
DROP
|
|
|
|
LOADCP 32000 ; word count
|
|
PUTSCREEN_L0:
|
|
SWAP ; [ count, addr ]
|
|
DUP
|
|
LOADI ; load pixel word
|
|
LOADC FB_IO
|
|
SWAP ; swap addr and value for STOREI
|
|
STOREI ; store to vmem io register
|
|
DROP
|
|
|
|
INC 4 ; next word
|
|
|
|
SWAP ; swap addr and count
|
|
DEC 1 ; decrement count
|
|
|
|
DUP
|
|
CBRANCH.NZ PUTSCREEN_L0 ; loop if count is not zero
|
|
|
|
DROP ; remove counter and addr
|
|
DROP
|
|
|
|
RET
|
|
|
|
|
|
%export _MUL
|
|
%export _MULU
|
|
%export _DIV
|
|
%export _DIVU
|
|
%export _MOD
|
|
%export _DIVMODU
|
|
%export _SHRM
|
|
%export _SHLM
|
|
%export _COPYWORDS
|
|
%export _CMPWORDS
|
|
%export _CLEARMEM
|