mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-02 18:20:55 +11:00
Compare commits
10 Commits
c968995ec0
...
98ca338aba
Author | SHA1 | Date | |
---|---|---|---|
|
98ca338aba | ||
|
51e500e8da | ||
|
b955a67daa | ||
|
6e1e8e0e59 | ||
|
8ded02bc78 | ||
|
f54e10f9fd | ||
|
10b925e0e0 | ||
|
1771ee8da7 | ||
|
0a9ac27cf6 | ||
|
a9dcba5793 |
@ -191,4 +191,10 @@ the instruction after the "foo" label would be "rjmp foo+1". In zasm, it's
|
|||||||
"rjmp foo+2". If your expression results in an odd number, the low bit of your
|
"rjmp foo+2". If your expression results in an odd number, the low bit of your
|
||||||
number will be ignored.
|
number will be ignored.
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
|
||||||
|
* `CALL` and `JMP` only support 16-bit numbers, not 22-bit ones.
|
||||||
|
* `BRLO` and `BRSH` are not there. Use `BRCS` and `BRCC` instead.
|
||||||
|
* No `high()` and `low()`. Use `&0xff` and `}8`.
|
||||||
|
|
||||||
[libz80]: https://github.com/ggambetta/libz80
|
[libz80]: https://github.com/ggambetta/libz80
|
||||||
|
@ -31,14 +31,19 @@ instrNames:
|
|||||||
.equ I_BRBS 16
|
.equ I_BRBS 16
|
||||||
.db "BRBS", 0
|
.db "BRBS", 0
|
||||||
.db "BRBC", 0
|
.db "BRBC", 0
|
||||||
|
.equ I_LD 18
|
||||||
|
.db "LD", 0
|
||||||
|
.db "ST", 0
|
||||||
; Rd(5) + Rr(5) (from here, instrTbl8)
|
; Rd(5) + Rr(5) (from here, instrTbl8)
|
||||||
.equ I_ADC 18
|
.equ I_ADC 20
|
||||||
.db "ADC", 0
|
.db "ADC", 0
|
||||||
.db "ADD", 0
|
.db "ADD", 0
|
||||||
.db "AND", 0
|
.db "AND", 0
|
||||||
.db "ASR", 0
|
.db "ASR", 0
|
||||||
|
.db "BCLR", 0
|
||||||
.db "BLD", 0
|
.db "BLD", 0
|
||||||
.db "BREAK", 0
|
.db "BREAK", 0
|
||||||
|
.db "BSET", 0
|
||||||
.db "BST", 0
|
.db "BST", 0
|
||||||
.db "CLC", 0
|
.db "CLC", 0
|
||||||
.db "CLH", 0
|
.db "CLH", 0
|
||||||
@ -64,6 +69,7 @@ instrNames:
|
|||||||
.db "LAC", 0
|
.db "LAC", 0
|
||||||
.db "LAS", 0
|
.db "LAS", 0
|
||||||
.db "LAT", 0
|
.db "LAT", 0
|
||||||
|
.db "LSL", 0
|
||||||
.db "LSR", 0
|
.db "LSR", 0
|
||||||
.db "MOV", 0
|
.db "MOV", 0
|
||||||
.db "MUL", 0
|
.db "MUL", 0
|
||||||
@ -83,6 +89,7 @@ instrNames:
|
|||||||
.db "SEH", 0
|
.db "SEH", 0
|
||||||
.db "SEI", 0
|
.db "SEI", 0
|
||||||
.db "SEN", 0
|
.db "SEN", 0
|
||||||
|
.db "SER", 0
|
||||||
.db "SES", 0
|
.db "SES", 0
|
||||||
.db "SET", 0
|
.db "SET", 0
|
||||||
.db "SEV", 0
|
.db "SEV", 0
|
||||||
@ -90,22 +97,33 @@ instrNames:
|
|||||||
.db "SLEEP", 0
|
.db "SLEEP", 0
|
||||||
.db "SUB", 0
|
.db "SUB", 0
|
||||||
.db "SWAP", 0
|
.db "SWAP", 0
|
||||||
|
.db "TST", 0
|
||||||
.db "WDR", 0
|
.db "WDR", 0
|
||||||
.db "XCH", 0
|
.db "XCH", 0
|
||||||
.equ I_ANDI 77
|
.equ I_ANDI 84
|
||||||
.db "ANDI", 0
|
.db "ANDI", 0
|
||||||
|
.db "CBR", 0
|
||||||
.db "CPI", 0
|
.db "CPI", 0
|
||||||
.db "LDI", 0
|
.db "LDI", 0
|
||||||
.db "ORI", 0
|
.db "ORI", 0
|
||||||
.db "SBCI", 0
|
.db "SBCI", 0
|
||||||
.db "SBR", 0
|
.db "SBR", 0
|
||||||
.db "SUBI", 0
|
.db "SUBI", 0
|
||||||
.equ I_RCALL 84
|
.equ I_RCALL 92
|
||||||
.db "RCALL", 0
|
.db "RCALL", 0
|
||||||
.db "RJMP", 0
|
.db "RJMP", 0
|
||||||
.equ I_CBI 86
|
.equ I_CBI 94
|
||||||
.db "CBI", 0
|
.db "CBI", 0
|
||||||
.db "SBI", 0
|
.db "SBI", 0
|
||||||
|
.db "SBIC", 0
|
||||||
|
.db "SBIS", 0
|
||||||
|
; 32-bit
|
||||||
|
; ZASM limitation: CALL and JMP constants are 22-bit. In ZASM, we limit
|
||||||
|
; ourselves to 16-bit. Supporting 22-bit would incur a prohibitive complexity
|
||||||
|
; cost. As they say, 64K words ought to be enough for anybody.
|
||||||
|
.equ I_CALL 98
|
||||||
|
.db "CALL", 0
|
||||||
|
.db "JMP", 0
|
||||||
.db 0xff
|
.db 0xff
|
||||||
|
|
||||||
; Instruction table
|
; Instruction table
|
||||||
@ -127,6 +145,7 @@ instrNames:
|
|||||||
; allow this kind of syntactic sugar with minimal complexity.
|
; allow this kind of syntactic sugar with minimal complexity.
|
||||||
;
|
;
|
||||||
; Bit 6: Second arg is a copy of the first
|
; Bit 6: Second arg is a copy of the first
|
||||||
|
; Bit 5: Second arg is inverted (complement)
|
||||||
|
|
||||||
; In the same order as in instrNames
|
; In the same order as in instrNames
|
||||||
instrTbl:
|
instrTbl:
|
||||||
@ -137,8 +156,10 @@ instrTbl:
|
|||||||
.db 0x02, 0b00001100, 0x00 ; ADD Rd, Rr
|
.db 0x02, 0b00001100, 0x00 ; ADD Rd, Rr
|
||||||
.db 0x02, 0b00100000, 0x00 ; AND Rd, Rr
|
.db 0x02, 0b00100000, 0x00 ; AND Rd, Rr
|
||||||
.db 0x01, 0b10010100, 0b00000101 ; ASR Rd
|
.db 0x01, 0b10010100, 0b00000101 ; ASR Rd
|
||||||
|
.db 0x0b, 0b10010100, 0b10001000 ; BCLR s, k
|
||||||
.db 0x05, 0b11111000, 0x00 ; BLD Rd, b
|
.db 0x05, 0b11111000, 0x00 ; BLD Rd, b
|
||||||
.db 0x00, 0b10010101, 0b10011000 ; BREAK
|
.db 0x00, 0b10010101, 0b10011000 ; BREAK
|
||||||
|
.db 0x0b, 0b10010100, 0b00001000 ; BSET s, k
|
||||||
.db 0x05, 0b11111010, 0x00 ; BST Rd, b
|
.db 0x05, 0b11111010, 0x00 ; BST Rd, b
|
||||||
.db 0x00, 0b10010100, 0b10001000 ; CLC
|
.db 0x00, 0b10010100, 0b10001000 ; CLC
|
||||||
.db 0x00, 0b10010100, 0b11011000 ; CLH
|
.db 0x00, 0b10010100, 0b11011000 ; CLH
|
||||||
@ -164,11 +185,12 @@ instrTbl:
|
|||||||
.db 0x01, 0b10010010, 0b00000110 ; LAC Rd
|
.db 0x01, 0b10010010, 0b00000110 ; LAC Rd
|
||||||
.db 0x01, 0b10010010, 0b00000101 ; LAS Rd
|
.db 0x01, 0b10010010, 0b00000101 ; LAS Rd
|
||||||
.db 0x01, 0b10010010, 0b00000111 ; LAT Rd
|
.db 0x01, 0b10010010, 0b00000111 ; LAT Rd
|
||||||
|
.db 0x41, 0b00001100, 0x00 ; LSL Rd
|
||||||
.db 0x01, 0b10010100, 0b00000110 ; LSR Rd
|
.db 0x01, 0b10010100, 0b00000110 ; LSR Rd
|
||||||
.db 0x00, 0b00000000, 0b00000000 ; NOP
|
|
||||||
.db 0x02, 0b00101100, 0x00 ; MOV Rd, Rr
|
.db 0x02, 0b00101100, 0x00 ; MOV Rd, Rr
|
||||||
.db 0x02, 0b10011100, 0x00 ; MUL Rd, Rr
|
.db 0x02, 0b10011100, 0x00 ; MUL Rd, Rr
|
||||||
.db 0x01, 0b10010100, 0b00000001 ; NEG Rd
|
.db 0x01, 0b10010100, 0b00000001 ; NEG Rd
|
||||||
|
.db 0x00, 0b00000000, 0b00000000 ; NOP
|
||||||
.db 0x02, 0b00101000, 0x00 ; OR Rd, Rr
|
.db 0x02, 0b00101000, 0x00 ; OR Rd, Rr
|
||||||
.db 0x87, 0b10111000, 0x00 ; OUT A, Rr (Bit 7)
|
.db 0x87, 0b10111000, 0x00 ; OUT A, Rr (Bit 7)
|
||||||
.db 0x01, 0b10010000, 0b00001111 ; POP Rd
|
.db 0x01, 0b10010000, 0b00001111 ; POP Rd
|
||||||
@ -183,6 +205,7 @@ instrTbl:
|
|||||||
.db 0x00, 0b10010100, 0b01011000 ; SEH
|
.db 0x00, 0b10010100, 0b01011000 ; SEH
|
||||||
.db 0x00, 0b10010100, 0b01111000 ; SEI
|
.db 0x00, 0b10010100, 0b01111000 ; SEI
|
||||||
.db 0x00, 0b10010100, 0b00101000 ; SEN
|
.db 0x00, 0b10010100, 0b00101000 ; SEN
|
||||||
|
.db 0x0a, 0b11101111, 0b00001111 ; SER Rd
|
||||||
.db 0x00, 0b10010100, 0b01001000 ; SES
|
.db 0x00, 0b10010100, 0b01001000 ; SES
|
||||||
.db 0x00, 0b10010100, 0b01101000 ; SET
|
.db 0x00, 0b10010100, 0b01101000 ; SET
|
||||||
.db 0x00, 0b10010100, 0b00111000 ; SEV
|
.db 0x00, 0b10010100, 0b00111000 ; SEV
|
||||||
@ -190,22 +213,29 @@ instrTbl:
|
|||||||
.db 0x00, 0b10010101, 0b10001000 ; SLEEP
|
.db 0x00, 0b10010101, 0b10001000 ; SLEEP
|
||||||
.db 0x02, 0b00011000, 0x00 ; SUB Rd, Rr
|
.db 0x02, 0b00011000, 0x00 ; SUB Rd, Rr
|
||||||
.db 0x01, 0b10010100, 0b00000010 ; SWAP Rd
|
.db 0x01, 0b10010100, 0b00000010 ; SWAP Rd
|
||||||
|
.db 0x41, 0b00100000, 0x00 ; TST Rd (Bit 6)
|
||||||
.db 0x00, 0b10010101, 0b10101000 ; WDR
|
.db 0x00, 0b10010101, 0b10101000 ; WDR
|
||||||
.db 0x01, 0b10010010, 0b00000100 ; XCH Rd
|
.db 0x01, 0b10010010, 0b00000100 ; XCH Rd
|
||||||
; Rd(4) + K(8): XXXXKKKK ddddKKKK
|
; Rd(4) + K(8): XXXXKKKK ddddKKKK
|
||||||
.db 0x04, 0b01110000, 0x00 ; ANDI
|
.db 0x04, 0b01110000, 0x00 ; ANDI Rd, K
|
||||||
.db 0x04, 0b00110000, 0x00 ; CPI
|
.db 0x24, 0b01110000, 0x00 ; CBR Rd, K (Bit 5)
|
||||||
.db 0x04, 0b11100000, 0x00 ; LDI
|
.db 0x04, 0b00110000, 0x00 ; CPI Rd, K
|
||||||
.db 0x04, 0b01100000, 0x00 ; ORI
|
.db 0x04, 0b11100000, 0x00 ; LDI Rd, K
|
||||||
.db 0x04, 0b01000000, 0x00 ; SBCI
|
.db 0x04, 0b01100000, 0x00 ; ORI Rd, K
|
||||||
.db 0x04, 0b01100000, 0x00 ; SBR
|
.db 0x04, 0b01000000, 0x00 ; SBCI Rd, K
|
||||||
.db 0x04, 0b01010000, 0x00 ; SUBI
|
.db 0x04, 0b01100000, 0x00 ; SBR Rd, K
|
||||||
|
.db 0x04, 0b01010000, 0x00 ; SUBI Rd, K
|
||||||
; k(12): XXXXkkkk kkkkkkkk
|
; k(12): XXXXkkkk kkkkkkkk
|
||||||
.db 0x08, 0b11010000, 0x00 ; RCALL k
|
.db 0x08, 0b11010000, 0x00 ; RCALL k
|
||||||
.db 0x08, 0b11000000, 0x00 ; RJMP k
|
.db 0x08, 0b11000000, 0x00 ; RJMP k
|
||||||
; A(5) + bit: XXXXXXXX AAAAAbbb
|
; A(5) + bit: XXXXXXXX AAAAAbbb
|
||||||
.db 0x09, 0b10011000, 0x00 ; CBI A, b
|
.db 0x09, 0b10011000, 0x00 ; CBI A, b
|
||||||
.db 0x09, 0b10011010, 0x00 ; SBI A, b
|
.db 0x09, 0b10011010, 0x00 ; SBI A, b
|
||||||
|
.db 0x09, 0b10011001, 0x00 ; SBIC A, b
|
||||||
|
.db 0x09, 0b10011011, 0x00 ; SBIS A, b
|
||||||
|
; k(16) (well, k(22)...)
|
||||||
|
.db 0x08, 0b10010100, 0b00001110 ; CALL k
|
||||||
|
.db 0x08, 0b10010100, 0b00001100 ; JMP k
|
||||||
|
|
||||||
; Same signature as getInstID in instr.asm
|
; Same signature as getInstID in instr.asm
|
||||||
; Reads string in (HL) and returns the corresponding ID (I_*) in A. Sets Z if
|
; Reads string in (HL) and returns the corresponding ID (I_*) in A. Sets Z if
|
||||||
@ -253,8 +283,11 @@ parseInstruction:
|
|||||||
ld bc, 0
|
ld bc, 0
|
||||||
ld e, a ; Let's keep that instrID somewhere safe
|
ld e, a ; Let's keep that instrID somewhere safe
|
||||||
; First, let's fetch our table row
|
; First, let's fetch our table row
|
||||||
cp I_ADC
|
cp I_LD
|
||||||
jp c, .BR ; BR is special, no table row
|
jp c, .BR ; BR is special, no table row
|
||||||
|
jp z, .LD ; LD is special
|
||||||
|
cp I_ADC
|
||||||
|
jp c, .ST ; ST is special
|
||||||
|
|
||||||
; *** Step 2: parse arguments
|
; *** Step 2: parse arguments
|
||||||
sub I_ADC ; Adjust index for table
|
sub I_ADC ; Adjust index for table
|
||||||
@ -267,7 +300,7 @@ parseInstruction:
|
|||||||
push hl \ pop ix ; IX is now our tblrow
|
push hl \ pop ix ; IX is now our tblrow
|
||||||
ld hl, 0
|
ld hl, 0
|
||||||
or a
|
or a
|
||||||
jr z, .spit ; No arg? spit right away
|
jp z, .spit ; No arg? spit right away
|
||||||
and 0xf ; lower nibble
|
and 0xf ; lower nibble
|
||||||
dec a ; argspec index is 1-based
|
dec a ; argspec index is 1-based
|
||||||
ld hl, argSpecs
|
ld hl, argSpecs
|
||||||
@ -290,6 +323,8 @@ parseInstruction:
|
|||||||
call nz, .swapHL ; Bit 7 set, swap H and L again!
|
call nz, .swapHL ; Bit 7 set, swap H and L again!
|
||||||
bit 6, (ix)
|
bit 6, (ix)
|
||||||
call nz, .cpHintoL ; Bit 6 set, copy H into L
|
call nz, .cpHintoL ; Bit 6 set, copy H into L
|
||||||
|
bit 5, (ix)
|
||||||
|
call nz, .invL ; Bit 5 set, invert L
|
||||||
ld a, e ; InstrID
|
ld a, e ; InstrID
|
||||||
cp I_ANDI
|
cp I_ANDI
|
||||||
jr c, .spitRegular
|
jr c, .spitRegular
|
||||||
@ -297,12 +332,18 @@ parseInstruction:
|
|||||||
jr c, .spitRdK8
|
jr c, .spitRdK8
|
||||||
cp I_CBI
|
cp I_CBI
|
||||||
jr c, .spitk12
|
jr c, .spitk12
|
||||||
; spit A(5) + bit
|
cp I_CALL
|
||||||
|
jr c, .spitA5Bit
|
||||||
|
; Spit k(16)
|
||||||
|
call .spit ; spit 16-bit const upcode
|
||||||
|
; divide HL by 2 (PC deals with words, not bytes)
|
||||||
|
srl h \ rr l
|
||||||
|
; spit 16-bit K, LSB first
|
||||||
|
ld a, l
|
||||||
|
call ioPutB
|
||||||
ld a, h
|
ld a, h
|
||||||
rla \ rla \ rla
|
jp ioPutB
|
||||||
or l
|
|
||||||
ld c, a
|
|
||||||
jr .spit
|
|
||||||
.spitRegular:
|
.spitRegular:
|
||||||
; Regular process which places H and L, ORring it with upcode. Works
|
; Regular process which places H and L, ORring it with upcode. Works
|
||||||
; in most cases.
|
; in most cases.
|
||||||
@ -319,6 +360,8 @@ parseInstruction:
|
|||||||
.spitk12:
|
.spitk12:
|
||||||
; k(12) in HL
|
; k(12) in HL
|
||||||
; We're doing the same dance as in _readk7. See comments there.
|
; We're doing the same dance as in _readk7. See comments there.
|
||||||
|
call zasmIsFirstPass
|
||||||
|
jr z, .spit
|
||||||
ld de, 0xfff
|
ld de, 0xfff
|
||||||
add hl, de
|
add hl, de
|
||||||
jp c, unsetZ ; Carry? number is way too high.
|
jp c, unsetZ ; Carry? number is way too high.
|
||||||
@ -339,6 +382,12 @@ parseInstruction:
|
|||||||
and 0xf
|
and 0xf
|
||||||
ld b, a
|
ld b, a
|
||||||
jr .spit
|
jr .spit
|
||||||
|
.spitA5Bit:
|
||||||
|
ld a, h
|
||||||
|
sla a \ rla \ rla
|
||||||
|
or l
|
||||||
|
ld c, a
|
||||||
|
jr .spit
|
||||||
|
|
||||||
.spit:
|
.spit:
|
||||||
; LSB is spit *before* MSB
|
; LSB is spit *before* MSB
|
||||||
@ -405,6 +454,40 @@ parseInstruction:
|
|||||||
; bit in H, k in L.
|
; bit in H, k in L.
|
||||||
jr .spitBR2
|
jr .spitBR2
|
||||||
|
|
||||||
|
.LD:
|
||||||
|
ld h, 'R'
|
||||||
|
ld l, 'z'
|
||||||
|
call _parseArgs
|
||||||
|
ret nz
|
||||||
|
ld d, 0b10000000
|
||||||
|
jr .LDST
|
||||||
|
.ST:
|
||||||
|
ld h, 'z'
|
||||||
|
ld l, 'R'
|
||||||
|
call _parseArgs
|
||||||
|
ret nz
|
||||||
|
ld d, 0b10000010
|
||||||
|
call .swapHL
|
||||||
|
; continue to .LDST
|
||||||
|
|
||||||
|
.LDST:
|
||||||
|
; Rd in H, Z in L, base upcode in D
|
||||||
|
call .placeRd
|
||||||
|
; We're spitting LSB first, so let's compose it.
|
||||||
|
ld a, l
|
||||||
|
and 0b00001111
|
||||||
|
or c
|
||||||
|
call ioPutB
|
||||||
|
; Now, MSB's bit 4 is L's bit 4. How convenient!
|
||||||
|
ld a, l
|
||||||
|
and 0b00010000
|
||||||
|
or d
|
||||||
|
or b
|
||||||
|
; MSB composed!
|
||||||
|
call ioPutB
|
||||||
|
cp a ; ensure Z
|
||||||
|
ret
|
||||||
|
|
||||||
; local routines
|
; local routines
|
||||||
; place number in H in BC at position .......d dddd....
|
; place number in H in BC at position .......d dddd....
|
||||||
; BC is assumed to be 0
|
; BC is assumed to be 0
|
||||||
@ -441,6 +524,12 @@ parseInstruction:
|
|||||||
ld l, h
|
ld l, h
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
.invL:
|
||||||
|
ld a, l
|
||||||
|
cpl
|
||||||
|
ld l, a
|
||||||
|
ret
|
||||||
|
|
||||||
; Argspecs: two bytes describing the arguments that are accepted. Possible
|
; Argspecs: two bytes describing the arguments that are accepted. Possible
|
||||||
; values:
|
; values:
|
||||||
;
|
;
|
||||||
@ -453,6 +542,9 @@ parseInstruction:
|
|||||||
; 'D' - A double-length number which will fill whole HL.
|
; 'D' - A double-length number which will fill whole HL.
|
||||||
; 'R' - an r5 value: r0-r31
|
; 'R' - an r5 value: r0-r31
|
||||||
; 'r' - an r4 value: r16-r31
|
; 'r' - an r4 value: r16-r31
|
||||||
|
; 'z' - an indirect register (X, Y or Z), with our without post-inc/pre-dec
|
||||||
|
; indicator. This will result in a 5-bit number, from which we can place
|
||||||
|
; bits 3:0 to upcode's 3:0 and bit 4 at upcode's 12 in LD and ST.
|
||||||
;
|
;
|
||||||
; All arguments accept expressions, even 'r' ones: in 'r' args, we start by
|
; 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,
|
; looking if the arg starts with 'r' or 'R'. If yes, it's a simple 'rXX' value,
|
||||||
@ -468,6 +560,8 @@ argSpecs:
|
|||||||
.db 'R', 'A' ; Rd(5) + A(6)
|
.db 'R', 'A' ; Rd(5) + A(6)
|
||||||
.db 'D', 0 ; K(12)
|
.db 'D', 0 ; K(12)
|
||||||
.db 'a', 'b' ; A(5) + bit
|
.db 'a', 'b' ; A(5) + bit
|
||||||
|
.db 'r', 0 ; Rd(4)
|
||||||
|
.db 'b', 0 ; bit
|
||||||
|
|
||||||
; Parse arguments from I/O according to specs in HL
|
; Parse arguments from I/O according to specs in HL
|
||||||
; H for first spec, L for second spec
|
; H for first spec, L for second spec
|
||||||
@ -528,6 +622,8 @@ _parseArgs:
|
|||||||
jr z, _readK8
|
jr z, _readK8
|
||||||
cp 'D'
|
cp 'D'
|
||||||
jr z, _readDouble
|
jr z, _readDouble
|
||||||
|
cp 'z'
|
||||||
|
jp z, _readz
|
||||||
ret ; something's wrong
|
ret ; something's wrong
|
||||||
|
|
||||||
_readBit:
|
_readBit:
|
||||||
@ -563,6 +659,10 @@ _readk7:
|
|||||||
push ix
|
push ix
|
||||||
call parseExpr
|
call parseExpr
|
||||||
jr nz, .end
|
jr nz, .end
|
||||||
|
; If we're in first pass, stop now. The value of HL doesn't matter and
|
||||||
|
; truncation checks might falsely fail.
|
||||||
|
call zasmIsFirstPass
|
||||||
|
jr z, .end
|
||||||
; IX contains an absolute value. Turn this into a -64/+63 relative
|
; 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
|
; 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
|
; add 0x7f to it, which we'll remove later. This will simplify bounds
|
||||||
@ -653,4 +753,63 @@ _readExpr:
|
|||||||
pop ix
|
pop ix
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
; Parse one of the following: X, Y, Z, X+, Y+, Z+, -X, -Y, -Z.
|
||||||
|
; For each of those values, return a 5-bit value than can then be interleaved
|
||||||
|
; with LD or ST upcodes.
|
||||||
|
_readz:
|
||||||
|
call strlen
|
||||||
|
cp 3
|
||||||
|
jp nc, unsetZ ; string too long
|
||||||
|
; Let's load first char in A and second in A'. This will free HL
|
||||||
|
ld a, (hl)
|
||||||
|
ex af, af'
|
||||||
|
inc hl
|
||||||
|
ld a, (hl) ; Good, HL is now free
|
||||||
|
ld hl, .tblStraight
|
||||||
|
or a
|
||||||
|
jr z, .parseXYZ ; Second char null? We have a single char
|
||||||
|
; Maybe +
|
||||||
|
cp '+'
|
||||||
|
jr nz, .skip
|
||||||
|
; We have a +
|
||||||
|
ld hl, .tblInc
|
||||||
|
jr .parseXYZ
|
||||||
|
.skip:
|
||||||
|
; Maybe a -
|
||||||
|
ex af, af'
|
||||||
|
cp '-'
|
||||||
|
ret nz ; we have nothing
|
||||||
|
; We have a -
|
||||||
|
ld hl, .tblDec
|
||||||
|
; continue to .parseXYZ
|
||||||
|
.parseXYZ:
|
||||||
|
; We have X, Y or Z in A'
|
||||||
|
ex af, af'
|
||||||
|
call upcase
|
||||||
|
; Now, let's place HL
|
||||||
|
cp 'X'
|
||||||
|
jr z, .fetch
|
||||||
|
inc hl
|
||||||
|
cp 'Y'
|
||||||
|
jr z, .fetch
|
||||||
|
inc hl
|
||||||
|
cp 'Z'
|
||||||
|
ret nz ; error
|
||||||
|
.fetch:
|
||||||
|
ld a, (hl)
|
||||||
|
; Z already set from earlier cp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.tblStraight:
|
||||||
|
.db 0b11100 ; X
|
||||||
|
.db 0b01000 ; Y
|
||||||
|
.db 0b00000 ; Z
|
||||||
|
.tblInc:
|
||||||
|
.db 0b11101 ; X+
|
||||||
|
.db 0b11001 ; Y+
|
||||||
|
.db 0b10001 ; Z+
|
||||||
|
.tblDec:
|
||||||
|
.db 0b11110 ; -X
|
||||||
|
.db 0b11010 ; -Y
|
||||||
|
.db 0b10010 ; -Z
|
||||||
|
|
||||||
|
@ -21,8 +21,8 @@ IJMP, NOP, RET, RETI, SEC, SEH, SEI, SEN, SES, SET, SEV, SEZ, SLEEP, SPM*, WDR
|
|||||||
|
|
||||||
XXXX XXXd dddd XXXX
|
XXXX XXXd dddd XXXX
|
||||||
|
|
||||||
ASR, COM, DEC, ELPM*, INC, LAC, LAS, LAT, LD*, LPM*, LSR, NEG, POP, PUSH, ROR,
|
ASR, COM, DEC, ELPM*, INC, LAC, LAS, LAT, LD*, LPM*, LSL*, LSR, NEG, POP, PUSH,
|
||||||
ST*, SWAP, XCH
|
ROR, ST*, SWAP, XCH
|
||||||
|
|
||||||
## Rd(5) + Rr(5)
|
## Rd(5) + Rr(5)
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
; TODO: implement instructions that are commented out
|
|
||||||
; REGISTER USAGE
|
; REGISTER USAGE
|
||||||
;
|
;
|
||||||
; R1: overflow counter
|
; R1: overflow counter
|
||||||
|
343
tools/tests/avra/seg7multiplex.asm
Normal file
343
tools/tests/avra/seg7multiplex.asm
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
; This is a copy of my seg7multiplex main program, translated for zasm.
|
||||||
|
; The output of zasm was verified against avra's.
|
||||||
|
|
||||||
|
; 7-segments multiplexer for an ATtiny45
|
||||||
|
;
|
||||||
|
; Register usage
|
||||||
|
; R0: Digit on AFF1 (rightmost, QH on the SR)
|
||||||
|
; R1: Digit on AFF2 (QG on the SR)
|
||||||
|
; R2: Digit on AFF3 (QF on the SR)
|
||||||
|
; R3: Digit on AFF4 (leftmost, QE on the SR)
|
||||||
|
; R5: always zero
|
||||||
|
; R6: generic tmp value
|
||||||
|
; R16: generic tmp value
|
||||||
|
; R18: value to send to the SR. cleared at every SENDSR call
|
||||||
|
; in input mode, holds the input buffer
|
||||||
|
; R30: (low Z) current digit being refreshed. cycles from 0 to 3
|
||||||
|
;
|
||||||
|
; Flags on GPIOs
|
||||||
|
; GPIOR0 - bit 0: Whether we need to refresh the display
|
||||||
|
; GPIOR0 - bit 1: Set when INT_INT0 has received a new bit
|
||||||
|
; GPIOR0 - bit 2: The value of the new bit received
|
||||||
|
; GPIOR0 - bit 4: input mode enabled
|
||||||
|
|
||||||
|
; Notes on register usage
|
||||||
|
; R0 - R3: 4 low bits are for digit, 5th bit is for dot. other bits are unused.
|
||||||
|
;
|
||||||
|
; Notes on AFF1-4
|
||||||
|
; They are reversed (depending on how you see things...). They read right to
|
||||||
|
; left. That means that AFF1 is least significant, AFF4 is most.
|
||||||
|
;
|
||||||
|
; Input mode counter
|
||||||
|
; When in input mode, TIMER0_OVF, instead of setting the refresh flag, increases
|
||||||
|
; the counter. When it reaches 3, we timeout and consider input invalid.
|
||||||
|
;
|
||||||
|
; Input procedure
|
||||||
|
;
|
||||||
|
; Input starts at INT_INT0. What it does there is very simple: is sets up a flag
|
||||||
|
; telling it received something and conditionally sets another flag with the
|
||||||
|
; value of the received bit.
|
||||||
|
;
|
||||||
|
; While we do that, we have the input loop eagerly checking for that flag. When
|
||||||
|
; it triggers, it records the bit in R18. The way it does so is that it inits
|
||||||
|
; R18 at 1 (not 0), then for every bit, it left shifts R18, then adds the new
|
||||||
|
; bit. When the 6th bit of R18 is set, it means we have every bit we need, we
|
||||||
|
; can flush it into Z.
|
||||||
|
|
||||||
|
; Z points directly to R3, then R2, then R1, then R0. Because display refresh
|
||||||
|
; is disabled during input, it won't result in weird displays, and because
|
||||||
|
; partial numbers result in error display, then partial result won't lead to
|
||||||
|
; weird displays, just error displays.
|
||||||
|
;
|
||||||
|
; When input mode begins, we change Z to point to R3 (the first digit we
|
||||||
|
; receive) and we decrease the Z pointer after every digit we receive. When we
|
||||||
|
; receive the last bit of the last digit and that we see that R30 is 0, we know
|
||||||
|
; that the next (and last) digit is the checksum.
|
||||||
|
|
||||||
|
.inc "avr.h"
|
||||||
|
.inc "tn254585.h"
|
||||||
|
.inc "tn45.h"
|
||||||
|
|
||||||
|
; pins
|
||||||
|
.equ RCLK 0 ; on PORTB
|
||||||
|
.equ SRCLK 3 ; on PORTB
|
||||||
|
.equ SER_DP 4 ; on PORTB
|
||||||
|
.equ INSER 1 ; on PORTB
|
||||||
|
|
||||||
|
; Let's begin!
|
||||||
|
|
||||||
|
.org 0x0000
|
||||||
|
RJMP MAIN
|
||||||
|
RJMP INT_INT0
|
||||||
|
RETI ; PCINT0
|
||||||
|
RETI ; TIMER1_COMPA
|
||||||
|
RETI ; TIMER1_OVF
|
||||||
|
RJMP INT_TIMER0_OVF
|
||||||
|
|
||||||
|
MAIN:
|
||||||
|
LDI R16, RAMEND&0xff
|
||||||
|
OUT SPL, R16
|
||||||
|
LDI R16, RAMEND}8
|
||||||
|
OUT SPH, R16
|
||||||
|
|
||||||
|
SBI DDRB, RCLK
|
||||||
|
SBI DDRB, SRCLK
|
||||||
|
SBI DDRB, SER_DP
|
||||||
|
|
||||||
|
; we generally keep SER_DP high to avoid lighting DP
|
||||||
|
SBI PORTB, SER_DP
|
||||||
|
|
||||||
|
; target delay: 600us. At 1Mhz, that's 75 ticks with a 1/8 prescaler.
|
||||||
|
LDI R16, 0x02 ; CS01, 1/8 prescaler
|
||||||
|
OUT TCCR0B, R16
|
||||||
|
LDI R16, 0xb5 ; TOP - 75 ticks
|
||||||
|
OUT TCNT0, R16
|
||||||
|
|
||||||
|
; Enable TIMER0_OVF
|
||||||
|
IN R16, TIMSK
|
||||||
|
ORI R16, 0x02 ; TOIE0
|
||||||
|
OUT TIMSK, R16
|
||||||
|
|
||||||
|
; Generate interrupt on rising edge of INT0
|
||||||
|
IN R16, MCUCR
|
||||||
|
ORI R16, 0b00000011 ; ISC00 + ISC01
|
||||||
|
OUT MCUCR, R16
|
||||||
|
IN R16, GIMSK
|
||||||
|
ORI R16, 0b01000000 ; INT0
|
||||||
|
OUT GIMSK, R16
|
||||||
|
|
||||||
|
; we never use indirect addresses above 0xff through Z and never use
|
||||||
|
; R31 in other situations. We can set it once and forget about it.
|
||||||
|
CLR R31 ; high Z
|
||||||
|
|
||||||
|
; put 4321 in R2-5
|
||||||
|
CLR R30 ; low Z
|
||||||
|
LDI R16, 0x04
|
||||||
|
ST Z+, R16 ; 4
|
||||||
|
DEC R16
|
||||||
|
ST Z+, R16 ; 3
|
||||||
|
DEC R16
|
||||||
|
ST Z+, R16 ; 2
|
||||||
|
DEC R16
|
||||||
|
ORI R16, 0b00010000 ; DP
|
||||||
|
ST Z, R16 ; 1
|
||||||
|
CLR R30 ; replace Z to 0
|
||||||
|
|
||||||
|
SEI
|
||||||
|
|
||||||
|
LOOP:
|
||||||
|
RCALL INPT_CHK ; verify that we shouldn't enter input mode
|
||||||
|
SBIC GPIOR0, 0 ; refesh flag cleared? skip next
|
||||||
|
RCALL RDISP
|
||||||
|
RJMP LOOP
|
||||||
|
|
||||||
|
; ***** DISPLAY *****
|
||||||
|
|
||||||
|
; refresh display with current number
|
||||||
|
RDISP:
|
||||||
|
; First things first: setup the timer for the next time
|
||||||
|
LDI R16, 0xb5 ; TOP - 75 ticks
|
||||||
|
OUT TCNT0, R16
|
||||||
|
CBI GPIOR0, 0 ; Also, clear the refresh flag
|
||||||
|
|
||||||
|
; Let's begin with the display selector. We select one display at once
|
||||||
|
; (not ready for multi-display refresh operations yet). Let's decode our
|
||||||
|
; binary value from R30 into R16.
|
||||||
|
MOV R6, R30
|
||||||
|
INC R6 ; we need values 1-4, not 0-3
|
||||||
|
LDI R16, 0x01
|
||||||
|
RDISP1:
|
||||||
|
DEC R6
|
||||||
|
BREQ RDISP2 ; == 0? we're finished
|
||||||
|
LSL R16
|
||||||
|
RJMP RDISP1
|
||||||
|
|
||||||
|
; select a digit to display
|
||||||
|
; we do so in a clever way: our registers just happen to be in SRAM
|
||||||
|
; locations 0x00, 0x01, 0x02 and 0x03. Handy eh!
|
||||||
|
RDISP2:
|
||||||
|
LD R18, Z+ ; Indirect load of Z into R18 then increment
|
||||||
|
CPI R30, 4
|
||||||
|
BRCS RDISP3 ; lower than 4 ? don't reset
|
||||||
|
CLR R30 ; not lower than 4? reset
|
||||||
|
|
||||||
|
; in the next step, we're going to join R18 and R16 together, but
|
||||||
|
; before we do, we have one thing to process: R18's 5th bit. If it's
|
||||||
|
; high, it means that DP is highlighted. We have to store this
|
||||||
|
; information in R6 and use it later. Also, we have to clear the higher
|
||||||
|
; bits of R18.
|
||||||
|
RDISP3:
|
||||||
|
SBRC R18, 4 ; 5th bit cleared? skip next
|
||||||
|
INC R6 ; if set, then set R6 as well
|
||||||
|
ANDI R18, 0xf ; clear higher bits
|
||||||
|
|
||||||
|
; Now we have our display selector in R16 and our digit to display in
|
||||||
|
; R18. We want it all in R18.
|
||||||
|
SWAP R18 ; digit goes in high "nibble"
|
||||||
|
OR R18, R16
|
||||||
|
|
||||||
|
; While we send value to the shift register, SER_DP will change.
|
||||||
|
; Because we want to avoid falsely lighting DP, we need to disable
|
||||||
|
; output (disable OE) while that happens. This is why we set RCLK,
|
||||||
|
; which is wired to OE too, HIGH (OE disabled) at the beginning of
|
||||||
|
; the SR operation.
|
||||||
|
;
|
||||||
|
; Because RCLK was low before, this triggers a "buffer clock" on
|
||||||
|
; the SR, but it doesn't matter because the value that was there
|
||||||
|
; before has just been invalidated.
|
||||||
|
SBI PORTB, RCLK ; high
|
||||||
|
RCALL SENDSR
|
||||||
|
; Flush out the buffer with RCLK
|
||||||
|
CBI PORTB, RCLK ; OE enabled, but SR buffer isn't flushed
|
||||||
|
NOP
|
||||||
|
SBI PORTB, RCLK ; SR buffer flushed, OE disabled
|
||||||
|
NOP
|
||||||
|
CBI PORTB, RCLK ; OE enabled
|
||||||
|
|
||||||
|
; We're finished! Oh no wait, one last thing: should we highlight DP?
|
||||||
|
; If we should, then we should keep SER_DP low rather than high for this
|
||||||
|
; SR round.
|
||||||
|
SBI PORTB, SER_DP ; SER_DP generally kept high
|
||||||
|
SBRC R6, 0 ; R6 is cleared? skip DP set
|
||||||
|
CBI PORTB, SER_DP ; SER_DP low highlight DP
|
||||||
|
|
||||||
|
RET ; finished for real this time!
|
||||||
|
|
||||||
|
; send R18 to shift register.
|
||||||
|
; We send highest bits first so that QH is the MSB and QA is the LSB
|
||||||
|
; low bits (QD - QA) control display's power
|
||||||
|
; high bits (QH - QE) select the glyph
|
||||||
|
SENDSR:
|
||||||
|
LDI R16, 8 ; we will loop 8 times
|
||||||
|
CBI PORTB, SER_DP ; low
|
||||||
|
SBRC R18, 7 ; if latest bit isn't cleared, set SER_DP high
|
||||||
|
SBI PORTB, SER_DP ; high
|
||||||
|
RCALL TOGCP
|
||||||
|
LSL R18 ; shift our data left
|
||||||
|
DEC R16
|
||||||
|
BRNE SENDSR+2 ; not zero yet? loop! (+2 to avoid reset)
|
||||||
|
RET
|
||||||
|
|
||||||
|
; toggle SRCLK, waiting 1us between pin changes
|
||||||
|
TOGCP:
|
||||||
|
CBI PORTB, SRCLK ; low
|
||||||
|
NOP ; At 1Mhz, this is enough for 1us
|
||||||
|
SBI PORTB, SRCLK ; high
|
||||||
|
RET
|
||||||
|
|
||||||
|
; ***** INPUT MODE *****
|
||||||
|
|
||||||
|
; check whether we should enter input mode and enter it if needed
|
||||||
|
INPT_CHK:
|
||||||
|
SBIS GPIOR0, 1 ; did we just trigger INT_INT0?
|
||||||
|
RET ; no? return
|
||||||
|
; yes? continue in input mode
|
||||||
|
|
||||||
|
; Initialize input mode and start the loop
|
||||||
|
INPT_BEGIN:
|
||||||
|
SBI GPIOR0, 4 ; enable input mode
|
||||||
|
CBI GPIOR0, 1 ; The first trigger was an empty one
|
||||||
|
|
||||||
|
; At 1/8 prescaler, a "full" counter overflow is 2048us. That sounds
|
||||||
|
; about right for an input timeout. So we co the easy route and simply
|
||||||
|
; clear TCNT0 whenever we want to reset the timer
|
||||||
|
OUT TCNT0, R5 ; R5 == 0
|
||||||
|
CBI GPIOR0, 0 ; clear refresh flag in case it was just set
|
||||||
|
LDI R30, 0x04 ; make Z point on R3+1 (we use pre-decrement)
|
||||||
|
LDI R18, 0x01 ; initialize input buffer
|
||||||
|
|
||||||
|
; loop in input mode. When in input mode, we don't refresh the display, we use
|
||||||
|
; all our processing power to process input.
|
||||||
|
INPT_LOOP:
|
||||||
|
RCALL INPT_READ
|
||||||
|
|
||||||
|
; Check whether we've reached timeout
|
||||||
|
SBIC GPIOR0, 0 ; refesh flag cleared? skip next
|
||||||
|
RCALL INPT_TIMEOUT
|
||||||
|
|
||||||
|
SBIC GPIOR0, 4 ; input mode cleared? skip next, to INPT_END
|
||||||
|
RJMP INPT_LOOP ; not cleared? loop
|
||||||
|
|
||||||
|
INPT_END:
|
||||||
|
; We received all our date or reached timeout. let's go back in normal
|
||||||
|
; mode.
|
||||||
|
CLR R30 ; Ensure Z isn't out of bounds
|
||||||
|
SBI GPIOR0, 0 ; set refresh flag so we start refreshing now
|
||||||
|
RET
|
||||||
|
|
||||||
|
; Read, if needed, the last received bit
|
||||||
|
INPT_READ:
|
||||||
|
SBIS GPIOR0, 1
|
||||||
|
RET ; flag cleared? nothing to do
|
||||||
|
|
||||||
|
; Flag is set, we have to read
|
||||||
|
CBI GPIOR0, 1 ; unset flag
|
||||||
|
LSL R18
|
||||||
|
SBIC GPIOR0, 2 ; data flag cleared? skip next
|
||||||
|
INC R18
|
||||||
|
|
||||||
|
; Now, let's check if we have our 5 digits
|
||||||
|
SBRC R18, 5 ; 6th bit cleared? nothing to do
|
||||||
|
RCALL INPT_PUSH
|
||||||
|
|
||||||
|
OUT TCNT0, R5 ; clear timeout counter
|
||||||
|
|
||||||
|
RET
|
||||||
|
|
||||||
|
; Push the digit currently in R18 in Z and reset R18.
|
||||||
|
INPT_PUSH:
|
||||||
|
ANDI R18, 0b00011111 ; Remove 6th bit flag
|
||||||
|
|
||||||
|
TST R30 ; is R30 zero?
|
||||||
|
BREQ INPT_CHECKSUM ; yes? it means we're at checksum phase.
|
||||||
|
|
||||||
|
; Otherwise, its a regular digit push
|
||||||
|
ST -Z, R18
|
||||||
|
LDI R18, 0x01
|
||||||
|
RET
|
||||||
|
|
||||||
|
INPT_CHECKSUM:
|
||||||
|
CBI GPIOR0, 4 ; clear input mode, whether we error or not
|
||||||
|
MOV R16, R0
|
||||||
|
ADD R16, R1
|
||||||
|
ADD R16, R2
|
||||||
|
ADD R16, R3
|
||||||
|
; only consider the first 5 bits of the checksum since we can't receive
|
||||||
|
; more. Otherwise, we couldn't possibly validate a value like 9999
|
||||||
|
ANDI R16, 0b00011111
|
||||||
|
CP R16, R18
|
||||||
|
BRNE INPT_ERROR
|
||||||
|
RET
|
||||||
|
|
||||||
|
INPT_TIMEOUT:
|
||||||
|
CBI GPIOR0, 4 ; timeout reached, clear input flag
|
||||||
|
; continue to INPT_ERROR
|
||||||
|
|
||||||
|
INPT_ERROR:
|
||||||
|
LDI R16, 0x0c ; some weird digit
|
||||||
|
MOV R0, R16
|
||||||
|
MOV R1, R16
|
||||||
|
MOV R2, R16
|
||||||
|
MOV R3, R16
|
||||||
|
RET
|
||||||
|
|
||||||
|
; ***** INTERRUPTS *****
|
||||||
|
|
||||||
|
; Record received bit
|
||||||
|
; The main loop has to be fast enough to process that bit before we receive the
|
||||||
|
; next one!
|
||||||
|
; no SREG fiddling because no SREG-modifying instruction
|
||||||
|
INT_INT0:
|
||||||
|
CBI GPIOR0, 2 ; clear received data
|
||||||
|
SBIC PINB, INSER ; INSER clear? skip next
|
||||||
|
SBI GPIOR0, 2 ; INSER set? record this
|
||||||
|
SBI GPIOR0, 1 ; indicate that we've received a bit
|
||||||
|
RETI
|
||||||
|
|
||||||
|
; Set refresh flag whenever timer0 overflows
|
||||||
|
; no SREG fiddling because no SREG-modifying instruction
|
||||||
|
INT_TIMER0_OVF:
|
||||||
|
SBI GPIOR0, 0
|
||||||
|
RETI
|
||||||
|
|
||||||
|
|
BIN
tools/tests/avra/seg7multiplex.expected
Normal file
BIN
tools/tests/avra/seg7multiplex.expected
Normal file
Binary file not shown.
@ -14,3 +14,13 @@ rcall baz
|
|||||||
baz:
|
baz:
|
||||||
out 0x2e, r12
|
out 0x2e, r12
|
||||||
in r0, 0x9
|
in r0, 0x9
|
||||||
|
cbr r31, 0xff
|
||||||
|
sbis 22, 5
|
||||||
|
ser r19
|
||||||
|
bset 4
|
||||||
|
bclr 7
|
||||||
|
call foo
|
||||||
|
jmp bar
|
||||||
|
mov r6, r30
|
||||||
|
lsl r3
|
||||||
|
tst r12
|
||||||
|
Binary file not shown.
18
tools/tests/avra/testldst.asm
Normal file
18
tools/tests/avra/testldst.asm
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
ld r0, X
|
||||||
|
ld r1, Y
|
||||||
|
ld r2, Z
|
||||||
|
ld r3, X+
|
||||||
|
ld r4, Y+
|
||||||
|
ld r5, Z+
|
||||||
|
ld r6, -X
|
||||||
|
ld r7, -Y
|
||||||
|
ld r8, -Z
|
||||||
|
st X, r9
|
||||||
|
st Y, r10
|
||||||
|
st Z, r11
|
||||||
|
st X+, r12
|
||||||
|
st Y+, r13
|
||||||
|
st Z+, r14
|
||||||
|
st -X, r15
|
||||||
|
st -Y, r16
|
||||||
|
st -Z, r17
|
2
tools/tests/avra/testldst.expected
Normal file
2
tools/tests/avra/testldst.expected
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<0C>€ €=<3D>I<EFBFBD>Q<EFBFBD>n<EFBFBD>z<EFBFBD>‚<EFBFBD>ś’¨‚°‚Í’Ů’á’ţ’
|
||||||
|
““
|
Loading…
Reference in New Issue
Block a user