1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-26 09:18:05 +11:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Virgil Dupras
ab578159b7 avra: refactor args parsing
This costs us a bit of space for now but should make things a lot
simpler down the road, especially with "alias ops" which are simple
syntactic sugar for another op.
2019-12-14 15:17:55 -05:00
Virgil Dupras
fa75f30ffb avra: add RJMP and RCALL instructions 2019-12-14 12:28:27 -05:00
5 changed files with 229 additions and 85 deletions

View File

@ -59,8 +59,11 @@ instrNames:
.db "BST", 0 .db "BST", 0
.db "SBRC", 0 .db "SBRC", 0
.db "SBRS", 0 .db "SBRS", 0
.equ I_RCALL 42
.db "RCALL", 0
.db "RJMP", 0
; no arg (from here, instrUpMasks2) ; no arg (from here, instrUpMasks2)
.equ I_BREAK 42 .equ I_BREAK 44
.db "BREAK", 0 .db "BREAK", 0
.db "CLC", 0 .db "CLC", 0
.db "CLH", 0 .db "CLH", 0
@ -88,7 +91,7 @@ instrNames:
.db "SLEEP", 0 .db "SLEEP", 0
.db "WDR", 0 .db "WDR", 0
; Rd(5) ; Rd(5)
.equ I_ASR 68 .equ I_ASR 70
.db "ASR", 0 .db "ASR", 0
.db "COM", 0 .db "COM", 0
.db "DEC", 0 .db "DEC", 0
@ -137,6 +140,9 @@ instrUpMasks1:
.db 0b11111010 ; BST .db 0b11111010 ; BST
.db 0b11111100 ; SBRC .db 0b11111100 ; SBRC
.db 0b11111110 ; SBRS .db 0b11111110 ; SBRS
; k(12): XXXXkkkk kkkkkkkk
.db 0b11010000 ; RCALL
.db 0b11000000 ; RJMP
; 16-bit constant masks associated with each instruction. In the same order as ; 16-bit constant masks associated with each instruction. In the same order as
; in instrNames ; in instrNames
@ -231,33 +237,35 @@ parseInstruction:
jr c, .spitRd5Rr5 jr c, .spitRd5Rr5
cp I_BLD cp I_BLD
jr c, .spitRdK8 jr c, .spitRdK8
cp I_BREAK cp I_RCALL
jr c, .spitRdBit jr c, .spitRdBit
cp I_BREAK
jr c, .spitK12
cp I_ASR cp I_ASR
jr c, .spitNoArg jr c, .spitNoArg
; spitRd5 ; spitRd5
call .readR5 ld ix, argSpecs ; 'R', 0
ret nz call _parseArgs
ld a, h
call .placeRd call .placeRd
; continue to .spitNoArg ; continue to .spitNoArg
.spitNoArg: .spitNoArg:
call .getUp2 call .getUp2
jr .spit jp .spit
.spitRd5Rr5: .spitRd5Rr5:
call .readR5 ld ix, argSpecs+2 ; 'R', 'R'
call _parseArgs
ret nz ret nz
ld a, h
call .placeRd call .placeRd
call readComma ld a, l
call .readR5
ret nz
push af ; --> lvl 1
; let's start with the 4 lower bits ; let's start with the 4 lower bits
and 0xf and 0xf
or c or c
; We now have our LSB in A. Let's spit it now. ; We now have our LSB in A. Let's spit it now.
call ioPutB call ioPutB
pop af ; <-- lvl 1 ld a, l
; and now that last high bit, currently bit 4, which must become bit 1 ; and now that last high bit, currently bit 4, which must become bit 1
and 0b00010000 and 0b00010000
rra \ rra \ rra rra \ rra \ rra
@ -268,24 +276,18 @@ parseInstruction:
jr .spitMSB jr .spitMSB
.spitRdK8: .spitRdK8:
call .readR4 ld ix, argSpecs+6 ; 'r', 8
call _parseArgs
ret nz ret nz
ld a, h ; Rd
call .placeRd call .placeRd
call readComma ld a, l ; K
call readWord
call parseExpr
ret nz
ld a, c
ld a, 0xff
call .IX2A
ret nz
push af ; --> lvl 1
; let's start with the 4 lower bits ; let's start with the 4 lower bits
and 0xf and 0xf
or c or c
; We now have our LSB in A. Let's spit it now. ; We now have our LSB in A. Let's spit it now.
call ioPutB call ioPutB
pop af ; <-- lvl 1 ld a, l
; and now those high 4 bits ; and now those high 4 bits
and 0xf0 and 0xf0
rra \ rra \ rra \ rra rra \ rra \ rra \ rra
@ -294,18 +296,47 @@ parseInstruction:
jr .spitMSB jr .spitMSB
.spitRdBit: .spitRdBit:
call .readR5 ld ix, argSpecs+8 ; 'R', 'b'
call _parseArgs
ret nz ret nz
ld a, h
call .placeRd call .placeRd
call readComma or l
ret nz
call .readBit
ret nz
; LSB is in A and is ready to go ; LSB is in A and is ready to go
call ioPutB call ioPutB
call .getUp1 call .getUp1
jr .spitMSB jr .spitMSB
.spitK12:
; Let's deal with the upcode constant before we destroy DE below
call .getUp1
ld b, (hl)
call readWord
call parseExpr
ret nz
push ix \ pop hl
; We're doing the same dance as in _readk7. See comments there.
ld de, 0xfff
add hl, de
jp c, unsetZ ; Carry? number is way too high.
ex de, hl
call zasmGetPC ; --> HL
inc hl \ inc hl
ex de, hl
sbc hl, de
jp c, unsetZ ; Carry? error
ld de, 0xfff
sbc hl, de
; We're within bounds! Now, divide by 2
ld a, l
rr h \ rra
; LSB in A, spit
call ioPutB
ld a, h
and 0xf
or b
jp ioPutB
.spit: .spit:
; LSB is spit *before* MSB ; LSB is spit *before* MSB
inc hl inc hl
@ -338,39 +369,24 @@ parseInstruction:
inc b inc b
.skip1: .skip1:
and 0b111 and 0b111
ld c, a ld c, a ; can't store in H now, (HL) is used
ld ix, argSpecs+4 ; 7, 0
call _parseArgs
ret nz
; ok, now we can
ld l, h ; k in L
ld h, c ; bit in H
.spitBR2: .spitBR2:
call readWord ; bit in H, k in L.
ret nz ; Our value in L is the number of relative *bytes*. The value we put
call parseExpr ; there is the number of words. Therefore, relevant bits are 7:1
ret nz
; IX contains an absolute value. Turn this into a -64/+63 relative
; value by subtracting PC from it. However, before we do that, let's
; add 0x7f to it, which we'll remove later. This will simplify bounds
; checks. (we use 7f instead of 3f because we deal in bytes here, not
; in words)
push ix \ pop hl
ld de, 0x7f
add hl, de ; Carry cleared
ex de, hl
call zasmGetPC ; --> HL
; The relative value is actually not relative to current PC, but to
; PC after the execution of this branching op. Increase HL by 2.
inc hl \ inc hl
ex de, hl
sbc hl, de
jp c, unsetZ ; Carry? error
ld de, 0x7f
sbc hl, de
; We're within bounds! However, our value in L is the number of
; relative *bytes*. The value we put there is the number of words.
; Thefore, relevant bits are 7:1
ld a, l ld a, l
sla a \ rl b sla a \ rl b
sla a \ rl b sla a \ rl b
and 0b11111000
; k is now shifted by 3, two of those bits being in B. Let's OR A and ; k is now shifted by 3, two of those bits being in B. Let's OR A and
; C and we have our LSB ready to go. ; H and we have our LSB ready to go.
or c or h
call ioPutB call ioPutB
; Good! MSB now. B is already good to go. ; Good! MSB now. B is already good to go.
ld a, b ld a, b
@ -380,10 +396,10 @@ parseInstruction:
; upcode becomes 0b111101 ; upcode becomes 0b111101
inc b inc b
.rdBRBS: .rdBRBS:
call .readBit ld ix, argSpecs+10 ; bit + k(7)
ret nz call _parseArgs
call readComma
ret nz ret nz
; bit in H, k in L.
jr .spitBR2 jr .spitBR2
; local routines ; local routines
@ -410,8 +426,141 @@ parseInstruction:
ld hl, instrUpMasks2 ld hl, instrUpMasks2
jp addHL jp addHL
.readR4: ; Argspecs: two bytes describing the arguments that are accepted. Possible
call .readR5 ; values:
;
; 0 - None
; 7 - a k(7) address, relative to PC, *in bytes* (divide by 2 before writing)
; 8 - a K(8) value
; 'a' - A 5-bit I/O port value
; 'A' - A 6-bit I/O port value
; 'b' - a 0-7 bit value
; 'R' - an r5 value: r0-r31
; 'r' - an r4 value: r16-r31
;
; All arguments accept expressions, even 'r' ones: in 'r' args, we start by
; looking if the arg starts with 'r' or 'R'. If yes, it's a simple 'rXX' value,
; if not, we try parsing it as an expression and validate that it falls in the
; correct 0-31 or 16-31 range
argSpecs:
.db 'R', 0 ; Rd(5)
.db 'R', 'R' ; Rd(5) + Rr(5)
.db 7, 0 ; k(7)
.db 'r', 8 ; Rd(4) + K(8)
.db 'R', 'b' ; Rd(5) + bit
.db 'b', 7 ; bit + k(7)
; Parse arguments in (HL) according to specs in IX
; Puts the results in HL (which is not needed anymore after the parsing).
; First arg in H, second in L.
; This routine is not used in all cases, some ops don't fit this pattern well
; and thus parse their args themselves.
; Z for success.
_parseArgs:
; For the duration of the routine, our final value will be in DE, and
; then placed in HL at the end.
push de
call readWord
jr nz, .end
ld a, (ix)
call .parse
jr nz, .end
ld d, a
ld a, (ix+1)
or a
jr z, .end ; no arg
call readComma
jr nz, .end
call readWord
jr nz, .end
ld a, (ix+1)
call .parse
jr nz, .end
; we're done with (HL) now
ld l, a
cp a ; ensure Z
.end:
ld h, d
pop de
ret
; Parse a single arg specified in A and returns its value in A
; Z for success
.parse:
cp 'R'
jr z, _readR5
cp 'r'
jr z, _readR4
cp 'b'
jr z, _readBit
cp 7
jr z, _readk7
cp 8
jr z, _readK8
ret ; something's wrong
_readBit:
push ix
call parseExpr
ld a, 7
call _IX2A
jr nz, .end
or c
ld c, a
cp a ; ensure Z
.end:
pop ix
ret
_readk7:
push hl
push de
push ix
call parseExpr
jr nz, .end
; IX contains an absolute value. Turn this into a -64/+63 relative
; value by subtracting PC from it. However, before we do that, let's
; add 0x7f to it, which we'll remove later. This will simplify bounds
; checks. (we use 7f instead of 3f because we deal in bytes here, not
; in words)
push ix \ pop hl
ld de, 0x7f
add hl, de ; Carry cleared
ex de, hl
call zasmGetPC ; --> HL
; The relative value is actually not relative to current PC, but to
; PC after the execution of this branching op. Increase HL by 2.
inc hl \ inc hl
ex de, hl
sbc hl, de
jp c, .err ; Carry? error
ld de, 0x7f
sbc hl, de
; We're within bounds! However, our value in L is the number of
; relative *bytes*.
ld a, l
cp a ; ensure Z
.end:
pop ix
pop de
pop hl
ret
.err:
call unsetZ
jr .end
_readK8:
push ix
call parseExpr
jr nz, .end
ld a, 0xff
call _IX2A
.end:
pop ix
ret
_readR4:
call _readR5
ret nz ret nz
; has to be in the 16-31 range ; has to be in the 16-31 range
sub 0x10 sub 0x10
@ -421,34 +570,25 @@ parseInstruction:
; read a rXX argument and return register number in A. ; read a rXX argument and return register number in A.
; Set Z for success. ; Set Z for success.
.readR5: _readR5:
call readWord push ix
ld a, (hl) ld a, (hl)
call upcase call upcase
cp 'R' cp 'R'
ret nz ; not a register jr nz, .end ; not a register
inc hl inc hl
call parseDecimal call parseDecimal
ret nz jr nz, .end
ld a, 31 ld a, 31
jr .IX2A call _IX2A
.end:
.readBit: pop ix
call readWord
ret nz
call parseExpr
ld a, 7
call .IX2A
ret nz
or c
ld c, a
cp a ; ensure Z
ret ret
; Put IX's LSB into A and, additionally, ensure that the new value is <= ; Put IX's LSB into A and, additionally, ensure that the new value is <=
; than what was previously in A. ; than what was previously in A.
; Z for success. ; Z for success.
.IX2A: _IX2A:
push ix \ pop hl push ix \ pop hl
cp l cp l
jp c, unsetZ ; A < L jp c, unsetZ ; A < L
@ -458,3 +598,5 @@ parseInstruction:
ld a, l ld a, l
; Z set from "or a" ; Z set from "or a"
ret ret

