diff --git a/Makefile b/Makefile index 128ba7a..eec98e4 100644 --- a/Makefile +++ b/Makefile @@ -4,5 +4,8 @@ RUSTC=rustc test: $(RUSTC) -Z debug-info -o appletest --test apple.rs ./appletest + +tui: + $(RUSTC) -Z debug-info tui.rs diff --git a/a2.rs b/a2.rs index 01dd814..377ea9b 100644 --- a/a2.rs +++ b/a2.rs @@ -2,14 +2,14 @@ use mem::Mem; use util::Xorshift; -static GR_TXMODE: int = 1; -static GR_MIXMODE: int = 2; -static GR_PAGE1: int = 4; -static GR_HIRES: int = 8; +pub static GR_TXMODE: u8 = 1; +pub static GR_MIXMODE: u8 = 2; +pub static GR_PAGE1: u8 = 4; +pub static GR_HIRES: u8 = 8; -static HW_LO: u16 = 0xC000; -static ROM_LO: u16 = 0xD000; -static ROM_LEN: u16 = 0x3000; +pub static HW_LO: u16 = 0xC000; +pub static ROM_LO: u16 = 0xD000; +pub static ROM_LEN: u16 = 0x3000; pub trait Peripheral { @@ -58,7 +58,7 @@ pub struct AppleII debugflags: int, kbdlatch: u8, - grswitch: u16, + grswitch: u8, soundstate: bool, aux: LangCardState, nreads: u16 // counts # of reads for noise() fn diff --git a/lazyterm.rs b/lazyterm.rs new file mode 100644 index 0000000..91e80d6 --- /dev/null +++ b/lazyterm.rs @@ -0,0 +1,145 @@ + +use std::vec; +use std::io; +use std::io::stdio::{StdReader,StdWriter}; +use std::io::Timer; + +// TODO: use these but they are inconvenient +pub struct TermColor(u8); + +#[deriving(Clone)] +pub struct TermCell +{ + bg: u8, + fg: u8, + ch: char +} + +//static BLACK: TermColor = TermColor(0); +//static WHITE: TermColor = TermColor(15); +pub static BLACK: u8 = 0; +pub static WHITE: u8 = 15; + +pub static EMPTY: TermCell = TermCell { fg:WHITE, bg:BLACK, ch:' ' }; + +#[deriving(Clone)] +pub struct Buffer +{ + buf: ~[~[TermCell]], + width: uint, + height: uint, +} + +impl Buffer +{ + pub fn new(cols: uint, rows: uint) -> Buffer + { + Buffer { width:cols, height:rows, buf:vec::from_elem(rows, vec::from_elem(cols, EMPTY)) } + } + + pub fn set(&mut self, col: uint, row: uint, cell: TermCell) + { + self.buf[row][col] = cell; + } +} + +pub struct Terminal +{ + hin : StdReader, + hout: StdWriter, + lastbuf: Buffer, +} + +impl Terminal +{ + pub fn new() -> Terminal + { + Terminal { lastbuf: Buffer::new(0,0), hin: io::stdin(), hout: io::stdout() } + } + + fn reset(&mut self) + { + self.hout.write_str(format!("\x1b[0")); + } + + fn update(&mut self, buf: &Buffer) + { + let ref mut hout = self.hout; + let mut prev = EMPTY; + for y in range(0,buf.height) + { + for x in range(0,buf.width) + { + let cell = buf.buf[y][x]; + let last = self.lastbuf.buf[y][x]; + let mut dirty = false; + if (cell.fg != prev.fg) + { + hout.write_str(format!("\x1b[38;5;{}m", cell.fg)); + dirty = true; + } + if (cell.bg != prev.bg) + { + hout.write_str(format!("\x1b[48;5;{}m", cell.bg)); + dirty = true; + } + // TODO + //if (dirty || cell.ch != last.ch) + { + hout.write_char(cell.ch); + } + prev = cell; + } + hout.write_char('\n'); + } + } + + fn redraw(&mut self, buf: &Buffer) + { + let ref mut hout = self.hout; + for y in range(0,buf.height) + { + for x in range(0,buf.width) + { + let cell = buf.buf[y][x]; + hout.write_str(format!("\x1b[38;5;{}m", cell.fg)); + hout.write_str(format!("\x1b[48;5;{}m", cell.bg)); + hout.write_char(cell.ch); + } + hout.write_char('\n'); + } + } + + pub fn refresh(&mut self, buf: &Buffer) + { + self.reset(); + if buf.width == self.lastbuf.width && buf.height == self.lastbuf.height + { + // scroll up N lines + self.hout.write_str(format!("\x1b[{}A", buf.height)); + // update dirty cells + self.update(buf); + } else { + // redraw entire window + // TODO: rescroll window + self.redraw(buf); + } + self.reset(); + self.lastbuf = buf.clone(); + } +} + +// + +fn main() +{ + let mut buf = Buffer::new(40,24); + let mut term = Terminal::new(); + let mut timer = Timer::new().unwrap(); + for i in range(0u8,15) + { + buf.set(1, 1, TermCell { bg:i, fg:i+1, ch:'#' } ); + term.refresh(&buf); + timer.sleep(50); + } +} diff --git a/tui.rs b/tui.rs new file mode 100644 index 0000000..381c4f2 --- /dev/null +++ b/tui.rs @@ -0,0 +1,104 @@ + +#[feature(link_args, macro_rules)]; + +use cpu::Cpu; +use mem::Mem; +use a2::AppleII; +use a2::Peripheral; +use diskii::DiskController; +use util::current_time_millis; +use lazyterm::{Terminal,Buffer}; + +// NB: This must be first to pick up the macro definitions. What a botch. +#[macro_escape] +pub mod util; + +#[macro_escape] +pub mod cpu; +pub mod mem; +pub mod a2; +pub mod diskii; + +pub mod lazyterm; + +static text_lut: [u16, ..8*3] = [ + 0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, + 0x028, 0x0a8, 0x128, 0x1a8, 0x228, 0x2a8, 0x328, 0x3a8, + 0x050, 0x0d0, 0x150, 0x1d0, 0x250, 0x2d0, 0x350, 0x3d0 +]; + +static flashInterval: u64 = 500; + +fn draw_text_line(a2: &AppleII, buf: &mut Buffer, flash: bool, y: uint) +{ + // get the base address of this line + let base = text_lut[y] + if (a2.grswitch & a2::GR_PAGE1) != 0 { 0x800 } else { 0x400 }; + for x in range(0u,40) + { + let mut b = a2.mem[base + x as u16]; + let invert: bool; + // invert flash characters 1/2 of the time + if (b >= 0x80) + { + invert = false; + } + else if (b >= 0x40) + { + invert = flash; + if (flash) { b -= 0x40; } else { b += 0x40; } + } + else + { + invert = true; + } + // if the char. changed, draw it + let ch = (b & 0x7f) as char; + let cell = lazyterm::TermCell { fg:lazyterm::WHITE, bg:lazyterm::BLACK, ch:ch }; + buf.set(x, y, cell); + //drawTextChar(x, y, b & 0x7f, invert); + } +} + +fn update_term_buf(a2: &AppleII, buf: &mut Buffer, flash: bool) +{ + for y in range(0u,24) + { + draw_text_line(a2, buf, flash, y); + } +} + +fn main() +{ + let mut a2 = AppleII::new(); + a2.read_roms(); + let mut dc: DiskController = DiskController::new(); + dc.load_disk(0, "JUNK4.DSK"); + assert!(dc.has_disk(0)); + a2.set_slot(6, ~dc); + let mut cpu = Cpu::new(a2); + cpu.reset(); + + let mut term = Terminal::new(); + let mut buf = Buffer::new(40,24); + + // mismatched types: expected `` but found `` + let speedup = 2; + let clocks_per_msec = (1000 * speedup); + let mut t0 = current_time_millis(); + loop + { + // cursor flashing? + let flash = (t0 % (flashInterval<<1)) > flashInterval; + update_term_buf(&cpu.mem, &mut buf, flash); + term.refresh(&buf); + + let t1 = current_time_millis(); + let cycle = cpu.cy + (t1-t0)*clocks_per_msec; + while cpu.cy < cycle + { + cpu.step(); + } + t0 = t1; + } +} +