pub const WOZMON_BASE: u64 = 0xff00;
pub const WOZMON_HELPERS_BASE: u64 = 0xfe00;
pub static WOZMON_PAGE: &[u8; 256] = &[
0xd8, 0x58, 0xa0, 0x7f, 0xa9, 0x1f, 0x8d, 0x03, 0x50, 0xa9, 0x0b, 0x8d, 0x02, 0x50, 0xea, 0xc9,
0xdf, 0xf0, 0x13, 0xc9, 0x9b, 0xf0, 0x03, 0xc8, 0x10, 0x0f, 0xa9, 0xdc, 0x20, 0xef, 0xff, 0xa9,
0x8d, 0x20, 0xef, 0xff, 0xa0, 0x01, 0x88, 0x30, 0xf6, 0x20, 0x00, 0xfe, 0xea, 0xea, 0xea, 0xea,
0xea, 0x99, 0x00, 0x02, 0x20, 0xef, 0xff, 0xc9, 0x8d, 0xd0, 0xd4, 0xa0, 0xff, 0xa9, 0x00, 0xaa,
0x0a, 0x85, 0x2b, 0xc8, 0xb9, 0x00, 0x02, 0xc9, 0x8d, 0xf0, 0xd4, 0xc9, 0xae, 0x90, 0xf4, 0xf0,
0xf0, 0xc9, 0xba, 0xf0, 0xeb, 0xc9, 0xd2, 0xf0, 0x3b, 0x86, 0x28, 0x86, 0x29, 0x84, 0x2a, 0xb9,
0x00, 0x02, 0x49, 0xb0, 0xc9, 0x0a, 0x90, 0x06, 0x69, 0x88, 0xc9, 0xfa, 0x90, 0x11, 0x0a, 0x0a,
0x0a, 0x0a, 0xa2, 0x04, 0x0a, 0x26, 0x28, 0x26, 0x29, 0xca, 0xd0, 0xf8, 0xc8, 0xd0, 0xe0, 0xc4,
0x2a, 0xf0, 0x97, 0x24, 0x2b, 0x50, 0x10, 0xa5, 0x28, 0x81, 0x26, 0xe6, 0x26, 0xd0, 0xb5, 0xe6,
0x27, 0x4c, 0x44, 0xff, 0x6c, 0x24, 0x00, 0x30, 0x2b, 0xa2, 0x02, 0xb5, 0x27, 0x95, 0x25, 0x95,
0x23, 0xca, 0xd0, 0xf7, 0xd0, 0x14, 0xa9, 0x8d, 0x20, 0xef, 0xff, 0xa5, 0x25, 0x20, 0xdc, 0xff,
0xa5, 0x24, 0x20, 0xdc, 0xff, 0xa9, 0xba, 0x20, 0xef, 0xff, 0xa9, 0xa0, 0x20, 0xef, 0xff, 0xa1,
0x24, 0x20, 0xdc, 0xff, 0x86, 0x2b, 0xa5, 0x24, 0xc5, 0x28, 0xa5, 0x25, 0xe5, 0x29, 0xb0, 0xc1,
0xe6, 0x24, 0xd0, 0x02, 0xe6, 0x25, 0xa5, 0x24, 0x29, 0x07, 0x10, 0xc8, 0x48, 0x4a, 0x4a, 0x4a,
0x4a, 0x20, 0xe5, 0xff, 0x68, 0x29, 0x0f, 0x09, 0xb0, 0xc9, 0xba, 0x90, 0x02, 0x69, 0x06, 0x4c,
0x0d, 0xfe, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00,
];
pub static WOZMON_HELPERS: &[u8; 30] = &[
0xad, 0x01, 0x50, 0x29, 0x08, 0xf0, 0xf9, 0xad, 0x00, 0x50, 0x09, 0x80, 0x60, 0x48, 0xad, 0x01,
0x50, 0x29, 0x10, 0xf0, 0xf9, 0x68, 0x29, 0x7f, 0x8d, 0x00, 0x50, 0x09, 0x80, 0x60,
];
pub static WOZMON_IMAGE: &[u8; 32_768] = &{
let mut image = [0xffu8; 32_768];
let mut i = 0;
while i < WOZMON_HELPERS.len() {
image[0x7e00 + i] = WOZMON_HELPERS[i];
i += 1;
}
let mut i = 0;
while i < WOZMON_PAGE.len() {
image[0x7f00 + i] = WOZMON_PAGE[i];
i += 1;
}
image
};
#[cfg(test)]
#[allow(dead_code)]
static MANUAL_ADDRESSES: &[u16] = &[
0xff00, 0xff01, 0xff02, 0xff04, 0xff07, 0xff09, 0xff0c, 0xff0f, 0xff11, 0xff13, 0xff15, 0xff17,
0xff18, 0xff1a, 0xff1c, 0xff1f, 0xff21, 0xff24, 0xff26, 0xff27, 0xff29, 0xff2c, 0xff2e, 0xff31,
0xff34, 0xff37, 0xff39, 0xff3b, 0xff3d, 0xff3f, 0xff40, 0xff41, 0xff43, 0xff44, 0xff47, 0xff49,
0xff4b, 0xff4d, 0xff4f, 0xff51, 0xff53, 0xff55, 0xff57, 0xff59, 0xff5b, 0xff5d, 0xff5f, 0xff62,
0xff64, 0xff66, 0xff68, 0xff6a, 0xff6c, 0xff6e, 0xff6f, 0xff70, 0xff71, 0xff72, 0xff74, 0xff75,
0xff77, 0xff79, 0xff7a, 0xff7c, 0xff7d, 0xff7f, 0xff81, 0xff83, 0xff85, 0xff87, 0xff89, 0xff8b,
0xff8d, 0xff8f, 0xff91, 0xff94, 0xff97, 0xff99, 0xff9b, 0xff9d, 0xff9f, 0xffa1, 0xffa2, 0xffa4,
0xffa6, 0xffa8, 0xffab, 0xffad, 0xffb0, 0xffb2, 0xffb5, 0xffb7, 0xffba, 0xffbc, 0xffbf, 0xffc1,
0xffc4, 0xffc6, 0xffc8, 0xffca, 0xffcc, 0xffce, 0xffd0, 0xffd2, 0xffd4, 0xffd6, 0xffd8, 0xffda,
0xffdc, 0xffdd, 0xffde, 0xffdf, 0xffe0, 0xffe1, 0xffe4, 0xffe5, 0xffe7, 0xffe9, 0xffeb, 0xffed,
0xffef, 0xfff2, 0xfff4, 0xfff7,
];
#[cfg(test)]
static ORIGINAL_PAGE: &[u8; 256] = &[
0xd8, 0x58, 0xa0, 0x7f, 0x8c, 0x12, 0xd0, 0xa9, 0xa7, 0x8d, 0x11, 0xd0, 0x8d, 0x13, 0xd0, 0xc9,
0xdf, 0xf0, 0x13, 0xc9, 0x9b, 0xf0, 0x03, 0xc8, 0x10, 0x0f, 0xa9, 0xdc, 0x20, 0xef, 0xff, 0xa9,
0x8d, 0x20, 0xef, 0xff, 0xa0, 0x01, 0x88, 0x30, 0xf6, 0xad, 0x11, 0xd0, 0x10, 0xfb, 0xad, 0x10,
0xd0, 0x99, 0x00, 0x02, 0x20, 0xef, 0xff, 0xc9, 0x8d, 0xd0, 0xd4, 0xa0, 0xff, 0xa9, 0x00, 0xaa,
0x0a, 0x85, 0x2b, 0xc8, 0xb9, 0x00, 0x02, 0xc9, 0x8d, 0xf0, 0xd4, 0xc9, 0xae, 0x90, 0xf4, 0xf0,
0xf0, 0xc9, 0xba, 0xf0, 0xeb, 0xc9, 0xd2, 0xf0, 0x3b, 0x86, 0x28, 0x86, 0x29, 0x84, 0x2a, 0xb9,
0x00, 0x02, 0x49, 0xb0, 0xc9, 0x0a, 0x90, 0x06, 0x69, 0x88, 0xc9, 0xfa, 0x90, 0x11, 0x0a, 0x0a,
0x0a, 0x0a, 0xa2, 0x04, 0x0a, 0x26, 0x28, 0x26, 0x29, 0xca, 0xd0, 0xf8, 0xc8, 0xd0, 0xe0, 0xc4,
0x2a, 0xf0, 0x97, 0x24, 0x2b, 0x50, 0x10, 0xa5, 0x28, 0x81, 0x26, 0xe6, 0x26, 0xd0, 0xb5, 0xe6,
0x27, 0x4c, 0x44, 0xff, 0x6c, 0x24, 0x00, 0x30, 0x2b, 0xa2, 0x02, 0xb5, 0x27, 0x95, 0x25, 0x95,
0x23, 0xca, 0xd0, 0xf7, 0xd0, 0x14, 0xa9, 0x8d, 0x20, 0xef, 0xff, 0xa5, 0x25, 0x20, 0xdc, 0xff,
0xa5, 0x24, 0x20, 0xdc, 0xff, 0xa9, 0xba, 0x20, 0xef, 0xff, 0xa9, 0xa0, 0x20, 0xef, 0xff, 0xa1,
0x24, 0x20, 0xdc, 0xff, 0x86, 0x2b, 0xa5, 0x24, 0xc5, 0x28, 0xa5, 0x25, 0xe5, 0x29, 0xb0, 0xc1,
0xe6, 0x24, 0xd0, 0x02, 0xe6, 0x25, 0xa5, 0x24, 0x29, 0x07, 0x10, 0xc8, 0x48, 0x4a, 0x4a, 0x4a,
0x4a, 0x20, 0xe5, 0xff, 0x68, 0x29, 0x0f, 0x09, 0xb0, 0xc9, 0xba, 0x90, 0x02, 0x69, 0x06, 0x2c,
0x12, 0xd0, 0x30, 0xfb, 0x8d, 0x12, 0xd0, 0x60, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00,
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_reset_vector_is_the_manuals_own() {
assert_eq!(WOZMON_PAGE[0xfc], 0x00);
assert_eq!(WOZMON_PAGE[0xfd], 0xff);
assert_eq!(&WOZMON_PAGE[0xfa..0xfc], &[0x00, 0x0f], "NMI, as printed");
assert_eq!(&WOZMON_PAGE[0xfe..], &[0x00, 0x00], "IRQ, as printed");
assert_eq!(WOZMON_IMAGE[0x7ffc], 0x00);
assert_eq!(WOZMON_IMAGE[0x7ffd], 0xff);
assert_eq!(&WOZMON_IMAGE[0x7e00..0x7e1e], &WOZMON_HELPERS[..]);
}
#[test]
fn only_the_pia_blocks_were_replaced() {
const PATCHED: [core::ops::Range<usize>; 3] = [0x02..0x0f, 0x29..0x31, 0xef..0xf8];
for (i, (&now, &then)) in WOZMON_PAGE.iter().zip(ORIGINAL_PAGE).enumerate() {
if PATCHED.iter().any(|r| r.contains(&i)) {
continue;
}
assert_eq!(now, then, "${:04x} is not the manual's byte", 0xff00 + i);
}
assert_eq!(
&WOZMON_PAGE[0x02..0x0e],
&[
0xa0, 0x7f, 0xa9, 0x1f, 0x8d, 0x03, 0x50, 0xa9, 0x0b, 0x8d, 0x02, 0x50
]
);
assert_eq!(WOZMON_PAGE[0x0e], 0xea, "one byte of padding");
assert_eq!(&WOZMON_PAGE[0x29..0x2c], &[0x20, 0x00, 0xfe], "JSR GETKEY");
assert_eq!(&WOZMON_PAGE[0xef..0xf2], &[0x4c, 0x0d, 0xfe], "JMP PUTC");
}
#[cfg(feature = "cpu-mos6502")]
#[test]
fn the_listing_walks_the_manuals_own_addresses() {
use crate::cpu::mos6502::disasm::disassemble_run;
let run = disassemble_run(WOZMON_BASE as u16, MANUAL_ADDRESSES.len(), |addr| {
ORIGINAL_PAGE
.get(usize::from(addr.wrapping_sub(WOZMON_BASE as u16)))
.copied()
});
let decoded: alloc::vec::Vec<u16> = run
.iter()
.inspect(|d| assert!(!d.truncated, "truncated decode at ${:04x}", d.pc))
.map(|d| d.pc)
.collect();
assert_eq!(decoded, MANUAL_ADDRESSES);
assert_eq!(ORIGINAL_PAGE[0xf7], 0x60);
assert_eq!(&ORIGINAL_PAGE[0xf8..0xfa], &[0x00, 0x00]);
}
#[test]
fn no_65c02_only_opcode_appears() {
for (i, &byte) in WOZMON_PAGE.iter().enumerate().take(0xf8) {
assert_ne!(byte, 0x3a, "DEC A at ${:04x}", 0xff00 + i);
}
assert!(!WOZMON_HELPERS.contains(&0x3a));
}
}