7 Commits

Author SHA1 Message Date
tmerritt c4e1f233ae more chips
more docs
2025-08-02 11:17:23 -04:00
tmerritt 7ac8bd86ba MOS6520 looking mostly there. 2025-07-29 13:12:33 -04:00
tmerritt 8f6f9cb64d adds docs
working on widetick
2025-07-26 11:02:36 -04:00
tmerritt b40c3c503f box swap 2025-07-22 15:51:21 -04:00
tmerritt d5efabdd36 prep to do big tick method 2025-07-21 13:41:17 -04:00
tmerritt 7498489b03 RamRomComputer now reads from ROM and writes to RAM and reads back from RAM 2025-07-18 16:09:41 -04:00
tmerritt 2939e1cac5 Lots of stuff. 2025-07-17 11:51:05 -04:00
193 changed files with 13628 additions and 2203 deletions
-3
View File
@@ -1,5 +1,2 @@
[alias]
coverage = "tarpaulin --out Html --skip-clean --output-dir coverage"
[build]
rustc-wrapper = "sccache"
+2
View File
@@ -1 +1,3 @@
/target
*produced.asm
*produced.bin
+5
View File
@@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/mos6502.iml" filepath="$PROJECT_DIR$/.idea/mos6502.iml" />
</modules>
</component>
</project>
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/cli/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/core/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/macroquad/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/beneater/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/core/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+479
View File
@@ -0,0 +1,479 @@
STA $f3 ; [ 0x85 0xf3 ]
PLA ; [ 0x68 ]
STA $f1 ; [ 0x85 0xf1 ]
PLA ; [ 0x68 ]
STA $ef ; [ 0x85 0xef ]
STA $fa ; [ 0x85 0xfa ]
PLA ; [ 0x68 ]
STA $f0 ; [ 0x85 0xf0 ]
STA $fb ; [ 0x85 0xfb ]
STY $f4 ; [ 0x84 0xf4 ]
STX $f5 ; [ 0x86 0xf5 ]
TSX ; [ 0xba ]
STX $f2 ; [ 0x86 0xf2 ]
JSR $1e88 ; [ 0x20 0x88 0x1e ]
JMP $1c4f ; [ 0x4c 0x4f 0x1c ]
JMP ($17fa) ; [ 0x6c 0xfa 0x17 ]
JMP ($17fe) ; [ 0x6c 0xfe 0x17 ]
LDX #$ff ; [ 0xa2 0xff ]
TXS ; [ 0x9a ]
STX $f2 ; [ 0x86 0xf2 ]
JSR $1e88 ; [ 0x20 0x88 0x1e ]
LDA #$ff ; [ 0xa9 0xff ]
STA $17f3 ; [ 0x8d 0xf3 0x17 ]
LDA #$01 ; [ 0xa9 0x01 ]
BIT $1740 ; [ 0x2c 0x40 0x17 ]
BNE #$19 ; [ 0xd0 0x19 ]
BMI #$f9 ; [ 0x30 0xf9 ]
LDA #$fc ; [ 0xa9 0xfc ]
CLC ; [ 0x18 ]
ADC #$01 ; [ 0x69 0x01 ]
BCC $03 ; [ 0x90 0x03 ]
INC $17f3 ; [ 0xee 0xf3 0x17 ]
LDY $1740 ; [ 0xac 0x40 0x17 ]
BPL #$f3 ; [ 0x10 0xf3 ]
STA $17f2 ; [ 0x8d 0xf2 0x17 ]
LDX #$08 ; [ 0xa2 0x08 ]
JSR $1e6a ; [ 0x20 0x6a 0x1e ]
JSR $1e8c ; [ 0x20 0x8c 0x1e ]
LDA #$01 ; [ 0xa9 0x01 ]
BIT $1740 ; [ 0x2c 0x40 0x17 ]
BNE #$1e ; [ 0xd0 0x1e ]
JSR $1e2f ; [ 0x20 0x2f 0x1e ]
LDX #$0a ; [ 0xa2 0x0a ]
JSR $1e31 ; [ 0x20 0x31 0x1e ]
JMP $1daf ; [ 0x4c 0xaf 0x1d ]
LDA #$00 ; [ 0xa9 0x00 ]
STA $f8 ; [ 0x85 0xf8 ]
STA $f9 ; [ 0x85 0xf9 ]
JSR $1e5a ; [ 0x20 0x5a 0x1e ]
CMP #$01 ; [ 0xc9 0x01 ]
BEQ $06 ; [ 0xf0 0x06 ]
JSR $1fac ; [ 0x20 0xac 0x1f ]
JMP $1ddb ; [ 0x4c 0xdb 0x1d ]
JSR $1f19 ; [ 0x20 0x19 0x1f ]
BNE #$d3 ; [ 0xd0 0xd3 ]
LDA #$01 ; [ 0xa9 0x01 ]
BIT $1740 ; [ 0x2c 0x40 0x17 ]
BEQ $cc ; [ 0xf0 0xcc ]
JSR $1f19 ; [ 0x20 0x19 0x1f ]
BEQ $f4 ; [ 0xf0 0xf4 ]
JSR $1f19 ; [ 0x20 0x19 0x1f ]
BEQ $ef ; [ 0xf0 0xef ]
JSR $1f6a ; [ 0x20 0x6a 0x1f ]
CMP #$15 ; [ 0xc9 0x15 ]
BPL #$bb ; [ 0x10 0xbb ]
CMP #$14 ; [ 0xc9 0x14 ]
BEQ $44 ; [ 0xf0 0x44 ]
CMP #$10 ; [ 0xc9 0x10 ]
BEQ $2c ; [ 0xf0 0x2c ]
CMP #$11 ; [ 0xc9 0x11 ]
BEQ $2c ; [ 0xf0 0x2c ]
CMP #$12 ; [ 0xc9 0x12 ]
BEQ $2f ; [ 0xf0 0x2f ]
CMP #$13 ; [ 0xc9 0x13 ]
BEQ $31 ; [ 0xf0 0x31 ]
ASL ; [ 0x0a ]
ASL ; [ 0x0a ]
ASL ; [ 0x0a ]
ASL ; [ 0x0a ]
STA $fc ; [ 0x85 0xfc ]
LDX #$04 ; [ 0xa2 0x04 ]
LDY $ff ; [ 0xa4 0xff ]
BNE #$0a ; [ 0xd0 0x0a ]
LDA ($fa),Y ; [ 0xb1 0xfa ]
ASL $fc ; [ 0x06 0xfc ]
ROL ; [ 0x2a ]
STA ($fa),Y ; [ 0x91 0xfa ]
JMP $1cc3 ; [ 0x4c 0xc3 0x1c ]
ASL ; [ 0x0a ]
ROL $fa ; [ 0x26 0xfa ]
ROL $fb ; [ 0x26 0xfb ]
DEX ; [ 0xca ]
BNE #$ea ; [ 0xd0 0xea ]
BEQ $08 ; [ 0xf0 0x08 ]
LDA #$01 ; [ 0xa9 0x01 ]
BNE #$02 ; [ 0xd0 0x02 ]
LDA #$00 ; [ 0xa9 0x00 ]
STA $ff ; [ 0x85 0xff ]
JMP $1c4f ; [ 0x4c 0x4f 0x1c ]
JSR $1f63 ; [ 0x20 0x63 0x1f ]
JMP $1c4f ; [ 0x4c 0x4f 0x1c ]
JMP $1dc8 ; [ 0x4c 0xc8 0x1d ]
LDA $ef ; [ 0xa5 0xef ]
STA $fa ; [ 0x85 0xfa ]
LDA $f0 ; [ 0xa5 0xf0 ]
STA $fb ; [ 0x85 0xfb ]
JMP $1c4f ; [ 0x4c 0x4f 0x1c ]
JSR $1e5a ; [ 0x20 0x5a 0x1e ]
CMP #$3b ; [ 0xc9 0x3b ]
BNE #$f9 ; [ 0xd0 0xf9 ]
LDA #$00 ; [ 0xa9 0x00 ]
STA $f7 ; [ 0x85 0xf7 ]
STA $f6 ; [ 0x85 0xf6 ]
JSR $1f9d ; [ 0x20 0x9d 0x1f ]
TAX ; [ 0xaa ]
JSR $1f91 ; [ 0x20 0x91 0x1f ]
JSR $1f9d ; [ 0x20 0x9d 0x1f ]
STA $fb ; [ 0x85 0xfb ]
JSR $1f91 ; [ 0x20 0x91 0x1f ]
JSR $1f9d ; [ 0x20 0x9d 0x1f ]
STA $fa ; [ 0x85 0xfa ]
JSR $1f91 ; [ 0x20 0x91 0x1f ]
TXA ; [ 0x8a ]
BEQ $0f ; [ 0xf0 0x0f ]
JSR $1f9d ; [ 0x20 0x9d 0x1f ]
STA ($fa),Y ; [ 0x91 0xfa ]
JSR $1f91 ; [ 0x20 0x91 0x1f ]
JSR $1f63 ; [ 0x20 0x63 0x1f ]
DEX ; [ 0xca ]
BNE #$f2 ; [ 0xd0 0xf2 ]
INX ; [ 0xe8 ]
JSR $1f9d ; [ 0x20 0x9d 0x1f ]
CMP $f6 ; [ 0xc5 0xf6 ]
BNE #$17 ; [ 0xd0 0x17 ]
JSR $1f9d ; [ 0x20 0x9d 0x1f ]
CMP $f7 ; [ 0xc5 0xf7 ]
BNE #$13 ; [ 0xd0 0x13 ]
TXA ; [ 0x8a ]
BNE #$b9 ; [ 0xd0 0xb9 ]
LDX #$0c ; [ 0xa2 0x0c ]
LDA #$27 ; [ 0xa9 0x27 ]
STA $1742 ; [ 0x8d 0x42 0x17 ]
JSR $1e31 ; [ 0x20 0x31 0x1e ]
JMP $1c4f ; [ 0x4c 0x4f 0x1c ]
JSR $1f9d ; [ 0x20 0x9d 0x1f ]
LDX #$11 ; [ 0xa2 0x11 ]
BNE #$ee ; [ 0xd0 0xee ]
LDA #$00 ; [ 0xa9 0x00 ]
STA $f8 ; [ 0x85 0xf8 ]
STA $f9 ; [ 0x85 0xf9 ]
LDA #$00 ; [ 0xa9 0x00 ]
STA $f6 ; [ 0x85 0xf6 ]
STA $f7 ; [ 0x85 0xf7 ]
JSR $1e2f ; [ 0x20 0x2f 0x1e ]
LDA #$3b ; [ 0xa9 0x3b ]
JSR $1ea0 ; [ 0x20 0xa0 0x1e ]
LDA $fa ; [ 0xa5 0xfa ]
CMP $17f7 ; [ 0xcd 0xf7 0x17 ]
LDA $fb ; [ 0xa5 0xfb ]
SBC $17f8 ; [ 0xed 0xf8 0x17 ]
BCC $18 ; [ 0x90 0x18 ]
LDA #$00 ; [ 0xa9 0x00 ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
JSR $1fcc ; [ 0x20 0xcc 0x1f ]
JSR $1e1e ; [ 0x20 0x1e 0x1e ]
LDA $f6 ; [ 0xa5 0xf6 ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
LDA $f7 ; [ 0xa5 0xf7 ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
JMP $1c64 ; [ 0x4c 0x64 0x1c ]
LDA #$18 ; [ 0xa9 0x18 ]
TAX ; [ 0xaa ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
JSR $1f91 ; [ 0x20 0x91 0x1f ]
JSR $1e1e ; [ 0x20 0x1e 0x1e ]
LDY #$00 ; [ 0xa0 0x00 ]
LDA ($fa),Y ; [ 0xb1 0xfa ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
JSR $1f91 ; [ 0x20 0x91 0x1f ]
JSR $1f63 ; [ 0x20 0x63 0x1f ]
DEX ; [ 0xca ]
BNE #$f0 ; [ 0xd0 0xf0 ]
LDA $f6 ; [ 0xa5 0xf6 ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
LDA $f7 ; [ 0xa5 0xf7 ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
INC $f8 ; [ 0xe6 0xf8 ]
BNE #$02 ; [ 0xd0 0x02 ]
INC $f9 ; [ 0xe6 0xf9 ]
JMP $1d48 ; [ 0x4c 0x48 0x1d ]
JSR $1fcc ; [ 0x20 0xcc 0x1f ]
JSR $1e2f ; [ 0x20 0x2f 0x1e ]
JSR $1e1e ; [ 0x20 0x1e 0x1e ]
JSR $1e9e ; [ 0x20 0x9e 0x1e ]
LDY #$00 ; [ 0xa0 0x00 ]
LDA ($fa),Y ; [ 0xb1 0xfa ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
JSR $1e9e ; [ 0x20 0x9e 0x1e ]
JMP $1c64 ; [ 0x4c 0x64 0x1c ]
JSR $1f63 ; [ 0x20 0x63 0x1f ]
JMP $1dac ; [ 0x4c 0xac 0x1d ]
LDX $f2 ; [ 0xa6 0xf2 ]
TXS ; [ 0x9a ]
LDA $fb ; [ 0xa5 0xfb ]
PHA ; [ 0x48 ]
LDA $fa ; [ 0xa5 0xfa ]
PHA ; [ 0x48 ]
LDA $f1 ; [ 0xa5 0xf1 ]
PHA ; [ 0x48 ]
LDX $f5 ; [ 0xa6 0xf5 ]
LDY $f4 ; [ 0xa4 0xf4 ]
LDA $f3 ; [ 0xa5 0xf3 ]
RTI ; [ 0x40 ]
CMP #$20 ; [ 0xc9 0x20 ]
BEQ $ca ; [ 0xf0 0xca ]
CMP #$7f ; [ 0xc9 0x7f ]
BEQ $1b ; [ 0xf0 0x1b ]
CMP #$0d ; [ 0xc9 0x0d ]
BEQ $db ; [ 0xf0 0xdb ]
CMP #$0a ; [ 0xc9 0x0a ]
BEQ $1c ; [ 0xf0 0x1c ]
CMP #$2e ; [ 0xc9 0x2e ]
BEQ $26 ; [ 0xf0 0x26 ]
CMP #$47 ; [ 0xc9 0x47 ]
BEQ $d5 ; [ 0xf0 0xd5 ]
CMP #$51 ; [ 0xc9 0x51 ]
BEQ $0a ; [ 0xf0 0x0a ]
CMP #$4c ; [ 0xc9 0x4c ]
BEQ $09 ; [ 0xf0 0x09 ]
JMP $1c6a ; [ 0x4c 0x6a 0x1c ]
JMP $1c4f ; [ 0x4c 0x4f 0x1c ]
JMP $1d42 ; [ 0x4c 0x42 0x1d ]
JMP $1ce7 ; [ 0x4c 0xe7 0x1c ]
SEC ; [ 0x38 ]
LDA $fa ; [ 0xa5 0xfa ]
SBC #$01 ; [ 0xe9 0x01 ]
STA $fa ; [ 0x85 0xfa ]
BCS $02 ; [ 0xb0 0x02 ]
DEC $fb ; [ 0xc6 0xfb ]
JMP $1dac ; [ 0x4c 0xac 0x1d ]
LDY #$00 ; [ 0xa0 0x00 ]
LDA $f8 ; [ 0xa5 0xf8 ]
STA ($fa),Y ; [ 0x91 0xfa ]
JMP $1dc2 ; [ 0x4c 0xc2 0x1d ]
LDA $fb ; [ 0xa5 0xfb ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
JSR $1f91 ; [ 0x20 0x91 0x1f ]
LDA $fa ; [ 0xa5 0xfa ]
JSR $1e3b ; [ 0x20 0x3b 0x1e ]
JSR $1f91 ; [ 0x20 0x91 0x1f ]
RTS ; [ 0x60 ]
LDX #$07 ; [ 0xa2 0x07 ]
LDA $1fd5,X ; [ 0xbd 0xd5 0x1f ]
JSR $1ea0 ; [ 0x20 0xa0 0x1e ]
DEX ; [ 0xca ]
BPL #$f7 ; [ 0x10 0xf7 ]
RTS ; [ 0x60 ]
STA $fc ; [ 0x85 0xfc ]
LSR ; [ 0x4a ]
LSR ; [ 0x4a ]
LSR ; [ 0x4a ]
LSR ; [ 0x4a ]
JSR $1e4c ; [ 0x20 0x4c 0x1e ]
LDA $fc ; [ 0xa5 0xfc ]
JSR $1e4c ; [ 0x20 0x4c 0x1e ]
LDA $fc ; [ 0xa5 0xfc ]
RTS ; [ 0x60 ]
AND #$0f ; [ 0x29 0x0f ]
CMP #$0a ; [ 0xc9 0x0a ]
CLC ; [ 0x18 ]
BMI #$02 ; [ 0x30 0x02 ]
ADC #$07 ; [ 0x69 0x07 ]
ADC #$30 ; [ 0x69 0x30 ]
JMP $1ea0 ; [ 0x4c 0xa0 0x1e ]
STX $fd ; [ 0x86 0xfd ]
LDX #$08 ; [ 0xa2 0x08 ]
LDA #$01 ; [ 0xa9 0x01 ]
BIT $1740 ; [ 0x2c 0x40 0x17 ]
BNE #$22 ; [ 0xd0 0x22 ]
BMI #$f9 ; [ 0x30 0xf9 ]
JSR $1ed4 ; [ 0x20 0xd4 0x1e ]
JSR $1eeb ; [ 0x20 0xeb 0x1e ]
LDA $1740 ; [ 0xad 0x40 0x17 ]
AND #$80 ; [ 0x29 0x80 ]
LSR $fe ; [ 0x46 0xfe ]
ORA $fe ; [ 0x05 0xfe ]
STA $fe ; [ 0x85 0xfe ]
JSR $1ed4 ; [ 0x20 0xd4 0x1e ]
DEX ; [ 0xca ]
BNE #$ef ; [ 0xd0 0xef ]
JSR $1eeb ; [ 0x20 0xeb 0x1e ]
LDX $fd ; [ 0xa6 0xfd ]
LDA $fe ; [ 0xa5 0xfe ]
ROL ; [ 0x2a ]
LSR ; [ 0x4a ]
RTS ; [ 0x60 ]
LDX #$01 ; [ 0xa2 0x01 ]
STX $ff ; [ 0x86 0xff ]
LDX #$00 ; [ 0xa2 0x00 ]
STX $1741 ; [ 0x8e 0x41 0x17 ]
LDX #$3f ; [ 0xa2 0x3f ]
STX $1743 ; [ 0x8e 0x43 0x17 ]
LDX #$07 ; [ 0xa2 0x07 ]
STX $1742 ; [ 0x8e 0x42 0x17 ]
CLD ; [ 0xd8 ]
SEI ; [ 0x78 ]
RTS ; [ 0x60 ]
LDA #$20 ; [ 0xa9 0x20 ]
STA $fe ; [ 0x85 0xfe ]
STX $fd ; [ 0x86 0xfd ]
JSR $1ed4 ; [ 0x20 0xd4 0x1e ]
LDA $1742 ; [ 0xad 0x42 0x17 ]
AND #$fe ; [ 0x29 0xfe ]
STA $1742 ; [ 0x8d 0x42 0x17 ]
JSR $1ed4 ; [ 0x20 0xd4 0x1e ]
LDX #$08 ; [ 0xa2 0x08 ]
LDA $1742 ; [ 0xad 0x42 0x17 ]
AND #$fe ; [ 0x29 0xfe ]
LSR $fe ; [ 0x46 0xfe ]
ADC #$00 ; [ 0x69 0x00 ]
STA $1742 ; [ 0x8d 0x42 0x17 ]
JSR $1ed4 ; [ 0x20 0xd4 0x1e ]
DEX ; [ 0xca ]
BNE #$ee ; [ 0xd0 0xee ]
LDA $1742 ; [ 0xad 0x42 0x17 ]
ORA #$01 ; [ 0x09 0x01 ]
STA $1742 ; [ 0x8d 0x42 0x17 ]
JSR $1ed4 ; [ 0x20 0xd4 0x1e ]
LDX $fd ; [ 0xa6 0xfd ]
RTS ; [ 0x60 ]
LDA $17f3 ; [ 0xad 0xf3 0x17 ]
STA $17f4 ; [ 0x8d 0xf4 0x17 ]
LDA $17f2 ; [ 0xad 0xf2 0x17 ]
SEC ; [ 0x38 ]
SBC #$01 ; [ 0xe9 0x01 ]
BCS $03 ; [ 0xb0 0x03 ]
DEC $17f4 ; [ 0xce 0xf4 0x17 ]
LDY $17f4 ; [ 0xac 0xf4 0x17 ]
BPL #$f3 ; [ 0x10 0xf3 ]
RTS ; [ 0x60 ]
LDA $17f3 ; [ 0xad 0xf3 0x17 ]
STA $17f4 ; [ 0x8d 0xf4 0x17 ]
LDA $17f2 ; [ 0xad 0xf2 0x17 ]
LSR ; [ 0x4a ]
LSR $17f4 ; [ 0x4e 0xf4 0x17 ]
BCC $e3 ; [ 0x90 0xe3 ]
ORA #$80 ; [ 0x09 0x80 ]
BCS $e0 ; [ 0xb0 0xe0 ]
LDY #$03 ; [ 0xa0 0x03 ]
LDX #$01 ; [ 0xa2 0x01 ]
LDA #$ff ; [ 0xa9 0xff ]
STX $1742 ; [ 0x8e 0x42 0x17 ]
INX ; [ 0xe8 ]
INX ; [ 0xe8 ]
AND $1740 ; [ 0x2d 0x40 0x17 ]
DEY ; [ 0x88 ]
BNE #$f5 ; [ 0xd0 0xf5 ]
LDY #$07 ; [ 0xa0 0x07 ]
STY $1742 ; [ 0x8c 0x42 0x17 ]
ORA #$80 ; [ 0x09 0x80 ]
EOR #$ff ; [ 0x49 0xff ]
RTS ; [ 0x60 ]
LDY #$00 ; [ 0xa0 0x00 ]
LDA ($fa),Y ; [ 0xb1 0xfa ]
STA $f9 ; [ 0x85 0xf9 ]
LDA #$7f ; [ 0xa9 0x7f ]
STA $1741 ; [ 0x8d 0x41 0x17 ]
LDX #$09 ; [ 0xa2 0x09 ]
LDY #$03 ; [ 0xa0 0x03 ]
LDA $00f8,Y ; [ 0xb9 0xf8 0x00 ]
LSR ; [ 0x4a ]
LSR ; [ 0x4a ]
LSR ; [ 0x4a ]
LSR ; [ 0x4a ]
JSR $1f48 ; [ 0x20 0x48 0x1f ]
LDA $00f8,Y ; [ 0xb9 0xf8 0x00 ]
AND #$0f ; [ 0x29 0x0f ]
JSR $1f48 ; [ 0x20 0x48 0x1f ]
DEY ; [ 0x88 ]
BNE #$eb ; [ 0xd0 0xeb ]
STX $1742 ; [ 0x8e 0x42 0x17 ]
LDA #$00 ; [ 0xa9 0x00 ]
STA $1741 ; [ 0x8d 0x41 0x17 ]
JMP $1efe ; [ 0x4c 0xfe 0x1e ]
STY $fc ; [ 0x84 0xfc ]
TAY ; [ 0xa8 ]
LDA $1fe7,Y ; [ 0xb9 0xe7 0x1f ]
LDY #$00 ; [ 0xa0 0x00 ]
STY $1740 ; [ 0x8c 0x40 0x17 ]
STX $1742 ; [ 0x8e 0x42 0x17 ]
STA $1740 ; [ 0x8d 0x40 0x17 ]
LDY #$7f ; [ 0xa0 0x7f ]
DEY ; [ 0x88 ]
BNE #$fd ; [ 0xd0 0xfd ]
INX ; [ 0xe8 ]
INX ; [ 0xe8 ]
LDY $fc ; [ 0xa4 0xfc ]
RTS ; [ 0x60 ]
INC $fa ; [ 0xe6 0xfa ]
BNE #$02 ; [ 0xd0 0x02 ]
INC $fb ; [ 0xe6 0xfb ]
RTS ; [ 0x60 ]
LDX #$21 ; [ 0xa2 0x21 ]
LDY #$01 ; [ 0xa0 0x01 ]
JSR $1f02 ; [ 0x20 0x02 0x1f ]
BNE #$07 ; [ 0xd0 0x07 ]
CPX #$27 ; [ 0xe0 0x27 ]
BNE #$f5 ; [ 0xd0 0xf5 ]
LDA #$15 ; [ 0xa9 0x15 ]
RTS ; [ 0x60 ]
LDY #$ff ; [ 0xa0 0xff ]
ASL ; [ 0x0a ]
BCS $03 ; [ 0xb0 0x03 ]
INY ; [ 0xc8 ]
BPL #$fa ; [ 0x10 0xfa ]
TXA ; [ 0x8a ]
AND #$0f ; [ 0x29 0x0f ]
LSR ; [ 0x4a ]
TAX ; [ 0xaa ]
TYA ; [ 0x98 ]
BPL #$03 ; [ 0x10 0x03 ]
CLC ; [ 0x18 ]
ADC #$07 ; [ 0x69 0x07 ]
DEX ; [ 0xca ]
BNE #$fa ; [ 0xd0 0xfa ]
RTS ; [ 0x60 ]
CLC ; [ 0x18 ]
ADC $f7 ; [ 0x65 0xf7 ]
STA $f7 ; [ 0x85 0xf7 ]
LDA $f6 ; [ 0xa5 0xf6 ]
ADC #$00 ; [ 0x69 0x00 ]
STA $f6 ; [ 0x85 0xf6 ]
RTS ; [ 0x60 ]
JSR $1e5a ; [ 0x20 0x5a 0x1e ]
JSR $1fac ; [ 0x20 0xac 0x1f ]
JSR $1e5a ; [ 0x20 0x5a 0x1e ]
JSR $1fac ; [ 0x20 0xac 0x1f ]
LDA $f8 ; [ 0xa5 0xf8 ]
RTS ; [ 0x60 ]
CMP #$30 ; [ 0xc9 0x30 ]
BMI #$1b ; [ 0x30 0x1b ]
CMP #$47 ; [ 0xc9 0x47 ]
BPL #$17 ; [ 0x10 0x17 ]
CMP #$40 ; [ 0xc9 0x40 ]
BMI #$03 ; [ 0x30 0x03 ]
CLC ; [ 0x18 ]
ADC #$09 ; [ 0x69 0x09 ]
ROL ; [ 0x2a ]
ROL ; [ 0x2a ]
ROL ; [ 0x2a ]
ROL ; [ 0x2a ]
LDY #$04 ; [ 0xa0 0x04 ]
ROL ; [ 0x2a ]
ROL $f8 ; [ 0x26 0xf8 ]
ROL $f9 ; [ 0x26 0xf9 ]
DEY ; [ 0x88 ]
BNE #$f8 ; [ 0xd0 0xf8 ]
LDA #$00 ; [ 0xa9 0x00 ]
RTS ; [ 0x60 ]
LDA $f8 ; [ 0xa5 0xf8 ]
STA $fa ; [ 0x85 0xfa ]
LDA $f9 ; [ 0xa5 0xf9 ]
STA $fb ; [ 0x85 0xfb ]
RTS ; [ 0x60 ]
BRK ; [ 0x00 ]
BRK ; [ 0x00 ]
BRK ; [ 0x00 ]
BRK ; [ 0x00 ]
BRK ; [ 0x00 ]
BRK ; [ 0x00 ]
ASL ; [ 0x0a ]
ORA $494d ; [ 0x0d 0x4d 0x49 ]
JSR $5213 ; [ 0x20 0x13 0x52 ]
EOR $20 ; [ 0x45 0x20 ]
STX $db ; [ 0x86 0xdb ]
INC $ed ; [ 0xe6 0xed ]
SBC $ff87,X ; [ 0xfd 0x87 0xff ]
LDA $f9de,Y ; [ 0xb9 0xde 0xf9 ]
SBC ($ff),Y ; [ 0xf1 0xff ]
Generated
+4213 -22
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -2,7 +2,9 @@
members = [
"core",
"cli",
"macroquad"
"macroquad",
# "beneater"
"egui"
]
resolver="2"
@@ -15,3 +17,4 @@ clap = { version = "4.5", features = ["derive"] }
# trevors_utilities = { git = "https://git.geekback.dev/tmerritt/trevors_utilities" }
trevors_utilities = { path = "/home/tmerritt/Projects/trevors_utilities" }
lipsum = "0.9"
macroquad = "0.4"
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "beneater"
version = "0.1.0"
edition = "2024"
[dependencies]
core = { path = "../core" }
macroquad.workspace = true
View File
+64
View File
@@ -0,0 +1,64 @@
// This is the GUI for the BenEater PC
use beneater::parts::backplane::Backplane;
use beneater::parts::ben_eater_pc::BenEaterPC;
use beneater::parts::cpu_display::CpuDisplay;
use macroquad::prelude::*;
use macroquad::telemetry::frame;
use core::mos6502cpu::cpu::Mos6502Cpu;
use core::periph::at28c256::At28C256;
use core::periph::hm62256::Hm62256;
use core::constants::constants_system::*;
use std::fs;
/// BenEater computer represents the 'Ben Eater' 6502 breadboard computer.
/// This was built along watching the video series where Ben builds a
/// 6502 on a breadboard and explains each step.
///
pub struct BenEater {
cpu: Mos6502Cpu,
ram: At28C256,
rom: Hm62256
}
impl BenEater {
// pub fn new(rom: &[u8; SIZE_32KB]) -> BenEater {
//
// }
}
#[macroquad::main("Ben Eaters PC")]
async fn main() {
println!("Taxation is Theft");
// let rom_to_run = fs::read("resources/beneater/roms/ror.bin");
// let mut pc = BenEater::new(&rom_to_run);
//
// let mut backplane = Backplane::new();
// backplane.load_rom("resources/beneater/roms/ror.bin");
// let mut dm = DisplayMatrix::new(200.0, 50.0);
// dm.push_letter('T');
let mut frame_number: u32 = 0x00;
loop {
clear_background(BLUE);
draw_text("Ben Eater", 20.0, 20.0, 30.0, BLACK);
// dm.render(20.0, 40.0);
// CpuDisplay::render(&computer.cpu, 20.0, 120.0);
frame_number += 1;
if frame_number.is_multiple_of(60) {
// dm.push_letter('X');
// computer.tick_system();
}
if frame_number.is_multiple_of(60 * 6) {
// dm.clear_display()
}
next_frame().await
}
}
+56
View File
@@ -0,0 +1,56 @@
#![feature(slice_as_array)]
use core::constants::constants_system::SIZE_32KB;
use core::constants::constants_isa_op::*;
use std::fs;
use std::path::Path;
fn le_swap(to_swap: &[u8; SIZE_32KB]) -> [u8; 32768] {
let mut work: [u8; SIZE_32KB] = [0x00; SIZE_32KB];
for i in (0..SIZE_32KB).step_by(2) {
work[i] = to_swap[i+1];
work[i + 1] = to_swap[i];
}
work
}
fn main() {
// make the rom data in memory.
// Fill with 0xea -> NOP
let mut vec: [u8; SIZE_32KB] = [ISA_OP_NOP; SIZE_32KB];
// Load to A
vec[0] = ISA_OP_LDA_I; // LDA #$ab
vec[1] = 0b1010_1011; // 1010 1011
// Jump to rotate cycle
vec[2] = 0x02; // --
vec[3] = 0x03; // --
// Jump to Main
vec[0x2210] = ISA_OP_JMP_ABS;
vec[0x2211] = 0x00;
vec[0x2212] = 0x40;
// load to a
vec[0x4000] = ISA_OP_LDA_I;
vec[0x4001] = 0b0101_0100;
// jump to top of load to a
vec[0x4002] = ISA_OP_JMP_ABS;
vec[0x4003] = 0x00;
vec[0x4004] = 0x40;
vec[0x4005] = ISA_OP_NOP;
vec[0x7ffa] = 0x22; // NMI Vector
vec[0x7ffb] = 0x11;
vec[0x7ffc] = 0x12; // Reset Vector
vec[0x7ffd] = 0x34;
vec[0x7ffe] = 0x43; // Interrupt Vector
vec[0x7fff] = 0x21;
vec = le_swap(&vec);
// write the rom to disk
fs::write("outputfile.bin", &vec[..]).expect("TODO: panic message");
}
+33
View File
@@ -0,0 +1,33 @@
use beneater::parts::backplane::Backplane;
use core::constants::constants_isa_op::*;
use core::constants::constants_system::*;
use std::fs;
use std::ops::Index;
fn main() {
println!("Taxation is Theft");
let mut backplane = Backplane::new();
for i in 0..12 {
backplane.tick();
}
//backplane.load_rom();
println!("Backplane is live.");
let mut new_program = [0x00u8; SIZE_32KB];
new_program[(OFFSET_RESET_VECTOR - SIZE_32KB as u16) as usize] = 0x00;
new_program[(OFFSET_RESET_VECTOR + 1 - SIZE_32KB as u16) as usize] = 0x60;
println!("Set offset in rom...");
println!(
"VALUE AT OFFSET_RESET_VECTOR = 0x{:04x} ",
new_program[(OFFSET_RESET_VECTOR - SIZE_32KB as u16) as usize]
);
// println!("{:?}", new_program);
// backplane.rom.program(&new_program);
// backplane.cpu.pc = 0x6000;
// backplane.memory[0x6000] = ISA_OP_LDA_I;
// backplane.memory[0x6001] = 0xab;
// backplane.tick();
}
+2
View File
@@ -0,0 +1,2 @@
pub mod parts;
pub mod backplane;
+9
View File
@@ -0,0 +1,9 @@
pub struct AddressBus {
pub address: u16,
}
impl AddressBus {
pub fn new() -> Self {
AddressBus { address: 0x0000 }
}
}
+127
View File
@@ -0,0 +1,127 @@
use crate::parts::mos6522_peripheral::Mos6522Peripheral;
use crate::parts::via6522::VIA6522;
use core::constants::constants_system::*;
use core::mos6502cpu::Mos6502Cpu;
use core::periph::at28c256::At28C256;
use core::traits::rom_chip::RomChip;
use core::constants::constants_via6522::*;
/// Backplane
///
/// A Backplane hold and coordinates the cpu with the
/// rest of the peripherals.
///
/// This system has
/// -> VIA6522 for peripherals
/// -> 322KB RAM
/// -> 32KB ROM
pub struct Backplane {
// pub for dev
pub cpu: Mos6502Cpu,
pub via: VIA6522,
pub memory: Box<[u8]>,
pub rom: At28C256,
data_bus: u8,
address_bus: u16
}
impl Backplane {
pub fn new() -> Self {
Backplane {
cpu: Mos6502Cpu::default(),
via: VIA6522::default(),
memory: Box::new([0x00; SIZE_32KB]),
rom: At28C256::default(),
data_bus: 0x00,
address_bus: 0x0000
}
}
pub fn load_rom(&mut self, to_load: &[u8; SIZE_32KB]) {
self.rom.program((*to_load).into());
}
pub fn tick(&mut self) {
// is the CPU in read or write state
self.address_bus = self.cpu.address_bus();
self.data_bus = self.cpu.data_bus();
let rw = self.cpu.read_signal;
println!("- TICK START:");
println!("| CPU: Address: 0b{:016b} Data: 0b{:08b} CPU RW: {rw}", self.address_bus, self.data_bus);
// via state
println!(
"| VIA 0b{:08b}/0b{:08b}/0b{:08b}/0b{:08b}/",
self.via.read(VIA6522_ORA),
self.via.read(VIA6522_ORA),
self.via.read(VIA6522_DDRB),
self.via.read(VIA6522_DDRA),
);
println!("| LCD");
// if we are reading
if rw {
println!("CPU HAS SET READ FLAG FOR ADDRESS {:04x} with data 0x{:02x}", self.address_bus, self.data_bus);
match self.address_bus {
0x0000..=0x3fff => {
// read from ram
},
0x4000..=0x7fff => {
// read from ROM
}
_ => {
println!("READ OUTSIDE DATA RANGE");
}
}
} else {
println!("CPU HAS SET WRITE FLAG FOR ADDRESS 0x{:04x} with data 0x{:02x}", self.address_bus, self.data_bus);
match self.address_bus {
0x6000..=0x600f => {
self.via.write((self.address_bus - 0x6000) as u8, self.data_bus);
},
0x0000..=0x3fff => {
self.memory[self.address_bus as usize] = self.data_bus;
}
_ => {
println!("ATTEMPT TO WRITE OUTSIDE OF MEMORY");
}
}
}
//
//
// match address {
// /// VIA
// 0x6000..=0x600f => {
// if self.cpu.read_signal {
// self.cpu
// .set_data_bus(self.via.read((address - 0x6000) as u8));
// } else {
// self.via
// .write((address - 0x6000) as u8, self.cpu.data_bus());
// }
// self.via.tick();
// }
// /// RAM
// 0x0000..=0x3fff => {
// if self.cpu.read_signal {
// self.cpu.set_data_bus(self.memory[address as usize]);
// } else {
// self.memory[address as usize] = data;
// }
// }
// /// ROM
// 0x4000..=0x7fff => {
// println!("ROM READ AT {address:04x} / ROM OFFSET {:04x}", address - 0x4000);
// self.rom.read(&(address - 0x4000));
// }
// /// The ether. Scarrrrrrrryyyy......
// _ => {
// println!("XXXXLost READ:?{} to {:04x}", self.cpu.read_signal, address);
// }
// }
println!("- TICK DONE");
}
}
+60
View File
@@ -0,0 +1,60 @@
use crate::parts::clock::Clock;
use core::constants::constants_system::*;
use core::mos6502cpu::cpu::Mos6502Cpu;
use std::fs;
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
pub struct BenEaterPC {
clock: Clock,
// pub because i am rendering it.
// there should be a proper interface to get the data
// for ui out of this.
pub cpu: Mos6502Cpu,
}
impl BenEaterPC {
pub fn new() -> Self {
println!("New BENEATERPC");
BenEaterPC {
clock: Clock::new(),
cpu: Mos6502Cpu::default(),
}
}
pub fn tick_system(&mut self) {
self.cpu.tick();
if self.cpu.microcode_step == 0 {
// tick the clock.
// tick the memory
// tick the VIA
} else {
self.cpu.tick();
}
}
pub fn load_rom(&mut self, rom_to_load: &str) {
println!("Preparing to load {rom_to_load}");
let file = File::open(rom_to_load).unwrap();
let mut reader = BufReader::new(file);
let mut chunks = Vec::new();
loop {
let mut buffer = vec![0u8; 1];
let bytes_read = reader.read(&mut buffer).unwrap_or(0);
if bytes_read == 0 {
break;
}
buffer.truncate(bytes_read);
chunks.push(buffer[0]);
}
let data_size = chunks.len();
print!("Loaded {}b of data. Poking into memory...", data_size);
for i in 0..SIZE_32KB {
self.cpu.poke(i as u16, chunks[i]);
}
println!("poke complete. Poked {}b of data.", data_size);
}
}
+21
View File
@@ -0,0 +1,21 @@
pub struct Clock {
ticks: u32,
}
impl Clock {
pub fn new() -> Self {
Clock { ticks: 0 }
}
pub fn tick(&mut self) {
self.ticks += 1;
}
pub fn ticks(&self) -> u32 {
self.ticks
}
pub fn reset(&mut self) {
self.ticks = 0;
}
}
+59
View File
@@ -0,0 +1,59 @@
use core::mos6502cpu::cpu::Mos6502Cpu;
use macroquad::color::{BLACK, Color};
use macroquad::prelude::*;
use macroquad::text::draw_text;
pub struct CpuDisplay {}
impl CpuDisplay {
pub fn render(cpu: &Mos6502Cpu, x_offset: f32, y_offset: f32) {
// get the data to display...
let (pc, a, x, y, address_bus, data_bus, microsteps_remaining, reset_vector, interrupt_vector, nmi_vector) = cpu.dump_data();
// ...build the interface
Self::draw_square(x_offset, y_offset, x_offset + 300.0, y_offset + 85.0, BLACK);
draw_text(
format!("PC: 0x{:04x} / {}", pc, pc).as_str(),
x_offset + 5.0,
y_offset + 18.0,
15.0,
BLACK,
);
draw_text(
format!("A: 0x{:02x} X: 0x{:02x} Y: 0x{:02x}", a, x, y).as_str(),
x_offset + 5.0,
y_offset + 35.0,
15.0,
BLACK,
);
draw_text(
format!("Address: {:016b} | {:04x}", address_bus, address_bus).as_str(),
x_offset + 5.0,
y_offset + 55.0,
15.0,
BLACK,
);
draw_text(
format!("Data: {:08b} | {:02x}", data_bus, data_bus).as_str(),
x_offset + 5.0,
y_offset + 75.0,
15.0,
BLACK,
);
draw_text(
format!("MS: {:02x}", microsteps_remaining).as_str(),
x_offset + 5.0,
y_offset + 95.0,
15.0,
BLACK,
);
}
fn draw_square(x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
// println!("Square from {x1:2.0}x{y1:2.0} to {x2:2.0}x{y2:2.0} with {:?}", color);
draw_line(x1, y1, x2, y1, 1.0, color);
draw_line(x1, y1, x1, y2, 1.0, color);
draw_line(x1, y2, x2, y2, 1.0, color);
draw_line(x2, y1, x2, y2, 1.0, color);
}
}
+9
View File
@@ -0,0 +1,9 @@
pub struct DataBus {
pub data: u8,
}
impl DataBus {
pub fn new() -> Self {
DataBus { data: 0x00 }
}
}
+274
View File
@@ -0,0 +1,274 @@
use crate::parts::mos6522_peripheral::Mos6522Peripheral;
use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct HD44780 {
// Bus inputs
data_bus: u8,
rs: bool,
rw: bool,
enable: bool,
prev_enable: bool,
// Internal memory
ddram: [u8; 80],
cgram: [u8; 64],
// Cursor & display state
cursor_position: u8,
display_on: bool,
cursor_on: bool,
blink_on: bool,
entry_increment: bool,
entry_shift: bool,
// Function set flags
data_length_8bit: bool,
two_line_mode: bool,
font_5x10: bool,
// Busy flag
busy: bool,
last_command_time: Instant,
}
impl Mos6522Peripheral for HD44780 {
fn write(&mut self, port: u8) {
println!("Writing {port:08b}/{port:02x} to HD44780");
self.set_data_bus(port);
}
/// Control
///
/// Takes a u8 with the bottom 3 bits representing
/// X X X X X rs rw en
fn control(&mut self, control: u8) {
self.write_control_lines(
control & 1 << 2 == 0,
control & 1 << 1 == 0,
control & 1 == 0,
);
}
fn read(&mut self) -> u8 {
self.data_bus
}
fn tick(&mut self) {
println!("TICK OF HD44780");
if self.busy && self.last_command_time.elapsed() > Duration::from_micros(50) {
self.busy = false;
}
}
}
impl HD44780 {
pub fn new() -> Self {
Self {
data_bus: 0,
rs: false,
rw: false,
enable: false,
prev_enable: false,
ddram: [b' '; 80],
cgram: [0; 64],
cursor_position: 0,
display_on: true,
cursor_on: false,
blink_on: false,
entry_increment: true,
entry_shift: false,
data_length_8bit: true,
two_line_mode: true,
font_5x10: false,
busy: false,
last_command_time: Instant::now(),
}
}
pub fn write_control_lines(&mut self, rs: bool, rw: bool, enable: bool) {
self.rs = rs;
self.rw = rw;
// On rising edge of Enable
if enable && !self.prev_enable {
self.evaluate();
}
self.prev_enable = enable;
}
pub fn set_data_bus(&mut self, value: u8) {
self.data_bus = value;
}
pub fn read_data_bus(&self) -> u8 {
if !self.rs && self.rw {
// Return busy flag + current address
let busy_flag = if self.busy { 0x80 } else { 0x00 };
busy_flag | (self.cursor_position & 0x7F)
} else {
// Not implemented: read from DDRAM/CGRAM
0
}
}
fn evaluate(&mut self) {
if self.rw {
return;
}
if self.rs {
self.write_data(self.data_bus);
} else {
self.execute_command(self.data_bus);
}
self.busy = true;
self.last_command_time = Instant::now();
}
fn write_data(&mut self, data: u8) {
if self.cursor_position < 0x50 {
self.ddram[self.cursor_position as usize] = data;
self.cursor_position = self.cursor_position.wrapping_add(1);
}
}
fn execute_command(&mut self, cmd: u8) {
match cmd {
0x01 => {
self.ddram.fill(b' ');
self.cursor_position = 0;
}
0x02 => {
self.cursor_position = 0;
}
0x04..=0x07 => {
self.entry_increment = (cmd & 0b10) != 0;
self.entry_shift = (cmd & 0b01) != 0;
}
0x08..=0x0F => {
self.display_on = (cmd & 0b100) != 0;
self.cursor_on = (cmd & 0b010) != 0;
self.blink_on = (cmd & 0b001) != 0;
}
0x10..=0x1F => {
// Cursor/display shift — not yet implemented
}
0x20..=0x3F => {
self.data_length_8bit = (cmd & 0b10000) != 0;
self.two_line_mode = (cmd & 0b1000) != 0;
self.font_5x10 = (cmd & 0b100) != 0;
}
0x40..=0x7F => {
// Set CGRAM address — stub
}
0x80..=0xFF => {
self.cursor_position = cmd & 0x7F;
}
_ => {}
}
}
pub fn get_display_lines(&self) -> (String, String) {
let row1: String = self.ddram[0x00..0x10].iter().map(|&b| b as char).collect();
let row2: String = self.ddram[0x40..0x50].iter().map(|&b| b as char).collect();
(row1, row2)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn pulse_enable(lcd: &mut HD44780) {
lcd.write_control_lines(lcd.rs, lcd.rw, true);
lcd.write_control_lines(lcd.rs, lcd.rw, false);
}
#[test]
fn test_clear_display() {
let mut lcd = HD44780::new();
lcd.set_data_bus(0x01);
pulse_enable(&mut lcd);
lcd.tick();
assert!(lcd.ddram.iter().all(|&b| b == b' '));
}
#[test]
#[ignore]
fn test_write_data() {
let mut lcd = HD44780::new();
lcd.set_data_bus(0x41); // 'A'
pulse_enable(&mut lcd);
lcd.tick();
assert_eq!(lcd.ddram[0], b'A');
}
#[test]
fn test_set_cursor() {
let mut lcd = HD44780::new();
lcd.set_data_bus(0x80 | 0x40);
pulse_enable(&mut lcd);
lcd.tick();
assert_eq!(lcd.cursor_position, 0x40);
}
#[test]
fn test_read_busy_flag_and_address() {
let mut lcd = HD44780::new();
lcd.busy = true;
lcd.cursor_position = 0x15;
lcd.write_control_lines(false, true, true);
let data = lcd.read_data_bus();
assert_eq!(data, 0x80 | 0x15);
}
#[test]
fn hello_world_display() {
let mut lcd = HD44780::new();
let setup_instructions = [
0b0000_0000, // cls
0b0011_1000, // display mode
0b0000_1110, // display on
0b0000_0110, // entry mode
];
for instruction in setup_instructions {
lcd.write_control_lines(false, false, true);
lcd.set_data_bus(instruction);
pulse_enable(&mut lcd);
lcd.tick();
}
let letter_instructions = [
0b0100_1000, // H
0b0100_0101, // E
0b0100_1100, // L
0b0100_1100, // L
0b0100_1111, // O
0b0100_0000, // _
0b0101_0111, // W
0b0100_1111, // O
0b0101_0010, // R
0b0100_1100, // L
0b0100_0100, // D
];
// move cursor to start of next line.
lcd.write_control_lines(true, false, true);
lcd.set_data_bus(0b1100_0000);
pulse_enable(&mut lcd);
lcd.tick();
let (line1, line2) = lcd.get_display_lines();
println!("LINE1 -> [{}]", line1);
println!("LINE2 -> [{}]", line2);
}
}
+10
View File
@@ -0,0 +1,10 @@
pub mod address_bus;
pub mod backplane;
pub mod ben_eater_pc;
pub mod clock;
pub mod cpu_display;
pub mod data_bus;
pub mod display_matrix;
pub mod mos6522_peripheral;
pub mod ram_display;
pub mod via6522;
+12
View File
@@ -0,0 +1,12 @@
pub trait Mos6522Peripheral {
/// Write to the data bus
fn write(&mut self, port: u8);
fn control(&mut self, control: u8);
/// Read the data bus
fn read(&mut self) -> u8;
/// Tick
///
/// Run 1 clock cycle of the peripheral
fn tick(&mut self);
}
+25
View File
@@ -0,0 +1,25 @@
use macroquad::prelude::*;
pub struct RamDisplay {}
impl RamDisplay {
pub fn render(ram: &Box<[u8]>, x_offset: f32, y_offset: f32) {
Self::draw_square(
x_offset,
y_offset,
x_offset + 200.0,
y_offset + 300.0,
BLACK,
);
draw_text("RAM", x_offset + 5.0, y_offset + 5.0, 15.0, BLACK);
}
fn draw_square(x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
// println!("Square from {x1:2.0}x{y1:2.0} to {x2:2.0}x{y2:2.0} with {:?}", color);
draw_line(x1, y1, x2, y1, 1.0, color);
draw_line(x1, y1, x1, y2, 1.0, color);
draw_line(x1, y2, x2, y2, 1.0, color);
draw_line(x2, y1, x2, y2, 1.0, color);
}
}
+219
View File
@@ -0,0 +1,219 @@
use crate::parts::display_matrix::HD44780;
use crate::parts::mos6522_peripheral::Mos6522Peripheral;
use core::constants::constants_system::*;
#[derive(Default)]
pub struct VIA6522 {
// Data registers
ora: u8,
orb: u8,
// Data direction
ddra: u8,
ddrb: u8,
// Control lines (external pins)
ca1: bool,
ca2: bool,
cb1: bool,
cb2: bool,
// Timers
t1_counter: u16,
t1_latch: u16,
t1_enabled: bool,
t2_counter: u16,
t2_latch: u16,
t2_enabled: bool,
// Interrupt flags
ifr: u8,
ier: u8,
// Peripheral (e.g., LCD)
pub lcd: Option<Box<dyn Mos6522Peripheral>>,
}
impl VIA6522 {
pub fn new() -> Self {
Self {
ora: 0,
orb: 0,
ddra: 0,
ddrb: 0,
ca1: false,
ca2: false,
cb1: false,
cb2: false,
t1_counter: 0,
t1_latch: 0,
t1_enabled: false,
t2_counter: 0,
t2_latch: 0,
t2_enabled: false,
ifr: 0,
ier: 0,
lcd: Some(Box::new(HD44780::new())),
}
}
pub fn read(&self, addr: u8) -> u8 {
match (addr & 0x0F) {
VIA6522_ORB => self.orb,
VIA6522_ORA => self.ora,
VIA6522_DDRB => self.ddrb,
VIA6522_DDRA => self.ddra,
0x4 => (self.t1_counter & 0xFF) as u8,
0x5 => (self.t1_counter >> 8) as u8,
0x6 => (self.t1_latch & 0xFF) as u8,
0x7 => (self.t1_latch >> 8) as u8,
0x8 => (self.t2_counter & 0xFF) as u8,
0x9 => (self.t2_latch & 0xFF) as u8,
0xD => self.ifr,
0xE => self.ier | 0x80,
_ => 0,
}
}
pub fn write(&mut self, addr: u8, value: u8) {
println!("VIA6522 write value 0x{value:02x} to address 0x{addr:02x}");
match addr & 0x0F {
VIA6522_ORB => self.orb = value,
VIA6522_ORA => self.ora = value,
VIA6522_DDRB => self.ddrb = value,
VIA6522_DDRA => self.ddra = value,
0x4 => {
self.t1_latch = (self.t1_latch & 0xFF00) | value as u16;
self.t1_counter = self.t1_latch;
}
0x5 => {
self.t1_latch = (value as u16) << 8 | (self.t1_latch & 0x00FF);
self.t1_counter = self.t1_latch;
self.t1_enabled = true;
}
0x6 => self.t1_latch = (self.t1_latch & 0xFF00) | value as u16,
0x7 => self.t1_latch = (value as u16) << 8 | (self.t1_latch & 0x00FF),
0x8 => self.t2_counter = value as u16,
0x9 => self.t2_latch = value as u16,
0xD => self.ifr &= !value, // Clear interrupt flags
0xE => {
if value & 0x80 != 0 {
self.ier |= value & 0x7F;
} else {
self.ier &= !(value & 0x7F);
}
}
_ => {}
}
}
pub fn tick(&mut self) {
if self.t1_enabled {
if self.t1_counter > 0 {
self.t1_counter -= 1;
if self.t1_counter == 0 {
self.ifr |= 0x40; // Set T1 interrupt
}
}
}
if self.t2_enabled {
if self.t2_counter > 0 {
self.t2_counter -= 1;
if self.t2_counter == 0 {
self.ifr |= 0x20; // Set T2 interrupt
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initialization() {
let via = VIA6522::new();
assert_eq!(via.ora, 0);
assert_eq!(via.orb, 0);
assert_eq!(via.ddra, 0);
assert_eq!(via.ddrb, 0);
assert_eq!(via.t1_counter, 0);
assert_eq!(via.t2_counter, 0);
assert!(via.lcd.is_some());
}
#[test]
fn test_read_write_registers() {
let mut via = VIA6522::new();
via.write(0x0, 0xAA);
assert_eq!(via.read(0x0), 0xAA);
via.write(0x1, 0xBB);
assert_eq!(via.read(0x1), 0xBB);
via.write(0x2, 0xCC);
assert_eq!(via.read(0x2), 0xCC);
via.write(0x3, 0xDD);
assert_eq!(via.read(0x3), 0xDD);
}
#[test]
#[ignore]
fn test_timer1_write_and_tick() {
let mut via = VIA6522::new();
via.write(0x4, 0x34); // Low byte
via.write(0x5, 0x12); // High byte, also enables timer
assert_eq!(via.t1_latch, 0x1234);
assert_eq!(via.t1_counter, 0x1234);
assert!(via.t1_enabled);
for _ in 0..0x1234 {
via.tick();
}
assert_eq!(via.t1_counter, 0);
assert_eq!(via.ifr & 0x40, 0x40); // T1 interrupt flag set
}
#[test]
#[ignore]
fn test_timer2_write_and_tick() {
let mut via = VIA6522::new();
via.t2_enabled = true;
via.write(0x8, 0x05); // Counter
for _ in 0..5 {
via.tick();
}
assert_eq!(via.t2_counter, 0);
assert_eq!(via.ifr & 0x20, 0x20); // T2 interrupt flag set
}
#[test]
#[ignore]
fn test_interrupt_enable_disable() {
let mut via = VIA6522::new();
via.write(0xE, 0x82); // Enable bit 0x02
assert_eq!(via.ier & 0x02, 0x02);
via.write(0xE, 0x02); // Disable bit 0x02
assert_eq!(via.ier & 0x02, 0x00);
}
#[test]
#[ignore]
fn test_clear_interrupt_flags() {
let mut via = VIA6522::new();
via.ifr = 0xFF;
via.write(0xD, 0x40); // Clear T1 interrupt
assert_eq!(via.ifr & 0x40, 0x00);
}
}
+223
View File
@@ -0,0 +1,223 @@
/*
64tass -a -o instructions.bin -b instructions.asm &&
cargo run --bin de6502 -- -v instructions.bin instructions_produced.asm &&
64tass -a -b instructions_produced.asm -o instructions_produced.bin
*/
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::path::PathBuf;
use std::thread::current;
use core::constants::constants_system::*;
use core::constants::constants_isa_op::*;
use clap::{Parser, Subcommand};
use core::instruction_table::INSTRUCTION_TABLE;
use std::io::Write;
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct CliOptions {
/// File to Decompile
input: PathBuf,
/// File to write
output: PathBuf,
/// Verbose output
#[arg(short, action = clap::ArgAction::Count)]
verbose: u8
}
#[derive(Debug)]
struct DecompiledLine {
offset: u16,
text: String,
label: Option<String>,
bytes: Vec<u8>
}
impl DecompiledLine {
/// is_jump
///
/// Check if the current line is a type of a jump
pub fn is_jump(&self) -> bool {
self.bytes[0] == ISA_OP_JMP_ABS || self.bytes[0] == ISA_OP_JMP_IND
}
/// is_branch
///
/// Check if the current line is a branch
pub fn is_branch(&self) -> bool {
self.bytes[0] == ISA_OP_BCC || self.bytes[0] == ISA_OP_BCS ||
self.bytes[0] == ISA_OP_BEQ || self.bytes[0] == ISA_OP_BMI ||
self.bytes[0] == ISA_OP_BNE || self.bytes[0] == ISA_OP_BPL ||
self.bytes[0] == ISA_OP_BVS || self.bytes[0] == ISA_OP_BVC
}
}
/// WorkingProgram
///
/// Where we keep the program being decompiled
struct WorkingProgram {
data: [u8; SIZE_64KB],
lines: Vec<DecompiledLine>
}
impl WorkingProgram {
}
fn parse_to_decompiled_lines(data: &[u8]) -> HashMap<u16, DecompiledLine> {
println!("PARSE GOT {}b", data.len());
let mut current_data_position = 0;
let mut lines: HashMap<u16, DecompiledLine> = HashMap::new();
while current_data_position < data.len() as u16 {
// read the next byte.
let next_byte = data[current_data_position as usize];
let mut bytes = vec![next_byte];
println!("Bytes = {bytes:?}");
let target_op = INSTRUCTION_TABLE[next_byte as usize].clone();
if let Some(top) = target_op {
// println!("Instruction {:?}/{:?} needs {:?} bytes.", top.operation, top.mode, top.length);
let num_bytes_to_load = top.length - 1;
// println!("Need {num_bytes_to_load} more bytes.");
let mut formatted_asm = String::from(top.format_prefix);
let mut param = None;
if num_bytes_to_load == 1 {
bytes.push(data[current_data_position as usize + 1]);
formatted_asm = format!("{}{:02x}{}", formatted_asm, bytes[1], top.format_postfix);
};
if num_bytes_to_load == 2 {
bytes.push(data[current_data_position as usize + 1]);
bytes.push(data[current_data_position as usize + 2]);
// 16 bit parameter.
param = Some(((bytes[2] as u16) << 8) | bytes[1] as u16);
formatted_asm = format!("{}{:04x}{}", formatted_asm, param.unwrap(), top.format_postfix);
};
// figure out how long the instruction is and read that much more data
lines.insert(current_data_position, DecompiledLine {
offset: current_data_position,
text: String::from(formatted_asm),
label: None,
bytes,
});
current_data_position = current_data_position + top.length as u16;
} else {
// invalid instruction
println!("Byte sequence 0x{} invalid.", next_byte);
current_data_position += 1;
}
}
lines
}
fn decompile(data: &Vec<u8>) -> Vec<(u16, DecompiledLine)> {
println!("Preparing to decompile {}b", data.len());
let mut current_data_position: u16 = 0;
let mut lines: HashMap<u16, DecompiledLine> = parse_to_decompiled_lines(data);
println!("Found {} instructions in pass 1. Adding labels for jumps.", lines.len());
//let targets = vec![];
let mut items: Vec<_> = lines.into_iter().collect();
items.sort_by(|a, b| a.0.cmp(&b.0));
// loop through the lines for JSR and label the 'target' as SUB<index>
// let mut jump_targets = vec![];
for (line_index, working_line) in &mut items {
if working_line.is_jump() {
println!("There are {}b in bytes", working_line.bytes.len());
let target = read_word(&working_line.bytes[1..=2]);
println!("Found a jump at index {line_index} / 0x{line_index} with target of 0x{:04x}", target);;
// Derp -> this doesnt work.
// items[target as usize].1.label = Some("Label1".parse().unwrap());
}
}
// // check the vectors to label those entrypoints.
// let (mut main, mut nmi, mut irq) = (0, 0, 0);
// match data.len() {
// SIZE_32KB | SIZE_64KB => {
// main = read_word(&data[OFFSET_RESET_VECTORS..=OFFSET_RESET_VECTORS + 1]);
// nmi = read_word(&data[OFFSET_NMI_VECTORS..=OFFSET_NMI_VECTORS + 1]);
// irq = read_word(&data[OFFSET_INT_VECTORS..=OFFSET_INT_VECTORS]);
// }
// _ => {
// // dont know where to look for our offsets. :(
// }
// }
// -> main
// -> NMI
// -> IRQ
items
}
/// read_word
///
/// Read a word from the provided data with first byte being
/// LSB
fn read_word(from: &[u8]) -> u16 {
(from[1] as u16) << 8 | from[0] as u16
}
/// read_word_swap
///
/// Read a word from the provided data with first byte being
/// MSB
fn read_word_swap(from: &[u8]) -> u16 {
(from[0] as u16) << 8 | from[1] as u16
}
/// format_bytes
///
/// formats data for stuff
fn format_bytes(from: &Vec<u8>) -> String {
let mut to_return = String::from("[");
for index in 0..from.len() {
to_return = format!("{} 0x{:02x}", to_return, from[index]);
}
to_return = format!("{} ]", to_return);
to_return
}
fn main() {
let opts = CliOptions::parse();
println!("Taxation is theft.");
// Load program to an array...
let data = vec![
0xa9, 0xab, 0x4c, 0x00, 0x40, 0xa9, 0x54, 0x4c, 0x00, 0x40,
ISA_OP_TYA, ISA_OP_TXA, ISA_OP_TSX
];
let loaded_data = fs::read(opts.input).unwrap();
println!("Loaded {}b", loaded_data.len());
// ...load the array to the WorkingProgram structure...
let result = decompile(&loaded_data);
let mut output_file = File::create(opts.output).unwrap();
// ...reap the decompiled code.
for (_, line) in &result {
let formatted_bytes = format_bytes(&line.bytes);
let output_line =format!("{:width$}; {}", line.text, formatted_bytes, width=30 );
// do we need a label
if let Some(label) = &line.label {
println!("There is a label - {label}");
}
if opts.verbose > 0 {
println!("{}", output_line);
}
writeln!(output_file, "{}", output_line).expect("cant write output file. you picked the red snapper.");
}
println!("OPTS = {}", opts.verbose);
}
+14 -5
View File
@@ -1,12 +1,21 @@
use core::instruction::Instruction;
use core::address_mode::AddressMode;
use core::isa::decoder::Decoder;
use core::instruction::Instruction;
use core::operand::Operand;
use core::operation::Operation;
fn main() {
println!("Taxation is Theft");
Decoder::decode(vec![0b11100011]);
let instructions = vec![(
Instruction {
op: Operation::NOP,
mode: AddressMode::Implied,
operand: Operand::None,
},
&[0xea],
)];
let instruction = Instruction::ADC(AddressMode::Immediate(0x45));
println!("Instruction = {:?}", instruction.to_string());
for (op, bytes) in instructions {
assert_eq!(Instruction::decode(bytes), Some(op));
}
}
-5
View File
@@ -1,5 +0,0 @@
fn main() {
println!("Taxation is theft.");
println!("TODO:");
println!(" Load specified binary and parse it out to ASM");
}
+26
View File
@@ -0,0 +1,26 @@
use core::computers::kim1::Kim1;
use core::constants::constants_system::*;
fn main() {
println!("Taxation is theft.");
let mut kim = Kim1::new();
let mut num_ticks = 0;
kim.running = true;
while kim.running {
kim.tick();
num_ticks += 1;
if num_ticks == MOS6502_RESET_CYCLE_COUNT {
println!("Got our 11 ticks. time to hotwire this pc.");
kim.running = true;
kim.dump();
}
if num_ticks == 15 {
kim.running = false;
kim.dump();
}
}
}
+48
View File
@@ -0,0 +1,48 @@
use core::computers::ram_rom::RamRomComputer;
use core::traits::backplane::Backplane;
use std::fs;
fn main() {
println!("Taxation is Theft");
// let opts = CliOptions::parse();
let path = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin";
let bytes = match fs::read(path) {
Ok(bytes) => {
println!("Read {} bytes.", bytes.len());
bytes
},
Err(e) => {
eprintln!("FAIL to read rom.");
panic!("No rom no run.");
vec![]
}
};
let mut computer = RamRomComputer::new();
// Read byte from ROM 0x4000
println!("-- ");
computer.set_read_mode(true);
computer.set_address_bus(0x4020);
computer.tick();
println!("-- ");
// Data Bus contains value from ROM
let read_from_rom = computer.data_bus();
// Write byte to RAM 0x0000
computer.set_read_mode(false);
computer.set_address_bus(0x0001);
computer.tick();
println!("-- ");
// Read byte from RAM
computer.set_read_mode(true);
computer.set_address_bus(0x0001);
computer.tick();
println!("-- ");
let read_from_ram = computer.data_bus();
assert_eq!(read_from_rom, read_from_ram);
println!("Test passed. We read the same from ROM as we did from RAM - {} == {}", read_from_rom, read_from_ram);
}
+6
View File
@@ -0,0 +1,6 @@
use core::computers::single_breadboard::SingleBreadboard;
fn main() {
let sb = SingleBreadboard::new();
}
+2
View File
@@ -0,0 +1,2 @@
mod ram_rom_tests;
mod rom_only_test;
+16
View File
@@ -0,0 +1,16 @@
use core::computers::ram_rom::RamRomComputer;
use core::traits::backplane::Backplane;
const RAM_START: u16 = 0x4000;
const ROM_START: u16 = 0x0000;
#[test]
fn ram_rom_thintick() {
let mut ramrom = RamRomComputer::new();
ramrom.tick2(0x0001, 1, 0x00);
println!("DataBus: {}", ramrom.data_bus());
ramrom.tick2(0x4001, 1,0xab);
println!("DataBus: {}", ramrom.data_bus());
ramrom.tick2(0x4001, 1,0x00);
println!("DataBus: {}", ramrom.data_bus());
}
+42
View File
@@ -0,0 +1,42 @@
use core::computers::rom_only::RomOnlyComputer;
use core::traits::backplane::Backplane;
#[test]
fn rom_only_widetick_test() {
let bytes = include_bytes!("/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin");
let mut rom_only = RomOnlyComputer::program((&bytes[..]).to_vec());
rom_only.tick2(0x05, 0x00, 0x00);
assert_eq!(rom_only.data_bus(), 0x05);
assert_eq!(rom_only.address_bus(), 0x05);
rom_only.tick2(0x07, 0x00, 0x00);
assert_eq!(rom_only.data_bus(), 0x07);
assert_eq!(rom_only.address_bus(), 0x07);
rom_only.tick2(0x09, 0x00, 0x00);
assert_eq!(rom_only.data_bus(), 0x09);
assert_eq!(rom_only.address_bus(), 0x09);
}
#[test]
fn rom_only_thintick_test() {
let bytes = include_bytes!("/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin");
let mut rom_only = RomOnlyComputer::program((&bytes[..]).to_vec());
rom_only.set_read_mode(true);
rom_only.set_address_bus(0x05);
rom_only.tick();
assert_eq!(rom_only.data_bus(), 0x05);
assert_eq!(rom_only.address_bus(), 0x05);
rom_only.set_read_mode(true);
rom_only.set_address_bus(0x07);
rom_only.tick();
assert_eq!(rom_only.data_bus(), 0x07);
assert_eq!(rom_only.address_bus(), 0x07);
}
+3 -1
View File
@@ -3,4 +3,6 @@ name = "core"
version = "0.1.0"
edition = "2024"
[dependencies]
log = "0.4.26"
log = "0.4"
rand = "0.9.0"
once_cell = "0.1"
+72 -12
View File
@@ -1,16 +1,76 @@
use std::ops::Add;
use crate::mos6502cpu::Mos6502Cpu;
#[derive(PartialEq, Debug)]
/// Represents the various addressing modes of the 6502 CPU.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum AddressMode {
/// Implied
///
/// No operand is needed; the instruction implicitly operates on a register or flag.
/// Example: `CLC` (Clear Carry Flag)
Implied,
/// Accumulator
///
/// Operates directly on the accumulator register.
/// Example: `ASL A` (Arithmetic Shift Left on Accumulator)
Accumulator,
Immediate(u8),
ZeroPage(u8),
ZeroPageX(u8),
Absolute(u16),
AbsoluteX(u16),
AbsoluteY(u16),
IndirectX(u8),
IndirectY(u8),
/// Immediate
///
/// Operand is a constant 8-bit value.
/// Example: `LDA #$01` loads the value 0x01 into the accumulator.
Immediate,
/// Zero Page
///
/// Operand is an address in the first 256 bytes of memory (0x00000x00FF).
/// Example: `LDA $10` reads from address 0x0010.
ZeroPage,
/// Zero Page X
///
/// Zero page address offset by the X register.
/// Example: If X = 0x10, `LDA $23,X` reads from 0x33.
ZeroPageX,
/// Zero Page Y
///
/// Zero page address offset by the Y register.
/// Used only by a few instructions like `LDX` and `STX`.
/// Example: If Y = 0x10, `LDX $23,Y` reads from 0x33.
ZeroPageY,
/// Absolute
///
/// Full 16-bit address is provided as the operand.
/// Example: `LDA $1234` reads from address 0x1234.
Absolute,
/// Absolute X
///
/// Absolute address offset by the X register.
/// Example: If X = 0x10, `LDA $1234,X` reads from 0x1244.
AbsoluteX,
/// Absolute Y
///
/// Absolute address offset by the Y register.
/// Example: If Y = 0x10, `LDA $1234,Y` reads from 0x1244.
AbsoluteY,
/// Indirect
///
/// Only used by `JMP`. Operand is a 16-bit address pointing to another 16-bit address.
/// Example: `JMP ($1234)` jumps to the address stored at 0x1234/0x1235.
Indirect,
/// Indirect X (Indexed Indirect)
///
/// Operand is a zero-page address. Add X to it, then fetch the 16-bit address from that location.
/// Example: If X = 0x04 and operand = $20, `LDA ($20,X)` reads from the address at $24/$25.
IndirectX,
/// Indirect Y (Indirect Indexed)
///
/// Operand is a zero-page address. Fetch the 16-bit address from that location, then add Y.
/// Example: If Y = 0x10 and ($20) = $3000, `LDA ($20),Y` reads from $3010.
IndirectY,
}
+52
View File
@@ -0,0 +1,52 @@
use crate::computers::beneater::BenEater;
use crate::traits::backplane::Backplane;
impl Backplane for BenEater {
fn data_bus(&self) -> u8 {
self.data_bus
}
fn address_bus(&self) -> u16 {
self.address_bus
}
fn read_mode(&self) -> bool {
self.read_mode
}
fn set_read_mode(&mut self, new_mode: bool) {
self.read_mode = new_mode
}
fn set_data_bus(&mut self, new_value: u8) {
self.data_bus = new_value
}
fn set_address_bus(&mut self, new_value: u16) {
self.address_bus = new_value
}
fn tick(&mut self) {
println!("Tick the system.");
self.tick_ram(self.address_bus, self.data_bus, true, true, true);
self.tick_rom(self.address_bus, true, true);
self.tick_via(self.address_bus, self.data_bus, true, true, self.read_mode, true, true);
}
fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 {
println!("Ticking RAM");
0x00
}
fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 {
println!("Ticking ROM");
0x00
}
fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) {
println!("Ticking VIA 6522");
let (new_address, new_data) = self.via.tick(self.address_bus, self.data_bus, false, self.read_mode);
(new_data, false, false)
}
}
+22
View File
@@ -0,0 +1,22 @@
mod backplane;
use crate::mos6502cpu::Mos6502Cpu;
use crate::periph::mos6522::Mos6522;
/*
SBC Designed by Ben Eater
0x0000 -> 0x3fff -> RAM (16KB)
0x4000 -> 0x5fff -> UNUSED
0x6000 -> 0x600f -> VIA
0x6010 -> 0x7fff -> UNUSED
0x8000 -> 0xffff -> ROM (32KB)
*/
pub struct BenEater {
cpu: Mos6502Cpu,
via: Mos6522,
data_bus: u8,
address_bus: u16,
read_mode: bool
}
+23
View File
@@ -0,0 +1,23 @@
pub mod new;
pub mod tick;
pub mod reset;
use crate::mos6502cpu::Mos6502Cpu;
use crate::periph::hm62256::Hm62256;
use crate::periph::kim1_keypad::Kim1Keypad;
use crate::periph::mos6530::Mos6530;
/// Represents a KIM-1
///
///
pub struct Kim1 {
pub running: bool,
pub cpu: Mos6502Cpu,
rriot1: Mos6530,
rriot2: Mos6530,
ram: Hm62256,
pub(crate) keypad: Kim1Keypad,
address_bus: u16,
data_bus: u8,
cpu_read: bool
}
+31
View File
@@ -0,0 +1,31 @@
use crate::computers::kim1::Kim1;
use crate::periph::hm62256::Hm62256;
use crate::periph::kim1_keypad::Kim1Keypad;
use crate::periph::mos6530::Mos6530;
impl Kim1 {
pub fn dump(&self) {
println!("DUMPING KIM-1 PC STATE");
self.cpu.dump();
self.rriot1.dump();
self.rriot2.dump();
self.keypad.dump();
}
pub fn new() -> Self {
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-002_fillerbyte00-0x1c00.bin");
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-003_fillerbyte00-0x1800.bin");
Self {
cpu: Default::default(),
rriot1: Mos6530::new(0x1700, 0x1780, 0x1800, &rriot1_rom),
rriot2: Mos6530::new(0x1740, 0x17C0, 0x1C00, &rriot2_rom),
ram: Hm62256::new(0x0000),
keypad: Kim1Keypad::new(),
address_bus: 0,
data_bus: 0,
cpu_read: false,
running: false
}
}
}
+18
View File
@@ -0,0 +1,18 @@
use crate::computers::kim1::Kim1;
use crate::periph::hm62256::Hm62256;
use crate::periph::mos6530::Mos6530;
impl Kim1 {
pub fn reset(&mut self) {
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-002_fillerbyte00-0x1c00.bin");
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-003_fillerbyte00-0x1800.bin");
self.cpu = Default::default();
self.rriot1 = Mos6530::new(0x1700, 0x1780, 0x1800, rriot1_rom.as_array().unwrap());
self.rriot2 = Mos6530::new(0x1740, 0x17c0, 0x1c00, rriot2_rom.as_array().unwrap());
self.ram = Hm62256::new(0x0000);
self.address_bus = 0x0000;
self.data_bus = 0x0000;
self.cpu_read = true;
self.cpu.pc = 0x0000;
}
}
+26
View File
@@ -0,0 +1,26 @@
use crate::computers::kim1::Kim1;
impl Kim1 {
pub fn tick(&mut self) {
println!("<- START KIM-1 Backplane Tick");
let (address_bus, data, rw) = self.cpu.tick2(self.address_bus, self.data_bus);
self.address_bus = address_bus;
self.data_bus = data;
self.cpu_read = rw;
// now tick the various items connected
self.rriot1.tick(self.address_bus, self.data_bus, false, self.cpu_read);
self.rriot2.tick(self.address_bus, self.data_bus, false, self.cpu_read);
self.ram.tick(self.address_bus, self.data_bus, self.cpu_read, true);
let (rr1_io, rr1_ram, rr1_rom) = self.rriot1.dump_data();
let (rr2_io, rr2_ram, rr2_rom) = self.rriot2.dump_data();
println!(" 0x0000 -> RAM / {}", self.ram.dump_data());
println!(" 0x1700 -> RRIOT 1 / 0x{rr1_io:04x}/0x{rr1_ram:04x}/0x{rr1_rom:04x}");
println!(" 0x1740 -> RRIOT 2 / 0x{rr2_io:04x}/0x{rr2_ram:04x}/0x{rr2_rom:04x}");
// display the memory map and device states
println!("-> FINISH KIM-1 Backplane Tick");
}
}
+6
View File
@@ -0,0 +1,6 @@
pub mod beneater;
pub mod rom_only;
pub mod kim1;
pub mod ram_rom;
pub mod tim1;
pub mod single_breadboard;
+71
View File
@@ -0,0 +1,71 @@
use log::debug;
use crate::computers::ram_rom::RamRomComputer;
use crate::periph::at28c256::At28C256;
use crate::traits::backplane::Backplane;
use crate::periph::hm62256::Hm62256;
impl Backplane for RamRomComputer {
fn data_bus(&self) -> u8 {
self.data_bus
}
fn address_bus(&self) -> u16 {
self.address_bus
}
fn read_mode(&self) -> bool {
self.read_mode
}
fn set_read_mode(&mut self, new_mode: bool) {
self.read_mode = new_mode;
}
fn set_data_bus(&mut self, new_value: u8) {
self.data_bus = new_value;
}
fn set_address_bus(&mut self, new_value: u16) {
self.address_bus = new_value;
}
fn tick(&mut self) {
println!("Preparing to tick the backplane. - ${:04x} ${:02x} {}", self.address_bus, self.data_bus, self.read_mode);
// who are we talking to?
match self.address_bus {
0x0000..=0x3fff => {
// RAM
println!("ADDRESSING RAM");
let (ram_address_bus, ram_data_bus) = self.ram.tick(self.address_bus, self.data_bus, self.read_mode, true);
if self.read_mode {
self.data_bus = ram_data_bus;
}
},
0x4000..=0x7fff => {
// ROM
println!("ADDRESSING ROM");
let rom_data_bus = self.rom.signal_tick(self.address_bus, self.data_bus, true, true, true);
self.data_bus = rom_data_bus;
}
_ => {
// Out of range
}
}
}
fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 {
debug!("Ticking ram with A${address:04x} D${data:02x} CS:{cs} OE:{oe} WE:{we}");
0
}
fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 {
debug!("Ticking rom with A${address:04x} CS:{cs} OE:{oe}");
0
}
fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) {
debug!("Ticking Via with A${address:04x} D${data:02x} cs0:{cs0} cs1:{cs1} rw:{rw} rs0:{rs0} rs1:{rs1}");
(0, false, false)
}
}
+15
View File
@@ -0,0 +1,15 @@
use crate::periph::at28c256::At28C256;
use crate::periph::hm62256::Hm62256;
pub mod backplane;
pub mod program_rom;
pub mod new;
pub mod tick2;
pub struct RamRomComputer {
pub(crate) rom: At28C256,
pub(crate) ram: Hm62256,
pub(crate) data_bus: u8,
pub(crate) address_bus: u16,
pub(crate) read_mode: bool,
}
+17
View File
@@ -0,0 +1,17 @@
use crate::computers::ram_rom::RamRomComputer;
use crate::periph::at28c256::At28C256;
use crate::periph::hm62256::Hm62256;
impl RamRomComputer {
pub fn new() -> RamRomComputer {
let rom = At28C256::new(0x4000, 0x7fff, (0..255).collect());
RamRomComputer {
rom,
ram: Hm62256::default(),
data_bus: 0x00,
address_bus: 0x0000,
/// is the CPU reading from the 'other' device?
read_mode: true
}
}
}
+16
View File
@@ -0,0 +1,16 @@
use crate::computers::ram_rom::RamRomComputer;
use crate::periph::at28c256::At28C256;
use crate::periph::hm62256::Hm62256;
impl RamRomComputer {
pub fn program_rom(rom: Vec<u8>) -> RamRomComputer {
let new_rom = At28C256::new(0x0000, 0x3fff, rom);
RamRomComputer {
rom: new_rom,
ram: Hm62256::new(0x3fff),
data_bus: 0x00,
address_bus: 0x00,
read_mode: false,
}
}
}
+43
View File
@@ -0,0 +1,43 @@
use crate::computers::ram_rom::RamRomComputer;
use crate::traits::backplane::Backplane;
struct ChipSignals {
cs: bool,
oe: bool,
we: bool
}
enum RomRamChips {
At28C256,
Hm62256
}
impl RamRomComputer {
pub fn tick2(&mut self, address: u16, control: u8, data: u8) -> (u8) {
println!("⏲RAM ROM Computer tick starting / {address:04x} {control:08b} {data:02x}");
// tick the parts
// map of memory
// 0x0000 -> 0x3fff -> RAM (HM62256)
// 0x4000 -> 0x7fff -> ROM (At28C256)
match address {
0x0000..=0x3fff => {
if self.read_mode {
println!("⏲__ROM BEING READ FROM");
let new_data = self.rom.signal_tick(address, data, true, true, true);
self.set_data_bus(new_data);
} else {
panic!("UNABLE TO WRITE TO ROM");
}
}
0x4000 ..=0x7fff => {
println!("⏲__DATA TARGETTING RRAAMM GETTING STORED ON DATA BUS");
}
_ => {}
};
let rom_data_bus = self.rom.signal_tick(self.address_bus ,self.data_bus, true ,true , true);
let (_, ram_data_bus) = self.ram.tick(address, data, control == 1, true);
0
}
}
+45
View File
@@ -0,0 +1,45 @@
use log::debug;
use crate::computers::rom_only::RomOnlyComputer;
use crate::traits::backplane::Backplane;
impl Backplane for RomOnlyComputer {
fn data_bus(&self) -> u8 { self.data_bus }
fn address_bus(&self) -> u16 { self.address_bus }
fn read_mode(&self) -> bool { self.read_mode }
fn set_read_mode(&mut self, new_mode: bool) {
self.read_mode = new_mode
}
fn set_data_bus(&mut self, new_value: u8) {
self.data_bus = new_value
}
fn set_address_bus(&mut self, new_value: u16) {
self.address_bus = new_value
}
fn tick(&mut self) {
// println!("COMPUTER: Preparing to tick.");
// do are we being addressed?
// println!("COMPUTER: BUSSES PRE: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode);
let new_data = self.rom.signal_tick(self.address_bus, self.data_bus, true, true, true);
// tick(self.address_bus, self.data_bus, self.read_mode);
self.set_address_bus(self.address_bus);
self.set_data_bus(new_data);
// println!("COMPUTER: BUSSES POST: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode);
// println!("COMPUTER: Done ticking.");
}
fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 {
debug!("This system has no ram. ROM only.");
0x00
}
fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 {
let data = self.rom.signal_tick(address, self.data_bus, cs, oe, true);
data
}
fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) {
debug!("This system has no VIA controllers. ROM only");
(0,false,false)
}
}
@@ -0,0 +1,12 @@
use crate::computers::rom_only::RomOnlyComputer;
impl RomOnlyComputer {
pub fn debug_memory(&self, start_offset: u16, end_offset: u16) -> Vec<u8> {
let mut data = vec![];
let size = end_offset - start_offset;
for index in 0..size {
println!("Index {index} for {}", index + start_offset);
}
data
}
}
+29
View File
@@ -0,0 +1,29 @@
use crate::periph::at28c256::At28C256;
use crate::traits::backplane::Backplane;
pub mod backplane;
pub mod new;
pub mod program;
pub mod debug_memory;
mod rom_chunks;
pub struct RomOnlyComputer {
pub rom: At28C256,
pub data_bus: u8,
pub address_bus: u16,
pub read_mode: bool,
}
impl RomOnlyComputer {
pub fn tick2(&mut self, address: u16, control: u8, data: u8) -> (u8) {
// tick the parts...
println!("WIDETICK: A:${address:04x} D:${data:02x} C:b{control:08b}");
self.address_bus = address;
self.data_bus = data;
let new_data = self.rom.signal_tick(self.address_bus, self.data_bus, true, true, true);
println!("\nNew Data : {new_data:02x}");
self.set_data_bus(new_data);
new_data
}
}
+12
View File
@@ -0,0 +1,12 @@
use crate::computers::rom_only::RomOnlyComputer;
use crate::constants::constants_system::SIZE_32KB;
impl RomOnlyComputer {
pub fn new() -> RomOnlyComputer {
let mut working = vec![0x00u8; SIZE_32KB];
for index in 0..SIZE_32KB {
working[index] = index as u8;
}
RomOnlyComputer::program(working)
}
}
+14
View File
@@ -0,0 +1,14 @@
use crate::computers::rom_only::RomOnlyComputer;
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
impl RomOnlyComputer {
pub fn program(rom: Vec<u8>) -> RomOnlyComputer {
RomOnlyComputer {
rom: At28C256::new(0x000, 0x3fff, rom),
address_bus: 0x0000,
data_bus: 0x00,
read_mode: true,
}
}
}
@@ -0,0 +1,8 @@
use std::slice::Chunks;
use crate::computers::rom_only::RomOnlyComputer;
impl RomOnlyComputer {
pub fn rom_chunks(&self, size: usize) -> Chunks<u8> {
self.rom.chunks(size)
}
}
@@ -0,0 +1,3 @@
Computer at
https://www.youtube.com/watch?v=s3t2QMukBRs
https://github.com/AndersBNielsen/6507SBC
@@ -0,0 +1,20 @@
use crate::constants::constants_system::SIZE_32KB;
use crate::mos6502cpu::Mos6502Cpu;
use crate::periph::at28c256::At28C256;
use crate::periph::mos6532::Mos6532;
pub struct SingleBreadboard {
pub cpu: Mos6502Cpu,
pub ram: At28C256,
pub via: Mos6532
}
impl SingleBreadboard {
pub fn new() -> Self {
SingleBreadboard {
cpu: Mos6502Cpu::default(),
ram: At28C256::new(0xf000, 0xffff, vec![0x00; SIZE_32KB]),
via: Mos6532::new(0x0000, 0x0080)
}
}
}
+46
View File
@@ -0,0 +1,46 @@
use std::net::IpAddr::V4;
use log::debug;
use crate::computers::tim1::Tim1;
use crate::traits::backplane::Backplane;
impl Backplane for Tim1 {
fn data_bus(&self) -> u8 { self.data_bus }
fn address_bus(&self) -> u16 { self.address_bus }
fn read_mode(&self) -> bool { self.read_mode }
fn set_read_mode(&mut self, new_mode: bool) {
self.read_mode = new_mode
}
fn set_data_bus(&mut self, new_value: u8) {
self.data_bus = new_value
}
fn set_address_bus(&mut self, new_value: u16) {
self.address_bus = new_value
}
fn tick(&mut self) {
println!("Starting tick for TIM-1");
}
fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 {
debug!("No dedicated RAM. Its in the VIA");
0x00
}
fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 {
debug!("No dedicated ROM. Its in the VIA");
0x00
}
fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) {
debug!("Starting VIA Tick");
self.pia.tick(address, data, false, self.read_mode);
// is this for the ROM?
// is this for the RAM?
// is this for the Interrupts?
// is this for the Timers?
(0x00, false, false)
}
}
+13
View File
@@ -0,0 +1,13 @@
mod backplane;
use crate::mos6502cpu::Mos6502Cpu;
use crate::periph::mos6530::Mos6530;
pub struct Tim1 {
cpu: Mos6502Cpu,
pia: Mos6530,
read_mode: bool,
data_bus: u8,
address_bus: u16
}
@@ -1,5 +1,6 @@
// Instruction OP Codes
/// Instruction OP Codes
/// Verified against
/// https://www.nesdev.org/obelisk-6502-guide/reference.html
/// ADC
pub const ISA_OP_ADC_I: u8 = 0x69;
pub const ISA_OP_ADC_Z: u8 = 0x65;
@@ -18,78 +19,59 @@ pub const ISA_OP_AND_ABSX: u8 = 0x3d;
pub const ISA_OP_AND_ABSY: u8 = 0x39;
pub const ISA_OP_AND_INDX: u8 = 0x21;
pub const ISA_OP_AND_INDY: u8 = 0x31;
/// ASL
pub const ISA_OP_ASL_A: u8 = 0x0a;
pub const ISA_OP_ASL_Z: u8 = 0x06;
pub const ISA_OP_ASL_ZX: u8 = 0x16;
pub const ISA_OP_ASL_ABS: u8 = 0x0e;
pub const ISA_OP_ASL_ABSX: u8 = 0x1e;
/// BCC
pub const ISA_OP_BCC: u8 = 0x90;
/// BCS
pub const ISA_OP_BCS: u8 = 0xb0;
/// BEQ
pub const ISA_OP_BEQ: u8 = 0xf0;
/// BIT
pub const ISA_OP_BIT_ZP: u8 = 0x24;
pub const ISA_OP_BIT_ABS: u8 = 0x2c;
/// BMI
pub const ISA_OP_BMI: u8 = 0x30;
/// BNE
pub const ISA_OP_BNE: u8 = 0xd0;
/// BPL
pub const ISA_OP_BPL: u8 = 0x10;
/// BRK
pub const ISA_OP_BRK: u8 = 0x00;
/// BVC
pub const ISA_OP_BVC: u8 = 0x50;
/// BVS
pub const ISA_OP_BVS: u8 = 0x70;
pub const ISA_OP_CLC: u8 = 0x18;
pub const ISA_OP_CLD: u8 = 0xd8;
pub const ISA_OP_CLI: u8 = 0x58;
pub const ISA_OP_CLV: u8 = 0xb8;
pub const ISA_OP_CMP_I: u8 = 0xc9;
pub const ISA_OP_CMP_ZP: u8 = 0xc5;
pub const ISA_OP_CMP_ZPX: u8 = 0xd5;
pub const ISA_OP_CMP_ABS: u8 = 0xcd;
pub const ISA_OP_CMP_ABSX: u8 = 0xdd;
pub const ISA_OP_CMP_ABSY: u8 = 0xd9;
pub const ISA_OP_CMP_INDX: u8 = 0xc1;
pub const ISA_OP_CMP_INDY: u8 = 0xd1;
pub const ISA_OP_CPX_I: u8 = 0xe0;
pub const ISA_OP_CPX_ZP: u8 = 0xe4;
pub const ISA_OP_CPX_ABS: u8 = 0xec;
pub const ISA_OP_CPY_I: u8 = 0xc0;
pub const ISA_OP_CPY_ZP: u8 = 0xc4;
pub const ISA_OP_CPY_ABS: u8 = 0xcc;
pub const ISA_OP_DEC_ZP: u8 = 0xc6;
pub const ISA_OP_DEC_ZPX: u8 = 0xd6;
pub const ISA_OP_DEC_ABS: u8 = 0xce;
pub const ISA_OP_DEC_ABSX: u8 = 0xde;
pub const ISA_OP_DEX: u8 = 0xca;
pub const ISA_OP_DEY: u8 = 0x88;
pub const ISA_OP_EOR_I: u8 = 0x49;
pub const ISA_OP_EOR_ZP: u8 = 0x45;
pub const ISA_OP_EOR_ZPX: u8 = 0x55;
@@ -98,50 +80,39 @@ pub const ISA_OP_EOR_ABSX: u8 = 0x5d;
pub const ISA_OP_EOR_ABSY: u8 = 0x59;
pub const ISA_OP_EOR_INDX: u8 = 0x41;
pub const ISA_OP_EOR_INDY: u8 = 0x51;
pub const ISA_OP_INC_ZP: u8 = 0xe6;
pub const ISA_OP_INC_ZPX: u8 = 0xf6;
pub const ISA_OP_INC_ABS: u8 = 0xee;
pub const ISA_OP_INC_ABSX: u8 = 0xfe;
pub const ISA_OP_INX: u8 = 0xe8;
pub const ISA_OP_INY: u8 = 0xc8;
pub const ISA_OP_JMP_ABS: u8 = 0x4c;
pub const ISA_OP_JMP_IND: u8 = 0x6c;
pub const ISA_OP_JSR: u8 = 0x20;
pub const ISA_OP_LDA_I: u8 = 0xA9;
pub const ISA_OP_LDA_Z: u8 = 0xA5;
pub const ISA_OP_LDA_ZX: u8 = 0xB5;
pub const ISA_OP_LDA_ABS: u8 = 0xAD;
pub const IAS_OP_LDA_ABSX: u8 = 0xBD;
pub const ISA_OP_LDA_ABSX: u8 = 0xBD;
pub const ISA_OP_LDA_ABSY: u8 = 0xB9;
pub const ISA_OP_LDA_INDX: u8 = 0xA1;
pub const ISA_OP_LDA_INDY: u8 = 0xB1;
pub const ISA_OP_LDX_I: u8 = 0xa2;
pub const ISA_OP_LDX_ZP: u8 = 0xa6;
pub const ISA_OP_LDX_ZPY: u8 = 0x86;
pub const ISA_OP_LDX_ZPY: u8 = 0xb6;
pub const ISA_OP_LDX_ABS: u8 = 0xae;
pub const ISA_OP_LDX_ABSY: u8 = 0xbe;
pub const ISA_OP_LDY_I: u8 = 0xa0;
pub const ISA_OP_LDY_ZP: u8 = 0xa4;
pub const ISA_OP_LDY_ZPX: u8 = 0xb4;
pub const ISA_OP_LDY_ABS: u8 = 0xac;
pub const ISA_OP_LDY_ABSX: u8 = 0xac;
pub const ISA_OP_LDY_ABSX: u8 = 0xbc;
pub const ISA_OP_LSR_A: u8 = 0x4a;
pub const ISA_OP_LSR_ZP: u8 = 0x46;
pub const ISA_OP_LSR_ZPX: u8 = 0x56;
pub const ISA_OP_LSR_ABS: u8 = 0x4e;
pub const ISA_OP_LSR_ABSX: u8 = 0x5e;
pub const ISA_OP_NOP: u8 = 0xEA;
pub const ISA_OP_ORA_I: u8 = 0x09;
pub const ISA_OP_ORA_ZP: u8 = 0x05;
pub const ISA_OP_ORA_ZPX: u8 = 0x15;
@@ -150,24 +121,15 @@ pub const ISA_OP_ORA_ABSX: u8 = 0x1d;
pub const ISA_OP_ORA_ABSY: u8 = 0x19;
pub const ISA_OP_ORA_INDX: u8 = 0x01;
pub const ISA_OP_ORA_INDY: u8 = 0x11;
pub const ISA_OP_PHA: u8 = 0x48;
pub const ISA_OP_PHP: u8 = 0x08;
///
pub const ISA_OP_PLA: u8 = 0x68;
///
pub const ISA_OP_PLP: u8 = 0x28;
///
///
pub const ISA_OP_ROL_A: u8 = 0x2a;
pub const ISA_OP_ROL_ZP: u8 = 0x26;
pub const ISA_OP_ROL_ZPX: u8 = 0x36;
pub const ISA_OP_ROL_ABS: u8 = 0x2e;
pub const ISA_OP_ROL_ABSX: u8 = 0x3e;
///
pub const ISA_OP_ROR_A: u8 = 0x6a;
pub const ISA_OP_ROR_ZP: u8 = 0x66;
pub const ISA_OP_ROR_ZPX: u8 = 0x76;
@@ -194,7 +156,7 @@ pub const ISA_OP_STA_ABSY: u8 = 0x99;
pub const ISA_OP_STA_INDX: u8 = 0x81;
pub const ISA_OP_STA_INDY: u8 = 0x91;
pub const ISA_OP_STX_ZP: u8 = 0x86;
pub const ISA_OP_STX_ZPX: u8 = 0x96;
pub const ISA_OP_STX_ZPY: u8 = 0x96;
pub const ISA_OP_STX_ABS: u8 = 0x8e;
pub const ISA_OP_STY_ZP: u8 = 0x84;
pub const ISA_OP_STY_ZPX: u8 = 0x94;
+57
View File
@@ -0,0 +1,57 @@
/// STUB Parts
pub const ISA_STUB_ADC: &str = "ADC";
pub const ISA_STUB_AND: &str = "AND";
pub const ISA_STUB_ASL: &str = "ASL";
pub const ISA_STUB_BCC: &str = "BCC";
pub const ISA_STUB_BCS: &str = "BCS";
pub const ISA_STUB_BEQ: &str = "BEQ";
pub const ISA_STUB_BIT: &str = "BIT";
pub const ISA_STUB_BMI: &str = "BMI";
pub const ISA_STUB_BNE: &str = "BNE";
pub const ISA_STUB_BPL: &str = "BPL";
pub const ISA_STUB_BRK: &str = "BRK";
pub const ISA_STUB_BVC: &str = "BVC";
pub const ISA_STUB_BVS: &str = "BVS";
pub const ISA_STUB_CLC: &str = "CLC";
pub const ISA_STUB_CLD: &str = "CLD";
pub const ISA_STUB_CLI: &str = "CLI";
pub const ISA_STUB_CLV: &str = "CLV";
pub const ISA_STUB_CMP: &str = "CMP";
pub const ISA_STUB_CPX: &str = "CPX";
pub const ISA_STUB_CPY: &str = "CPY";
pub const ISA_STUB_DEC: &str = "DEC";
pub const ISA_STUB_DEX: &str = "DEX";
pub const ISA_STUB_DEY: &str = "DEY";
pub const ISA_STUB_EOR: &str = "EOR";
pub const ISA_STUB_INC: &str = "INC";
pub const ISA_STUB_INX: &str = "INX";
pub const ISA_STUB_INY: &str = "INY";
pub const ISA_STUB_JMP: &str = "JMP";
pub const ISA_STUB_JSR: &str = "JSR";
pub const ISA_STUB_LDA: &str = "LDA";
pub const ISA_STUB_LDX: &str = "LDX";
pub const ISA_STUB_LDY: &str = "LDY";
pub const ISA_STUB_LSR: &str = "LSR";
pub const ISA_STUB_NOP: &str = "NOP";
pub const ISA_STUB_ORA: &str = "ORA";
pub const ISA_STUB_PHA: &str = "PHA";
pub const ISA_STUB_PHP: &str = "PHP";
pub const ISA_STUB_PLA: &str = "PLA";
pub const ISA_STUB_PLP: &str = "PLP";
pub const ISA_STUB_ROL: &str = "ROL";
pub const ISA_STUB_ROR: &str = "ROR";
pub const ISA_STUB_RTI: &str = "RTI";
pub const ISA_STUB_RTS: &str = "RTS";
pub const ISA_STUB_SBC: &str = "SBC";
pub const ISA_STUB_SEC: &str = "SEC";
pub const ISA_STUB_SED: &str = "SED";
pub const ISA_STUB_SEI: &str = "SEI";
pub const ISA_STUB_STA: &str = "STA";
pub const ISA_STUB_STX: &str = "STX";
pub const ISA_STUB_STY: &str = "STY";
pub const ISA_STUB_TAX: &str = "TAX";
pub const ISA_STUB_TAY: &str = "TAY";
pub const ISA_STUB_TSX: &str = "TSX";
pub const ISA_STUB_TXA: &str = "TXA";
pub const ISA_STUB_TXS: &str = "TXS";
pub const ISA_STUB_TYA: &str = "TYA";
+23
View File
@@ -0,0 +1,23 @@
pub const MOS6530_DRA: u8 = 0x00;
pub const MOS6530_DDRA: u8 = 0x01;
pub const MOS6530_DRB: u8 = 0x02;
pub const MOS6530_DDRB: u8 = 0x03;
/*
0 X Data Register A
1 X Data Direction Register A
2 X Data Register B
3 X Data Direction Register B
4 0 Count down from value, divide by 1, disable IRQ 1 ???
5 0 Count down from value, divide by 8, disable IRQ 1 ???
6 0 Count down from value, divide by 64, disable IRQ 1 Read current counter value, disable IRQ
7 0 Count down from value, divide by 1024, disable IRQ 1 Read counter status, bit7 = 1 means counter past zero
8 X Data Register A (mirror ?)
9 X Data Direction Register A (mirror ?)
A X Data Register B (mirror ?)
B X Data Direction Register B (mirror ?)
C 0 Count down from value, divide by 1, enable IRQ 1 ???
D 0 Count down from value, divide by 8, enable IRQ 1 ???
E 0 Count down from value, divide by 64, enable IRQ 1 Read current counter value, enable IRQ
F 0 Count down from value, divide by 1024, enable IRQ 1 Read counter status, bit7 = 1 means counter past zero
*/
+16
View File
@@ -0,0 +1,16 @@
pub const SIZE_1KB: usize = 1024;
pub const SIZE_32KB: usize = SIZE_1KB * 32;
pub const SIZE_64KB: usize = SIZE_1KB * 64;
// S Suffixed constants are for indexing slices
pub const OFFSET_NMI_VECTOR: u16 = 0xfffa;
pub const OFFSET_NMI_VECTORS: usize = 0xfffa;
pub const OFFSET_RESET_VECTOR: u16 = 0xfffc;
pub const OFFSET_RESET_VECTORS: usize = 0xffff;
pub const OFFSET_INT_VECTOR: u16 = 0xfffe;
pub const OFFSET_INT_VECTORS: usize = 0xfffe;
// 7 cycles for internal reset
// 6 to read the vectors from RAM
pub const MOS6502_RESET_CYCLE_COUNT: u16 = 8;
+7
View File
@@ -0,0 +1,7 @@
use std::borrow::ToOwned;
use once_cell::unsync::Lazy;
pub const TEST_RESOURCES_ROOT: &str = "/home/tmerritt/Projects/resources/test";
pub const TEST_PERIPH_ROOT: &str = "/home/tmerritt/Projects/resources/test/periph";
pub const TEST_PERIPH_AT28C256_ROOT: &str = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256";
+19
View File
@@ -0,0 +1,19 @@
pub const VIA6522_ORB: u8 = 0b0000;
pub const VIA6522_ORA: u8 = 0b0001;
pub const VIA6522_DDRB: u8 = 0b0010;
pub const VIA6522_DDRA: u8 = 0b0011;
/// Timer 1 Write Latch
pub const VIA6522_T1WL: u8 = 0b0100;
/// Timer 1 Read Counter High
pub const VIA6522_T1CL: u8 = 0b0101;
pub const VIA6522_T1CH: u8 = 0b0110;
pub const VIA6522_T1LL: u8 = 0b0111;
pub const VIA6522_T1LH: u8 = 0b1000;
pub const VIA6522_T2LL: u8 = 0b1001;
pub const VIA6522_T2CH: u8 = 0b1010;
pub const VIA6522_SR: u8 = 0b1011;
pub const VIA6522_ACR: u8 = 0b1100;
pub const VIA6522_PCR: u8 = 0b1101;
pub const VIA6522_IFR: u8 = 0b1110;
pub const VIA6522_IER: u8 = 0b1111;
+6
View File
@@ -0,0 +1,6 @@
pub mod constants_isa_op;
pub mod constants_isa_stub;
pub mod constants_system;
pub mod constants_via6522;
pub mod constants_mos6530;
pub mod constants_test;
+30 -1726
View File
File diff suppressed because it is too large Load Diff
-21
View File
@@ -1,21 +0,0 @@
use crate::address_mode::AddressMode;
pub struct InstructionStringify {}
impl InstructionStringify {
pub fn format(mode: AddressMode, prefix: &str) -> String {
let suffix = match mode {
AddressMode::Implied => "",
AddressMode::Accumulator => "A",
AddressMode::Immediate(value) => &*format!("#${value:02x}"),
AddressMode::ZeroPage(value) => &*format!("${value:02x}"),
AddressMode::ZeroPageX(value) => &*format!("${value:02x},X"),
AddressMode::Absolute(offset) => &*format!("${offset:04x}"),
AddressMode::AbsoluteX(offset) => &*format!("${offset:04x},X"),
AddressMode::AbsoluteY(offset) => &*format!("${offset:04x},Y"),
AddressMode::IndirectX(value) => &*format!("(${value:02x},X)"),
AddressMode::IndirectY(value) => &*format!("(${value:02x}),Y")
};
format!("{} {}", prefix, suffix)
}
}
File diff suppressed because it is too large Load Diff
-74
View File
@@ -1,74 +0,0 @@
use crate::address_mode::AddressMode::*;
use crate::constants::*;
use crate::instruction::Instruction;
use crate::instruction::Instruction::*;
pub struct Decoder {}
impl Decoder {
/// decode
///
/// Returns the decoded instruction or a NOP.
/// NOP will be returned when an instruction without a valid parameter
/// and more data is required to decode.
pub fn decode(decode_from: Vec<u8>) -> Instruction {
NOP
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn valid_decodes() {
let params = vec![
(vec![ISA_OP_ADC_I, 0xab], ADC(Immediate(0xab))),
(vec![ISA_OP_ADC_Z, 0xab], ADC(ZeroPage(0xab))),
(vec![ISA_OP_ADC_ZX, 0xab], ADC(ZeroPageX(0xab))),
(vec![ISA_OP_ADC_ABS, 0xab, 0xcd], ADC(Absolute(0xcdab))),
(vec![ISA_OP_ADC_ABSX, 0xcd, 0xab], ADC(AbsoluteX(0xabcd))),
(vec![ISA_OP_ADC_ABSY, 0xcd, 0xab], ADC(AbsoluteY(0xabcd))),
(vec![ISA_OP_ADC_INDX, 0xab], ADC(IndirectX(0xab))),
(vec![ISA_OP_ADC_INDY, 0xcd], ADC(IndirectY(0xcd))),
(vec![ISA_OP_AND_I, 0xab], AND(Immediate(0xab))),
(vec![ISA_OP_AND_Z, 0xab], AND(ZeroPage(0xab))),
(vec![ISA_OP_AND_ZX, 0xab], AND(ZeroPageX(0xab))),
(vec![ISA_OP_AND_ABS, 0xcd, 0xab], AND(Absolute(0xabcd))),
(vec![ISA_OP_ASL_A], ASL(Accumulator)),
(vec![ISA_OP_ASL_Z, 0xab], ASL(ZeroPage(0xab))),
(vec![ISA_OP_ASL_ZX, 0xab], ASL(ZeroPageX(0xab))),
(vec![ISA_OP_ASL_ABS, 0xab, 0xcd], ASL(Absolute(0xcdab))),
(vec![ISA_OP_ASL_ABSX, 0xab, 0xcd], ASL(AbsoluteX(0xcdab))),
(vec![ISA_OP_BCC, 0xab], BCC(Immediate(0xab))),
(vec![ISA_OP_BEQ, 0xab], BEQ(Immediate(0xab))),
(vec![ISA_OP_BIT_ZP, 0xab], BIT(ZeroPage(0xab))),
(vec![ISA_OP_BIT_ABS, 0xab, 0xcd], BIT(Absolute(0xcdab))),
(vec![ISA_OP_BMI, 0xab], BMI(Immediate(0xab))),
(vec![ISA_OP_BNE, 0xab], BNE(Immediate(0xab))),
(vec![ISA_OP_BPL, 0xab], BPL(Immediate(0xab))),
(vec![ISA_OP_BVC, 0xab], BVC(Immediate(0xab))),
(vec![ISA_OP_BVS, 0xab], BVS(Immediate(0xab))),
(vec![ISA_OP_BRK], BRK),
];
for (bytes, instruction) in params {
println!("Expecting {:?} to be {:?}", bytes, instruction);
assert_eq!(
Decoder::decode(bytes),
instruction
)
}
}
}
-129
View File
@@ -1,129 +0,0 @@
use crate::address_mode::AddressMode::*;
use crate::constants::*;
use crate::instruction::Instruction;
use crate::instruction::Instruction::*;
pub struct Encoder {}
impl Encoder {
pub fn encode(to_encode: Instruction) -> Vec<u8> {
match to_encode {
// ADC(_) => {}
// AND(_) => {}
// ASL(_) => {}
BCC(mode) => {
match mode {
Immediate(address)=> {
vec![ISA_OP_BCC, address]
}
_ => NOP.to_bytes()
}
}
// BCS(_) => {}
// BEQ(_) => {}
// BIT(_) => {}
// BMI(_) => {}
// BNE(_) => {}
// BPL(_) => {}
// BRK => {}
// BVC(_) => {}
// BVS(_) => {}
// CLC => {}
// CLD => {}
// CLI => {}
// CLV => {}
// CMP(_) => {}
// CPX(_) => {}
// CPY(_) => {}
// DEC(_) => {}
// DEX => {}
// DEY => {}
// EOR(_) => {}
// INC(_) => {}
// INX => {}
// INY => {}
// JMP(_) => {}
// JSR(_) => {}
// LDA(_) => {}
// LDX(_) => {}
// LDY(_) => {}
// LSR(_) => {}
// NOP => {}
// ORA(_) => {}
// PHA => {}
// PHP => {}
// PLA => {}
// PLP => {}
// ROL(_) => {}
// ROR(_) => {}
// RTI => {}
// RTS => {}
// SBC(_) => {}
// SEC => {}
// SED => {}
// SEI => {}
// STA(_) => {}
// STX(_) => {}
// STY(_) => {}
// TAX => {}
// TAY => {}
// TSX => {}
// TXA => {}
// TXS => {}
// TYA => {}
_ => NOP.to_bytes()
}
}
}
#[cfg(test)]
mod test {
use crate::constants::*;
use super::*;
#[test]
fn adc_decode() {
let params = vec![
(vec![ISA_OP_ADC_I, 0xab], ADC(Immediate(0xab))),
(vec![ISA_OP_ADC_Z, 0xab], ADC(ZeroPage(0xab))),
(vec![ISA_OP_ADC_ZX, 0xab], ADC(ZeroPageX(0xab))),
(vec![ISA_OP_ADC_ABS, 0xab, 0xcd], ADC(Absolute(0xcdab))),
(vec![ISA_OP_ADC_ABSX, 0xcd, 0xab], ADC(AbsoluteX(0xabcd))),
(vec![ISA_OP_ADC_ABSY, 0xcd, 0xab], ADC(AbsoluteY(0xabcd))),
(vec![ISA_OP_ADC_INDX, 0xab], ADC(IndirectX(0xab))),
(vec![ISA_OP_ADC_INDY, 0xcd], ADC(IndirectY(0xcd))),
(vec![ISA_OP_AND_I, 0xab], AND(Immediate(0xab))),
(vec![ISA_OP_AND_Z, 0xab], AND(ZeroPage(0xab))),
(vec![ISA_OP_AND_ZX, 0xab], AND(ZeroPageX(0xab))),
(vec![ISA_OP_AND_ABS, 0xcd, 0xab], AND(Absolute(0xabcd))),
(vec![ISA_OP_ASL_A], ASL(Accumulator)),
(vec![ISA_OP_ASL_Z, 0xab], ASL(ZeroPage(0xab))),
(vec![ISA_OP_ASL_ZX, 0xab], ASL(ZeroPageX(0xab))),
(vec![ISA_OP_ASL_ABS, 0xab, 0xcd], ASL(Absolute(0xcdab))),
(vec![ISA_OP_ASL_ABSX, 0xab, 0xcd], ASL(AbsoluteX(0xcdab))),
(vec![ISA_OP_BCC, 0xab], BCC(Immediate(0xab))),
(vec![ISA_OP_BEQ, 0xab], BEQ(Immediate(0xab))),
(vec![ISA_OP_BIT_ZP, 0xab], BIT(ZeroPage(0xab))),
(vec![ISA_OP_BIT_ABS, 0xab, 0xcd], BIT(Absolute(0xcdab))),
(vec![ISA_OP_BMI, 0xab], BMI(Immediate(0xab))),
(vec![ISA_OP_BNE, 0xab], BNE(Immediate(0xab))),
(vec![ISA_OP_BPL, 0xab], BPL(Immediate(0xab))),
(vec![ISA_OP_BVC, 0xab], BVC(Immediate(0xab))),
(vec![ISA_OP_BVS, 0xab], BVS(Immediate(0xab))),
(vec![ISA_OP_BRK], BRK),
];
// for (bytes, instruction) in params {
// let encoded = Encoder::encode(bytes);
// assert_eq!(encoded, instruction.into());
// }
}
}
-18
View File
@@ -1,18 +0,0 @@
use crate::mos6502flags::Mos6502Flag;
pub enum MicrocodeStep {
ReadRegisterA,
ReadRegisterX,
ReadRegisterY,
ReadFlag(Mos6502Flag),
WriteRegisterA,
WriteRegisterX,
WriteRegisterY,
WriteFlag(Mos6502Flag, bool),
ReadMemory(u16),
WriteMemory(u16, u8),
ALUAdd(u8, u8),
ALUSub(u8, u8),
ALUAddC(u8, u8, bool),
ALUSubC(u8, u8, bool),
}
-4
View File
@@ -1,4 +0,0 @@
pub mod encode;
pub mod decoder;
pub mod microcode_steps;
+12 -5
View File
@@ -1,7 +1,14 @@
#![feature(slice_as_array)]
pub mod computers;
pub mod address_mode;
pub mod mos6502cpu;
pub mod instruction;
pub mod mos6502flags;
pub mod isa;
pub mod constants;
mod instruction_stringify;
pub mod instruction;
pub mod instruction_table;
pub mod mos6502cpu;
pub mod mos6502flags;
pub mod op_info;
pub mod operand;
pub mod operation;
pub mod periph;
pub mod traits;
-72
View File
@@ -1,72 +0,0 @@
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
pub const SIZE_1KB: usize = 1024 * 1024;
pub const SIZE_64KB: usize = SIZE_1KB * 64;
pub struct Mos6502Cpu {
memory: [u8; SIZE_64KB],
a: u8,
x: u8,
y: u8,
flags: Mos6502Flags,
pc: u16,
s: u8,
microcode_step: u8
}
impl Mos6502Cpu {
pub fn new() -> Mos6502Cpu {
Mos6502Cpu {
memory: [0; SIZE_64KB],
a: 0,
x: 0,
y: 0,
flags: Mos6502Flags::default(),
pc: 0,
s: 0xfd,
microcode_step: 0
}
}
pub fn peek_flag(&self, flag_to_read: Mos6502Flag) -> bool {
self.flags.flag(flag_to_read)
}
pub fn poke_flag(&mut self, flag_to_set: Mos6502Flag, new_value: bool) {
if new_value { self.flags.set_flag(flag_to_set) } else { self.flags.clear_flag(flag_to_set) }
}
pub fn peek(&self, offset: u16) -> u8 {
self.memory[offset as usize]
}
pub fn poke(&mut self, offset: u16, value: u8) {
self.memory[offset as usize] = value
}
pub fn peek_a(&self) -> u8 {
self.a
}
pub fn poke_a(&mut self, new_a: u8) {
self.a = new_a;
}
pub fn peek_x(&self) -> u8 {
self.x
}
pub fn poke_x(&mut self, new_x: u8) {
self.x = new_x
}
pub fn peek_y(&self) -> u8 {
self.y
}
pub fn poke_y(&mut self, new_y: u8) {
self.y = new_y
}
pub fn tick(&mut self) {
}
}
+27
View File
@@ -0,0 +1,27 @@
use crate::mos6502cpu::Mos6502Cpu;
use crate::traits::bus_device::BusDevice;
impl BusDevice for Mos6502Cpu {
fn min_offset(&self) -> u16 { 0 }
fn max_offset(&self) -> u16 {
0x7fff
}
fn address_bus(&self) -> u16 {
self.address_bus
}
fn data_bus(&self) -> u8 {
self.data_bus
}
fn set_address_bus(&mut self, new_value: u16) {
self.address_bus = new_value;
}
fn set_data_bus(&mut self, new_value: u8) {
self.data_bus = new_value;
}
}
+684
View File
@@ -0,0 +1,684 @@
use crate::address_mode::AddressMode;
use crate::constants::constants_isa_op::ISA_OP_NOP;
use crate::constants::constants_system::*;
use crate::instruction::Instruction;
use crate::instruction_table::INSTRUCTION_TABLE;
use crate::mos6502flags::Mos6502Flag::*;
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
use crate::op_info::OpInfo;
use crate::operand::Operand;
use crate::operation::Operation;
use log::trace;
use crate::mos6502cpu::Mos6502Cpu;
use crate::mos6502cpu::tick_stages::Mos6502TickStates;
use crate::mos6502cpu::tick_stages::Mos6502TickStates::*;
enum Mos6502Registers {
A,
X,
Y
}
impl Mos6502Cpu {
//
// fn read_word(&self, offset: &u16) -> u16 {
// println!("READING OFFSET 0x{offset:04x} and 0x{:04x}", offset + 1);
// let low = self.memory[*offset as usize];
// let high = self.memory[*offset as usize + 1];
// println!("LOW = 0x{low:02x} HIGH = 0x{high:02x}");
// let result = (high as u16) << 8 | low as u16;
// // println!("MEMORY: {:?}", self.memory);
// println!("READ {result:04x}");
// result
// }
pub fn peek_register(&self, register_to_peek: Mos6502Registers) -> u8 {
match register_to_peek {
Mos6502Registers::A => self.a,
Mos6502Registers::X => self.x,
Mos6502Registers::Y => self.y
}
}
pub fn poke_register(&mut self, register_to_poke: Mos6502Registers, new_value: u8) {
match register_to_poke {
Mos6502Registers::A => self.a = new_value,
Mos6502Registers::X => self.x = new_value,
Mos6502Registers::Y => self.y = new_value
}
}
pub fn peek_flag(&self, flag_to_read: Mos6502Flag) -> bool {
self.flags.flag(flag_to_read)
}
pub fn poke_flag(&mut self, flag_to_set: Mos6502Flag, new_value: bool) {
if new_value {
self.flags.set_flag(flag_to_set)
} else {
self.flags.clear_flag(flag_to_set)
}
}
pub fn peek_memory(&self, offset: u16) -> u8 {
self.memory[offset as usize]
}
pub fn poke_memory(&mut self, offset: u16, value: u8) {
println!("Setting memory at {offset:04x} to {value:02x}");
self.memory[offset as usize] = value
}
fn advance_pc(&mut self, how_far: u16) {
self.pc += how_far;
}
fn set_pc_to(&mut self, new_pc: u16) {
self.pc = new_pc;
}
/// Ticks the CPU
pub fn tick(&mut self) {
println!("PREPARiNG TO TICK CPU AT PC 0x{:04x}", self.pc);
match self.tick_stage {
LoadingInstruction => {
println!("Loading instruction from data bus -> {}", self.data_bus);
let instruction = INSTRUCTION_TABLE[self.data_bus as usize].clone();
if let Some(inst) = instruction {
println!("DECODED INSTRUCTION [{:?}]/[{:?}]", inst.operation, inst.mode);
match inst.mode {
AddressMode::Absolute | AddressMode::AbsoluteX | AddressMode::AbsoluteY => {
println!("NEED TO LOAD a 16bit VALUE FOR INSTRUCTION");
self.tick_stage = Loading16BitParameter1;
}
AddressMode::Immediate => {
println!("LOADING A 8BIT VALUE FOR INSTRUCTION");
self.tick_stage = Loading8BitParameter;
}
_ => {}
}
} else {
println!("INVALID DECODE OF [${:02x}", self.data_bus);
}
}
Loading8BitParameter => {
println!("Loading parameter for 8bit ");
},
Loading16BitParameter1 => {
println!("Loading high bits of parameter");
},
Loading16BitParameter2 => {
println!("Loading low bits of parameter");
},
Stall(length) => {
println!("PREPARING TO STALL FOR {} CYCLES", length);
},
Waiting => {
println!("CPU IS WAITING.");
}
}
if self.microcode_step == 0 {
println!("OUT OF MICROSTEPS. Decoding the next instruction");
let offset = self.pc as usize;
// TODO: this calls opinfo 2x
self.oi = Instruction::opinfo(&self.memory[offset..offset + 4]).unwrap();
self.ir = Instruction::decode(&self.memory[offset..offset + 4]).unwrap();
self.microcode_step = self.oi.cycles;
println!("Decoded [[{:?}]]", self.ir);
self.advance_pc(self.oi.length as u16);
// load the microstep buffer with what steps to run
// set the counter to the number of steps left
} else {
// run 1 microcode step
println!(
"Microstep {}/{} for {:?}",
self.microcode_step, self.oi.cycles, self.ir.op
);
match self.ir.op {
Operation::ADC => match self.microcode_step {
1 => match self.ir.mode {
AddressMode::Immediate => {}
AddressMode::ZeroPage => {}
AddressMode::ZeroPageX => {}
AddressMode::Absolute => {}
AddressMode::AbsoluteX => {}
AddressMode::AbsoluteY => {}
AddressMode::Indirect => {}
AddressMode::IndirectX => {}
AddressMode::IndirectY => {}
_ => {}
},
2 => {}
_ => {}
},
Operation::AND => {}
Operation::ASL => {}
Operation::BCC => {}
Operation::BCS => {}
Operation::BEQ => {}
Operation::BIT => {}
Operation::BMI => {}
Operation::BNE => {}
Operation::BPL => {}
Operation::BRK => {}
Operation::BVC => {}
Operation::BVS => {}
Operation::CLC => {
self.flags.clear_flag(Carry);
}
Operation::CLD => {
self.flags.clear_flag(Decimal);
}
Operation::CLI => {
self.flags.clear_flag(Interrupt);
}
Operation::CLV => {
self.flags.clear_flag(Overflow);
}
Operation::CMP => {}
Operation::CPX => {}
Operation::CPY => {}
Operation::DEC => {
match self.microcode_step {
// DEC Step 1
1 => {
let working_value = match self.oi.mode {
AddressMode::ZeroPage => {
// read from
let offset = match self.ir.operand {
Operand::Byte(z) => z,
_ => 0x00,
};
trace!("READING FROM MEMORY AT 0x{offset:04x}");
self.memory[offset as usize]
// self.peek(offset);
}
AddressMode::ZeroPageX => {
let offset = match self.ir.operand {
Operand::Byte(z) => z,
_ => 0x00,
};
// self.memory.peek(offset + self.x);
self.memory[offset as usize]
}
AddressMode::Absolute => {
let offset = match self.ir.operand {
Operand::Word(offset) => offset,
_ => 0x00,
};
// self.memory.peek(offset)
self.memory[offset as usize]
}
AddressMode::AbsoluteX => {
let offset = match self.ir.operand {
Operand::Word(offset) => offset,
_ => 0x00,
};
// self.memory.peek(offset + self.x);
self.memory[offset as usize]
}
_ => 0x00,
};
}
// DEC write memory
2 => {
self.a = self.cycle_carry as u8;
}
_ => {}
}
}
Operation::DEX => {
if self.microcode_step == 1 {
let (new_x, new_carry) = self.x.overflowing_sub(1);
self.poke_register(Mos6502Registers::X, new_x);
self.poke_flag(Carry, new_carry);
}
}
Operation::DEY => {
if self.microcode_step == 1 {
(self.y, _) = self.y.overflowing_sub(1);
}
}
Operation::EOR => {}
Operation::INC => {}
Operation::INX => {
if self.microcode_step == 1 {
let (new_x, new_carry) = self.x.overflowing_add(1);
self.poke_register(Mos6502Registers::X, new_x);
self.poke_flag(Carry, new_carry);
self.address_bus = self.pc;
self.data_bus = 0x00;
}
}
Operation::INY => {
if self.microcode_step == 1 {
let (new_y, new_carry) = self.y.overflowing_add(1);
self.poke_register(Mos6502Registers::Y, new_y);
self.poke_flag(Carry, new_carry);
self.address_bus = self.pc;
self.data_bus = 0x00;
}
}
Operation::JMP => match self.ir.operand {
Operand::Word(offset) => {
self.pc = offset;
self.address_bus = self.pc;
self.data_bus = 0x00;
}
_ => {}
},
Operation::JSR => {
// push pc to stack.
// jump to the subroutine.
}
Operation::LDA => match self.oi.mode {
AddressMode::Immediate => match self.ir.operand {
Operand::Byte(value) => {
println!("Loading 0x{value:02x} ({value}) into A");
self.a = value;
}
_ => {}
},
AddressMode::ZeroPage => match self.ir.operand {
Operand::Byte(value) => {
println!("Loading from zero page at 0x{value:02x} ({value})");
self.a = self.memory[value as usize];
}
_ => {}
},
AddressMode::ZeroPageX => match self.ir.operand {
Operand::Byte(value) => {
let x_offset = self.x;
self.a = self.memory[(value + x_offset) as usize];
}
_ => {}
},
AddressMode::Absolute => {
if let Operand::Word(offset) = self.ir.operand {
println!("Loading from absolute address 0x{offset:04x}");
self.a = self.memory[offset as usize];
}
}
AddressMode::AbsoluteX => {
if let Operand::Word(offset) = self.ir.operand {
self.a = self.memory[(offset + self.x as u16) as usize];
}
}
AddressMode::AbsoluteY => {
if let Operand::Word(offset) = self.ir.operand {
let real_offset = offset + self.y as u16;
println!("offset: {offset:04x} + {:02x}", self.y);
self.a = self.memory[(offset + self.y as u16) as usize];
}
}
AddressMode::Indirect => {}
AddressMode::IndirectX => {}
AddressMode::IndirectY => {}
_ => {
println!("INVALID ADDRESS MODE FOR LDA");
}
},
Operation::LDX => {}
Operation::LDY => {}
Operation::LSR => {}
Operation::NOP => {
// do nothing.
}
Operation::ORA => {}
Operation::PHA => {}
Operation::PHP => {}
Operation::PLA => {}
Operation::PLP => {}
Operation::ROL => {
if self.microcode_step == 1 {
self.a = self.a.rotate_left(1);
}
}
Operation::ROR => {
// rotate A
if self.microcode_step == 1 {
self.a = self.a.rotate_right(1);
}
}
Operation::RTI => {}
Operation::RTS => {}
Operation::SBC => {}
Operation::SEC => {
self.flags.set_flag(Carry);
}
Operation::SED => {
self.flags.set_flag(Decimal);
}
Operation::SEI => {
self.flags.set_flag(Interrupt);
}
Operation::STA => {
match self.oi.mode {
AddressMode::ZeroPage => {
// write to the zero page.
match self.ir.operand {
Operand::Byte(target) => {
self.memory[target as usize] = self.a;
}
_ => {
// Invalid parameter
}
}
}
AddressMode::ZeroPageX => {
match self.ir.operand {
Operand::Byte(target) => {
let x = self.x;
self.memory[(x + target) as usize] = self.a;
}
_ => {
// Invalid Parameter
}
}
}
AddressMode::Absolute => {
// write from A to the specified memory location
match self.ir.operand {
Operand::Word(offset) => {
self.memory[offset as usize] = self.a;
}
_ => {
// Invalid Parameter
}
}
}
AddressMode::AbsoluteX => {
match self.ir.operand {
Operand::Word(offset) => {
self.memory[(offset + self.x as u16) as usize] = self.a;
}
_ => {
// Invalid Parameter
}
}
}
AddressMode::AbsoluteY => {
match self.ir.operand {
Operand::Word(offset) => {
self.memory[(offset + self.y as u16) as usize] = self.a;
}
_ => {
// Invalid Parameter
}
}
}
AddressMode::IndirectX => {}
AddressMode::IndirectY => {}
_ => {
// invalid memory mode
}
}
}
Operation::STX => {}
Operation::STY => {}
Operation::TAX => {
self.x = self.a;
}
Operation::TAY => {
self.y = self.a;
}
Operation::TSX => {}
Operation::TXA => {
self.a = self.x;
}
Operation::TXS => {}
Operation::TYA => {
self.y = self.a;
}
}
self.microcode_step -= 1;
}
}
}
/*
#[cfg(test)]
mod test {
use super::*;
use crate::constants::constants_isa_op::*;
use crate::instruction_table::{instruction_cycles, INSTRUCTION_TABLE};
#[test]
fn clc() {
// setup the CPU for our test
let mut cpu = Mos6502Cpu::default();
// tick through the reset cycle
while !cpu.has_reset {
cpu.tick();
}
println!("DONE RESET TICKS");
cpu.flags.set_flag(Carry);
// Load our 'test program'
cpu.memory[0x6000] = ISA_OP_CLC;
// Start the PC at our program
cpu.pc = 0x6000;
// Tick the CPU through the instruction
for _ in 0..instruction_cycles(ISA_OP_CLC) {
cpu.tick();
}
assert!(!cpu.peek_flag(Carry));
}
#[test]
fn cld() {
let mut cpu = Mos6502Cpu::default();
cpu.flags.set_flag(Decimal);
cpu.memory[0x6000] = ISA_OP_CLD;
cpu.pc = 0x6000;
for _ in 0..instruction_cycles(ISA_OP_CLD) {
cpu.tick();
}
assert!(!cpu.peek_flag(Decimal));
}
#[test]
fn cli() {
let mut cpu = Mos6502Cpu::default();
cpu.flags.set_flag(Interrupt);
cpu.memory[0x6000] = ISA_OP_CLI;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_CLI) {
cpu.tick();
}
assert!(!cpu.peek_flag(Interrupt));
}
#[test]
fn clv() {
let mut cpu = Mos6502Cpu::default();
cpu.flags.set_flag(Overflow);
cpu.memory[0x6000] = ISA_OP_CLV;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_CLV) {
cpu.tick();
}
assert!(!cpu.peek_flag(Overflow));
}
#[test]
fn lda_immediate() {
let mut cpu = Mos6502Cpu::default();
cpu.memory[0x6000] = ISA_OP_LDA_I;
cpu.memory[0x6001] = 0xab;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_LDA_I) {
cpu.tick();
}
assert_eq!(cpu.a, 0xab);
}
#[test]
fn lda_zx() {
let mut cpu = Mos6502Cpu::default();
cpu.poke_x(1);
cpu.memory[0x6000] = ISA_OP_LDA_ZX;
cpu.memory[0x6001] = 0xab;
cpu.memory[0x00ac] = 0xbe;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_LDA_ZX) {
cpu.tick();
}
// println!("MEMORY AT 0x00aa, ab, ac, ad, ae -> {:02x} {:02x} {:02x} {:02x} {:02x}", cpu.memory[0x00aa], cpu.memory[0x00ab], cpu.memory[0x00ac], cpu.memory[0x00ad], cpu.memory[0x00ae]);
// cpu.dump();
assert_eq!(cpu.peek_a(), 0xbe);
assert!(!cpu.peek_flag(Zero));
assert!(!cpu.peek_flag(Carry));
assert!(!cpu.peek_flag(Negative));
}
#[test]
fn lda_zeropage() {
let mut cpu = Mos6502Cpu::default();
cpu.memory[0x6000] = ISA_OP_LDA_Z;
cpu.memory[0x6001] = 0xab;
// Load ZeroPage
cpu.memory[0x00ab] = 0xbe;
cpu.pc = 0x6000;
for _ in 0..instruction_cycles(ISA_OP_LDA_Z) {
cpu.tick();
}
assert_eq!(cpu.a, 0xbe);
}
#[test]
fn lda_absolute() {
let mut cpu = Mos6502Cpu::default();
cpu.memory[0x6000] = ISA_OP_LDA_ABS;
cpu.memory[0x6001] = 0xef;
cpu.memory[0x6002] = 0x0e;
cpu.memory[0x0eef] = 0xab;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_LDA_ABS) {
cpu.tick();
}
assert_eq!(cpu.a, 0xab);
}
#[test]
fn lda_absolutex() {
let mut cpu = Mos6502Cpu::default();
cpu.memory[0x6000] = ISA_OP_LDA_ABSX;
cpu.memory[0x6001] = 0xef;
cpu.memory[0x6002] = 0x0e;
cpu.poke_x(0x01);
cpu.memory[0x0ef0] = 0xab;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_LDA_ABSX) {
cpu.tick();
}
assert_eq!(cpu.a, 0xab);
}
#[test]
fn lda_absolutey() {
let mut cpu = Mos6502Cpu::default();
cpu.memory[0x6000] = ISA_OP_LDA_ABSY;
cpu.memory[0x6001] = 0xef;
cpu.memory[0x6002] = 0x0e;
cpu.poke_y(0x01);
cpu.memory[0x0ef0] = 0xab;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_LDA_ABSY) {
cpu.tick();
}
assert_eq!(cpu.a, 0xab);
}
#[test]
fn dex_inx() {
let mut cpu = Mos6502Cpu::default();
cpu.x = 0xab;
cpu.memory[0x6000] = ISA_OP_DEX;
cpu.memory[0x6001] = ISA_OP_INX;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_DEX) {
cpu.tick();
}
assert_eq!(0xaa, cpu.x);
for _ in 0..=instruction_cycles(ISA_OP_INX) {
cpu.tick();
}
assert_eq!(0xab, cpu.x);
}
#[test]
fn dey_iny() {
let mut cpu = Mos6502Cpu::default();
cpu.poke_y(0xab);
cpu.memory[0x6000] = ISA_OP_DEY;
cpu.memory[0x6001] = ISA_OP_INY;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_DEY) {
cpu.tick();
}
assert_eq!(0xaa, cpu.peek_y());
for _ in 0..=instruction_cycles(ISA_OP_INY) {
cpu.tick();
}
assert_eq!(0xab, cpu.peek_y());
}
#[test]
fn rol_a_ror_a() {
let mut cpu = Mos6502Cpu::default();
cpu.poke_a(0b1010_1010); // 0xaa
cpu.memory[0x6000] = ISA_OP_ROL_A;
cpu.memory[0x6001] = ISA_OP_ROR_A;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_ROL_A) {
cpu.tick();
}
assert_eq!(cpu.peek_a(), 0b0101_0101);
for _ in 0..=instruction_cycles(ISA_OP_ROR_A) {
cpu.tick();
}
assert_eq!(cpu.peek_a(), 0b1010_1010);
}
#[test]
fn rol_zp_ror_zp() {
let mut cpu = Mos6502Cpu::default();
cpu.memory[0x00ab] = 0b0101_0101;
cpu.memory[0x6000] = ISA_OP_ROL_ZP;
cpu.memory[0x6001] = 0xab;
cpu.pc = 0x6000;
for _ in 0..=instruction_cycles(ISA_OP_ROL_ZP) {
cpu.tick();
}
assert_eq!(cpu.memory[0xab], 0b1010_1010);;
}
}
*/
+39
View File
@@ -0,0 +1,39 @@
use crate::mos6502cpu::Mos6502Cpu;
impl Mos6502Cpu {
/// dump_data
///
/// returns
/// PC, A, X, Y, Address_Bus, Data_Bus, Microcode_Step
pub fn dump_data(&self) -> (u16, u8, u8, u8, u16, u8, u8, u16, u16, u16) {
(
self.pc,
self.a,
self.x,
self.y,
self.address_bus,
self.data_bus,
self.microcode_step,
self.reset_vector,
self.int_vector,
self.nmi_vector
)
}
pub fn dump(&self) {
println!(
"CPU State: PC: ${:04x} / A: ${:02x} / X: ${:02x} / Y: ${:02x} / ADDRESS: ${:04x} / DATA: ${:02x} / MICROSTEPS: {:02} / S: {} / NMI: ${:04x} / RST: ${:04x} / INT: ${:04x}",
self.pc,
self.a,
self.x,
self.y,
self.address_bus,
self.data_bus,
self.microcode_step,
self.flags.dump(),
self.nmi_vector,
self.reset_vector,
self.int_vector
);
}
}
+43
View File
@@ -0,0 +1,43 @@
use crate::address_mode::AddressMode;
use crate::constants::constants_isa_op::ISA_OP_NOP;
use crate::constants::constants_system::SIZE_64KB;
use crate::instruction::Instruction;
use crate::instruction_table::INSTRUCTION_TABLE;
use crate::mos6502cpu::Mos6502Cpu;
use crate::mos6502cpu::tick_stages::Mos6502TickStates::LoadingInstruction;
use crate::operand::Operand;
use crate::operation::Operation;
impl Default for Mos6502Cpu {
fn default() -> Self {
let mut working = Mos6502Cpu {
memory: [0x00; SIZE_64KB],
a: 0x00,
x: 0x00,
y: 0x00,
flags: Default::default(),
pc: 0xfffd,
s: 0x00,
microcode_step: 0x00,
address_bus: 0x00,
data_bus: 0x00,
ir: Instruction {
op: Operation::NOP,
mode: AddressMode::Implied,
operand: Operand::None,
},
oi: INSTRUCTION_TABLE[ISA_OP_NOP as usize].clone().unwrap(),
has_reset: false,
iv: 0xfffe,
cycle_carry: 0x0000,
ir_bytes: [0x00; 4],
read_signal: true,
reset_vector: 0x0000,
int_vector: 0x0000,
nmi_vector: 0x0000,
tick_stage: LoadingInstruction
};
working.reset_cpu();
working
}
}
+44
View File
@@ -0,0 +1,44 @@
use crate::constants::constants_system::SIZE_64KB;
use crate::instruction::Instruction;
use crate::mos6502cpu::tick_stages::Mos6502TickStates;
use crate::mos6502flags::Mos6502Flags;
use crate::op_info::OpInfo;
pub mod cpu;
pub mod new;
pub mod tick2;
pub mod dbg;
pub mod tick_stages;
pub mod default;
pub mod bus_device;
pub struct Mos6502Cpu {
pub(crate) memory: [u8; SIZE_64KB],
/// accumulator
pub(crate) a: u8,
/// x register
pub(crate) x: u8,
/// y register
pub(crate) y: u8,
/// cpu flags
pub(crate) flags: Mos6502Flags,
/// program counter
pub pc: u16,
/// stack offset
pub(crate) s: u8,
pub microcode_step: u8,
pub(crate) address_bus: u16,
pub(crate) data_bus: u8,
pub(crate) ir: Instruction, // Instruction Register
pub(crate) oi: OpInfo,
pub(crate) has_reset: bool,
pub(crate) iv: u16, // Interrupt Vector
pub(crate) cycle_carry: u16, // Value to hold between microsteps
pub(crate) ir_bytes: [u8; 4],
/// CPU Read signal
pub read_signal: bool,
pub(crate) reset_vector: u16,
pub(crate) int_vector: u16,
pub(crate) nmi_vector: u16,
pub tick_stage: Mos6502TickStates
}
+24
View File
@@ -0,0 +1,24 @@
use crate::constants::constants_system::{MOS6502_RESET_CYCLE_COUNT, OFFSET_RESET_VECTOR, SIZE_64KB};
use crate::mos6502cpu::Mos6502Cpu;
impl Mos6502Cpu {
pub fn new() -> Mos6502Cpu {
let array = [0x00u8; SIZE_64KB];
let mut working = Mos6502Cpu {
memory: array,
ir_bytes: [0x00; 4],
..Default::default()
};
working.reset_cpu();
working
}
pub(crate) fn reset_cpu(&mut self) {
self.microcode_step = MOS6502_RESET_CYCLE_COUNT as u8;
// self = &mut Mos6502Cpu::default();
println!("Should tick 7 times, then 6 cycles to read the reset and int vectors.");
// read the value at 0xfffa 0xfffb for our NMI vector.
// read the value at 0xfffc 0xfffd for our reset vector.
// read the value at 0xfffe 0xffff for our int vector
}
}
+92
View File
@@ -0,0 +1,92 @@
use crate::constants::constants_system::{OFFSET_INT_VECTOR, OFFSET_RESET_VECTOR};
use crate::mos6502cpu::Mos6502Cpu;
enum Mos6502ResetSteps {
/// there are 6 of these
DummyRead(u8),
ReadRstVectorLow,
ReadRstVectorHigh,
ReadPcLow,
ReadPcHigh
}
impl Mos6502Cpu {
fn reset_step(&mut self, address_bus: u16, data_bus: u8, read: bool) -> (u16, u8, bool) {
println!("Reset microstep {}", self.microcode_step);
// we need to do the reset steps
// reduce the number of remaining microsteps
self.read_signal = true;
match self.microcode_step {
6 => {
// NMI High byte
}
5 => {
// NMI low byte
}
4 => {
// read first byte of reset vector
self.address_bus = OFFSET_RESET_VECTOR;
}
3 => {
// at this point data holds the upper byte of our reset vector
self.reset_vector = (data_bus as u16) << 8;
println!("Loaded reset vector of 0x{:04x}", self.reset_vector);
// read secondd byte of reset vector
self.address_bus = OFFSET_RESET_VECTOR + 1;
}
2 => {
self.reset_vector |= data_bus as u16;
println!("Loaded reset vector of 0x{:04x}", self.reset_vector);
// read first byte of interrupt vector
self.address_bus = OFFSET_INT_VECTOR;
}
1 => {
// read second byte of interrupt vector
self.address_bus = OFFSET_INT_VECTOR + 1;
}
0 => {
self.int_vector |= data_bus as u16;
println!("Loaded interrupt vector of 0x{:04x}", self.int_vector);
self.pc = self.reset_vector;
println!("Set PC to Reset Vector. Giddy-up!");
println!("START HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK");
// the KIM-1 uses 0x0000 for its initial PC
self.pc = 0x0000;
println!("END HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK");
self.has_reset = true;
}
_ => {
}
}
if self.microcode_step > 0 {
self.microcode_step -= 1;
}
(address_bus, data_bus, read)
}
/// AccurateTick
///
/// In: address_bus > Address of data operationm
/// data_bus > Data read or written
/// State:
/// read_bus > Flag for if cpu is reading or writing the data bus
/// cycle_step > Index for what step of the Decode->Load->Execute cycle we are in
/// Out: address_bus > address for operation
/// data_bus > data for the operation
/// read_bus > lets rest of the computer know if the CPU is reading from the address
/// provided or if we are writing to the address
pub fn tick2(&mut self, address_bus: u16, data_bus: u8) -> (u16, u8, bool) {
println!("STARTING TICK2");
if !self.has_reset { return self.reset_step(address_bus, data_bus, self.read_signal) }
// we have completed the reset cycle
if self.read_signal {
// we should see new data in the data_bus for us
let read_data = data_bus;
println!("READ 0x{read_data:02x} from data bus.");
self.data_bus = read_data;
} else {
// we are writing to the bus.
}
(self.address_bus, self.data_bus, self.read_signal)
}
}
+19
View File
@@ -0,0 +1,19 @@
/// Mos6502TickStates
///
/// The set of what a tick can be doing
///
pub enum Mos6502TickStates {
/// Loading the first byte into the IR
LoadingInstruction,
/// Loading an 8 bit parameter
Loading8BitParameter,
/// Loading the MSB 8 bits
Loading16BitParameter1,
/// Loading the LSB 8 bits
Loading16BitParameter2,
/// Stalling for accurate emulation
Stall(u8),
/// Waiting for the next instruction
Waiting
}
+168 -56
View File
@@ -1,86 +1,198 @@
use crate::mos6502flags::Mos6502Flag::*;
pub const BIT_NEGATIVE: u8 = 7;
pub const BIT_OVERFLOW: u8 = 6;
pub const BIT_BREAK: u8 = 4;
pub const BIT_DECIMAL: u8 = 3;
pub const BIT_INTERRUPT: u8 = 2;
pub const BIT_ZERO: u8 = 1;
pub const BIT_CARRY: u8 = 0;
/// Represents the status flags in the 6502 processor's status register (P).
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Mos6502Flag {
/// Carry Flag (C)
///
/// Set if an arithmetic operation results in a carry out of the most significant bit (for addition),
/// or a borrow (for subtraction). Also used for bit shifts and rotates.
Carry,
/// Zero Flag (Z)
///
/// Set if the result of an operation is zero.
Zero,
/// Interrupt Disable Flag (I)
///
/// When set, disables maskable interrupts (IRQ).
Interrupt,
/// Decimal Mode Flag (D)
///
/// When set, arithmetic operations use Binary-Coded Decimal (BCD) mode.
/// Note: Not supported on all 6502 variants (e.g., not on the NES CPU).
Decimal,
/// Break Command Flag (B)
///
/// Set when a BRK (break) instruction is executed.
/// Used to distinguish software interrupts from hardware ones.
Break,
/// Overflow Flag (V)
///
/// Set when an arithmetic operation results in a signed overflow.
/// For example, adding two positive numbers results in a negative.
Overflow,
Negative
/// Negative Flag (N)
///
/// Set if the result of an operation has bit 7 set (i.e., the result is negative in two's complement).
Negative,
}
#[derive(Default)]
impl Mos6502Flag {
pub fn index(&self) -> u8 {
match self {
Carry => BIT_CARRY,
Zero => BIT_ZERO,
Interrupt => BIT_INTERRUPT,
Decimal => BIT_DECIMAL,
Break => BIT_BREAK,
Overflow => BIT_OVERFLOW,
Negative => BIT_NEGATIVE,
}
}
}
#[derive(Default, PartialEq, Debug)]
pub struct Mos6502Flags {
carry: bool,
zero: bool,
interrupt: bool,
decimal: bool,
break_flag: bool,
overflow: bool,
negative: bool,
// TODO: This is pub for tests. shouldn't be that way
pub carry: bool,
pub zero: bool,
pub interrupt: bool,
pub decimal: bool,
pub break_flag: bool,
pub overflow: bool,
pub negative: bool,
}
impl Mos6502Flags {
pub fn dump(&self) -> String {
format!(
"{}{}{}{}{}{}{}",
if self.carry { 'C' } else { 'c' },
if self.zero { 'Z' } else { 'z' },
if self.interrupt { 'I' } else { 'i' },
if self.decimal { 'D' } else { 'd' },
if self.break_flag { 'B' } else { 'b' },
if self.overflow { 'O' } else { 'o' },
if self.negative { 'N' } else { 'n' }
)
}
}
impl Mos6502Flags {
pub fn set_flag(&mut self, flag_to_set: Mos6502Flag) {
self.change_flag(flag_to_set, true);
println!("Setting {flag_to_set:?} flag");
match flag_to_set {
Carry => self.carry = true,
Zero => self.zero = true,
Interrupt => self.interrupt = true,
Decimal => self.decimal = true,
Break => self.break_flag = true,
Overflow => self.overflow = true,
Negative => self.negative = true,
}
}
pub fn clear_flag(&mut self, flag_to_clear: Mos6502Flag) {
self.change_flag(flag_to_clear, false);
println!("Clearing {flag_to_clear:?} flag");
match flag_to_clear {
Carry => self.carry = false,
Zero => self.zero = false,
Interrupt => self.interrupt = false,
Decimal => self.decimal = false,
Break => self.break_flag = false,
Overflow => self.overflow = false,
Negative => self.negative = false,
}
}
fn change_flag(&mut self, flag_to_change: Mos6502Flag, new_value: bool) {
match flag_to_change {
Mos6502Flag::Carry => {
self.carry = new_value
}
Mos6502Flag::Zero => {
self.zero = new_value
}
Mos6502Flag::Interrupt => {
self.interrupt = new_value
}
Mos6502Flag::Decimal => {
self.decimal = new_value
}
Mos6502Flag::Break => {
self.break_flag = new_value
}
Mos6502Flag::Overflow => {
self.overflow = new_value
}
Mos6502Flag::Negative => {
self.negative = new_value
}
if new_value {
self.set_flag(flag_to_change);
} else {
self.clear_flag(flag_to_change);
}
}
pub fn flag(&self, flag_to_read: Mos6502Flag) -> bool {
match flag_to_read {
Mos6502Flag::Carry => {
self.carry
}
Mos6502Flag::Zero => {
self.zero
}
Mos6502Flag::Interrupt => {
self.interrupt
}
Mos6502Flag::Decimal => {
self.decimal
}
Mos6502Flag::Break => {
self.break_flag
}
Mos6502Flag::Overflow => {
self.overflow
}
Mos6502Flag::Negative => {
self.negative
}
Mos6502Flag::Negative => self.negative,
Mos6502Flag::Overflow => self.overflow,
// 5
Mos6502Flag::Break => self.break_flag,
Mos6502Flag::Decimal => self.decimal,
Mos6502Flag::Interrupt => self.interrupt,
Mos6502Flag::Zero => self.zero,
Mos6502Flag::Carry => self.carry,
}
}
pub fn as_byte(&self) -> u8 {
let mut working = 0x00;
if self.flag(Negative) {
working += 1 << Negative.index();
}
if self.flag(Overflow) {
working += 1 << Overflow.index();
}
working += 1 << 5; // Always Set
if self.flag(Break) {
working += 1 << Break.index();
}
if self.flag(Decimal) {
working += 1 << Decimal.index();
}
if self.flag(Interrupt) {
working += 1 << Interrupt.index();
}
if self.flag(Zero) {
working += 1 << Zero.index();
}
if self.flag(Carry) {
working += 1 << Carry.index();
}
working
}
pub fn from_byte(src: u8) -> Self {
let mut working = Self::default();
working.change_flag(Negative, Self::bit(src, Negative.index()));
working.change_flag(Overflow, Self::bit(src, Overflow.index()));
working.change_flag(Break, Self::bit(src, Break.index()));
working.change_flag(Decimal, Self::bit(src, Decimal.index()));
working.change_flag(Interrupt, Self::bit(src, Interrupt.index()));
working.change_flag(Zero, Self::bit(src, Zero.index()));
working.change_flag(Carry, Self::bit(src, Carry.index()));
working
}
/// bit
///
/// src -> Source byte to check in
/// pos -> Which bit to check
///
/// returns bool
///
/// True if the bit is set.
/// False if the bit is not set
#[inline]
fn bit(src: u8, pos: u8) -> bool {
(src >> pos) & 1 != 0
}
}
+17
View File
@@ -0,0 +1,17 @@
use crate::address_mode::AddressMode;
use crate::operation::Operation;
#[derive(Debug, Clone)]
pub struct OpInfo {
/// What is the operation
pub operation: Operation,
/// How does this operation access memory
pub mode: AddressMode,
/// Bytes to represent the instruction and parameters
pub length: u8,
/// CPU Cycles to complete the instruction
pub cycles: u8,
/// Format string for disassembly
pub format_prefix: &'static str,
pub format_postfix: &'static str
}
+6
View File
@@ -0,0 +1,6 @@
#[derive(Debug, PartialEq)]
pub enum Operand {
None,
Byte(u8),
Word(u16),
}
+359
View File
@@ -0,0 +1,359 @@
/// Represents all official 6502 CPU instructions.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Operation {
/// Add with Carry
///
/// Affects flags: N, V, Z, C
///
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4),
/// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5)
ADC,
/// Logical AND with Accumulator
///
/// Affects flags: N, Z
///
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4),
/// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5)
AND,
/// Arithmetic Shift Left
///
/// Affects flags: N, Z, C
///
/// Addressing Modes: Accumulator (1/2), ZeroPage (2/5), ZeroPageX (2/6), Absolute (3/6),
/// AbsoluteX (3/7)
ASL,
/// Branch if Carry Clear
///
/// Addressing Modes: Relative (2/2)
BCC,
/// Branch if Carry Set
///
/// Addressing Modes: Relative (2/2)
BCS,
/// Branch if Equal (Zero Set)
///
/// Addressing Modes: Relative (2/2)
BEQ,
/// Bit Test
///
/// Affects flags: N, V, Z
///
/// Addressing Modes: ZeroPage (2/3), Absolute (3/4)
BIT,
/// Branch if Minus (Negative Set)
///
/// Addressing Modes: Relative (2/2)
BMI,
/// Branch if Not Equal (Zero Clear)
///
/// Addressing Modes: Relative (2/2)
BNE,
/// Branch if Positive (Negative Clear)
///
/// Addressing Modes: Relative (2/2)
BPL,
/// Force Interrupt
///
/// Affects flags: B
///
/// Addressing Modes: Implied (1/7)
BRK,
/// Branch if Overflow Clear
///
/// Addressing Modes: Relative (2/2)
BVC,
/// Branch if Overflow Set
///
/// Addressing Modes: Relative (2/2)
BVS,
/// Clear Carry Flag
///
/// Affects flags: C
///
/// Addressing Modes: Implied (1/2)
CLC,
/// Clear Decimal Mode
///
/// Affects flags: D
///
/// Addressing Modes: Implied (1/2)
CLD,
/// Clear Interrupt Disable
///
/// Affects flags: I
///
/// Addressing Modes: Implied (1/2)
CLI,
/// Clear Overflow Flag
///
/// Affects flags: V
///
/// Addressing Modes: Implied (2/2)
CLV,
/// Compare Accumulator
///
/// Affects flags: N, Z, C
///
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4),
/// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5)
CMP,
/// Compare X Register
///
/// Affects flags: N, Z, C
///
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), Absolute (3/4)
CPX,
/// Compare Y Register
///
/// Affects flags: N, Z, C
///
/// Addressing Modes: Immediate, ZeroPage, Absolute
CPY,
/// Decrement Memory
///
/// Affects flags: N, Z
///
/// Addressing Modes: ZeroPage, ZeroPageX, Absolute, AbsoluteX
DEC,
/// Decrement X Register
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
DEX,
/// Decrement Y Register
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
DEY,
/// Exclusive OR with Accumulator
///
/// Affects flags: N, Z
///
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
EOR,
/// Increment Memory
///
/// Affects flags: N, Z
///
/// Addressing Modes: ZeroPage, ZeroPageX, Absolute, AbsoluteX
INC,
/// Increment X Register
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
INX,
/// Increment Y Register
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
INY,
/// Jump to Address
///
/// Addressing Modes: Absolute, Indirect
JMP,
/// Jump to Subroutine
///
/// Addressing Modes: Absolute
JSR,
/// Load Accumulator
///
/// Affects flags: N, Z
///
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
LDA,
/// Load X Register
///
/// Affects flags: N, Z
///
/// Addressing Modes: Immediate, ZeroPage, ZeroPageY, Absolute, AbsoluteY
LDX,
/// Load Y Register
///
/// Affects flags: N, Z
///
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX
LDY,
/// Logical Shift Right
///
/// Affects flags: N, Z, C
///
/// Addressing Modes: Accumulator, ZeroPage, ZeroPageX, Absolute, AbsoluteX
LSR,
/// No Operation
///
/// Addressing Modes: Implied
NOP,
/// Logical Inclusive OR with Accumulator
///
/// Affects flags: N, Z
///
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
ORA,
/// Push Accumulator on Stack
///
/// Addressing Modes: Implied
PHA,
/// Push Processor Status on Stack
///
/// Addressing Modes: Implied
PHP,
/// Pull Accumulator from Stack
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
PLA,
/// Pull Processor Status from Stack
///
/// Addressing Modes: Implied
PLP,
/// Rotate Left
///
/// Affects flags: N, Z, C
///
/// Addressing Modes: Accumulator, ZeroPage, ZeroPageX, Absolute, AbsoluteX
ROL,
/// Rotate Right
///
/// Affects flags: N, Z, C
///
/// Addressing Modes: Accumulator, ZeroPage, ZeroPageX, Absolute, AbsoluteX
ROR,
/// Return from Interrupt
///
/// Addressing Modes: Implied
RTI,
/// Return from Subroutine
///
/// Addressing Modes: Implied
RTS,
/// Subtract with Carry
///
/// Affects flags: N, V, Z, C
///
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
SBC,
/// Set Carry Flag
///
/// Affects flags: C
///
/// Addressing Modes: Implied
SEC,
/// Set Decimal Flag
///
/// Affects flags: D
///
/// Addressing Modes: Implied
SED,
/// Set Interrupt Disable
///
/// Affects flags: I
///
/// Addressing Modes: Implied
SEI,
/// Store Accumulator
///
/// Addressing Modes: ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
STA,
/// Store X Register
///
/// Addressing Modes: ZeroPage, ZeroPageY, Absolute
STX,
/// Store Y Register
///
/// Addressing Modes: ZeroPage, ZeroPageX, Absolute
STY,
/// Transfer Accumulator to X
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
TAX,
/// Transfer Accumulator to Y
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
TAY,
/// Transfer Stack Pointer to X
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
TSX,
/// Transfer X to Accumulator
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
TXA,
/// Transfer X to Stack Pointer
///
/// Addressing Modes: Implied
TXS,
/// Transfer Y to Accumulator
///
/// Affects flags: N, Z
///
/// Addressing Modes: Implied
TYA,
}
+9
View File
@@ -0,0 +1,9 @@
Peripherals
At28C256 - 256 byte static ram
Hm682256 - 256 byte ROM
Kim1_Keypad - Keypad for KIM-1 Computer
mos6520 - Peripheral Adapter
mos6522 - Versatile Interface Adapter (6520++)
mos6530 - RRIOT (Ram, Rom, Input, Output, Timers)
+8
View File
@@ -0,0 +1,8 @@
use std::slice::Chunks;
use crate::periph::at28c256::At28C256;
impl At28C256 {
pub fn chunks(&self, size: usize) -> Chunks<u8> {
self.data.chunks(size)
}
}
+28
View File
@@ -0,0 +1,28 @@
use crate::periph::at28c256::At28C256;
use crate::traits::bus_device::BusDevice;
impl BusDevice for At28C256 {
fn min_offset(&self) -> u16 {
self.offset
}
fn max_offset(&self) -> u16 {
self.max_offset
}
fn address_bus(&self) -> u16 {
self.address_bus
}
fn data_bus(&self) -> u8 {
self.data_bus
}
fn set_address_bus(&mut self, new_value: u16) {
self.address_bus = new_value
}
fn set_data_bus(&mut self, new_value: u8) {
self.data_bus = new_value
}
}
+17
View File
@@ -0,0 +1,17 @@
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
use crate::constants::constants_test::*;
impl At28C256 {
/// checksum
///
/// calculates and returns the checksum for the loaded binary.
/// files with all zero will calculate to zero
pub fn checksum(&self) -> u8 {
At28C256::checksum_static(&self.data[..])
}
pub fn checksum_static(data: &[u8]) -> u8 {
data.iter().fold(0u8, |acc, &b| acc.wrapping_add(b))
}
}
+8
View File
@@ -0,0 +1,8 @@
use crate::periph::at28c256::At28C256;
use crate::traits::bus_control_byte::BusControlByte;
const CTRL_CS: u8 = 0b0000_0001;
const CTRL_WE: u8 = 0b0000_0010;
const CTRL_OE: u8 = 0b0000_0100;
pub struct At28c256Control {}
+23
View File
@@ -0,0 +1,23 @@
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
use crate::periph::hm62256::Hm62256;
impl Default for At28C256 {
fn default() -> Self {
let vec = vec![0xea; SIZE_32KB];
let boxed_slice: Box<[u8]> = vec.into_boxed_slice();
let boxed_array: Box<[u8; SIZE_32KB]> = boxed_slice
.try_into()
.expect("Failed to convert Vec to boxed array");
At28C256 {
data: boxed_array,
address_bus: 0x0000,
data_bus: 0x00,
offset: 0x0000,
max_offset: 0x3fff,
cs: false,
oe: false,
we: false
}
}
}
+15
View File
@@ -0,0 +1,15 @@
use crate::periph::at28c256::At28C256;
pub struct At28C256State {
offset: u16,
max_offset: u16
}
impl At28C256 {
pub fn dump(&self) -> At28C256State {
At28C256State {
offset: self.offset,
max_offset: self.max_offset
}
}
}
+16
View File
@@ -0,0 +1,16 @@
use log::debug;
use crate::periph::at28c256::At28C256;
use crate::traits::bus_device::BusDevice;
use crate::traits::memory_chip::MemoryChip;
impl MemoryChip for At28C256 {
/// read
///
/// Reads a byte from memory.
/// Returns a 0x00 if there is no data at that location but is still in ROM address range
fn read(&self, offset: &u16) -> u8 {
let effective = self.internal_address(*offset);
debug!("STARTING READ FROM At28C256 ${:04x} | ${:04x} / ${effective:04x} | ${:04x}", self.offset, offset, self.max_offset);
if effective >= self.data.len() as u16 { 0x00 } else { self.data[effective as usize] }
}
}
+31
View File
@@ -0,0 +1,31 @@
pub mod default;
pub mod rom_chip;
pub mod new;
pub mod program;
pub mod dump;
pub mod checksum;
pub mod blocks;
pub mod control;
pub mod signal_tick;
pub mod memory_chip;
pub mod bus_device;
/// At28C256
///
/// Represents a single At28C256 EEPROM Chip
///
/// 256kbit storage
/// 32kbyte storage
pub struct At28C256 {
// Logical parts
data_bus: u8,
address_bus: u16,
pub data: Box<[u8]>,
// where in the computer memory map do we live?
offset: u16,
max_offset: u16,
// Physical Parts
cs: bool,
we: bool,
oe: bool
}
+19
View File
@@ -0,0 +1,19 @@
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
impl At28C256 {
pub fn new(offset: u16, max_offset: u16, data: Vec<u8>) -> Self {
println!("NEW At28C256 with checksum ${:02x}", At28C256::checksum_static(&data[..]));
At28C256 {
data: data.into_boxed_slice(),
address_bus: 0x0000,
data_bus: 0x00,
offset,
max_offset,
cs: false,
oe: false,
we: false
}
}
}
+11
View File
@@ -0,0 +1,11 @@
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
impl At28C256 {
pub fn program(&mut self, new_program: Box<[u8]>) {
// panic!("FAIL. Cant program the chip.");
// println!("PROGRAMMING {:?}", new_program);
self.data = new_program;
}
}
+18
View File
@@ -0,0 +1,18 @@
use log::debug;
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
use crate::traits::bus_device::BusDevice;
use crate::traits::memory_chip::MemoryChip;
use crate::traits::rom_chip::RomChip;
impl RomChip for At28C256 {
/// program
///
/// Writes new data to the memory chip
fn program(new_data: &[u8]) -> Box<At28C256> {
println!("Writing new chip.");
let mut working = At28C256::default();
working.data = new_data.to_vec().into_boxed_slice();
Box::new(working)
}
}
+33
View File
@@ -0,0 +1,33 @@
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
const CHIP_SIZE: usize = SIZE_32KB ;
impl At28C256 {
/// Tick the ROM
/// address_bus
/// data_bus
/// CS -> Chip Select
/// OE -> Output Enable
/// WE -> Write Enable
pub fn signal_tick(&mut self, address_bus: u16, data_bus: u8, cs: bool, oe: bool, we: bool) -> (u8) {
// if we aren't selected and we aren't able to write to the bus...
if !cs || !we || !oe { return data_bus };
// if we aren't being addressed directly
if !(address_bus <= self.max_offset && address_bus >= self.offset) {
return data_bus
};
let internal_address = address_bus - self.offset;
let result = if internal_address < CHIP_SIZE as u16 {
self.data[internal_address as usize]
} else {
data_bus
};
println!("At28C256 EADDR: ${address_bus:04x} IADDR: ${internal_address:04x} = {result:02x}");
result
}
}

Some files were not shown because too many files have changed in this diff Show More