From 8d506773bfa87c5412f4e08c37356e2062e7e570 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 19 Jun 2023 12:27:35 +0200 Subject: [PATCH] Add assembly example code This should make it easier for beginners to understand how to test this emulator. --- README.md | 51 ++++++++++++++++++ examples/asm/euclid/euclid.a65 | 33 ++++++++++++ examples/asm/euclid/euclid.bin | Bin 0 -> 34 bytes examples/asm/linker.cfg | 11 ++++ examples/euclid.rs | 36 +++++++++++++ .../{euclidean_algo.rs => euclid_bytes.rs} | 2 +- 6 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 examples/asm/euclid/euclid.a65 create mode 100644 examples/asm/euclid/euclid.bin create mode 100644 examples/asm/linker.cfg create mode 100644 examples/euclid.rs rename examples/{euclidean_algo.rs => euclid_bytes.rs} (93%) diff --git a/README.md b/README.md index 0f7f207..e86e3b8 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,57 @@ fn main() { } ``` +The same can be achieved, by compiling the euclid example yourself. + +First install a 6502 assembler and linker, e.g. [cc65](https://cc65.github.io/cc65/). + +```sh +brew install cc65 +``` + +Then compile and link the assembly file: + +```sh +cd examples/asm/euclid +ca65 euclid.asm +ld65 -C ../linker.cfg -o euclid.bin euclid.o +``` + +This will create a binary file `euclid.bin` that you can load into the emulator: + +```rust +use mos6502::memory::Bus; +use mos6502::memory::Memory; +use mos6502::cpu; +use std::fs::read; + +fn main() { + // Calculate the greatest common divisor of 56 and 49 + // using Euclid's algorithm. + let zero_page_data = [56, 49]; + + // Load the binary file from disk + let program = match read("examples/asm/euclid/euclid.bin") { + Ok(data) => data, + Err(err) => { + println!("Error reading euclid.bin: {}", err); + return; + } + }; + + let mut cpu = cpu::CPU::new(Memory::new()); + + cpu.memory.set_bytes(0x00, &zero_page_data); + cpu.memory.set_bytes(0x10, &program); + cpu.registers.program_counter = 0x10; + + cpu.run(); + + // The expected GCD is 7 + assert_eq!(7, cpu.registers.accumulator); +} +``` + ## Credits This started off as a fork of [amw-zero/6502-rs](https://github.com/amw-zero/6502-rs), diff --git a/examples/asm/euclid/euclid.a65 b/examples/asm/euclid/euclid.a65 new file mode 100644 index 0000000..150847c --- /dev/null +++ b/examples/asm/euclid/euclid.a65 @@ -0,0 +1,33 @@ +; euclid.a65 +; A program to find the greatest common divisor of two numbers + +.ORG $1000 + +; .algo +LDA $00 ; Load from F to A +; .algo_ +sec ; Set carry flag +SBC $01 ; Subtract S from the number in A (from F) +BEQ end ; Jump to .end if the difference is zero +BMI swap ; Jump to .swap if the difference is negative +STA $00 ; Load A to F +JMP algo_ ; Jump to .algo_ + +; .end +end: +LDA $00 ; Load from F to A +BRK ; Break (end program) + +; .swap +swap: +LDX $00 ; Load F to X +LDY $01 ; Load S to Y +STX $01 ; Store X to S +STY $00 ; Store Y to F +JMP algo ; Jump to .algo + +algo: +JMP algo ; Infinite loop to prevent program from ending + +algo_: +JMP algo_ ; Infinite loop to prevent program from ending diff --git a/examples/asm/euclid/euclid.bin b/examples/asm/euclid/euclid.bin new file mode 100644 index 0000000000000000000000000000000000000000..0c957020e5fe177cf5965ac0de3daf7535b8709d GIT binary patch literal 34 ncmZ3=VDXgk1G@o7D}#@`z)}W=WeiIg+ZbCId}IVbNL~N{s9*?L literal 0 HcmV?d00001 diff --git a/examples/asm/linker.cfg b/examples/asm/linker.cfg new file mode 100644 index 0000000..0a82281 --- /dev/null +++ b/examples/asm/linker.cfg @@ -0,0 +1,11 @@ +MEMORY { + ZP: start = $0000, size = $0100, type = rw, define = yes; + RAM: start = $0100, size = $0200, type = rw, define = yes; + ROM: start = $8000, size = $8000, type = ro; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + DATA: load = RAM, type = rw; + CODE: load = ROM, type = ro; +} diff --git a/examples/euclid.rs b/examples/euclid.rs new file mode 100644 index 0000000..97a301e --- /dev/null +++ b/examples/euclid.rs @@ -0,0 +1,36 @@ +use mos6502::cpu; +use mos6502::memory::Bus; +use mos6502::memory::Memory; +use std::fs::read; + +fn main() { + println!("Enter two numbers (< 128) separated by a space to know their GCD."); + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + + let zero_page_data = input + .split_whitespace() + .map(|s| s.parse::().unwrap()) + .collect::>(); + + // Load the binary file from disk + let program = match read("examples/asm/euclid/euclid.bin") { + Ok(data) => data, + Err(err) => { + println!("Error reading euclid.bin: {}", err); + // print all files in the current directory + println!("Files in current directory:"); + return; + } + }; + + let mut cpu = cpu::CPU::new(Memory::new()); + + cpu.memory.set_bytes(0x00, &zero_page_data); + cpu.memory.set_bytes(0x10, &program); + cpu.registers.program_counter = 0x10; + + cpu.run(); + + println!("GCD is {}", cpu.registers.accumulator); +} diff --git a/examples/euclidean_algo.rs b/examples/euclid_bytes.rs similarity index 93% rename from examples/euclidean_algo.rs rename to examples/euclid_bytes.rs index 04fc087..d261fa0 100644 --- a/examples/euclidean_algo.rs +++ b/examples/euclid_bytes.rs @@ -5,7 +5,7 @@ use mos6502::memory::Bus; use mos6502::memory::Memory; fn main() { - println!("Enter two numbers (< 128) to know their GCD:"); + println!("Enter two numbers (< 128) separated by a space to know their GCD."); let mut input = String::new(); std::io::stdin().read_line(&mut input).unwrap();