Skip to content

Commit

Permalink
Merge v0.01 m6502 implementation dev branch (#3)
Browse files Browse the repository at this point in the history
* Add 'ValueOnly' data type to AddressingModeOutput enum

The `Fetched` data type needs to store both the fetched value and the
address it has been fetched from. In order for this to happen
additional field is needed, so that the addressing modes which only
return value (e.g `Implied` and `Immediate`) can be used properly. This
field is called `ValueOnly`.

(All tests are updated to check the new return types and are
 successfully passing.)

* Add ADC, AND, ASL instructions

* Add branching instructions (BCC, BCS, BMI, BNE, BPL, BVC, BVS)

Each one of them is implemented using a helping function which wraps the
actual branching logic. The specific branch instruction checks its
predicate and if is correct a branch will be made.

* Add BRK, BIT and all "Clear * flag" instructions

* Add all "load" and "store" instructions

The LDA, LDX, LDY are considered "load" instructions and STA, STX, STY
are considered - "store".

* Add "increment" instructions

These include INX, INY, INC.

* Add compare instructions: CMP, CPY, CPX

* Add return instructions: RTI, RTS

* Add monitors for cpu internals and memory

* Demonstrate monitor usage with a fibonacci sequence program

* Finish implementation of the instructions associated with legal opcodes

Implementation to the following instructions has been included:

- the "decrement" instructions: **DEC**, **DEY**, **DEX**
- **EOR**, **ORA**
- **JMP**, **JSR**
- **LSR** + additional change to **ASL**
- the "push" instructions: **PHA**, **PHP**
- the "pull" instructions: **PLA**, **PLP**
- the "rotate" instructions: **ROL**, **ROR**
- **RTS**
- the "flag set" instructions: **SEI**, **SED**, **SEC**
- **NOP**
- the "substract" instruction: **SBC**
- the transfer instructions: **TAX**, **TAY**, **TSX**, **TXA**,
    **TXS**, **TYA**

An additional changes have been made to the following:
- **ASL** - Cleanup

To all instructions has been a brief description.

Some of the instructions have associated unit tests.

* Add gcd example
  • Loading branch information
boki1 authored Apr 18, 2021
1 parent bae8f5b commit 81203dc
Show file tree
Hide file tree
Showing 11 changed files with 2,732 additions and 336 deletions.
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/target
target/
Cargo.lock
.idea/
.vscode/

target
example/target/

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "nersy"
name = "m6502"
version = "0.1.0"
authors = ["boki1 <[email protected]>"]
edition = "2018"

[dependencies]
getset = "0.1.1"
getset = "0.1.1"
11 changes: 11 additions & 0 deletions examples/fib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "fibonacci"
version = "0.1.0"
authors = ["boki1 <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
m6502 = { path = "../../" }
olc_pixel_game_engine = "0.5.0"
Binary file added examples/fib/src/fib.bin
Binary file not shown.
198 changes: 198 additions & 0 deletions examples/fib/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
extern crate m6502;
extern crate olc_pixel_game_engine;

use crate::olc_pixel_game_engine as olc;
use m6502::mos6502::{Address, Asm, Cpu, CpuError};

enum FibonaciiExampleView {
CpuMonitor,
MemoryMonitor(Address),
}

struct FibonacciExample {
cpu: Cpu,
source_file: String,
disassembly: Asm,
pov: FibonaciiExampleView,
finished: bool,
continuing: bool,
}

impl FibonacciExample {
fn new(source_file: &str) -> Self {
Self {
cpu: Cpu::default(),
source_file: source_file.to_string(),
disassembly: Asm::default(),
pov: FibonaciiExampleView::CpuMonitor,
finished: false,
continuing: false,
}
}
}

impl olc::Application for FibonacciExample {
fn on_user_create(&mut self) -> Result<(), olc::Error> {
if let Err(CpuError::FailedLoadingProgram) =
self.cpu.load_file(&self.source_file, 0x8000, true)
{
panic!("Failed opening source file.");
}

self.disassembly = Asm::from_addr_range(&mut self.cpu, 0x8000, 32);
let regs = self.cpu.regset_mut();
regs.set_prog_counter(0x8000);
regs.set_accumulator(0xFF);
regs.set_x_index(0xEE);
regs.set_y_index(0xDD);

Ok(())
}

fn on_user_update(&mut self, _elapsed_time: f32) -> Result<(), olc::Error> {
olc::clear(olc::BLACK);

match self.pov {
FibonaciiExampleView::CpuMonitor => self.draw_cpu_monitor(),
FibonaciiExampleView::MemoryMonitor(beginning) => self.draw_mem(beginning),
};

self.finished = (self.cpu.pc() == 0x0000);

// Next instruction
if !self.finished {
if self.continuing || olc::get_key(olc::Key::SPACE).released {
self.cpu.full_instruction();
}

// Continue execution
if olc::get_key(olc::Key::E).released {
self.continuing = true;
}
}

// Reset cpu
if olc::get_key(olc::Key::R).released {
self.cpu.reset();
}

// Watch cpu
if olc::get_key(olc::Key::C).released {
self.pov = FibonaciiExampleView::CpuMonitor;
}

// Watch memory
if olc::get_key(olc::Key::M).released {
self.pov = FibonaciiExampleView::MemoryMonitor(0xE0);
}

Ok(())
}

fn on_user_destroy(&mut self) -> Result<(), olc::Error> {
Ok(())
}
}

impl FibonacciExample {
fn draw_cpu_monitor(&mut self) -> Result<(), olc::Error> {
let pc = self.cpu.pc();

// Draw all instructions
let mut this_y = 0;
for (idx, instr) in self.disassembly.code().iter().enumerate() {
let y = 10 + (idx as i32 * 10);
olc::draw_string(20, y, &format!("{}", instr), olc::WHITE)?;
if instr.load_address() == pc {
this_y = y + 2;
}
}

// Draw marking of current instruction
if this_y > 0 {
olc::fill_circle(3, this_y, 2, olc::GREEN);
}

// Separator
olc::draw_line(0, 190, 260, 190, olc::WHITE);

// Monitor
let regs = self.cpu.regset();
olc::draw_string(
20,
200,
&format!(
"A = {:#4X?} SP = {:#4X?}",
regs.accumulator(),
regs.stk_ptr()
),
olc::WHITE,
)?;
olc::draw_string(
20,
210,
&format!(
"X = {:#04X?} PC = {:#06X?}",
regs.x_index(),
regs.prog_counter()
),
olc::WHITE,
)?;
olc::draw_string(
20,
220,
&format!("Y = {:#04X?} PS = {:#04X?}", regs.y_index(), regs.status()),
olc::WHITE,
)?;

// Separator
let col = if self.finished {
olc::GREEN
} else {
olc::DARK_CYAN
};
olc::draw_line(0, 240, 260, 240, col);
olc::draw_string(66, 245, &format!("CPU Monitor"), col);

Ok(())
}

fn draw_mem(&mut self, begin_address: Address) -> Result<(), olc::Error> {
let mut x = 60;
let mut y = 10;

olc::draw_string(0, 10, &format!("{:#06X?}: ", begin_address), olc::YELLOW)?;
let mut address = begin_address;
let end_address = begin_address + 160;
while address < end_address {
if address % 8 == 0 && address != begin_address {
y += 10;
x = 60;
olc::draw_string(0, y, &format!("{:#06X?}: ", address), olc::YELLOW)?;
}

let data = self.cpu.read_word(address);
olc::draw_string(x, y, &format!("{:#06x?}", data.to_be()), olc::WHITE)?;
x += 50;

address += 2;
}

// Separator
let col = if self.finished {
olc::GREEN
} else {
olc::YELLOW
};
olc::draw_line(0, 240, 260, 240, col);
olc::draw_string(66, 245, &format!("Mem Monitor"), col);

Ok(())
}
}

fn main() {
let mut fib_program = FibonacciExample::new("src/fib.bin");

olc::start("Fibonacci example", &mut fib_program, 400, 260, 2, 2).unwrap();
}
11 changes: 11 additions & 0 deletions examples/gcd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "gcd"
version = "0.1.0"
authors = ["boki1 <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
m6502 = { path = "../../" }
olc_pixel_game_engine = "0.5.0"
45 changes: 45 additions & 0 deletions examples/gcd/src/gcd.a65
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
; ------------------------------------------------------------------
; A short program to perform the euclidean algorithm on 2 numbers |
; |
; 17-04-2021 |
; ------------------------------------------------------------------
; Note: The program is compiled using (this)[https://www.asm80.com/index.html] online tool
;
; \brief The routine calculates the greatest common divider between 2 numbers
;
; \param The first number is stored at address $A
; \param The second number is stored at address $B
;
; \ret The result of the routine is stored in the accumulator
;

main:
lda #$c
sta $A ; *($A) = 12
lda #$12
sta $B ; *($B) = 18

gcd:
lda $A
cmp $B
beq end
bcs dec_a
bmi dec_b

dec_a:
lda $A
sec
sbc $B
sta $A
jmp gcd

dec_b:
lda $B
sec
sbc $A
sta $B
jmp gcd

end:
lda $A
rts
Binary file added examples/gcd/src/gcd.bin
Binary file not shown.
Loading

0 comments on commit 81203dc

Please sign in to comment.