Got first CPU instructions working

+ added interface
+ added CPU, Bus, and memory implementations
+ implemented a few CPU instructions
This commit is contained in:
Steins7 2022-02-15 22:24:58 +01:00
parent 8cc0019dca
commit 012992c4ad
10 changed files with 80860 additions and 3 deletions

1415
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,16 @@
[package] [package]
name = "nes_emulator" name = "nes_emulator"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
log = "0.4.14"
chrono = "0.4.19"
fern = "0.6.0"
winit = "0.24.0"
mini_gl_fb = "0.9.0"
bitflags = "1.3.2"
rusttype = "0.9.2"

BIN
fonts/DejaVuSansMono.ttf Normal file

Binary file not shown.

78380
output.log Normal file

File diff suppressed because one or more lines are too long

106
src/bus.rs Normal file
View File

@ -0,0 +1,106 @@
use std::fmt;
use crate::memory::{Memory, Ram};
pub struct Bus {
ram: Ram<0x800>,
ppu: Ram<0x8>,
apu: Ram<0x1F>,
cartridge: Ram<0xBFE0>,
}
impl fmt::Debug for Bus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Bus")
.field("ram", &self.ram)
.finish_non_exhaustive()
}
}
impl Memory for Bus {
fn read_addr(&self, addr: u16) -> u8 {
match addr {
0x0000..=0x1FFF => self.ram.read_addr(addr % 0x0800), //RAM is mirrored 3 times
0x2000..=0x3FFF => self.ppu.read_addr(addr % 0x8), //PPU is mirrored every 8 bytes
0x4000..=0x401F => self.apu.read_addr(addr),
0x4020..=0xFFFF => self.cartridge.read_addr(addr),
}
}
fn write_addr(&mut self, addr: u16, data: u8) {
match addr {
0x0000..=0x1FFF => self.ram.write_addr(addr % 0x0800, data),
0x2000..=0x3FFF => self.ppu.write_addr(addr % 0x8, data),
0x4000..=0x401F => self.apu.write_addr(addr, data),
0x4020..=0xFFFF => self.cartridge.write_addr(addr, data),
};
}
}
impl Bus {
pub fn new() -> Self {
let mut ram = Ram::<0x800>::new();
//TODO temporary for instruction tests
ram.buffer[0x01] = 0x0A;
ram.buffer[0x02] = 0x90;;
ram.buffer[0x03] = (-3 as i8) as u8;
ram.buffer[0x04] = 0x90;
ram.buffer[0x05] = (-3 as i8) as u8;
ram.buffer[0x06] = 0x0A;
ram.buffer[0x07] = 0x0A;
ram.buffer[0x08] = 0x0A;
ram.buffer[0x09] = 0x01;
ram.buffer[0x0A] = 0x3D;
ram.buffer[0x0B] = 0xFE;
ram.buffer[0x0C] = 0x01;
ram.buffer[0x0D] = 0x39;
ram.buffer[0x0E] = 0xFD;
ram.buffer[0x0F] = 0x01;
ram.buffer[0x10] = 0x21;
ram.buffer[0x11] = 0x9F;
ram.buffer[0x12] = 0x31;
ram.buffer[0x13] = 0xB0;
// boundary
ram.buffer[0x14] = 0x3D;
ram.buffer[0x15] = 0xFF;
ram.buffer[0x16] = 0x02;
ram.buffer[0x17] = 0x39;
ram.buffer[0x18] = 0xFE;
ram.buffer[0x19] = 0x02;
ram.buffer[0x1A] = 0x31;
ram.buffer[0x1B] = 0xB2;
// ptr to val
ram.buffer[0xA0] = 0xFF;
ram.buffer[0xA1] = 0x01;
// ptr to val as table
ram.buffer[0xB0] = 0xFD;
ram.buffer[0xB1] = 0x01;
ram.buffer[0xB2] = 0xFE; //boundary
ram.buffer[0xB3] = 0x02;
// zero page val
ram.buffer[0xFF] = 0x01;
// val
ram.buffer[0x01FF] = 0x01;
ram.buffer[0x0300] = 0x01; //boundary
Bus {
ram,
ppu: Ram::<0x8>::new(),
apu: Ram::<0x1F>::new(),
cartridge: Ram::<0xBFE0>::new(),
}
}
}

