First commit, still porting Odin code
This commit is contained in:
commit
102c08569a
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusted-nes"
|
||||||
|
version = "0.1.0"
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "rusted-nes"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
15
basic.asm
Normal file
15
basic.asm
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
; basic.s - Simple 6502 assembly example for cc65
|
||||||
|
|
||||||
|
.segment "CODE"
|
||||||
|
.proc main
|
||||||
|
; Initialize some registers
|
||||||
|
ldx #0 ; X = 0
|
||||||
|
ldy #10 ; Y = 10
|
||||||
|
|
||||||
|
loop:
|
||||||
|
inx ; Increment X
|
||||||
|
dey ; Decrement Y
|
||||||
|
bne loop ; Branch if Y not equal to 0
|
||||||
|
|
||||||
|
rts ; Return from subroutine
|
||||||
|
.endproc
|
839
full.asm
Normal file
839
full.asm
Normal file
@ -0,0 +1,839 @@
|
|||||||
|
; 6502 Instruction Testing File
|
||||||
|
; Contains all official and unofficial instructions with all valid addressing modes
|
||||||
|
|
||||||
|
.org $8000
|
||||||
|
|
||||||
|
; ADC - Add With Carry
|
||||||
|
adc_tests:
|
||||||
|
ADC #$10 ; Immediate
|
||||||
|
ADC $10 ; Zero Page
|
||||||
|
ADC $10,X ; Zero Page,X
|
||||||
|
ADC $1000 ; Absolute
|
||||||
|
ADC $1000,X ; Absolute,X
|
||||||
|
ADC $1000,Y ; Absolute,Y
|
||||||
|
ADC ($10,X) ; Indirect,X
|
||||||
|
ADC ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; ALR (unofficial) - AND + LSR
|
||||||
|
alr_tests:
|
||||||
|
ALR #$10 ; Immediate
|
||||||
|
|
||||||
|
; AND - Logical AND
|
||||||
|
and_tests:
|
||||||
|
AND #$10 ; Immediate
|
||||||
|
AND $10 ; Zero Page
|
||||||
|
AND $10,X ; Zero Page,X
|
||||||
|
AND $1000 ; Absolute
|
||||||
|
AND $1000,X ; Absolute,X
|
||||||
|
AND $1000,Y ; Absolute,Y
|
||||||
|
AND ($10,X) ; Indirect,X
|
||||||
|
AND ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; ANC (unofficial) - AND + set C as bit 7
|
||||||
|
anc_tests:
|
||||||
|
ANC #$10 ; Immediate
|
||||||
|
|
||||||
|
; ARR (unofficial) - AND + ROR
|
||||||
|
arr_tests:
|
||||||
|
ARR #$10 ; Immediate
|
||||||
|
|
||||||
|
; ASL - Arithmetic Shift Left
|
||||||
|
asl_tests:
|
||||||
|
ASL A ; Accumulator
|
||||||
|
ASL $10 ; Zero Page
|
||||||
|
ASL $10,X ; Zero Page,X
|
||||||
|
ASL $1000 ; Absolute
|
||||||
|
ASL $1000,X ; Absolute,X
|
||||||
|
|
||||||
|
; AXS (unofficial) - Store A&X into memory
|
||||||
|
;; axs_tests:
|
||||||
|
;; AXS #$10 ; Immediate
|
||||||
|
;; AXS $10 ; Zero Page
|
||||||
|
;; AXS $10,Y ; Zero Page,Y
|
||||||
|
;; AXS $1000 ; Absolute
|
||||||
|
|
||||||
|
; Branch Instructions
|
||||||
|
branch_tests:
|
||||||
|
BCC label1 ; Relative
|
||||||
|
BCS label1 ; Relative
|
||||||
|
BEQ label1 ; Relative
|
||||||
|
BMI label1 ; Relative
|
||||||
|
BNE label1 ; Relative
|
||||||
|
BPL label1 ; Relative
|
||||||
|
BVC label1 ; Relative
|
||||||
|
BVS label1 ; Relative
|
||||||
|
label1:
|
||||||
|
|
||||||
|
; BIT - Bit Test
|
||||||
|
bit_tests:
|
||||||
|
BIT $10 ; Zero Page
|
||||||
|
BIT $1000 ; Absolute
|
||||||
|
|
||||||
|
; BRK - Force Interrupt
|
||||||
|
brk_tests:
|
||||||
|
BRK ; Implicit
|
||||||
|
|
||||||
|
; CLC/CLD/CLI/CLV - Clear flags
|
||||||
|
clear_tests:
|
||||||
|
CLC ; Implicit
|
||||||
|
CLD ; Implicit
|
||||||
|
CLI ; Implicit
|
||||||
|
CLV ; Implicit
|
||||||
|
|
||||||
|
; CMP - Compare
|
||||||
|
cmp_tests:
|
||||||
|
CMP #$10 ; Immediate
|
||||||
|
CMP $10 ; Zero Page
|
||||||
|
CMP $10,X ; Zero Page,X
|
||||||
|
CMP $1000 ; Absolute
|
||||||
|
CMP $1000,X ; Absolute,X
|
||||||
|
CMP $1000,Y ; Absolute,Y
|
||||||
|
CMP ($10,X) ; Indirect,X
|
||||||
|
CMP ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; CPX - Compare X Register
|
||||||
|
cpx_tests:
|
||||||
|
CPX #$10 ; Immediate
|
||||||
|
CPX $10 ; Zero Page
|
||||||
|
CPX $1000 ; Absolute
|
||||||
|
|
||||||
|
; CPY - Compare Y Register
|
||||||
|
cpy_tests:
|
||||||
|
CPY #$10 ; Immediate
|
||||||
|
CPY $10 ; Zero Page
|
||||||
|
CPY $1000 ; Absolute
|
||||||
|
|
||||||
|
; DCP (unofficial) - DEC + CMP
|
||||||
|
dcp_tests:
|
||||||
|
DCP $10 ; Zero Page
|
||||||
|
DCP $10,X ; Zero Page,X
|
||||||
|
DCP $1000 ; Absolute
|
||||||
|
DCP $1000,X ; Absolute,X
|
||||||
|
DCP $1000,Y ; Absolute,Y
|
||||||
|
DCP ($10,X) ; Indirect,X
|
||||||
|
DCP ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; DEC - Decrement Memory
|
||||||
|
dec_tests:
|
||||||
|
DEC $10 ; Zero Page
|
||||||
|
DEC $10,X ; Zero Page,X
|
||||||
|
DEC $1000 ; Absolute
|
||||||
|
DEC $1000,X ; Absolute,X
|
||||||
|
|
||||||
|
; DEX/DEY - Decrement Register
|
||||||
|
dex_dey_tests:
|
||||||
|
DEX ; Implicit
|
||||||
|
DEY ; Implicit
|
||||||
|
|
||||||
|
; EOR - Exclusive OR
|
||||||
|
eor_tests:
|
||||||
|
EOR #$10 ; Immediate
|
||||||
|
EOR $10 ; Zero Page
|
||||||
|
EOR $10,X ; Zero Page,X
|
||||||
|
EOR $1000 ; Absolute
|
||||||
|
EOR $1000,X ; Absolute,X
|
||||||
|
EOR $1000,Y ; Absolute,Y
|
||||||
|
EOR ($10,X) ; Indirect,X
|
||||||
|
EOR ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; ISC/ISB (unofficial) - INC + SBC
|
||||||
|
isc_tests:
|
||||||
|
ISC $10 ; Zero Page
|
||||||
|
ISC $10,X ; Zero Page,X
|
||||||
|
ISC $1000 ; Absolute
|
||||||
|
ISC $1000,X ; Absolute,X
|
||||||
|
ISC $1000,Y ; Absolute,Y
|
||||||
|
ISC ($10,X) ; Indirect,X
|
||||||
|
ISC ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; INC - Increment Memory
|
||||||
|
inc_tests:
|
||||||
|
INC $10 ; Zero Page
|
||||||
|
INC $10,X ; Zero Page,X
|
||||||
|
INC $1000 ; Absolute
|
||||||
|
INC $1000,X ; Absolute,X
|
||||||
|
|
||||||
|
; INX/INY - Increment Register
|
||||||
|
inx_iny_tests:
|
||||||
|
INX ; Implicit
|
||||||
|
INY ; Implicit
|
||||||
|
|
||||||
|
; JMP - Jump
|
||||||
|
jmp_tests:
|
||||||
|
JMP $1000 ; Absolute
|
||||||
|
JMP ($1000) ; Absolute Indirect
|
||||||
|
|
||||||
|
; JSR - Jump to Subroutine
|
||||||
|
jsr_tests:
|
||||||
|
JSR $1000 ; Absolute
|
||||||
|
|
||||||
|
; KIL (unofficial) - Halt the CPU
|
||||||
|
;; kil_tests:
|
||||||
|
;; KIL ; Implicit
|
||||||
|
|
||||||
|
; LAX (unofficial) - LDA + LDX
|
||||||
|
lax_tests:
|
||||||
|
LAX #$10 ; Immediate
|
||||||
|
LAX $10 ; Zero Page
|
||||||
|
LAX $10,Y ; Zero Page,Y
|
||||||
|
LAX $1000 ; Absolute
|
||||||
|
LAX $1000,Y ; Absolute,Y
|
||||||
|
LAX ($10,X) ; Indirect,X
|
||||||
|
LAX ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; LAS (unofficial) - LDA/TSX with stack pointer
|
||||||
|
las_tests:
|
||||||
|
LAS $1000,Y ; Absolute,Y
|
||||||
|
|
||||||
|
; LDA - Load Accumulator
|
||||||
|
lda_tests:
|
||||||
|
LDA #$10 ; Immediate
|
||||||
|
LDA $10 ; Zero Page
|
||||||
|
LDA $10,X ; Zero Page,X
|
||||||
|
LDA $1000 ; Absolute
|
||||||
|
LDA $1000,X ; Absolute,X
|
||||||
|
LDA $1000,Y ; Absolute,Y
|
||||||
|
LDA ($10,X) ; Indirect,X
|
||||||
|
LDA ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; LDX - Load X Register
|
||||||
|
ldx_tests:
|
||||||
|
LDX #$10 ; Immediate
|
||||||
|
LDX $10 ; Zero Page
|
||||||
|
LDX $10,Y ; Zero Page,Y
|
||||||
|
LDX $1000 ; Absolute
|
||||||
|
LDX $1000,Y ; Absolute,Y
|
||||||
|
|
||||||
|
; LDY - Load Y Register
|
||||||
|
ldy_tests:
|
||||||
|
LDY #$10 ; Immediate
|
||||||
|
LDY $10 ; Zero Page
|
||||||
|
LDY $10,X ; Zero Page,X
|
||||||
|
LDY $1000
|
||||||
|
|
||||||
|
|
||||||
|
LDY $1000 ; Absolute
|
||||||
|
LDY $1000,X ; Absolute,X
|
||||||
|
|
||||||
|
; LSR - Logical Shift Right
|
||||||
|
lsr_tests:
|
||||||
|
LSR A ; Accumulator
|
||||||
|
LSR $10 ; Zero Page
|
||||||
|
LSR $10,X ; Zero Page,X
|
||||||
|
LSR $1000 ; Absolute
|
||||||
|
LSR $1000,X ; Absolute,X
|
||||||
|
|
||||||
|
; NOP - No Operation
|
||||||
|
nop_tests:
|
||||||
|
NOP ; Implicit
|
||||||
|
NOP #$10 ; Immediate (unofficial)
|
||||||
|
NOP $10 ; Zero Page (unofficial)
|
||||||
|
NOP $10,X ; Zero Page,X (unofficial)
|
||||||
|
NOP $1000 ; Absolute (unofficial)
|
||||||
|
NOP $1000,X ; Absolute,X (unofficial)
|
||||||
|
|
||||||
|
; ORA - Logical Inclusive OR
|
||||||
|
ora_tests:
|
||||||
|
ORA #$10 ; Immediate
|
||||||
|
ORA $10 ; Zero Page
|
||||||
|
ORA $10,X ; Zero Page,X
|
||||||
|
ORA $1000 ; Absolute
|
||||||
|
ORA $1000,X ; Absolute,X
|
||||||
|
ORA $1000,Y ; Absolute,Y
|
||||||
|
ORA ($10,X) ; Indirect,X
|
||||||
|
ORA ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; Stack Operations
|
||||||
|
stack_tests:
|
||||||
|
PHA ; Push Accumulator - Implicit
|
||||||
|
PHP ; Push Processor Status - Implicit
|
||||||
|
PLA ; Pull Accumulator - Implicit
|
||||||
|
PLP ; Pull Processor Status - Implicit
|
||||||
|
|
||||||
|
; RLA (unofficial) - ROL + AND
|
||||||
|
rla_tests:
|
||||||
|
RLA $10 ; Zero Page
|
||||||
|
RLA $10,X ; Zero Page,X
|
||||||
|
RLA $1000 ; Absolute
|
||||||
|
RLA $1000,X ; Absolute,X
|
||||||
|
RLA $1000,Y ; Absolute,Y
|
||||||
|
RLA ($10,X) ; Indirect,X
|
||||||
|
RLA ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; ROL - Rotate Left
|
||||||
|
rol_tests:
|
||||||
|
ROL A ; Accumulator
|
||||||
|
ROL $10 ; Zero Page
|
||||||
|
ROL $10,X ; Zero Page,X
|
||||||
|
ROL $1000 ; Absolute
|
||||||
|
ROL $1000,X ; Absolute,X
|
||||||
|
|
||||||
|
; ROR - Rotate Right
|
||||||
|
ror_tests:
|
||||||
|
ROR A ; Accumulator
|
||||||
|
ROR $10 ; Zero Page
|
||||||
|
ROR $10,X ; Zero Page,X
|
||||||
|
ROR $1000 ; Absolute
|
||||||
|
ROR $1000,X ; Absolute,X
|
||||||
|
|
||||||
|
; RRA (unofficial) - ROR + ADC
|
||||||
|
rra_tests:
|
||||||
|
RRA $10 ; Zero Page
|
||||||
|
RRA $10,X ; Zero Page,X
|
||||||
|
RRA $1000 ; Absolute
|
||||||
|
RRA $1000,X ; Absolute,X
|
||||||
|
RRA $1000,Y ; Absolute,Y
|
||||||
|
RRA ($10,X) ; Indirect,X
|
||||||
|
RRA ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; RTI - Return from Interrupt
|
||||||
|
rti_tests:
|
||||||
|
RTI ; Implicit
|
||||||
|
|
||||||
|
; RTS - Return from Subroutine
|
||||||
|
rts_tests:
|
||||||
|
RTS ; Implicit
|
||||||
|
|
||||||
|
; SAX (unofficial) - Store A&X
|
||||||
|
sax_tests:
|
||||||
|
SAX $10 ; Zero Page
|
||||||
|
SAX $10,Y ; Zero Page,Y
|
||||||
|
SAX $1000 ; Absolute
|
||||||
|
SAX ($10,X) ; Indirect,X
|
||||||
|
|
||||||
|
; SBC - Subtract with Carry
|
||||||
|
sbc_tests:
|
||||||
|
SBC #$10 ; Immediate
|
||||||
|
SBC $10 ; Zero Page
|
||||||
|
SBC $10,X ; Zero Page,X
|
||||||
|
SBC $1000 ; Absolute
|
||||||
|
SBC $1000,X ; Absolute,X
|
||||||
|
SBC $1000,Y ; Absolute,Y
|
||||||
|
SBC ($10,X) ; Indirect,X
|
||||||
|
SBC ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; SEC/SED/SEI - Set Flags
|
||||||
|
set_tests:
|
||||||
|
SEC ; Implicit
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SEC ; Implicit
|
||||||
|
SED ; Implicit
|
||||||
|
SEI ; Implicit
|
||||||
|
|
||||||
|
; SHX (unofficial) - Store X&H
|
||||||
|
shx_tests:
|
||||||
|
SHX $1000,Y ; Absolute,Y
|
||||||
|
|
||||||
|
; SHY (unofficial) - Store Y&H
|
||||||
|
shy_tests:
|
||||||
|
SHY $1000,X ; Absolute,X
|
||||||
|
|
||||||
|
; SLO (unofficial) - ASL + ORA
|
||||||
|
slo_tests:
|
||||||
|
SLO $10 ; Zero Page
|
||||||
|
SLO $10,X ; Zero Page,X
|
||||||
|
SLO $1000 ; Absolute
|
||||||
|
SLO $1000,X ; Absolute,X
|
||||||
|
SLO $1000,Y ; Absolute,Y
|
||||||
|
SLO ($10,X) ; Indirect,X
|
||||||
|
SLO ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; SRE (unofficial) - LSR + EOR
|
||||||
|
sre_tests:
|
||||||
|
SRE $10 ; Zero Page
|
||||||
|
SRE $10,X ; Zero Page,X
|
||||||
|
SRE $1000 ; Absolute
|
||||||
|
SRE $1000,X ; Absolute,X
|
||||||
|
SRE $1000,Y ; Absolute,Y
|
||||||
|
SRE ($10,X) ; Indirect,X
|
||||||
|
SRE ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; STA - Store Accumulator
|
||||||
|
sta_tests:
|
||||||
|
STA $10 ; Zero Page
|
||||||
|
STA $10,X ; Zero Page,X
|
||||||
|
STA $1000 ; Absolute
|
||||||
|
STA $1000,X ; Absolute,X
|
||||||
|
STA $1000,Y ; Absolute,Y
|
||||||
|
STA ($10,X) ; Indirect,X
|
||||||
|
STA ($10),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; STX - Store X Register
|
||||||
|
stx_tests:
|
||||||
|
STX $10 ; Zero Page
|
||||||
|
STX $10,Y ; Zero Page,Y
|
||||||
|
STX $1000 ; Absolute
|
||||||
|
|
||||||
|
; STY - Store Y Register
|
||||||
|
sty_tests:
|
||||||
|
STY $10 ; Zero Page
|
||||||
|
STY $10,X ; Zero Page,X
|
||||||
|
STY $1000 ; Absolute
|
||||||
|
|
||||||
|
; TAS (unofficial) - Store A&X into SP and A&X&H into memory
|
||||||
|
tas_tests:
|
||||||
|
TAS $1000,Y ; Absolute,Y
|
||||||
|
|
||||||
|
; Register Transfers
|
||||||
|
transfer_tests:
|
||||||
|
TAX ; Implicit
|
||||||
|
TAY ; Implicit
|
||||||
|
TSX ; Implicit
|
||||||
|
TXA ; Implicit
|
||||||
|
TXS ; Implicit
|
||||||
|
TYA ; Implicit
|
||||||
|
|
||||||
|
; XAA (unofficial) - Complex behavior with A,X,# (unstable)
|
||||||
|
;; xaa_tests:
|
||||||
|
;; XAA #$10 ; Immediate
|
||||||
|
|
||||||
|
; Additional unused opcodes/variants for complete coverage
|
||||||
|
additional_tests:
|
||||||
|
; Some assemblers might not recognize all unofficial opcodes by name
|
||||||
|
; So here's a section with .byte directives for any missed opcodes
|
||||||
|
|
||||||
|
; Additional NOPs (different variants)
|
||||||
|
.byte $1A ; NOP implicit (unofficial)
|
||||||
|
.byte $3A ; NOP implicit (unofficial)
|
||||||
|
.byte $5A ; NOP implicit (unofficial)
|
||||||
|
.byte $7A ; NOP implicit (unofficial)
|
||||||
|
.byte $DA ; NOP implicit (unofficial)
|
||||||
|
.byte $FA ; NOP implicit (unofficial)
|
||||||
|
|
||||||
|
; JAM/KIL/HLT variants (these halt the CPU)
|
||||||
|
.byte $02 ; KIL/JAM (unofficial)
|
||||||
|
.byte $12 ; KIL/JAM (unofficial)
|
||||||
|
.byte $22 ; KIL/JAM (unofficial)
|
||||||
|
.byte $32 ; KIL/JAM (unofficial)
|
||||||
|
.byte $42 ; KIL/JAM (unofficial)
|
||||||
|
.byte $52 ; KIL/JAM (unofficial)
|
||||||
|
.byte $62 ; KIL/JAM (unofficial)
|
||||||
|
.byte $72 ; KIL/JAM (unofficial)
|
||||||
|
.byte $92 ; KIL/JAM (unofficial)
|
||||||
|
.byte $B2 ; KIL/JAM (
|
||||||
|
|
||||||
|
|
||||||
|
.byte $B2 ; KIL/JAM (unofficial)
|
||||||
|
.byte $D2 ; KIL/JAM (unofficial)
|
||||||
|
.byte $F2 ; KIL/JAM (unofficial)
|
||||||
|
|
||||||
|
; LAX variants
|
||||||
|
.byte $AB ; LAX immediate (unofficial) - might not be recognized as LAX #$
|
||||||
|
|
||||||
|
; SAX variants
|
||||||
|
.byte $87 ; SAX zero page (unofficial)
|
||||||
|
.byte $97 ; SAX zero page,Y (unofficial)
|
||||||
|
.byte $8F ; SAX absolute (unofficial)
|
||||||
|
.byte $83 ; SAX (indirect,X) (unofficial)
|
||||||
|
|
||||||
|
; ANE/XAA (highly unstable)
|
||||||
|
.byte $8B ; XAA/ANE immediate (unofficial)
|
||||||
|
|
||||||
|
; SHY variants
|
||||||
|
.byte $9C ; SHY absolute,X (unofficial)
|
||||||
|
|
||||||
|
; SHX variants
|
||||||
|
.byte $9E ; SHX absolute,Y (unofficial)
|
||||||
|
|
||||||
|
; TAS/SHS variants
|
||||||
|
.byte $9B ; TAS/SHS absolute,Y (unofficial)
|
||||||
|
|
||||||
|
; LAS/LAR variants
|
||||||
|
.byte $BB ; LAS/LAR absolute,Y (unofficial)
|
||||||
|
|
||||||
|
; Additional addressing mode testing for all instructions
|
||||||
|
|
||||||
|
; Test boundary conditions
|
||||||
|
LDA $FF ; Zero page boundary
|
||||||
|
LDA $FF,X ; Zero page wrap-around
|
||||||
|
LDA $FFFF ; Absolute address boundary
|
||||||
|
LDA $FFFF,X ; Absolute,X wrap-around
|
||||||
|
LDA $FFFF,Y ; Absolute,Y wrap-around
|
||||||
|
;; JMP ($FFFF) ; Indirect JMP boundary
|
||||||
|
|
||||||
|
; Test with specific values that might be edge cases
|
||||||
|
LDA #$00 ; Immediate with 0
|
||||||
|
LDA #$FF ; Immediate with max value
|
||||||
|
LDA #$80 ; Immediate with sign bit
|
||||||
|
|
||||||
|
; Exhaustive branch testing
|
||||||
|
label_forward:
|
||||||
|
BEQ label_forward_target
|
||||||
|
BNE label_forward_target
|
||||||
|
BCS label_forward_target
|
||||||
|
BCC label_forward_target
|
||||||
|
BMI label_forward_target
|
||||||
|
BPL label_forward_target
|
||||||
|
BVS label_forward_target
|
||||||
|
BVC label_forward_target
|
||||||
|
label_forward_target:
|
||||||
|
|
||||||
|
; Backward branches
|
||||||
|
BEQ label_backward
|
||||||
|
BNE label_backward
|
||||||
|
BCS label_backward
|
||||||
|
BCC label_backward
|
||||||
|
BMI label_backward
|
||||||
|
BPL label_backward
|
||||||
|
BVS label_backward
|
||||||
|
BVC label_backward
|
||||||
|
label_backward:
|
||||||
|
|
||||||
|
; Exercise all unofficial instructions with all addressing modes
|
||||||
|
; Some of these combinations might not exist on real hardware
|
||||||
|
|
||||||
|
; DCP (DEC + CMP) all modes
|
||||||
|
DCP $20 ; Zero Page
|
||||||
|
DCP $20,X ; Zero Page,X
|
||||||
|
DCP $2000 ; Absolute
|
||||||
|
DCP $2000,X ; Absolute,X
|
||||||
|
DCP $2000,Y ; Absolute,Y
|
||||||
|
DCP ($20,X) ; Indirect,X
|
||||||
|
DCP ($20),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; ISC/ISB (INC + SBC) all modes
|
||||||
|
ISC $20 ; Zero Page
|
||||||
|
ISC $20,X ; Zero Page,X
|
||||||
|
ISC $2000 ; Absolute
|
||||||
|
ISC $2000,X ; Absolute,X
|
||||||
|
ISC $2000,Y ; Absolute,Y
|
||||||
|
ISC ($20,X) ; Indirect,X
|
||||||
|
ISC ($20),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; SLO (ASL + ORA) all modes
|
||||||
|
SLO $20 ; Zero Page
|
||||||
|
SLO $20,X ; Zero Page,X
|
||||||
|
SLO $2000 ; Absolute
|
||||||
|
|
||||||
|
|
||||||
|
SLO $2000 ; Absolute
|
||||||
|
SLO $2000,X ; Absolute,X
|
||||||
|
SLO $2000,Y ; Absolute,Y
|
||||||
|
SLO ($20,X) ; Indirect,X
|
||||||
|
SLO ($20),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; RLA (ROL + AND) all modes
|
||||||
|
RLA $20 ; Zero Page
|
||||||
|
RLA $20,X ; Zero Page,X
|
||||||
|
RLA $2000 ; Absolute
|
||||||
|
RLA $2000,X ; Absolute,X
|
||||||
|
RLA $2000,Y ; Absolute,Y
|
||||||
|
RLA ($20,X) ; Indirect,X
|
||||||
|
RLA ($20),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; SRE (LSR + EOR) all modes
|
||||||
|
SRE $20 ; Zero Page
|
||||||
|
SRE $20,X ; Zero Page,X
|
||||||
|
SRE $2000 ; Absolute
|
||||||
|
SRE $2000,X ; Absolute,X
|
||||||
|
SRE $2000,Y ; Absolute,Y
|
||||||
|
SRE ($20,X) ; Indirect,X
|
||||||
|
SRE ($20),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; RRA (ROR + ADC) all modes
|
||||||
|
RRA $20 ; Zero Page
|
||||||
|
RRA $20,X ; Zero Page,X
|
||||||
|
RRA $2000 ; Absolute
|
||||||
|
RRA $2000,X ; Absolute,X
|
||||||
|
RRA $2000,Y ; Absolute,Y
|
||||||
|
RRA ($20,X) ; Indirect,X
|
||||||
|
RRA ($20),Y ; Indirect,Y
|
||||||
|
|
||||||
|
; LAX (LDA + LDX) all documented modes
|
||||||
|
LAX $20 ; Zero Page
|
||||||
|
LAX $20,Y ; Zero Page,Y
|
||||||
|
LAX $2000 ; Absolute
|
||||||
|
LAX $2000,Y ; Absolute,Y
|
||||||
|
LAX ($20,X) ; Indirect,X
|
||||||
|
LAX ($20),Y ; Indirect,Y
|
||||||
|
LAX #$20 ; Immediate (unstable)
|
||||||
|
|
||||||
|
; SAX (A&X store) all modes
|
||||||
|
SAX $20 ; Zero Page
|
||||||
|
SAX $20,Y ; Zero Page,Y
|
||||||
|
SAX $2000 ; Absolute
|
||||||
|
SAX ($20,X) ; Indirect,X
|
||||||
|
|
||||||
|
; Data regions to test loading and storing
|
||||||
|
.byte $AA, $55, $00, $FF, $01, $80, $7F, $81
|
||||||
|
|
||||||
|
; Test multiple data byte formats
|
||||||
|
data_section:
|
||||||
|
.byte $01, $02, $03, $04
|
||||||
|
.word $1234, $5678
|
||||||
|
.dword $12345678 ; Some assemblers support this
|
||||||
|
.byte %01010101 ; Binary format
|
||||||
|
.byte 'A', 'B', 'C' ; Character format
|
||||||
|
|
||||||
|
; Test self-modifying code scenarios
|
||||||
|
LDA #$EA ; Load NOP opcode
|
||||||
|
STA modify_target ; Modify the instruction below
|
||||||
|
modify_target:
|
||||||
|
BRK ; This will be modified to NOP
|
||||||
|
|
||||||
|
; Additional instruction combinations to ensure all opcodes are covered
|
||||||
|
|
||||||
|
; NOP variants (unofficial)
|
||||||
|
.byte $04 ; NOP zero page
|
||||||
|
.byte $44 ; NOP zero page
|
||||||
|
.byte $64 ; NOP zero page
|
||||||
|
.byte $0C ; NOP absolute
|
||||||
|
.byte $1C ; NOP absolute,X
|
||||||
|
.byte $3C ; NOP absolute,X
|
||||||
|
.byte $5C ; NOP absolute,X
|
||||||
|
.byte $7C ; NOP absolute,X
|
||||||
|
.byte $DC ; NOP absolute,X
|
||||||
|
.byte $FC ; NOP absolute,X
|
||||||
|
.byte $14 ; NOP zero page,X
|
||||||
|
.byte $34 ; NOP zero page,X
|
||||||
|
.byte $54 ; NOP zero page,X
|
||||||
|
.byte $74 ; NOP zero page,X
|
||||||
|
.byte $D4 ; NOP zero page,X
|
||||||
|
.byte $F4 ; NOP zero page,X
|
||||||
|
|
||||||
|
; ANC variants
|
||||||
|
.byte $0B ; ANC immediate
|
||||||
|
.byte $2B ; ANC immediate (another variant)
|
||||||
|
|
||||||
|
; ALR/ASR variant
|
||||||
|
.byte $4B ; ALR immediate
|
||||||
|
|
||||||
|
; ARR variant
|
||||||
|
.byte $6B ; ARR immediate
|
||||||
|
|
||||||
|
; AXS/SBX variant
|
||||||
|
.byte $CB ; AXS immediate
|
||||||
|
|
||||||
|
; Test decimal mode instructions
|
||||||
|
SED ; Set decimal mode
|
||||||
|
ADC #$09 ; Add in decimal mode
|
||||||
|
SBC #$05 ; Subtract in decimal mode
|
||||||
|
CLD ; Clear decimal mode
|
||||||
|
|
||||||
|
; Complex addressing modes with concrete values
|
||||||
|
LDA $1234,X ; Absolute,X
|
||||||
|
LDA $1234,Y ; Absolute,Y
|
||||||
|
LDA ($12,X) ; Indirect,X
|
||||||
|
LDA ($34),Y ; Indirect,Y
|
||||||
|
JMP ($5678) ; Indirect
|
||||||
|
|
||||||
|
; Specific tests for page boundary crossing
|
||||||
|
; Set X and Y to values that will cause page crosses
|
||||||
|
LDX #$FF
|
||||||
|
LDY #$FF
|
||||||
|
|
||||||
|
; These will cross page boundaries with the above X/Y values
|
||||||
|
LDA $1001,X ; Cross to $1100
|
||||||
|
LDA $1001,Y ; Cross to $1100
|
||||||
|
LDA ($01),Y ; Indirect cross
|
||||||
|
|
||||||
|
; Specific tests for zero page wrap-around
|
||||||
|
LDX #$FF
|
||||||
|
LDA $01,X ; Should wrap to $0000
|
||||||
|
|
||||||
|
; Complete set of stack operations
|
||||||
|
LDX #$FF
|
||||||
|
TXS ; Set stack pointer to $FF
|
||||||
|
PHA ; Push A
|
||||||
|
PHP ; Push processor status
|
||||||
|
PLA ; Pull A
|
||||||
|
PLP ; Pull processor status
|
||||||
|
|
||||||
|
; Test every possible branch
|
||||||
|
LDA #$80 ; Set N flag
|
||||||
|
BPL no_branch1 ; Should not branch
|
||||||
|
LDA #$00 ; Clear N flag
|
||||||
|
BMI no_branch2 ; Should not branch
|
||||||
|
no_branch1:
|
||||||
|
no_branch2:
|
||||||
|
|
||||||
|
LDA #$00
|
||||||
|
BEQ branch1 ; Should branch
|
||||||
|
LDA #$01 ; This should be skipped
|
||||||
|
branch1:
|
||||||
|
LDA #$01
|
||||||
|
BNE branch2 ; Should branch
|
||||||
|
LDA #$02 ; This should be skipped
|
||||||
|
branch2:
|
||||||
|
|
||||||
|
CLC ; Clear carry
|
||||||
|
BCS no_branch3 ; Should not branch
|
||||||
|
SEC ; Set carry
|
||||||
|
BCC no_branch4 ; Should not branch
|
||||||
|
no_branch3:
|
||||||
|
no_branch4:
|
||||||
|
|
||||||
|
CLV ; Clear overflow
|
||||||
|
BVS no_branch5 ; Should not branch
|
||||||
|
; Need to set overflow - more complex
|
||||||
|
; For test purposes we'll use a .byte directive
|
||||||
|
.byte $B8 ; CLV
|
||||||
|
.byte $70, $FE ; BVS -2 (back to the CLV)
|
||||||
|
no_branch5:
|
||||||
|
|
||||||
|
; Exhaustive testing of BIT instruction
|
||||||
|
LDA #$C0 ; Set bits 7 and 6
|
||||||
|
STA $30 ; Store in zero page
|
||||||
|
BIT $30 ; Test BIT with N and V set
|
||||||
|
|
||||||
|
LDA #$00 ; Clear all bits
|
||||||
|
STA $31 ; Store in zero page
|
||||||
|
BIT $31 ; Test BIT with all flags cleared
|
||||||
|
|
||||||
|
; Test all compare instructions
|
||||||
|
LDA #$50
|
||||||
|
CMP #$30 ; Compare with smaller (carry set)
|
||||||
|
CMP #$50 ; Compare with equal (carry set, zero set)
|
||||||
|
CMP #$70 ; Compare with larger (carry clear)
|
||||||
|
|
||||||
|
LDX #$40
|
||||||
|
CPX #$20 ; Compare X with smaller
|
||||||
|
CPX #$40 ; Compare X with equal
|
||||||
|
CPX #$60 ; Compare X with larger
|
||||||
|
|
||||||
|
|
||||||
|
LDY #$60
|
||||||
|
CPY #$40 ; Compare Y with smaller
|
||||||
|
CPY #$60 ; Compare Y with equal
|
||||||
|
CPY #$80 ; Compare Y with larger
|
||||||
|
|
||||||
|
; Test shifts and rotates with various values
|
||||||
|
LDA #$01 ; Test shift left with 1
|
||||||
|
ASL A ; Should become 2
|
||||||
|
|
||||||
|
LDA #$80 ; Test shift left with sign bit
|
||||||
|
ASL A ; Should set carry, clear result
|
||||||
|
|
||||||
|
LDA #$01 ; Test rotate left with 1
|
||||||
|
CLC ; Clear carry
|
||||||
|
ROL A ; Should become 2
|
||||||
|
|
||||||
|
LDA #$80 ; Test rotate left with sign bit
|
||||||
|
CLC ; Clear carry
|
||||||
|
ROL A ; Should set carry, clear result
|
||||||
|
|
||||||
|
LDA #$80 ; Test shift right with sign bit
|
||||||
|
LSR A ; Should become 0x40, clear carry
|
||||||
|
|
||||||
|
LDA #$01 ; Test shift right with 1
|
||||||
|
LSR A ; Should become 0, set carry
|
||||||
|
|
||||||
|
LDA #$01 ; Test rotate right with 1
|
||||||
|
CLC ; Clear carry
|
||||||
|
ROR A ; Should become 0
|
||||||
|
|
||||||
|
LDA #$01 ; Test rotate right with 1
|
||||||
|
SEC ; Set carry
|
||||||
|
ROR A ; Should become 0x80 + carry
|
||||||
|
|
||||||
|
; Exhaustive increment/decrement tests
|
||||||
|
LDA #$FF
|
||||||
|
STA $40 ; Store FF in memory
|
||||||
|
INC $40 ; Should wrap to 0
|
||||||
|
|
||||||
|
LDA #$01
|
||||||
|
STA $41 ; Store 01 in memory
|
||||||
|
DEC $41 ; Should become 0
|
||||||
|
|
||||||
|
LDX #$FF ; X = FF
|
||||||
|
INX ; Should wrap to 0
|
||||||
|
|
||||||
|
LDX #$01 ; X = 01
|
||||||
|
DEX ; Should become 0
|
||||||
|
|
||||||
|
LDY #$FF ; Y = FF
|
||||||
|
INY ; Should wrap to 0
|
||||||
|
|
||||||
|
LDY #$01 ; Y = 01
|
||||||
|
DEY ; Should become 0
|
||||||
|
|
||||||
|
; Test conditional behavior after operations
|
||||||
|
LDA #$00
|
||||||
|
BEQ is_zero1 ; Should branch
|
||||||
|
BRK ; Should be skipped
|
||||||
|
is_zero1:
|
||||||
|
|
||||||
|
LDA #$40
|
||||||
|
BNE not_zero1 ; Should branch
|
||||||
|
BRK ; Should be skipped
|
||||||
|
not_zero1:
|
||||||
|
|
||||||
|
LDA #$80
|
||||||
|
BMI is_negative1 ; Should branch
|
||||||
|
BRK ; Should be skipped
|
||||||
|
is_negative1:
|
||||||
|
|
||||||
|
LDA #$7F
|
||||||
|
BPL is_positive1 ; Should branch
|
||||||
|
BRK ; Should be skipped
|
||||||
|
is_positive1:
|
||||||
|
|
||||||
|
; Test unofficial instructions with various values
|
||||||
|
|
||||||
|
; SLO (ASL memory, then ORA with A)
|
||||||
|
LDA #$01
|
||||||
|
STA $50
|
||||||
|
LDA #$02
|
||||||
|
SLO $50 ; $50 becomes $02, A becomes $02|$02 = $02
|
||||||
|
|
||||||
|
; RLA (ROL memory, then AND with A)
|
||||||
|
LDA #$03
|
||||||
|
STA $51
|
||||||
|
LDA #$01
|
||||||
|
SEC ; Set carry
|
||||||
|
RLA $51 ; $51 becomes $07, A becomes $01&$07 = $01
|
||||||
|
|
||||||
|
; SRE (LSR memory, then EOR with A)
|
||||||
|
LDA #$01
|
||||||
|
STA $52
|
||||||
|
LDA #$03
|
||||||
|
SRE $52 ; $52 becomes $00 with carry, A becomes $03^$00 = $03
|
||||||
|
|
||||||
|
; RRA (ROR memory, then ADC with A)
|
||||||
|
LDA #$80
|
||||||
|
STA $53
|
||||||
|
LDA #$01
|
||||||
|
CLC ; Clear carry
|
||||||
|
RRA $53 ; $53 becomes $40, A becomes $01+$40 = $41
|
||||||
|
|
||||||
|
; Test memory wrapping behavior
|
||||||
|
LDX #$FF
|
||||||
|
LDA $00,X ; Zero page wrap from $FF to $00
|
||||||
|
|
||||||
|
; Test processor flags after arithmetical operations
|
||||||
|
CLC
|
||||||
|
LDA #$7F
|
||||||
|
ADC #$01 ; Should set overflow and negative (0x7F + 0x01 = 0x80)
|
||||||
|
|
||||||
|
SEC
|
||||||
|
LDA #$80
|
||||||
|
SBC #$01 ; Should set overflow and clear negative (0x80 - 0x01 = 0x7F)
|
||||||
|
|
||||||
|
; More page boundary crossings
|
||||||
|
LDX #$FF
|
||||||
|
LDA $1000,X ; Read from $10FF
|
||||||
|
|
||||||
|
LDY #$FF
|
||||||
|
LDA $1000,Y ; Read from $10FF
|
||||||
|
|
||||||
|
LDY #$FF
|
||||||
|
LDA ($80),Y ; If $80/$81 contains $1000, reads from $10FF
|
||||||
|
|
||||||
|
; Comprehensive unofficial opcodes testing
|
||||||
|
; ANC (AND immediate then copy bit 7 to carry)
|
||||||
|
LDA #$80
|
||||||
|
ANC #$FF ; Should set carry
|
||||||
|
|
||||||
|
LDA #$7F
|
||||||
|
ANC #$FF ; Should clear carry
|
||||||
|
|
||||||
|
; ALR/ASR (AND immediate then LSR)
|
||||||
|
LDA #$FF
|
||||||
|
ALR #$55 ; A becomes $55, then $2A with carry set
|
||||||
|
|
||||||
|
; ARR (AND immediate then ROR)
|
||||||
|
LDA #$FF
|
||||||
|
SEC
|
||||||
|
ARR #$55 ; A becomes $55
|
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod sim6502;
|
19
src/main.rs
Normal file
19
src/main.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use rusted_nes::sim6502::*;
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut f = File::open("basic.bin").expect("File not found");
|
||||||
|
let metadata = fs::metadata("basic.bin").expect("Could not load metadata");
|
||||||
|
let bytes = metadata.len();
|
||||||
|
let mut buffer = vec![0; bytes as usize];
|
||||||
|
let bytes_read = f.read(&mut buffer).expect("Problem reading file into buffer");
|
||||||
|
|
||||||
|
println!("Read {} bytes", bytes_read);
|
||||||
|
|
||||||
|
let instructions = decode_instructions(&buffer, bytes_read as i32);
|
||||||
|
for inst in instructions {
|
||||||
|
println!("{:?}", inst);
|
||||||
|
}
|
||||||
|
}
|
356
src/sim6502.rs
Normal file
356
src/sim6502.rs
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use InstructionName::*;
|
||||||
|
use AddressingShort::*;
|
||||||
|
use Register::*;
|
||||||
|
use Flag::*;
|
||||||
|
|
||||||
|
pub enum Addressing {
|
||||||
|
Implicit,
|
||||||
|
Accumulator,
|
||||||
|
Immediate,
|
||||||
|
ZeroPage,
|
||||||
|
ZeroPageX,
|
||||||
|
ZeroPageY,
|
||||||
|
Absolute,
|
||||||
|
AbsoluteX,
|
||||||
|
AbsoluteY,
|
||||||
|
IndirectX,
|
||||||
|
IndirectY,
|
||||||
|
AbsoluteIndirect,
|
||||||
|
Relative,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum InstructionName {
|
||||||
|
ADC, AHX, ALR, AND, ANC, ARR, ASL, AXS, BCC, BCS, BEQ, BIT, BMI, BNE, BPL, BRK,
|
||||||
|
BVC, BVS, CLC, CLD, CLI, CLV, CMP, CPX, CPY, DCP, DEC, DEX, DEY, EOR, ISC, INC,
|
||||||
|
INX, INY, JMP, JSR, KIL, LAX, LAS, LDA, LDX, LDY, LSR, NOP, ORA, PHA, PHP, PLA,
|
||||||
|
PLP, RLA, ROL, ROR, RRA, RTI, RTS, SAX, SBC, SEC, SED, SEI, SHX, SHY, SLO, SRE,
|
||||||
|
STA, STX, STY, TAS, TAX, TAY, TSX, TXA, TXS, TYA, XAA,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AddressingShort {
|
||||||
|
Imp, Acc, Imm, ZPg, ZPX,
|
||||||
|
ZPY, Abs, AbX, AbY, InX,
|
||||||
|
InY, AbI, Rel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InstructionDefinition {
|
||||||
|
name: InstructionName,
|
||||||
|
addressing: AddressingShort,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Register {
|
||||||
|
A,
|
||||||
|
X,
|
||||||
|
Y,
|
||||||
|
S,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Flag {
|
||||||
|
Carry,
|
||||||
|
Zero,
|
||||||
|
Interrupt,
|
||||||
|
Decimal,
|
||||||
|
Overflow,
|
||||||
|
Negative,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_indexing {
|
||||||
|
($array_type:ty, $enum_name:ident) => {
|
||||||
|
impl std::ops::Index<$enum_name> for $array_type {
|
||||||
|
type Output = <$array_type as std::ops::Index<usize>>::Output;
|
||||||
|
|
||||||
|
fn index(&self, flag: $enum_name) -> &Self::Output {
|
||||||
|
&self[flag as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::IndexMut<$enum_name> for $array_type {
|
||||||
|
fn index_mut(&mut self, flag: $enum_name) -> &mut Self::Output {
|
||||||
|
&mut self[flag as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_indexing!([bool; 6], Flag);
|
||||||
|
impl_indexing!([u8; 4], Register);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Operand {
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cpu {
|
||||||
|
interrupt_request: bool,
|
||||||
|
registers: [u8; 4],
|
||||||
|
memory: Vec<u8>,
|
||||||
|
stack: Vec<u8>,
|
||||||
|
status_flags: [bool; 6],
|
||||||
|
program_counter: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Instruction {
|
||||||
|
opcode: u8,
|
||||||
|
name: InstructionName,
|
||||||
|
src_opr: Operand,
|
||||||
|
dst_opr: Operand,
|
||||||
|
bytes_read: i32,
|
||||||
|
raw_data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cpu {
|
||||||
|
pub fn store(&mut self, _operand: &Operand, _value: u8) {}
|
||||||
|
pub fn load(&mut self, _operand: &Operand) -> u8 { 0 }
|
||||||
|
pub fn load_u16(&mut self, _operand: &Operand) -> u16 { 0 }
|
||||||
|
|
||||||
|
pub fn check_z_n_flags(&mut self, result: u8) -> u8 {
|
||||||
|
self.status_flags[Zero] = result == 0;
|
||||||
|
self.status_flags[Negative] = result & 0x80 > 0;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_overflow_flag(&mut self, value: u8) {
|
||||||
|
self.status_flags[Overflow] = value & 0x40 > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_flags_as_byte(&self) -> u8 {
|
||||||
|
let mut byte_: u8 = 0;
|
||||||
|
for (i,t) in self.status_flags.iter().enumerate() {
|
||||||
|
byte_ |= (0x1 << i as u8) & if *t { 0x1 } else { 0x0 }
|
||||||
|
}
|
||||||
|
return byte_
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_flags_from_byte(&mut self, byte_: u8) {
|
||||||
|
for (i,_) in (0..8).enumerate() {
|
||||||
|
let idx = i as u8;
|
||||||
|
self.status_flags[i] = (byte_ & (idx + 1)) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn rotate_shift(&mut self, inst: &Instruction, head_bit: u8, tail_bit: u8) {
|
||||||
|
let val = self.load(&inst.src_opr);
|
||||||
|
let bit = val & head_bit;
|
||||||
|
let mut val = if head_bit > tail_bit { val << 1 } else { val >> 1 };
|
||||||
|
val |= if self.status_flags[Carry] { tail_bit } else { 0x0 };
|
||||||
|
let flags_result = self.check_z_n_flags(val);
|
||||||
|
self.store(&inst.dst_opr, flags_result);
|
||||||
|
self.status_flags[Carry] = bit > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn logic_shift(&mut self, inst: &Instruction, head_bit: u8, tail_bit: u8) {
|
||||||
|
let mut val = self.load(&inst.src_opr);
|
||||||
|
let bit = val & head_bit;
|
||||||
|
val = if head_bit > tail_bit { val << 1 } else { val >> 1 };
|
||||||
|
let flags_result = self.check_z_n_flags(val);
|
||||||
|
self.store(&inst.dst_opr, flags_result);
|
||||||
|
self.status_flags[Carry] = bit > 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn branch(mut self, offset: i8) {
|
||||||
|
self.program_counter += 2 + offset as i16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! instruction_defs {
|
||||||
|
($({$name:ident, $addr:ident}),* $(,)?) => {
|
||||||
|
[
|
||||||
|
$(
|
||||||
|
InstructionDefinition {
|
||||||
|
name: $name,
|
||||||
|
addressing: $addr
|
||||||
|
},
|
||||||
|
)*
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const INSTRUCTION_DEFS: [InstructionDefinition; 256] = instruction_defs![
|
||||||
|
// x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF
|
||||||
|
/*0x*/{BRK,Imp},{ORA,InX},{KIL,Imp},{SLO,InX},{NOP,ZPg},{ORA,ZPg},{ASL,ZPg},{SLO,ZPg},{PHP,Imp},{ORA,Imm},{ASL,Acc},{ANC,Imm},{NOP,Abs},{ORA,Abs},{ASL,Abs},{SLO,Abs},
|
||||||
|
/*1x*/{BPL,Rel},{ORA,InY},{KIL,Imp},{SLO,InY},{NOP,ZPX},{ORA,ZPX},{ASL,ZPX},{SLO,ZPX},{CLC,Imp},{ORA,AbY},{NOP,Imp},{SLO,AbY},{NOP,AbX},{ORA,AbX},{ASL,AbX},{SLO,AbX},
|
||||||
|
/*2x*/{JSR,Abs},{AND,InX},{KIL,Imp},{RLA,InX},{BIT,ZPg},{AND,ZPg},{ROL,ZPg},{RLA,ZPg},{PLP,Imp},{AND,Imm},{ROL,Acc},{ANC,Imm},{BIT,Abs},{AND,Abs},{ROL,Abs},{RLA,Abs},
|
||||||
|
/*3x*/{BMI,Rel},{AND,InY},{KIL,Imp},{RLA,InY},{NOP,ZPX},{AND,ZPX},{ROL,ZPX},{RLA,ZPX},{SEC,Imp},{AND,AbY},{NOP,Imp},{RLA,AbY},{NOP,AbX},{AND,AbX},{ROL,AbX},{RLA,AbX},
|
||||||
|
/*4x*/{RTI,Imp},{EOR,InX},{KIL,Imp},{SRE,InX},{NOP,ZPg},{EOR,ZPg},{LSR,ZPg},{SRE,ZPg},{PHA,Imp},{EOR,Imm},{LSR,Acc},{ALR,Imm},{JMP,Abs},{EOR,Abs},{LSR,Abs},{SRE,Abs},
|
||||||
|
/*5x*/{BVC,Rel},{EOR,InY},{KIL,Imp},{SRE,InY},{NOP,ZPX},{EOR,ZPX},{LSR,ZPX},{SRE,ZPX},{CLI,Imp},{EOR,AbY},{NOP,Imp},{SRE,AbY},{NOP,AbX},{EOR,AbX},{LSR,AbX},{SRE,AbX},
|
||||||
|
/*6x*/{RTS,Imp},{ADC,InX},{KIL,Imp},{RRA,InX},{NOP,ZPg},{ADC,ZPg},{ROR,ZPg},{RRA,ZPg},{PLA,Imp},{ADC,Imm},{ROR,Acc},{ARR,Imm},{JMP,AbI},{ADC,Abs},{ROR,Abs},{RRA,Abs},
|
||||||
|
/*7x*/{BVS,Rel},{ADC,InY},{KIL,Imp},{RRA,InY},{NOP,ZPX},{ADC,ZPX},{ROR,ZPX},{RRA,ZPX},{SEI,Imp},{ADC,AbY},{NOP,Imp},{RRA,AbY},{NOP,AbX},{ADC,AbX},{ROR,AbX},{RRA,AbX},
|
||||||
|
/*8x*/{NOP,Imm},{STA,InX},{NOP,Imm},{SAX,InX},{STY,ZPg},{STA,ZPg},{STX,ZPg},{SAX,ZPg},{DEY,Imp},{NOP,Imm},{TXA,Imp},{XAA,Imm},{STY,Abs},{STA,Abs},{STX,Abs},{SAX,Abs},
|
||||||
|
/*9x*/{BCC,Rel},{STA,InY},{KIL,Imp},{AHX,InY},{STY,ZPX},{STA,ZPX},{STX,ZPY},{SAX,ZPY},{TYA,Imp},{STA,AbY},{TXS,Imp},{TAS,AbY},{SHY,AbX},{STA,AbX},{SHX,AbY},{AHX,AbY},
|
||||||
|
/*Ax*/{LDY,Imm},{LDA,InX},{LDX,Imm},{LAX,InX},{LDY,ZPg},{LDA,ZPg},{LDX,ZPg},{LAX,ZPg},{TAY,Imp},{LDA,Imm},{TAX,Imp},{LAX,Imm},{LDY,Abs},{LDA,Abs},{LDX,Abs},{LAX,Abs},
|
||||||
|
/*Bx*/{BCS,Rel},{LDA,InY},{KIL,Imp},{LAX,InY},{LDY,ZPX},{LDA,ZPX},{LDX,ZPY},{LAX,ZPY},{CLV,Imp},{LDA,AbY},{TSX,Imp},{LAS,AbY},{LDY,AbX},{LDA,AbX},{LDX,AbY},{LAX,AbY},
|
||||||
|
/*Cx*/{CPY,Imm},{CMP,InX},{NOP,Imm},{DCP,InX},{CPY,ZPg},{CMP,ZPg},{DEC,ZPg},{DCP,ZPg},{INY,Imp},{CMP,Imm},{DEX,Imp},{AXS,Imm},{CPY,Abs},{CMP,Abs},{DEC,Abs},{DCP,Abs},
|
||||||
|
/*Dx*/{BNE,Rel},{CMP,InY},{KIL,Imp},{DCP,InY},{NOP,ZPX},{CMP,ZPX},{DEC,ZPX},{DCP,ZPX},{CLD,Imp},{CMP,AbY},{NOP,Imp},{DCP,AbY},{NOP,AbX},{CMP,AbX},{DEC,AbX},{DCP,AbX},
|
||||||
|
/*Ex*/{CPX,Imm},{SBC,InX},{NOP,Imm},{ISC,InX},{CPX,ZPg},{SBC,ZPg},{INC,ZPg},{ISC,ZPg},{INX,Imp},{SBC,Imm},{NOP,Imp},{SBC,Imm},{CPX,Abs},{SBC,Abs},{INC,Abs},{ISC,Abs},
|
||||||
|
/*Fx*/{BEQ,Rel},{SBC,InY},{KIL,Imp},{ISC,InY},{NOP,ZPX},{SBC,ZPX},{INC,ZPX},{ISC,ZPX},{SED,Imp},{SBC,AbY},{NOP,Imp},{ISC,AbY},{NOP,AbX},{SBC,AbX},{INC,AbX},{ISC,AbX},
|
||||||
|
];
|
||||||
|
|
||||||
|
fn execute(cpu: &mut Cpu, inst: &Instruction) {
|
||||||
|
match inst.name {
|
||||||
|
// Access
|
||||||
|
LDA | STA | LDX | STX | LDY | STY => {
|
||||||
|
let val = cpu.load(&inst.src_opr);
|
||||||
|
cpu.store(&inst.dst_opr, val);
|
||||||
|
},
|
||||||
|
// Transfer
|
||||||
|
TAX | TAY | TXA | TYA => {
|
||||||
|
let val = cpu.load(&inst.src_opr);
|
||||||
|
let flags_result = cpu.check_z_n_flags(val);
|
||||||
|
cpu.store(&inst.dst_opr, flags_result);
|
||||||
|
}
|
||||||
|
// Arithmetic
|
||||||
|
ADC => {
|
||||||
|
let memory = cpu.load(&inst.src_opr);
|
||||||
|
let result = cpu.registers[A] as u16 + memory as u16;
|
||||||
|
let carry_val = if cpu.status_flags[Carry] { 1 } else { 0 };
|
||||||
|
cpu.registers[A] = result as u8 + carry_val;
|
||||||
|
cpu.status_flags[Carry] = result > 0xFF;
|
||||||
|
let reg_a = cpu.registers[A];
|
||||||
|
let res = result as u8;
|
||||||
|
cpu.status_flags[Overflow] = (res ^ reg_a) & (res ^ memory) & 0x80 > 0;
|
||||||
|
cpu.check_z_n_flags(result as u8);
|
||||||
|
},
|
||||||
|
// Arithmetic
|
||||||
|
INC | INX | INY => {
|
||||||
|
let val = cpu.load(&inst.src_opr) + 1;
|
||||||
|
let flags_result = cpu.check_z_n_flags(val);
|
||||||
|
cpu.store(&inst.dst_opr, flags_result);
|
||||||
|
},
|
||||||
|
DEC | DEX | DEY => {
|
||||||
|
let val = cpu.load(&inst.src_opr) - 1;
|
||||||
|
let flags_result = cpu.check_z_n_flags(val);
|
||||||
|
cpu.store(&inst.dst_opr, flags_result);
|
||||||
|
},
|
||||||
|
// Compare
|
||||||
|
SBC | CMP | CPX | CPY => {
|
||||||
|
let reg = match inst.name {
|
||||||
|
SBC | CMP => A,
|
||||||
|
CPX => X,
|
||||||
|
_ => Y
|
||||||
|
};
|
||||||
|
let memory = cpu.load(&inst.src_opr);
|
||||||
|
let reg_val = cpu.registers[reg] as u16;
|
||||||
|
let result = reg_val - (memory as u16);
|
||||||
|
if inst.name == SBC {
|
||||||
|
let carry_val = if cpu.status_flags[Carry] { 1 } else { 0 };
|
||||||
|
cpu.registers[reg] = result as u8 - carry_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.status_flags[Carry] = result < 0;
|
||||||
|
|
||||||
|
let reg_a = cpu.registers[A];
|
||||||
|
let res = result as u8;
|
||||||
|
cpu.status_flags[Overflow] = (res ^ reg_a) & (res ^ !memory) & 0x80 > 0;
|
||||||
|
cpu.check_z_n_flags(res);
|
||||||
|
},
|
||||||
|
// Shift
|
||||||
|
ASL => cpu.logic_shift(inst, 0x80, 0x01),
|
||||||
|
LSR => cpu.logic_shift(inst, 0x01, 0x08),
|
||||||
|
ROL => cpu.rotate_shift(inst, 0x80, 0x01),
|
||||||
|
ROR => cpu.rotate_shift(inst, 0x01, 0x08),
|
||||||
|
// Bitwise
|
||||||
|
AND => {
|
||||||
|
cpu.registers[A] &= cpu.load(&inst.src_opr);
|
||||||
|
cpu.check_z_n_flags(cpu.registers[A]);
|
||||||
|
},
|
||||||
|
BIT => {
|
||||||
|
let mem_val = cpu.load(&inst.src_opr);
|
||||||
|
let result = cpu.registers[A] & mem_val;
|
||||||
|
cpu.check_z_n_flags(result);
|
||||||
|
cpu.check_overflow_flag(result);
|
||||||
|
},
|
||||||
|
ORA => {
|
||||||
|
cpu.registers[A] |= cpu.load(&inst.src_opr);
|
||||||
|
cpu.check_z_n_flags(cpu.registers[A]);
|
||||||
|
},
|
||||||
|
EOR => {
|
||||||
|
cpu.registers[A] != cpu.load(&inst.src_opr);
|
||||||
|
cpu.check_z_n_flags(cpu.registers[A]);
|
||||||
|
},
|
||||||
|
// Branching
|
||||||
|
BCC => if !cpu.status_flags[Carry] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
|
||||||
|
BCS => if cpu.status_flags[Carry] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
|
||||||
|
BNE => if !cpu.status_flags[Zero] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
|
||||||
|
BEQ => if cpu.status_flags[Zero] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
|
||||||
|
BPL => if !cpu.status_flags[Negative] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
|
||||||
|
BMI => if cpu.status_flags[Negative] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
|
||||||
|
BVC => if !cpu.status_flags[Overflow] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
|
||||||
|
BVS => if cpu.status_flags[Overflow] { cpu.branch(cpu.load(&inst.src_opr) as i8) },
|
||||||
|
// Jump
|
||||||
|
JMP => cpu.program_counter = cpu.load_u16(&inst.src_opr) as i16,
|
||||||
|
JSR => {
|
||||||
|
// TODO: This is likely wrong, you can't pass in a u8 just like that
|
||||||
|
cpu.stack.push((cpu.program_counter + 2) as u8);
|
||||||
|
cpu.program_counter = cpu.load_u16(&inst.src_opr) as i16;
|
||||||
|
},
|
||||||
|
RTS => {
|
||||||
|
let pc = cpu.stack.pop().unwrap();
|
||||||
|
cpu.program_counter = (pc + 1) as i16;
|
||||||
|
},
|
||||||
|
BRK => {
|
||||||
|
cpu.stack.push((cpu.program_counter + 2) as u8);
|
||||||
|
cpu.stack.push(cpu.get_flags_as_byte());
|
||||||
|
let pc_constant = 0xFFFE as u16;
|
||||||
|
cpu.program_counter = pc_constant as i16;
|
||||||
|
cpu.status_flags[Interrupt] = true;
|
||||||
|
},
|
||||||
|
RTI => {
|
||||||
|
let flags_byte = cpu.stack.pop().unwrap();
|
||||||
|
let pc = cpu.stack.pop().unwrap();
|
||||||
|
cpu.set_flags_from_byte(flags_byte);
|
||||||
|
// TODO: This is likely wrong, you can't pass in a u8 just like that
|
||||||
|
cpu.program_counter = pc as i16;
|
||||||
|
cpu.status_flags[Interrupt] = false;
|
||||||
|
}
|
||||||
|
// Stack
|
||||||
|
PHA | PHP | PLA | PLP | TSX | TXS => {},
|
||||||
|
// Something about PusH/PulL accumulator or PS
|
||||||
|
|
||||||
|
// Flags
|
||||||
|
SEC => cpu.status_flags[Carry] = true,
|
||||||
|
SED => cpu.status_flags[Decimal] = true,
|
||||||
|
SEI => cpu.status_flags[Interrupt] = true,
|
||||||
|
|
||||||
|
CLC => cpu.status_flags[Carry] = false,
|
||||||
|
CLD => cpu.status_flags[Decimal] = false,
|
||||||
|
CLI => cpu.status_flags[Interrupt] = false,
|
||||||
|
CLV => cpu.status_flags[Overflow] = false,
|
||||||
|
|
||||||
|
// We can figure this out later
|
||||||
|
NOP | KIL => {},
|
||||||
|
|
||||||
|
// Unofficial/Illegal
|
||||||
|
AHX | ALR | ANC | ARR | AXS | DCP | ISC | LAX | LAS
|
||||||
|
| RLA | RRA | SAX | SHX | SHY | SLO | SRE | TAS | XAA => {},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_instructions(data: &[u8], bytes_to_read: i32) -> Vec<&InstructionDefinition>{
|
||||||
|
let mut idx: usize = 0;
|
||||||
|
let mut instructions: Vec<&InstructionDefinition> =
|
||||||
|
Vec::with_capacity((bytes_to_read / 2) as usize);
|
||||||
|
while (idx as i32) < bytes_to_read {
|
||||||
|
let mut processed = 0;
|
||||||
|
let inst = &INSTRUCTION_DEFS[data[idx] as usize];
|
||||||
|
instructions.push(inst);
|
||||||
|
processed += match inst.addressing {
|
||||||
|
Imp | Acc => 1,
|
||||||
|
Imm | ZPg | Rel | AbI
|
||||||
|
| ZPX | InX | InY => 2,
|
||||||
|
ZPY | AbX | AbY | Abs => 3,
|
||||||
|
};
|
||||||
|
idx += processed
|
||||||
|
}
|
||||||
|
instructions
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user