collapseos/apps/zasm/avr.asm

498 lines
10 KiB
NASM
Raw Normal View History

2019-12-14 09:38:40 +11:00
; Same thing as instr.asm, but for AVR instructions
; *** Instructions table ***
; List of mnemonic names separated by a null terminator. Their index in the
; list is their ID. Unlike in zasm, not all mnemonics have constant associated
; to it because it's generally not needed. This list is grouped by argument
; categories, and then alphabetically. Categories are ordered so that the 8bit
; opcodes come first, then the 16bit ones. 0xff ends the chain
instrNames:
; Branching instructions. They are all shortcuts to BRBC/BRBS. These are not in
; alphabetical order, but rather in "bit order". All "bit set" instructions
; first (10th bit clear), then all "bit clear" ones (10th bit set). Inside this
; order, they're then in "sss" order (bit number alias for BRBC/BRBS).
2019-12-14 14:49:15 +11:00
.db "BRCS", 0
.db "BREQ", 0
.db "BRMI", 0
.db "BRVS", 0
.db "BRLT", 0
.db "BRHS", 0
.db "BRTS", 0
.db "BRIE", 0
.db "BRCC", 0
.db "BRNE", 0
.db "BRPL", 0
.db "BRVC", 0
.db "BRGE", 0
.db "BRHC", 0
.db "BRTC", 0
.db "BRID", 0
.equ I_BRBS 16
.db "BRBS", 0
.db "BRBC", 0
; Rd(5) + Rr(5) (from here, instrUpMasks1)
2019-12-14 14:49:15 +11:00
.equ I_ADC 18
2019-12-14 09:38:40 +11:00
.db "ADC", 0
.db "ADD", 0
.db "AND", 0
.db "CLR", 0
.db "CP", 0
.db "CPC", 0
.db "CPSE", 0
.db "EOR", 0
.db "MOV", 0
.db "MUL", 0
.db "OR", 0
.db "SBC", 0
.db "SUB", 0
.equ I_ANDI 31
.db "ANDI", 0
.db "CPI", 0
.db "LDI", 0
.db "ORI", 0
.db "SBCI", 0
.db "SBR", 0
.db "SUBI", 0
2019-12-15 01:33:46 +11:00
.equ I_BLD 38
.db "BLD", 0
.db "BST", 0
.db "SBRC", 0
.db "SBRS", 0
2019-12-15 04:28:27 +11:00
.equ I_RCALL 42
.db "RCALL", 0
.db "RJMP", 0
; no arg (from here, instrUpMasks2)
2019-12-15 04:28:27 +11:00
.equ I_BREAK 44
2019-12-14 09:38:40 +11:00
.db "BREAK", 0
.db "CLC", 0
.db "CLH", 0
.db "CLI", 0
.db "CLN", 0
.db "CLS", 0
.db "CLT", 0
.db "CLV", 0
.db "CLZ", 0
.db "EICALL", 0
.db "EIJMP", 0
.db "ICALL", 0
.db "IJMP", 0
.db "NOP", 0
.db "RET", 0
.db "RETI", 0
.db "SEC", 0
.db "SEH", 0
.db "SEI", 0
.db "SEN", 0
.db "SES", 0
.db "SET", 0
.db "SEV", 0
.db "SEZ", 0
.db "SLEEP", 0
.db "WDR", 0
; Rd(5)
2019-12-15 04:28:27 +11:00
.equ I_ASR 70
2019-12-14 09:38:40 +11:00
.db "ASR", 0
.db "COM", 0
.db "DEC", 0
.db "INC", 0
.db "LAC", 0
.db "LAS", 0
.db "LAT", 0
.db "LSR", 0
.db "NEG", 0
.db "POP", 0
.db "PUSH", 0
.db "ROR", 0
.db "SWAP", 0
.db "XCH", 0
.db 0xff
; 8-bit constant masks associated with each instruction. In the same order as
; in instrNames
instrUpMasks1:
; Rd(5) + Rd(5): XXXXXXrd ddddrrrr
.db 0b00011100 ; ADC
.db 0b00001100 ; ADD
.db 0b00100000 ; AND
.db 0b00100100 ; CLR
.db 0b00010100 ; CP
.db 0b00000100 ; CPC
.db 0b00010000 ; CPSE
.db 0b00100100 ; EOR
.db 0b00101100 ; MOV
.db 0b10011100 ; MUL
.db 0b00101000 ; OR
.db 0b00001000 ; SBC
.db 0b00011000 ; SUB
2019-12-15 01:33:46 +11:00
; Rd(4) + K(8): XXXXKKKK ddddKKKK
.db 0b01110000 ; ANDI
.db 0b00110000 ; CPI
.db 0b11100000 ; LDI
.db 0b01100000 ; ORI
.db 0b01000000 ; SBCI
.db 0b01100000 ; SBR
.db 0b01010000 ; SUBI
2019-12-15 01:33:46 +11:00
; Rd(5) + bit: XXXXXXXd ddddXbbb: lonely bit in LSB is 0 in all cases, so we
; ignore it.
.db 0b11111000 ; BLD
.db 0b11111010 ; BST
.db 0b11111100 ; SBRC
.db 0b11111110 ; SBRS
2019-12-15 04:28:27 +11:00
; k(12): XXXXkkkk kkkkkkkk
.db 0b11010000 ; RCALL
.db 0b11000000 ; RJMP
2019-12-14 09:38:40 +11:00
; 16-bit constant masks associated with each instruction. In the same order as
; in instrNames
instrUpMasks2:
; no arg
.db 0b10010101, 0b10011000 ; BREAK
.db 0b10010100, 0b10001000 ; CLC
.db 0b10010100, 0b11011000 ; CLH
.db 0b10010100, 0b11111000 ; CLI
.db 0b10010100, 0b10101000 ; CLN
.db 0b10010100, 0b11001000 ; CLS
.db 0b10010100, 0b11101000 ; CLT
.db 0b10010100, 0b10111000 ; CLV
.db 0b10010100, 0b10011000 ; CLZ
.db 0b10010101, 0b00011001 ; EICALL
.db 0b10010100, 0b00011001 ; EIJMP
.db 0b10010101, 0b00001001 ; ICALL
.db 0b10010100, 0b00001001 ; IJMP
.db 0b00000000, 0b00000000 ; NOP
.db 0b10010101, 0b00001000 ; RET
.db 0b10010101, 0b00011000 ; RETI
.db 0b10010100, 0b00001000 ; SEC
.db 0b10010100, 0b01011000 ; SEH
.db 0b10010100, 0b01111000 ; SEI
.db 0b10010100, 0b00101000 ; SEN
.db 0b10010100, 0b01001000 ; SES
.db 0b10010100, 0b01101000 ; SET
.db 0b10010100, 0b00111000 ; SEV
.db 0b10010100, 0b00011000 ; SEZ
.db 0b10010101, 0b10001000 ; SLEEP
.db 0b10010101, 0b10101000 ; WDR
; Rd(5): XXXXXXXd ddddXXXX
.db 0b10010100, 0b00000101 ; ASR
.db 0b10010100, 0b00000000 ; COM
.db 0b10010100, 0b00001010 ; DEC
.db 0b10010100, 0b00000011 ; INC
.db 0b10010010, 0b00000110 ; LAC
.db 0b10010010, 0b00000101 ; LAS
.db 0b10010010, 0b00000111 ; LAT
.db 0b10010100, 0b00000110 ; LSR
.db 0b10010100, 0b00000001 ; NEG
.db 0b10010000, 0b00001111 ; POP
.db 0b10010010, 0b00001111 ; PUSH
.db 0b10010100, 0b00000111 ; ROR
.db 0b10010100, 0b00000010 ; SWAP
.db 0b10010010, 0b00000100 ; XCH
; Same signature as getInstID in instr.asm
; Reads string in (HL) and returns the corresponding ID (I_*) in A. Sets Z if
; there's a match.
getInstID:
push bc
push hl
push de
ex de, hl ; DE makes a better needle
; haystack. -1 because we inc HL at the beginning of the loop
ld hl, instrNames-1
ld b, 0xff ; index counter
.loop:
inc b
inc hl
ld a, (hl)
inc a ; check if 0xff
jr z, .notFound
call strcmpIN
jr nz, .loop
; found!
ld a, b ; index
cp a ; ensure Z
.end:
pop de
pop hl
pop bc
ret
.notFound:
dec a ; unset Z
jr .end
; Same signature as parseInstruction in instr.asm
; Parse instruction specified in A (I_* const) with args in I/O and write
; resulting opcode(s) in I/O.
; Sets Z on success. On error, A contains an error code (ERR_*)
parseInstruction:
; BC, during .spit, is ORred to the spitted opcode.
ld bc, 0
2019-12-15 01:33:46 +11:00
; Save Instr ID in D, which is less volatile than A. In almost all
; cases, we fetch the opcode constant at the end of the processing.
ld d, a
2019-12-14 14:49:15 +11:00
cp I_ADC
jp c, .BR
cp I_ANDI
2019-12-14 09:38:40 +11:00
jr c, .spitRd5Rr5
2019-12-15 01:33:46 +11:00
cp I_BLD
jr c, .spitRdK8
2019-12-15 04:28:27 +11:00
cp I_RCALL
2019-12-15 01:33:46 +11:00
jr c, .spitRdBit
2019-12-15 04:28:27 +11:00
cp I_BREAK
jr c, .spitK12
2019-12-14 09:38:40 +11:00
cp I_ASR
jr c, .spitNoArg
; spitRd5
call .readR5
ret nz
call .placeRd
; continue to .spitNoArg
.spitNoArg:
call .getUp2
2019-12-15 04:28:27 +11:00
jp .spit
2019-12-14 09:38:40 +11:00
.spitRd5Rr5:
call .readR5
ret nz
call .placeRd
call readComma
call .readR5
ret nz
push af ; --> lvl 1
; let's start with the 4 lower bits
and 0xf
or c
; We now have our LSB in A. Let's spit it now.
call ioPutB
pop af ; <-- lvl 1
; and now that last high bit, currently bit 4, which must become bit 1
and 0b00010000
rra \ rra \ rra
or b
ld b, a
2019-12-14 14:49:15 +11:00
call .getUp1
2019-12-14 09:38:40 +11:00
; now that's our MSB
jr .spitMSB
.spitRdK8:
call .readR4
ret nz
call .placeRd
call readComma
call readWord
call parseExpr
ret nz
ld a, 0xff
call .IX2A
ret nz
push af ; --> lvl 1
; let's start with the 4 lower bits
and 0xf
or c
; We now have our LSB in A. Let's spit it now.
call ioPutB
pop af ; <-- lvl 1
; and now those high 4 bits
and 0xf0
rra \ rra \ rra \ rra
ld b, a
call .getUp1
2019-12-15 01:33:46 +11:00
jr .spitMSB
.spitRdBit:
call .readR5
ret nz
call .placeRd
call readComma
ret nz
call .readBit
ret nz
; LSB is in A and is ready to go
call ioPutB
call .getUp1
jr .spitMSB
2019-12-15 04:28:27 +11:00
.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 .BR. 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
2019-12-14 09:38:40 +11:00
.spit:
; LSB is spit *before* MSB
inc hl
ld a, (hl)
or c
call ioPutB
dec hl
.spitMSB:
ld a, (hl)
or b
call ioPutB
xor a ; ensure Z, set success
ret
2019-12-14 14:49:15 +11:00
; Spit a branching mnemonic.
.BR:
; While we have our index in A, let's settle B straight: Our base
; upcode is 0b11110000 for "bit set" types and 0b11110100 for "bit
; clear" types. However, we'll have 2 left shift operation done on B
; later on, so we need those bits shifted right.
ld b, 0b111100
cp I_BRBS
jr z, .rdBRBS
jr nc, .rdBRBC
; We have an alias. Our "sss" value is index & 0b111
; Before we get rid of that 3rd bit, let's see, is it set? if yes, we'll
; want to increase B
bit 3, a
jr z, .skip1 ; 3rd bit unset
inc b
.skip1:
and 0b111
ld c, a
.spitBR2:
call readWord
ret nz
call parseExpr
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
sla a \ rl b
sla a \ rl b
; 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.
or c
call ioPutB
; Good! MSB now. B is already good to go.
ld a, b
jp ioPutB
.rdBRBC:
; In addition to reading "sss", we also need to inc B so that our base
; upcode becomes 0b111101
inc b
.rdBRBS:
2019-12-15 01:33:46 +11:00
call .readBit
2019-12-14 14:49:15 +11:00
ret nz
call readComma
ret nz
jr .spitBR2
2019-12-14 09:38:40 +11:00
; local routines
; place number in A in BC at position .......d dddd....
; BC is assumed to be 0
.placeRd:
sla a \ rla \ rla \ rla ; last RLA might set carry
rl b
ld c, a
ret
2019-12-14 14:49:15 +11:00
2019-12-15 01:33:46 +11:00
; Fetch a 8-bit upcode specified by instr index in D and set that upcode in HL
2019-12-14 14:49:15 +11:00
.getUp1:
2019-12-15 01:33:46 +11:00
ld a, d
2019-12-14 14:49:15 +11:00
sub I_ADC
ld hl, instrUpMasks1
jp addHL
2019-12-15 01:33:46 +11:00
; Fetch a 16-bit upcode specified by instr index in D and set that upcode in HL
2019-12-14 09:38:40 +11:00
.getUp2:
2019-12-15 01:33:46 +11:00
ld a, d
2019-12-14 09:38:40 +11:00
sub I_BREAK
sla a ; A * 2
ld hl, instrUpMasks2
jp addHL
.readR4:
call .readR5
ret nz
; has to be in the 16-31 range
sub 0x10
jp c, unsetZ
cp a ; ensure Z
ret
2019-12-14 09:38:40 +11:00
; read a rXX argument and return register number in A.
; Set Z for success.
.readR5:
call readWord
ld a, (hl)
call upcase
cp 'R'
ret nz ; not a register
inc hl
call parseDecimal
ret nz
2019-12-14 14:49:15 +11:00
ld a, 31
jr .IX2A
2019-12-15 01:33:46 +11:00
.readBit:
call readWord
ret nz
call parseExpr
ld a, 7
call .IX2A
ret nz
or c
ld c, a
cp a ; ensure Z
ret
2019-12-14 14:49:15 +11:00
; Put IX's LSB into A and, additionally, ensure that the new value is <=
; than what was previously in A.
; Z for success.
.IX2A:
2019-12-14 09:38:40 +11:00
push ix \ pop hl
2019-12-14 14:49:15 +11:00
cp l
jp c, unsetZ ; A < L
2019-12-14 09:38:40 +11:00
ld a, h
or a
ret nz ; should be zero
ld a, l
2019-12-14 14:49:15 +11:00
; Z set from "or a"
2019-12-14 09:38:40 +11:00
ret