0
src/cpu.precomp.rs Normal file
View File

639
src/cpu.rs Normal file
View File

@ -0,0 +1,639 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use bitflags::bitflags;
use crate::{
bus::Bus,
memory::Memory,
};
//--------------------------------------------------------------------------------------------------
trait Input {
fn get_cycles(&self) -> u32;
}
trait RInput: Input {
fn read(&self, acc: &u8, bus: &Bus) -> u8;
}
trait WInput: Input {
fn write(&mut self, acc: &mut u8, bus: &mut Bus, data: u8);
}
trait RWInput: RInput + WInput {}
struct Accumulator {}
impl Input for Accumulator {
fn get_cycles(&self) -> u32 { 2 }
}
impl RInput for Accumulator {
fn read(&self, acc: &u8, _bus: &Bus) -> u8 { *acc }
}
impl WInput for Accumulator {
fn write(&mut self, acc: &mut u8, _bus: &mut Bus, data: u8) { *acc = data }
}
impl RWInput for Accumulator {}
struct Data {
data: u8,
}
impl Input for Data {
fn get_cycles(&self) -> u32 { 2 }
}
impl RInput for Data {
fn read(&self, _acc: &u8, _bus: &Bus) -> u8 { self.data }
}
struct MemoryVal {
addr: u16,
cycles: u32,
}
impl Input for MemoryVal {
fn get_cycles(&self) -> u32 { self.cycles }
}
impl RInput for MemoryVal {
fn read(&self, _acc: &u8, bus: &Bus) -> u8 {
bus.read_addr(self.addr)
}
}
impl WInput for MemoryVal {
fn write(&mut self, _acc: &mut u8, bus: &mut Bus, data: u8) {
bus.write_addr(self.addr, data);
}
}
impl RWInput for MemoryVal {}
struct MemoryValExtra {
addr: u16,
cycles: u32,
extra_cycle: bool,
}
impl Input for MemoryValExtra {
fn get_cycles(&self) -> u32 {
match self.extra_cycle {
false => self.cycles,
true => self.cycles + 1,
}
}
}
impl RInput for MemoryValExtra {
fn read(&self, _acc: &u8, bus: &Bus) -> u8 {
bus.read_addr(self.addr)
}
}
impl WInput for MemoryValExtra {
fn write(&mut self, _acc: &mut u8, bus: &mut Bus, data: u8) {
bus.write_addr(self.addr, data);
self.extra_cycle = true;
}
}
impl RWInput for MemoryValExtra {}
macro_rules! parse_opcode {
($cpu:expr, $opcode:expr, $($code:expr => $inst:ident ($mode:ident)),* $(,)?) => (
match $opcode {
$(
$code => {
let input = $cpu.$mode();
$cpu.$inst(input)
}
)*
_ => panic!("unimplemented opcode: 0x{:x}", $opcode),
}
)
}
//--------------------------------------------------------------------------------------------------
bitflags! {
struct StatusReg: u8 {
const C = 0x1 << 0;
const Z = 0x1 << 1;
const I = 0x1 << 2;
const D = 0x1 << 3;
const B = 0x1 << 4;
const U = 0x1 << 5;
const V = 0x1 << 6;
const N = 0x1 << 7;
}
}
#[derive(Debug)]
pub struct Cpu {
a: u8,
x: u8,
y: u8,
pc: u16,
s: u8,
p: StatusReg,
bus: Bus,
}
impl Cpu {
pub fn new() -> Self {
Cpu {
a: 1,
x: 1, //TODO temporary
y: 2, //TODO temporary
pc: 0, //TODO to be verified
s: 0x34,
p: StatusReg::from_bits(0xFD).unwrap(),
bus: Bus::new(),
}
}
pub fn tick(&mut self) {
self.execute();
}
fn execute(&mut self) {
let opcode = self.read_pc_addr();
let cycles = parse_opcode!(self, opcode,
0x69 => adc(mode_immediate),
0x65 => adc(mode_zero_page),
0x75 => adc(mode_zero_page_x),
0x6D => adc(mode_absolute),
0x7D => adc(mode_absolute_x),
0x79 => adc(mode_absolute_y),
0x61 => adc(mode_indirect_x),
0x71 => adc(mode_indirect_y),
0x29 => and(mode_immediate),
0x25 => and(mode_zero_page),
0x35 => and(mode_zero_page_x),
0x2D => and(mode_absolute),
0x3D => and(mode_absolute_x),
0x39 => and(mode_absolute_y),
0x21 => and(mode_indirect_x),
0x31 => and(mode_indirect_y),
0x0A => asl(mode_accumulator),
0x06 => asl(mode_zero_page),
0x16 => asl(mode_zero_page_x),
0x0E => asl(mode_absolute),
0x1E => asl(mode_absolute_x),
0x90 => bcc(mode_relative),
0xB0 => bcs(mode_relative),
0xF0 => beq(mode_relative),
0x24 => bit(mode_zero_page),
0x2C => bit(mode_absolute),
0x30 => bmi(mode_relative),
0xD0 => bne(mode_relative),
0x10 => bpl(mode_relative),
0x00 => brk(mode_implicit),
0x50 => bvc(mode_relative),
0x70 => bvs(mode_relative),
0x18 => clc(mode_implicit),
0xD8 => cld(mode_implicit),
0x58 => cli(mode_implicit),
0xB8 => clv(mode_implicit),
0xC9 => cmp(mode_immediate),
0xC5 => cmp(mode_zero_page),
0xD5 => cmp(mode_zero_page_x),
0xCD => cmp(mode_absolute),
0xDD => cmp(mode_absolute_x),
0xD9 => cmp(mode_absolute_y),
0xC1 => cmp(mode_indirect_x),
0xD1 => cmp(mode_indirect_y),
0xE0 => cpx(mode_immediate),
0xE4 => cpx(mode_zero_page),
0xEC => cpx(mode_absolute),
0xC0 => cpy(mode_immediate),
0xC4 => cpy(mode_zero_page),
0xCC => cpy(mode_absolute),
0xC6 => dec(mode_zero_page),
0xD6 => dec(mode_zero_page_x),
0xCE => dec(mode_absolute),
0xDE => dec(mode_absolute_x),
0xCA => dex(mode_implicit),
0x88 => dey(mode_implicit),
);
info!("0x{:X}: {} cycles", opcode, cycles);
}
/// Implicit mode
/// instructions that use implied mode don't need inputs
fn mode_implicit(&self) -> () {}
/// Immediate mode "#v":
/// use 8bit operand as input
fn mode_immediate(&mut self) -> Data {
Data {
data: self.read_pc_addr(),
}
}
/// Immediate mode "#v":
/// use 8bit operand as signed offset for PC
fn mode_relative(&mut self) -> Data {
self.mode_immediate()
}
/// Zero-page mode "d":
/// Fetch value from the zero page (8bit adresses)
/// val = PEEK(arg & 0xFF)
fn mode_zero_page(&mut self) -> MemoryVal {
MemoryVal {
addr: self.read_pc_addr() as u16,
cycles: 3,
}
}
/// Zero-page indexed mode "d,x":
/// Fetch value from the zero page (8bit adresses), indexed on X register
/// val = PEEK((arg + X) & 0xFF)
fn mode_zero_page_x(&mut self) -> MemoryVal {
MemoryVal {
addr: (self.read_pc_addr() + self.x) as u16 & 0x00FF,
cycles: 4,
}
}
/// Zero-page indexed mode "d,y":
/// Fetch value from the zero page (8bit adresses), indexed on Y register
/// val = PEEK((arg + Y) & 0xFF)
fn mode_zero_page_y(&mut self) -> MemoryVal {
MemoryVal {
addr: (self.read_pc_addr() + self.y) as u16 & 0x00FF,
cycles: 4,
}
}
/// Absolute mode "a"
/// Fetch value from anywhere in memory
/// val = PEEK(arg)
fn mode_absolute(&mut self) -> MemoryVal {
// compute full address
let addr_lo = self.read_pc_addr() as u16;
let addr_hi = self.read_pc_addr() as u16;
let addr = addr_lo | (addr_hi << 8);
MemoryVal {
addr,
cycles: 4,
}
}
/// Absolute mode "a.x"
/// Fetch value from anywhere in memory, indexed on X register
/// val = PEEK(arg + X)
fn mode_absolute_x(&mut self) -> MemoryValExtra {
// compute full address
let addr_lo = self.read_pc_addr() as u16;
let addr_hi = self.read_pc_addr() as u16;
let mut addr = addr_lo | (addr_hi << 8);
// compute index
let extra_cycle = (addr_lo + (self.x as u16)) & 0xFF00 != 0;
addr += self.x as u16;
MemoryValExtra {
addr,
cycles: 4,
extra_cycle,
}
}
/// Absolute mode "a.Y"
/// Fetch value from anywhere in memory, indexed on Y register
/// val = PEEK(arg + Y)
fn mode_absolute_y(&mut self) -> MemoryValExtra {
// compute full address
let addr_lo = self.read_pc_addr() as u16;
let addr_hi = self.read_pc_addr() as u16;
let mut addr = addr_lo | (addr_hi << 8);
// compute index
let extra_cycle = (addr_lo + (self.y as u16)) & 0xFF00 != 0;
addr += self.y as u16;
MemoryValExtra {
addr,
cycles: 4,
extra_cycle,
}
}
/// Indirect mode "(a)"
/// Fetch 16bit address from anywhere in memory, only used by JMP. This instruction behaves
/// uncorrectly on page boundary on the original 6502 (emulated here)
/// addr = PEEK(arg) + PEEK(arg + 1) << 8
fn mode_indirect(&mut self) -> MemoryVal {
// compute full address
let addr_ind_lo = self.read_pc_addr() as u16;
let addr_ind_hi = self.read_pc_addr() as u16;
let addr_ind = addr_ind_lo | (addr_ind_hi << 8);
// fetch indirect addr to jump to
let addr_lo = self.bus.read_addr(addr_ind) as u16;
//note: bug of the 6502, the carry isn't applied when incrementing the indirect address
let addr_hi = self.bus.read_addr(((addr_ind + 1) & 0xFF) | (addr_ind_hi << 8)) as u16;
let addr = addr_lo | (addr_hi << 8);
MemoryVal {
addr,
cycles: 6, //only used by JMP that uses cycles - 1
}
}
/// Indirect mode "(d.x)"
/// Fetch value from anywhere in memory using an address table built by the user and stored in
/// zero page, indexed on X
/// val = PEEK(PEEK((arg + X) & 0xFF) + PEEK((arg + X + 1) & 0xFF) << 8)
fn mode_indirect_x(&mut self) -> MemoryVal {
let addr_ind = self.read_pc_addr() as u16 + self.x as u16;
// fetch indirect addr to jump to
let addr_lo = self.bus.read_addr(addr_ind & 0xFF) as u16;
let addr_hi = self.bus.read_addr((addr_ind + 1) & 0xFF) as u16;
let addr = addr_lo | (addr_hi << 8);
MemoryVal {
addr,
cycles: 6,
}
}
/// Indirect mode "(d).y"
/// Fetch value from anywhere in memory using a stored address, indexed on Y
/// val = PEEK(PEEK(arg) + PEEK((arg + 1) & 0xFF) << 8 + Y)
fn mode_indirect_y(&mut self) -> MemoryValExtra {
let addr_ind = self.read_pc_addr() as u16;
// fetch indirect addr to jump to
let addr_lo = self.bus.read_addr(addr_ind) as u16;
let addr_hi = self.bus.read_addr((addr_ind + 1) & 0xFF) as u16;
let mut addr = addr_lo | (addr_hi << 8) ;
// compute index
let extra_cycle = (addr_lo + (self.y as u16)) & 0xFF00 != 0;
addr += self.y as u16;
MemoryValExtra {
addr,
cycles: 5,
extra_cycle,
}
}
/// Accumulator mode "A"
/// Directly use the accumulator as input and/or output
fn mode_accumulator(&self) -> Accumulator {
Accumulator {}
}
/// Increment PC, then read corresponding value in memory
fn read_pc_addr(&mut self) -> u8 {
self.pc += 1;
let val = self.bus.read_addr(self.pc);
val
}
fn adc<I: RInput>(&mut self, input: I) -> u32 {
let val = input.read(&self.a, &self.bus);
// add acc and value
let prev_a = self.a as u16;
let mut a = self.a as u16;
if self.p.contains(StatusReg::C) {
a = a.wrapping_add(1);
}
a = a.wrapping_add((val & 0xFF) as u16);
// handle status bits
self.p.set(StatusReg::C, a & 0xFF00 != 0);
self.p.set(StatusReg::Z, a & 0xFF == 0);
if ((prev_a & 0x80) !=0 && (val & 0x80) != 0 && (a & 0x80) == 0)
|| ((prev_a & 0x80) == 0 && (val & 0x80) == 0 && (a & 0x80) != 0) {
self.p.set(StatusReg::V, true);
} else {
self.p.set(StatusReg::V, false);
}
self.p.set(StatusReg::N, a & 0x80 != 0);
// truncate accumulator to 8 bits
self.a = a as u8;
// handle zero bit
input.get_cycles()
}
fn and<I: RInput>(&mut self, input: I) -> u32 {
let val = input.read(&self.a, &self.bus);
// logical AND between acc and value
self.a &= val;
// handle status bits
self.p.set(StatusReg::Z, self.a == 0);
self.p.set(StatusReg::N, self.a & 0x80 != 0);
input.get_cycles()
}
fn asl<I: RWInput>(&mut self, mut input: I) -> u32 {
let mut target = input.read(&self.a, &self.bus) as u16;
// shift target one bit left
target = target << 1;
// handle status bits
self.p.set(StatusReg::C, target & 0xFF00 != 0);
self.p.set(StatusReg::Z, target & 0xFF == 0);
self.p.set(StatusReg::N, target & 0x80 != 0);
// store result
input.write(&mut self.a, &mut self.bus, target as u8);
input.get_cycles() + 2
}
fn branch_if<I: RInput>(&mut self, cond: bool, input: I) -> u32 {
let offset = input.read(&self.a, &self.bus) as i8;
match cond {
false => input.get_cycles(),
true => {
let old_pc = self.pc;
self.pc = self.pc.wrapping_add(offset as u16);
debug!("PC: 0x{:X}", self.pc);
match old_pc & 0xFF00 == self.pc & 0xFF00 {
true => input.get_cycles() + 1,
false => input.get_cycles() + 2,
}
}
}
}
fn bcc<I: RInput>(&mut self, input: I) -> u32 {
self.branch_if(!self.p.contains(StatusReg::C), input)
}
fn bcs<I: RInput>(&mut self, input: I) -> u32 {
self.branch_if(self.p.contains(StatusReg::C), input)
}
fn beq<I: RInput>(&mut self, input: I) -> u32 {
self.branch_if(self.p.contains(StatusReg::Z), input)
}
fn bit<I: RInput>(&mut self, input: I) -> u32 {
let val = input.read(&self.a, &self.bus);
// logical AND between acc and value
let and = self.a & val;
// handle status bits
self.p.set(StatusReg::Z, and == 0);
self.p.set(StatusReg::V, (val & 0x1 << 6) != 0);
self.p.set(StatusReg::N, (val & 0x1 << 7) != 0);
input.get_cycles()
}
fn bmi<I: RInput>(&mut self, input: I) -> u32 {
self.branch_if(self.p.contains(StatusReg::N), input)
}
fn bne<I: RInput>(&mut self, input: I) -> u32 {
self.branch_if(!self.p.contains(StatusReg::Z), input)
}
fn bpl<I: RInput>(&mut self, input: I) -> u32 {
self.branch_if(!self.p.contains(StatusReg::N), input)
}
fn brk(&mut self, _: ()) -> u32 {
//TODO
panic!("BRK not implemented");
}
fn bvc<I: RInput>(&mut self, input: I) -> u32 {
self.branch_if(!self.p.contains(StatusReg::V), input)
}
fn bvs<I: RInput>(&mut self, input: I) -> u32 {
self.branch_if(self.p.contains(StatusReg::V), input)
}
fn clc(&mut self, _: ()) -> u32 {
self.p.set(StatusReg::C, false);
2
}
fn cld(&mut self, _: ()) -> u32 {
self.p.set(StatusReg::D, false);
2
}
fn cli(&mut self, _: ()) -> u32 {
self.p.set(StatusReg::I, false);
2
}
fn clv(&mut self, _: ()) -> u32 {
self.p.set(StatusReg::V, false);
2
}
fn compute_cmp<I: RInput>(&mut self, reg: u8, input: I) -> u32 {
let val = input.read(&self.a, &self.bus) as u8;
self.p.set(StatusReg::C, reg >= val);
self.p.set(StatusReg::Z, reg == val);
self.p.set(StatusReg::N, ((reg - val) & (0x1 << 7)) != 0);
input.get_cycles()
}
fn cmp<I: RInput>(&mut self, input: I) -> u32 {
self.compute_cmp(self.a, input)
}
fn cpx<I: RInput>(&mut self, input: I) -> u32 {
self.compute_cmp(self.x, input)
}
fn cpy<I: RInput>(&mut self, input: I) -> u32 {
self.compute_cmp(self.y, input)
}
fn dec<I: RWInput>(&mut self, mut input: I) -> u32 {
let mut val = input.read(&self.a, &self.bus);
// compute
val = val.wrapping_sub(1);
self.p.set(StatusReg::Z, val == 0);
self.p.set(StatusReg::N, (val & (0x1 << 7)) != 0);
// store result
input.write(&mut self.a, &mut self.bus, val);
input.get_cycles() + 2
}
fn dex(&mut self, _: ()) -> u32 {
// compute
self.x = self.x.wrapping_sub(1);
self.p.set(StatusReg::Z, self.x == 0);
self.p.set(StatusReg::N, (self.x & (0x1 << 7)) != 0);
2
}
fn dey(&mut self, _: ()) -> u32 {
// compute
self.y = self.y.wrapping_sub(1);
self.p.set(StatusReg::Z, self.y == 0);
self.p.set(StatusReg::N, (self.y & (0x1 << 7)) != 0);
2
}
}

