Files
SyncHome/trunk/workspace/AVR-Computer/avr-vic20/AVRCODE/6502cpu.asm
2023-03-13 08:36:51 +00:00

795 lines
15 KiB
NASM

;TODO, sort out overflow flag
;DEBUG STUFF
.equ ZP_offset = 0x00 ;offset for Zero page, set to 0 for non debug
.MACRO cp1
; clc
cp @0,@1
brcs PC+3 ;invert carry
sec
rjmp PC+2
clc
in Pstat1,SREG ;save status bits
.ENDMACRO
.MACRO move_to_reg
mov r17,Pstat1 ;TEST ME
tst @0 ;all this is need becase the Z and N flags get changed, but dont in a mov operation
;add zero should test register
in r16,SREG
;N flag
sbrs r16,2
cbr r17,EXP2(Ns)
sbrc r16,2
sbr r17,EXP2(Ns)
;Z flag
sbrs r16,1
cbr r17,EXP2(Zs)
sbrc r16,1
sbr r17,EXP2(Zs)
mov Pstat1,r17
.ENDMACRO
;-----------01-----------------------------------------------------------
op_ORA: ;OR
rcall get_value_01
out SREG,Pstat1 ;still need to do this to preserve carry
or A,retL ;perform OR function
in Pstat1,SREG ;save status bits
rjmp next_op
op_AND: ;AND
rcall get_value_01
out SREG,Pstat1
and A,retL
in Pstat1,SREG ;save status bits
rjmp next_op
op_EOR: ;Exclusive or
rcall get_value_01
out SREG,Pstat1
eor A,retL
in Pstat1,SREG ;save status bits
rjmp next_op
op_ADC: ;ADD with carry
rcall get_value_01
out SREG,Pstat1 ;restor status reg for carry op
ldi r17,1
; in r16,SREG
; sbrc r16,0
; add A,r17
adc A,retL
in r16,SREG
mov Pstat1,r16 ;save status bits
mov r17,Pstat2
;sort out O(V) flag
sbrs r16,3
cbr r17,EXP2(Os)
sbrc r16,3
sbr r17,EXP2(Os)
;sbr r17,EXP2(Os)
mov Pstat2,r17 ;copy back
rjmp next_op
op_STA: ;Store A to mem
rcall get_value_01 ;Dont acctualy need to get that value, but called to work out address for write
mov retL,A
st_cpuZ A
rjmp next_op
op_LDA: ;Load A with mem
rcall get_value_01
mov A,retL
move_to_reg A
rjmp next_op
op_CMP: ;Compare A and mem
rcall get_value_01
cp1 A,retL
rjmp next_op
op_SBC: ;Subtract A-M with carry
rcall get_value_01
; mov r18,A ;TEST
out SREG,Pstat1 ;restor status reg for carry op
brcs PC+3 ;Strange subtraction as works on an inverted carry
sec
rjmp PC+2
clc
; ldi r17,1
;---TEST
; in r16,SREG
; sbrc r16,0
; add A,r17
; SUB A,retL
SEZ ;needed because only clears Z if result <> 0!
sbc A,retL
brcs PC+3 ;again..it inverts the carry???
sec
rjmp PC+2
clc
in Pstat1,SREG ;save status bits
; sbc r18,retL
in r16,SREG
mov r17,Pstat2
;sort out O(V) flag
sbrs r16,3
cbr r17,EXP2(Os)
sbrc r16,3
sbr r17,EXP2(Os)
; sbr r17,EXP2(Os)
mov Pstat2,r17 ;copy back
rjmp next_op
;-----------10-----------------------------------------------------------
op_ASL: ;Logcial shift left
rcall get_value_10
lsl retL
in Pstat1,SREG ;save status bits
st_cpuZ retL
rjmp next_op
op_ROL: ;Left shit with caryy
rcall get_value_10
out SREG,Pstat1 ;restor status reg for carry op
rol retL
in Pstat1,SREG ;save status bits
st_cpuZ retL
rjmp next_op
op_LSR: ;Logcial shift right
rcall get_value_10
lsr retL
in Pstat1,SREG ;save status bits
st_cpuZ retL
rjmp next_op
op_ROR: ;Right shit with caryy
rcall get_value_10
out SREG,Pstat1 ;restor status reg for carry op
ror retL
in Pstat1,SREG ;save status bits
st_cpuZ retL
rjmp next_op
op_STX: ;Store X to memory
rcall get_value_10
mov retL,Xr
st_cpuZ retL
rjmp next_op
op_LDX: ;Load X with memory
rcall get_value_10
mov Xr,retL
move_to_reg Xr
rjmp next_op
op_DEC: ;dec memory
rcall get_value_10
out SREG,Pstat1
dec retL
in Pstat1,SREG
st_cpuZ retL
rjmp next_op
op_INC:
rcall get_value_10
out SREG,Pstat1
inc retL
in Pstat1,SREG
st_cpuZ retL
rjmp next_op
;-----------00-----------------------------------------------------------
;same as 10 but accum missing
op_BIT: ;This instructions is used to test if one or more bits are set in a target memory location.
;The mask pattern in A is ANDed with the value in memory to set or clear the zero flag,
;but the result is not kept. Bits 7 and 6 of the value from memory are copied into the N and V flags.
rcall get_value_10
out SREG,Pstat1
mov r16,A ;make copy
and r16,retL ;perform AND
in r16,SREG ;copy SREG for Z flag
;sort out N flag
sbrs retL,7
cbr r16,EXP2(Ns)
sbrc retL,7
sbr r16,EXP2(Ns)
mov Pstat1,r16 ;copy back
mov r16,Pstat2
;sort out O(V) flag
sbrs retL,6
cbr r16,EXP2(Os)
sbrc retL,6
sbr r16,EXP2(Os)
mov Pstat2,r16 ;copy back
rjmp next_op
op_JMP: ;Jump absolute
get_pc_mem r18
get_pc_mem r17
mov PL,r18
mov PH,r17
rjmp next_op
op_JMPI: ;Jump absolute indirect
get_pc_mem YL ;get indirect address
get_pc_mem YH
ld_cpuY PL ;load PC with indriect value
inc YL ;NOTE, not using Y+ because of CPU bug which does not carry the carry to high byte
ld_cpuY PH
rjmp next_op
op_STY: ;Store Y to memory
rcall get_value_10
mov retL,Yr
st_cpuZ retL
rjmp next_op
op_LDY:
rcall get_value_10
mov Yr,retL
move_to_reg Yr
rjmp next_op
op_CPY: ;Compare Y with memory
rcall get_value_10
cp1 Yr,retL
rjmp next_op
op_CPX: ;Compare X with memory
rcall get_value_10
cp1 Xr,retL
rjmp next_op
;--BRANCHES---
op_BPL: ;Branch if Positive (N = 0)
mov r16,Pstat1
sbrs r16,Ns
rjmp Branch_take
adiw PL,1 ;skip over address if no branch
rjmp next_op
op_BMI: ;Branch if minus (N = 1)
mov r16,Pstat1
sbrc r16,Ns
rjmp Branch_take
adiw PL,1 ;skip over address if no branch
rjmp next_op
op_BVC: ;Branch if (O = 0)
mov r16,Pstat2
sbrs r16,Os
rjmp Branch_take
adiw PL,1 ;skip over address if no branch
rjmp next_op
op_BVS: ;Branch if (O = 1)
mov r16,Pstat2
sbrc r16,Os
rjmp Branch_take
adiw PL,1 ;skip over address if no branch
rjmp next_op
op_BCC: ;Branch is Carry clear
mov r16,Pstat1
sbrs r16,Cs
rjmp Branch_take
adiw PL,1 ;skip over address if no branch
rjmp next_op
op_BCS:
mov r16,Pstat1
sbrc r16,Cs
rjmp Branch_take
adiw PL,1 ;skip over address if no branch
rjmp next_op
op_BNE: ;Branch is (Z=0)
mov r16,Pstat1
sbrs r16,Zs
rjmp Branch_take
adiw PL,1 ;skip over address if no branch
rjmp next_op
op_BEQ:
mov r16,Pstat1
sbrc r16,Zs
rjmp Branch_take
adiw PL,1 ;skip over address if no branch
rjmp next_op
op_BRK:
;TODO
ldi r16,'B'
rcall rs232_0_tx
nop
nop
rjmp next_op
op_JSRA:
get_pc_mem r17 ;get jump address
stack_push PH ;push address to stack, done here because accutaly save PC-1
stack_push PL
get_pc_mem r18 ;get high jump address
mov PL,r17
mov PH,r18
rjmp next_op
op_RTI:
stack_pop retL
rcall stack_decomp
stack_pop PL
stack_pop PH
nop ;TODO
nop
rjmp next_op
op_RTS:
stack_pop PL ;get return address from stack
stack_pop PH
adiw PL,1 ;add on to PC
rjmp next_op
;--------SPECIAL CASES and operations only on the accumulator---------
op_ASL_A: ;logical shift left
lsl A
in Pstat1,SREG ;save status bits
rjmp next_op
op_ROL_A: ;shift left with carry
out SREG,Pstat1 ;restor status reg for carry op
rol A
in Pstat1,SREG ;save status bits
rjmp next_op
op_LSR_A:
lsr A
in Pstat1,SREG ;save status bits
rjmp next_op
op_ROR_A:
out SREG,Pstat1 ;restor status reg for carry op
ror A
in Pstat1,SREG ;save status bits
rjmp next_op
op_STX_ZPY: ;Store X to memory
rcall get_value_ZP_idx_Y
mov retL,Xr
st_cpuZ retL
rjmp next_op
op_LDX_ZPY:
rcall get_value_ZP_idx_Y
mov Xr,retL
move_to_reg Xr
rjmp next_op
op_LDX_XY:
rcall get_value_ZP_abs_idx_Y
mov Xr,retL
move_to_reg Xr
rjmp next_op
op_PHP:
rcall stack_build
stack_push retL
rjmp next_op
op_PLP:
stack_pop retL
rcall stack_decomp
rjmp next_op
op_PHA:
stack_push A
rjmp next_op
op_PLA:
stack_pop A
move_to_reg A
rjmp next_op
op_DEY:
out SREG,Pstat1 ;restor SREG so carry isnt changed
dec Yr ;perform dec
in Pstat1,SREG ;save changed N and Z
rjmp next_op
op_TAY: ;Transfer A to Y
mov Yr,A ;
move_to_reg Yr
rjmp next_op
op_INY:
out SREG,Pstat1
inc Yr
in Pstat1,SREG
rjmp next_op
op_INX:
out SREG,Pstat1
inc Xr
in Pstat1,SREG
rjmp next_op
op_CLC:
mov r16,Pstat1
cbr r16,EXP2(Cs)
mov Pstat1,r16
rjmp next_op
op_SEC:
mov r16,Pstat1
sbr r16,EXP2(Cs)
mov Pstat1,r16
rjmp next_op
op_CLI:
mov r16,Pstat2
cbr r16,EXP2(Is)
mov Pstat2,r16
rjmp next_op
op_SEI:
mov r16,Pstat2
sbr r16,EXP2(Is)
mov Pstat2,r16
rjmp next_op
op_TYA: ;Transfer Y to A
mov A,Yr ;
move_to_reg A
rjmp next_op
op_CLV:
mov r16,Pstat2
cbr r16,EXP2(Os)
mov Pstat2,r16
rjmp next_op
op_CLD:
mov r16,Pstat2
cbr r16,EXP2(Ds)
mov Pstat2,r16
rjmp next_op
op_SED:
mov r16,Pstat2
sbr r16,EXP2(Ds)
mov Pstat2,r16
rjmp next_op
op_TXA: ;TRans X to A
mov A,Xr ;
move_to_reg A
rjmp next_op
op_TXS: ;transfer X to stack pointer
mov stack,Xr
rjmp next_op
op_TAX: ;Transfer A to X
mov Xr,A ;
move_to_reg Xr
rjmp next_op
op_TSX:
mov Xr,stack ;
move_to_reg Xr
rjmp next_op
op_DEX:
out SREG,Pstat1 ;restor SREG so carry isnt changed
dec Xr ;perform dec
in Pstat1,SREG ;save changed N and Z
rjmp next_op
op_NOP: ;nopys
nop
nop
rjmp next_op
;---------------------------
;---------------------------
;---------------------------
get_value_01: ;MEMORY type decoder for xxxxxx01 opcodes
andi retL,0b00111000 ;retL still contains opcode, but lsl by one, get mask of memory access type
;should be a fatser way of doing this with jump tables
;for now, order with most frequently used at top
cpi retL,0b00000000
breq get_value_ZP_inx_X_indirj
cpi retL,0b00001000
breq get_value_ZPj
cpi retL,0b00010000
breq get_value_imedj
cpi retL,0b00011000
breq get_value_absj
cpi retL,0b00100000
breq get_value_ZP_indir_idx_Yj
cpi retL,0b00101000
breq get_value_ZP_idx_Xj
cpi retL,0b00110000
breq get_value_ZP_abs_idx_Yj
cpi retL,0b00111000
breq get_value_ZP_abs_idx_Xj
;---------------------------------
get_value_10: ;MEMORY type decoder for xxxxxx10 opcodes
andi retL,0b00111000
cpi retL,0b00000000
breq get_value_imedj
cpi retL,0b00001000
breq get_value_ZPj
cpi retL,0b00010000
breq get_value_accumj
cpi retL,0b00011000
breq get_value_absj
cpi retL,0b00101000
breq get_value_ZP_idx_Xj
cpi retL,0b00111000
breq get_value_ZP_abs_idx_Xj
get_value_ZP_inx_X_indirj:
rjmp get_value_ZP_inx_X_indir
get_value_ZPj:
rjmp get_value_ZP
get_value_imedj:
rjmp get_value_imed
get_value_absj:
rjmp get_value_abs
get_value_ZP_indir_idx_Yj:
rjmp get_value_ZP_indir_idx_Y
get_value_ZP_idx_Xj:
rjmp get_value_ZP_idx_X
get_value_ZP_idx_Yj:
rjmp get_value_ZP_idx_Y
get_value_ZP_abs_idx_Yj:
rjmp get_value_ZP_abs_idx_Y
get_value_ZP_abs_idx_Xj:
rjmp get_value_ZP_abs_idx_X
get_value_accumj:
rjmp get_value_accum
;----------------------------
get_value_ZP_inx_X_indir: ;Zero page index X, indirect
ldi YH,ZP_offset ;Zero page, so high byte 0
get_pc_mem YL ;get next byte into YL
add YL,Xr
adc YH,zero_reg
ld_cpuYp ZL ;now load Z with indrected address Y
ld_cpuY ZH
ld_cpuZ retL ;load actual value!
ret
get_value_ZP: ;Zero Page
ldi ZH,ZP_offset ;Zero page..
get_pc_mem ZL ;Get pos in ZP
ld_cpuZ retL ;load value
ret
get_value_imed: ;immediate mode
get_pc_mem retL
ret
get_value_abs: ;get absolute (one byte)
get_pc_mem ZL
get_pc_mem ZH
ld_cpuZ retL
ret
get_value_ZP_indir_idx_Y: ;Zero page indirect, indexed Y
ldi YH,ZP_offset
get_pc_mem YL
ld_cpuYp ZL ;now load Z with indrected address Y
ld_cpuY ZH
add ZL,Yr ;add Y reg for indexing
adc ZH,zero_reg ;no wrap around on this one
ld_cpuZ retL ;load value
ret
get_value_ZP_idx_X: ;Zero page indexd X
ldi ZH,ZP_offset
get_pc_mem ZL
add ZL,Xr ;add index,
adc ZH,zero_reg
ld_cpuZ retL ;get value
ret
get_value_ZP_idx_Y: ;Zero page indexd Y (only used with LDX and STX)
ldi ZH,ZP_offset
get_pc_mem ZL
add ZL,Yr ;add index, with wrap around..humm TODO..?
adc ZH,zero_reg
ld_cpuZ retL ;get value
ret
get_value_ZP_abs_idx_Y: ;absolute, indexd with Y
get_pc_mem ZL ;get absolut value
get_pc_mem ZH
add ZL,Yr
adc ZH,zero_reg
ld_cpuZ retL
ret
get_value_ZP_abs_idx_X: ;absolute, indexd with X
get_pc_mem ZL ;get absolut value
get_pc_mem ZH
add ZL,Xr
adc ZH,zero_reg
ld_cpuZ retL
ret
get_value_accum: ;accumulator, Should not use this, there should be special cases for A ops
mov retL,A
ret
;----------------------------------------------------------------------
;----------------------------------------------------------------------
;Branch_take
;purpose: if a branch is sucessfull
;Takes:
;Give:
;----------------------------------------------------------------------
;----------------------------------------------------------------------
Branch_take:
get_pc_mem r18 ;get branch modifiyer
sbrc r18,7 ;gotta go signed 8 bit X 16 bit unsigned
ldi r17,0xff ;todo FIX, DONT USE R16
sbrs r18,7
ldi r17,0
add PL,r18
adc PH,r17
rjmp next_op
;----------------------------------------------------------------------
;----------------------------------------------------------------------
;stack_build TODO: make a macro
;purpose: Here teh stack is stored as 2 bytes for speed, in order to store on the stack
;it must be built into one byte
;Takes:
;Give: retL as built stack
;----------------------------------------------------------------------
;----------------------------------------------------------------------
stack_build:
mov r16,Pstat1 ;copy byte for bottom 3 bits (C,Z,N)
andi r16,0b00000111 ;clear top bits
mov r17,Pstat2
andi r17,0b11111000
or r16,r17
mov retL,r16
ret
;----------------------------------------------------------------------
;----------------------------------------------------------------------
;stack_decomp TODO: make a macro
;purpose: reverse of stack_build
;Takes: retL
;Give:
;----------------------------------------------------------------------
;----------------------------------------------------------------------
stack_decomp:
mov Pstat1,retL
mov Pstat2,retL
ret
op_INVALID:
ldi r16,'V'
rcall rs232_0_tx
rjmp next_op
p: rjmp p