Skip to content

Commit

Permalink
Support IRQ. Now basic works.
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanizag committed Nov 4, 2023
1 parent df9ac30 commit 372a017
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 21 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["Ivan Izaguirre <[email protected]>"]
edition = "2018"

[dependencies]
iz80 = "^0.3.2"
iz80 = "^0.4.0"
#iz80 = {path = "../iz80"}
chrono = "0.4"

Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ $ ./z80-mbc2-emu
Usage: z80-mbc2-emu IMAGE
IMAGE can be:
basic for Basic using sd/basic47.bin
forth for Forth using sd/forth13.bin
autoboot for Autoboot using sd/autoboot.bin
cpm22 for CP/M 2.2 using sd/cpm22.bin
qpm for QP/M 2.71 using sd/QPMLDR.BIN
cpm3 for CP/M 3.0 using sd/CPMLDR.COM
pascal for UCSD Pascal using sd/ucsdldr.bin
collapse for Collapse OS using sd/cos.bin
fuzix for Fuzix OS using sd/fuzix.bin
Download the images from https://cdn.hackaday.io/files/1599736844284832/S220718-R240620_IOS-Z80-MBC2.zip into the 'sd' directory.
```
Expand Down Expand Up @@ -65,7 +67,7 @@ This emulator emulates the Z80 and provides the same services given by the Atmeg

## TODO

- Support interrupt driven serial transmission to support the Basic image
- Change the way to exit to host to allow control-c to be used on the emulation.
- User led, button and GPIO are not connected to anything
- Adapt to the latest firmware version
- Save the printer output to a file
- Tick based interrupts
32 changes: 18 additions & 14 deletions src/images.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,32 @@ pub struct ImageDefinition {
pub file: &'static str,
pub address: u16,
pub disk_set: u8,
pub ints: bool,
pub int_rx: bool,
pub int_sys_tick: bool,
}

const IMAGES_FOLDER: &str = "sd";


static IMAGES: [ImageDefinition; 8] = [
static IMAGES: [ImageDefinition; 9] = [
ImageDefinition {id: "basic", name: "Basic", file: "basic47.bin",
address: 0x0000, disk_set: 0xff, ints: true},
address: 0x0000, disk_set: 0xff, int_rx: true, int_sys_tick: false},
ImageDefinition {id: "forth", name: "Forth", file: "forth13.bin",
address: 0x0100, disk_set: 0xff, ints: false},
address: 0x0100, disk_set: 0xff, int_rx: false, int_sys_tick: false},
ImageDefinition {id: "autoboot", name: "Autoboot", file: "autoboot.bin",
address: 0x0000, disk_set: 0xff, ints: false},
address: 0x0000, disk_set: 0xff, int_rx: false, int_sys_tick: false},
ImageDefinition {id: "cpm22", name: "CP/M 2.2", file: "cpm22.bin",
address: 0xD1E0, disk_set: 0, ints: false},
address: 0xD1E0, disk_set: 0, int_rx: false, int_sys_tick: false},
ImageDefinition {id: "qpm", name: "QP/M 2.71", file: "QPMLDR.BIN",
address: 0x0080, disk_set: 1, ints: false},
address: 0x0080, disk_set: 1, int_rx: false, int_sys_tick: false},
ImageDefinition {id: "cpm3", name: "CP/M 3.0", file: "CPMLDR.COM",
address: 0x0100, disk_set: 2, ints: false},
address: 0x0100, disk_set: 2, int_rx: false, int_sys_tick: false},
ImageDefinition {id: "pascal", name: "UCSD Pascal", file: "ucsdldr.bin",
address: 0x0000, disk_set: 3, ints: false},
address: 0x0000, disk_set: 3, int_rx: false, int_sys_tick: false},
ImageDefinition {id: "collapse", name: "Collapse OS", file: "cos.bin",
address: 0x0000, disk_set: 4, ints: false},
address: 0x0000, disk_set: 4, int_rx: false, int_sys_tick: false},
ImageDefinition {id: "fuzix", name: "Fuzix OS", file: "fuzix.bin",
address: 0x0000, disk_set: 6, int_rx: true, int_sys_tick: false},
];

const USAGE: &'static str =
Expand All @@ -59,7 +62,7 @@ pub fn select_image() -> &'static ImageDefinition {
let selection = &args[1];

for i in 0..IMAGES.len() {
if IMAGES[i].id == selection && !IMAGES[i].ints{
if IMAGES[i].id == selection {
return &IMAGES[i];
}
}
Expand All @@ -73,9 +76,7 @@ pub fn usage() {
println!("{}", USAGE);
for i in 0..IMAGES.len() {
let filename = Path::new(IMAGES_FOLDER).join(Path::new(IMAGES[i].file));
if !IMAGES[i].ints {
println!(" {} for {} using {}", IMAGES[i].id, IMAGES[i].name, filename.to_str().unwrap());
}
println!(" {} for {} using {}", IMAGES[i].id, IMAGES[i].name, filename.to_str().unwrap());
}
println!("{}", USAGE2);
}
Expand Down Expand Up @@ -109,5 +110,8 @@ pub fn load_image(machine: &mut Mbc2Machine, image: &ImageDefinition) -> bool {
machine.poke(image.address + i as u16, buf[i]);
}

machine.int_rx = image.int_rx;
machine.int_sys_tick = image.int_sys_tick;

true
}
16 changes: 16 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,28 @@ fn main() {
machine.set_disk_set(image.disk_set);
cpu.registers().set_pc(image.address);
cpu.set_trace(false);
//machine.trace = true;

// Start the cpu
println!("{}", WELCOME);
let mut ref_time = std::time::Instant::now();
let mut reg_count = 0 as u64;

while !machine.quit {
cpu.execute_instruction(&mut machine);

reg_count += 1;
if reg_count % 1000 == 0 {
let now = std::time::Instant::now();
let elapsed = now - ref_time;
if elapsed.as_micros() > 1000 {
machine.tick_ms();
ref_time = ref_time - std::time::Duration::from_millis(1);
}
}

cpu.signal_interrupt(machine.int_raised);

if cpu.is_halted() {
println!("HALT instruction");
break;
Expand Down
116 changes: 114 additions & 2 deletions src/mbc2_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const RAM_SIZE: usize = 128*1024;

const OPCODE_NOP: u8 = 0xff;

const INT_RX_MASK: u8 = 1;
const INT_SYS_TICK_MASK: u8 = 2;

pub struct Mbc2Machine {
mem: [u8; RAM_SIZE],
disk_set: u8,
Expand All @@ -36,7 +39,18 @@ pub struct Mbc2Machine {
ggpu_a: u8,
ggpu_b: u8,

trace: bool,
pub int_raised: bool,
int_status: u8,
rx_done: bool,
pub int_rx: bool,
pub int_sys_tick: bool,
sys_tick_time: u8,

cpm_warm_boot: bool,
spp: bool,
spp_fd: bool,

pub trace: bool,
}

impl Mbc2Machine {
Expand All @@ -62,6 +76,17 @@ impl Mbc2Machine {
io_dir_b: 0,
ggpu_a: 0,
ggpu_b: 0,

int_raised: false,
int_status: 0,
rx_done: true,
int_rx: false,
int_sys_tick: false,
sys_tick_time: 0,

cpm_warm_boot: false,
spp: false,
spp_fd: false,

trace: false,
}
Expand All @@ -87,6 +112,17 @@ impl Mbc2Machine {
}
}
}

pub fn tick_ms(&mut self) {
if self.int_sys_tick {
// TODO
}
if self.int_rx && self.con.status() && self.rx_done {
self.int_status = self.int_status | INT_RX_MASK;
self.int_raised = true;
self.rx_done = false;
}
}
}

impl Machine for Mbc2Machine {
Expand Down Expand Up @@ -150,6 +186,31 @@ impl Machine for Mbc2Machine {
self.bank = value
}
},
0x0e => { // SETIRQ
// I/O DATA: D7 D6 D5 D4 D3 D2 D1 D0
// ---------------------------------------------------------
// X X X X X X X 0 Serial Rx IRQ not enabled
// X X X X X X X 1 Serial Rx IRQ enabled
// X X X X X X 0 X Systick IRQ not enabled
// X X X X X X 1 X Systick IRQ enabled
self.int_rx = value & INT_RX_MASK != 0;
self.int_sys_tick = value & INT_SYS_TICK_MASK != 0;
},
0x0f => { // SETTICK
if value > 0 {
self.sys_tick_time = value;
}
},
0x10 => { // SETOPT
self.cpm_warm_boot = value & 1 != 0;
},
0x11 => { // SETSPP
self.spp = true;
self.spp_fd = value & 1 != 0;
},
//0x12 => { // WRSPP
// // Todo: write value to a printer.out file.
//},
_ => implemented = false,
}

Expand Down Expand Up @@ -184,6 +245,10 @@ impl Machine for Mbc2Machine {
// NOTE 3: This is the only I/O that do not require any previous STORE OPCODE operation (for fast polling).
// NOTE 4: A "RX buffer empty" flag and a "Last Rx char was empty" flag are available in the SYSFLAG opcode
// to allow 8 bit I/O.
self.int_status = self.int_status & !INT_RX_MASK; // Reset the RX signal
self.int_raised = false;
self.rx_done = true;

if self.con.status() {
let mut ch = self.con.read();
if ch == 3 { // Control C
Expand Down Expand Up @@ -217,15 +282,20 @@ impl Machine for Mbc2Machine {
// X X X X X 1 X X Serial RX char available
// X X X X 0 X X X Previous RX char valid
// X X X X 1 X X X Previous RX char was a "buffer empty" flag
// X X X 0 X X X X CP/M warm boot message disabled
// X X X 1 X X X X CP/M warm boot message enabled
//
// NOTE: Currently only D0-D3 are used
// NOTE: Currently only D0-D4 are used
let mut sysflags: u8 = 0b0010;
if self.con.status() {
sysflags += 0b0100;
}
if self.last_rx_is_empty {
sysflags += 0b1000;
}
if self.cpm_warm_boot {
sysflags += 0b1_0000;
}
sysflags
},
0x84 => {
Expand Down Expand Up @@ -262,6 +332,40 @@ impl Machine for Mbc2Machine {
value
}
0x87 => 0, //SDMOUNT
0x88 => 255, // ATXBUFF, buffers are never full
0x89 => { // SYSIRQ
// I/O DATA: D7 D6 D5 D4 D3 D2 D1 D0
// ---------------------------------------------------------
// X X X X X X X 0 Serial Rx IRQ not set
// X X X X X X X 1 Serial Rx IRQ set
// X X X X X X 0 X Systick IRQ not set
// X X X X X X 1 X Systick IRQ set
let value = self.int_status;
self.int_status = 0;
value
},
0x90 => { // GETSPP
// I/O DATA: D7 D6 D5 D4 D3 D2 D1 D0
// ---------------------------------------------------------
// 0 0 0 0 0 0 0 0 SPP emulation disabled
// D7 D6 D5 D4 D3 0 0 1 SPP emulation enabled
//
// bit | SPP Status line
// ----------------------------------
// D0 | 1 (SPP emulation enabled)
// D1 | 0 (not used)
// D2 | 0 (not used)
// D3 | ACK (active Low) -> 0
// D4 | BUSY (active High) -> 0
// D5 | PAPEREND (active High) -> 0
// D6 | SELECT (active High) -> 1
// D7 | ERROR (active Low) -> 0
if self.spp {
0b0100_0001 // The pseudo printer is always ready
} else {
0
}
},
_ => {
implemented = false;
0
Expand Down Expand Up @@ -299,6 +403,11 @@ fn opcode_name(opcode: u8) -> &'static str {
0x0B => "SELSECT",
0x0C => "WRITESECT",
0x0D => "SETBANK",
0x0E => "SETIRQ",
0x0F => "SETTICK",
0x10 => "SETOPT",
0x11 => "SETSPP",
0x12 => "WRSPP",

0x80 => "USER KEY",
0x81 => "GPIOA R",
Expand All @@ -308,6 +417,9 @@ fn opcode_name(opcode: u8) -> &'static str {
0x85 => "ERRDISK",
0x86 => "READSECT",
0x87 => "SDMOUNT",
0x88 => "ATXBUFF",
0x89 => "SYSIRQ",
0x8A => "GETSPP",

0xFF => "NOP",
_ => "UNKNOWN"
Expand Down

0 comments on commit 372a017

Please sign in to comment.