View File

@ -1,3 +1,89 @@
fn main() { #[allow(unused_imports)]
println!("Hello, world!"); use log::{debug, error, info, trace, warn};
mod cpu;
use cpu::Cpu;
mod bus;
mod memory;
mod utils;
fn main() -> Result<(), &'static str> {
use utils::{Pixel, PixelBuffer, FontRenderer};
use winit::{
event_loop::EventLoop,
dpi::LogicalSize,
};
use mini_gl_fb::config;
// setup
setup_logger().map_err(|_| "Failed to setup logger")?;
let mut event_loop = EventLoop::new();
let config = config! {
window_title: "Test".to_string(),
window_size: LogicalSize::new(800.0, 600.0),
invert_y: false,
};
let mut fb = mini_gl_fb::get_fancy(config, &event_loop);
let mut buffer = PixelBuffer::new(800, 600);
let mut renderer = FontRenderer::new(20.0, [0u8, 0, 0]);
let vec: Vec<[u8; 4]> = (&buffer).into();
fb.update_buffer(&vec);
let mut cpu = Cpu::new();
// event loop
fb.glutin_handle_basic_input(&mut event_loop, |fb, input| {
use winit::event::VirtualKeyCode;
use std::fmt::Write;
// wait for input before updating
input.wait = true;
// close if escape is pressed
if input.key_pressed(VirtualKeyCode::Escape) {
return false
}
// execute next cpu instruction
if input.key_pressed(VirtualKeyCode::S) {
cpu.tick();
let mut debug_str = String::new();
write!(debug_str, "{:#?}", cpu).unwrap();
buffer.fill([20, 20], [760, 560], Pixel::rgba(255, 255, 255, 255));
renderer.draw(&mut buffer, &debug_str, [20, 20], [760, 560]);
}
let vec: Vec<[u8; 4]> = (&buffer).into();
fb.update_buffer(&vec);
true
});
Ok(())
} }
fn setup_logger() -> Result<(), fern::InitError> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}] {}",
chrono::Local::now().format("[%H:%M:%S]"),
record.target(),
//record.file(),
record.level(),
message
))
})
.level(log::LevelFilter::Debug)
.chain(std::io::stdout())
.chain(fern::log_file("output.log")?)
.apply()?;
Ok(())
}

