Back up to VCS
This commit is contained in:
commit
fe62f96006
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/apple-one.iml" filepath="$PROJECT_DIR$/.idea/apple-one.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,271 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "apple-one"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crossterm",
|
||||
"r6502",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r6502"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "757565cafd699d777dee11fba136fb84cbe22817302426bef5901709dbf5e1f4"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "apple-one"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
r6502 = "1.1.1"
|
||||
crossterm = "0.27"
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
|
||||
|
||||
pub mod variables {
|
||||
pub const KBD: u16 = 0xD010;
|
||||
pub const KBD_CR: u16 = 0xD011;
|
||||
pub const DSP: u16 = 0xD012;
|
||||
pub const DSP_CR: u16 = 0xD013;
|
||||
}
|
|
@ -0,0 +1,391 @@
|
|||
// TODO: ACIE
|
||||
|
||||
#![warn(clippy::pedantic, clippy::perf)]
|
||||
#![allow(clippy::too_many_lines)]
|
||||
|
||||
mod constants;
|
||||
|
||||
use crossterm::{
|
||||
cursor::{EnableBlinking, MoveTo, MoveToNextLine},
|
||||
event::{poll, read, Event, KeyCode, KeyEvent, KeyEventKind},
|
||||
execute,
|
||||
style::Print,
|
||||
terminal::{
|
||||
disable_raw_mode, enable_raw_mode, Clear, ClearType, DisableLineWrap, EnableLineWrap,
|
||||
EnterAlternateScreen, LeaveAlternateScreen,
|
||||
},
|
||||
};
|
||||
use r6502::{Emulator, FunctionReadCallback, FunctionWriteCallback, Opcode, State};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
fs, io,
|
||||
path::PathBuf,
|
||||
process::ExitCode,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let Err(err) = inner_main() else {
|
||||
disable_raw_mode().expect("failed to disable raw mode");
|
||||
execute!(io::stdout(), LeaveAlternateScreen, EnableLineWrap)
|
||||
.expect("failed to clear alternate screen");
|
||||
return ExitCode::SUCCESS;
|
||||
};
|
||||
disable_raw_mode().expect("failed to disable raw mode");
|
||||
execute!(io::stdout(), LeaveAlternateScreen, EnableLineWrap)
|
||||
.expect("failed to clear alternate screen");
|
||||
eprintln!("io error: {err}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
|
||||
struct EmulatorState {
|
||||
keyboard_ready: bool,
|
||||
keyboard_register: u8,
|
||||
display_x: u8,
|
||||
display_y: u8,
|
||||
display_reg: u8,
|
||||
display_buffer: [u8; 40 * 24],
|
||||
old_display_buffer: Option<[u8; 40 * 24]>,
|
||||
paused: bool,
|
||||
step: bool,
|
||||
step_counter: u16,
|
||||
last_display: Instant,
|
||||
}
|
||||
|
||||
static CHARACTER_SET: &[u8] = br##"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?"##;
|
||||
|
||||
fn inner_main() -> io::Result<()> {
|
||||
execute!(
|
||||
io::stdout(),
|
||||
EnterAlternateScreen,
|
||||
DisableLineWrap,
|
||||
EnableBlinking,
|
||||
Clear(ClearType::All)
|
||||
)?;
|
||||
|
||||
enable_raw_mode()?;
|
||||
|
||||
let emu_state = Rc::new(RefCell::new(EmulatorState {
|
||||
keyboard_ready: false,
|
||||
keyboard_register: 0b1000_0000,
|
||||
display_x: 0,
|
||||
display_y: 0,
|
||||
display_reg: 0b0000_0000,
|
||||
display_buffer: [b' '; 40 * 24],
|
||||
old_display_buffer: None,
|
||||
paused: false,
|
||||
step: false,
|
||||
step_counter: 0,
|
||||
last_display: Instant::now()
|
||||
}));
|
||||
|
||||
let mut emulator = Emulator::default()
|
||||
.with_program_counter(0xFF00)
|
||||
.with_rom_from(include_bytes!("basic.rom"), 0xE000)
|
||||
.with_rom_from(include_bytes!("wozmon.rom"), 0xFF00)
|
||||
.with_write_callback(FunctionWriteCallback(
|
||||
|state: &mut State, addr, mut byte| {
|
||||
if emu_state.borrow_mut().step {
|
||||
let _ = execute!(
|
||||
io::stdout(),
|
||||
Print(format!("WRITE {addr:04X} {byte:02X}")),
|
||||
MoveToNextLine(1)
|
||||
);
|
||||
}
|
||||
match addr {
|
||||
0..=0x7FFF => state.memory[addr as usize] = byte,
|
||||
constants::variables::DSP => {
|
||||
let mut emu_state = emu_state.borrow_mut();
|
||||
byte &= 0b0111_1111;
|
||||
emu_state.display_reg = byte | 0b1000_0000;
|
||||
emu_state.last_display = Instant::now();
|
||||
match byte {
|
||||
0x00 | 0x7F => {}
|
||||
b'\n' | b'\r' => {
|
||||
emu_state.old_display_buffer = Some(emu_state.display_buffer);
|
||||
emu_state.display_x = 0;
|
||||
emu_state.display_y += 1;
|
||||
}
|
||||
other => {
|
||||
emu_state.old_display_buffer = Some(emu_state.display_buffer);
|
||||
let index = (emu_state.display_y as usize) * 40
|
||||
+ (emu_state.display_x as usize);
|
||||
emu_state.display_buffer[index] = other;
|
||||
emu_state.display_x += 1;
|
||||
}
|
||||
}
|
||||
if emu_state.display_x == 40 {
|
||||
if emu_state.old_display_buffer.is_none() {
|
||||
emu_state.old_display_buffer = Some(emu_state.display_buffer);
|
||||
}
|
||||
emu_state.display_x = 0;
|
||||
emu_state.display_y += 1;
|
||||
}
|
||||
if emu_state.display_y > 23 {
|
||||
if emu_state.old_display_buffer.is_none() {
|
||||
emu_state.old_display_buffer = Some(emu_state.display_buffer);
|
||||
}
|
||||
emu_state.display_y = 23;
|
||||
// Copy the screen buffer up for a new line
|
||||
emu_state.display_buffer.rotate_left(40);
|
||||
emu_state.display_buffer[40 * 23..].fill(b' ');
|
||||
}
|
||||
}
|
||||
_ => {} // ROM
|
||||
}
|
||||
},
|
||||
))
|
||||
.with_read_callback(FunctionReadCallback(|state: &mut State, addr| {
|
||||
let res = match addr {
|
||||
constants::variables::KBD => {
|
||||
let mut emu_state = emu_state.borrow_mut();
|
||||
emu_state.keyboard_ready = false;
|
||||
emu_state.keyboard_register
|
||||
}
|
||||
constants::variables::KBD_CR => {
|
||||
if emu_state.borrow_mut().keyboard_ready {
|
||||
0xa7
|
||||
} else {
|
||||
0x00
|
||||
}
|
||||
}
|
||||
constants::variables::DSP => emu_state.borrow_mut().display_reg,
|
||||
constants::variables::DSP_CR => 0b1000_0000,
|
||||
_ => state.memory[addr as usize],
|
||||
};
|
||||
if emu_state.borrow_mut().step {
|
||||
let _ = execute!(
|
||||
io::stdout(),
|
||||
Print(format!("READ {addr:04X} {res:02X}")),
|
||||
MoveToNextLine(1)
|
||||
);
|
||||
}
|
||||
res
|
||||
}));
|
||||
|
||||
let mut last = Instant::now();
|
||||
|
||||
loop {
|
||||
let mut emu_state = emu_state.borrow_mut();
|
||||
emu_state.step = false;
|
||||
|
||||
if poll(Duration::ZERO)? {
|
||||
let event = read()?;
|
||||
if let Event::Key(KeyEvent {
|
||||
code,
|
||||
kind: KeyEventKind::Press,
|
||||
..
|
||||
}) = event
|
||||
{
|
||||
match keycode_ascii(code) {
|
||||
Ok(ascii) => {
|
||||
emu_state.keyboard_ready = true;
|
||||
emu_state.keyboard_register = ascii | 0b1000_0000;
|
||||
}
|
||||
Err(KeyCode::End | KeyCode::F(1)) => {
|
||||
// Stop
|
||||
break Ok(());
|
||||
}
|
||||
Err(KeyCode::Pause | KeyCode::F(2)) => {
|
||||
// Pause
|
||||
emu_state.paused ^= true;
|
||||
emu_state.step_counter = 0;
|
||||
emu_state.old_display_buffer = Some(emu_state.display_buffer);
|
||||
}
|
||||
Err(KeyCode::Home | KeyCode::F(3)) => {
|
||||
// Reset
|
||||
let reset = u16::from_le_bytes([
|
||||
emulator.state.memory[0xFFFC],
|
||||
emulator.state.memory[0xFFFD],
|
||||
]);
|
||||
emulator.state.program_counter = reset;
|
||||
}
|
||||
Err(KeyCode::Insert | KeyCode::F(4)) => {
|
||||
// Clear screen
|
||||
emu_state.display_buffer = [b' '; 40 * 24];
|
||||
emu_state.display_x = 0;
|
||||
emu_state.display_y = 0;
|
||||
emu_state.step_counter = 0;
|
||||
emu_state.old_display_buffer = Some([b'@'; 40 * 24]);
|
||||
execute!(io::stdout(), Clear(ClearType::All))?;
|
||||
}
|
||||
Err(KeyCode::F(5)) => {
|
||||
disable_raw_mode()?;
|
||||
|
||||
// Load a file
|
||||
execute!(
|
||||
io::stdout(),
|
||||
MoveTo(0, 32),
|
||||
EnableLineWrap,
|
||||
Print("File path?")
|
||||
)?;
|
||||
let mut contents = None;
|
||||
while contents.is_none() {
|
||||
execute!(io::stdout(), MoveTo(0, 33), Clear(ClearType::CurrentLine))?;
|
||||
let mut path_str = String::new();
|
||||
io::stdin().read_line(&mut path_str)?;
|
||||
if path_str.trim().is_empty() {
|
||||
break;
|
||||
}
|
||||
let path_buf = PathBuf::from(path_str.trim());
|
||||
if path_buf.exists() {
|
||||
contents = fs::read(path_buf).ok();
|
||||
}
|
||||
}
|
||||
let Some(contents) = contents else {
|
||||
enable_raw_mode()?;
|
||||
continue;
|
||||
};
|
||||
execute!(
|
||||
io::stdout(),
|
||||
MoveTo(0, 34),
|
||||
EnableLineWrap,
|
||||
Print(format!(
|
||||
"Read {} bytes.\nPlace in ROM (in hex):",
|
||||
contents.len()
|
||||
))
|
||||
)?;
|
||||
let mut placed = false;
|
||||
while !placed {
|
||||
execute!(io::stdout(), MoveTo(0, 36), Clear(ClearType::CurrentLine))?;
|
||||
let mut input = String::new();
|
||||
io::stdin().read_line(&mut input)?;
|
||||
if input.trim().is_empty() {
|
||||
break;
|
||||
}
|
||||
let Ok(place) = usize::from_str_radix(input.trim(), 16) else {
|
||||
continue;
|
||||
};
|
||||
if (place + contents.len()) <= 0xFFFF {
|
||||
placed = true;
|
||||
emulator.state.memory[place..place + contents.len()]
|
||||
.copy_from_slice(&contents);
|
||||
}
|
||||
}
|
||||
execute!(
|
||||
io::stdout(),
|
||||
MoveTo(0, 32),
|
||||
DisableLineWrap,
|
||||
Clear(ClearType::FromCursorDown)
|
||||
)?;
|
||||
enable_raw_mode()?;
|
||||
}
|
||||
Err(KeyCode::F(6)) if emu_state.paused => {
|
||||
emu_state.step = true;
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if emu_state.last_display.elapsed() > Duration::from_secs_f64(1.0 / 60.0) {
|
||||
emu_state.last_display = Instant::now();
|
||||
emu_state.display_reg &= 0b0111_1111;
|
||||
if let Some(old) = emu_state.old_display_buffer.take() {
|
||||
// Display screen
|
||||
for y in 0..24 {
|
||||
for x in 0..40 {
|
||||
if old[(y * 40 + x) as usize] == emu_state.display_buffer[(y * 40 + x) as usize]
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let byte_char =
|
||||
CHARACTER_SET[emu_state.display_buffer[(y * 40 + x) as usize] as usize];
|
||||
execute!(io::stdout(), MoveTo(x, y), Print(byte_char as char))?;
|
||||
}
|
||||
}
|
||||
|
||||
execute!(
|
||||
io::stdout(),
|
||||
MoveTo(0, 30),
|
||||
Clear(ClearType::CurrentLine),
|
||||
Print(if emu_state.paused {
|
||||
"F1/End: Quit F2/Pause: Resume F3/Home: Reset F4/Insert: Clear F5: Load File F6: Step"
|
||||
} else {
|
||||
"F1/End: Quit F2/Pause: Pause F3/Home: Reset F4/Insert: Clear F5: Load File"
|
||||
}),
|
||||
MoveTo(
|
||||
u16::from(emu_state.display_x),
|
||||
u16::from(emu_state.display_y)
|
||||
)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if !emu_state.paused || emu_state.step {
|
||||
if emu_state.step {
|
||||
let opcode_str =
|
||||
Opcode::load(&emulator.state.memory[emulator.state.program_counter as usize..])
|
||||
.unwrap_or_default()
|
||||
.map_or("<INVALID>".into(), |op| format!("{op}"));
|
||||
execute!(
|
||||
io::stdout(),
|
||||
MoveTo(0, 31),
|
||||
Clear(ClearType::CurrentLine),
|
||||
Print(&opcode_str),
|
||||
MoveTo(80, emu_state.step_counter),
|
||||
Print(format!(
|
||||
"{:04X} {opcode_str}",
|
||||
emulator.state.program_counter
|
||||
)),
|
||||
MoveTo(0, 32),
|
||||
Clear(ClearType::CurrentLine),
|
||||
Print(format!("{:04X?}", emulator.state)),
|
||||
MoveTo(0, 33),
|
||||
Print(format!(
|
||||
"XAM: {:04x} ST: {:04x} HEX: {:04x} YSAV: {:02x} MODE: {:02x}",
|
||||
u16::from_le_bytes([
|
||||
emulator.state.memory[0x24],
|
||||
emulator.state.memory[0x25]
|
||||
]),
|
||||
u16::from_le_bytes([
|
||||
emulator.state.memory[0x26],
|
||||
emulator.state.memory[0x27]
|
||||
]),
|
||||
u16::from_le_bytes([
|
||||
emulator.state.memory[0x28],
|
||||
emulator.state.memory[0x29]
|
||||
]),
|
||||
emulator.state.memory[0x2A],
|
||||
emulator.state.memory[0x2B]
|
||||
)),
|
||||
MoveTo(0, 34),
|
||||
Clear(ClearType::FromCursorDown)
|
||||
)?;
|
||||
emu_state.step_counter = emu_state.step_counter.wrapping_add(1);
|
||||
}
|
||||
|
||||
drop(emu_state);
|
||||
|
||||
let Ok(interrupt_requested) = emulator.step() else {
|
||||
// !!! INVALID OPCODE!
|
||||
continue;
|
||||
};
|
||||
if interrupt_requested {
|
||||
// Jump to IRQ vector
|
||||
let vector = u16::from_le_bytes([emulator.read(0xFFFE), emulator.read(0xFFFF)]);
|
||||
emulator.state.program_counter = vector;
|
||||
};
|
||||
}
|
||||
std::thread::sleep(Duration::from_micros(1).saturating_sub(last.elapsed()));
|
||||
last = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
fn keycode_ascii(code: KeyCode) -> Result<u8, KeyCode> {
|
||||
use KeyCode::{BackTab, Backspace, Char, Delete, Enter, Esc, Null, Tab};
|
||||
|
||||
match code {
|
||||
Enter => Ok(b'\r'),
|
||||
Tab | BackTab => Ok(b'\t'),
|
||||
Delete => Ok(0x7f),
|
||||
Backspace => Ok(b'_'),
|
||||
Char(c) => Ok(c.to_ascii_uppercase() as u8),
|
||||
Null => Ok(0),
|
||||
Esc => Ok(0x1B),
|
||||
c => Err(c),
|
||||
}
|
||||
}
|
||||
|
||||
// 0:a9 0 aa 20 ef ff e8 8a 4c 2 0
|
Binary file not shown.
Loading…
Reference in New Issue