Started work on tui debugger!

This commit is contained in:
2024-06-15 22:45:27 -04:00
parent b1b9c64468
commit 0ec54d6672
13 changed files with 725 additions and 68 deletions
+12 -11
View File
@@ -1,8 +1,9 @@
// use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError};
use crate::instructions::{get_instruction, Instruction};
use crate::memory::Mem;
use crate::types::{Byte, Word};
use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::mpsc::Receiver;
use std::sync::Arc;
@@ -157,9 +158,9 @@ impl Cpu {
sleep(Duration::from_nanos(100));
self.pending_cycles -= 1;
}
if !self.get_flag(StatusFlag::IrqDisable) && self.irq.recv().unwrap() {
self.interrupt();
}
// if !self.get_flag(StatusFlag::IrqDisable) && self.irq.recv().unwrap() {
// self.interrupt();
// }
let opcode = match self.read(self.pc) {
Ok(byte) => byte,
Err(_) => {
@@ -170,12 +171,12 @@ impl Cpu {
let instruction = get_instruction(opcode);
match instruction {
Instruction::Valid(valid_instruction) => {
//println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi);
//println!(
// "Instruction: {:?}, {:#04x}",
// valid_instruction.opcode, opcode
//);
//println!("");
// println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi);
// println!(
// "Instruction: {:?}, {:#04x}",
// valid_instruction.opcode, opcode
// );
// println!("");
self.pc += 1;
match valid_instruction.opcode.call(self) {
Ok(_) => {
@@ -196,7 +197,7 @@ impl Cpu {
}
};
println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq.try_recv().unwrap_or_default(), nmi = self.nmi);
// println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq.try_recv().unwrap_or_default(), nmi = self.nmi);
memory
.dump(PathBuf::from_str("./cpu_dump.bin").unwrap())
.unwrap();
+1 -6
View File
@@ -1,10 +1,5 @@
use minifb::{InputCallback, Key};
use std::{
path::PathBuf,
process::exit,
str::FromStr,
sync::{Arc, Mutex},
};
use std::sync::{Arc, Mutex};
use crate::memory::Mem;
+46 -20
View File
@@ -5,6 +5,7 @@ mod error;
mod instructions;
mod keyboard;
mod memory;
mod tui;
mod types;
mod video;
@@ -13,18 +14,28 @@ use crate::keyboard::Keyboard;
use crate::memory::Mem;
use crate::video::Crtc;
use clap::Parser;
// use clap::Parser;
use minifb::{Scale, ScaleMode, Window, WindowOptions};
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::Read;
use std::process::exit;
use std::str::FromStr;
use std::sync::{mpsc, Mutex};
use std::{path::PathBuf, sync::Arc, thread};
use std::{
fs::File,
io::{stdout, Read, Result},
path::PathBuf,
str::FromStr,
sync::{mpsc, Arc, Mutex},
thread,
};
use toml::Table;
use crossterm::{
event::{self, KeyCode, KeyEventKind},
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
};
use ratatui::prelude::{CrosstermBackend, Terminal};
// use toml::Table;
//#[derive(Parser)]
//struct Cli {
@@ -39,16 +50,21 @@ struct Config {
rom: String,
}
fn main() {
fn main() -> Result<()> {
stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
terminal.clear()?;
let config: Config = match File::open("./config.toml") {
Ok(mut file) => {
let mut string = String::new();
file.read_to_string(&mut string).unwrap();
toml::from_str(string.as_str()).unwrap()
}
Err(_) => return,
Err(_) => return Ok(()),
};
println!("{config:#?}");
let mut memory = Mem::new();
let rom = match std::fs::File::open(config.rom) {
Ok(file) => file,
@@ -58,23 +74,18 @@ fn main() {
println!("{:?}", error);
};
let (interrupt_tx, interrupt_rx) = mpsc::channel();
let (window_tx, window_rx) = mpsc::channel();
memory
.dump(PathBuf::from_str("./coredump.bin").unwrap())
.unwrap();
let shared_memory = Arc::new(Mutex::new(memory));
let cpu_memory = shared_memory.clone();
let display_memory = shared_memory.clone();
let keyboard_memory = shared_memory.clone();
thread::spawn(move || {
let mut cpu = Cpu::new(cpu_memory, interrupt_rx);
cpu.reset().unwrap();
cpu.execute();
});
let (interrupt_tx, interrupt_rx) = mpsc::channel();
let (window_tx, window_rx) = mpsc::channel();
thread::spawn(move || {
let mut screen = Crtc::new(
@@ -105,8 +116,23 @@ fn main() {
window.set_input_callback(Box::new(Keyboard::new(keyboard_memory)));
while window.is_open() {
let cpu = Cpu::new(cpu_memory, interrupt_rx);
let mut tui = tui::App::new(cpu);
tui.init();
loop {
if event::poll(std::time::Duration::from_millis(16))? {
if let event::Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
break;
}
}
}
let buffer = window_rx.recv().unwrap();
window.update_with_buffer(&buffer, 512, 380).unwrap();
tui.update(&mut terminal)?;
}
stdout().execute(LeaveAlternateScreen)?;
disable_raw_mode()?;
Ok(())
}
Binary file not shown.
+172 -16
View File
@@ -10,7 +10,7 @@ cursor = $202
char_buffer = $300 ; 256 byte character buffer
kb_row = $4400 ; keyboard hardware register, there are 5 more but i can just increment from here
kb_row = $4400 ; keyboard hardware register
kb_row_cache = $203 ; cache
.org $8000
@@ -34,32 +34,188 @@ cleardisplay:
sta $6700,y ; this goes slightly over but it's fine
iny
bne cleardisplay
cli
; cli
main:
jsr draw
jsr keyboard
; key_zero:
; stz keyboard_cache, x
; dex
; bpl key_zero
; fim:
; cli
; bra fim
; jsr kitty_keys
jmp main
; ; copying @smal rn: https://github.com/smaldragon/KittyEMU/blob/main/roms/foxmon.asm
; char_timer = $10
; line_buffer = $0200
; char_cur = $11
; line_buffer_i = $12
; line_buffer_l = $13
; keyboard_cache = $14
; line_cur = $20
; irq:
; lda line_buffer_i
; lda $e0
; lda $6f
; sei
; stz char_cur
; lda line_buffer_i
; sta line_buffer_l
; ldx #4
; kitty_keys: ; reads pressed key and writes keymap value to char_cur
; phx
; txa
; asl
; asl
; asl
; asl
; asl ; i think this is supposed to be once for every keyboard row
; tax
; lda kb_row, x
; plx
; pha
; cmp keyboard_cache, x
; bne change
; jmp nochange
; change:
; bit7:
; asl keyboard_cache, x
; bcs bit6
; bit #0b10000000
; beq bit6
; pha
; lda keymap_7, x
; sta char_cur
; pla
; bit6:
; asl keyboard_cache, x
; bcs bit5
; bit #0b01000000
; beq bit5
; pha
; lda keymap_6, x
; sta char_cur
; pla
; bit5:
; asl keyboard_cache, x
; bcs bit4
; bit #0b00100000
; beq bit4
; pha
; lda keymap_5, x
; sta char_cur
; pla
; bit4:
; asl keyboard_cache, x
; bcs bit3
; bit #0b00010000
; beq bit3
; pha
; lda keymap_4, x
; sta char_cur
; pla
; bit3:
; asl keyboard_cache, x
; bcs bit2
; bit #0b00001000
; beq bit2
; pha
; lda keymap_3, x
; sta char_cur
; pla
; bit2:
; asl keyboard_cache, x
; bcs bit1
; bit #0b00000100
; beq bit1
; pha
; lda keymap_2, x
; sta char_cur
; pla
; bit1:
; asl keyboard_cache, x
; bcs bit0
; bit #0b00000010
; beq bit0
; pha
; lda keymap_1, x
; sta char_cur
; pla
; bit0:
; asl keyboard_cache, x
; bcs bitend
; bit #0b00000001
; beq bitend
; pha
; lda keymap_0, x
; sta char_cur
; pla
; bitend:
; nochange:
; pla
; sta keyboard_cache, x
; dex
; bmi keyend
; jmp kitty_keys
; keyend:
; write:
; lda char_cur
; ldy cursor
; sta $6000, y
; inc cursor
; rts
; ; col = keyboard row, row = keyboard bit placement inverted
; keymap_0:
; .byte "??????"
; keymap_1:
; .byte "wqdzx "
; keymap_2:
; .byte "esvfc "
; keymap_3:
; .byte "rghbn?"
; keymap_4:
; .byte "tykjm?"
; keymap_5:
; .byte "ui?l??"
; keymap_6:
; .byte "op?21?"
; keymap_7:
; .byte "??a43?"
;
;
keyboard:
ldy #0
x
not_keyboard:
ldy #0
.loop: ; loop through each row
.check_row: ; loop through each row
lda kb_row, y
beq .skip ; if row has no key pressed, skip checking which key
beq .skip_row ; if row has no key pressed, skip checking which key
sta kb_row_cache, y ; if key pressed, cache it
lda kb_row, y
cmp kb_row_cache, y ; has key changed?
beq key_down
.skip:
.skip_row:
iny
cpy #5
bne .loop
bne .check_row
rts
key_down: ; a is loaded with the row byte
sty key_row ; store character row
inc cursor
pha
phy
sty key_row ; store character row
ldy #0
.find_col: ; test each row bit, store column if key pressed
lsr ; test bit 7
@@ -90,12 +246,12 @@ keymap_index:
lda 0, x
tay
do_something_w_key: ; we've stored the character position, now let's
print: ; we've stored the character position, now let's
lda keymap, y
ldy cursor
sta $6000, y
inc cursor
ply
pla
rts
keymap:
@@ -107,9 +263,9 @@ keymap:
.byte "????? m"
draw:
push_coords #0, #0
push_char #$00
jsr draw_char
; push_coords #0, #0
; push_char #$00
; jsr draw_char
rts
draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
@@ -151,7 +307,7 @@ isr: ; interrupt service routine
pha
phx
phy
jsr keyboard
; jsr irq
ply
plx
pla
Binary file not shown.
+1 -1
View File
@@ -26,7 +26,7 @@
dex
.endm
.macro push_char, char ; pushes an ascii character code onto the stack
.macro push_char, char; pushes an ascii character code onto the stack
lda \char
push
sta 0, x ; char low byte
+150
View File
@@ -0,0 +1,150 @@
// use std::time::Duration;
mod tabs;
mod term;
use std::{io::Result, time::Duration};
use crossterm::event::{self, poll, Event, KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
prelude::*,
widgets::{Block, Cell, Paragraph, Row, Table, TableState},
};
use crate::cpu::Cpu;
pub struct App {
cpu: Cpu,
running: bool,
table_state: TableState,
}
impl App {
pub fn new(cpu: Cpu) -> Self {
Self {
cpu,
running: true,
table_state: TableState::default(),
}
}
pub fn init(&mut self) {
let _ = self.cpu.reset();
}
pub fn update(&mut self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
self.draw(terminal)?;
self.handle_events()?;
self.handle_cpu()?;
Ok(())
}
fn handle_cpu(&mut self) -> Result<()> {
if self.running {
self.cpu.cycle();
}
Ok(())
}
/// Draw a single frame of the app.
fn draw(&self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
terminal
.draw(|frame| {
frame.render_widget(self, frame.size());
})
.unwrap();
Ok(())
}
fn handle_events(&mut self) -> Result<()> {
if poll(Duration::from_secs_f32(1.0 / 25.0))? {
match event::read()? {
Event::Key(key) if key.kind == KeyEventKind::Press => self.handle_key_press(key),
_ => {}
}
}
Ok(())
}
fn handle_key_press(&mut self, key: KeyEvent) {
match key.code {
KeyCode::Enter => {
if !self.running {
self.cpu.cycle()
}
}
KeyCode::Char(' ') => {
self.running = !self.running;
}
_ => {}
};
}
}
/// Implement Widget for &App rather than for App as we would otherwise have to clone or copy the
/// entire app state on every frame. For this example, the app state is small enough that it doesn't
/// matter, but for larger apps this can be a significant performance improvement.
impl Widget for &App {
fn render(self, area: Rect, buf: &mut Buffer) {
let [cpu_area, memory_area] =
Layout::horizontal([Constraint::Percentage(50), Constraint::Fill(1)]).areas(area);
let cpu_info = format!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.cpu.a, x = self.cpu.x, y = self.cpu.y, pc = self.cpu.pc, s = self.cpu.s, p = self.cpu.p, irq = self.cpu.irq, nmi = self.cpu.nmi);
Paragraph::new(cpu_info)
.block(Block::bordered().title("cpu info!"))
.render(cpu_area, buf);
let memory = self.cpu.memory.lock().unwrap();
let table_height = memory_area.rows().count() - 2;
let rows: Vec<Row> = memory.data[0..table_height * 16]
.chunks(16)
.map(|chunk| {
chunk
.iter()
.map(|content| Cell::from(Text::from(format!("{content:#02}"))))
.collect::<Row>()
})
.collect();
let widths = vec![Constraint::Length(2); 16];
Widget::render(
Table::new(rows, widths)
.header(
Row::new(vec![
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e",
"f",
])
.style(Style::new().bold())
// To add space between the header and the rest of the rows, specify the margin
.bottom_margin(1),
)
.block(Block::bordered().title("memory!")),
memory_area,
buf,
);
}
}
// impl Widget for App {
// fn render_title_bar(&self, area: Rect, buf: &mut Buffer) {
// // let layout = Layout::horizontal([Constraint::Min(0), Constraint::Length(43)]);
// // let [title] = layout.areas(area);
// Span::styled("Ratatui", Style::default()).render(area, buf);
// // let titles = Tab::iter().map(Tab::title);
// // Tabs::new(titles)
// // .style(THEME.tabs)
// // .highlight_style(THEME.tabs_selected)
// // .select(self.tab as usize)
// // .divider("")
// // .padding("", "")
// // .render(tabs, buf);
// }
// // fn render_selected_tab(&self, area: Rect, buf: &mut Buffer) {
// // match self.tab {
// // Tab::About => self.about_tab.render(area, buf),
// // Tab::Recipe => self.recipe_tab.render(area, buf),
// // Tab::Email => self.email_tab.render(area, buf),
// // Tab::Traceroute => self.traceroute_tab.render(area, buf),
// // Tab::Weather => self.weather_tab.render(area, buf),
// // };
// // }
// }
+9 -10
View File
@@ -3,12 +3,9 @@ use std::{
fs::File,
io::Read,
path::Path,
sync::{
mpsc::{Sender, SyncSender},
Arc, Mutex,
},
sync::{mpsc::Sender, Arc, Mutex},
thread::sleep,
time::{Duration, Instant},
time::Duration,
};
const FG_COLOR: u32 = 0xFFCC00;
@@ -31,7 +28,7 @@ where
let mut file = File::open(path).unwrap();
let mut bin = vec![0; 0x8000];
file.read_exact(&mut bin).unwrap();
println!("reading char rom");
// println!("reading char rom");
bin
}
None => include_bytes!("./roms/cozette.rom").to_vec(),
@@ -65,7 +62,9 @@ impl Crtc {
return;
}
};
// the rest of this function is arcane wizardry based on the specifics of george's weird
// the rest of this function is arcane wizardry
// based on the specifics of george's weird
// display and characters... don't fuck around w it
let mut i = 0;
for char_row in 0..29 {
@@ -88,15 +87,15 @@ impl Crtc {
let buffer = self.buffer.to_owned();
self.window.send(buffer).unwrap();
let _ = self.window.send(buffer);
}
pub fn run(&mut self) {
loop {
self.interrupt.send(false).unwrap();
let _ = self.interrupt.send(false);
sleep(Duration::from_millis(16));
self.draw();
self.interrupt.send(true).unwrap();
let _ = self.interrupt.send(true);
}
}
}