54
src/memory.rs Normal file
View File

@ -0,0 +1,54 @@
use std::fmt;
//--------------------------------------------------------------------------------------------------
pub trait Memory {
fn read_addr(&self, addr: u16) -> u8;
fn write_addr(&mut self, addr: u16, data: u8);
}
//--------------------------------------------------------------------------------------------------
pub struct Ram<const SIZE: usize> {
pub buffer: [u8; SIZE],
}
impl<const SIZE: usize> fmt::Debug for Ram<SIZE> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.buffer.iter().enumerate()
.try_for_each(|(i, elem)| {
if i <= 0xFF {
if i%0x10 == 0 {
f.write_fmt(format_args!("\n {:0>4X}", i))?;
}
f.write_fmt(format_args!(" {:0>2X}", elem))
} else {
Ok(())
}
})?;
Ok(())
}
}
impl<const SIZE: usize> Memory for Ram<SIZE> {
fn read_addr(&self, addr: u16) -> u8 {
self.buffer[addr as usize]
}
fn write_addr(&mut self, addr: u16, data: u8) {
self.buffer[addr as usize] = data;
}
}
impl<const SIZE: usize> Ram<SIZE> {
pub fn new() -> Self {
Ram {
buffer: [0u8; SIZE],
}
}
}

169
src/utils.rs Normal file
View File