View File

@ -80,8 +80,10 @@ ioGetB:
call ioInInclude call ioInInclude
jr z, .normalmode jr z, .normalmode
; We're in "include mode", read from FS ; We're in "include mode", read from FS
push ix ; --> lvl 1
ld ix, IO_INCLUDE_BLK ld ix, IO_INCLUDE_BLK
call _blkGetB call _blkGetB
pop ix ; <-- lvl 1
jr nz, .includeEOF jr nz, .includeEOF
cp 0x0a ; newline cp 0x0a ; newline
ret nz ; not newline? nothing to do ret nz ; not newline? nothing to do
@ -111,9 +113,11 @@ ioGetB:
; continue on to "normal" reading. We don't want to return our zero ; continue on to "normal" reading. We don't want to return our zero
.normalmode: .normalmode:
; normal mode, read from IN stream ; normal mode, read from IN stream
push ix ; --> lvl 1
ld ix, IO_IN_BLK ld ix, IO_IN_BLK
call _blkGetB call _blkGetB
cp 0x0a ; newline pop ix ; <-- lvl 1
cp LF ; newline
ret nz ; not newline? return ret nz ; not newline? return
; inc current lineno ; inc current lineno
push hl push hl
@ -130,10 +134,6 @@ ioGetB:
pop af pop af
ret ret
_callIX:
jp (ix)
ret
; Put back non-zero character A into the "ioGetB stack". The next ioGetB call, ; Put back non-zero character A into the "ioGetB stack". The next ioGetB call,
; instead of reading from IO_IN_BLK, will return that character. That's the ; instead of reading from IO_IN_BLK, will return that character. That's the
; easiest way I found to handle the readWord/gotoNextLine problem. ; easiest way I found to handle the readWord/gotoNextLine problem.

View File

@ -155,8 +155,7 @@ readComma:
cp ',' cp ','
ret z ret z
call ioPutBack call ioPutBack
call unsetZ jp unsetZ
ret
; Read ioGetB until we reach the beginning of next line, skipping comments if ; Read ioGetB until we reach the beginning of next line, skipping comments if
; necessary. This skips all whitespace, \n, \r, comments until we reach the ; necessary. This skips all whitespace, \n, \r, comments until we reach the

View File

@ -9,3 +9,6 @@ bar:
brbs 6, foo brbs 6, foo
ori r22, 0x34+4 ori r22, 0x34+4
sbrs r1, 3 sbrs r1, 3
rjmp foo
rcall baz
baz:

Binary file not shown.