@ -0,0 +1,169 @@
use rusttype::{point, Font, Scale, VMetrics};
//--------------------------------------------------------------------------------------------------
/* Pixel struct */
#[derive(Clone, Copy)]
pub struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
impl Pixel {
pub fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Pixel {r, g, b, a, }
}
}
impl std::ops::Add for Pixel {
type Output = Self;
fn add(self, other: Self) -> Self {
let alpha = other.a as f32 / 255.0;
let inv_alpha = 1.0 - alpha;
Pixel {
r: ((self.r as f32)*inv_alpha + (other.r as f32)*alpha) as u8,
g: ((self.g as f32)*inv_alpha + (other.g as f32)*alpha) as u8,
b: ((self.b as f32)*inv_alpha + (other.b as f32)*alpha) as u8,
a: 255,
}
}
}
impl std::ops::AddAssign for Pixel {
fn add_assign(&mut self, other: Self) {
let alpha = other.a as f32 / 255.0;
let inv_alpha = 1.0 - alpha;
self.r = ((self.r as f32)*inv_alpha + (other.r as f32)*alpha) as u8;
self.g = ((self.g as f32)*inv_alpha + (other.g as f32)*alpha) as u8;
self.b = ((self.b as f32)*inv_alpha + (other.b as f32)*alpha) as u8;
}
}
impl From<&Pixel> for [u8; 4] {
fn from(pixel: &Pixel) -> Self {
[pixel.r, pixel.g, pixel.b, pixel.a]
}
}
//--------------------------------------------------------------------------------------------------
/* PixelBuffer struct */
pub struct PixelBuffer {
buffer: Vec<Pixel>,
h: usize,
w: usize,
}
impl PixelBuffer {
pub fn new(h: usize, w: usize) -> Self {
PixelBuffer {
buffer: vec![Pixel::rgba(255u8, 255u8, 255u8, 255); h * w],
h,
w,
}
}
pub fn put_pixel(&mut self, x: usize, y: usize, pixel: Pixel) {
self.buffer[x + y*self.h] += pixel
}
pub fn fill(&mut self, origin: [usize; 2], size: [usize; 2], pixel: Pixel) {
for x in origin[0]..(origin[0] + size[0]) {
for y in origin[1]..(origin[1] + size[1]) {
self.buffer[x + y*self.h] += pixel;
}
}
}
}
impl From<&PixelBuffer> for Vec<[u8; 4]> {
fn from(buffer: &PixelBuffer) -> Self {
buffer.buffer.iter()
.map(|pixel| pixel.into())
.collect()
}
}
//--------------------------------------------------------------------------------------------------
/* FontRenderer struct */
pub struct FontRenderer {
font: Font<'static>, scale: Scale, v_metrics: VMetrics, color: [u8; 3],
}
impl FontRenderer {
pub fn new(size: f32, color: [u8; 3]) -> Self {
// Load the font
let font = {
let font_data = include_bytes!("../fonts/DejaVuSansMono.ttf");
// This only succeeds if collection consists of one font
Font::try_from_bytes(font_data as &[u8]).expect("Error constructing Font")
};
let scale = Scale::uniform(size);
let v_metrics = font.v_metrics(scale);
FontRenderer {
font,
scale,
v_metrics,
color,
}
}
pub fn draw(&mut self, buffer: &mut PixelBuffer, text: &str, origin: [usize; 2],
size: [usize; 2]) {
let line_offset = (self.v_metrics.line_gap + self.v_metrics.ascent
- self.v_metrics.descent) as usize;
// split text along '\n'
text.split('\n')
.enumerate()
.for_each(|(id, line)| {
// layout the glyphs in a line with 20 pixels padding
let glyphs: Vec<_> = self.font
.layout(line, self.scale, point(origin[0] as f32,
origin[1] as f32 + self.v_metrics.ascent))
.collect();
// Loop through the glyphs in the text, positing each one on a line
for glyph in glyphs {
if let Some(bounding_box) = glyph.pixel_bounding_box() {
// Draw the glyph into the image per-pixel by using the draw closure
glyph.draw(|x, y, v| {
let x_pos = (x + bounding_box.min.x as u32) as usize;
let y_pos = (y + bounding_box.min.y as u32) as usize + id * line_offset;
if x_pos < origin[0] + size[0] && y_pos < origin[1] + size[1] {
buffer.put_pixel(
x_pos,
y_pos,
// Turn the coverage into an alpha value
Pixel::rgba(
self.color[0],
self.color[1],
self.color[2],
(v * 255.0) as u8),
)
}
});
}
}
});
}
}