diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..4644b2a
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/trevors_chip8_toy.iml b/.idea/trevors_chip8_toy.iml
new file mode 100644
index 0000000..ca38ecd
--- /dev/null
+++ b/.idea/trevors_chip8_toy.iml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 231b130..7580cc3 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -9,13 +9,10 @@
-
-
-
-
-
-
-
+
+
+
+
@@ -59,14 +56,16 @@
"Cargo.Build `Test chip8::util::test::ubln`.executor": "Run",
"Cargo.Build `Test chip8::video::test::poke_byte_test`.executor": "Run",
"Cargo.Build `Test chip8::video::test::poke_byte`.executor": "Run",
+ "Cargo.Build `Test chip8::video::test::scroll_down_1_row_test`.executor": "Run",
"Cargo.Build `Test computer::test`.executor": "Run",
"Cargo.Build `Test instructions::test (1)`.executor": "Run",
"Cargo.Build `Test instructions::test`.executor": "Run",
+ "Cargo.Build `Test video::test`.executor": "Run",
"Cargo.Build gemma.executor": "Run",
"Cargo.Run emmagui.executor": "Run",
"Cargo.Run gemmaegui.executor": "Debug",
"Cargo.Run gemmaegui_viewer.executor": "Debug",
- "Cargo.Run gemmaimgui.executor": "Run",
+ "Cargo.Run gemmaimgui.executor": "Debug",
"Cargo.Run trevors_chip8_toy.executor": "Debug",
"Cargo.Test chip8::computer::test::cls_test.executor": "Run",
"Cargo.Test chip8::computer::test::decoder_test_valid_instructions.executor": "Run",
@@ -91,12 +90,16 @@
"Cargo.Test chip8::video::test::poke_byte.executor": "Run",
"Cargo.Test chip8::video::test::poke_byte_test.executor": "Run",
"Cargo.Test chip8::video::test::poke_sprite_test.executor": "Debug",
+ "Cargo.Test chip8::video::test::reset_test.executor": "Run",
+ "Cargo.Test chip8::video::test::scroll_down_10_row_test.executor": "Run",
+ "Cargo.Test chip8::video::test::scroll_down_1_row_test.executor": "Run",
+ "Cargo.Test chip8::video::test::write_checkboard.executor": "Run",
"Cargo.Test computer::test.executor": "Debug",
"Cargo.Test instructions::test (1).executor": "Debug",
"Cargo.Test instructions::test.executor": "Debug",
"Cargo.Test sound_timer::test.executor": "Run",
"Cargo.Test util::test.executor": "Run",
- "Cargo.Test video::test.executor": "Coverage",
+ "Cargo.Test video::test.executor": "Debug",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.rust.reset.selective.auto.import": "true",
"git-widget-placeholder": "master",
@@ -107,7 +110,7 @@
"org.rust.cargo.project.model.PROJECT_DISCOVERY": "true",
"org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon": "",
"org.rust.first.attach.projects": "true",
- "settings.editor.selected.configurable": "language.rust.build.tool.cargo"
+ "settings.editor.selected.configurable": "advanced.settings"
},
"keyToStringList": {
"com.intellij.ide.scratch.ScratchImplUtil$2/New Scratch File": [
@@ -124,41 +127,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -176,8 +145,59 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -195,11 +215,11 @@
-
-
-
-
+
+
+
+
@@ -237,6 +257,8 @@
+
+
diff --git a/gemma/src/chip8/computer.rs b/gemma/src/chip8/computer.rs
index 4c9a8b5..3ee4de5 100644
--- a/gemma/src/chip8/computer.rs
+++ b/gemma/src/chip8/computer.rs
@@ -44,6 +44,7 @@ impl Chip8Computer {
self.registers.reset();
self.delay_timer.reset();
self.sound_timer.reset();
+ self.stack.reset();
}
pub fn dump_keypad_to_string(&self) -> String {
@@ -55,7 +56,7 @@ impl Chip8Computer {
}
pub fn dump_video_to_string(&self) -> String {
- self.video_memory.format_as_string()
+ self.clone().video_memory.format_as_string()
}
pub fn new_with_program(new_program: Vec) -> Self {
diff --git a/gemma/src/chip8/computer_manager.rs b/gemma/src/chip8/computer_manager.rs
index 3eb7e49..9cba622 100644
--- a/gemma/src/chip8/computer_manager.rs
+++ b/gemma/src/chip8/computer_manager.rs
@@ -5,14 +5,12 @@ use std::time::{Duration, Instant};
use crate::chip8::computer::Chip8Computer;
use crate::chip8::cpu_states::Chip8CpuStates;
use crate::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;
-
pub enum ManagerDumpables {
Video,
Registers,
Keyboard
}
-
pub struct Chip8ComputerManager {
core_should_run: bool,
one_step: bool,
@@ -76,7 +74,7 @@ impl Chip8ComputerManager {
WaitingForInstruction => {
self.core_last_cycle_start = Instant::now();
self.computer.step_system();
- println!("SYSTEM STEP");
+ // println!("SYSTEM STEP");
}
_ => {}
}
diff --git a/gemma/src/chip8/instructions.rs b/gemma/src/chip8/instructions.rs
index 8f960b8..67e8d3b 100644
--- a/gemma/src/chip8/instructions.rs
+++ b/gemma/src/chip8/instructions.rs
@@ -880,25 +880,27 @@ impl Chip8CpuInstructions {
}
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
Chip8CpuInstructions::SDN(x) => {
- println!("SCROLLING DOWN {x} LINES");
+ input.video_memory.scroll_down(*x as i32);
}
Chip8CpuInstructions::SRT => {
- println!("SCROLING RIGHT 4 LINES");
+ input.video_memory.scroll_right();
}
Chip8CpuInstructions::SLF => {
- println!("SCROLLING LEFT 4 LINES");
+ input.video_memory.scroll_left();
}
Chip8CpuInstructions::DIS => {
- println!("DISABLE VIDEO MODE");
+ input.video_memory.set_lowres();
}
Chip8CpuInstructions::ENA => {
- println!("ENABLE VIDEO MODE");
+ input.video_memory.set_highres();
}
Chip8CpuInstructions::EXIT => {
println!("EXIT INTERPRETER");
}
Chip8CpuInstructions::LDF2(x) => {
println!("POINTING TO FONT AT {x:02x}");
+ // base = 0x100 + 0x0A*X
+ input.registers.poke_i(0x100 + (0xA * x) as u16);
}
Chip8CpuInstructions::STR(x) => {
println!("STORING FROM RPL FOR {x}");
@@ -922,7 +924,7 @@ impl Chip8CpuInstructions {
#[cfg(test)]
mod test {
- use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_9};
+ use crate::chip8::system_memory::{*};
use crate::constants::CHIP8_VIDEO_MEMORY;
use super::*;
diff --git a/gemma/src/chip8/stack.rs b/gemma/src/chip8/stack.rs
index 43259c8..70d2588 100644
--- a/gemma/src/chip8/stack.rs
+++ b/gemma/src/chip8/stack.rs
@@ -34,6 +34,10 @@ impl Chip8Stack {
items: vec![]
}
}
+
+ pub fn reset(&mut self) {
+ self.items = vec![]
+ }
}
#[cfg(test)]
diff --git a/gemma/src/chip8/system_memory.rs b/gemma/src/chip8/system_memory.rs
index 2b1a0c7..3c1f809 100644
--- a/gemma/src/chip8/system_memory.rs
+++ b/gemma/src/chip8/system_memory.rs
@@ -1,24 +1,6 @@
use log::{trace};
-use crate::constants::{CHIP8_MEMORY_SIZE};
-
-pub const CHIP8_PROGRAM_LOAD_OFFSET: i32 = 0x200;
-pub const CHIP8FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];
-pub const CHIP8FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];
-pub const CHIP8FONT_2: [u8; 5] = [0xF0, 0x10, 0xF0, 0x80, 0xF0];
-pub const CHIP8FONT_3: [u8; 5] = [0xF0, 0x10, 0xF0, 0x10, 0xF0];
-pub const CHIP8FONT_4: [u8; 5] = [0x90, 0x90, 0xF0, 0x10, 0x10];
-pub const CHIP8FONT_5: [u8; 5] = [0xF0, 0x80, 0xF0, 0x10, 0xF0];
-pub const CHIP8FONT_6: [u8; 5] = [0xF0, 0x80, 0xF0, 0x90, 0xF0];
-pub const CHIP8FONT_7: [u8; 5] = [0xF0, 0x10, 0x20, 0x40, 0x40];
-pub const CHIP8FONT_8: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0xF0];
-pub const CHIP8FONT_9: [u8; 5] = [0xF0, 0x90, 0xF0, 0x10, 0xF0];
-pub const CHIP8FONT_A: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0x90];
-pub const CHIP8FONT_B: [u8; 5] = [0xE0, 0x90, 0xE0, 0x90, 0xE0];
-pub const CHIP8FONT_C: [u8; 5] = [0xF0, 0x80, 0x80, 0x80, 0xF0];
-pub const CHIP8FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];
-pub const CHIP8FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];
-pub const CHIP8FONT_F: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0x80];
+use crate::constants::*;
#[derive(Clone, Copy)]
pub struct Chip8SystemMemory {
@@ -33,6 +15,7 @@ impl Default for Chip8SystemMemory {
};
x.load_fonts_to_memory();
+ x.load_schip_fonts_to_memory();
x
}
}
@@ -87,6 +70,22 @@ impl Chip8SystemMemory {
}
}
}
+
+ pub fn load_schip_fonts_to_memory(&mut self) {
+ let all_font_characters = [
+ SCHIPFONT_0, SCHIPFONT_1, SCHIPFONT_2, SCHIPFONT_3,
+ SCHIPFONT_4, SCHIPFONT_5, SCHIPFONT_6, SCHIPFONT_7,
+ SCHIPFONT_8, SCHIPFONT_9, SCHIPFONT_A, SCHIPFONT_B,
+ SCHIPFONT_C, SCHIPFONT_D, SCHIPFONT_E, SCHIPFONT_F
+ ];
+ for (font_index, current_font) in all_font_characters.iter().enumerate() {
+ let base_offset = 0x100;
+ for font_mem_offset in 0..=4 {
+ let real_offset = base_offset + font_index * 0x10 + font_mem_offset;
+ self.poke(real_offset as u16, current_font[font_mem_offset]);
+ }
+ }
+ }
}
#[cfg(test)]
diff --git a/gemma/src/chip8/video.rs b/gemma/src/chip8/video.rs
index 7d458c6..5bff2eb 100644
--- a/gemma/src/chip8/video.rs
+++ b/gemma/src/chip8/video.rs
@@ -1,18 +1,18 @@
use log::{debug};
use crate::chip8::video::Chip8VideoModes::{HighRes, LowRes};
-use crate::constants::{CHIP8_VIDEO_MEMORY, CHIP8_VIDEO_WIDTH, SCHIP_VIDE_MEMORY};
+use crate::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_MEMORY, CHIP8_VIDEO_WIDTH, SCHIP_VIDE_MEMORY, SCHIP_VIDEO_HEIGHT, SCHIP_VIDEO_WIDTH};
#[derive(Clone, Copy)]
enum Chip8VideoModes {
LowRes,
- HighRes
+ HighRes,
}
-#[derive(Clone, Copy)]
+#[derive(Clone)]
pub struct Chip8Video {
- memory: [bool; CHIP8_VIDEO_MEMORY],
+ memory: Vec,
pub has_frame_changed: bool,
- current_res: Chip8VideoModes
+ current_res: Chip8VideoModes,
}
impl Chip8Video {
@@ -21,13 +21,13 @@ impl Chip8Video {
self.start_frame();
}
-
pub fn is_highres(&self) -> bool {
matches!(self.current_res, HighRes)
}
pub fn set_highres(&mut self) {
- self.current_res = HighRes
+ self.current_res = HighRes;
+ self.cls();
}
pub fn set_lowres(&mut self) {
@@ -39,14 +39,17 @@ impl Chip8Video {
}
pub fn cls(&mut self) {
- match self.current_res {
+ self.memory.clear();
+ let num_loops = match self.current_res {
LowRes => {
- self.memory = [false; CHIP8_VIDEO_MEMORY];
+ CHIP8_VIDEO_MEMORY
}
HighRes => {
-
- // self.memory = [0u8; ]
+ SCHIP_VIDE_MEMORY
}
+ };
+ for i in 0..num_loops {
+ self.memory.push(false);
}
}
@@ -54,15 +57,15 @@ impl Chip8Video {
self.has_frame_changed = false;
}
- pub fn new(initial_configuration: [bool; CHIP8_VIDEO_MEMORY]) -> Self {
+ pub fn new(initial_configuration: Box>) -> Self {
Self {
- memory: initial_configuration,
+ memory: *initial_configuration,
has_frame_changed: false,
current_res: Chip8VideoModes::LowRes,
}
}
- pub fn peek(self, address: u16) -> bool {
+ pub fn peek(&self, address: u16) -> bool {
let loop_value: u16 = if self.is_highres() {
SCHIP_VIDE_MEMORY as u16
} else {
@@ -75,12 +78,7 @@ impl Chip8Video {
}
pub fn poke(&mut self, address: u16, new_value: bool) {
- // println!("OFFSET: {address} - POKING {new_value}");
- let loop_value: u16 = if self.is_highres() {
- SCHIP_VIDE_MEMORY as u16
- } else {
- CHIP8_VIDEO_MEMORY as u16
- };
+ let loop_value: u16 = self.get_memory_size() as u16;
// Loop the address
let effective_address = address % loop_value;
@@ -93,6 +91,7 @@ impl Chip8Video {
};
}
+ // println!("VIDEO POKE COMPLETE WITH {effective_address} SET TO {xored_value}");
self.memory[effective_address as usize] = xored_value;
}
@@ -110,13 +109,15 @@ impl Chip8Video {
}
}
- pub fn format_as_string(self) -> String {
+ pub fn format_as_string(&self) -> String {
+ let (width, height) = self.get_resolution();
+ println!("FORMATTING {width}x{height}");
let mut output = String::new();
- for row in 0..32 {
- for column in 0..64 {
- let data_offset = row * 64 + column;
- debug!("Rendering {data_offset} with value {}", self.memory[data_offset]);
- if self.memory[data_offset] {
+ for row in 0..height {
+ for column in 0..width {
+ let data_offset = row * width + column;
+ debug!("Rendering {data_offset} with value {}", self.memory[data_offset as usize]);
+ if self.memory[data_offset as usize] {
output += "*"
} else {
output += " "
@@ -124,39 +125,127 @@ impl Chip8Video {
}
output += "\n";
}
-
output
}
+ pub fn get_resolution(&self) -> (i32, i32) {
+ if self.is_highres() {
+ (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT)
+ } else {
+ (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)
+ }
+ }
+
+ fn get_memory_size(&self) -> i32 {
+ let w = self.get_resolution();
+ w.1 * w.0
+ }
+
pub fn tick(&mut self) {
self.has_frame_changed = false;
}
+
+ pub fn scroll_right(&mut self) {
+ let (width, height) = self.get_resolution();
+
+ for current_row in 0..height {
+ let row_offset: usize = (current_row * width) as usize;
+ for current_column in (0..(width - 4)).rev() {
+ let source_address = row_offset + current_column as usize;
+ let target_address = source_address + 4;
+ self.memory[target_address] = self.memory[source_address];
+ }
+ self.memory[row_offset..row_offset + 4].fill(false);
+ }
+ }
+
+ pub fn scroll_left(&mut self) {
+ let (width, height) = self.get_resolution();
+
+ for current_row in 0..height {
+ let row_offset = current_row * width;
+ for current_column in (0..width - 4) {
+ let source: usize = (row_offset + current_column) as usize;
+ let target: usize = source + 4;
+ self.memory[target] = self.memory[source];
+ }
+
+ let clear_start: usize = (row_offset + width - 4) as usize;
+ let clear_end: usize = clear_start + 4;
+
+ self.memory[clear_start..clear_end].fill(false);
+ }
+ }
+
+ pub fn scroll_down(&mut self, how_far: i32) {
+ let (width, height) = self.get_resolution();
+ let row_shift = how_far * width;
+
+ let max_source_row = height - how_far;
+ for current_source_row in (0..max_source_row).rev() {
+ let current_source_offset = current_source_row * width;
+ for current_source_column in (0..width) {
+ let base_offset: usize = (current_source_offset + current_source_column) as usize;
+ let extended_offset: usize = base_offset + row_shift as usize;
+ self.memory[extended_offset] = self.memory[base_offset];
+ };
+ }
+
+ // Clear the new top rows after shifting
+ let clear_end = (how_far * width) as usize;
+ self.memory[0..clear_end].fill(false);
+ }
}
impl Default for Chip8Video {
fn default() -> Self {
- Chip8Video { memory: [false; CHIP8_VIDEO_MEMORY], has_frame_changed: false, current_res: Chip8VideoModes::LowRes }
+ let mut mem = vec![];
+ for _ in 0..CHIP8_VIDEO_MEMORY { mem.push(false); }
+ Chip8Video { memory: mem, has_frame_changed: false, current_res: Chip8VideoModes::LowRes }
}
}
#[cfg(test)]
mod test {
use std::io::Read;
- use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7};
+ use crate::constants::*;
use super::*;
const TEST_OUTPUT_SAMPLE_DIR: &str = "../resources/test/";
- fn build_checkerboard() -> Chip8Video {
+ fn real_build_checkboard(in_hd: bool) -> Chip8Video {
let mut r = Chip8Video::default();
+ let (width, height) = if in_hd {
+ r.set_highres();
+ (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT)
+ } else {
+ (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)
+ };
- for i in 0..CHIP8_VIDEO_MEMORY {
- r.poke(i as u16, i % 2 == 0);
+ println!("BUILDING BOARD WITH SIZE OF {width}x{height}");
+
+ for row in 0..height {
+ let data_offset = row * width;
+
+ for col in 0..width {
+ // XOR row and column indices to alternate in a checkerboard pattern
+ let to_poke = (row % 2) ^ (col % 2) == 1;
+ let local_offset: u16 = (data_offset + col) as u16;
+
+ r.poke(local_offset, to_poke);
+ }
}
-
r
}
+ fn build_checkboard_hd() -> Chip8Video {
+ real_build_checkboard(true)
+ }
+
+ fn build_checkerboard() -> Chip8Video {
+ real_build_checkboard(false)
+ }
+
fn read_test_result(suffix: &str) -> String {
std::fs::read_to_string(TEST_OUTPUT_SAMPLE_DIR.to_owned() + suffix)
.unwrap()
@@ -178,36 +267,7 @@ mod test {
}
#[test]
- fn string_test_1() {
- let mut x = Chip8Video::default();
- let mut working_string = String::new();
- for i in 0..32 {
- working_string += &*(" ".repeat(64) + "\n");
- }
-
- assert_eq!(working_string, x.format_as_string());
-
- let mut working_string = String::new();
- // set a checkerboard...
- for cb_row in 0..32 {
- for cb_col in 0..64 {
- let data_offset = cb_row * 64 + cb_col;
- if data_offset % 2 == 0 {
- x.poke(data_offset, true);
- working_string += "*";
- } else {
- x.poke(data_offset, false);
- working_string += " ";
- }
- }
- working_string += "\n";
- }
-
- assert_eq!(working_string, x.format_as_string());
- }
-
- #[test]
- fn set_initial_memory() {
+ fn set_initial_memory_sd() {
let mut x = Chip8Video::default();
// let mut initial_memory = [false; CHIP8_VIDEO_MEMORY];
let mut ws = String::new();
@@ -242,10 +302,10 @@ mod test {
#[test]
fn poke_2byte_test() {
- let to_poke: [u8; 2] = [
- 0b11001111,
- 0b00111100
- ];
+ let to_poke: [u8; 2] = [
+ 0b11001111,
+ 0b00111100
+ ];
let mut x = Chip8Video::default();
x.poke_2byte(0x00, to_poke);
@@ -265,22 +325,18 @@ mod test {
}
#[test]
- fn cls() {
- let mut initial_memory = [false; CHIP8_VIDEO_MEMORY];
+ fn cls_stddef() {
+ let width = 64;
+ let height = 32;
+ let mut initial_memory = vec![];
let mut ws = String::new();
-
+ let mut set_x = Chip8Video::new(initial_memory.into());
for cbr in 0..32 {
- for cbc in 0..64 {
- let dof = cbr * 64 + cbc;
- if (dof as i32 % 2) == 0 {
- initial_memory[dof] = true;
- }
- ws += " ";
- }
+ ws += &*" ".repeat(width);
ws += "\n";
}
- let mut set_x = Chip8Video::new(initial_memory);
set_x.cls();
+
assert_eq!(set_x.format_as_string(), ws);
}
@@ -289,16 +345,16 @@ mod test {
let to_poke = 0b10101010;
let mut v = Chip8Video::default();
v.poke_byte(0x00, to_poke);
- assert!(v.peek(0x00));
- assert!(!v.peek(0x01));
- assert!(v.peek(0x02));
- assert!(!v.peek(0x03));
- assert!(v.peek(0x04));
- assert!(!v.peek(0x05));
- assert!(v.peek(0x06));
- assert!(!v.peek(0x07));
+ assert!(v.clone().peek(0x00));
+ assert!(!v.clone().peek(0x01));
+ assert!(v.clone().peek(0x02));
+ assert!(!v.clone().peek(0x03));
+ assert!(v.clone().peek(0x04));
+ assert!(!v.clone().peek(0x05));
+ assert!(v.clone().peek(0x06));
+ assert!(!v.clone().peek(0x07));
for i in 0x8..CHIP8_VIDEO_MEMORY {
- assert!(!v.peek(i as u16));
+ assert!(!v.clone().peek(i as u16));
}
}
@@ -329,7 +385,7 @@ mod test {
assert!(v.clone().peek(0x47));
// row 3 column 1
- assert!(!v.peek(0xC0));
+ assert!(!v.clone().peek(0xC0));
assert!(v.clone().peek(0xC1));
assert!(!v.clone().peek(0xC2));
assert!(v.clone().peek(0xC3));
@@ -360,7 +416,7 @@ mod test {
}
let test_offset = (x_offset * 64 + y_offset) as u16;
- assert!(!v.peek(test_offset));
+ assert!(!v.clone().peek(test_offset));
assert!(!v.clone().peek(test_offset + 1));
assert!(!v.clone().peek(test_offset + 2));
assert!(!v.clone().peek(test_offset + 3));
@@ -394,8 +450,7 @@ mod test {
#[test]
fn write_checkboard() {
let mut v = build_checkerboard();
-
- assert_eq!(v.format_as_string(), read_test_result("test_video_write_checkerboard.asc"));
+ assert_eq!(v.clone().format_as_string(), read_test_result("test_video_write_checkerboard.asc"));
}
#[test]
@@ -426,16 +481,7 @@ mod test {
#[test]
fn reset_test() {
- let mut x = Chip8Video::default();
-
- let to_draw = [CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7];
- for (index, sprite) in to_draw.iter().enumerate() {
- let data_base_offset = index * 0x8;
- for (index, offset) in (0..=0x100).step_by(0x40).enumerate() {
- x.poke_byte((data_base_offset + offset) as u16, sprite[index]);
- }
- }
-
+ let mut x = build_checkerboard();
x.reset();
assert_eq!(x.format_as_string(), read_test_result("test_reset_clears_video.asc"));
}
@@ -453,7 +499,7 @@ mod test {
// it becomes unset and theres a frame changed
assert_eq!(false, x.peek(0x00));
- assert_eq!(true, x.has_frame_changed);
+ assert_eq!(true, x.clone().has_frame_changed);
}
#[test]
@@ -481,5 +527,70 @@ mod test {
assert!(true);
}
+ #[test]
+ fn scroll_down_1_row_test() {
+ let mut x = build_checkerboard();
+ x.scroll_down(1);
+ assert_eq!(read_test_result("test_video_scroll_down_1.asc"), x.format_as_string());
+ }
+ #[test]
+ fn scroll_down_10_row_test() {
+ let mut x = build_checkerboard();
+ x.scroll_down(10);
+ assert_eq!(read_test_result("test_video_scroll_down_10.asc"), x.format_as_string());
+ }
+
+ #[test]
+ fn high_res_has_right_resolution() {
+ let mut x = build_checkboard_hd();
+ println!("[{}]", x.format_as_string());
+ assert_eq!(read_test_result("test_video_highdef.asc"), x.format_as_string());
+ }
+
+ #[test]
+ fn scroll_down_1_row_test_schip() {
+ let mut x = build_checkboard_hd();
+ x.scroll_down(1);
+
+ println!("[{}]", x.format_as_string());
+ println!("[{}]", read_test_result("test_scroll_down_1_hd.asc"));
+
+ assert_eq!(read_test_result("test_scroll_down_1_hd.asc"), x.format_as_string());
+ }
+
+ #[test]
+ fn scroll_down_10_row_test_schip() {
+ let mut x = build_checkboard_hd();
+ x.scroll_down(10);
+ assert_eq!(read_test_result("test_scroll_down_10_hd.asc"), x.format_as_string());
+ }
+
+ #[test]
+ fn scroll_left_4_row_test_std_def() {
+ let mut x = build_checkerboard();
+ x.scroll_left();
+ assert_eq!(read_test_result("test_scroll_left_4.asc"), x.format_as_string());
+ }
+
+ #[test]
+ fn scroll_left_4_row_test_high_def() {
+ let mut x = build_checkboard_hd();
+ x.scroll_left();
+ assert_eq!(read_test_result("test_scroll_left_4_hd.asc"), x.format_as_string());
+ }
+
+ #[test]
+ fn scroll_right_4_row_test_std_def() {
+ let mut x = build_checkerboard();
+ x.scroll_right();
+ assert_eq!(read_test_result("test_scroll_right_4.asc"), x.format_as_string());
+ }
+
+ #[test]
+ fn scroll_right_4_row_test_high_def() {
+ let mut x = build_checkboard_hd();
+ x.scroll_right();
+ assert_eq!(read_test_result("test_scroll_right_4_hd.asc"), x.format_as_string());
+ }
}
diff --git a/gemma/src/constants.rs b/gemma/src/constants.rs
index 65a2c3a..ddb8c2d 100644
--- a/gemma/src/constants.rs
+++ b/gemma/src/constants.rs
@@ -63,3 +63,65 @@ pub const INST_ORY: &str = "ORY";
+pub const CHIP8_PROGRAM_LOAD_OFFSET: i32 = 0x200;
+pub const CHIP8FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];
+pub const CHIP8FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];
+pub const CHIP8FONT_2: [u8; 5] = [0xF0, 0x10, 0xF0, 0x80, 0xF0];
+pub const CHIP8FONT_3: [u8; 5] = [0xF0, 0x10, 0xF0, 0x10, 0xF0];
+pub const CHIP8FONT_4: [u8; 5] = [0x90, 0x90, 0xF0, 0x10, 0x10];
+pub const CHIP8FONT_5: [u8; 5] = [0xF0, 0x80, 0xF0, 0x10, 0xF0];
+pub const CHIP8FONT_6: [u8; 5] = [0xF0, 0x80, 0xF0, 0x90, 0xF0];
+pub const CHIP8FONT_7: [u8; 5] = [0xF0, 0x10, 0x20, 0x40, 0x40];
+pub const CHIP8FONT_8: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0xF0];
+pub const CHIP8FONT_9: [u8; 5] = [0xF0, 0x90, 0xF0, 0x10, 0xF0];
+pub const CHIP8FONT_A: [u8; 5] = [0xF0, 0x90, 0xF0, 0x90, 0x90];
+pub const CHIP8FONT_B: [u8; 5] = [0xE0, 0x90, 0xE0, 0x90, 0xE0];
+pub const CHIP8FONT_C: [u8; 5] = [0xF0, 0x80, 0x80, 0x80, 0xF0];
+pub const CHIP8FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];
+pub const CHIP8FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];
+pub const CHIP8FONT_F: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0x80];
+
+
+pub const SCHIPFONT_0: [u8; 0x10] = [0xF0, 0xFC, 0xFE, 0xFF, 0xF3, 0xE1, 0xE0, 0xE0,
+ 0xE0, 0xE0, 0xE0, 0xF1, 0xFB, 0xFF, 0xFE, 0x7C];
+pub const SCHIPFONT_1: [u8; 0x10] = [0x18, 0x3C, 0x7E, 0xFF, 0xF7, 0xE3, 0xC1, 0xC0,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0
+];
+pub const SCHIPFONT_2: [u8; 0x10] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC7, 0xC3, 0xC0, 0xE0,
+ 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0xFF, 0xFF
+];
+pub const SCHIPFONT_3: [u8; 0x10] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC7, 0xC3, 0xC0, 0xE0,
+0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0xFF, 0xFF];
+pub const SCHIPFONT_4: [u8; 0x10] = [0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xF7, 0xF3, 0xF1,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0];
+pub const SCHIPFONT_5: [u8; 0x10] = [0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x3F, 0x7F, 0x7F,
+ 0x01, 0x01, 0xC1, 0xE3, 0xFF, 0xFE, 0xFC, 0x78];
+pub const SCHIPFONT_6: [u8; 0x10] = [0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
+ 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
+];
+pub const SCHIPFONT_7: [u8; 0x10] = [0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF0, 0x78, 0x3C,
+ 0x1E, 0x0F, 0x07, 0x03, 0x01, 0x01, 0x01, 0x01
+];
+pub const SCHIPFONT_8: [u8; 0x10] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF,
+ 0x7E, 0xFE, 0xC3, 0xC3, 0xFF, 0xFF, 0xFE, 0x7C
+];
+pub const SCHIPFONT_9: [u8; 0x10] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF,
+ 0xFF, 0x7F, 0x03, 0x03, 0xC7, 0xFF, 0xFE, 0x7C
+];
+pub const SCHIPFONT_A: [u8; 0x10] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF,
+ 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3
+];
+pub const SCHIPFONT_B: [u8; 0x10] = [0xFE, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xFE, 0xFF,
+ 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xFE
+];
+pub const SCHIPFONT_C: [u8; 0x10] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0x01, 0x01,
+ 0x01, 0x01, 0xC3, 0xC3, 0xFF, 0xFE, 0xFC, 0x78
+];
+pub const SCHIPFONT_D: [u8; 0x10] = [0xFE, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3,
+ 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xFE, 0x7C
+];
+pub const SCHIPFONT_E: [u8; 0x10] = [0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0x03, 0xFF,
+ 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, 0xFF, 0xFF
+];
+pub const SCHIPFONT_F: [u8; 0x10] = [0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0x03, 0xFF,
+ 0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03];
diff --git a/gemmaimgui/src/bin/support/emmagui_support.rs b/gemmaimgui/src/bin/support/emmagui_support.rs
index eaf4122..8cf2f9e 100644
--- a/gemmaimgui/src/bin/support/emmagui_support.rs
+++ b/gemmaimgui/src/bin/support/emmagui_support.rs
@@ -16,6 +16,8 @@ use crate::ImGuiUiState;
use crate::support::gui_file_list::GuiFileList;
use super::ui_state;
+const ROM_ROOT: &str = "/home/tmerritt/Projects/trevors_chip8_toy/resources/octoroms";
+
pub struct GemmaImguiSupport {}
const CELL_WIDTH: i32 = 5i32;
@@ -49,23 +51,30 @@ impl GemmaImguiSupport {
ui.window(format!("Display {cell_width}x{cell_height}"))
.size([300.0, 300.0], Condition::Once)
.build(|| {
+ let (width, height) = system_to_control.video_memory.get_resolution();
let origin = ui.cursor_screen_pos();
let fg = ui.get_window_draw_list();
- ui.text("This is the video display.");
- for current_row in 0..=31 {
- for current_column in 0..=63 {
- let x_offset = origin[0] as i32 + (current_column * cell_width);
+ if (system_to_control.video_memory.is_highres()) {
+ ui.text("High Def Video here");
+
+ } else {
+ ui.text("StdDef video here.");
+
+ for current_row in 0..=height {
let y_offset = origin[1] as i32 + (current_row * cell_height);
- let current_origin = [x_offset as f32, y_offset as f32];
- let current_limit = [(x_offset + cell_width) as f32, (y_offset + cell_height) as f32];
- let memory_offset = (current_row * 64 + current_column) as u16;
- let to_render = system_to_control.video_memory.peek(memory_offset);
- let color: ImColor32 = if to_render {
- gui_state.on_colour
- } else {
- gui_state.off_colour
- };
- fg.add_rect_filled_multicolor(current_origin, current_limit, color, color, color, color);
+ for current_column in 0..=width {
+ let x_offset = origin[0] as i32 + (current_column * cell_width);
+ let current_origin = [x_offset as f32, y_offset as f32];
+ let current_limit = [(x_offset + cell_width) as f32, (y_offset + cell_height) as f32];
+ let memory_offset = (current_row * width + current_column) as u16;
+ let to_render = system_to_control.video_memory.peek(memory_offset);
+ let color: ImColor32 = if to_render {
+ gui_state.on_colour
+ } else {
+ gui_state.off_colour
+ };
+ fg.add_rect_filled_multicolor(current_origin, current_limit, color, color, color, color);
+ }
}
}
});
@@ -79,7 +88,7 @@ impl GemmaImguiSupport {
ui.text(format!("Step {:04x}", system_to_control.num_cycles()).as_str());
/* ROM Lister */
- let new_filename = GuiFileList::display_path(PathBuf::from("resources/roms"), &gui_state.filename_to_load, ui);
+ let new_filename = GuiFileList::display_path(PathBuf::from("/home/tmerritt/Projects/trevors_chip8_toy/resources/octoroms/"), &gui_state.filename_to_load, ui);
if !new_filename.is_empty() {
if new_filename != gui_state.filename_to_load {
debug!("NEW FILENAME SELECTED -> {new_filename}");
@@ -89,22 +98,23 @@ impl GemmaImguiSupport {
let mut buffer = Vec::new();
debug!("PREPARING TO LOAD {}", gui_state.filename_to_load);
// let mut input_file = File::open(Path::new("./1-chip8-logo.ch8")).expect("put 1-chip8-logo.ch8 in this directory");
- let mut input_file = File::open(Path::new(&("resources/roms/".to_string() + &gui_state.filename_to_load))).expect("put 1-chip8-logo.ch8 in this directory");
+ let mut input_file = File::open(Path::new(&("/home/tmerritt/Projects/trevors_chip8_toy/resources/octoroms/".to_string() + &gui_state.filename_to_load))).expect("put 1-chip8-logo.ch8 in this directory");
input_file.read_to_end(&mut buffer).expect("unable to read file");
system_to_control.load_bytes_to_system_memory((&*buffer).into());
}
}
ui.separator();
if ui.button("Step") {
- system_to_control.one_step = true;
+ system_to_control.step();
+
};
ui.same_line();
if ui.button("Run") {
- system_to_control.core_should_run = true;
+ system_to_control.start();
}
ui.same_line();
if ui.button("Stop") {
- system_to_control.core_should_run = false;
+ system_to_control.stop();
}
ui.same_line();
if ui.button("Reset") {
diff --git a/gemmautil/src/bin/ch8asm.rs b/gemmautil/src/bin/ch8asm.rs
index 937e2b1..ef5f21e 100644
--- a/gemmautil/src/bin/ch8asm.rs
+++ b/gemmautil/src/bin/ch8asm.rs
@@ -60,17 +60,6 @@ mod test {
println!("RULE PAIR THING: {:?}", i);
}
-
- let parsed = Chip8AsmParser::parse(Rule::record, "ADDI ; comment");
- for i in parsed {
- println!("RULE PAIR THING: {:?}", i);
- }
-
-
- println!("PARSED: {:?}",
- Chip8AsmParser::parse(Rule::record, "ADD 0x01 0x02 ; Comment")
- );
-
println!("PARSED: {:?}",
Chip8AsmParser::parse(Rule::record, "ADD ADD ADD")
);
@@ -82,6 +71,5 @@ mod test {
println!("PARSED: {:?}",
Chip8AsmParser::parse(Rule::record, "ADD 0x01 0x02 ; Comment")
);
-
}
}
\ No newline at end of file
diff --git a/gemmautil/src/chip8_asm.pest b/gemmautil/src/chip8_asm.pest
index 727d8de..054c309 100644
--- a/gemmautil/src/chip8_asm.pest
+++ b/gemmautil/src/chip8_asm.pest
@@ -3,5 +3,5 @@ instruction = { ASCII_ALPHA+ }
parameter = { "0X" ~ ASCII_HEX_DIGIT+ }
comment = { ";" ~ WHITESPACE* ~ ASCII* }
parameters = { parameter ~ (WHITESPACE* ~ "," ~ WHITESPACE* ~ parameter)* }
-record = { instruction ~ WHITESPACE ~ parameters? ~ WHITESPACE* ~ comment? }
+record = { instruction ~ WHITESPACE* ~ parameters? ~ WHITESPACE* ~ comment? }
file = { SOI ~ (record ~ ("\r\n" | "\n"))* ~ EOI }
diff --git a/resources/octoroms/caveexplorer.8o b/resources/octoroms/caveexplorer.8o
new file mode 100644
index 0000000..1084788
--- /dev/null
+++ b/resources/octoroms/caveexplorer.8o
@@ -0,0 +1,1213 @@
+###########################################
+#
+# Cave Explorer
+#
+# Discover the secrets of a mysterious
+# tunnel system. A fairly elaborate
+# Chip8 adventure game. Best played at
+# about 20 cycles/frame.
+#
+# On the overworld, press ASWD to move.
+# In platformer levels, A/D move,
+# E picks up/sets down blocks,
+# Q resets the level.
+#
+###########################################
+
+: font
+0x20 0x20 0x20 0x00 0x20 0xF0 0x10 0x70 0x00 0x40 0xE0 0x90 0xE0 0x90 0x90 0x90
+0xE0 0x90 0xE0 0x90 0xE0 0x80 0x80 0xD0 0xB0 0xB0 0x90 0x90 0x90 0x90 0x60 0x60
+0x90 0x90 0x90 0x60 0x90 0xD0 0xB0 0x90 0x90 0x90 0x50 0x20 0x40 0x00 0x00 0x00
+0x00 0x00 0x60 0x90 0xF0 0x90 0x90 0xF0 0x90 0x90 0xF0 0x80 0xE0 0x80 0x80 0x80
+0x80 0xF0 0x80 0xE0 0x80 0xF0 0x40 0x40 0x40 0xF0 0x20 0x40 0x80 0xF0 0x20 0x20
+0x20 0xC0 0x60 0x90 0x80 0x90 0x60 0x90 0x90 0xB0 0x70 0x80 0xB0 0x90 0x70 0x80
+0x60 0x10 0xE0 0x90 0xA0 0xC0 0xA0 0x90 0x90 0xB0 0xB0 0xD0 0xF0 0x40 0x40 0x40
+0x40 0x90 0x90 0x70 0x10 0xE0
+
+: special-complete
+: title1
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x07 0xE0 0x00 0x00 0x00 0x00 0x00 0x00
+0x08 0x18 0x00 0x00 0x00 0x00 0x00 0x00 0x10 0x04 0x00 0x00 0x00 0x00 0x00 0x00
+0x20 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x20 0x02 0x00 0x00 0x00 0x00 0x00 0x00
+0x28 0x21 0x00 0x00 0x00 0x00 0x00 0x00 0x40 0x01 0x00 0x00 0x00 0x00 0x00 0x00
+0x40 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x40 0x01 0x00 0x00 0x00 0x00 0x0F 0xC0
+0x4F 0x81 0x00 0x00 0x00 0x00 0x10 0x30 0x46 0x01 0x00 0x00 0x00 0x00 0x20 0x08
+0x20 0x02 0x00 0x00 0x00 0x00 0x30 0xE8 0x20 0x02 0x00 0x00 0x00 0x00 0x4F 0x04
+0x10 0x0C 0x00 0x00 0x00 0x00 0x50 0x84 0x1F 0xF6 0x00 0x00 0x00 0x00 0x40 0x04
+0x77 0x09 0x00 0x00 0x00 0x00 0x4F 0x84 0x97 0x90 0xC0 0x00 0x00 0x00 0x50 0x4C
+0x0E 0xE0 0x20 0x00 0x00 0x00 0x20 0x1E 0x0C 0x00 0x10 0x00 0x00 0x00 0x7F 0xFF
+
+: block-reg-buffer
+: phrase1
+0x0A 0x41 0x32 0x0C 0x71 0x2D 0x3A 0x1F 0x0A 0x2D 0x2D 0x2D 0x5E 0x52 0x35 0x1F
+0x1F 0x3D 0x05
+
+: phrase2
+0x2D 0x2D 0x2D 0x2D 0x2D 0x2D 0x2D 0x2D 0x2D 0x24 0x1F 0x00
+
+: phrase3
+0x35 0x1F 0x67 0x2D 0x32 0x10 0x1F 0x1A 0x6C 0x2D 0x2D 0x2D 0x2D
+
+: level-buffer
+: title2
+0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x70 0x00 0x00
+0x00 0x3E 0x00 0x70 0x3C 0x8C 0x7F 0x00 0x01 0xC1 0x81 0x88 0x42 0x84 0x80 0xE0
+0x02 0x00 0x42 0x04 0x42 0x84 0x80 0x10 0x04 0x00 0x44 0x02 0x21 0x04 0x80 0x10
+0x08 0x00 0x44 0x01 0x21 0x05 0x00 0x10 0x10 0x03 0x88 0x01 0x10 0x05 0x07 0x10
+0x14 0x07 0x08 0x00 0x90 0x45 0x0F 0xD0 0x10 0x0C 0x08 0x60 0x90 0x09 0x06 0x70
+0x20 0x18 0x08 0x70 0x90 0x09 0x01 0xA0 0x20 0x18 0x10 0xB0 0x54 0x19 0x00 0x80
+0x28 0x10 0x10 0x90 0x4C 0x1A 0x07 0x00 0x20 0x10 0xD0 0xF0 0xA8 0x32 0x46 0x60
+0x20 0x0F 0x58 0x00 0x28 0xB2 0x03 0x90 0x29 0x20 0xD1 0x00 0x24 0x33 0x00 0x10
+0x30 0x09 0xD4 0x72 0x17 0x73 0x40 0x10 0x14 0x43 0x98 0xF8 0xB6 0x63 0x02 0x10
+0x1A 0xA7 0x1B 0x9A 0x33 0x61 0xD0 0x30 0x0D 0x1E 0x1F 0x8D 0x63 0xC1 0xFA 0xD0
+0x0F 0xFC 0x0D 0x07 0xE1 0x81 0xFF 0x70 0x04 0x60 0x00 0x02 0xC0 0x00 0x8C 0x20
+0x00 0x00 0x00 0x00 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+0x00 0xFB 0x37 0x98 0x73 0xCF 0xBC 0x00 0x00 0xFB 0xF7 0xD8 0xFB 0xEF 0xBE 0x00
+0x00 0xC1 0xE6 0xD8 0xDB 0x6C 0x36 0x00 0x00 0xF0 0xC7 0xD8 0xDB 0xEF 0x3E 0x00
+0x00 0xC1 0xE7 0x98 0xDB 0xCC 0x3C 0x00 0x00 0xFB 0xF6 0x1E 0xFB 0x6F 0xB6 0x00
+0x00 0xFB 0x36 0x1E 0x73 0x6F 0xB6 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+: bus
+0x00 0x04 0x00 0x3D 0x97 0xFF 0xFF 0xFF 0xFF 0xFD 0xFF 0x9D 0x4B 0xFF 0x8B 0x47
+0x20 0x04 0x00 0x5D 0xCB 0xFF 0xD8 0x4F 0x26 0x04 0x00 0x1D 0x05 0xFF 0xDB 0x5F
+0x21 0x04 0x00 0x1D 0x85 0xFF 0xDB 0x47 0x20 0x84 0x7C 0x1C 0x05 0xFF 0xFF 0xFF
+0x20 0x84 0xDA 0x5D 0xC5 0xFF 0x8B 0x47 0x20 0x94 0xBC 0xDD 0xE5 0xFF 0x99 0x5B
+0x20 0xB4 0xE6 0x1D 0xE6 0x1F 0xBA 0x5B 0x20 0x94 0xC2 0xDD 0xCF 0xE7 0x8B 0x47
+0x20 0x85 0x7D 0xDC 0x3F 0xF9 0xFF 0xFF 0x2F 0xF5 0x83 0x9F 0xFF 0xFE 0xFF 0xFF
+0x20 0x05 0xFF 0x1F 0xFF 0xFF 0x7F 0xFF 0xFF 0xFC 0xFE 0x1F 0xFF 0xF3 0xBF 0x07
+0xFF 0xFC 0xFE 0x1F 0xFF 0xED 0xDE 0xFB 0xA2 0x2D 0xF8 0x0C 0x07 0xDD 0xED 0xFD
+0x2A 0xA8 0x07 0xF7 0xF9 0xD9 0xE3 0x7E 0xAA 0xAB 0xFF 0xEC 0x1E 0xD6 0xF3 0xFE
+0xA2 0x25 0xF8 0x00 0x06 0xE7 0x1B 0xFE 0xFF 0xFA 0x07 0xF8 0x03 0x77 0xC0 0x7E
+0x00 0x0B 0xBF 0xFE 0x03 0x7B 0xF8 0xFC 0xFF 0xFB 0x9F 0xFF 0x81 0x7C 0xED 0xF8
+0x00 0x0B 0xBF 0xFF 0x81 0x7F 0x0E 0x00 0xFF 0xFB 0xAF 0xF0 0x61 0x7F 0xEE 0x60
+0xFF 0xFB 0xBA 0x3F 0xF9 0x7F 0xDC 0x98 0xFF 0xFD 0xBD 0xFF 0xFE 0x3F 0xDC 0xF8
+0xFF 0xFD 0xBC 0xFF 0xFF 0xCF 0xDC 0xFC 0x00 0x01 0xBD 0x7F 0xFF 0xF1 0xBC 0xFC
+0xFF 0xFD 0xBD 0xBF 0x00 0x0E 0x3C 0xDC 0x00 0x01 0x1D 0xC8 0xFF 0xFF 0x8F 0xDE
+0x00 0x00 0x1D 0xF3 0xFF 0xFF 0xF3 0xBE 0x00 0x00 0x04 0xFB 0xFF 0xFF 0xFC 0x7C
+
+: mouth 0xF8
+
+: rumbletext # "YOU HEAR A RUMBLE IN THE DISTANCE"
+0x71 0x1F 0x1A 0x2D 0x35 0x41 0x32 0x0A 0x2D 0x2D 0x2D 0x2D 0x32 0x2D 0x0A 0x1A
+0x17 0x10 0x3D 0x41 0x2D 0x45 0x24 0x2D 0x6C 0x35 0x41 0x2D 0x0C 0x45 0x5E 0x6C
+0x32 0x24 0x52 0x41
+
+: uselesstext # "JUST A BUNCHOF GOLD? HOWUSELESS!"
+0x4D 0x1A 0x5E 0x6C 0x2D 0x32 0x2D 0x10 0x1A 0x24 0x52 0x35 0x1F 0x3A 0x2D 0x5A
+0x1F 0x3D 0x0C 0x05 0x2D 0x35 0x1F 0x67 0x1A 0x5E 0x41 0x3D 0x41 0x5E 0x5E 0x00
+
+: exittext # "CLIMBING OUTYOU EMERGE TO DAZZLING SUNLIGHT"
+0x52 0x3D 0x45 0x17 0x10 0x45 0x24 0x5A 0x2D 0x1F 0x1A 0x6C 0x71 0x1F 0x1A 0x2D
+0x41 0x17 0x41 0x0A 0x5A 0x41 0x2D 0x2D 0x6C 0x1F 0x2D 0x0C 0x32 0x49 0x49 0x3D
+0x45 0x24 0x5A 0x2D 0x5E 0x1A 0x24 0x3D 0x45 0x5A 0x35 0x6C
+
+: pyramidtext # "A VISION OF A PYRAMID? ASECRET WALL?"
+0x32 0x2D 0x27 0x45 0x5E 0x45 0x1F 0x24 0x2D 0x1F 0x3A 0x2D 0x32 0x2D 0x12 0x71
+0x0A 0x32 0x17 0x45 0x0C 0x05 0x2D 0x32 0x5E 0x41 0x52 0x0A 0x41 0x6C 0x2D 0x67
+0x32 0x3D 0x3D 0x05
+
+: skulltext # "SOMEONE LEFTTHEIR SKULL HERE? GROSS!"
+0x5E 0x1F 0x17 0x41 0x1F 0x24 0x41 0x2D 0x3D 0x41 0x3A 0x6C 0x6C 0x35 0x41 0x45
+0x0A 0x2D 0x5E 0x63 0x1A 0x3D 0x3D 0x2D 0x35 0x41 0x0A 0x41 0x05 0x2D 0x5A 0x0A
+0x1F 0x5E 0x5E 0x00
+
+: puzzlestext # "YOU MUST RETURN TO WHERE THIS ALL BEGAN"
+0x71 0x1F 0x1A 0x2D 0x17 0x1A 0x5E 0x6C 0x2D 0x2D 0x2D 0x2D 0x0A 0x41 0x6C 0x1A
+0x0A 0x24 0x2D 0x6C 0x1F 0x2D 0x2D 0x2D 0x67 0x35 0x41 0x0A 0x41 0x2D 0x6C 0x35
+0x45 0x5E 0x2D 0x2D 0x32 0x3D 0x3D 0x2D 0x10 0x41 0x5A 0x32 0x24
+
+: deadendtext # "A DEAD END? NUTS!"
+0x32 0x2D 0x0C 0x41 0x32 0x0C 0x2D 0x41 0x24 0x0C 0x05 0x2D 0x24 0x1A 0x6C 0x5E
+0x00
+
+: mazetext # "SNWEW"
+0x5E 0x24 0x67 0x41 0x67
+
+: draw-bitmap
+ clear
+ v0 := 0 # x
+ v1 := 0 # y
+ v2 := 0 # byte
+ v3 := 1 # constant
+ loop
+ sprite v0 v1 1
+ i += v3
+ v2 += 1
+ v0 += 8
+ if v0 == 64 then v1 += 1
+ if v0 == 64 then v0 := 0
+ if v1 != 32 then
+ again
+;
+
+: draw-text
+ v1 := 2 # x
+ v2 := 1 # y
+ v3 := 0 # byte
+ # v4 contains length
+ loop
+ : text-addr i := 0 # self-modify to alter
+ i += v3
+ load v0
+ i := font
+ i += v0
+ sprite v1 v2 5
+ v1 += 5
+ if v1 == 62 then v2 += 6
+ if v1 == 62 then v1 := 2
+ v3 += 1
+ if v3 != v4 then
+ again
+;
+
+: wait
+ v0 := 128
+ delay := v0
+ loop
+ v0 := delay
+ if v0 != 0 then
+ again
+;
+
+: swapmouth
+ i := mouth
+ v0 := 4
+ v1 := 22
+ sprite v0 v1 1
+ v0 := 52
+ v1 := 29
+ sprite v0 v1 1
+;
+
+: draw-phrase-1
+ :unpack 0xA phrase1
+ i := text-addr
+ save v1
+ v4 := 19
+ draw-text
+;
+
+: draw-phrase-2
+ :unpack 0xA phrase2
+ i := text-addr
+ save v1
+ v4 := 12
+ draw-text
+;
+
+: draw-phrase-3
+ :unpack 0xA phrase3
+ i := text-addr
+ save v1
+ v4 := 13
+ draw-text
+;
+
+###########################################
+#
+# Block pushing puzzle
+#
+###########################################
+
+: solid 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+: empty 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+: floor 0x00 0xFE 0x55 0x22 0x88 0x00 0x00 0x00
+: roof 0x00 0x00 0x00 0x11 0x91 0x8B 0x8F 0xDF
+: exit 0xC3 0xBD 0xBD 0xBD 0xB9 0xB9 0xBD 0xBD
+: block 0x81 0x7E 0x5A 0x7E 0x7E 0x5A 0x7E 0x81
+
+: left 0xE7 0xE7 0xC3 0x81 0x82 0x42 0xDB 0x93
+: right 0xE7 0xE7 0xC3 0x81 0x41 0x42 0xDB 0xC9
+
+:const SOLID 0
+:const EMPTY 1
+:const FLOOR 2
+:const ROOF 3
+:const EXIT 4
+:const BLOCK 5
+:const PLAYER_L 6
+:const PLAYER_R 7
+
+:alias levelno ve
+:alias playerx vd
+:alias playery vc
+:alias input vb
+:alias direction va
+:alias win v9
+
+# v0-v8 reserved for temporary use.
+# v0-v7 are used together for fast buffer copies.
+
+# player facing
+:const FACING_LEFT 0
+:const FACING_RIGHT 1
+
+# input keys:
+:const MOVE_LT 7
+:const MOVE_RT 9
+:const RESET 4
+:const ACTION 6
+
+###########################################
+#
+# Level data
+#
+###########################################
+
+: level-0
+ 4 1 1 3 1 1 3 0
+ 2 1 1 1 1 1 1 3
+ 0 2 1 1 1 1 1 1
+ 0 0 2 2 2 2 1 6
+: level-1
+ 4 1 3 1 3 1 1 1
+ 2 1 1 1 1 1 1 5
+ 0 1 1 6 1 1 2 2
+ 0 1 2 2 1 5 0 0
+: level-2
+ 3 1 1 1 1 1 1 4
+ 1 1 1 2 2 2 2 2
+ 5 1 1 1 1 1 1 1
+ 2 5 1 1 6 1 1 5
+: level-3
+ 1 1 1 1 3 1 1 3
+ 4 1 6 1 1 1 1 5
+ 2 1 2 1 1 1 2 2
+ 0 1 0 5 1 1 0 0
+: level-4
+ 5 1 3 0 3 1 1 4
+ 1 1 1 3 1 1 1 2
+ 1 1 1 1 1 1 1 0
+ 2 2 1 6 1 5 2 0
+: level-5
+ 4 1 1 1 1 1 1 1
+ 2 1 1 6 1 5 1 5
+ 0 1 1 2 1 1 1 1
+ 0 1 1 0 1 5 1 5
+: level-6
+ 0 3 1 5 1 1 1 4
+ 3 1 1 1 1 1 1 2
+ 1 1 1 5 2 1 1 0
+ 5 5 1 6 0 1 1 0
+: level-7
+ 3 3 1 4 1 5 3 3
+ 5 5 1 1 1 1 5 5
+ 1 1 1 1 1 1 1 1
+ 2 1 1 6 1 1 1 5
+
+###########################################
+#
+# Level utilities
+#
+###########################################
+
+: level-table
+ i := level-0 return
+ i := level-1 return
+ i := level-2 return
+ i := level-3 return
+ i := level-4 return
+ i := level-5 return
+ i := level-6 return
+ i := level-7 return
+
+: get-level
+ v0 <<= levelno
+ v0 <<= v0
+ jump0 level-table
+
+: copy-level
+ v8 := 0
+ loop
+ get-level
+ i += v8
+ load v7
+
+ i := level-buffer
+ i += v8
+ save v7
+
+ v8 += 8
+ if v8 != 32 then
+ again
+;
+
+: get-tile
+ # takes tile index in v0
+ v0 <<= v0
+ v0 <<= v0
+ v0 <<= v0
+ i := solid
+ i += v0
+;
+
+: spawn-player
+ direction := FACING_LEFT
+ win := 0
+ playerx := v1
+ playerx >>= playerx
+ playerx >>= playerx
+ playerx >>= playerx
+ playery := v2
+ playery >>= playery
+ playery >>= playery
+ playery >>= playery
+;
+
+: init-level
+ copy-level
+ clear
+ v1 := 0 # sprite x
+ v2 := 0 # sprite y
+ v3 := 0 # tile index
+ loop
+ i := level-buffer
+ i += v3
+ load v0
+ if v0 == PLAYER_L then spawn-player
+ get-tile
+ sprite v1 v2 8
+ v3 += 1
+ v1 += 8
+ if v1 == 64 then v2 += 8
+ if v1 == 64 then v1 := 0
+ if v2 != 32 then
+ again
+;
+
+: index-at
+ # takes x/y in v1/v2
+ # returns type in v0
+ v0 <<= v2
+ v0 <<= v0
+ v0 <<= v0
+ v0 += v1
+ i := level-buffer
+ i += v0
+;
+
+: draw-at
+ # takes x/y in v1/v2
+ # trashes v0-v2
+ index-at
+ load v0
+ get-tile
+ v1 <<= v1
+ v1 <<= v1
+ v1 <<= v1
+ v2 <<= v2
+ v2 <<= v2
+ v2 <<= v2
+ sprite v1 v2 8
+;
+
+: copy-loc
+ v1 := v3
+ v2 := v4
+;
+
+: set-at
+ # takes x/y in v3/v4, value in v5
+ copy-loc
+ draw-at
+ copy-loc
+ index-at
+ v0 := v5
+ save v0
+ copy-loc
+ draw-at
+;
+
+###########################################
+#
+# Block pusher player helpers
+#
+###########################################
+
+:alias canpass v7
+:alias deltadir v6
+
+: player-loc
+ v1 := playerx
+ v2 := playery
+;
+
+: player-copy
+ v3 := playerx
+ v4 := playery
+;
+
+: player-ahead
+ player-loc
+ v1 += deltadir
+;
+
+: player-above
+ player-loc
+ v2 += -1
+;
+
+: player-below
+ player-loc
+ v2 += 1
+;
+
+: draw-player
+ player-loc
+ draw-at
+;
+
+: player-above-type
+ player-above
+ index-at
+ load v0
+;
+
+: carrying?
+ # returns result in v0
+ v0 := 0
+ if playery == 0 then return
+ player-above-type
+ if v0 != BLOCK then v0 := 0
+ if v0 == BLOCK then v0 := 1
+;
+
+###########################################
+#
+# Block pusher movement
+#
+###########################################
+
+: passable?
+ # takes x/y in v1/v2
+ # returns flag in canpass
+ canpass := 0
+ if v1 > 7 then return
+ if v2 > 3 then return
+ index-at
+ load v0
+ if v0 == EMPTY then canpass := 1
+ if v0 == EXIT then canpass := 1
+;
+
+: fall-scan
+ player-copy
+ v3 += deltadir
+ loop
+ copy-loc
+ v2 += 1
+ passable?
+ while canpass == 1
+ v4 += 1
+ again
+;
+
+: move-player
+ # if we're stepping onto
+ # an exit, we win.
+ copy-loc
+ index-at
+ load v0
+ if v0 == EXIT then win := 1
+
+ # takes x/y in v3/v4
+ draw-player
+ player-loc
+ index-at
+ v0 := EMPTY
+ save v0
+ draw-player
+
+ playerx := v3
+ playery := v4
+
+ v5 := PLAYER_L
+ v5 += direction
+ set-at
+;
+
+: hide-block
+ v5 := EMPTY
+
+: set-block
+ player-copy
+ v4 += -1
+ set-at
+;
+
+: show-block
+ v5 := BLOCK
+ jump set-block
+
+: climb-move
+ player-copy
+ v3 += deltadir
+ v4 += -1
+ move-player
+;
+
+: climb-carry
+ # can't climb if blocked above-side
+ player-above
+ v1 += deltadir
+ passable?
+ if canpass != 1 then return
+
+ # can't climb if blocked above-above-side
+ player-above
+ v1 += deltadir
+ v2 += -1
+ passable?
+ if canpass != 1 then return
+
+ hide-block
+ climb-move
+ show-block
+;
+
+: climb
+ # can't climb if blocked above:
+ player-above
+ passable?
+ if canpass != 1 then return
+
+ # can't climb if blocked above-side
+ player-above
+ v1 += deltadir
+ passable?
+ if canpass != 1 then return
+
+ climb-move
+;
+
+: carry
+ # make sure we don't need to climb:
+ player-loc
+ v1 += deltadir
+ passable?
+ if canpass != 1 then jump climb-carry
+
+ # make sure there's horizontal space for the block
+ player-loc
+ v1 += deltadir
+ v2 += -1
+ passable?
+ if canpass != 1 then return
+
+ hide-block
+
+ # scan down and 'fall':
+ fall-scan
+ move-player
+
+ show-block
+;
+
+: walk
+ # make sure we aren't carrying a block:
+ carrying?
+ if v0 != 0 then jump carry
+
+ # make sure we don't need to climb:
+ player-loc
+ v1 += deltadir
+ passable?
+ if canpass != 1 then jump climb
+
+ # scan down and 'fall':
+ fall-scan
+ move-player
+;
+
+: walk-left
+ deltadir := -1
+ if direction == FACING_LEFT then jump walk
+
+ # face left
+ player-copy
+ v5 := PLAYER_L
+ set-at
+ direction := FACING_LEFT
+;
+
+: walk-right
+ deltadir := 1
+ if direction == FACING_RIGHT then jump walk
+
+ # face right
+ player-copy
+ v5 := PLAYER_R
+ set-at
+ direction := FACING_RIGHT
+;
+
+: drop
+ # there must be at least one empty space
+ player-ahead
+ passable?
+ if canpass != 1 then return
+
+ # erase block
+ player-copy
+ v4 += -1
+ v5 := EMPTY
+ set-at
+
+ # scan for drop position
+ fall-scan
+
+ # draw repositioned block
+ v5 := BLOCK
+ set-at
+;
+
+: action
+ deltadir := 1
+ if direction == FACING_LEFT then deltadir := -1
+ carrying?
+ if v0 != 0 then jump drop
+
+ # we can only pick up block tiles
+ player-ahead
+ index-at
+ load v0
+ if v0 != BLOCK then return
+
+ # the space above us must be empty
+ player-above-type
+ if v0 != EMPTY then return
+
+ # erase block
+ player-copy
+ v3 += deltadir
+ v5 := EMPTY
+ set-at
+
+ # draw repositioned block
+ player-copy
+ v4 += -1
+ v5 := BLOCK
+ set-at
+;
+
+###########################################
+#
+# Block pusher main routine
+#
+###########################################
+
+: puzzle-finished-count 0
+
+: block-puzzle
+ i := block-reg-buffer
+ save vd
+
+ init-level
+ loop
+ input := key
+ if input == RESET then init-level
+ if input == MOVE_LT then walk-left
+ if input == MOVE_RT then walk-right
+ if input == ACTION then action
+ if win == 0 then
+ again
+
+ v0 := 32
+ buzzer := v0
+ clear
+
+ i := puzzle-finished-count
+ load v0
+ v0 += 1
+ i := puzzle-finished-count
+ save v0
+
+ i := block-reg-buffer
+ load vd
+;
+
+###########################################
+#
+# Overworld
+#
+###########################################
+
+: rocks1 0x00 0x40 0x00 0x00
+: rocks2 0x00 0x20 0x40 0x00
+: rocks3 0x00 0x40 0x20 0x00
+: rocks4 0x00 0x00 0x00 0x00
+
+: path 0x60 0xF0 0xF0 0x60
+: man 0x00 0x60 0x60 0x00
+: target 0x70 0x40 0xF0 0x50
+
+: bits 1 2 4 8 16 32 64 128
+
+:alias boardno vd
+:alias manx vc
+:alias many vb
+:alias specialx va
+:alias specialy v9
+:alias oldx v8
+:alias oldy v7
+:alias specialflag v6
+
+###########################################
+#
+# Board data
+#
+###########################################
+
+: special-pos
+ 3 6 # board 0
+ 7 4 # board 1
+ 14 6 # board 2
+ 7 4 # board 3
+ 3 3 # board 4
+ 12 4 # board 5
+ 7 5 # board 6
+ 7 4 # board 7
+ 7 6 # board 8
+ 9 4 # board 9
+ 10 4 # board A
+ 11 3 # board B
+ 5 3 # board C
+ 5 3 # board D
+ 5 3 # board E
+ 5 3 # board F
+
+# board in each direction
+# 0 1 2 3 4 5 6 7 8 9 A B C D E F
+: board-n
+ 0x0 0x2 0x1 0x5 0xA 0x6 0x7 0x7 0x8 0x9 0x3 0xC 0xC 0xC 0xC 0xE
+: board-e
+ 0x3 0x4 0x2 0x1 0x9 0x8 0x6 0x7 0xA 0x9 0xA 0xB 0xD 0xE 0xF 0xD
+: board-s
+ 0x0 0x2 0x1 0xA 0xD 0x3 0x5 0x6 0x8 0x9 0x4 0xB 0xB 0xB 0xB 0xB
+: board-w
+ 0x0 0x3 0x2 0x0 0x1 0x5 0x6 0x7 0x5 0x4 0x8 0xB 0xD 0xD 0x3 0xF
+
+# pixel data
+: board0
+ 0x28 0xEB 0x0A 0x7A 0x02 0xEF 0x28 0x2A
+ 0x6A 0x4A 0x5A 0x42 0x7A 0x0A 0x6E 0x28
+: board1
+ 0x08 0x08 0x4A 0x08 0xFF 0x40 0x5E 0x52
+ 0x42 0x7E 0x40 0xCF 0x08 0x4A 0x08 0x08
+: board2
+ 0x00 0x70 0x50 0x50 0xD7 0x54 0x54 0x54
+ 0x54 0x54 0x54 0xD5 0x14 0x14 0x7C 0x00
+: board3
+ 0x28 0x28 0x28 0xEB 0x0A 0x6A 0x0A 0x3A
+ 0x22 0x3A 0x2A 0x2E 0x20 0x38 0x08 0x28
+: board4
+ 0x08 0x08 0x1C 0x7F 0x5C 0xC9 0x08 0x1C
+ : gate1 0x60 0x1C 0x08 0x08 0x1C : gate2 0x60 0x1C 0x08
+: board5
+ 0x10 0x00 0x03 0xF3 0x10 0x10 0x10 0xDF
+ 0xD0 0x16 0x16 0x10 0x10 0x10 0x10 0x10
+: board6
+ 0x00 0x02 0x4A 0x66 0x70 0x78 0x7C 0xFF
+ 0x7C 0x78 0x70 0x66 0x4A 0x02 0x00 0x00
+: board7
+ 0x00 0x76 0x52 0x5A 0x04 0x00 0x39 0xFB
+ 0x39 0x00 0x04 0x5A 0x52 0x76 0x00 0x00
+: board8
+ 0x10 0x7E 0x08 0x7E 0x10 0x7E 0x08 0x7E
+ 0x10 0x7E 0x08 0x7E 0x10 0x7E 0x10 0x10
+: board9
+ 0x08 0x08 0xD8 0x52 0xD0 0x10 0x38 0x2B
+ 0x3A 0x12 0x02 0xC2 0x4E 0x78 0x10 0x18
+: boardA
+ 0x10 0x54 0x10 0xFF 0x04 0x7C 0x40 0x7C
+ 0x04 0x1C 0x10 0x00 0x28 0x6C 0x74 0x38
+: boardB
+ 0x00 0xF2 0xEA 0x02 0x02 0xC3 0x0A 0xFA
+ 0x02 0xFA 0xA2 0x8A 0x22 0xF2 0x9A 0x38
+: boardC
+ 0x08 0x08 0x08 0x3E 0x22 0xFB 0x22 0x3E
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+: boardD
+ 0x08 0x08 0x08 0x3E 0x22 0xFB 0x22 0x3E
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x0A 0x08
+: boardE
+ 0x08 0x08 0x08 0x3E 0x22 0xFB 0x22 0x3E
+ 0x08 0x08 0x08 0x08 0x0A 0x08 0x0A 0x08
+: boardF
+ 0x08 0x08 0x08 0x3E 0x22 0xFB 0x22 0x3E
+ 0x08 0x08 0x0A 0x08 0x0A 0x08 0x0A 0x08
+
+###########################################
+#
+# Overworld game logic
+#
+###########################################
+
+: secret-wall
+ if boardno != 6 then return
+ if v4 != 7 then return
+ v5 := 0b11111100
+;
+
+: draw-board
+ clear
+ v1 := 0 # x tile
+ v4 := 0 # data offset
+ v3 <<= boardno # data base
+ v3 <<= v3
+ v3 <<= v3
+ v3 <<= v3
+ loop
+ i := board0
+ i += v3
+ i += v4
+ load v0
+ v5 := v0 # column
+ v2 := 0 # y tile
+ secret-wall
+
+ loop
+ i := rocks1
+ v0 := random 0b1100
+ i += v0
+ v5 >>= v5
+ if vf != 0 then i := path
+ sprite v1 v2 4
+ v2 += 4
+ if v2 != 32 then
+ again
+
+ v1 += 4
+ v4 += 1
+ if v4 != 16 then
+ again
+
+ specialx := -1
+ specialy := -1
+ i := special-complete
+ i += boardno
+ load v0
+ if v0 != 0 then return
+
+ v0 <<= boardno
+ i := special-pos
+ i += v0
+ load v1
+ specialx := v0
+ specialy := v1
+
+ v0 <<= v0
+ v0 <<= v0
+ v1 <<= v1
+ v1 <<= v1
+ i := path
+ sprite v0 v1 4
+ i := target
+ sprite v0 v1 4
+;
+
+: draw-man
+ v0 <<= manx
+ v0 <<= v0
+ v1 <<= many
+ v1 <<= v1
+ i := man
+ sprite v0 v1 4
+;
+
+: copy-pos
+ oldx := manx
+ oldy := many
+;
+
+: try-special
+ if manx != specialx then return
+ if many != specialy then return
+ specialflag := 1
+;
+
+: try-move
+ # fetch column data
+ i := board0
+ v0 <<= boardno
+ v0 <<= v0
+ v0 <<= v0
+ v0 <<= v0
+ i += v0
+
+ v3 := 15
+ v2 := manx
+ v2 &= v3
+ i += v2
+ load v0
+ v1 := v0
+
+ # fetch row mask
+ i := bits
+ v3 := 7
+ v2 := many
+ v2 &= v3
+ i += v2
+ load v0
+
+ v0 &= v1
+ vf := 1
+ if v0 != 0 then try-special
+ if v0 != 0 then return
+
+ # roll back
+ manx := oldx
+ many := oldy
+ vf := 0
+;
+
+: change-board
+ i += boardno
+ load v0
+ if v0 == boardno then return
+ boardno := v0
+ draw-board
+;
+
+: overworld-walk-w
+ manx += -1
+ try-move
+ if vf == 0 then return
+ if manx != -1 then return
+ i := board-w
+ manx := 15
+ jump change-board
+
+: overworld-walk-e
+ manx += 1
+ try-move
+ if vf == 0 then return
+ if manx != 16 then return
+ manx := 0
+ i := board-e
+ jump change-board
+
+: overworld-walk-n
+ many += -1
+ try-move
+ if vf == 0 then return
+ if many != -1 then return
+ many := 7
+ i := board-n
+ jump change-board
+
+: overworld-walk-s
+ many += 1
+ try-move
+ if vf == 0 then return
+ if many != 8 then return
+ many := 0
+ i := board-s
+ jump change-board
+
+: overworld-walk
+ if v2 == 7 then jump overworld-walk-w
+ if v2 == 9 then jump overworld-walk-e
+ if v2 == 5 then jump overworld-walk-n
+ if v2 == 8 then jump overworld-walk-s
+;
+
+###########################################
+#
+# Special scripted events
+#
+###########################################
+
+: rumble
+ v0 := 0b00001100
+ save v0
+
+ clear
+ :unpack 0xA rumbletext
+ i := text-addr
+ save v1
+ v4 := 36
+ draw-text
+ v0 := key
+;
+
+: open-gate-1
+ i := gate1
+ rumble
+;
+
+: open-gate-2
+ i := gate2
+ rumble
+;
+
+: useless-gold
+ clear
+ :unpack 0xA uselesstext
+ i := text-addr
+ save v1
+ v4 := 32
+ draw-text
+ v0 := key
+;
+
+: pyramid-hint
+ clear
+ :unpack 0xA pyramidtext
+ i := text-addr
+ save v1
+ v4 := 36
+ draw-text
+ v0 := key
+;
+
+: gross-skull
+ clear
+ :unpack 0xA skulltext
+ i := text-addr
+ save v1
+ v4 := 36
+ draw-text
+ v0 := key
+;
+
+: dead-end
+ clear
+ :unpack 0xA deadendtext
+ i := text-addr
+ save v1
+ v4 := 17
+ draw-text
+ v0 := key
+;
+
+: maze-hint
+ clear
+ :unpack 0xA mazetext
+ i := text-addr
+ save v1
+ v4 := 5
+ draw-text
+ v0 := key
+;
+
+: nop ;
+
+: puzzle-0 ve := 0 block-puzzle ;
+: puzzle-1 ve := 1 block-puzzle ;
+: puzzle-2 ve := 2 block-puzzle ;
+: puzzle-3 ve := 3 block-puzzle ;
+: puzzle-4 ve := 4 block-puzzle ;
+: puzzle-5 ve := 5 block-puzzle ;
+: puzzle-6 ve := 6 block-puzzle ;
+
+: puzzle-7
+ ve := 7
+ block-puzzle
+
+ clear
+ :unpack 0xA exittext
+ i := text-addr
+ save v1
+ v4 := 44
+ draw-text
+ v0 := key
+
+ i := bus
+ draw-bitmap
+ loop again
+
+: special-things
+ jump puzzle-7 # 0
+ jump useless-gold # 1
+ jump puzzle-1 # 2
+ jump puzzle-0 # 3
+ jump open-gate-2 # 4
+ jump gross-skull # 5
+ jump puzzle-2 # 6
+ jump open-gate-1 # 7
+ jump puzzle-3 # 8
+ jump puzzle-6 # 9
+ jump dead-end # A
+ return # B
+ jump pyramid-hint # C
+ jump puzzle-4 # D
+ jump maze-hint # E
+ jump puzzle-5 # F
+
+: check-puzzles
+ i := puzzle-finished-count
+ load v0
+ if v0 != 7 then return
+ v0 += 1
+ i := puzzle-finished-count
+ save v0
+
+ clear
+ :unpack 0xA puzzlestext
+ i := text-addr
+ save v1
+ v4 := 45
+ draw-text
+ v0 := key
+
+ i := board-e
+ v0 := 0
+ save v0
+;
+
+###########################################
+#
+# Overworld main routine
+#
+###########################################
+
+: do-special
+ i := special-complete
+ i += boardno
+ v0 := 1
+ save v0
+
+ v0 <<= boardno
+ jump0 special-things
+
+: main
+ # intro sequence
+ i := title1
+ draw-bitmap
+ draw-phrase-1
+ wait
+ draw-phrase-1
+ swapmouth
+ draw-phrase-2
+ wait
+ draw-phrase-2
+ swapmouth
+ draw-phrase-3
+ wait
+ draw-phrase-3
+ i := title2
+ draw-bitmap
+ v0 := key
+
+ # main routine
+ manx := 10
+ many := 4
+ draw-board
+ loop
+ draw-man
+ v2 := key
+ draw-man
+ copy-pos
+ specialflag := 0
+ overworld-walk
+ if specialflag != 0 then do-special
+ check-puzzles
+ if specialflag != 0 then draw-board
+ again
diff --git a/resources/octoroms/chipwar.8o b/resources/octoroms/chipwar.8o
new file mode 100644
index 0000000..4dbc410
--- /dev/null
+++ b/resources/octoroms/chipwar.8o
@@ -0,0 +1,1016 @@
+###########################################
+#
+# ChipWar
+#
+# War. War never changes.
+# This game takes place on a toroidal field
+# of 16 territories. Each has some number
+# of troops stationed there (1-5).
+# at the beginning of the game, territories
+# are divided between you (white)
+# and an AI opponent (black).
+#
+# You can order your territories to attack
+# adjacent enemy territories provided you
+# have more than one troop available.
+# When you attack, a number of 8-sided dice
+# will be rolled and summed based on the
+# number of attackers and defenders.
+# If the attackers win, they capture the
+# territory and transfer all but one of
+# their troops over.
+# If the defenders win, they destroy all
+# but one of the attackers and lose one troop,
+# or none if they have only a single troop.
+#
+# After white has taken a turn,
+# each territory has a 50% chance of
+# producing one new troop.
+# The game ends when black or white has
+# conquered the entire map.
+#
+# When selecting a territory, ASWD move
+# your cursor, E selects the territory
+# and Q ends your turn.
+# With a territory selected, ASWD choose
+# the direction in which to attack,
+# E confirms the attack and Q cancels.
+# When the game is over press any key to
+# play again.
+#
+# Based loosely on the Flash game DiceWars:
+#
+# http://www.gamedesign.jp/flash/dice/dice.html
+#
+###########################################
+
+: whitedice
+ 0xFE 0x82 0x82 0x92 0x82 0x82 0xFE 0x00 # 1
+ 0xFE 0x82 0x8A 0x82 0xA2 0x82 0xFE 0x00 # 2
+ 0xFE 0x82 0xAA 0x82 0x92 0x82 0xFE 0x00 # 3
+ 0xFE 0x82 0xAA 0x82 0xAA 0x82 0xFE 0x00 # 4
+ 0xFE 0x82 0xAA 0x92 0xAA 0x82 0xFE 0x00 # 5
+
+: blackdice
+ 0xFE 0xFE 0xFE 0xEE 0xFE 0xFE 0xFE 0x00 # 1
+ 0xFE 0xFE 0xF6 0xFE 0xDE 0xFE 0xFE 0x00 # 2
+ 0xFE 0xFE 0xD6 0xFE 0xEE 0xFE 0xFE 0x00 # 3
+ 0xFE 0xFE 0xD6 0xFE 0xD6 0xFE 0xFE 0x00 # 4
+ 0xFE 0xFE 0xD6 0xEE 0xD6 0xFE 0xFE 0x00 # 5
+
+: arrows
+ 0x10 0x38 0x7C 0xFE 0x38 0x38 0x38 0x00 # n
+ 0x10 0x18 0xFC 0xFE 0xFC 0x18 0x10 0x00 # e
+ 0x38 0x38 0x38 0xFE 0x7C 0x38 0x10 0x00 # s
+ 0x10 0x30 0x7E 0xFE 0x7E 0x30 0x10 0x00 # w
+
+: p1
+ 0x00 0x78 0x44 0x44 0x78 0x40 0x40 0x00
+ 0x10 0x30 0x10 0x10 0x7C 0x00
+: p2
+ 0xFE 0x86 0xBA 0xBA 0x86 0xBE 0xBE 0xFE
+ 0xC6 0xBA 0xE6 0xDE 0x82 0xFE
+
+: curs1 0xAA 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0xAA
+: curs2 0x80 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0x80
+
+: empty 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+: fill 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+: plus 0x00 0x40 0xE0 0x40
+
+# game over
+: go1 0x7E 0xFE 0xC0 0xDE 0xC6 0xFE 0x7E 0x00 0x7C 0xFE 0xC6 0xC6 0xC6 0xFE 0x7C
+: go2 0x7C 0xFE 0xC6 0xFE 0xC6 0xC6 0xC6 0x00 0xC6 0xC6 0xC6 0xC6 0xEE 0x7C 0x38
+: go3 0xC6 0xEE 0xFE 0xD6 0xC6 0xC6 0xC6 0x00 0x7E 0xFE 0xC0 0xF8 0xC0 0xFE 0x7E
+: go4 0x7E 0xFE 0xC0 0xF8 0xC0 0xFE 0x7E 0x00 0xFC 0xFE 0xC6 0xFE 0xFC 0xCE 0xC6
+
+# you win
+: yw1 0xC6 0xC6 0xEE 0x7C 0x38 0x38 0x38 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+: yw2 0x7C 0xFE 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x63 0x63 0x63 0x6B 0x7F 0x77 0x63
+: yw3 0xC6 0xC6 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x7E 0x7E 0x18 0x18 0x18 0x7E 0x7E
+: yw4 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xC6 0xE6 0xF6 0xFE 0xDE 0xCE 0xC6
+
+# title/loading screen
+: ti1 0xFE 0x82 0xAA 0x92 0xAA 0x82 0xFE 0x00 0x06 0x06 0x06 0x06 0x06 0x07 0x03
+: ti2 0x00 0x00 0x75 0x47 0x45 0x75 0x00 0x00 0x19 0x1B 0xDB 0xDB 0xDB 0xFB 0x33
+: ti3 0x00 0x00 0x5C 0x54 0x5C 0x50 0x00 0x00 0xE7 0xF7 0x36 0xF7 0xF7 0x36 0x36
+: ti4 0xFE 0xFE 0xF6 0xFE 0xDE 0xFE 0xFE 0x00 0xC0 0xE0 0x60 0xC0 0xE0 0x60 0x60
+: lo1 0x8E 0x8A 0x8A 0xEE
+: lo2 0x4C 0xAA 0xEA 0xAC
+: lo3 0xB3 0xAA 0xAA 0xAB
+: lo4 0x80 0x00 0x80 0xAA
+
+# n e s w
+: delta-x 0 1 0 -1
+: delta-y -1 0 1 0
+
+###########################################
+#
+# Register Map:
+#
+###########################################
+
+# vf is always temporary
+:alias mode ve
+:alias cursorx vd
+:alias cursory vc
+:alias dir vb
+:alias wdice v9
+:alias bdice v8
+:alias wtotal v7
+:alias btotal v6
+:alias wroll v5
+:alias broll v4
+:alias rolls v3
+# v0-v2 are temporary
+
+###########################################
+#
+# Mutable Data
+#
+###########################################
+
+# game modes (mode)
+:const MOVE 0
+:const ATTACK 1
+:const AIMOVE 2
+:const WIN 3
+:const LOSE 4
+:const RESET 5
+
+# space owners (owns-at)
+:const START 0
+:const WHITE 1
+:const BLACK 2
+:const EMPTY 3
+
+: bcd-buffer 0
+: tens 0
+: ones 0
+
+: owns-at 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+: dice-at 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+
+###########################################
+#
+# Utility Subroutines
+#
+###########################################
+
+: random-vector
+ # trashes v0-v2
+ # returns in v1
+ v1 := random 0xFF
+ v2 := v1
+ v0 := 0
+ loop
+ while v2 != 0
+ v2 <<= v2
+ v0 += vf
+ again
+ if v0 != 4 then jump random-vector
+;
+
+: vfwait
+ delay := vf
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+;
+
+: longwait
+ vf := 60
+ jump vfwait
+
+: wait
+ vf := 30
+ jump vfwait
+
+: uwait
+ vf := 10
+ jump vfwait
+
+: fillscreen
+ # trashes v0-v1
+ clear
+ i := fill
+ v0 := 0
+ loop
+ v1 := 0
+ sprite v0 v1 15
+ v1 := 15
+ sprite v0 v1 15
+ v1 := 30
+ sprite v0 v1 2
+ v0 += 8
+ if v0 != 64 then
+ again
+;
+
+: draw-end-screen
+ v0 := 16
+ v1 := 8
+ v2 := 15
+ loop
+ sprite v0 v1 15
+ i += v2
+ v0 += 8
+ if v0 != 48 then
+ again
+ v0 := key
+ mode := RESET
+;
+
+: draw-game-over
+ fillscreen
+ i := go1
+ jump draw-end-screen
+
+: draw-you-win
+ clear
+ i := yw1
+ jump draw-end-screen
+
+###########################################
+#
+# Battle
+#
+###########################################
+
+: draw-wtotal
+ # trashes v0-v2
+ v2 := 23
+ v1 := 11
+ i := bcd-buffer
+ bcd wtotal
+ i := tens load v0 i := hex v0
+ sprite v1 v2 5
+ v1 += 5
+ i := ones load v0 i := hex v0
+ sprite v1 v2 5
+;
+
+: draw-btotal
+ # trashes v0-v2
+ v2 := 23
+ v1 := 43
+ i := bcd-buffer
+ bcd btotal
+ i := tens load v0 i := hex v0
+ sprite v1 v2 5
+ v1 += 5
+ i := ones load v0 i := hex v0
+ sprite v1 v2 5
+;
+
+: draw-wroll
+ # trashes v0-v2
+ v2 := 16
+ v1 := 14
+ i := hex wroll
+ sprite v1 v2 5
+ v1 += -4
+ i := plus
+ sprite v1 v2 4
+;
+
+: draw-broll
+ # trashes v0-v2
+ v2 := 16
+ v1 := 46
+ i := hex broll
+ sprite v1 v2 5
+ v1 += -4
+ i := plus
+ sprite v1 v2 4
+;
+
+: draw-battle
+ # trashes v0-v2
+ clear
+ i := fill
+ v0 := 32
+ v1 := 0
+ loop
+ v1 := 0
+ sprite v0 v1 15
+ v1 := 15
+ sprite v0 v1 15
+ v1 := 30
+ sprite v0 v1 2
+ v0 += 8
+ if v0 != 64 then
+ again
+
+
+ # dice counters:
+ v1 := 4
+ v0 := 12
+
+ i := whitedice
+ v2 := wdice
+ v2 <<= v2
+ v2 <<= v2
+ v2 <<= v2
+ v2 += -8
+ i += v2
+ sprite v0 v1 7
+
+ i := whitedice
+ v2 := bdice
+ v2 <<= v2
+ v2 <<= v2
+ v2 <<= v2
+ v2 += -8
+ i += v2
+ v0 := 45
+ sprite v0 v1 7
+
+ draw-wtotal
+ draw-btotal
+;
+
+: b-do-roll
+ broll := random 7
+ broll += 1
+ draw-broll
+ draw-btotal
+ btotal += broll
+ draw-btotal
+;
+
+: w-do-roll
+ wroll := random 7
+ wroll += 1
+ draw-wroll
+ draw-wtotal
+ wtotal += wroll
+ draw-wtotal
+;
+
+: defender-wins
+ # set attacker to 1
+ cursor-to-index
+ i := dice-at
+ i += vf
+ v0 := 1
+ save v0
+
+ # set defender to max(1, old-1)
+ direction-to-index
+ i := dice-at
+ i += vf
+ load v0
+ v0 += -1
+ if v0 == 0 then v0 := 1
+ i := dice-at
+ i += vf
+ save v0
+;
+
+: attacker-wins
+ # set dest to attackers - 1
+ cursor-to-index
+ i := dice-at
+ i += vf
+ load v0
+ va := v0
+ va += -1
+ direction-to-index
+ i := dice-at
+ i += vf
+ v0 := va
+ save v0
+
+ # set dest to be attacker owned
+ direction-to-index
+ i := owns-at
+ i += vf
+ if mode == ATTACK then v0 := WHITE
+ if mode != ATTACK then v0 := BLACK
+ save v0
+
+ # set attackers to 1
+ cursor-to-index
+ i := dice-at
+ i += vf
+ v0 := 1
+ save v0
+;
+
+: white-wins-attack
+ attacker-wins
+
+ # if there are no black zones,
+ # game over and white wins.
+ i := owns-at
+ v1 := 0
+ loop
+ load v0
+ if v0 == BLACK then return
+ v1 += 1
+ if v1 != 18 then
+ again
+ mode := WIN
+;
+
+: black-wins-attack
+ attacker-wins
+
+ # if there are no white zones,
+ # game over and black wins.
+ i := owns-at
+ v1 := 0
+ loop
+ load v0
+ if v0 == WHITE then return
+ v1 += 1
+ if v1 != 18 then
+ again
+ mode := LOSE
+;
+
+: white-wins
+ if mode == ATTACK then jump white-wins-attack
+ jump defender-wins
+
+: black-wins
+ if mode != ATTACK then jump black-wins-attack
+ jump defender-wins
+
+: battle
+ wtotal := 0
+ btotal := 0
+ rolls := 0
+
+ draw-battle
+ wait
+
+ loop
+ if bdice > rolls then b-do-roll
+ if wdice > rolls then w-do-roll
+ wait
+ if bdice > rolls then draw-broll
+ if wdice > rolls then draw-wroll
+ uwait
+
+ rolls += 1
+
+ # get max of dice count
+ v0 := bdice
+ if wdice > bdice then v0 := wdice
+
+ if rolls != v0 then
+ again
+ longwait
+
+ # note that this gives black a fundamental
+ # advantage; it always wins ties:
+ if wtotal > btotal then jump white-wins
+ jump black-wins
+
+###########################################
+#
+# Map
+#
+###########################################
+
+: map-tile
+ # trashes v0-v2.
+ # takes an index in vf
+ # initializes i to the appropriate
+ # sprite data to draw
+
+ v2 := vf
+
+ i := dice-at
+ i += v2
+ load v0
+ v0 += -1
+ v1 <<= v0
+ v1 <<= v1
+ v1 <<= v1
+
+ i := owns-at
+ i += v2
+ load v0
+
+ i := whitedice
+ if v0 == BLACK then i := blackdice
+ i += v1
+ if v0 == EMPTY then i := empty
+;
+
+: draw-map
+ # trashes v0-v5
+ v4 := 4 # y position
+ v5 := 0 # map array index
+
+ clear
+ loop
+ v3 := 8 # x position
+ loop
+ vf := v5 map-tile
+ sprite v3 v4 7
+
+ v5 += 1
+ v3 += 8
+
+ if v3 != 56 then
+ again
+ v4 += 8
+ if v4 != 28 then
+ again
+;
+
+: draw-p1
+ i := p1
+ v0 := 0
+ v1 := 9
+ sprite v0 v1 14
+;
+
+: draw-p2
+ i := p2
+ v0 := 56
+ v1 := 9
+ sprite v0 v1 14
+;
+
+###########################################
+#
+# Cursor
+#
+###########################################
+
+: draw-cursor
+ # trash v0-v2
+ v0 <<= cursorx
+ v0 <<= v0
+ v0 <<= v0
+ v0 += 7
+
+ v1 <<= cursory
+ v1 <<= v1
+ v1 <<= v1
+ v1 += 3
+
+ v2 := v0
+ v2 += 8
+ i := curs1
+ sprite v0 v1 9
+ i := curs2
+ sprite v2 v1 9
+;
+
+: cursor-to-index
+ # leave result in vf
+ vf := cursorx
+ if cursory == 1 then vf += 6
+ if cursory == 2 then vf += 12
+;
+
+: direction-to-index
+ # trashes v0-v2
+ # leave the result in vf
+ v1 := cursorx
+ i := delta-x
+ i += dir
+ load v0
+ v1 += v0
+ if v1 == -1 then v1 := 5
+ if v1 == 6 then v1 := 0
+
+ v2 := cursory
+ i := delta-y
+ i += dir
+ load v0
+ v2 += v0
+ if v2 == -1 then v2 := 2
+ if v2 == 3 then v2 := 0
+
+ if v2 == 1 then v1 += 6
+ if v2 == 2 then v1 += 12
+ vf := v1
+;
+
+: cursor-to-xy
+ # leave result in v0/v1
+ v0 <<= cursorx
+ v0 <<= v0
+ v0 <<= v0
+ v0 += 8
+
+ v1 <<= cursory
+ v1 <<= v1
+ v1 <<= v1
+ v1 += 4
+;
+
+: erase-selected
+ # trash v0-v2
+ cursor-to-index
+ map-tile
+ cursor-to-xy
+ sprite v0 v1 7
+;
+
+: draw-arrow
+ i := arrows
+ v0 <<= dir
+ v0 <<= v0
+ v0 <<= v0
+ i += v0
+ cursor-to-xy
+ sprite v0 v1 7
+;
+
+: start-attack
+ dir := 0
+
+ # ignore spaces we don't control
+ cursor-to-index
+ i := owns-at
+ i += vf
+ load v0
+ if v0 != WHITE then return
+
+ # ignore spaces with 1 die
+ i := dice-at
+ i += vf
+ load v0
+ if v0 == 1 then return
+
+ # switch to attack selection mode
+ mode := ATTACK
+ draw-cursor
+ erase-selected
+ draw-arrow
+;
+
+: cancel-attack
+ draw-arrow
+ mode := MOVE
+ draw-cursor
+ erase-selected
+;
+
+: start-fight
+ direction-to-index
+
+ # if the adjacent tile isn't black, give up
+ i := owns-at
+ i += vf
+ load v0
+ if v0 != BLACK then return
+
+ # set up and fight
+ i := dice-at
+ i += v1
+ load v0
+ bdice := v0
+
+ i := dice-at
+ cursor-to-index
+ i += vf
+ load v0
+ wdice := v0
+
+ battle
+ if mode == WIN then return
+ draw-map
+ draw-p1
+ draw-cursor
+ mode := MOVE
+;
+
+: start-ai-turn
+ restock
+ draw-map
+ longwait
+ draw-p2
+ mode := AIMOVE
+ ai-move
+ if mode == LOSE then return
+ mode := MOVE
+ draw-p2
+ draw-p1
+;
+
+: move-cursor
+ va := key
+ draw-cursor
+ if va == 7 then cursorx += -1
+ if va == 9 then cursorx += 1
+ if va == 5 then cursory += -1
+ if va == 8 then cursory += 1
+ if va == 6 then start-attack
+ if va == 4 then start-ai-turn
+
+ if cursorx == 6 then cursorx := 0
+ if cursorx == -1 then cursorx := 5
+ if cursory == 3 then cursory := 0
+ if cursory == -1 then cursory := 2
+ draw-cursor
+;
+
+: attack-cursor
+ va := key
+ draw-cursor
+ draw-arrow
+ if va == 7 then dir := 3
+ if va == 9 then dir := 1
+ if va == 5 then dir := 0
+ if va == 8 then dir := 2
+ draw-arrow
+ draw-cursor
+ if va == 4 then cancel-attack
+ if va == 6 then start-fight
+;
+
+###########################################
+#
+# AI Opponent
+#
+###########################################
+
+: ai-pickdir
+ direction-to-index
+
+ # must be white
+ i := owns-at
+ i += vf
+ load v0
+ if v0 != WHITE then return
+
+ # is this zone weaker?
+ i := dice-at
+ i += vf
+ load v0
+ if v5 < v0 then return
+
+ # our new choice
+ v4 := dir
+ v5 := v0
+;
+
+: ai-single
+ # pick a random territory
+ v2 := random 0b1111
+ v1 := 0
+ cursorx := 0
+ cursory := 0
+ i := owns-at
+ loop
+ while v2 != 0
+ load v0
+ if v0 != EMPTY then v2 += -1
+ v1 += 1
+ cursorx += 1
+ if cursorx == 6 then cursory += 1
+ if cursorx == 6 then cursorx := 0
+ again
+ # v1 now contains the (0-17) index of
+ # our chosen territory.
+
+ # if it's not black, ignore it
+ i := owns-at
+ i += v1
+ load v0
+ if v0 != BLACK then return
+
+ # if it has 1 die, ignore it
+ i := dice-at
+ i += v1
+ load v0
+ if v0 == 1 then return
+ bdice := v0
+
+ # attack the weakest adjacent white zone
+ v4 := -1 # best direction
+ v5 := 0xFF # best die count (lower is better)
+ dir := 0 ai-pickdir
+ dir := 1 ai-pickdir
+ dir := 2 ai-pickdir
+ dir := 3 ai-pickdir
+ if v4 == -1 then return
+ dir := v4
+ wdice := v5
+
+ # show arrow
+ draw-cursor
+ longwait
+ draw-cursor
+ erase-selected
+ draw-arrow
+ longwait
+
+ # FIRE ZE MISSILES
+ battle
+ draw-map
+ draw-p2
+ wait
+;
+
+# unroll these loops so that I don't
+# need to keep track of a loop index:
+
+: ai-moves
+ ai-single
+ ai-single
+ ai-single
+ ai-single
+ ai-single
+;
+
+: ai-move
+ ai-moves
+ ai-moves
+ ai-moves
+ ai-moves
+;
+
+###########################################
+#
+# Restocking
+#
+###########################################
+
+: restock-single
+ # take index in va
+ i := owns-at
+ i += va
+ load v0
+ if v0 == EMPTY then return
+
+ vb += 1
+ i := dice-at
+ i += va
+ load v0
+ v1 <<= v1
+ v0 += vf
+ if v0 == 6 then v0 := 5
+
+ i := dice-at
+ i += va
+ save v0
+;
+
+: restock
+ va := 0 # cell index
+ vb := 0 # bit index
+ random-vector
+ loop
+ restock-single
+ va += 1
+ if vb != 8 then
+ again
+ random-vector
+ loop
+ restock-single
+ va += 1
+ if vb != 16 then
+ again
+;
+
+###########################################
+#
+# Map Generation
+#
+###########################################
+
+: fill-boards
+ # expects i to be initialized
+ # takes the fill value in v0
+ v1 := 0
+ loop
+ save v0
+ v1 += 1
+ if v1 != 18 then
+ again
+;
+
+: reset-owned
+ i := owns-at
+ v0 := START
+ jump fill-boards
+
+: reset-dice
+ i := dice-at
+ v0 := 1
+ jump fill-boards
+
+: place-empty
+ # trashes v0-v2
+ # pick an index 0-17
+ v2 := random 0b11111
+ if v2 > 17 then jump place-empty
+
+ # make sure that spot is uninitialized
+ i := owns-at
+ i += v2
+ load v0
+ if v0 != START then jump place-empty
+
+ # set it to be empty
+ i := owns-at
+ i += v2
+ v0 := EMPTY
+ save v0
+;
+
+: place-owned-single
+ # take index in va
+ i := owns-at
+ i += va
+ load v0
+ if v0 == EMPTY then return
+
+ v1 <<= v1
+ vb += 1
+ v0 := WHITE
+ if vf == 1 then v0 := BLACK
+ i := owns-at
+ i += va
+ save v0
+;
+
+: place-owned
+ va := 0 # cell index
+ vb := 0 # bit index
+ random-vector
+ loop
+ place-owned-single
+ va += 1
+ if vb != 8 then
+ again
+ random-vector
+ loop
+ place-owned-single
+ va += 1
+ if vb != 16 then
+ again
+;
+
+: random-map
+ reset-owned
+ reset-dice
+ place-empty
+ place-empty
+ place-owned
+ restock
+;
+
+###########################################
+#
+# Main
+#
+###########################################
+
+: title-screen
+ v0 := 16
+ v1 := 4
+ v2 := 0
+ v3 := 15
+ i := ti1
+ loop
+ sprite v0 v1 15
+ v0 += 8
+ v2 += 1
+ i += v3
+ if v2 != 4 then
+ again
+
+ v0 := 16
+ v1 := 27
+ v2 := 0
+ v3 := 4
+ loop
+ sprite v0 v1 4
+ v0 += 8
+ v2 += 1
+ i += v3
+ if v2 != 4 then
+ again
+;
+
+: init-game
+ random-map
+ draw-map
+ draw-p1
+ draw-cursor
+ mode := MOVE
+;
+
+: main
+ title-screen
+ init-game
+ loop
+ if mode == MOVE then move-cursor
+ if mode == ATTACK then attack-cursor
+ if mode == WIN then draw-you-win
+ if mode == LOSE then draw-game-over
+ if mode == RESET then init-game
+ again
diff --git a/resources/octoroms/deep8.8o b/resources/octoroms/deep8.8o
new file mode 100644
index 0000000..e6315c3
--- /dev/null
+++ b/resources/octoroms/deep8.8o
@@ -0,0 +1,393 @@
+###########################################
+#
+# Deep8
+#
+# A stripped down port of the Mako game
+# "Deep" for Chip8.
+# Move your boat left and right with A/D
+# Press S to drop a bomb and release S
+# to detonate it. Destroy incoming
+# squid before they tip your boat!
+#
+# This game was one of the earliest
+# programs written using Octo.
+#
+###########################################
+
+: waves
+ 0b00110011
+ 0b11101110
+
+: boat
+ 0b00000100
+ 0b00000100
+ 0b11111111
+ 0b11111111
+ 0b01111101
+
+: boat-flip
+ 0b00000000
+ 0b00000000
+ 0b01111101
+ 0b11111111
+ 0b11111111
+
+: bomb
+ 0b00001000
+ 0b00010100
+ 0b00010000
+ 0b00111000
+ 0b00111000
+ 0b00111000
+
+: fall
+ 0b01000000
+ 0b01010000
+ 0b01100000
+ 0b11110000
+ 0b01100000
+ 0b01000000
+
+: squid-0
+ 0b00111100
+ 0b00111100
+ 0b01011010
+ 0b01011010
+
+: squid-1
+ 0b00111100
+ 0b00111100
+ 0b11011011
+ 0b00100100
+
+: boom
+ 0b10010000
+ 0b00010010
+ 0b01100000
+ 0b00000000
+ 0b10000011
+ 0b00100100
+ 0b00100001
+
+: title-0
+ 0b00010000
+ 0b00010000
+ 0b00010000
+ 0b00010000
+ 0b01110000
+ 0b10010000
+ 0b10010000
+ 0b10010000
+ 0b10010000
+ 0b01110000
+: title-1
+ 0b01100000
+ 0b10010000
+ 0b11110000
+ 0b10000000
+ 0b10000000
+ 0b01110000
+: title-2
+ 0b11100000
+ 0b10010000
+ 0b10010000
+ 0b10010000
+ 0b10010000
+ 0b11100010
+ 0b10000000
+ 0b10000000
+ 0b10000000
+ 0b10000000
+
+: end-0
+ 0b11101010
+ 0b01001110
+ 0b01001010
+ 0b01001010
+: end-1
+ 0b11100111
+ 0b11000110
+ 0b10000100
+ 0b11100111
+: end-2
+ 0b01100110
+ 0b01010101
+ 0b01010101
+ 0b01010110
+
+# register scratchpad:
+: scratch 0 0 0 0
+
+# storage for BCD decoding:
+: score-digits 0 0 0
+
+# enemy data:
+: e1 1 0 0
+: e2 20 0 0
+: e3 40 0 0
+: e4 90 0 0
+
+###########################################
+#
+# Introduction
+#
+###########################################
+
+: draw-waves
+ i := waves
+ v0 := 0 # x
+ v1 := 7 # y
+ loop
+ sprite v0 v1 2
+ v0 += 8
+ while v0 != 64
+ again
+;
+
+: title
+ draw-waves
+ i := title-0
+ v0 := 16
+ v1 := 12
+ sprite v0 v1 10
+ i := title-1
+ v0 += 8
+ v1 += 4
+ sprite v0 v1 6
+ v0 += 8
+ sprite v0 v1 6
+ i := title-2
+ v0 += 8
+ sprite v0 v1 10
+ v0 := key
+;
+
+: draw-score
+ i := score-digits
+ load v2
+ i := hex v0
+ v0 := 49
+ v3 := 1
+ sprite v0 v3 5
+ i := hex v1
+ v0 += 5
+ sprite v0 v3 5
+ i := hex v2
+ v0 += 5
+ sprite v0 v3 5
+;
+
+: inc-score
+ i := scratch
+ save v3
+ draw-score
+ v9 += 1
+ i := score-digits
+ bcd v9
+ draw-score
+ i := scratch
+ load v3
+;
+
+: init-game
+ clear
+ draw-waves
+ v8 := 0 # enemy tick timer
+ v9 := 0 # player score
+ va := 32 # player horizontal position
+ vb := 1 # player vertical position
+ vc := 10 # bomb horizontal position
+ vd := 10 # bomb vertical position
+ ve := 0 # bomb state (0=invisible, 1=falling, 2+=exploding)
+
+ # draw initial boat
+ i := boat
+ sprite va vb 5
+
+ # draw initial score
+ draw-score
+;
+
+###########################################
+#
+# Game Over
+#
+###########################################
+
+: delay-loop
+ v0 := 0
+ loop
+ v0 += 1
+ while v0 != 32
+ again
+;
+
+: game-over
+ # erase player
+ i := boat
+ sprite va vb 5
+
+ # flip boat
+ i := boat-flip
+ sprite va vb 5
+
+ # animate the player sinking
+ i := fall
+ vb := 8
+ sprite va vb 6
+ loop
+ delay-loop
+ sprite va vb 6
+ vb += 1
+ sprite va vb 6
+ while vb != 26
+ again
+ sprite va vb 6
+
+ # show 'the end'
+ va := 20
+ vb := 14
+ i := end-0
+ sprite va vb 4
+ i := end-1
+ va += 8
+ sprite va vb 4
+ i := end-2
+ va += 8
+ sprite va vb 4
+
+ loop again
+
+###########################################
+#
+# Enemy Logic
+#
+###########################################
+
+# v0 - enemy timer. (0=swim, >0=time until next spawn)
+# v1 - horizontal position
+# v2 - vertical position
+
+: enemy-sprite
+ v4 := 1
+ v4 &= v2
+ i := squid-0
+ if v4 == 1 then i := squid-1
+;
+
+: spawn-enemy
+ v1 := random 63
+ v2 := 28
+ enemy-sprite
+ sprite v1 v2 4
+;
+
+: count-enemy
+ v0 -= v3
+ if v0 == 0 then spawn-enemy
+;
+
+: update-enemy
+ if v0 != 0 then jump count-enemy
+ enemy-sprite
+ sprite v1 v2 4
+ v2 -= v3
+
+ # kill the player if
+ # an enemy reaches the surface:
+ if v2 == 8 then jump game-over
+
+ enemy-sprite
+ sprite v1 v2 4
+
+ # die on collision:
+ if vf == 0 then return
+ sprite v1 v2 4
+ v0 := random 31
+ v0 += 10
+ inc-score
+;
+
+: update-enemies
+ v3 := 1
+ v8 ^= v3
+ if v8 == 0 then return
+ i := e1 load v2 update-enemy i := e1 save v2
+ i := e2 load v2 update-enemy i := e2 save v2
+ i := e3 load v2 update-enemy i := e3 save v2
+ i := e4 load v2 update-enemy i := e4 save v2
+;
+
+###########################################
+#
+# Player/Bomb Logic
+#
+###########################################
+
+: bomb-fire
+ if ve != 0 then return
+ vc := va
+ vd := 10
+ ve := 1
+ i := bomb
+ sprite vc vd 6
+;
+
+: update-player
+ v2 := 3
+ v1 := va
+ v0 := 7
+ if v0 key then va -= v2
+ v0 := 9
+ if v0 key then va += v2
+ v0 := 8
+ if v0 key then bomb-fire
+ if v1 == va then return
+ i := boat
+ sprite v1 vb 5
+ sprite va vb 5
+;
+
+: bomb-explode
+ ve := 2
+ i := boom
+ sprite vc vd 7
+;
+
+: bomb-falling
+ i := bomb
+ sprite vc vd 6
+ vd += 1
+ if vd == 25 then jump bomb-explode
+ v0 := 8
+ if v0 -key then jump bomb-explode
+ sprite vc vd 6
+;
+
+: bomb-exploding
+ ve += 1
+ if ve != 4 then return
+ ve := 0
+ i := boom
+ sprite vc vd 7
+;
+
+: update-bomb
+ if ve == 1 then jump bomb-falling
+ if ve != 0 then jump bomb-exploding
+;
+
+###########################################
+#
+# Main Loop
+#
+###########################################
+
+: main
+ title
+ init-game
+ loop
+ update-player
+ update-bomb
+ update-enemies
+ again
diff --git a/resources/octoroms/default.8o b/resources/octoroms/default.8o
new file mode 100644
index 0000000..68a7319
--- /dev/null
+++ b/resources/octoroms/default.8o
@@ -0,0 +1,34 @@
+# Chip8 is a virtual machine designed in 1977 for programming video games.
+# Octo is a high level assembler, disassembler and simulator for Chip8.
+# Click 'Run' and then press ASWD to move the sprite around the screen.
+# Click the Octo logo for source, documentation and examples.
+
+:alias px v1
+:alias py v2
+
+: main
+ px := random 0b0011111
+ py := random 0b0001111
+ i := person
+ sprite px py 8
+
+ loop
+ # erase the player, update its position and then redraw:
+ sprite px py 8
+ v0 := OCTO_KEY_W if v0 key then py += -1
+ v0 := OCTO_KEY_S if v0 key then py += 1
+ v0 := OCTO_KEY_A if v0 key then px += -1
+ v0 := OCTO_KEY_D if v0 key then px += 1
+ sprite px py 8
+
+ # lock the framerate of this program via the delay timer:
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+ vf := 3
+ delay := vf
+ again
+
+: person
+ 0x70 0x70 0x20 0x70 0xA8 0x20 0x50 0x50
diff --git a/resources/octoroms/demos/chipenstein.8o b/resources/octoroms/demos/chipenstein.8o
new file mode 100644
index 0000000..972d59a
--- /dev/null
+++ b/resources/octoroms/demos/chipenstein.8o
@@ -0,0 +1,310 @@
+###########################################
+#
+# Chipenstein 3D
+#
+# A work-in-progress experiment
+# in creating a raycast 2.5d shooter.
+# Uses PWM techniques to simulate extra
+# colors and must run at 1000 cycles/frame.
+# Epilepsy warning!
+#
+###########################################
+
+# The largest a map for this approach could be is 16x16.
+# Technically we don't have to surround all sides with
+# walls due to wraparound, but if there is a ray path
+# around the map without hitting a wall our raycast
+# routine will get stuck in an infinite loop:
+
+: map-data
+ 0xFF 0x01 0xFF 0x01 0xFF 0x01 0xFF 0x01 0xFF 0x01
+ 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF
+ 0xFF 0xFF 0x01 0xFF 0x00 0x00 0x00 0x00 0x00 0x01
+ 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF
+ 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01
+ 0x01 0x00 0xFF 0x01 0x00 0x00 0x00 0xFF 0x00 0xFF
+ 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01
+ 0x01 0xFF 0x01 0xFF 0x01 0xFF 0x01 0xFF 0x01 0xFF
+
+: collision-test
+ # take whole x/y in vc/vd, return map color in v0
+ i := map-data
+ vF := 0b111
+ vc &= vF
+ i += vc # + (x % 8)
+ vd &= vF
+ v0 <<= vd
+ v0 <<= v0
+ v0 <<= v0
+ i += v0 # + (y % 8)*8
+ load v0 # the current map color
+;
+
+: player-position
+ 0 0 0 0 # scratchpad
+
+: save-position
+ v0 := va
+ v1 := vb
+ v2 := vc
+ v3 := vd
+ i := player-position
+ save v3
+;
+
+: restore-position
+ i := player-position
+ load v3
+ va := v0
+ vb := v1
+ vc := v2
+ vd := v3
+;
+
+###########################################
+#
+# Raycasting
+#
+###########################################
+
+# This is a table of the deltas and magnitudes for a fixed-point representation
+# of a cosine function. 64 entries correspond to ~5.6 degrees each.
+# deltas are interleaved with magnitudes- 0 for positive and 1 for negative.
+# deltas are normalized to slightly over half a step to make the distance calculations
+# come up with a maximum distance of roughly 15. Generated like so:
+#
+# for(int x = 0; x < 64; x++) {
+# double s = Math.cos(Math.PI*2/64*x);
+# System.out.format("0x%02X 0x%02X ", (int)(140 * Math.abs(s)), (s>=0) ?0 : 1);
+# if (x % 8 == 7) { System.out.println(); }
+# }
+
+: cosine-table
+ 0x8C 0x00 0x8B 0x00 0x89 0x00 0x85 0x00 0x81 0x00 0x7B 0x00 0x74 0x00 0x6C 0x00
+ 0x62 0x00 0x58 0x00 0x4D 0x00 0x41 0x00 0x35 0x00 0x28 0x00 0x1B 0x00 0x0D 0x00
+ 0x00 0x00 0x0D 0x01 0x1B 0x01 0x28 0x01 0x35 0x01 0x41 0x01 0x4D 0x01 0x58 0x01
+ 0x62 0x01 0x6C 0x01 0x74 0x01 0x7B 0x01 0x81 0x01 0x85 0x01 0x89 0x01 0x8B 0x01
+ 0x8C 0x01 0x8B 0x01 0x89 0x01 0x85 0x01 0x81 0x01 0x7B 0x01 0x74 0x01 0x6C 0x01
+ 0x62 0x01 0x58 0x01 0x4D 0x01 0x41 0x01 0x35 0x01 0x28 0x01 0x1B 0x01 0x0D 0x01
+ 0x00 0x01 0x0D 0x00 0x1B 0x00 0x28 0x00 0x35 0x00 0x41 0x00 0x4D 0x00 0x58 0x00
+ 0x62 0x00 0x6C 0x00 0x74 0x00 0x7B 0x00 0x81 0x00 0x85 0x00 0x89 0x00 0x8B 0x00
+
+: get-cosine
+ # takes angle in v0,
+ # returns delta, magnitude in v0, v1
+ vf := 0b111111
+ v0 &= vf # mod 64
+ v0 <<= v0
+ i := cosine-table
+ i += v0
+ load v1
+;
+
+: get-angles
+ # takes angle in v3,
+ # unpacks x/y delta/mag into v4-v7
+
+ # cos(a) = cosine-table[(a % 64) << 1]
+ v0 := v3
+ get-cosine
+ v4 := v0 # x delta
+ v5 := v1 # x magnitude
+
+ # sin(a) = cosine-table[(a+16 % 64) << 1]
+ v0 := v3
+ v0 += 16
+ get-cosine
+ v6 := v0 # y delta
+ v7 := v1 # y magnitude
+;
+
+: delta-step
+ vf := 0
+ if v5 == 0 then va += v4 # positive x delta
+ if vf == 1 then vc += 1 # carry in
+
+ vf := 0
+ if v5 == 1 then va -= v4 # negative x delta
+ if vf == 0 then vc += -1 # borrow out
+
+ vf := 0
+ if v7 == 0 then vb += v6 # positive y delta
+ if vf == 1 then vd += 1 # carry in
+
+ vf := 0
+ if v7 == 1 then vb -= v6 # negative y delta
+ if vf == 0 then vd += -1 # borrow out
+;
+
+: heights
+ # {height, color}
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+
+: raycast
+ save-position
+
+ #v0-v1 are available as scratch.
+
+ v8 := 0 # loop index * 2
+ v3 := v9 # scan angle
+ v3 += -8
+ loop
+ get-angles
+
+ v2 := 16 # height+1
+ loop
+ delta-step
+ v2 += -1 # count down height
+ collision-test # returns result in v0
+ while v2 != 0
+ if v0 == 0 then
+ again
+
+ # save height and color in heights table:
+ i := heights
+ i += v8
+ v1 := v0
+ v0 := v2
+ save v1
+
+ vf := v3
+ restore-position
+ v3 := vf
+
+ v8 += 2
+ v3 += 1 # 16 slices * 5.625 = 90 degree FoV
+ if v8 != 32 then
+ again
+;
+
+###########################################
+#
+# Rendering
+#
+###########################################
+
+# this 45-byte table allows me to construct all the necessary vertical
+# strips using only immediate i and a height offset:
+
+: top 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+: btm 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+: sync
+ # take advantage of the fact that vf
+ # is always a free register if we aren't
+ # doing any operations that can carry:
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+ vf := 1
+ delay := vf
+;
+
+: draw-heights
+ # registers for draw loop:
+ # v0 is used to store the current height
+ # v1 stores the 'color' bitmask
+ v2 := 0 # heightmap index
+ v3 := 1 # upward strip Y
+ v4 := 16 # down strip Y
+ v5 := 0 # strip X (avoid shifting v1 in loop)
+ v6 := 15 # constant
+
+ sync clear
+ loop
+ # fetch height of column
+ i := heights
+ i += v2
+ load v1
+
+ # apply PWM duty cycle for 'color'
+ v1 &= ve
+ if v1 == 0 then v0 := 0
+
+ # draw upward strip
+ i := top
+ i += v0
+ v0 =- v6
+ sprite v5 v3 15
+
+ # draw downward strip
+ i := btm
+ i += v0
+ sprite v5 v4 15
+
+ # draw 16 columns
+ v2 += 2
+ v5 += 4
+ if v2 != 32 then
+ again
+
+;
+
+###########################################
+#
+# Main Loop
+#
+###########################################
+
+: gun
+ 0x10 0x18 0x24 0x24 0x24 0x24 0x5A 0xC2 0xE7 0x7F 0x3E 0x21 0x41 0x41
+
+: spin-left
+ v9 += -1
+ raycast
+;
+
+: spin-right
+ v9 += 1
+ raycast
+;
+
+: walk-forward
+ v3 := v9
+ get-angles
+ delta-step
+ raycast
+;
+
+: walk-backward
+ v3 := v9
+ get-angles
+ vf := 1
+ v5 ^= vf
+ v7 ^= vf
+ delta-step
+ raycast
+;
+
+: main
+ # global state:
+ v9 := 30 # player angle
+ va := 128 # x position (fractional)
+ vb := 128 # y position (fractional)
+ vc := 5 # x position (whole)
+ vd := 5 # y position (whole)
+ ve := 0 # rolling frame counter
+
+ raycast
+
+ loop
+ draw-heights
+ i := gun
+ v0 := 38
+ v1 := 18
+ sprite v0 v1 14
+
+ ve += 1
+
+ v0 := 7
+ if v0 key then spin-left
+ v0 := 9
+ if v0 key then spin-right
+ v0 := 5
+ if v0 key then walk-forward
+ v0 := 8
+ if v0 key then walk-backward
+ again
diff --git a/resources/octoroms/demos/computer.8o b/resources/octoroms/demos/computer.8o
new file mode 100644
index 0000000..0febf09
--- /dev/null
+++ b/resources/octoroms/demos/computer.8o
@@ -0,0 +1,83 @@
+###########################################
+#
+# Sprite scrolling demo:
+#
+# Draw a computer monitor with a scrolling
+# image by using two copies of its sprites
+# and adjusting an offset into that data
+# before each draw.
+#
+###########################################
+
+: main
+ # draw the background:
+
+ v0 := 16
+ v1 := 4
+ i := comp-LT
+ sprite v0 v1 11
+ v0 += 8
+ i := comp-T
+ sprite v0 v1 3
+ v0 += 8
+ sprite v0 v1 3
+ v0 += 8
+ i := comp-RT
+ sprite v0 v1 11
+
+ v0 := 16
+ v1 += 11
+ i := comp-LB
+ sprite v0 v1 15
+ v0 += 8
+ v1 += 7
+ i := comp-B
+ sprite v0 v1 8
+ v0 += 8
+ sprite v0 v1 8
+ v0 += 8
+ v1 += -7
+ i := comp-RB
+ sprite v0 v1 15
+
+ # main animation loop:
+
+ va := 24 # left x
+ vb := 32 # right x
+ vc := 7 # common y
+ v9 := 0 # scroll offset
+ v8 := 0b1111 # constant
+ draw-texture
+ loop
+ draw-texture
+ v9 += 1
+ v9 &= v8
+ draw-texture
+
+ vF := 4
+ delay := vF
+ loop
+ vF := delay
+ if vF != 0 then
+ again
+ again
+
+: draw-texture
+ i := grenade-L
+ i += v9
+ sprite va vc 15
+ i := grenade-R
+ i += v9
+ sprite vb vc 15
+;
+
+: grenade-L 0x0F 0x30 0x7C 0x7C 0xF8 0xF4 0xE0 0xE8 0xF0 0xE8 0xE0 0x68 0x70 0x34 0x08 0x00
+ 0x0F 0x30 0x7C 0x7C 0xF8 0xF4 0xE0 0xE8 0xF0 0xE8 0xE0 0x68 0x70 0x34 0x08 0x00
+: grenade-R 0xF0 0x0C 0x46 0x66 0x33 0x13 0x0B 0x0B 0x1F 0x0F 0x0F 0x1E 0x1E 0x1C 0x30 0x00
+ 0xF0 0x0C 0x46 0x66 0x33 0x13 0x0B 0x0B 0x1F 0x0F 0x0F 0x1E 0x1E 0x1C 0x30 0x00
+: comp-LT 0x3F 0x3F 0x3F 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C
+: comp-RT 0xFC 0xFC 0xFC 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C
+: comp-T 0xFF 0xFF 0xFF
+: comp-LB 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3F 0x3F 0x3F 0x00 0x07 0x1C 0x73 0x7F
+: comp-RB 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0xFC 0xFC 0xFC 0x00 0xE0 0xD8 0x26 0xFE
+: comp-B 0xFF 0xFF 0xFF 0xFF 0x33 0xCC 0x33 0xFF
diff --git a/resources/octoroms/demos/draw.8o b/resources/octoroms/demos/draw.8o
new file mode 100644
index 0000000..c25cb4c
--- /dev/null
+++ b/resources/octoroms/demos/draw.8o
@@ -0,0 +1,23 @@
+###########################################
+#
+# Key input test program.
+# Move a dot around the screen in response
+# to keypresses.
+#
+###########################################
+
+: dot
+ 0b10000000
+
+: main
+ v0 := 10
+ v1 := 10
+ i := dot
+
+ loop
+ sprite v0 v1 1
+ v2 := 7 if v2 key then v0 += -1 # left
+ v2 := 9 if v2 key then v0 += 1 # right
+ v2 := 5 if v2 key then v1 += -1 # up
+ v2 := 8 if v2 key then v1 += 1 # down
+ again
diff --git a/resources/octoroms/demos/smile.8o b/resources/octoroms/demos/smile.8o
new file mode 100644
index 0000000..7c86b11
--- /dev/null
+++ b/resources/octoroms/demos/smile.8o
@@ -0,0 +1,30 @@
+###########################################
+#
+# Smile
+#
+# Draw smiley faces on the screen randomly
+# and periodically clear the display.
+#
+###########################################
+
+: smile
+ 0b00100100
+ 0b00100100
+ 0b00000000
+ 0b10000001
+ 0b01000010
+ 0b00111100
+
+: main
+ i := smile
+ loop
+ v2 := 0
+ loop
+ v0 := random 0b00111111
+ v1 := random 0b00011111
+ sprite v0 v1 6
+ v2 += 1
+ if v2 != 32 then
+ again
+ clear
+ again
diff --git a/resources/octoroms/demos/sort1.8o b/resources/octoroms/demos/sort1.8o
new file mode 100644
index 0000000..3abf488
--- /dev/null
+++ b/resources/octoroms/demos/sort1.8o
@@ -0,0 +1,73 @@
+###########################################
+#
+# An Octo implementation of an in-place
+# selection sort capable of operating on
+# arrays of a fixed size up to 256 elements.
+#
+###########################################
+
+:const SIZE 16
+:calc SIZE-1 { SIZE - 1 }
+
+:alias here v1
+:alias rest v2
+:alias min-index v3
+:alias min-value v4
+:alias here-value v5
+
+: selection-sort
+ here := 0
+ loop
+ min-index := here
+ i := data
+ i += here
+ load v0
+ min-value := v0
+ here-value := v0
+
+ rest := here
+ rest += 1
+ i := data
+ i += rest
+ loop
+ load v0
+ if v0 < min-value begin
+ min-index := rest
+ min-value := v0
+ end
+
+ rest += 1
+ if rest != SIZE then
+ again
+
+ if min-index != here begin
+ v0 := here-value
+ i := data
+ i += min-index
+ save v0
+
+ v0 := min-value
+ i := data
+ i += here
+ save v0
+ end
+
+ here += 1
+ if here != SIZE-1 then
+ again
+;
+
+###########################################
+#
+# Usage Example
+#
+###########################################
+
+: data 14 5 15 6 1 3 10 7 0 9 11 4 2 13 8 12
+
+: main
+ selection-sort
+ i := data
+ load vf
+ :breakpoint sort-complete
+;
diff --git a/resources/octoroms/demos/sort2.8o b/resources/octoroms/demos/sort2.8o
new file mode 100644
index 0000000..9cd5b32
--- /dev/null
+++ b/resources/octoroms/demos/sort2.8o
@@ -0,0 +1,116 @@
+###########################################
+#
+# An Octo implementation of an in-place
+# heap sort capable of operating on
+# arrays of a fixed size up to 255 elements.
+#
+###########################################
+
+:const SIZE 16
+:calc SIZE-1 { SIZE - 1 }
+:calc LIMIT { ( SIZE / 2 ) + 1 }
+
+:alias left-val v0
+:alias right-val v1
+:alias start v2
+:alias root v3
+:alias last v4
+:alias best v5
+:alias left v6
+:alias best-val v7
+:alias root-val v8
+
+: heap-sort
+ start := LIMIT
+ last := SIZE
+ loop
+ root := start
+ sift-down
+ start += -1
+ if start != -1 then
+ again
+
+ start := SIZE-1
+ loop
+ # swap data[0] with data[start]:
+ i := data
+ load v0
+ vf := v0
+
+ i := data
+ i += start
+ load v0
+ i := data
+ save v0
+
+ i := data
+ i += start
+ v0 := vf
+ save v0
+
+ start += -1
+ root := 0
+ last := start
+ sift-down
+
+ if start != 0 then
+ again
+;
+
+: assign-best
+ best := left
+ best-val := left-val
+ jump found-best
+
+: sift-down
+ i := data
+ i += root
+ load v0
+ root-val := v0
+
+ loop
+ left <<= root
+
+ if left > last then return
+
+ i := data
+ i += left
+ load v1
+
+ best := left
+ best += 1
+ best-val := right-val
+
+ if left-val > right-val then jump assign-best
+ if left == last then jump assign-best
+ : found-best
+
+ if root-val >= best-val then return
+
+ i := data
+ i += root
+ v0 := best-val
+ save v0
+
+ i := data
+ i += best
+ v0 := root-val
+ save v0
+
+ root := best
+ again
+
+###########################################
+#
+# Usage Example
+#
+###########################################
+
+: data 14 5 15 6 1 3 10 7 0 9 11 4 2 13 8 12
+
+: main
+ heap-sort
+ i := data
+ load vf
+ :breakpoint sort-complete
+;
diff --git a/resources/octoroms/demos/sort3.8o b/resources/octoroms/demos/sort3.8o
new file mode 100644
index 0000000..906ccb3
--- /dev/null
+++ b/resources/octoroms/demos/sort3.8o
@@ -0,0 +1,131 @@
+###########################################
+#
+# An Octo implementation of an unusual
+# sorting algorithm which combines an
+# optimal sorting network and a merge pass.
+# This implementation is designed to sort
+# exactly 16 items. The 'sort-8' subroutine
+# can be used alone to sort the values
+# stored in the bottom 8 registers.
+#
+###########################################
+
+: sort-8
+ # compare-and-swap:
+ :macro cas A B {
+ if A > B begin
+ vf := A
+ A := B
+ B := vf
+ end
+ }
+
+ cas v0 v1
+ cas v2 v3
+ cas v4 v5
+ cas v6 v7
+ cas v0 v2
+ cas v4 v6
+ cas v1 v3
+ cas v0 v4
+ cas v5 v7
+ cas v3 v7
+ cas v1 v5
+ cas v3 v5
+ cas v2 v6
+ cas v2 v4
+ cas v1 v2
+ cas v3 v6
+ cas v2 v4
+ cas v5 v6
+ cas v3 v4
+;
+
+: heap1 0 0 0 0 0 0 0 0
+: heap2 0 0 0 0 0 0 0 0
+
+:alias val1 v8
+:alias val2 v9
+:alias dest va
+:alias index1 vb
+:alias index2 vc
+
+: fused-sort
+ i := data
+ load v7
+ sort-8
+ val1 := v0
+ i := heap1
+ save v7
+
+ i := data
+ load v7
+ load v7 # cheaper than adding an offset of 8
+ sort-8
+ val2 := v0
+ i := heap2
+ save v7
+
+ index1 := 1
+ index2 := 1
+ dest := 0
+
+: merge
+ if val1 > val2 then jump merge-2
+ append-1
+ if index1 != 9 then jump merge
+ loop
+ append-2
+ if dest == 16 then return
+ again
+
+: merge-2
+ append-2
+ if index2 != 9 then jump merge
+ loop
+ append-1
+ if dest == 16 then return
+ again
+
+: append-1
+ v0 := val1
+ i := data
+ i += dest
+ save v0
+ dest += 1
+
+ i := heap1
+ i += index1
+ load v0
+ val1 := v0
+ index1 += 1
+;
+
+: append-2
+ v0 := val2
+ i := data
+ i += dest
+ save v0
+ dest += 1
+
+ i := heap2
+ i += index2
+ load v0
+ val2 := v0
+ index2 += 1
+;
+
+###########################################
+#
+# Usage Example
+#
+###########################################
+
+: data 14 5 15 6 1 3 10 7 0 9 11 4 2 13 8 12
+
+: main
+ fused-sort
+ i := data
+ load vf
+ :breakpoint sort-complete
+;
diff --git a/resources/octoroms/demos/sort4.8o b/resources/octoroms/demos/sort4.8o
new file mode 100644
index 0000000..6ed0202
--- /dev/null
+++ b/resources/octoroms/demos/sort4.8o
@@ -0,0 +1,90 @@
+###########################################
+#
+# An Octo implementation of a
+# bucket sort capable of operating on
+# arrays of a fixed size up to 256 elements,
+# with values ranging 0-15.
+# This technique could be generalized
+# for full-byte values given a larger
+# bucket array.
+#
+###########################################
+
+:const SIZE 16
+:const MAX_VAL 16
+
+: empty
+ 0 0 0 0 0 0 0 0
+
+: buckets
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+
+: bucket-sort
+ # zero bucket array
+ i := empty
+ load v7
+ save v7 # unrolled loop to zero bucket array
+ save v7 # and initialize loop counters for later.
+
+ # bucket the elements
+ # v1 is data index (already 0)
+ loop
+ # load data
+ i := data
+ i += v1
+ load v0
+ vf := v0
+
+ # increment bucket
+ i := buckets
+ i += vf
+ load v0
+ v0 += 1
+ i := buckets
+ i += vf
+ save v0
+
+ v1 += 1
+ if v1 != SIZE then
+ again
+
+ # unpack bucket counts
+ # v1 is temporary count
+ # v2 is data index (already 0)
+ # v3 is bucket index (already 0)
+ loop
+ i := buckets
+ i += v3
+ load v0
+ if v0 != 0 begin
+ v1 := v0
+ v0 := v3
+ i := data
+ i += v2
+ v2 += v1
+ loop
+ save v0
+ v1 += -1
+ if v1 != 0 then
+ again
+ end
+ v3 += 1
+ if v3 != MAX_VAL then
+ again
+;
+
+###########################################
+#
+# Usage Example
+#
+###########################################
+
+: data 14 5 15 6 1 3 10 7 0 9 11 4 2 13 8 12
+
+: main
+ bucket-sort
+ i := data
+ load vf
+ :breakpoint sort-complete
+;
+
diff --git a/resources/octoroms/demos/stack1.8o b/resources/octoroms/demos/stack1.8o
new file mode 100644
index 0000000..c8e87b5
--- /dev/null
+++ b/resources/octoroms/demos/stack1.8o
@@ -0,0 +1,74 @@
+###########################################
+#
+# A modular stack data structure using
+# a v0-based calling convention.
+# vf is used as a working temporary
+# register because it is "fragile".
+# maximum stack size is 256 bytes.
+#
+###########################################
+
+# the first value is the pointer index
+: stack 0 0 0 0 0 0 0 0 0
+
+: push
+ # modifies vf, v0
+ # takes argument in v0
+ # stack[++stack[0] + 1] := v0
+
+ i := stack
+ vf := v0
+ load v0
+ i += v0
+ v0 := vf
+ save v0
+
+ i := stack
+ load v0
+ v0 += 1
+ i := stack
+ save v0
+;
+
+: pop
+ # modifies vf, v0
+ # returns result in v0
+ # v0 := stack[stack[0]-- + 1]
+
+ i := stack
+ load v0
+ v0 += -1
+ vf := v0
+ i := stack
+ save v0
+ i += vf
+ load v0
+;
+
+###########################################
+#
+# Usage Example:
+#
+###########################################
+
+: print
+ # takes an arg in v0
+ i := hex v0
+ sprite va vb 5
+ va += 6
+;
+
+: main
+ va := 3
+ vb := 3
+
+ v0 := 5 push
+ v0 := 3 push
+ v0 := 1 push
+ pop print
+ v0 := 9 push
+ pop print
+ pop print
+ pop print
+
+ # should print '1935'
diff --git a/resources/octoroms/demos/stack2.8o b/resources/octoroms/demos/stack2.8o
new file mode 100644
index 0000000..72c330b
--- /dev/null
+++ b/resources/octoroms/demos/stack2.8o
@@ -0,0 +1,80 @@
+###########################################
+#
+# A modular stack data structure using
+# a vf-based calling convention.
+# This approach can only be extended up to
+# a size 16 stack.
+#
+###########################################
+
+: scratch 0 0 0 0 0 0 0 0
+: under 0
+: stack 0
+: over 0 0 0 0 0 0 0
+
+: push
+ # here we take our argument from vf.
+ # and do not corrupt any other registers.
+ # we can eliminate the scratch saving
+ # and restoring if low registers don't
+ # need to be preserved.
+
+ i := scratch
+ save v6
+
+ i := stack
+ load v6
+ i := over
+ save v6
+ i := stack
+ v0 := vf
+ save v0
+
+ i := scratch
+ load v6
+;
+
+: pop
+ # result is left in vf.
+ # again, we don't corrupt any other registers.
+
+ i := scratch
+ save v7
+
+ i := stack
+ load v7
+ vf := v0
+ i := under
+ save v7
+
+ i := scratch
+ load v7
+;
+
+###########################################
+#
+# Usage Example:
+#
+###########################################
+
+: print
+ # takes an arg in vf
+ i := hex vf
+ sprite va vb 5
+ va += 6
+;
+
+: main
+ va := 3
+ vb := 3
+
+ vf := 5 push
+ vf := 3 push
+ vf := 1 push
+ pop print
+ vf := 9 push
+ pop print
+ pop print
+ pop print
+
+ # should print '1935'
diff --git a/resources/octoroms/demos/stack3.8o b/resources/octoroms/demos/stack3.8o
new file mode 100644
index 0000000..9d60131
--- /dev/null
+++ b/resources/octoroms/demos/stack3.8o
@@ -0,0 +1,55 @@
+###########################################
+#
+# A modular stack data structure using
+# a v0-based calling convention.
+# This approach reserves a dedicated
+# register for a stack pointer and as
+# a result is very simple and fast.
+# maximum stack size is 256 bytes.
+#
+###########################################
+
+:alias stack-ptr vd
+: stack 0 0 0 0 0 0 0 0
+
+: push
+ i := stack
+ i += stack-ptr
+ save v0
+ stack-ptr += 1
+;
+
+: pop
+ stack-ptr += -1
+ i := stack
+ i += stack-ptr
+ load v0
+;
+
+###########################################
+#
+# Usage Example:
+#
+###########################################
+
+: print
+ # takes an arg in v0
+ i := hex v0
+ sprite va vb 5
+ va += 6
+;
+
+: main
+ va := 3
+ vb := 3
+
+ v0 := 5 push
+ v0 := 3 push
+ v0 := 1 push
+ pop print
+ v0 := 9 push
+ pop print
+ pop print
+ pop print
+
+ # should print '1935'
diff --git a/resources/octoroms/eaty.8o b/resources/octoroms/eaty.8o
new file mode 100644
index 0000000..0ce79d2
--- /dev/null
+++ b/resources/octoroms/eaty.8o
@@ -0,0 +1,1320 @@
+###########################################
+#
+# Eaty The Alien
+#
+# An incredible intergalactic adventure.
+#
+# Use ASWD to move Eaty.
+# Use E to activate Eaty's Magic Neck.
+#
+# Icons in the lower right corner indicate
+# when you are standing on a special area.
+# Find all three parts of Eaty's phone,
+# activate it at the "C" marker and
+# be on the marker with 4 arrows by
+# the time the countdown ends.
+# Watch your life force- every step you
+# take on this inhospitable planet drains
+# your health. Consume Skittles(tm)
+# candies and drain the essence of flowers
+# to stay alive.
+#
+###########################################
+
+:alias px vd
+:alias py vc
+:alias pdir vb
+:alias pframe va
+:alias has-item v9 #(in pits)
+:alias has-enemy v9 #(on ground)
+:alias is-dead v8
+:alias roomindex v7
+:alias pitindex v6
+:alias inventory v5
+:alias timer v4
+
+# v0-v3 are reserved as temporaries.
+
+:const FACE_LEFT 0
+:const FACE_RIGHT 128
+:const KEY_LEFT 7
+:const KEY_RIGHT 9
+:const KEY_UP 5
+:const KEY_DOWN 8
+:const KEY_NECK 6
+
+:const ITEM_X 12
+:const ITEM_Y 32
+:const START_LIFE 2
+:const ROSE_LIFE 3
+:const PIECES_LIFE 1
+:const PART_1 8
+:const PART_2 16
+:const PART_3 32
+:const ALL_PARTS 56
+
+:const NOTHING 0
+:const ROSE 1 # (in pit)
+:const PIECES 1 # (on ground)
+:const NECK_ESP 2
+
+:const CALL_SHIP 4
+:const GO_SHIP 5
+
+:const TIMER_LO 20
+:const TIMER_HI 8
+
+###########################################
+#
+# Main Logic
+#
+###########################################
+
+: main
+ loop
+ draw-title
+
+ hires
+ clear
+ randomize-loot
+
+ v0 := START_LIFE
+ v1 := 0
+ i := life-force-hi
+ save v1
+
+ px := 60
+ py := 16
+ pdir := 0
+ pframe := 0
+ has-item := 0
+ is-dead := 0
+ roomindex := 4
+ inventory := 0
+ timer := 0
+
+ draw-room
+ draw-hud
+ draw-eaty
+
+ main-loop
+ again
+
+: draw-title
+ lores
+ clear
+ v0 := 0
+ v1 := 0
+ v2 := 32
+ i := title-screen
+ loop
+ sprite v0 v1 0
+ v0 += 16
+ i += v2
+ if v0 == 64 then v1 += 16
+ if v0 == 64 then v0 := 0
+ if v1 != 32 then
+ again
+ v0 := 48
+ v1 := 22
+ v2 := KEY_NECK
+ i := fingat
+ loop
+ sprite v0 v1 1
+ if v2 -key then
+ again
+: wait-release
+ loop
+ if v2 key then
+ again
+;
+
+: draw-end
+ # display image
+ lores
+ clear
+ v0 := 16
+ v1 := 4
+ sprite v0 v1 0
+ v0 := 32
+ i += v0
+ sprite v0 v1 0
+
+ # show score
+ v1 := 22
+ v2 := -7
+ i := life-force-hi
+ draw-pair
+ i := life-force-lo
+ draw-pair
+
+ v0 := key
+ jump wait-release
+
+: main-loop
+
+ spawn-room
+ loop
+ if has-enemy == 1 then draw-enemy
+
+ draw-eaty
+ draw-hud
+ save-position
+ move-overworld
+ dec-timer
+ if is-dead != 0 then return
+
+ draw-eaty
+ if vf != 0 then overworld-collide
+ if has-enemy == 1 then move-enemy
+ if has-enemy == 1 then draw-enemy
+ draw-hud
+
+ sync
+ again
+;
+
+###########################################
+#
+# Utility Routines
+#
+###########################################
+
+: animate-magic-neck
+ i := eaty-neck
+ i += pdir
+ v0 := 32
+ v1 := 0
+ loop
+ sprite px py 0
+ sync
+ sprite px py 0
+ i += v0
+ v1 += 1
+ if v1 == 3 then buzzer := v1
+ if v1 != 4 then
+ again
+;
+
+: animate-death
+ i := eaty-walk
+ sprite px py 0
+ sync
+ sprite px py 0
+ i := die-0
+ sprite px py 0
+ sync
+ sprite px py 0
+ i := die-1
+ sprite px py 0
+ sync
+ v0 := key
+ is-dead := 1
+ i := game-over
+ jump draw-end
+
+: move-right
+ if pdir == FACE_RIGHT begin
+ px += 2
+ else
+ pdir := FACE_RIGHT
+ pframe := -32
+ end
+;
+
+: move-left
+ if pdir == FACE_LEFT begin
+ px += -2
+ else
+ pdir := FACE_LEFT
+ pframe := -32
+ end
+;
+
+: draw-eaty
+ i := eaty-walk
+ i += pdir
+ i += pframe
+ sprite px py 0
+;
+
+: next-frame
+ pframe += 32
+ v0 := 127
+ pframe &= v0
+ dec-life
+;
+
+: sync
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+ vf := 5
+ delay := vf
+;
+
+: wait
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+ vf := 1
+ delay := vf
+;
+
+: position-temp 0x00 0x00 0x00
+
+: save-position
+ v0 := px
+ v1 := py
+ v2 := pdir
+ i := position-temp
+ save v2
+;
+
+: restore-position
+ i := position-temp
+ load v2
+ px := v0
+ py := v1
+ pdir := v2
+;
+
+: iplus16
+ v0 <<= v0
+ v0 <<= v0
+ v0 <<= v0
+ v0 <<= v0
+ i += v0
+;
+
+: bounce-out
+ loop
+ if vf == 0 then return
+ draw-eaty
+ px := random 127
+ py := random 63
+ px += 2
+ if px > 110 then px += -63
+ py += 1
+ if py > 47 then py += -32
+ draw-eaty
+ again
+;
+
+###########################################
+#
+# Enemies
+#
+###########################################
+
+: enemy-data 00 00
+
+: spawn-room
+ has-enemy := 0
+ if roomindex == 1 then jump spawn-enemy
+ if roomindex == 3 then jump spawn-enemy
+ if timer == 0 then return
+ if roomindex != 4 then return
+: spawn-enemy
+ has-enemy := 1
+ v0 := random 63
+ v0 += 32
+ v1 := 0
+ i := enemy-data
+ save v1
+;
+
+: draw-enemy
+ i := enemy-data
+ load v1
+ i := copter
+ sprite v0 v1 0
+;
+
+: move-enemy
+ i := enemy-data
+ load v1
+ vf := random 0b11
+ if vf == 0 then v0 += -3
+ if vf == 1 then v0 += 3
+ vf := 127
+ v0 &= vf
+ v1 += 1
+ vf := 63
+ v1 &= vf
+ i := enemy-data
+ save v1
+
+ # check player overlap
+ px += 128
+ py += 64
+ v0 += 128
+ v1 += 64
+ v0 += -16 if px < v0 then jump cleanup
+ v0 += 32 if px > v0 then jump cleanup
+ v1 += -16 if py < v1 then jump cleanup
+ v1 += 32 if py > v1 then jump cleanup
+ dec-life
+ dec-life
+ dec-life
+ dec-life
+ vf := 5
+ buzzer := vf
+
+: cleanup
+ px += -128
+ py += -64
+;
+
+###########################################
+#
+# Hud and Life Force
+#
+###########################################
+
+: timer-lo 00
+: life-force-hi 00
+: life-force-lo 00
+: life-force-temp 0x00
+: life-force-tens 0x00
+: life-force-ones 0x00
+
+: draw-hud
+ i := heart
+ v1 := 2
+ v2 := -7
+ sprite v1 v2 5
+ v1 += 6
+ i := life-force-hi
+ draw-pair
+ i := life-force-lo
+ draw-pair
+
+ v1 := -10
+ i := part-tiles
+ i += inventory
+ if timer != 0 then i := bighex timer
+ sprite v1 v1 8
+
+ if roomindex > 4 then return
+ loot-px
+ v1 := -19
+ v2 := -10
+ i := blank
+ if v0 == GO_SHIP then i := go-ship
+ if v0 == CALL_SHIP then i := call-ship
+ if v0 == NECK_ESP then i := neck-esp
+ if v0 == PIECES then i := pieces
+ sprite v1 v2 8
+;
+
+: draw-part
+ v0 := inventory
+ v0 &= vf
+ if v0 != 0 then sprite v1 v1 8
+;
+
+: draw-pair
+ load v0
+ i := life-force-temp
+ bcd v0
+ i := life-force-tens
+ draw-digit
+ i := life-force-ones
+ draw-digit
+;
+
+: draw-digit
+ load v0
+ i := hex v0
+ sprite v1 v2 5
+ v1 += 5
+;
+
+: dec-life
+ i := life-force-hi
+ load v1
+ vf := 1
+ v1 -= vf
+ if vf != 1 begin
+ v0 += -1
+ if v0 == -1 begin
+ animate-death
+ return
+ else
+ v1 := 99
+ end
+ end
+ i := life-force-hi
+ save v1
+;
+
+: dec-timer
+ if timer == 0 then return
+ i := timer-lo
+ load v0
+ if v0 == 0 begin
+ if timer == 1 begin
+ timer := 0
+ loot-px
+ if v0 != GO_SHIP then return
+ is-dead := 1
+ i := victory
+ jump draw-end
+ else
+ timer += -1
+ v0 := TIMER_LO
+ end
+ end
+ v0 += -1
+ i := timer-lo
+ save v0
+;
+
+: give-life # takes argument (in hundreds) in vf
+ i := life-force-hi
+ load v0
+ v0 += vf
+ i := life-force-hi
+ save v0
+;
+
+###########################################
+#
+# Overworld Logic
+#
+###########################################
+
+: draw-room
+ v2 := 0
+ loop
+ this-room
+ # col stride is 4, data stride is 2, so shift right.
+ v0 >>= v2
+ i += v0
+ load v1
+ i := overworld-tiles
+ iplus16
+ sprite v2 v1 15
+ v2 += 4
+ if v2 != 128 then
+ again
+;
+
+: scroll-room-left
+ roomindex += 1
+ if roomindex == 5 then roomindex := 0
+ v2 := 124
+ v3 := 0
+ loop
+ this-room
+ i += v3
+ load v1
+ i := overworld-tiles
+ iplus16
+ scroll-left
+ sprite v2 v1 15
+ wait
+ v3 += 2
+ if v3 != 64 then
+ again
+ px := 1
+ spawn-room
+;
+
+: scroll-room-right
+ roomindex += -1
+ if roomindex == -1 then roomindex := 4
+ v2 := 0
+ v3 := 62
+ loop
+ this-room
+ i += v3
+ load v1
+ i := overworld-tiles
+ iplus16
+ scroll-right
+ sprite v2 v1 15
+ wait
+ v3 += -2
+ if v3 != -2 then
+ again
+ px := 111
+ spawn-room
+;
+
+: move-overworld
+ v0 := KEY_NECK if v0 key begin
+ animate-magic-neck
+ loot-px
+ if v0 == NECK_ESP begin
+ # neck esp reveals all nearby items in pits
+ reveal-pit-items
+ return
+ end
+ if v0 == PIECES begin
+ # pieces are a single-use health pickup
+ vf := PIECES_LIFE give-life
+ v0 := 0
+ v1 >>= px # px / 16
+ v1 >>= v1
+ v1 >>= v1
+ v1 >>= v1
+ loot-set
+ return
+ end
+ if v0 == CALL_SHIP begin
+ # you gotta do this to end the game
+ if inventory == ALL_PARTS then timer := TIMER_HI
+ v0 := 0
+ v1 >>= px # px / 16
+ v1 >>= v1
+ v1 >>= v1
+ v1 >>= v1
+ loot-set
+ try-place-call
+ end
+ return
+ end
+
+ # track whether any keys are held down
+ v1 := 0
+
+ if py != 1 begin
+ v0 := KEY_UP if v0 key begin
+ v1 := 1
+ py += -1
+ end
+ end
+ if py != 47 begin
+ v0 := KEY_DOWN if v0 key begin
+ v1 := 1
+ py += 1
+ end
+ end
+ v0 := KEY_LEFT if v0 key begin
+ v1 := 1
+ move-left
+ if px < 2 then scroll-room-right
+ end
+ v0 := KEY_RIGHT if v0 key begin
+ v1 := 1
+ move-right
+ if px > 110 then scroll-room-left
+ end
+
+ # if no keys were pressed, snap to neutral pose:
+ if v1 == 0 then pframe := 0
+ if v1 != 0 then next-frame
+;
+
+###########################################
+#
+# Neck ESP
+#
+###########################################
+
+: reveal-pit-items
+ sparkle-pit-items
+ sync
+ sparkle-pit-items
+;
+
+: sparkle-pit-items
+ v2 := 0 # byte offset to room data
+ loop
+ this-room
+ i += v2
+ load v1
+ # v0 sprite index
+ # v1 y-coord
+
+ # make sure it's a pit
+ if v0 == 1 begin
+ v3 := v1
+ v3 += 4
+ v1 >>= v2
+ v1 >>= v1
+ v1 >>= v1 # v2 / 8
+ loot-here
+ # v0 loot
+ # v3 y-coord
+
+ # make sure an item is down there
+ if v0 != 0 begin
+ v1 += 4
+ i := sparkle
+ v2 <<= v2
+ v2 += 3
+ sprite v2 v3 8
+ v2 += -3
+ v2 >>= v2
+ end
+ end
+
+ v2 += 8
+ if v2 != 64 then
+ again
+;
+
+###########################################
+#
+# Overworld Collision
+#
+###########################################
+
+: overworld-collide
+ py += 64
+
+ this-room
+ v3 := 0
+ loop
+ load v1
+ # v0 - slice sprite typecode
+ # v1 - slice Y position
+ # v3 - slice X position
+
+ # ignore blank columns entirely
+ if v0 == 0 then jump overworld-continue
+ v1 += 64
+
+ # is eaty vertically within the slice?
+ v1 += 15
+ if py > v1 then jump overworld-continue
+ v1 += -30
+ if py < v1 then jump overworld-continue
+
+ # is eaty horizontally within the slice?
+ v2 := v3
+ v2 += 4
+ if px > v2 then jump overworld-continue
+ v2 += -16
+ if px < v2 then jump overworld-continue
+
+ # perform an action based on sprite id
+ v0 += -1
+ v0 >>= v0
+ v0 >>= v0
+ if v0 == 0 begin
+ v3 >>= v3
+ v3 >>= v3
+ v3 >>= v3
+ pitindex >>= v3
+ fall-pit
+ else
+ # tree
+ draw-eaty
+ restore-position
+ draw-eaty
+ bounce-out
+ end
+ return
+
+: overworld-continue
+ v3 += 4
+ if v3 != 128 then
+ again
+;
+
+###########################################
+#
+# Pit Items
+#
+###########################################
+
+: draw-item
+ v0 := ITEM_X
+ v1 := ITEM_Y
+ if has-item == ROSE begin
+ i := rose-0
+ sprite v0 v1 0
+ return
+ end
+ i := blank
+ if has-item == PART_1 then i := part-1
+ if has-item == PART_2 then i := part-2
+ if has-item == PART_3 then i := part-3
+ sprite v0 v1 8
+;
+
+: near-item # return result in v0
+ v0 := 0
+ if has-item == 0 then return
+ if px < 36 then v0 := 1
+;
+
+: wilt-rose
+ v0 := ITEM_X
+ v1 := ITEM_Y
+ v2 := 0
+ i := rose-0
+ loop
+ sprite v0 v1 0
+ vf := 32
+ i += vf
+ sprite v0 v1 0
+ sync
+ v2 += 1
+ if v2 != 4 then
+ again
+ vf := ROSE_LIFE give-life
+;
+
+: take-item
+ if has-item == ROSE begin
+ wilt-rose
+ else
+ draw-item
+ inventory |= has-item
+ end
+ has-item := 0
+ v0 := 0
+ v1 := pitindex
+ roomindex += -5
+ loot-set
+ roomindex += 5
+;
+
+###########################################
+#
+# Pit Logic
+#
+###########################################
+
+: draw-pit
+ i := pit-wall
+ v1 := 0
+ v0 := -8
+ loop
+ sprite v0 v1 0
+ v1 += 16
+ if v1 != 64 then
+ again
+
+ i := pit-ground
+ v0 := 8
+ v1 := 48
+ loop
+ sprite v0 v1 0
+ v0 += 16
+ if v0 != 120 then
+ again
+
+ v1 := 33
+ i := pit-lf
+ v0 := 8
+ sprite v0 v1 15
+ i := pit-rt
+ v0 := -16
+ sprite v0 v1 15
+
+ draw-item
+;
+
+: move-pit
+ # track whether any keys are held down
+ v1 := 0
+ v0 := KEY_LEFT if v0 key begin
+ v1 := 1
+ if px != 24 then move-left
+ end
+ v0 := KEY_RIGHT if v0 key begin
+ v1 := 1
+ if px != 100 then move-right
+ end
+
+ # if no keys were pressed, snap to neutral pose:
+ if v1 == 0 then pframe := 0
+ if v1 != 0 then next-frame
+;
+
+: fly-pit
+ draw-eaty
+ animate-magic-neck
+ draw-eaty
+ loop
+ draw-eaty
+ py += -2
+ draw-eaty
+ sync
+ if py != 0 then
+ again
+ clear
+
+ restore-position
+ roomindex += -5
+ draw-room
+ draw-eaty
+ bounce-out
+;
+
+: fall-pit
+ v1 := pitindex
+ loot-here
+ has-item := v0
+
+ roomindex += 5
+ clear
+ draw-pit
+ px := 60
+ py := 0
+ pdir := FACE_LEFT
+ pframe := 0
+ i := eaty-walk
+
+ draw-eaty
+ loop
+ draw-eaty
+ py += 2
+ draw-eaty
+ sync
+ if py != 32 then
+ again
+
+ draw-hud
+ loop
+ draw-eaty
+ draw-hud
+
+ move-pit
+ dec-timer
+ draw-eaty
+
+ v0 := KEY_NECK if v0 key begin
+ near-item
+ if v0 == 1 begin
+ draw-eaty
+ animate-magic-neck
+ draw-eaty
+ take-item
+ else
+ fly-pit
+ return
+ end
+ end
+
+ draw-hud
+ sync
+
+ if is-dead == 0 then
+ again
+;
+
+###########################################
+#
+# Randomized Loot
+#
+###########################################
+
+: randomize-loot
+ # distribute common/nonvital items:
+
+ v1 := 0 # room-0 offset
+ v2 := 0 # loot-data offset
+ loop
+ # pits can be 0-1
+ # ground can be 0-3
+ is-pit
+ v3 := random 0b11
+ if v0 == 1 then v3 := random 0b01
+ v0 := v3
+ i := loot-data
+ i += v2
+ save v0
+
+ v1 += 8
+ v2 += 1
+ if v2 != 32 then
+ again
+
+ # place CALL_SHIP on ground
+ try-place-call
+
+ # place parts on pits
+ v4 := PART_1
+ try-place-part
+ v4 := PART_2
+ try-place-part
+ v4 := PART_3
+ try-place-part
+;
+
+: try-place-call
+ v3 := random 31
+ is-v3-pit
+ if v0 == 1 then jump try-place-call
+ i := loot-data
+ i += v3
+ load v0
+ if v0 > 3 then jump try-place-call
+ i := loot-data
+ i += v3
+ v0 := CALL_SHIP
+ #:breakpoint call-place
+ save v0
+;
+
+: try-place-part
+ v3 := random 31
+ is-v3-pit
+ if v0 != 1 then jump try-place-part
+ i := loot-data
+ i += v3
+ load v0
+ if v0 > 3 then jump try-place-part
+ i := loot-data
+ i += v3
+ v0 := v4
+ #:breakpoint part-place
+ save v0
+;
+
+: is-v3-pit
+ v1 <<= v3
+ v1 <<= v1
+ v1 <<= v1 # offset * 8
+: is-pit
+ # takes offset in v1,
+ # leaves 1 in v0 if it's a pit.
+ i := room-0
+ i += v1
+ load v0
+ if v0 != 1 then v0 := 0
+;
+
+###########################################
+#
+# Level Data
+#
+###########################################
+
+# loot definitions are 8 bytes per room.
+: loot-data
+ 0 0 0 0 0 0 0 0 # room 0
+ 0 0 0 0 0 0 0 0 # room 1
+ 0 0 0 0 0 0 0 0 # room 2
+ 0 0 0 0 0 0 0 0 # room 3
+ 0 0 5 0 0 0 0 0 # room 4
+
+: loot-px
+ # leaves result in v0
+ v1 >>= px
+ v1 >>= v1
+ v1 >>= v1
+ v1 >>= v1 # px / 16
+: loot-here
+ # takes column in v1, leaves result in v0
+ v0 <<= roomindex
+ v0 <<= v0
+ v0 <<= v0 # roomindex * 8
+ i := loot-data
+ i += v0
+ i += v1
+ load v0
+;
+
+: loot-set
+ # takes column in v1, value in v0
+ i := loot-data
+ i += v1
+ v1 <<= roomindex
+ v1 <<= v1
+ v1 <<= v1 # roomindex * 8
+ i += v1
+ save v0
+;
+
+# rooms are 64 bytes each.
+# each room is a series of packed pairs:
+#
+: room-0
+ 0 0 0 0 0 0 0 0
+ 1 8 2 8 3 8 4 8 #
+ 0 0 0 0 0 0 0 0
+ 1 40 2 40 3 40 4 40 #
+ 0 0 0 0 0 0 0 0
+ 1 8 2 8 3 8 4 8 #
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+: room-1
+ 0 0 0 0 0 0 0 0
+ 5 6 6 6 7 6 8 6 #
+ 0 0 0 0 0 0 0 0
+ 5 40 6 40 7 40 8 40 #
+ 5 32 6 32 7 32 8 32 #
+ 0 0 0 0 0 0 0 0
+ 5 20 6 20 7 20 8 20 #
+ 0 0 0 0 0 0 0 0
+: room-2
+ 0 0 0 0 0 0 0 0
+ 1 39 2 40 3 41 4 42 #
+ 1 6 2 5 3 4 4 3 #
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 1 3 2 4 3 5 4 6 #
+ 1 42 2 41 3 40 4 39 #
+ 0 0 0 0 0 0 0 0
+: room-3
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 5 30 6 30 7 30 8 30 #
+ 5 40 6 40 7 40 8 40 #
+ 5 40 6 40 7 40 8 40 #
+ 5 6 6 6 7 6 8 6 #
+ 1 42 2 41 3 40 4 39 #
+ 0 0 0 0 0 0 0 0
+: room-4
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 5 40 6 40 7 40 8 40 #
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+
+: this-room
+ v0 <<= roomindex
+ v0 <<= v0
+ jump0 room-table
+
+: room-table
+ i := room-0 ;
+ i := room-1 ;
+ i := room-2 ;
+ i := room-3 ;
+ i := room-4 ;
+
+###########################################
+#
+# Graphics
+#
+###########################################
+
+# hud tiles
+: heart 0x50 0xF8 0xF8 0x70 0x20
+: call-ship 0x1C 0x3C 0x7C 0x70 0x70 0x7C 0x3C 0x1C
+: go-ship 0xE7 0xC3 0xA5 0x18 0x18 0xA5 0xC3 0xE7
+: pieces 0x30 0x48 0x4E 0x39 0x39 0x4E 0x48 0x30
+: neck-esp 0x08 0x04 0x12 0x4A 0x4A 0x12 0x04 0x08
+: sparkle 0x00 0x08 0x01 0x00 0x48 0x00 0x01 0x10
+
+: part-tiles 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # 0 - nothing
+: part-1 0x7E 0xFF 0x81 0x00 0x00 0x00 0x00 0x00 # 1 - 1
+: part-2 0x00 0x00 0x24 0x3C 0x3C 0x24 0x00 0x00 # 2 - 2
+ 0x7E 0xFF 0xA5 0x3C 0x3C 0x24 0x00 0x00 # 3 - 1,2
+: part-3 0x00 0x00 0x00 0x00 0x18 0x18 0x7E 0xFF # 4 - 3
+ 0x7E 0xFF 0x81 0x00 0x18 0x18 0x7E 0xFF # 5 - 1,3
+ 0x00 0x00 0x24 0x3C 0x24 0x3C 0x7E 0xFF # 6 - 2,3
+ 0x7E 0xFF 0xA5 0x3C 0x24 0x3C 0x7E 0xFF # 7 - 1,2,3
+
+: fingat 0xC0
+
+# overworld tiles
+# packed into 4 pixel wide vertical slices,
+# so I can draw as I scroll horizontally.
+: overworld-tiles
+: blank 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # 0
+: pit-0 0x10 0x70 0xF0 0xC0 0xB0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0x70 0x10 0x00 # 1
+: pit-1 0xF0 0xF0 0x00 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0x00 # 2
+: pit-2 0xF0 0xF0 0x00 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0x00 # 3
+: pit-3 0x80 0xE0 0xF0 0x30 0xD0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xE0 0x80 0x00 # 4
+: tree-0 0x00 0x00 0x00 0x00 0x10 0x30 0xF0 0x70 0x30 0x60 0xF0 0x70 0x50 0x00 0x00 0x00 # 5
+: tree-1 0x10 0x20 0x60 0xB0 0x60 0xF0 0xE0 0x50 0xF0 0xB0 0x70 0xF0 0xB0 0x30 0x30 0x00 # 6
+: tree-2 0x80 0x40 0x20 0x10 0x00 0x50 0x30 0x30 0x70 0xB0 0x70 0xF0 0xD0 0xC0 0xC0 0x00 # 7
+: tree-3 0x00 0x00 0x00 0x00 0x80 0xC0 0x70 0xE0 0xC0 0xE0 0x50 0xE0 0xC0 0x00 0x00 0x00 # 8
+
+# pit background tiles
+: pit-ground 0xF7 0xF7 0x7F 0x7F 0xED 0xED
+: pit-wall 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF
+: pit-lf 0x80 0x80 0xC0 0xC0 0xC0 0xE0 0xE0 0xE0 0xF0 0xF0 0xF0 0xF8 0xF8 0xFC 0xFE
+: pit-rt 0x01 0x01 0x03 0x03 0x03 0x07 0x07 0x07 0x0F 0x0F 0x0F 0x1F 0x1F 0x3F 0x7F
+
+# rose wilting animation
+: rose-0
+ 0x00 0x00 0x00 0x00 0x05 0xE0 0x05 0xE0
+ 0x05 0xE0 0x06 0xE0 0x03 0xC0 0x01 0x18
+ 0x19 0xA8 0x14 0xD0 0x0A 0x60 0x07 0xC0
+ 0x01 0x80 0x03 0x00 0x03 0x00 0x01 0x80
+: rose-1
+ 0x00 0x00 0x00 0x00 0x01 0x00 0x0B 0x70
+ 0x0B 0xE0 0x0D 0xE0 0x07 0xC0 0x01 0x00
+ 0x01 0x80 0x1C 0xF0 0x22 0x68 0x1F 0xD8
+ 0x01 0x80 0x03 0x00 0x03 0x00 0x01 0x80
+: rose-2
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x40
+ 0x03 0xE0 0x0D 0xF0 0x07 0xF0 0x0F 0x30
+ 0x01 0x80 0x00 0xE0 0x06 0x70 0x0F 0xD0
+ 0x0D 0xB0 0x0B 0x20 0x03 0x00 0x01 0x80
+: rose-3
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x01 0x20 0x13 0xC0 0x0B 0xF0
+ 0x1B 0xB0 0x0A 0xC0 0x00 0x60 0x03 0xC0
+ 0x05 0xA0 0x0F 0x30 0x0B 0x70 0x01 0xA0
+: rose-4
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x01 0x00 0x03 0xC0 0x03 0xC0
+ 0x03 0xC0 0x03 0xC0 0x00 0x60 0x01 0xC0
+ 0x01 0x80 0x03 0x00 0x03 0x70 0x0D 0xB8
+
+# eaty walk animation
+: eaty-walk
+: walk-l0
+ 0x00 0x00 0x00 0x00 0x1F 0xF0 0x3F 0xF8
+ 0x19 0xF8 0x3F 0xF8 0x3F 0x9C 0x2A 0x1C
+ 0x00 0x1C 0x07 0xFC 0x3F 0xC8 0x2F 0xD8
+ 0x0F 0xF8 0x0F 0xF8 0x0D 0x18 0x1C 0x78
+: walk-l1
+ 0x00 0x00 0x00 0x00 0x1F 0xF0 0x3F 0xF8
+ 0x19 0xF8 0x3F 0xF8 0x3F 0x9C 0x2A 0x1C
+ 0x00 0x1C 0x07 0xCC 0x0F 0xD8 0x3F 0xF8
+ 0x2F 0xF8 0x0F 0x98 0x0C 0xB8 0x1C 0x00
+: walk-l2
+ 0x00 0x00 0x00 0x00 0x1F 0xF0 0x3F 0xF8
+ 0x19 0xF8 0x3F 0xF8 0x3F 0x9C 0x2A 0x1C
+ 0x00 0x1C 0x07 0xFC 0x3F 0xC8 0x2F 0xD8
+ 0x0F 0xF8 0x0F 0xF8 0x0D 0x18 0x1C 0x78
+: walk-l3
+ 0x00 0x00 0x00 0x00 0x1F 0xF0 0x3F 0xF8
+ 0x19 0xF8 0x3F 0xF8 0x3F 0x9C 0x2A 0x1C
+ 0x00 0x1C 0x37 0xFC 0x2F 0xF8 0x0F 0xC8
+ 0x0F 0xD8 0x0D 0xF8 0x1D 0x18 0x00 0x78
+: walk-r0
+ 0x00 0x00 0x00 0x00 0x0F 0xF8 0x1F 0xFC
+ 0x1F 0x98 0x1F 0xFC 0x39 0xFC 0x38 0x54
+ 0x38 0x00 0x3F 0xE0 0x13 0xFC 0x1B 0xF4
+ 0x1F 0xF0 0x1F 0xF0 0x18 0xB0 0x1E 0x38
+: walk-r1
+ 0x00 0x00 0x00 0x00 0x0F 0xF8 0x1F 0xFC
+ 0x1F 0x98 0x1F 0xFC 0x39 0xFC 0x38 0x54
+ 0x38 0x00 0x33 0xE0 0x1B 0xF0 0x1F 0xFC
+ 0x1F 0xF4 0x19 0xF0 0x1D 0x30 0x00 0x38
+: walk-r2
+ 0x00 0x00 0x00 0x00 0x0F 0xF8 0x1F 0xFC
+ 0x1F 0x98 0x1F 0xFC 0x39 0xFC 0x38 0x54
+ 0x38 0x00 0x3F 0xE0 0x13 0xFC 0x1B 0xF4
+ 0x1F 0xF0 0x1F 0xF0 0x18 0xB0 0x1E 0x38
+: walk-r3
+ 0x00 0x00 0x00 0x00 0x0F 0xF8 0x1F 0xFC
+ 0x1F 0x98 0x1F 0xFC 0x39 0xFC 0x38 0x54
+ 0x38 0x00 0x3F 0xEC 0x1F 0xF4 0x13 0xF0
+ 0x1B 0xF0 0x1F 0xB0 0x18 0xB8 0x1E 0x00
+
+# magic neck animation
+: eaty-neck
+: neck-l-0
+ 0x00 0x00 0x00 0x00 0x1F 0xF0 0x3F 0xF8
+ 0x19 0xF8 0x3F 0xF8 0x3F 0x9C 0x2A 0x1C
+ 0x00 0x1C 0x07 0xFC 0x3F 0xC8 0x2F 0xD8
+ 0x0F 0xF8 0x0F 0xF8 0x0D 0x18 0x1C 0x78
+: neck-l-1
+ 0x00 0x00 0x1F 0xF0 0x3F 0xF0 0x1A 0xF8
+ 0x1D 0xF8 0x3F 0x9C 0x2A 0x1C 0x00 0x1C
+ 0x00 0x1C 0x07 0xF8 0x3F 0xC8 0x2F 0xD8
+ 0x0F 0xF8 0x0F 0xF8 0x0D 0x18 0x1C 0x78
+: neck-l-2
+ 0x3F 0xF0 0x18 0xF0 0x1A 0xF8 0x18 0xF8
+ 0x3F 0x98 0x2A 0x1C 0x00 0x1C 0x00 0x1C
+ 0x00 0x18 0x07 0xF8 0x3F 0xC8 0x2F 0xD8
+ 0x0F 0xF8 0x0F 0xF8 0x0D 0x18 0x1C 0x78
+: neck-l-3
+ 0x00 0x00 0x1F 0xF0 0x3F 0xF0 0x1A 0xF8
+ 0x1D 0xF8 0x3F 0x9C 0x2A 0x1C 0x00 0x1C
+ 0x00 0x1C 0x07 0xF8 0x3F 0xC8 0x2F 0xD8
+ 0x0F 0xF8 0x0F 0xF8 0x0D 0x18 0x1C 0x78
+: neck-r-0
+ 0x00 0x00 0x00 0x00 0x0F 0xF8 0x1F 0xFC
+ 0x1F 0x98 0x1F 0xFC 0x39 0xFC 0x38 0x54
+ 0x38 0x00 0x3F 0xE0 0x13 0xFC 0x1B 0xF4
+ 0x1F 0xF0 0x1F 0xF0 0x18 0xB0 0x1E 0x38
+: neck-r-1
+ 0x00 0x00 0x0F 0xF8 0x0F 0xFC 0x1F 0x58
+ 0x1F 0xB8 0x39 0xFC 0x38 0x54 0x38 0x00
+ 0x38 0x00 0x1F 0xE0 0x13 0xFC 0x1B 0xF4
+ 0x1F 0xF0 0x1F 0xF0 0x18 0xB0 0x1E 0x38
+: neck-r-2
+ 0x0F 0xFC 0x0F 0x18 0x1F 0x58 0x1F 0x18
+ 0x19 0xFC 0x38 0x54 0x38 0x00 0x38 0x00
+ 0x18 0x00 0x1F 0xE0 0x13 0xFC 0x1B 0xF4
+ 0x1F 0xF0 0x1F 0xF0 0x18 0xB0 0x1E 0x38
+: neck-r-3
+ 0x00 0x00 0x0F 0xF8 0x0F 0xFC 0x1F 0x58
+ 0x1F 0xB8 0x39 0xFC 0x38 0x54 0x38 0x00
+ 0x38 0x00 0x1F 0xE0 0x13 0xFC 0x1B 0xF4
+ 0x1F 0xF0 0x1F 0xF0 0x18 0xB0 0x1E 0x38
+
+# death animation
+: die-0
+ 0x00 0x00 0x03 0xC0 0x00 0x00 0x1F 0xF0
+ 0x3F 0xF8 0x3F 0xF8 0x1B 0xF8 0x3F 0x9C
+ 0x2A 0x1C 0x00 0x1C 0x07 0xFC 0x0F 0xE8
+ 0x1F 0xD8 0x2F 0xD8 0x0F 0xF8 0x1D 0x78
+: die-1
+ 0x00 0x00 0x03 0xC0 0x04 0x20 0x03 0xC0
+ 0x00 0x00 0x1F 0xF0 0x3F 0xF8 0x3F 0xF8
+ 0x3F 0xFC 0x3F 0x9C 0x2A 0x7C 0x0F 0xFC
+ 0x0F 0xE8 0x0F 0xE8 0x0F 0xF8 0x1F 0xF8
+
+: copter
+ 0xF8 0x1F 0x20 0x04 0xF9 0x1F 0xF9 0x1F
+ 0x89 0x11 0x73 0xCE 0x17 0xE8 0x0D 0xB0
+ 0x07 0xE0 0x06 0x60 0xFB 0xDF 0x23 0xC4
+ 0xFC 0x3F 0xF8 0x1F 0x88 0x11 0x70 0x0E
+
+: title-screen
+ 0x00 0x00 0x00 0xF8 0x07 0xF8 0x07 0xC0
+ 0x07 0x00 0x07 0xB9 0x03 0xFB 0x03 0xE3
+ 0x03 0x81 0x03 0xC5 0x01 0xDE 0x01 0xFE
+ 0x01 0xFC 0x00 0xF0 0x00 0x80 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x08 0x00 0x4C
+ 0x03 0x6C 0x9F 0xB8 0xD6 0x18 0x66 0x08
+ 0xF3 0x0C 0xB3 0x0C 0x93 0x80 0xC0 0x00
+ 0x00 0x75 0x00 0x27 0x00 0x25 0x00 0x25
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x70 0xC8 0x61 0x28 0x41 0xE8 0x71 0x2E
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0xBB 0x40 0xB2 0xC0 0xA2 0x40 0xBA 0x40
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00
+ 0x00 0x01 0x00 0x03 0x00 0x01 0x00 0x03
+ 0x00 0x01 0x00 0x01 0x00 0x01 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x12 0x00 0x0F 0xFF
+ 0x35 0x00 0xC0 0x00 0xD0 0x00 0x82 0xC2
+ 0x83 0xCB 0xD0 0x1C 0x94 0x00 0xDF 0xAA
+ 0xAE 0xEB 0xD6 0x3E 0xEB 0x00 0xF5 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0xC0 0x03 0x20 0x1C 0x21 0x23 0xC0 0xCC
+ 0xC0 0x90 0x21 0xA0 0x61 0x3E 0xC3 0x9D
+ 0x01 0x41 0x00 0xEA 0x00 0xFE 0x00 0xB8
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0xE0 0x00 0x20 0x00 0xC0 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x80 0x00 0x00 0x00 0x00 0x00
+
+: game-over
+ 0x1E 0x79 0x3E 0xFD 0x30 0xCD 0x36 0xFD
+ 0x32 0xCD 0x3E 0xCD 0x1E 0xCD 0x00 0x00
+ 0x0E 0x63 0x1F 0x63 0x1B 0x63 0x1B 0x63
+ 0x1B 0x77 0x1F 0x3E 0x0E 0x1C 0x00 0x00
+ 0x83 0x7C 0xC7 0x7C 0xEF 0x60 0xBB 0x78
+ 0x93 0x60 0x83 0x7C 0x83 0x7C 0x00 0x00
+ 0x7D 0xF0 0x7D 0xF8 0x61 0x98 0x79 0xF0
+ 0x61 0x98 0x7D 0x98 0x7D 0x98 0x00 0x00
+
+: victory
+ 0x00 0x32 0x00 0x4A 0xEE 0x7A 0xEE 0x4A
+ 0xEE 0x4B 0xEE 0x00 0xEE 0x9B 0xFE 0xA1
+ 0x7C 0xA1 0x7C 0xA1 0x38 0xA1 0x38 0xA1
+ 0x10 0x99 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x2E 0x90 0x28 0xD0 0x2E 0xB0 0x28 0xB0
+ 0xAE 0x90 0x00 0x00 0xBB 0x36 0x2A 0xB6
+ 0x2A 0xB6 0x2B 0x36 0x2A 0xB6 0x2A 0xBE
+ 0x3A 0x9E 0x00 0x06 0x00 0x3E 0x00 0x3C
diff --git a/resources/octoroms/f8z.8o b/resources/octoroms/f8z.8o
new file mode 100644
index 0000000..0e3a93e
--- /dev/null
+++ b/resources/octoroms/f8z.8o
@@ -0,0 +1,254 @@
+###########################################
+#
+# F8Z: a chip8/Octo tech demo
+#
+# A/D move gomez left and right.
+# Q/E rotate the world around him.
+# Runs best at 100 cycles/frame.
+#
+###########################################
+
+: letter-F 0x07 0x19 0x61 0x81 0x87 0x99 0x81 0x81 0x87 0x9E 0x98 0x90 0x90 0xF0 0xE0
+: letter-8 0x0E 0x33 0x41 0x89 0x99 0x93 0x81 0xC1 0x89 0x99 0x91 0x83 0xCE 0x78 0x60
+: letter-Z 0x07 0x19 0x61 0x81 0x91 0xE1 0x43 0x86 0x89 0x81 0x81 0x87 0x9E 0xF8 0x60
+
+: cloud1 0x07 0x0F 0x7F 0xFF
+: cloud2 0xC0 0xE0 0xFE 0xFF
+: cloud3 0x30 0x7E 0xFF
+
+: pillar # (padded to 16 bytes/side)
+ 0x06 0x1B 0x65 0x8F 0xD5 0xBD 0xFB 0x7E 0x83 0xBB 0xBB 0xBB 0x83 0x7F 0xFD 0x00
+ 0x78 0xD6 0xFE 0xFD 0xBF 0xFF 0xFE 0xFF 0x83 0xAB 0xAB 0xBB 0x82 0xFF 0xBF 0x00
+ 0x60 0xB8 0xFE 0xFB 0x7B 0x7F 0xFF 0xDF 0x83 0xBA 0xA3 0xBB 0x83 0x7F 0xFD 0x00
+ 0x16 0xD9 0xA6 0xBB 0xFB 0xFF 0xDF 0xFE 0x82 0xBB 0xAB 0xAB 0x83 0x7F 0xF9 0x00
+
+: floors # (padded to 8 bytes/tile)
+ 0xFE 0xFE 0xFE 0xE0 0xEF 0xEF 0xEF 0x00
+ 0xEF 0xEF 0xEF 0xE0 0xFE 0xFE 0xFE 0x00
+ 0xEF 0xEF 0xEF 0xE0 0xEE 0xEE 0xEE 0x00
+ 0xFE 0xFE 0xFE 0xE0 0xEE 0xEE 0xEE 0x00
+
+: gomez # (padded to 16 bytes/frame)
+ 0x00 0x00 0x60 0xE0 0x7E 0xFF 0xBD 0xF7 0x7E 0x18 0x3C 0x5E 0x3C 0x3C 0x24 0x00 # facing right
+ 0x00 0x60 0x60 0xFE 0xFF 0xBD 0xF7 0x7E 0x18 0x3C 0x5E 0x3C 0x3C 0x48 0x00 0x00
+ 0x00 0x60 0x60 0xFE 0xFF 0xBD 0xF7 0x7E 0x18 0x7E 0x1C 0x3C 0x3C 0x24 0x00 0x00
+ 0x00 0x00 0x60 0xFE 0xFF 0xFF 0xBD 0x76 0x18 0x3C 0x5E 0x3C 0x3C 0x12 0x00 0x00
+ 0x00 0x00 0x06 0x07 0x7E 0xFF 0xBD 0xEF 0x7E 0x18 0x3C 0x7A 0x3C 0x3C 0x24 0x00 # facing left
+ 0x00 0x06 0x06 0x7F 0xFF 0xBD 0xEF 0x7E 0x18 0x3C 0x7A 0x3C 0x3C 0x12 0x00 0x00
+ 0x00 0x06 0x06 0x7F 0xFF 0xBD 0xEF 0x7E 0x18 0x7E 0x38 0x3C 0x3C 0x24 0x00 0x00
+ 0x00 0x00 0x06 0x7F 0xFF 0xFF 0xBD 0x6E 0x18 0x3C 0x7A 0x3C 0x3C 0x48 0x00 0x00
+
+: pillar-pos
+ 46 10 16 46
+
+: cloud-delta
+ 254 255 254 255 2 1 2 1
+
+: sync
+ v2 := 4
+ delay := v2
+ loop
+ v2 := delay
+ if v2 != 0 then
+ again
+;
+
+: draw-objects
+ v0 := 2
+ v1 := vd
+ v1 += 8
+ v2 := 1
+ i := gomez
+ i += vc # animation frame
+ i += v8 # direction facing
+ sprite va vb 15
+
+ # clouds
+ i := cloud1
+ sprite vd v0 4
+ i := cloud2
+ sprite v1 v0 4
+ i := cloud3
+ sprite ve v2 3
+
+ # pillar
+ i := pillar-pos # get x coord
+ i += v5
+ load v0
+ v1 := 9
+ i := pillar
+ v2 <<= v5 # add (rotation * 16)
+ v2 <<= v2
+ v2 <<= v2
+ v2 <<= v2
+ i += v2
+ sprite v0 v1 15
+;
+
+: draw-background
+ v0 := 0
+ v1 := 25
+ loop
+ i := floors
+ v2 := random 0b11000
+ i += v2
+ sprite v0 v1 7
+ v0 += 8
+ if v0 != 64 then
+ again
+;
+
+###########################################
+#
+# Wipe Effects
+#
+###########################################
+
+: wipe-common
+ v1 := 16
+ buzzer := v1
+
+ v1 := 0b11 # wrap rotation modulo 4
+ v5 &= v1
+
+ i := gomez
+ loop
+ i += v1
+
+ v1 := 1
+ sprite v0 v1 15
+ v1 := 16
+ sprite v0 v1 15
+
+ sync
+
+ v1 := 1
+ sprite v0 v1 15
+ v1 := 16
+ sprite v0 v1 15
+ v2 := v0
+
+ v0 += v3
+ v1 := 0b111111
+ v2 := v0
+ v2 &= v1
+ if v2 != 0 then
+ again
+ clear
+ draw-background
+;
+
+: wipe-left
+ # rotate player's position
+ v0 := va
+ va := v7
+ v7 := 64
+ v7 -= v0
+
+ v5 += -1
+ v0 := 64
+ v3 := -16
+ jump wipe-common
+
+: wipe-right
+ # rotate player's position
+ v0 := va
+ va := v7
+ v7 := 64
+ v7 += v0
+
+ v5 += 1
+ v0 := 0
+ v3 := 16
+ jump wipe-common
+
+###########################################
+#
+# Movement and Animation
+#
+###########################################
+
+: walk
+ v9 += -1 # count down anim timer
+ vc += 16 # advance frame
+ v0 := 0b0110000
+ vc &= v0
+ if v8 == 0 then va += 1 # move
+ if v8 != 0 then va += -1
+;
+
+: face-left v8 := 64 ;
+: move-left if v8 == 0 then jump face-left v9 := 4 ;
+: face-right v8 := 0 ;
+: move-right if v8 == 64 then jump face-right v9 := 4 ;
+
+: move-player
+ if v9 != 0 then jump walk
+ v0 := 7
+ if v0 key then jump move-left
+ v0 := 9
+ if v0 key then jump move-right
+ v0 := 4
+ if v0 key then jump wipe-left
+ v0 := 6
+ if v0 key then jump wipe-right
+;
+
+###########################################
+#
+# Main Loop
+#
+###########################################
+
+: main
+ # title screen
+ v0 := 15
+ v1 := 13
+ i := letter-F
+ sprite v0 v1 15
+ v0 += 12
+ v1 += -5
+ i := letter-8
+ sprite v0 v1 15
+ v0 += 12
+ v1 += -5
+ i := letter-Z
+ sprite v0 v1 15
+ v0 := 16
+ loop
+ sync
+ v0 += -1
+ if v0 != 0 then
+ again
+ clear
+
+ # v0-v3 are used extensively as temporaries
+ # v4 is still free
+
+ v5 := 0 # rotation index
+ v6 := 0 # real x
+ v7 := 0 # real y
+ v8 := 0 # 0 - right, 64 - left
+ v9 := 0 # moving?
+ va := 10 # player x position
+ vb := 10 # player y position
+ vc := 0 # player anim frame
+ vd := 10 # big cloud x
+ ve := 40 # small cloud x
+
+ draw-background
+ draw-objects
+ loop
+ draw-objects
+ move-player
+
+ i := cloud-delta
+ i += v5
+ i += v5
+ load v1
+ vd += v0
+ ve += v1
+
+ draw-objects
+ sync
+ again
diff --git a/resources/octoroms/fuse.8o b/resources/octoroms/fuse.8o
new file mode 100644
index 0000000..7ccc90f
--- /dev/null
+++ b/resources/octoroms/fuse.8o
@@ -0,0 +1,231 @@
+###########################################
+#
+# Fuse
+#
+# A minimalist reinterpretation of
+# the classic Pipe Dream. Assemble lines
+# to guide a burning fuse. Don't let it
+# burn out!
+#
+# Press WASD to move the current line
+# fragments and E to lay them down.
+# You cannot lay down a line overlapping
+# any existing lines.
+#
+# Runs best at 7 cycles/frame.
+#
+# John Earnest
+#
+###########################################
+
+:alias ex vd # fuse position x
+:alias ey vc # fuse position y
+:alias px vb # cursor position x
+:alias py va # cursor position y
+:alias tile v9 # selected tile
+:alias timer-0 v8 # fuse movement cookoff
+:alias timer-1 v7 # score (low digits)
+:alias timer-2 v6 # score (high digits)
+
+:const tile-mask 0b111000
+:const fuse-speed 3 # higher is slower
+
+: main
+ init-game
+
+ # draw starting cursor:
+ i := tiles
+ i += tile
+ sprite px py 8
+
+ # wait for an initial key press:
+ show-title
+ v0 := key
+ show-title
+
+ loop
+ i := tiles
+ i += tile
+
+ v0 := 6 if v0 key begin # keyboard E
+ # wait for key release:
+ loop if v0 key then again
+
+ # we can't drop a tile overlapping
+ # any existing lines:
+ sprite px py 8 # erase
+ sprite px py 8 # redraw
+
+ if vf == 0 begin
+ # drop a tile:
+ tile := random tile-mask
+ i := tiles
+ i += tile
+ sprite px py 8
+ end
+ else
+ # erase the cursor:
+ sprite px py 8
+
+ # move cursor:
+ v0 := 5 if v0 key then py += -1 # keyboard W
+ v0 := 8 if v0 key then py += 1 # keyboard S
+ v0 := 7 if v0 key then px += -1 # keyboard A
+ v0 := 9 if v0 key then px += 1 # keyboard D
+
+ # handle fuse movement without
+ # the cursor drawn, to avoid
+ # extreme weirdness:
+ move-fuse
+
+ # redraw cursor:
+ i := tiles
+ i += tile
+ sprite px py 8
+ end
+
+ # sync framerate
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+ vf := 6
+ delay := vf
+ again
+
+: init-game
+ clear
+
+ # set up a random starting position:
+ ex := random 15
+ ex += 8
+ ey := random 31
+ ey += 16
+ v0 := random 0b11000
+ i := starts
+ i += v0
+ sprite ex ey 8
+
+ # initialize the cursor:
+ tile := random tile-mask
+ px := 1
+ py := 1
+
+ # reset score:
+ timer-0 := 0
+ timer-1 := 0
+ timer-2 := 0
+;
+
+: move-fuse
+ # don't move every single frame:
+ timer-0 += 1
+ if timer-0 != fuse-speed then return
+ timer-0 := 0
+
+ # increment score:
+ timer-1 += 1
+ if timer-1 == 100 begin
+ timer-1 := 0
+ timer-2 += 1
+ end
+
+ :macro try-move SPRITE REG DELTA {
+ i := SPRITE
+ sprite ex ey 5
+ if vf != 0 begin
+ REG += DELTA
+ return
+ end
+ sprite ex ey 5
+ }
+ try-move dot-n ey -1 # north
+ try-move dot-e ex 1 # east
+ try-move dot-s ey 1 # south
+ try-move dot-w ex -1 # west
+
+: game-over
+ # no trail, so we've failed!
+ i := dot
+ v0 := 6 # keyboard E
+ loop
+ sprite ex ey 5
+ if v0 -key then # key down
+ again
+ loop
+ if v0 key then # key up
+ again
+ clear
+
+: show-score
+ v3 := 37 # x position
+ v4 := 10 # y position
+
+ # draw low two digits:
+ i := digit-buffer
+ bcd timer-1
+ show-score-byte
+
+ # draw high two digits:
+ i := digit-buffer
+ bcd timer-2
+ show-score-byte
+
+ # wait for any key:
+ v0 := key
+
+ init-game
+;
+
+: show-score-byte
+ load v2
+ i := hex v2
+ sprite v3 v4 5
+ v3 += -5
+ i := hex v1
+ sprite v3 v4 5
+ v3 += -5
+;
+
+: show-title
+ v0 := 52
+ v1 := 2
+ v2 := 1
+ i := title
+ loop
+ sprite v0 v1 1
+ v1 += 1
+ i += v2
+ if v1 != 30 then
+ again
+;
+
+: digit-buffer 0 0 0
+
+: title
+ 0x7F 0xFF 0xC0 0xFC 0xC0 0xC0 0x00 0xC3
+ 0xC3 0xC3 0xFF 0x7E 0x00 0x7F 0xFF 0xC0
+ 0xFF 0x03 0xFF 0xFE 0x00 0x7F 0xFF 0xC0
+ 0xFC 0xC0 0xFF 0x7F
+
+: dot 0x00 0x00 0x00 0x10 0x00
+: dot-n 0x00 0x00 0x10 0x00 0x00
+: dot-s 0x00 0x00 0x00 0x00 0x10
+: dot-e 0x00 0x00 0x00 0x08 0x00
+: dot-w 0x00 0x00 0x00 0x20 0x00
+
+: starts
+ 0x40 0x5C 0x54 0x44 0x44 0x7C 0x00 0x00 # N
+ 0x00 0x7F 0x40 0x4C 0x44 0x7C 0x00 0x00 # E
+ 0x00 0x7C 0x44 0x44 0x54 0x74 0x04 0x00 # S
+ 0x00 0x3C 0x24 0x24 0x04 0xFC 0x00 0x00 # W
+
+: tiles
+ 0x20 0x20 0xE0 0x00 0x0F 0x08 0x08 0x08 # cross 1
+ 0x04 0x04 0x0C 0x08 0xEF 0x20 0x20 0x20 # cross 2
+ 0x02 0x7E 0x40 0x7E 0x02 0x7E 0x40 0x7E # zig n-s
+ 0xC0 0x5C 0x54 0x55 0x55 0x55 0x57 0x70 # zig e-w
+ 0x14 0x14 0x34 0xE7 0x00 0x7E 0xC3 0x00 # tee N
+ 0x40 0x5F 0x50 0x57 0x54 0x54 0x54 0x54 # tee E
+ 0x00 0xF0 0x1F 0xC0 0x6E 0x2A 0x2B 0x28 # tee S
+ 0x22 0xE2 0x02 0x72 0x56 0x54 0xD4 0x14 # tee W
diff --git a/resources/octoroms/gradsim.8o b/resources/octoroms/gradsim.8o
new file mode 100644
index 0000000..7f629ca
--- /dev/null
+++ b/resources/octoroms/gradsim.8o
@@ -0,0 +1,215 @@
+###########################################
+#
+# Grad School Simulator 2014
+#
+# "Welcome to the sunless depths of my ennui."
+#
+###########################################
+
+: main
+ hires
+ draw-background
+ loop
+ do-behavior
+ again
+
+: do-behavior
+ # by AND masking an 8 bit value with 0b1110
+ # we can create the numbers 0,2,4,8,10,12,14,16:
+ v0 := random 0b1110
+ jump0 behavior-table
+
+: behavior-table
+ # this is a jump table. Each entry
+ # is 1 instruction long, or 2 bytes:
+ jump blink
+ jump blink
+ jump idle
+ jump idle
+ jump grimace
+ jump show-icon
+ jump show-icon
+ jump show-icon
+
+# here I've overlapped two subroutines,
+# since they share the same ending instructions:
+: blink
+ blink-toggle
+ vf := 10 wait
+ blink-toggle
+: idle
+ vf := 45 wait
+;
+
+: blink-toggle
+ i := blink-mask
+ v0 := 55
+ v1 := 26
+ sprite v0 v1 3
+ v0 := 65
+ v1 := 26
+ sprite v0 v1 3
+;
+
+: wait
+ # takes a time argument in vf.
+ # vf gets destroyed by +/-/>>/<> 16 } }
+
+:macro mirror-v_ { :byte { @ from + 7 - HERE - to } }
+:macro mirror-h_ { :calc t { @ from + HERE - to } rev8 t }
+:macro mirror-8 BASE OP { :calc to { HERE } :calc from { BASE } OP OP OP OP OP OP OP OP }
+:macro mirror-v BASE { mirror-8 BASE mirror-v_ }
+:macro mirror-h BASE { mirror-8 BASE mirror-h_ }
+
+: sprite-1 0x18 0x2C 0x46 0x83 0xEF 0x2C 0x2C 0x3C
+: sprite-3 0x18 0x14 0xF2 0x81 0xF3 0xF6 0x1C 0x18
+: sprite-2 mirror-v sprite-1
+: sprite-4 mirror-h sprite-3
diff --git a/resources/octoroms/mondrian.8o b/resources/octoroms/mondrian.8o
new file mode 100644
index 0000000..6f6381e
--- /dev/null
+++ b/resources/octoroms/mondrian.8o
@@ -0,0 +1,80 @@
+###########################################
+#
+# Mondri8
+#
+# Draw random compositions in the style
+# of Piet Mondrian. When drawing completes,
+# press any Chip8 key to draw another.
+#
+###########################################
+
+:const LINE_COUNT 10
+:const FILL_COUNT 5
+
+:alias x v0
+:alias y v1
+:alias px v2 # x position when scanning
+:alias py v3 # y position when scanning
+:alias dx v4 # change (delta) in x position when scanning
+:alias dy v5 # change (delta) in y position when scanning
+:alias counter v6
+
+: main
+ hires
+ i := point
+ loop
+ counter := LINE_COUNT
+ loop
+ somewhere h-line
+ somewhere v-line
+ counter += -1
+ if counter != 0 then
+ again
+ counter := FILL_COUNT
+ loop
+ somewhere fill
+ counter += -1
+ if counter != 0 then
+ again
+ v0 := key
+ clear
+ again
+
+: somewhere
+ x := random 0xFF
+ y := random 0xFF
+;
+
+: scan
+ loop
+ sprite px py 1
+ while vf == 0
+ px += dx
+ py += dy
+ again
+ sprite px py 1
+;
+
+: v-line
+ px := x dx := 0
+ py := y dy := -1 scan
+ py := y dy := 1 py += dy scan
+;
+
+: h-line
+ py := y dy := 0
+ px := x dx := -1 scan
+ px := x dx := 1 px += dx scan
+;
+
+: fill
+ loop
+ h-line
+ y += 1
+ sprite x y 1
+ while vf == 0
+ again
+ sprite x y 1
+;
+
+: point 0x80
diff --git a/resources/octoroms/murder.8o b/resources/octoroms/murder.8o
new file mode 100644
index 0000000..a6677a1
--- /dev/null
+++ b/resources/octoroms/murder.8o
@@ -0,0 +1,4010 @@
+###########################################
+#
+# An Evening to Die For
+#
+# It is a dark and stormy night.
+# A murer has been committed.
+# Track down a murder weapon and identify
+# the killer to bring them to justice!
+#
+# Arrow keys/ASWD move and change selections,
+# Space/E advance dialog, confirm selections,
+# and search the surrounding area for clues.
+#
+# An entry in the 2019 Octojam 6
+# by John Earnest
+#
+###########################################
+
+:calc CODE_POS { 0x200 }
+:calc DATA_POS { 0x1000 }
+:macro to-code { :calc DATA_POS { HERE } :org { CODE_POS } }
+:macro to-data { :calc CODE_POS { HERE } :org { DATA_POS } }
+
+:macro unpack16 ADDR {
+ :calc hi { 0xFF & ADDR >> 8 } v0 := hi
+ :calc lo { 0xFF & ADDR } v1 := lo
+}
+:macro pointer ADDR {
+ :byte { 0xFF & ADDR >> 8 }
+ :byte { 0xFF & ADDR }
+}
+:macro indirect LABEL {
+ 0xF0 0x00 : LABEL 0x00 0x00 # i := long NNNN
+}
+
+:macro mirror-byte X {
+ :byte { ( 1 & X >> 7 ) | ( 2 & X >> 5 ) | ( 4 & X >> 3 ) | ( 8 & X >> 1 ) |
+ ( 128 & X << 7 ) | ( 64 & X << 5 ) | ( 32 & X << 3 ) | ( 16 & X << 1 ) }
+}
+:macro mirror-8x15-row {
+ :calc val { @ base + index }
+ mirror-byte val
+ :calc index { index + 1 }
+}
+:macro mirror-8x15 ADDR {
+ :calc base { ADDR }
+ :calc index { 0 }
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+ mirror-8x15-row
+}
+
+: sync
+ vf := 5
+: sync-custom
+ delay := vf
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+;
+:macro wait TIME {
+ vf := TIME
+ sync-custom
+}
+
+: full-screen-blit #lrtb
+ v0 := 0
+ v1 := 0
+ v2 := 16
+ v3 := 32
+ v4 := 48
+ loop
+ sprite v0 v1 0 i += v3
+ sprite v0 v2 0 i += v3
+ sprite v0 v3 0 i += v3
+ sprite v0 v4 0 i += v3
+ v0 += 16
+ if v0 != 128 then
+ again
+;
+
+###########################################
+#
+# Global State
+#
+###########################################
+
+# v0-v4 and ve are currently reserved for temporaries
+:const GAME_OVER 0xFA # set current-room to this to bail out.
+:alias current-room vd
+:alias suspicion-level vc # 0-255
+:alias has-spyglass vb # 0/1
+:alias has-weapon va # 0/1
+
+# used during room exploration mode:
+:alias prev-x v5
+:alias prev-y v6
+:alias player-face v7
+:alias player-x v8
+:alias player-y v9
+
+# used during endgame:
+:alias accused-npc v9
+:alias guilty-npc v8
+:alias guilty-weapon v7
+
+# how much does various actions advanced the game?
+:const SUSPICION_TALK_NPC 2
+:const SUSPICION_TALK_MURDERER 5
+:const SUSPICION_EXAMINE_DECO 1
+:const SUSPICION_EXAMINE_ITEM 2
+:const SUSPICION_ENTER_ROOM 1
+:const SUSPICION_TAKE_WEAPON 5
+:const SUSPICION_TAKE_SPYGLASS 5
+
+: thicken-plot-sub
+ suspicion-level += ve
+ if vf != 0 then suspicion-level := 0xFF
+;
+:macro thicken-plot CONST {
+ ve := CONST
+ thicken-plot-sub
+}
+
+to-data
+
+: has-body-room 0x00 # 0-9
+: has-passage-room 0x00 # 0-9, replaces desc 1
+: has-secret-room 0x00 # 0-9, replaces desc 2
+: murder-weapon 0x00 # 0-7
+
+: initial-item-positions
+ 0 1 2 3 4 5 6 7 8 -1 -1
+: item-positions
+ 0 0 0 0 0 0 0 0 0 0 0
+
+: npc-positions
+ 0 1 # room 0, [slot 1, slot 2] ...
+ 2 3 # room 1
+ 4 5 # room 2
+ 6 7 # room 3
+ -1 -1 # room 4
+ -1 -1 # room 5
+ -1 -1 # room 6
+ -1 -1 # room 7
+ -1 -1 # room 8
+ -1 -1 # room 9
+ -1 -1 # room 10
+
+: npc-alibis
+ : npc-alibi-p1 0 1 # first pair
+ : npc-alibi-p2 2 3 # second pair
+ : npc-alibi-p3 4 5 # third pair
+ : npc-alone 6 # by themselves
+ : npc-murderer 7 # what it says on the tin
+
+: npc-quip-index
+: npc-quip-1 0 1
+: npc-quip-2 2 3
+ 4 5 6 7
+
+: npc-scoff-index
+ 0 1 2 3 4 5 6 7
+
+: npc-alibi-rooms
+ : npc-alibi-r1 0
+ : npc-alibi-r2 1
+ : npc-alibi-r3 2
+ : npc-alibi-r4 3
+ : npc-alibi-r5 4
+ 5 6 7 8 9 # and the rest aren't occupied
+
+to-code
+
+: random-upto-v1
+ loop
+ v0 := random 0xF
+ if v0 > v1 then
+ again
+;
+
+: shuffle-base i := long 0 ; # SMC
+: shuffle-array
+ loop
+ v1 := v4
+ random-upto-v1
+ v3 := v0
+
+ if v3 != v4 begin
+ # swap a[v4] / a[v3]...
+ shuffle-base
+ i += v3
+ load v1 - v1 # has a[v3]
+ shuffle-base
+ i += v4
+ load v2 - v2 # has a[v4]
+ save v1 - v1
+ shuffle-base
+ i += v3
+ save v2 - v2
+ end
+
+ v4 += -1
+ if v4 != 2 then
+ again
+;
+
+:macro random-scalar MAX ADDR {
+ v1 := MAX
+ random-upto-v1
+ i := long ADDR
+ save v0
+}
+:macro shuffle LEN ADDR {
+ :calc target { shuffle-base + 2 }
+ i := target
+ unpack16 ADDR
+ save v1
+ :calc maxindex { LEN - 1 }
+ v4 := maxindex
+ shuffle-array
+}
+
+:macro plot-a-murder {
+ # gotta re-initialize this,
+ # as items can be removed during gameplay:
+ i := long initial-item-positions
+ load vA
+ i := long item-positions
+ save vA
+
+ # generate randomized scenario
+ random-scalar 9 has-body-room
+ random-scalar 9 has-passage-room
+ random-scalar 9 has-secret-room
+ random-scalar 7 murder-weapon
+ shuffle 11 item-positions
+ shuffle 18 npc-positions
+ shuffle 8 npc-alibis
+ shuffle 8 npc-quip-index
+ shuffle 8 npc-scoff-index
+ shuffle 10 npc-alibi-rooms
+
+ # re-init global game state
+ suspicion-level := 0
+ has-spyglass := 0
+ has-weapon := 0
+ current-room := 5
+}
+
+###########################################
+#
+# Text rendering routines
+#
+# These generic routines can draw pre-wrapped
+# bytecoded strings with 2 colors, 2 font sizes,
+# and 2 inlineable text fragment slots,
+# as created by EZ-Writer:
+#
+# http://beyondloom.com/tools/ezwriter.html
+#
+# Pointers are "passed" to these routines
+# by rewriting immediate i-loads,
+# and macros help make the process more compact.
+#
+# Note that fragments should NOT contain STR_POS
+# or recursive fragment references!
+#
+###########################################
+
+:const STR_END 0xFF
+:const STR_POS 0xFE
+:const STR_COLOR 0xFD
+:const STR_SIZE 0xFC
+:const STR_SLOT_0 0xFB
+:const STR_SLOT_1 0xFA
+:const STR_SPACE 0xF9
+
+:const CHAR_WIDTH_SMALL 6
+:const CHAR_WIDTH_LARGE 8
+:calc CHAR_WIDTH_DIFF { CHAR_WIDTH_LARGE - CHAR_WIDTH_SMALL }
+
+:alias fragment-offset v3
+:alias string-offset v4
+:alias cursor-x v5
+:alias cursor-y v6
+:alias font-size v7
+:alias font-color v8
+:alias bit-flip v9
+
+: print-char
+ if v0 == STR_POS begin
+ string-offset += 2
+ cursor-x := v1
+ cursor-y := v2
+ return
+ end
+ if v0 == STR_SPACE begin
+ cursor-x += CHAR_WIDTH_SMALL
+ if font-size != 0 then cursor-x += CHAR_WIDTH_DIFF
+ return
+ end
+ if v0 == STR_COLOR begin
+ font-color ^= bit-flip
+ return
+ end
+ if v0 == STR_SIZE begin
+ font-size ^= bit-flip
+ return
+ end
+ if v0 == STR_SLOT_0 begin
+ fragment-offset := 0
+ loop
+ indirect print-string0-read
+ i += fragment-offset
+ load v0
+ if v0 == STR_END then return
+ fragment-offset += 1
+ print-char
+ again
+ end
+ if v0 == STR_SLOT_1 begin
+ fragment-offset := 0
+ loop
+ indirect print-string1-read
+ i += fragment-offset
+ load v0
+ if v0 == STR_END then return
+ fragment-offset += 1
+ print-char
+ again
+ end
+ # draw a plain character
+ if font-size == 0 begin
+ i := long font
+ i += v0 # add v0 * 9
+ v0 += v0
+ i += v0
+ i += v0
+ i += v0
+ i += v0
+ sprite cursor-x cursor-y 9
+ if font-color != 0 begin
+ plane 2
+ sprite cursor-x cursor-y 9
+ plane 1
+ end
+ cursor-x += CHAR_WIDTH_SMALL
+ else
+ i := long bigfont
+ v0 += v0 # add v0 * 14
+ i += v0
+ i += v0
+ i += v0
+ i += v0
+ i += v0
+ i += v0
+ i += v0
+ sprite cursor-x cursor-y 14
+ if font-color != 0 begin
+ plane 2
+ sprite cursor-x cursor-y 14
+ plane 1
+ end
+ cursor-x += CHAR_WIDTH_LARGE
+ end
+;
+: print-text
+ i := print-string-read
+ save v1
+ i := long string-stash
+ save vf
+ i := long print-text-registers
+ load string-offset - bit-flip
+ loop
+ indirect print-string-read
+ i += string-offset
+ load v2
+ while v0 != STR_END
+ string-offset += 1
+ print-char
+ again
+ i := long string-stash
+ load vf
+;
+
+:macro print ADDR { unpack16 ADDR print-text }
+
+to-data
+
+: print-text-registers
+ 0 0 0 0 0 1
+: string-stash
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+: font
+ 0x1C 0x22 0x22 0x3E 0x22 0x22 0x22 0x00 0x00 # A
+ 0x3C 0x22 0x22 0x3C 0x22 0x22 0x3C 0x00 0x00 # B
+ 0x1C 0x22 0x20 0x20 0x20 0x22 0x1C 0x00 0x00 # C
+ 0x3C 0x22 0x22 0x22 0x22 0x22 0x3C 0x00 0x00 # D
+ 0x3E 0x20 0x20 0x3C 0x20 0x20 0x3E 0x00 0x00 # E
+ 0x3E 0x20 0x20 0x3C 0x20 0x20 0x20 0x00 0x00 # F
+ 0x1C 0x22 0x20 0x26 0x22 0x22 0x1C 0x00 0x00 # G
+ 0x22 0x22 0x22 0x3E 0x22 0x22 0x22 0x00 0x00 # H
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x00 0x00 # I
+ 0x02 0x02 0x02 0x02 0x22 0x22 0x1C 0x00 0x00 # J
+ 0x22 0x24 0x28 0x30 0x28 0x24 0x22 0x00 0x00 # K
+ 0x20 0x20 0x20 0x20 0x20 0x20 0x3E 0x00 0x00 # L
+ 0x22 0x36 0x2A 0x22 0x22 0x22 0x22 0x00 0x00 # M
+ 0x22 0x32 0x2A 0x26 0x22 0x22 0x22 0x00 0x00 # N
+ 0x1C 0x22 0x22 0x22 0x22 0x22 0x1C 0x00 0x00 # O
+ 0x3C 0x22 0x22 0x3C 0x20 0x20 0x20 0x00 0x00 # P
+ 0x1C 0x22 0x22 0x22 0x22 0x22 0x1C 0x02 0x00 # Q
+ 0x3C 0x22 0x22 0x3C 0x22 0x22 0x22 0x00 0x00 # R
+ 0x1C 0x22 0x20 0x1C 0x02 0x22 0x1C 0x00 0x00 # S
+ 0x3E 0x08 0x08 0x08 0x08 0x08 0x08 0x00 0x00 # T
+ 0x22 0x22 0x22 0x22 0x22 0x22 0x1C 0x00 0x00 # U
+ 0x22 0x22 0x22 0x14 0x14 0x08 0x08 0x00 0x00 # V
+ 0x22 0x22 0x22 0x22 0x2A 0x36 0x22 0x00 0x00 # W
+ 0x22 0x14 0x08 0x08 0x08 0x14 0x22 0x00 0x00 # X
+ 0x22 0x22 0x22 0x14 0x08 0x08 0x08 0x00 0x00 # Y
+ 0x3E 0x02 0x04 0x08 0x10 0x20 0x3E 0x00 0x00 # Z
+ 0x00 0x00 0x1E 0x22 0x22 0x26 0x1A 0x00 0x00 # a
+ 0x20 0x20 0x3C 0x22 0x22 0x22 0x3C 0x00 0x00 # b
+ 0x00 0x00 0x1C 0x22 0x20 0x20 0x1E 0x00 0x00 # c
+ 0x02 0x02 0x1E 0x22 0x22 0x22 0x1E 0x00 0x00 # d
+ 0x00 0x00 0x1C 0x22 0x3E 0x20 0x1E 0x00 0x00 # e
+ 0x06 0x08 0x1C 0x08 0x08 0x08 0x08 0x00 0x00 # f
+ 0x00 0x00 0x1E 0x22 0x22 0x22 0x1E 0x02 0x1C # g
+ 0x20 0x20 0x3C 0x22 0x22 0x22 0x22 0x00 0x00 # h
+ 0x08 0x00 0x08 0x08 0x08 0x08 0x08 0x00 0x00 # i
+ 0x08 0x00 0x08 0x08 0x08 0x08 0x08 0x08 0x30 # j
+ 0x20 0x20 0x24 0x28 0x38 0x24 0x22 0x00 0x00 # k
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x0C 0x00 0x00 # l
+ 0x00 0x00 0x3C 0x2A 0x2A 0x2A 0x2A 0x00 0x00 # m
+ 0x00 0x00 0x2C 0x32 0x22 0x22 0x22 0x00 0x00 # n
+ 0x00 0x00 0x1C 0x22 0x22 0x22 0x1C 0x00 0x00 # o
+ 0x00 0x00 0x3C 0x22 0x22 0x22 0x3C 0x20 0x20 # p
+ 0x00 0x00 0x1E 0x22 0x22 0x22 0x1E 0x02 0x02 # q
+ 0x00 0x00 0x2C 0x32 0x20 0x20 0x20 0x00 0x00 # r
+ 0x00 0x00 0x1E 0x20 0x1C 0x02 0x3C 0x00 0x00 # s
+ 0x08 0x08 0x1E 0x08 0x08 0x08 0x06 0x00 0x00 # t
+ 0x00 0x00 0x22 0x22 0x22 0x26 0x1A 0x00 0x00 # u
+ 0x00 0x00 0x22 0x22 0x14 0x14 0x08 0x00 0x00 # v
+ 0x00 0x00 0x2A 0x2A 0x2A 0x2A 0x14 0x00 0x00 # w
+ 0x00 0x00 0x22 0x14 0x08 0x14 0x22 0x00 0x00 # x
+ 0x00 0x00 0x22 0x22 0x22 0x22 0x1E 0x02 0x1C # y
+ 0x00 0x00 0x3E 0x04 0x08 0x10 0x3E 0x00 0x00 # z
+ 0x1C 0x22 0x26 0x2A 0x32 0x22 0x1C 0x00 0x00 # 0
+ 0x08 0x18 0x08 0x08 0x08 0x08 0x08 0x00 0x00 # 1
+ 0x1C 0x22 0x02 0x04 0x08 0x10 0x3E 0x00 0x00 # 2
+ 0x1C 0x22 0x02 0x0C 0x02 0x22 0x1C 0x00 0x00 # 3
+ 0x04 0x0C 0x14 0x24 0x3E 0x04 0x04 0x00 0x00 # 4
+ 0x3E 0x20 0x3C 0x02 0x02 0x22 0x1C 0x00 0x00 # 5
+ 0x1C 0x20 0x3C 0x22 0x22 0x22 0x1C 0x00 0x00 # 6
+ 0x3E 0x02 0x02 0x04 0x08 0x08 0x08 0x00 0x00 # 7
+ 0x1C 0x22 0x22 0x1C 0x22 0x22 0x1C 0x00 0x00 # 8
+ 0x1C 0x22 0x22 0x22 0x1E 0x02 0x1C 0x00 0x00 # 9
+ 0x00 0x00 0x00 0x00 0x00 0x0C 0x0C 0x00 0x00 # .
+ 0x08 0x08 0x08 0x08 0x08 0x00 0x08 0x00 0x00 # !
+ 0x1C 0x22 0x02 0x04 0x08 0x00 0x08 0x00 0x00 # ?
+ 0x00 0x00 0x00 0x00 0x00 0x18 0x18 0x08 0x10 # ,
+ 0x08 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # '
+ 0x00 0x00 0x00 0x3E 0x00 0x00 0x00 0x00 0x00 # -
+ 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0x00 0x00 # "
+
+: bigfont
+ 0x38 0x7C 0x6C 0x6C 0xC6 0xC6 0xFE 0xFE 0xC6 0xC6 0xC6 0xC6 0x00 0x00 # A
+ 0xF8 0xFC 0xCC 0xCC 0xF8 0xFC 0xC6 0xC6 0xC6 0xC6 0xFE 0xFC 0x00 0x00 # B
+ 0x7C 0xFE 0xC6 0xC0 0xC0 0xC0 0xC0 0xC0 0xC0 0xC6 0xFE 0x7C 0x00 0x00 # C
+ 0xF8 0xFC 0xCE 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xCE 0xFC 0xF8 0x00 0x00 # D
+ 0xFE 0xFE 0xC0 0xC0 0xC0 0xF8 0xF8 0xC0 0xC0 0xC0 0xFE 0xFE 0x00 0x00 # E
+ 0xFE 0xFE 0xC0 0xC0 0xC0 0xF8 0xF8 0xC0 0xC0 0xC0 0xC0 0xC0 0x00 0x00 # F
+ 0x7C 0xFE 0xC6 0xC6 0xC0 0xDE 0xDE 0xC6 0xC6 0xE6 0x7E 0x3E 0x00 0x00 # G
+ 0xC6 0xC6 0xC6 0xC6 0xC6 0xFE 0xFE 0xC6 0xC6 0xC6 0xC6 0xC6 0x00 0x00 # H
+ 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x00 0x00 # I
+ 0x0C 0x0C 0x0C 0x0C 0x0C 0x0C 0xCC 0xCC 0xCC 0xCC 0xFC 0x78 0x00 0x00 # J
+ 0xC6 0xCE 0xDC 0xF8 0xF0 0xF0 0xF8 0xD8 0xCC 0xCC 0xC6 0xC6 0x00 0x00 # K
+ 0xC0 0xC0 0xC0 0xC0 0xC0 0xC0 0xC0 0xC0 0xC0 0xC0 0xFE 0xFE 0x00 0x00 # L
+ 0xEC 0xFE 0xFE 0xD6 0xD6 0xD6 0xD6 0xC6 0xC6 0xC6 0xC6 0xC6 0x00 0x00 # M
+ 0xF8 0xFC 0xCC 0xCE 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0x00 0x00 # N
+ 0x7C 0xFE 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x00 # O
+ 0xFC 0xFE 0xC6 0xC6 0xC6 0xFE 0xFC 0xC0 0xC0 0xC0 0xC0 0xC0 0x00 0x00 # P
+ 0x7C 0xFE 0xC6 0xC6 0xC6 0xC6 0xD6 0xDE 0xDC 0xCE 0xF6 0x76 0x00 0x00 # Q
+ 0xFC 0xFE 0xC6 0xC6 0xC6 0xFE 0xFC 0xD8 0xCC 0xCC 0xC6 0xC6 0x00 0x00 # R
+ 0x7C 0xFE 0xC6 0xC0 0xC0 0xFC 0x7E 0x06 0x06 0xC6 0xFE 0x7C 0x00 0x00 # S
+ 0xFE 0xFE 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x38 0x18 0x00 0x00 # T
+ 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x00 # U
+ 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xE6 0x66 0x76 0x3E 0x1E 0x0E 0x00 0x00 # V
+ 0xC6 0xC6 0xC6 0xC6 0xC6 0xC6 0xD6 0xD6 0xD6 0xD6 0xFE 0xEC 0x00 0x00 # W
+ 0xC6 0xC6 0xEE 0x6C 0x7C 0x38 0x7C 0x6C 0x6C 0xC6 0xC6 0xC6 0x00 0x00 # X
+ 0xC6 0xC6 0xC6 0xE6 0x7E 0x3E 0x06 0x06 0x06 0xC6 0xFE 0x7C 0x00 0x00 # Y
+ 0xFE 0xFE 0x0C 0x0C 0x18 0x18 0x30 0x30 0x60 0x60 0xFE 0xFE 0x00 0x00 # Z
+ 0x00 0x00 0x00 0x00 0x76 0xFE 0xCE 0xC6 0xC6 0xC6 0xFE 0x7E 0x00 0x00 # a
+ 0xC0 0xC0 0xC0 0xC0 0xDC 0xFE 0xE6 0xC6 0xC6 0xC6 0xFE 0xFC 0x00 0x00 # b
+ 0x00 0x00 0x00 0x00 0x7C 0xFE 0xC6 0xC0 0xC0 0xC6 0xFE 0x7C 0x00 0x00 # c
+ 0x06 0x06 0x06 0x06 0x76 0xFE 0xCE 0xC6 0xC6 0xC6 0xFE 0x7E 0x00 0x00 # d
+ 0x00 0x00 0x00 0x00 0x7C 0xFE 0xC6 0xFE 0xF8 0xC6 0xFE 0x7C 0x00 0x00 # e
+ 0x00 0x1E 0x3E 0x30 0xFE 0xFE 0x30 0x30 0x30 0x30 0x70 0x60 0x00 0x00 # f
+ 0x00 0x00 0x00 0x00 0x7C 0xFE 0xC6 0xC6 0xC6 0xFE 0x7E 0x06 0xFE 0xFC # g
+ 0xC0 0xC0 0xC0 0xC0 0xDC 0xFE 0xE6 0xC6 0xC6 0xC6 0xC6 0xC6 0x00 0x00 # h
+ 0x00 0x18 0x18 0x00 0x18 0x18 0x18 0x18 0x18 0x18 0x1C 0x1C 0x00 0x00 # i
+ 0x00 0x18 0x18 0x00 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0xD8 0xF8 0x70 # j
+ 0xC0 0xC0 0xC0 0xC6 0xCE 0xDC 0xF8 0xF0 0xF8 0xDC 0xCE 0xC6 0x00 0x00 # k
+ 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x3C 0x3C 0x00 0x00 # l
+ 0x00 0x00 0x00 0x00 0xEC 0xFE 0xFE 0xD6 0xD6 0xC6 0xC6 0xC6 0x00 0x00 # m
+ 0x00 0x00 0x00 0x00 0xBC 0xFE 0xE6 0xC6 0xC6 0xC6 0xC6 0xC6 0x00 0x00 # n
+ 0x00 0x00 0x00 0x00 0x7C 0xFE 0xC6 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x00 # o
+ 0x00 0x00 0x00 0x00 0xBC 0xFE 0xC6 0xC6 0xC6 0xC6 0xFE 0xFC 0xC0 0xC0 # p
+ 0x00 0x00 0x00 0x00 0x76 0xFE 0xCE 0xC6 0xC6 0xC6 0xFE 0x7E 0x06 0x06 # q
+ 0x00 0x00 0x00 0x00 0xDC 0xFE 0xE6 0xC6 0xC0 0xC0 0xC0 0xC0 0x00 0x00 # r
+ 0x00 0x00 0x00 0x00 0x7E 0xFE 0xC0 0xFC 0x7E 0x06 0xFE 0xFC 0x00 0x00 # s
+ 0x00 0x30 0x30 0x30 0xFE 0xFE 0x30 0x30 0x30 0x30 0x3C 0x1C 0x00 0x00 # t
+ 0x00 0x00 0x00 0x00 0xC6 0xC6 0xC6 0xC6 0xC6 0xCE 0xFE 0x7A 0x00 0x00 # u
+ 0x00 0x00 0x00 0x00 0xC6 0xC6 0xEE 0x6C 0x7C 0x38 0x38 0x10 0x00 0x00 # v
+ 0x00 0x00 0x00 0x00 0xC6 0xC6 0xD6 0xD6 0xD6 0xFE 0xFE 0xEC 0x00 0x00 # w
+ 0x00 0x00 0x00 0x00 0xC6 0xEE 0x6C 0x38 0x7C 0xEE 0xC6 0xC6 0x00 0x00 # x
+ 0x00 0x00 0x00 0x00 0xC6 0xC6 0xC6 0xC6 0xFE 0x7E 0x06 0x06 0xFE 0xFC # y
+ 0x00 0x00 0x00 0x00 0xFE 0xFE 0x0E 0x1C 0x38 0x70 0xFE 0xFE 0x00 0x00 # z
+ 0x7C 0xFE 0xC6 0xD6 0xD6 0xD6 0xD6 0xD6 0xD6 0xC6 0xFE 0x7C 0x00 0x00 # 0
+ 0x18 0x38 0x78 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x7E 0x7E 0x00 0x00 # 1
+ 0x7C 0xFE 0xC6 0x06 0x3E 0x7C 0xE0 0xC0 0xC0 0xC0 0xFE 0xFE 0x00 0x00 # 2
+ 0x7C 0xFE 0xC6 0x06 0x1E 0x1C 0x06 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x00 # 3
+ 0x0C 0x1C 0x3C 0x6C 0xFE 0xFE 0x0C 0x0C 0x0C 0x0C 0x0C 0x0C 0x00 0x00 # 4
+ 0xFE 0xFE 0xC0 0xC0 0xFC 0xFE 0x06 0x06 0x06 0xC6 0xFE 0x7C 0x00 0x00 # 5
+ 0x7C 0xFE 0xC6 0xC0 0xFC 0xFE 0xC6 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x00 # 6
+ 0xFE 0xFE 0x06 0x0E 0x1C 0x18 0xFE 0xFE 0x30 0x30 0x30 0x30 0x00 0x00 # 7
+ 0x7C 0xFE 0xC6 0xC6 0x7C 0xFE 0xC6 0xC6 0xC6 0xC6 0xFE 0x7C 0x00 0x00 # 8
+ 0x7C 0xFE 0xC6 0xC6 0xFE 0x7E 0x06 0x06 0xC6 0xC6 0xFE 0x7C 0x00 0x00 # 9
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x18 0x18 0x18 0x00 0x00 # .
+ 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x00 0x18 0x18 0x18 0x00 0x00 # !
+ 0x7C 0xFE 0xC6 0xC6 0xDE 0x1C 0x18 0x18 0x00 0x18 0x18 0x18 0x00 0x00 # ?
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x18 0x18 0x18 0x08 0x10 # ,
+ 0x18 0x18 0x18 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # '
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x3C 0x3C 0x00 0x00 0x00 0x00 0x00 0x00 # -
+ 0x6C 0x6C 0x6C 0x24 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # "
+
+: fill-stash
+ 0x00 0x00 0x00 0x00 0x00
+: fill-sprite
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+
+: string-yes-no
+ 0xFE 0x1E 0x34 0x18 0x1E 0x2C 0xF9 0xFE 0x50 0x34 0x0D 0x28 0xFF
+: mask-yes-no
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+to-code
+
+: dialog-fill-screen
+ plane 3
+ clear
+ plane 1
+ i := long fill-stash
+ save v0 - v4
+ i := long fill-sprite
+ full-screen-blit
+ i := long fill-stash
+ load v0 - v4
+;
+
+: dialog-pause
+ # debounced key input, to prevent
+ # missing out on dialog.
+ vf := OCTO_KEY_E
+ loop if vf key then again # wait for release,
+ loop if vf -key then again # wait for press,
+ loop if vf key then again # wait for release...
+ wait 2 # ...plus a minimum time delay.
+ plane 3
+ clear
+ plane 1
+;
+
+: dialog-draw-answer
+ v0 := 24
+ if v2 == 0 then v0 += 48
+ v1 := 50
+ i := long mask-yes-no
+ sprite v0 v1 0
+ v0 += 16
+ sprite v0 v1 0
+;
+
+: dialog-yes-no
+ # returns 0/1 in v2
+ print string-yes-no
+ ve := OCTO_KEY_E
+ loop if ve key then again # wait for release
+ wait 2 # ...plus a minimum time delay.
+ v2 := 1
+ loop
+ dialog-draw-answer
+ ve := key
+ dialog-draw-answer
+ while ve != OCTO_KEY_E
+ if ve == OCTO_KEY_A then v2 := 1
+ if ve == OCTO_KEY_D then v2 := 0
+ again
+ ve := OCTO_KEY_E
+ loop if ve key then again # wait for release
+ clear
+;
+
+:macro print-from-table REG TABLE {
+ i := long TABLE
+ i += REG
+ i += REG
+ load v1
+ print-text
+}
+
+:macro fmt-from-table REG TABLE DEST {
+ i := long TABLE
+ i += REG
+ i += REG
+ load v3 - v4
+ i := DEST
+ save v3 - v4
+}
+
+###########################################
+#
+# Prelude
+#
+###########################################
+
+to-data
+
+: cake 0x08 0x22 0x08 0x2A 0x54 0xDD 0x7E 0x81 0xFF 0x7E 0x81 0xFF 0x7E
+
+: prelude-placing
+ 32 8
+ 80 10
+ 98 15
+ 22 13
+ 80 30
+ 90 35
+ 30 24
+ 18 35
+
+: prelude-0
+ 0xFE 0x24 0x11 0x44 0x16 0x21 0x1A 0x2D 0xF9 0x1A 0xFE 0x24 0x1B 0x2C 0x29 0x25
+ 0x1E 0x27 0x1D 0x22 0x1D 0xFE 0x24 0x25 0x29 0x1A 0x2B 0x2D 0x32 0x3F 0x44 0xFF
+: prelude-1
+ 0xFE 0x12 0x0F 0x44 0x08 0x27 0x1D 0x1E 0x1E 0x1D 0x3E 0xFE 0x12 0x19 0x07 0x1A
+ 0x29 0x29 0x32 0xF9 0x1B 0x22 0x2B 0x2D 0x21 0x1D 0x1A 0x32 0x41 0xFE 0x12 0x23
+ 0x02 0x25 0x1A 0x22 0x2B 0x1E 0x3F 0x44 0xFF
+: prelude-2
+ 0xFE 0x08 0x0D 0x44 0x0F 0x1E 0x2B 0x21 0x1A 0x29 0x2C 0xF9 0x30 0x1E 0xF9 0x2C
+ 0x21 0x28 0x2E 0x25 0x1D 0xFE 0x08 0x17 0x2E 0x2C 0x1E 0xF9 0x28 0x2E 0x2B 0xF9
+ 0x1E 0x2C 0x2D 0x1E 0x1E 0x26 0x1E 0x1D 0xFE 0x08 0x21 0x1C 0x28 0x25 0x25 0x1E
+ 0x1A 0x20 0x2E 0x1E 0x42 0x2C 0xF9 0x27 0x1E 0x30 0xFE 0x08 0x2B 0x2D 0x22 0x2D
+ 0x25 0x1E 0x40 0x44 0xFF
+: prelude-3
+ 0xFE 0x03 0x0B 0x44 0x10 0x2E 0x22 0x2D 0x1E 0xF9 0x2B 0x22 0x20 0x21 0x2D 0x3E
+ 0xFE 0x03 0x15 0x00 0xF9 0x1F 0x22 0x27 0x1E 0xF9 0x1B 0x22 0x2B 0x2D 0x21 0x1D
+ 0x1A 0x32 0xFE 0x03 0x1F 0x2D 0x28 0xF9 0x32 0x28 0x2E 0x41 0xFE 0x03 0x29 0x08
+ 0x27 0x2C 0x29 0x1E 0x1C 0x2D 0x28 0x2B 0xF9 0x01 0x25 0x1E 0x1C 0x21 0x26 0x1A
+ 0x27 0x3F 0x44 0xFF
+: prelude-4
+ 0xFE 0x03 0x0B 0x44 0x16 0x1E 0xF9 0x30 0x22 0x25 0x25 0xF9 0x1A 0x25 0x25 0xF9
+ 0x26 0x28 0x2C 0x2D 0xFE 0x03 0x15 0x1A 0x2C 0x2C 0x2E 0x2B 0x1E 0x1D 0x25 0x32
+ 0xF9 0x1F 0x1E 0x1E 0x25 0xF9 0x2C 0x1A 0x1F 0x1E 0x2B 0xFE 0x03 0x1F 0x30 0x22
+ 0x2D 0x21 0xF9 0x32 0x28 0x2E 0xF9 0x28 0x27 0xF9 0x2D 0x21 0x1E 0xF9 0x23 0x28
+ 0x1B 0x41 0xFE 0x03 0x29 0x1D 0x1E 0x1A 0x2B 0x3E 0x44 0xFF
+: prelude-5
+ 0xFE 0x05 0x0C 0x44 0x0D 0x28 0x2D 0xF9 0x2D 0x28 0xF9 0x2C 0x29 0x28 0x22 0x25
+ 0xF9 0x2D 0x21 0x1E 0xFE 0x05 0x16 0x26 0x28 0x28 0x1D 0x41 0xF9 0x1B 0x2E 0x2D
+ 0xF9 0x08 0xF9 0x1C 0x1A 0x27 0x42 0x2D 0xFE 0x05 0x20 0x21 0x1E 0x25 0x29 0xF9
+ 0x1B 0x2E 0x2D 0xF9 0x28 0x1B 0x2C 0x1E 0x2B 0x2F 0x1E 0xF9 0x1A 0x27 0xFE 0x05
+ 0x2A 0x1A 0x1B 0x2C 0x1E 0x27 0x1C 0x1E 0x3E 0x3E 0x3E 0x44 0xFF
+: prelude-6
+ 0xFE 0x02 0x08 0x44 0x06 0x28 0x28 0x1D 0xF9 0x29 0x28 0x22 0x27 0x2D 0x3E 0xFE
+ 0x02 0x12 0x00 0x1F 0x2D 0x1E 0x2B 0xF9 0x1A 0x25 0x25 0xF9 0x2D 0x21 0x1E 0xF9
+ 0x1E 0x1F 0x1F 0x28 0x2B 0x2D 0xFE 0x02 0x1C 0x2D 0x28 0xF9 0x2D 0x21 0x2B 0x28
+ 0x30 0xF9 0x2D 0x21 0x22 0x2C 0xF9 0x29 0x1A 0x2B 0x2D 0x32 0x41 0xFE 0x02 0x26
+ 0x30 0x21 0x1E 0x2B 0x1E 0xF9 0x1C 0x28 0x2E 0x25 0x1D 0xF9 0x28 0x2E 0x2B 0xFE
+ 0x02 0x30 0x21 0x28 0x2C 0x2D 0xF9 0x1B 0x1E 0x40 0x44 0xFF
+: prelude-7
+ 0xFE 0x02 0x08 0x44 0x08 0x2D 0x42 0x2C 0xF9 0x1B 0x1E 0x1E 0x27 0xF9 0x2A 0x2E
+ 0x22 0x2D 0x1E 0xF9 0x1A 0xFE 0x02 0x12 0x30 0x21 0x22 0x25 0x1E 0x41 0xF9 0x22
+ 0x27 0xF9 0x1F 0x1A 0x1C 0x2D 0x3E 0xFE 0x02 0x1C 0x0F 0x1E 0x2B 0x21 0x1A 0x29
+ 0x2C 0xF9 0x30 0x1E 0xF9 0x2C 0x21 0x28 0x2E 0x25 0x1D 0xF9 0x20 0x28 0xFE 0x02
+ 0x26 0x25 0x28 0x28 0x24 0x22 0x27 0x20 0xF9 0x1F 0x28 0x2B 0xF9 0x2D 0x21 0x1E
+ 0xFE 0x02 0x30 0x28 0x25 0x1D 0xF9 0x1B 0x1E 0x1A 0x27 0x40 0x44 0xFF
+
+: prelude-dialog-table
+ pointer prelude-0
+ pointer prelude-1
+ pointer prelude-2
+ pointer prelude-3
+ pointer prelude-4
+ pointer prelude-5
+ pointer prelude-6
+ pointer prelude-7
+
+to-code
+
+: prelude
+ dialog-fill-screen
+ wait 60
+
+ current-room := 11
+ clear
+ draw-room
+ player-face := 1
+ i := long room-stash-start
+ load player-x - player-y
+ ve := 0
+ draw-player
+ i := long cake
+ v0 := 60
+ v1 := 20
+ sprite v0 v1 13
+
+ v2 := 0
+ v3 := 0
+ loop
+ i := long prelude-placing
+ i += v2
+ i += v2
+ load v1
+ i := long npc-sprites
+ i += v3
+ sprite v0 v1 15
+ v3 += 16
+ v2 += 1
+ if v2 != 8 then
+ again
+ dialog-pause
+
+ v2 := 0
+ loop
+ introduce-npc
+ print-from-table v2 prelude-dialog-table
+ dialog-pause
+ v2 += 1
+ if v2 != 8 then
+ again
+
+ dialog-fill-screen
+ wait 60
+ jump intro-sequence
+
+###########################################
+#
+# Introduction
+#
+###########################################
+
+to-data
+
+: title-screen
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x69 0x01 0xFB
+ 0x01 0x9B 0x03 0x1B 0x03 0x3B 0x03 0xFB
+ 0x01 0xEA 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x00
+ 0xF8 0x00 0xDC 0x00 0xDF 0x00 0xEF 0x00
+ 0xEF 0x00 0xED 0x90 0xFD 0xB0 0xFD 0xF8
+ 0xFA 0xFC 0xFF 0xEC 0xEF 0xEF 0xEF 0x6F
+ 0xDB 0xEB 0xFF 0xFA 0xFB 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xDF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFE
+ 0xFF 0xF8 0xFF 0xF1 0xFF 0x87 0xFF 0x3F
+ 0xF8 0xFF 0xF7 0xFE 0xEF 0xFF 0xFF 0xFF
+ 0xDF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0xE0 0x79 0xF0 0xFD
+ 0xB0 0xCD 0x30 0xF9 0x70 0xE1 0x60 0xFD
+ 0x70 0x79 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x08 0x00 0x0C 0x00 0x7F 0x3C
+ 0x7F 0x7E 0x1C 0x66 0x18 0x66 0x1A 0x6C
+ 0x1E 0x7C 0x0C 0x38 0x00 0x00 0x00 0x00
+ 0x60 0x00 0x60 0x00 0xE0 0x00 0xF0 0x00
+ 0xF0 0x00 0xF0 0x00 0xF0 0xC0 0xF1 0xD8
+ 0xF1 0xFC 0xF3 0xFC 0xD3 0xFC 0xDB 0xBD
+ 0xFF 0xBC 0xFF 0x9E 0xFF 0xFF 0xFF 0xFB
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFE 0xFF 0xF0
+ 0xFF 0x80 0xFE 0x01 0xE0 0x03 0x00 0x07
+ 0x01 0xFF 0xFD 0xFF 0xE3 0xFF 0xDF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x13 0xC7 0xB7 0xEF
+ 0xB6 0x6E 0xB7 0xCC 0xE7 0x0D 0xC7 0xAD
+ 0x83 0xC9 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x3F 0xFF 0x1F 0xFF
+ 0x1F 0xFF 0x1F 0xFF 0x1F 0xFF 0x1F 0xFF
+ 0x1F 0xE0 0x1F 0xE0 0x1F 0xE0 0x1F 0xC0
+ 0x3F 0xC0 0x3F 0xC0 0x3F 0xC0 0x3F 0xC0
+ 0x7F 0x80 0x7F 0x81 0x7F 0xFF 0x7F 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0x00 0x00 0x00 0x00 0xC0 0x00 0xFC 0x00
+ 0xFF 0xC0 0xFC 0x39 0x03 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFD 0xFF 0xFB
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xDF 0xFF 0xFF
+ 0xFF 0xBF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x89 0xE3 0xDB 0xF7
+ 0xC3 0xB4 0xDB 0x36 0xDB 0x77 0x9B 0x67
+ 0xD2 0x71 0x00 0x03 0x00 0x0F 0x00 0x06
+ 0x00 0x00 0x00 0x00 0xF0 0x00 0xFC 0x07
+ 0xFE 0x1F 0xFF 0x3F 0xFF 0x26 0xFF 0xA6
+ 0x7F 0xBF 0x1F 0x9D 0x0F 0xCF 0x0F 0xCA
+ 0x0F 0xC0 0x0F 0xDF 0x0F 0xCF 0x1F 0xCF
+ 0x7F 0xCF 0xFF 0x9F 0xFF 0x9F 0xFF 0x9F
+ 0xFF 0x1F 0xFE 0x1F 0xFC 0x3F 0xF0 0x3F
+ 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x07
+ 0x0F 0xF7 0xFB 0xFF 0xF7 0xDF 0xCF 0xBF
+ 0x3F 0x7F 0xFF 0xFF 0xF9 0xFF 0xF7 0xFF
+ 0xFF 0xFF 0xDF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x80 0x00 0xC0 0x00
+ 0xC0 0x00 0xC0 0x00 0xC0 0x00 0xC0 0x00
+ 0x80 0x00 0x80 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x3F 0x80 0xFF
+ 0xC1 0xFF 0xE3 0xFF 0x67 0xFF 0x67 0xF8
+ 0xCF 0xF0 0xCF 0xE0 0x8F 0xFF 0x9F 0xFF
+ 0x1F 0xFF 0xDF 0xFF 0x9F 0xC0 0xBF 0xC0
+ 0xBF 0xC0 0xBF 0xE1 0xBF 0xFF 0x9F 0xFF
+ 0x1F 0xFF 0x0F 0xFF 0x07 0xFF 0x81 0xFF
+ 0x0E 0x00 0x3F 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0xE0 0x00 0xFE 0x00
+ 0xFE 0x00 0xFF 0x00 0xFF 0x00 0x3F 0x80
+ 0x1F 0x83 0x1F 0xDF 0xFF 0xDF 0xFF 0xDF
+ 0xFF 0xDF 0xFF 0xDF 0x00 0x1F 0x00 0x3F
+ 0xFF 0xBF 0xFF 0xBF 0xFF 0x7F 0xFF 0x7F
+ 0xFE 0xFF 0xFD 0xF8 0xFB 0xF0 0xE7 0xF3
+ 0x1F 0xC0 0xFF 0xE0 0xFF 0xF3 0xFF 0xC3
+ 0xFF 0xE7 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFC 0xFF 0xF8
+ 0x00 0x00 0x00 0x0C 0x00 0x0C 0x00 0x1E
+ 0x00 0x1E 0x00 0x3F 0x00 0x3F 0x00 0x3F
+ 0x00 0x7F 0x00 0x7F 0x00 0x7F 0x00 0x0F
+ 0x00 0x0E 0x00 0x0E 0x00 0x0F 0x00 0x0F
+ 0x00 0x0F 0x00 0x0F 0x00 0x0F 0x03 0xC0
+ 0x07 0xFF 0x1F 0xFF 0x7F 0xFE 0xFF 0xF0
+ 0xFF 0x87 0xFE 0x1F 0xFD 0xFF 0xF0 0xFF
+ 0xF3 0xFF 0xF0 0x7F 0xF8 0x7F 0xFC 0x03
+ 0xFF 0x00 0xFF 0xE0 0xFF 0xF8 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0x43 0x43 0x81 0x01
+ 0x99 0x91 0x99 0x13 0x91 0x27 0x81 0x21
+ 0xC3 0x21 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xF8 0xFF 0xC4 0xFF 0x00 0xF2 0x00
+ 0xD0 0x00 0x00 0x00 0x00 0x04 0x00 0x01
+ 0x00 0x00 0x0F 0xFF 0x3F 0xFF 0x3F 0xFF
+ 0x0F 0xBD 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xF7 0xBF 0xF7 0xBF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x03 0xE0
+ 0xFF 0x3F 0xF3 0xFF 0x0F 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0x7F 0xFF 0x0F 0xFF 0x01 0xFF 0x00 0x7F
+ 0xC0 0x1F 0xE0 0x07 0xF8 0x01 0xFD 0x00
+ 0xFE 0x01 0xFF 0x00 0xFF 0x80 0xFF 0xA0
+ 0xFF 0xC0 0xFF 0xC0 0xFF 0xF0 0xFF 0xE0
+ 0xFF 0xF0 0xFF 0xE0 0xFF 0xE0 0xFF 0xE0
+ 0xFF 0xC0 0xFF 0x80 0xF8 0x01 0xA0 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x40
+ 0x01 0x13 0x10 0x3F 0x0D 0xFF 0x3F 0xFF
+
+# a little 3x5 semifont
+: tiny-dash 0x00 0x00 0xE0 0x00 0x00
+: tiny-space 0x00 0x00 0x00 0x00 0x00
+: tiny-P 0xC0 0xA0 0xC0 0x80 0x80
+: tiny-R 0xC0 0xA0 0xC0 0xA0 0xA0
+: tiny-E 0xE0 0x80 0xC0 0x80 0xE0
+: tiny-S 0x60 0x80 0x40 0x20 0xC0
+: tiny-A 0x40 0xA0 0xE0 0xA0 0xA0
+: tiny-C 0x40 0xA0 0x80 0xA0 0x40
+: tiny-table
+ pointer tiny-dash
+ pointer tiny-P
+ pointer tiny-R
+ pointer tiny-E
+ pointer tiny-S
+ pointer tiny-S
+ pointer tiny-space
+ pointer tiny-S
+ pointer tiny-P
+ pointer tiny-A
+ pointer tiny-C
+ pointer tiny-E
+ pointer tiny-dash
+: tiny-table-end
+: wiggle-table 0 0 1 1 2 1 1 0 0 -1 -1 -2 -1 -1 0 0
+
+: lightning-right
+ 0x00 0x04 0x00 0x08 0x00 0x70 0x00 0x88 0x01 0x08 0x1E 0x00 0x28 0x00 0x08 0x00
+ 0x04 0x00 0x03 0x00 0x01 0x00 0x03 0x00 0x24 0x80 0x58 0x80 0x81 0x00 0x80 0x00
+: lightning-left
+ 0x78 0x00 0x1C 0x00 0x0C 0x00 0x0C 0x00 0x0E 0x00 0x0B 0x00 0x18 0xC0 0x10 0x60
+ 0x20 0x20 0x20 0x20 0x20 0x1C 0x60 0x12 0x90 0x09 0x88 0x00 0x04 0x00 0x04 0x00
+: rain # (padded to 8x16)
+ 0x80 0x00 0x00 0x80 0x80 0x00 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x00
+ 0x80 0x00 0x80 0x80 0x80 0x80 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x00 0x00 0x00 0x00
+ 0x80 0x80 0x80 0x80 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x00 0x00 0x00
+: dark-and-stormy # "It is a dark and stormy night."
+ 0xFE 0x0B 0x15 0x08 0x2D 0xF9 0x22 0x2C 0xF9 0x1A 0xF9 0x1D 0x1A 0x2B 0x24 0xFE
+ 0x0B 0x1F 0x1A 0x27 0x1D 0xF9 0x2C 0x2D 0x28 0x2B 0x26 0x32 0xF9 0x27 0x22 0x20
+ 0x21 0x2D 0x3E 0xFF
+: a-murder-has-been-committed
+ 0xFE 0x0B 0x15 0xFD 0x00 0xF9 0x26 0x2E 0x2B 0x1D 0x1E 0x2B 0xF9 0x21 0x1A 0x2C
+ 0xF9 0x1B 0x1E 0x1E 0x27 0xF9 0xFE 0x0B 0x1F 0x1C 0x28 0x26 0x26 0x22 0x2D 0x2D
+ 0x1E 0x1D 0x3E 0x3E 0x3E 0xFF
+
+: a-shocking-end
+ 0xFE 0x00 0x12 0x08 0x2D 0xF9 0x2C 0x1E 0x1E 0x26 0x2C 0xF9 0x2D 0x21 0x1E 0xF9
+ 0x27 0x22 0x20 0x21 0x2D 0x42 0x2C 0xFE 0x00 0x1C 0x1F 0x1E 0x2C 0x2D 0x22 0x2F
+ 0x22 0x2D 0x22 0x1E 0x2C 0xF9 0x21 0x1A 0x2F 0x1E 0xF9 0x1C 0x28 0x26 0x1E 0xFE
+ 0x00 0x26 0x2D 0x28 0xF9 0x1A 0xF9 0x2C 0x21 0x28 0x1C 0x24 0x22 0x27 0x20 0xF9
+ 0x1E 0x27 0x1D 0x3E 0xFF
+: a-pool-of-blood
+ 0xFE 0x02 0x0D 0x0E 0x2E 0x2B 0xF9 0x25 0x1A 0x2D 0x1E 0xF9 0x21 0x28 0x2C 0x2D
+ 0x42 0x2C 0xF9 0x1B 0x28 0x1D 0x32 0xFE 0x02 0x17 0x30 0x1A 0x2C 0xF9 0x23 0x2E
+ 0x2C 0x2D 0xF9 0x1D 0x22 0x2C 0x1C 0x28 0x2F 0x1E 0x2B 0x1E 0x1D 0xFE 0x02 0x21
+ 0x1F 0x1A 0x1C 0x1E 0x43 0x1D 0x28 0x30 0x27 0xF9 0x22 0x27 0xF9 0x1A 0xF9 0x29
+ 0x28 0x28 0x25 0xFE 0x02 0x2B 0x28 0x1F 0xF9 0x21 0x22 0x2C 0xF9 0x28 0x30 0x27
+ 0xF9 0x1B 0x25 0x28 0x28 0x1D 0x3E 0x3E 0x3E 0xFF
+: get-to-the-bottom-of-it
+ 0xFE 0x00 0x0C 0x08 0xF9 0x26 0x2E 0x2C 0x2D 0xF9 0x20 0x1E 0x2D 0xF9 0x2D 0x28
+ 0xF9 0x2D 0x21 0x1E 0xFE 0x00 0x16 0x1B 0x28 0x2D 0x2D 0x28 0x26 0xF9 0x28 0x1F
+ 0xF9 0x2D 0x21 0x22 0x2C 0xF9 0x1A 0x2D 0xFE 0x00 0x20 0x28 0x27 0x1C 0x1E 0x43
+ 0xF9 0x1B 0x1E 0x1F 0x28 0x2B 0x1E 0xF9 0x28 0x2E 0x2B 0xFE 0x00 0x2A 0x24 0x22
+ 0x25 0x25 0x1E 0x2B 0xF9 0x2C 0x2D 0x2B 0x22 0x24 0x1E 0x2C 0xF9 0x1A 0x20 0x1A
+ 0x22 0x27 0x3F 0xFF
+
+: intro-1-quip-0 # "Good grief, he's dead!"
+ 0xFE 0x1A 0x14 0x44 0x06 0x28 0x28 0x1D 0xF9 0x20 0x2B 0x22 0x1E 0x1F 0x41 0xFE
+ 0x1A 0x1E 0x21 0x1E 0x42 0x2C 0xF9 0x1D 0x1E 0x1A 0x1D 0x3F 0x44 0xFF
+: intro-1-quip-1 # "Hmm. No sign of a pulse."
+ 0xFE 0x03 0x14 0x44 0x07 0x26 0x26 0x3E 0xFE 0x03 0x1E 0x0D 0x28 0xF9 0x2C 0x22
+ 0x20 0x27 0xF9 0x28 0x1F 0xF9 0x1A 0xF9 0x29 0x2E 0x25 0x2C 0x1E 0x3E 0x44 0xFF
+: intro-1-quip-2 # "I can't believe it! Murdered in his own home?"
+ 0xFE 0x03 0x11 0x44 0x08 0xF9 0x1C 0x1A 0x27 0x42 0x2D 0xF9 0x1B 0x1E 0x25 0x22
+ 0x1E 0x2F 0x1E 0xF9 0x22 0x2D 0x3F 0xFE 0x03 0x1B 0x0C 0x2E 0x2B 0x1D 0x1E 0x2B
+ 0x1E 0x1D 0xF9 0x22 0x27 0xF9 0x21 0x22 0x2C 0xFE 0x03 0x25 0x28 0x30 0x27 0xF9
+ 0x21 0x28 0x26 0x1E 0x40 0x44 0xFF
+: intro-1-quip-3 # "He's Dead? When could this have happened?"
+ 0xFE 0x10 0x10 0x44 0x07 0x1E 0x42 0x2C 0xF9 0x03 0x1E 0x1A 0x1D 0x40 0xFE 0x10
+ 0x1A 0x16 0x21 0x1E 0x27 0xF9 0x1C 0x28 0x2E 0x25 0x1D 0xF9 0x2D 0x21 0x22 0x2C
+ 0xFE 0x10 0x24 0x21 0x1A 0x2F 0x1E 0xF9 0x21 0x1A 0x29 0x29 0x1E 0x27 0x1E 0x1D
+ 0x40 0x44 0xFF
+: intro-1-quip-4 # "Doesn't take a coroner to classify this one, I'm afraid."
+ 0xFE 0x02 0x11 0x44 0x03 0x28 0x1E 0x2C 0x27 0x42 0x2D 0xF9 0x2D 0x1A 0x24 0x1E
+ 0xF9 0x1A 0xFE 0x02 0x1B 0x1C 0x28 0x2B 0x28 0x27 0x1E 0x2B 0xF9 0x2D 0x28 0xF9
+ 0x1C 0x1A 0x25 0x25 0xF9 0x2D 0x21 0x22 0x2C 0xFE 0x02 0x25 0x28 0x27 0x1E 0x41
+ 0xF9 0x08 0x42 0x26 0xF9 0x1A 0x1F 0x2B 0x1A 0x22 0x1D 0x3E 0x44 0xFF
+: intro-1-quip-5 # "Stone dead. What a mess!"
+ 0xFE 0x1A 0x18 0x44 0x12 0x2D 0x28 0x27 0x1E 0xF9 0x1D 0x1E 0x1A 0x1D 0x3E 0xFE
+ 0x1A 0x22 0x16 0x21 0x1A 0x2D 0xF9 0x1A 0xF9 0x26 0x1E 0x2C 0x2C 0x3F 0x44 0xFF
+: intro-1-quip-6 # "Oh dear. The carpet is simply ruined."
+ 0xFE 0x14 0x11 0x44 0x0E 0x21 0xF9 0x1D 0x1E 0x1A 0x2B 0x3E 0xFE 0x14 0x1B 0x13
+ 0x21 0x1E 0xF9 0x1C 0x1A 0x2B 0x29 0x1E 0x2D 0xF9 0x22 0x2C 0xFE 0x14 0x25 0x2C
+ 0x22 0x26 0x29 0x25 0x32 0xF9 0x2B 0x2E 0x22 0x27 0x1E 0x1D 0x3E 0x44 0xFF
+: intro-1-quip-7 # "I can't imagine he'll pull through..."
+ 0xFE 0x02 0x16 0x44 0x02 0x1A 0x27 0x42 0x2D 0xF9 0x22 0x26 0x1A 0x20 0x22 0x27
+ 0x1E 0xF9 0x21 0x1E 0x42 0x25 0x25 0xFE 0x02 0x20 0x29 0x2E 0x25 0x25 0xF9 0x2D
+ 0x21 0x2B 0x28 0x2E 0x20 0x21 0x3E 0x3E 0x3E 0x44 0xFF
+
+: intro-2-quip-0 # "Who could have done such a thing?"
+ 0xFE 0x02 0x16 0x44 0x16 0x21 0x28 0xF9 0x1C 0x28 0x2E 0x25 0x1D 0xF9 0x21 0x1A
+ 0x2F 0x1E 0xF9 0x1D 0x28 0x27 0x1E 0xFE 0x02 0x20 0x2C 0x2E 0x1C 0x21 0xF9 0x1A
+ 0xF9 0x2D 0x21 0x22 0x27 0x20 0x40 0x44 0xFF
+: intro-2-quip-1 # "I think I'm going to be sick!"
+ 0xFE 0x07 0x16 0x44 0x08 0xF9 0x2D 0x21 0x22 0x27 0x24 0xF9 0x08 0x42 0x26 0xF9
+ 0x20 0x28 0x22 0x27 0x20 0xFE 0x07 0x20 0x2D 0x28 0xF9 0x1B 0x1E 0xF9 0x2C 0x22
+ 0x1C 0x24 0x3F 0x44 0xFF
+: intro-2-quip-2 # "I simply can't bear the signt of blood..."
+ 0xFE 0x0F 0x12 0x44 0x08 0xF9 0x2C 0x22 0x26 0x29 0x25 0x32 0xF9 0x1C 0x1A 0x27
+ 0x42 0x2D 0xFE 0x0F 0x1C 0x1B 0x1E 0x1A 0x2B 0xF9 0x2D 0x21 0x1E 0xF9 0x2C 0x22
+ 0x20 0x21 0x2D 0xFE 0x0F 0x26 0x28 0x1F 0xF9 0x1B 0x25 0x28 0x28 0x1D 0x3E 0x3E
+ 0x3E 0x44 0xFF
+: intro-2-quip-3 # "Farewell, old chum. Such a way to go!"
+ 0xFE 0x02 0x16 0x44 0x05 0x1A 0x2B 0x1E 0x30 0x1E 0x25 0x25 0x41 0xF9 0x28 0x25
+ 0x1D 0xF9 0x1C 0x21 0x2E 0x26 0x3E 0xFE 0x02 0x20 0x12 0x2E 0x1C 0x21 0xF9 0x1A
+ 0xF9 0x30 0x1A 0x32 0xF9 0x2D 0x28 0xF9 0x20 0x28 0x3F 0x44 0xFF
+: intro-2-quip-4 # "How simply dreadful!"
+ 0xFE 0x1A 0x16 0x44 0x07 0x28 0x30 0xF9 0x2C 0x22 0x26 0x29 0x25 0x32 0xFE 0x1A
+ 0x20 0x1D 0x2B 0x1E 0x1A 0x1D 0x1F 0x2E 0x25 0x3F 0x44 0xFF
+: intro-2-quip-5 # "I think I'm going to need a few stiff drinks..."
+ 0xFE 0x05 0x11 0x44 0x08 0xF9 0x2D 0x21 0x22 0x27 0x24 0xF9 0x08 0x42 0x26 0xF9
+ 0x20 0x28 0x22 0x27 0x20 0xFE 0x05 0x1B 0x2D 0x28 0xF9 0x27 0x1E 0x1E 0x1D 0xF9
+ 0x1A 0xF9 0x1F 0x1E 0x30 0xF9 0x2C 0x2D 0x22 0x1F 0x1F 0xFE 0x05 0x25 0x1D 0x2B
+ 0x22 0x27 0x24 0x2C 0x3E 0x3E 0x3E 0x44 0xFF
+: intro-2-quip-6 # "There's... so much blood..."
+ 0xFE 0x0C 0x16 0x44 0x13 0x21 0x1E 0x2B 0x1E 0x42 0x2C 0x3E 0x3E 0x3E 0xFE 0x0C
+ 0x20 0x2C 0x28 0xF9 0x26 0x2E 0x1C 0x21 0xF9 0x1B 0x25 0x28 0x28 0x1D 0x3E 0x3E
+ 0x3E 0x44 0xFF
+: intro-2-quip-7 # "The old man always did know how to make an exit, I suppose."
+ 0xFE 0x02 0x11 0x44 0x13 0x21 0x1E 0xF9 0x28 0x25 0x1D 0xF9 0x26 0x1A 0x27 0xF9
+ 0x1A 0x25 0x30 0x1A 0x32 0x2C 0xFE 0x02 0x1B 0x1D 0x22 0x1D 0xF9 0x24 0x27 0x28
+ 0x30 0xF9 0x21 0x28 0x30 0xF9 0x2D 0x28 0xF9 0x26 0x1A 0x24 0x1E 0xFE 0x02 0x25
+ 0x1A 0x27 0xF9 0x1E 0x31 0x22 0x2D 0x41 0xF9 0x08 0xF9 0x2C 0x2E 0x29 0x29 0x28
+ 0x2C 0x1E 0x3E 0x44 0xFF
+
+: intro-table-1
+ pointer intro-1-quip-0
+ pointer intro-1-quip-1
+ pointer intro-1-quip-2
+ pointer intro-1-quip-3
+ pointer intro-1-quip-4
+ pointer intro-1-quip-5
+ pointer intro-1-quip-6
+ pointer intro-1-quip-7
+: intro-table-2
+ pointer intro-2-quip-0
+ pointer intro-2-quip-1
+ pointer intro-2-quip-2
+ pointer intro-2-quip-3
+ pointer intro-2-quip-4
+ pointer intro-2-quip-5
+ pointer intro-2-quip-6
+ pointer intro-2-quip-7
+: intro-sequence-registers
+ 0 # ve: animation index
+ 0 # vd: space key latch
+ 0 # vc: 1 key latch
+ 0xF # vb: constant index mask
+
+to-code
+
+: draw-lightning
+ clear
+ vf := random 0b1
+ v1 := 0
+ if vf == 0 begin
+ i := long lightning-right
+ v0 := 75
+ else
+ i := long lightning-left
+ v0 := 25
+ end
+ v2 := 10
+ loop
+ sprite v0 v1 0
+ sync
+ vf := 5
+ buzzer := vf
+ v2 += -1
+ if v2 != 0 then
+ again
+ clear
+;
+
+: draw-rain
+ loop
+ dialog-fill-screen
+ v2 := 20 # rain density
+ loop
+ i := long rain
+ vf := random 0b110000 # 0,16,32,48
+ i += vf
+ v0 := random 0xFF
+ v1 := random 0xFF
+ sprite v0 v1 15
+
+ v2 += -1
+ if v2 != 0 then
+ again
+ sync
+
+ v3 += -1
+ if v3 != 0 then
+ again
+ clear
+;
+
+: draw-wiggle-prompt
+ # -PRESS SPACE-
+ :calc prompt-length { 0xFF & ( tiny-table-end - tiny-table ) / 2 }
+ :calc prompt-offset-x { 0xFF & 64 - ( prompt-length * 4 ) / 2 }
+ v0 := prompt-offset-x
+ v1 := 58
+ v2 := 0
+ va := ve
+ loop
+ i := long tiny-table
+ i += v2
+ i += v2
+ load v3 - v4
+ i := long prompt-pointer
+ save v3 - v4
+
+ i := long wiggle-table
+ i += va
+ load v1 - v1
+ v1 += 57
+
+ indirect prompt-pointer
+ sprite v0 v1 5
+
+ v0 += 4
+ v2 += 1
+ va += 1
+ va &= vb
+ if v2 != prompt-length then
+ again
+;
+
+:macro latch-key KEY LATCH {
+ vf := KEY
+ if LATCH == 0 begin
+ if vf key then LATCH := 1
+ else
+ while vf key
+ end
+}
+
+: test-fight
+ fencing-minigame
+ jump intro-sequence
+
+: intro-sequence
+ clear
+ i := long title-screen
+ full-screen-blit
+
+ i := long intro-sequence-registers
+ load ve - vb
+ draw-wiggle-prompt
+ loop
+ draw-wiggle-prompt
+ ve += 1
+ ve &= vb
+ draw-wiggle-prompt
+ latch-key OCTO_KEY_E vd
+ latch-key OCTO_KEY_1 vc
+ vf := OCTO_KEY_2
+ if vf key then jump test-fight
+ wait 2
+ again
+
+ plane 3
+ clear
+ plane 1
+ if vc == 1 then jump prelude
+
+ v3 := 30 # rain duration
+ draw-rain
+ dialog-fill-screen
+ print dark-and-stormy
+ plot-a-murder # do this here to hide the delay
+ wait 120
+
+ draw-lightning
+ v3 := 10
+ draw-rain
+ draw-lightning
+ v3 := 10
+ draw-rain
+ draw-lightning
+ v3 := 40
+ draw-rain
+
+ dialog-fill-screen
+ print a-murder-has-been-committed
+ wait 120
+ dialog-fill-screen
+ wait 60
+
+ clear
+ print a-shocking-end
+ dialog-pause
+ print a-pool-of-blood
+ dialog-pause
+
+ i := long npc-quip-1
+ load v2 - v3
+ introduce-npc
+ print-from-table v3 intro-table-1
+ dialog-pause
+
+ i := long npc-quip-2
+ load v2 - v3
+ introduce-npc
+ print-from-table v3 intro-table-1
+ dialog-pause
+
+ print get-to-the-bottom-of-it
+ dialog-pause
+;
+
+###########################################
+#
+# Inventory Items
+#
+###########################################
+
+to-data
+
+: no-time-for-that
+ 0xFE 0x0B 0x12 0x0D 0x28 0xF9 0x2D 0x22 0x26 0x1E 0xF9 0x1F 0x28 0x2B 0xF9 0x2D
+ 0x21 0x1A 0x2D 0x43 0xFE 0x0B 0x1C 0x2D 0x21 0x1E 0x2B 0x1E 0x42 0x2C 0xF9 0x1A
+ 0xF9 0x26 0x2E 0x2B 0x1D 0x1E 0x2B 0xFE 0x0B 0x26 0x2D 0x28 0xF9 0x1B 0x1E 0xF9
+ 0x2C 0x28 0x25 0x2F 0x1E 0x1D 0x3F 0xFF
+: upon-closer-inspection
+ 0xFE 0x0F 0x12 0x18 0x28 0x2E 0xF9 0x1E 0x31 0x1A 0x26 0x22 0x27 0x1E 0xF9 0x22
+ 0x2D 0xFE 0x0F 0x1C 0x1C 0x1A 0x2B 0x1E 0x1F 0x2E 0x25 0x25 0x32 0xF9 0x30 0x22
+ 0x2D 0x21 0xFE 0x0F 0x26 0x2D 0x21 0x1E 0xF9 0x2C 0x29 0x32 0x20 0x25 0x1A 0x2C
+ 0x2C 0x3E 0x3E 0x3E 0xFF
+: nothing-unusual
+ 0xFE 0x0B 0x16 0x18 0x28 0x2E 0xF9 0x1F 0x22 0x27 0x1D 0xF9 0x27 0x28 0x2D 0x21
+ 0x22 0x27 0x20 0xFE 0x0B 0x20 0x2E 0x27 0x2E 0x2C 0x2E 0x1A 0x25 0xF9 0x1A 0x1B
+ 0x28 0x2E 0x2D 0xF9 0x22 0x2D 0x3E 0xFF
+: traces-of-blood
+ 0xFE 0x0C 0x16 0xFD 0x13 0x21 0x1E 0x2B 0x1E 0xF9 0x1A 0x2B 0x1E 0xF9 0x2D 0x2B
+ 0x1A 0x1C 0x1E 0x2C 0xF9 0xFE 0x10 0x20 0x28 0x1F 0xF9 0x1B 0x25 0x28 0x28 0x1D
+ 0xF9 0x28 0x27 0xF9 0x22 0x2D 0x3F 0xFF
+: take-murder-weapon
+ 0xFE 0x0E 0x12 0x13 0x1A 0x24 0x1E 0xF9 0x2D 0x21 0x1E 0xF9 0x26 0x2E 0x2B 0x1D
+ 0x1E 0x2B 0xFE 0x0E 0x1C 0x30 0x1E 0x1A 0x29 0x28 0x27 0xF9 0x30 0x22 0x2D 0x21
+ 0xF9 0x32 0x28 0x2E 0x40 0xFF
+
+: item-lead-pipe # "Seems a bit dangerous."
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFE 0xE1 0x7D 0xDD 0x83 0x22 0xFE 0x19 0x86
+ 0x0C 0x06 0x0E 0x0A 0x07 0x56 0x03 0xFC 0x01 0xF8 0x00 0x00 0x00 0x00 0x00 0x00
+: item-bullet # "An ordinary household bullet."
+ 0x01 0x80 0x03 0x40 0x03 0x40 0x07 0xA0 0x07 0xA0 0x07 0xE0 0x0B 0xD0 0x08 0x30
+ 0x0A 0xF0 0x0A 0xF0 0x0A 0xF0 0x0A 0xF0 0x1A 0xF8 0x17 0xE8 0x08 0x10 0x07 0xE0
+: item-pewter-vase # "Tacky, but quite heavy."
+ 0x01 0x80 0x1E 0x78 0x21 0x84 0x1C 0x38 0x1F 0xF8 0x37 0xEC 0x24 0x24 0x44 0x22
+ 0x49 0x92 0x4A 0x92 0x49 0x12 0x6C 0xB6 0x6C 0x36 0x36 0x6C 0x1F 0xF8 0x07 0xE0
+: item-jar-of-caviar # "Fish eggs, Fish eggs. Eat them up, yum."
+ 0x00 0x00 0x7F 0xFE 0x5F 0xFA 0x40 0x02 0x40 0x02 0x3F 0xFC 0x5C 0x9A 0x57 0x62
+ 0x5B 0xAA 0x5C 0xDA 0x5B 0x3A 0x51 0x2A 0x6F 0xF6 0x30 0x0C 0x1F 0xF8 0x00 0x00
+: item-jade-skull # "Supposedly stolen from a famous rapper."
+ 0x07 0xF0 0x18 0x0C 0x20 0x02 0x20 0x41 0x6F 0x21 0x67 0x81 0x47 0x83 0x62 0x3B
+ 0xF0 0x73 0xB3 0xE6 0x81 0xFE 0x55 0xBC 0x2B 0x78 0x57 0xC0 0x7F 0xC0 0x3F 0x80
+: item-brandy-snifter # "I'm not in the mood for a drink."
+ 0x07 0xE0 0x0B 0xD0 0x08 0x10 0x10 0x08 0x10 0x08 0x24 0x24 0x30 0x0C 0x2A 0x54
+ 0x3C 0x3C 0x1B 0xD8 0x0E 0x70 0x03 0xC0 0x01 0x80 0x07 0xE0 0x0F 0xF0 0x03 0xC0
+: item-glass-orb # "It catches the light from every angle..."
+ 0x03 0xC0 0x0C 0x30 0x11 0x08 0x2A 0x64 0x5A 0x6A 0x59 0x02 0xB4 0xF1 0xA4 0x09
+ 0xBA 0x65 0xB3 0xF5 0x5F 0xB2 0x56 0xFA 0x2F 0xF4 0x13 0xC8 0x0C 0x30 0x03 0xC0
+: item-fire-poker # "I prefer Texas Hold-em."
+ 0x00 0x03 0x00 0x05 0x00 0x0A 0x00 0x16 0x00 0x2C 0x00 0x58 0x00 0xB0 0x01 0xE0
+ 0x03 0xC0 0x77 0x00 0xFC 0x00 0xB8 0x00 0x30 0x00 0x60 0x00 0xC0 0x00 0x80 0x00
+: item-spyglass # "Ah, my spyglass! I can examine things more closely now."
+ 0x0F 0x80 0x10 0x40 0x2F 0xA0 0x50 0x50 0xA3 0x28 0xE3 0x28 0xA0 0xA8 0xE8 0x28
+ 0x70 0x70 0x3F 0xD0 0x1F 0xF8 0x0F 0xB4 0x00 0x1A 0x00 0x0F 0x00 0x07 0x00 0x02
+
+: item-title-pipe 0xFE 0x1C 0x2D 0xFC 0x0B 0x1E 0x1A 0x1D 0xF9 0x0F 0x22 0x29 0x1E 0xFF
+: item-title-bullet 0xFE 0x2A 0x2D 0xFC 0x01 0x2E 0x25 0x25 0x1E 0x2D 0xFF
+: item-title-vase 0xFE 0x15 0x2D 0xFC 0x0F 0x1E 0x30 0x2D 0x1E 0x2B 0xF9 0x15 0x1A 0x2C 0x1E 0xFF
+: item-title-caviar 0xFE 0x0D 0x2D 0xFC 0x09 0x1A 0x2B 0xF9 0x28 0x1F 0xF9 0x02 0x1A 0x2F 0x22 0x1A 0x2B 0xFF
+: item-title-skull 0xFE 0x1A 0x2D 0xFC 0x09 0x1A 0x1D 0x1E 0xF9 0x12 0x24 0x2E 0x25 0x25 0xFF
+: item-title-snifter 0xFE 0x09 0x2D 0xFC 0x01 0x2B 0x1A 0x27 0x1D 0x32 0xF9 0x12 0x27 0x22 0x1F 0x2D 0x1E 0x2B 0xFF
+: item-title-orb 0xFE 0x1D 0x2D 0xFC 0x06 0x25 0x1A 0x2C 0x2C 0xF9 0x0E 0x2B 0x1B 0xFF
+: item-title-poker 0xFE 0x1A 0x2D 0xFC 0x05 0x22 0x2B 0x1E 0xF9 0x0F 0x28 0x24 0x1E 0x2B 0xFF
+: item-title-spyglass 0xFE 0x21 0x2D 0xFC 0x12 0x29 0x32 0x20 0x25 0x1A 0x2C 0x2C 0xFF
+
+: item-desc-pipe 0xFE 0x18 0x12 0x12 0x1E 0x1E 0x26 0x2C 0xF9 0x1A 0xF9 0x1B 0x22 0x2D 0xFE
+ 0x18 0x1C 0x1D 0x1A 0x27 0x20 0x1E 0x2B 0x28 0x2E 0x2C 0xF9 0x2D 0x28 0xF9
+ 0xFE 0x18 0x26 0x2C 0x26 0x28 0x24 0x1E 0x3E 0x3E 0x3E 0xFF
+: item-desc-bullet 0xFE 0x0A 0x16 0x00 0x27 0xF9 0x28 0x2B 0x1D 0x22 0x27 0x1A 0x2B 0x32 0xFE
+ 0x0A 0x20 0x21 0x28 0x2E 0x2C 0x1E 0x21 0x28 0x25 0x1D 0xF9 0x1B 0x2E 0x25
+ 0x25 0x1E 0x2D 0x3E 0xFF
+: item-desc-vase 0xFE 0x10 0x14 0x13 0x1A 0x1C 0x24 0x32 0x41 0xF9 0x1B 0x2E 0x2D 0xF9 0x1A
+ 0x25 0x2C 0x28 0xFE 0x10 0x1E 0x2A 0x2E 0x22 0x2D 0x1E 0xF9 0x21 0x1E 0x1A
+ 0x2F 0x32 0x3E 0x3E 0x3E 0xFF
+: item-desc-caviar 0xFE 0x0B 0x12 0x05 0x22 0x2C 0x21 0xF9 0x1E 0x20 0x20 0x2C 0x41 0xF9 0xFE
+ 0x0B 0x1C 0x05 0x22 0x2C 0x21 0xF9 0x1E 0x20 0x20 0x2C 0x41 0xF9 0xFE 0x0B
+ 0x26 0x04 0x1A 0x2D 0xF9 0x2D 0x21 0x1E 0x26 0xF9 0x2E 0x29 0x43 0xF9 0x32
+ 0x2E 0x26 0x3E 0xFF
+: item-desc-skull 0xFE 0x00 0x12 0x12 0x2E 0x29 0x29 0x28 0x2C 0x1E 0x1D 0x25 0x32 0xF9 0x2C
+ 0x2D 0x28 0x25 0x1E 0x27 0xFE 0x00 0x1C 0x1F 0x2B 0x28 0x26 0xF9 0x1A 0xF9
+ 0x1F 0x1A 0x26 0x28 0x2E 0x2C 0xFE 0x00 0x26 0x2B 0x1A 0x29 0x29 0x1E 0x2B
+ 0xF9 0x1A 0x27 0x1D 0xF9 0x26 0x1E 0x2B 0x1C 0x1E 0x27 0x1A 0x2B 0x32 0x3E
+ 0xFF
+: item-desc-snifter 0xFE 0x0A 0x10 0x08 0x42 0x2F 0x1E 0xF9 0x21 0x1A 0x1D 0xF9 0x2A 0x2E 0x22
+ 0x2D 0x1E 0xFE 0x0A 0x1A 0x1E 0x27 0x28 0x2E 0x20 0x21 0xF9 0x2D 0x28 0xF9
+ 0x1D 0x2B 0x22 0x27 0x24 0xFE 0x0A 0x24 0x1A 0x25 0x2B 0x1E 0x1A 0x1D 0x32
+ 0x41 0xF9 0x08 0xF9 0x30 0x1A 0x20 0x1E 0x2B 0x3E 0xFF
+: item-desc-orb 0xFE 0x01 0x16 0x08 0x2D 0xF9 0x1C 0x1A 0x2D 0x1C 0x21 0x1E 0x2C 0xF9 0x2D
+ 0x21 0x1E 0xF9 0x25 0x22 0x20 0x21 0x2D 0xFE 0x01 0x20 0x1F 0x2B 0x28 0x26
+ 0xF9 0x1E 0x2F 0x1E 0x2B 0x32 0xF9 0x1A 0x27 0x20 0x25 0x1E 0x3E 0x3E 0x3E
+ 0xFF
+: item-desc-poker 0xFE 0x06 0x16 0x08 0xF9 0x1F 0x1A 0x2B 0xF9 0x29 0x2B 0x1E 0x1F 0x1E 0x2B
+ 0xF9 0x13 0x1E 0x31 0x1A 0x2C 0xF9 0xFE 0x06 0x20 0x21 0x28 0x25 0x1D 0x43
+ 0x1E 0x26 0x41 0xF9 0x00 0x1C 0x1E 0x2C 0xF9 0x30 0x22 0x25 0x1D 0x3E 0xFF
+: item-desc-spyglass 0xFE 0x03 0x12 0x00 0x21 0x1A 0x41 0xF9 0x1A 0xF9 0x2C 0x29 0x32 0x20 0x25
+ 0x1A 0x2C 0x2C 0x3F 0xFE 0x03 0x1C 0x08 0xF9 0x1C 0x1A 0x27 0xF9 0x1E 0x31
+ 0x1A 0x26 0x22 0x27 0x1E 0xF9 0x2D 0x21 0x22 0x27 0x20 0x2C 0xFE 0x03 0x26
+ 0x26 0x28 0x2B 0x1E 0xF9 0x1C 0x25 0x28 0x2C 0x1E 0x25 0x32 0xF9 0x27 0x28
+ 0x30 0x3E 0xFF
+
+: item-icon-table
+ pointer item-lead-pipe
+ pointer item-bullet
+ pointer item-pewter-vase
+ pointer item-jar-of-caviar
+ pointer item-jade-skull
+ pointer item-brandy-snifter
+ pointer item-glass-orb
+ pointer item-fire-poker
+ pointer item-spyglass
+
+: item-title-table
+ pointer item-title-pipe
+ pointer item-title-bullet
+ pointer item-title-vase
+ pointer item-title-caviar
+ pointer item-title-skull
+ pointer item-title-snifter
+ pointer item-title-orb
+ pointer item-title-poker
+ pointer item-title-spyglass
+
+: item-desc-table
+ pointer item-desc-pipe
+ pointer item-desc-bullet
+ pointer item-desc-vase
+ pointer item-desc-caviar
+ pointer item-desc-skull
+ pointer item-desc-snifter
+ pointer item-desc-orb
+ pointer item-desc-poker
+ pointer item-desc-spyglass
+
+to-code
+
+: take-item
+ i := long item-positions
+ i += current-room
+ v0 := -1
+ save v0
+;
+
+: portrait
+ i += v2
+ i += v2
+ load v1
+ i := icon-indirection
+ save v1
+ indirect icon-indirection
+ v0 := 56
+ v1 := 20
+ sprite v0 v1 0
+;
+
+: describe-item
+ # (pass the item index in v2...)
+ thicken-plot SUSPICION_EXAMINE_ITEM
+ clear
+
+ if has-weapon == 1 begin
+ print no-time-for-that
+ jump dialog-pause
+ end
+
+ i := long item-icon-table
+ portrait
+
+ print-from-table v2 item-title-table
+ dialog-pause
+
+ print-from-table v2 item-desc-table
+ dialog-pause
+
+ if v2 == 8 begin
+ thicken-plot SUSPICION_TAKE_SPYGLASS
+ has-spyglass := 1
+ jump take-item
+ end
+
+ if has-spyglass != 1 then return
+
+ print upon-closer-inspection
+ dialog-pause
+
+ i := long murder-weapon
+ load v0
+ if v0 == v2 begin
+ print traces-of-blood
+ dialog-pause
+ print take-murder-weapon
+ dialog-yes-no
+ if v2 == 0 then return
+ thicken-plot SUSPICION_TAKE_WEAPON
+ has-weapon := 1
+ jump take-item
+ end
+
+ print nothing-unusual
+ dialog-pause
+;
+
+###########################################
+#
+# The Body
+#
+###########################################
+
+to-data
+
+: body-sprite # 16x16
+ 0x70 0x00 0x89 0xF0 0x86 0x0C 0x40 0x62 0x30 0xDC 0x20 0x20 0x40 0x10 0x48 0x08
+ 0x54 0x64 0x54 0x52 0x52 0x29 0x51 0x29 0x20 0x95 0x00 0x4A 0x00 0x48 0x00 0x30
+: cycle-left 0x08 0x18 0x38 0x7F 0xFF 0x7F 0x38 0x18 0x08
+: cycle-right 0x10 0x18 0x1C 0xFE 0xFF 0xFE 0x1C 0x18 0x10
+: gather-npcs
+ 0xFE 0x02 0x0A 0x00 0x26 0xF9 0x08 0xF9 0x2B 0x1E 0x1A 0x1D 0x32 0xF9 0x2D 0x28
+ 0xF9 0x20 0x1A 0x2D 0x21 0x1E 0x2B 0xFE 0x02 0x14 0x2D 0x21 0x1E 0xF9 0x29 0x1A
+ 0x2B 0x2D 0x32 0xF9 0x20 0x2E 0x1E 0x2C 0x2D 0x2C 0xF9 0x1A 0x27 0x1D 0xFE 0x02
+ 0x1E 0x1E 0x31 0x29 0x28 0x2C 0x1E 0xF9 0x2D 0x21 0x1E 0xF9 0x24 0x22 0x25 0x25
+ 0x1E 0x2B 0x40 0xFF
+: who-dunnit
+ 0xFE 0x08 0x04 0x16 0x21 0x28 0xF9 0x22 0x2C 0xF9 0x2D 0x21 0x1E 0xF9 0x24 0x22
+ 0x25 0x25 0x1E 0x2B 0x40 0xFF
+: make-your-case
+ 0xFE 0x00 0x0C 0x13 0x21 0x1E 0xF9 0x20 0x2E 0x1E 0x2C 0x2D 0x2C 0xF9 0x20 0x1A
+ 0x2D 0x21 0x1E 0x2B 0x1E 0x1D 0x41 0xFE 0x00 0x16 0x32 0x28 0x2E 0xF9 0x25 0x1A
+ 0x32 0xF9 0x28 0x2E 0x2D 0xF9 0x32 0x28 0x2E 0x2B 0xF9 0x1C 0x1A 0x2C 0x1E 0xFE
+ 0x00 0x20 0x1A 0x27 0x1D 0xF9 0x29 0x2B 0x1E 0x29 0x1A 0x2B 0x1E 0xF9 0x2D 0x28
+ 0xF9 0x1C 0x1A 0x25 0x25 0xFE 0x00 0x2A 0x2D 0x21 0x1E 0xF9 0x29 0x28 0x25 0x22
+ 0x1C 0x1E 0x3E 0x3E 0x3E 0xFF
+: no-weapon-part-1 # $0 is murderer...
+ 0xFE 0x00 0x03 0xFB 0xFE 0x00 0x0D 0x1B 0x2E 0x2B 0x2C 0x2D 0x2C 0xF9 0x28 0x2E
+ 0x2D 0xF9 0x25 0x1A 0x2E 0x20 0x21 0x22 0x27 0x20 0x3E 0xFE 0x00 0x17 0xFE 0x00
+ 0x21 0x44 0x18 0x28 0x2E 0x2B 0xF9 0x1E 0x2F 0x22 0x1D 0x1E 0x27 0x1C 0x1E 0xF9
+ 0x22 0x2C 0xFE 0x00 0x2B 0xF9 0x27 0x28 0x2D 0x21 0x22 0x27 0x20 0xF9 0x1B 0x2E
+ 0x2D 0xF9 0x21 0x1E 0x1A 0x2B 0x2C 0x1A 0x32 0x3F 0xFE 0x00 0x35 0xF9 0x16 0x21
+ 0x1A 0x2D 0xF9 0x1A 0xF9 0x1F 0x28 0x28 0x25 0x3F 0x44 0xFF
+: no-weapon-part-2
+ 0xFE 0x00 0x03 0x16 0x22 0x2D 0x21 0x28 0x2E 0x2D 0xF9 0x1A 0xF9 0x30 0x1E 0x1A
+ 0x29 0x28 0x27 0x41 0xFE 0x00 0x0D 0x2D 0x21 0x1E 0xF9 0x29 0x28 0x25 0x22 0x1C
+ 0x1E 0xF9 0x1A 0x2B 0x1E 0x27 0x42 0x2D 0xFE 0x00 0x17 0x1C 0x28 0x27 0x2F 0x22
+ 0x27 0x1C 0x1E 0x1D 0x41 0xF9 0x1E 0x22 0x2D 0x21 0x1E 0x2B 0x3E 0xFE 0x00 0x21
+ 0xFE 0x00 0x2B 0x13 0x21 0x1E 0x32 0x42 0x2B 0x1E 0xF9 0x1F 0x28 0x2B 0x1C 0x1E
+ 0x1D 0xF9 0x2D 0x28 0xF9 0x25 0x1E 0x2D 0xFE 0x00 0x35 0x32 0x28 0x2E 0xF9 0x1A
+ 0x25 0x25 0xF9 0x20 0x28 0x41 0xF9 0x1F 0x28 0x2B 0xF9 0x27 0x28 0x30 0x3E 0xFF
+: killer-goes-free
+ 0xFE 0x02 0x0A 0x12 0x28 0x26 0x1E 0x30 0x21 0x1E 0x2B 0x1E 0x41 0xF9 0x1A 0xF9
+ 0x24 0x22 0x25 0x25 0x1E 0x2B 0xF9 0xF9 0xFE 0x02 0x14 0x30 0x1A 0x25 0x24 0x2C
+ 0xF9 0x1F 0x2B 0x1E 0x1E 0xF9 0x1A 0x26 0x28 0x27 0x20 0xF9 0x2E 0x2C 0x3E 0xF9
+ 0xF9 0xFE 0x23 0x28 0x13 0x07 0x04 0xF9 0x04 0x0D 0x03 0x3E 0xFF
+: wrong-culprit-part-1
+ 0xFE 0x00 0x10 0xFB 0xFE 0x00 0x1A 0x2C 0x29 0x2E 0x2D 0x2D 0x1E 0x2B 0x2C 0xF9
+ 0x22 0x27 0xF9 0x1D 0x22 0x2C 0x1B 0x1E 0x25 0x22 0x1E 0x1F 0xFE 0x00 0x24 0x1A
+ 0x2D 0xF9 0x32 0x28 0x2E 0x2B 0xF9 0x1A 0x1C 0x1C 0x2E 0x2C 0x1A 0x2D 0x22 0x28
+ 0x27 0x3F 0xFF
+: wrong-culprit-part-2
+ 0xFE 0x00 0x03 0x13 0x21 0x1E 0xF9 0x28 0x2D 0x21 0x1E 0x2B 0xF9 0x20 0x2E 0x1E
+ 0x2C 0x2D 0x2C 0xF9 0xFE 0x00 0x0D 0x26 0x2E 0x2B 0x26 0x2E 0x2B 0xF9 0x2D 0x28
+ 0xF9 0x28 0x27 0x1E 0xF9 0xFE 0x00 0x17 0x1A 0x27 0x28 0x2D 0x21 0x1E 0x2B 0xF9
+ 0x30 0x1A 0x2B 0x22 0x25 0x32 0x3E 0xF9 0xFE 0x00 0x2B 0x13 0x21 0x1E 0xF9 0x29
+ 0x28 0x25 0x22 0x1C 0x1E 0xF9 0xFE 0x00 0x35 0x2C 0x1E 0x1E 0x26 0xF9 0x1D 0x2E
+ 0x1B 0x22 0x28 0x2E 0x2C 0x3E 0xFF
+: wrong-culprit-part-3
+ 0xFE 0x00 0x03 0x08 0x27 0xF9 0x1C 0x28 0x2E 0x2B 0x2D 0x41 0xF9 0xFE 0x00 0x0D
+ 0xFB 0xF9 0x22 0x2C 0xF9 0xFE 0x00 0x17 0x1F 0x28 0x2E 0x27 0x1D 0xF9 0x22 0x27
+ 0x27 0x28 0x1C 0x1E 0x27 0x2D 0x3E 0xF9 0xFE 0x00 0x2B 0x18 0x28 0x2E 0xF9 0x26
+ 0x2E 0x2C 0x2D 0xF9 0x21 0x1A 0x2F 0x1E 0xF9 0x26 0x22 0x2C 0x2C 0x1E 0x1D 0xF9
+ 0xFE 0x00 0x35 0x2C 0x28 0x26 0x1E 0x2D 0x21 0x22 0x27 0x20 0x3E 0x3E 0x3E 0xFF
+: good-end-1
+ 0xFE 0x07 0x08 0xFB 0x42 0x2C 0xF9 0xFE 0x07 0x12 0x1F 0x1A 0x1C 0x1E 0xF9 0x1C
+ 0x28 0x27 0x2D 0x28 0x2B 0x2D 0x2C 0xF9 0x30 0x22 0x2D 0x21 0xF9 0xFE 0x07 0x1C
+ 0x2B 0x1A 0x20 0x1E 0x3E 0xF9 0xFE 0x07 0x30 0x44 0x07 0x28 0x30 0xF9 0x1D 0x22
+ 0x1D 0xF9 0x32 0x28 0x2E 0x3E 0x3E 0x3E 0x40 0x44 0xFF
+: good-end-2
+ 0xFE 0x05 0x10 0x13 0x21 0x1E 0xF9 0x29 0x28 0x25 0x22 0x1C 0x1E 0xF9 0x2C 0x28
+ 0x28 0x27 0xFE 0x05 0x1A 0x1A 0x2B 0x2B 0x22 0x2F 0x1E 0xF9 0x2D 0x28 0xF9 0x2D
+ 0x1A 0x24 0x1E 0xF9 0x2D 0x21 0x1E 0x26 0xFE 0x05 0x24 0x22 0x27 0x2D 0x28 0xF9
+ 0x1C 0x2E 0x2C 0x2D 0x28 0x1D 0x32 0x3E 0xFF
+: good-end-3
+ 0xFE 0x05 0x04 0x00 0x27 0x28 0x2D 0x21 0x1E 0x2B 0xF9 0x2C 0x2E 0x1C 0x1C 0x1E
+ 0x2C 0x2C 0x1F 0x2E 0x25 0xFE 0x05 0x0E 0x1C 0x1A 0x2C 0x1E 0x3E 0xF9 0x18 0x28
+ 0x2E 0xF9 0x1C 0x1A 0x27 0xF9 0x2B 0x1E 0x2C 0x2D 0xFE 0x05 0x18 0x1E 0x1A 0x2C
+ 0x32 0xF9 0x1A 0x2D 0xF9 0x27 0x22 0x20 0x21 0x2D 0x41 0xFE 0x05 0x22 0x1F 0x28
+ 0x2B 0xF9 0x27 0x28 0x30 0x3E 0x3E 0x3E 0xF9 0xFE 0x23 0x32 0x13 0x07 0x04 0xF9
+ 0x04 0x0D 0x03 0x3E 0xFF
+: murder-dialog-1 # "Well, if it isn't our friend the detective."
+ 0xFE 0x09 0x11 0x44 0x16 0x1E 0x25 0x25 0x41 0xF9 0x22 0x1F 0xF9 0x22 0x2D 0xF9
+ 0x22 0x2C 0x27 0x42 0x2D 0xFE 0x09 0x1B 0x28 0x2E 0x2B 0xF9 0x1F 0x2B 0x22 0x1E
+ 0x27 0x1D 0xF9 0x2D 0x21 0x1E 0xFE 0x09 0x25 0x1D 0x1E 0x2D 0x1E 0x1C 0x2D 0x22
+ 0x2F 0x1E 0x3E 0x44 0xFF
+: murder-dialog-2 # "I can't afford to risk this charade any longer. Game over, old chum."
+ 0xFE 0x06 0x08 0x44 0x08 0xF9 0x1C 0x1A 0x27 0x42 0x2D 0xF9 0x1A 0x1F 0x1F 0x28
+ 0x2B 0x1D 0xF9 0x2D 0x28 0xF9 0xF9 0xFE 0x06 0x12 0x2B 0x22 0x2C 0x24 0xF9 0x2D
+ 0x21 0x22 0x2C 0xF9 0x1C 0x21 0x1A 0x2B 0x1A 0x1D 0x1E 0xF9 0xF9 0xFE 0x06 0x1C
+ 0x1A 0x27 0x32 0xF9 0x25 0x28 0x27 0x20 0x1E 0x2B 0x3E 0xF9 0xFE 0x00 0x30 0xFD
+ 0x06 0x1A 0x26 0x1E 0xF9 0x28 0x2F 0x1E 0x2B 0x41 0xF9 0x28 0x25 0x1D 0xF9 0x1C
+ 0x21 0x2E 0x26 0x3E 0x44 0xFF
+: terrible-fate
+ 0xFE 0x0A 0x0E 0xFD 0x18 0x28 0x2E 0xF9 0x21 0x1A 0x2F 0x1E 0xF9 0x26 0x1E 0x2D
+ 0xF9 0x30 0x22 0x2D 0x21 0xFE 0x0A 0x18 0x1A 0xF9 0x2D 0x1E 0x2B 0x2B 0x22 0x1B
+ 0x25 0x1E 0xF9 0x1F 0x1A 0x2D 0x1E 0x3E 0xF9 0xF9 0xFE 0x23 0x32 0x13 0x07 0x04
+ 0xF9 0x04 0x0D 0x03 0x3E 0xFF
+
+: final-rank 0xFE 0x1D 0x0C 0x05 0x22 0x27 0x1A 0x25 0xF9 0x11 0x1A 0x27 0x24 0xFF
+
+: rank-0 0xFE 0x09 0x1E 0xFC 0x01 0x25 0x28 0x28 0x1D 0x32 0xF9 0x02 0x21 0x1E 0x1A 0x2D 0x1E 0x2B 0xFF
+: rank-1 0xFE 0x27 0x1E 0xFC 0x12 0x25 0x1E 0x2E 0x2D 0x21 0xFF
+: rank-2 0xFE 0x1B 0x1E 0xFC 0x03 0x1E 0x2D 0x1E 0x1C 0x2D 0x22 0x2F 0x1E 0xFF
+: rank-3 0xFE 0x23 0x1E 0xFC 0x06 0x2E 0x26 0x2C 0x21 0x28 0x1E 0xFF
+: rank-4 0xFE 0x1F 0x1E 0xFC 0x02 0x25 0x2E 0x1E 0x25 0x1E 0x2C 0x2C 0xFF
+
+: rank-thresholds
+ 20 60 90 150 255
+: rank-table
+ pointer rank-0
+ pointer rank-1
+ pointer rank-2
+ pointer rank-3
+ pointer rank-4
+
+to-code
+
+: murder-player
+ wait 60
+ i := long npc-murderer
+ load v2 - v2
+ introduce-npc
+
+ print murder-dialog-1
+ dialog-pause
+ print murder-dialog-2
+ dialog-pause
+
+ dialog-fill-screen
+ wait 60
+ print terrible-fate
+ jump show-rank
+
+: describe-body
+ clear
+ print gather-npcs # confirm gathering...
+ dialog-yes-no
+ if v2 == 0 then return
+
+ # accuse!
+ v2 := random 0b111
+ loop
+ print who-dunnit
+ i := long npc-portrait-table
+ portrait
+ v1 := 25
+ v0 := 40
+ i := long cycle-left
+ sprite v0 v1 9
+ v0 := 80
+ i := long cycle-right
+ sprite v0 v1 9
+ print-from-table v2 npc-title-table
+ vf := key
+ clear
+ if vf == OCTO_KEY_D then v2 += 1
+ if vf == OCTO_KEY_A then v2 += -1
+ v0 := 0b111
+ v2 &= v0
+ if vf != OCTO_KEY_E then
+ again
+ accused-npc := v2
+
+ # the truth, and nothing but the truth:
+ i := long npc-murderer
+ load guilty-npc - guilty-npc
+ i := long murder-weapon
+ load guilty-weapon - guilty-weapon
+
+ print make-your-case
+ dialog-pause
+
+ if has-weapon == 0 begin
+ fmt-from-table guilty-npc npc-name-table print-string0-read
+ print no-weapon-part-1
+ dialog-pause
+ print no-weapon-part-2
+ dialog-pause
+ print killer-goes-free
+ suspicion-level := 255
+ jump show-rank
+ end
+
+ fmt-from-table accused-npc npc-name-table print-string0-read
+
+ if guilty-npc != accused-npc begin
+ print wrong-culprit-part-1
+ dialog-pause
+ print wrong-culprit-part-2
+ dialog-pause
+ print wrong-culprit-part-3
+ dialog-pause
+ print killer-goes-free
+ :calc bad-score { @ rank-thresholds + 3 }
+ suspicion-level := bad-score
+ jump show-rank
+ end
+
+ print good-end-1
+ dialog-pause
+ jump fencing-minigame
+
+: show-rank
+ dialog-pause
+ v2 := 0
+ loop
+ i := long rank-thresholds
+ i += v2
+ load v0
+ while suspicion-level > v0
+ v2 += 1
+ again
+ print final-rank
+ print-from-table v2 rank-table
+ dialog-pause
+ current-room := GAME_OVER
+;
+
+###########################################
+#
+# NPCs
+#
+###########################################
+
+to-data
+
+# 8x15 overworld sprites padded to 8x16
+: npc-sprites
+: npc-0 0x3E 0x7C 0x76 0x66 0x7C 0x38 0x5C 0x5C 0x5C 0x64 0x7E 0x7E 0x7E 0x28 0x3C 0x00
+: npc-1 0x78 0xFC 0x4C 0x58 0x78 0x7E 0x3E 0x3E 0x7A 0x78 0x3E 0x3E 0x36 0x36 0x7E 0x00
+: npc-2 0x3C 0x3C 0x7A 0x24 0x1C 0x3E 0x7E 0x7E 0x7D 0x7D 0x34 0x34 0x38 0x38 0x1E 0x00
+: npc-3 0x38 0x7C 0x72 0x24 0x34 0x7E 0x62 0x7E 0x3E 0x7F 0x77 0x36 0x36 0x3A 0x1D 0x00
+: npc-4 0x78 0x58 0x4C 0x4C 0x7E 0x6E 0x3F 0x3F 0x1D 0x3F 0x3F 0x3F 0x3F 0x3F 0x12 0x00
+: npc-5 0x7C 0x3A 0x34 0x64 0x24 0x3C 0x42 0x6A 0x5A 0x7E 0x7E 0x7E 0x7E 0x34 0x34 0x00
+: npc-6 0x38 0x7C 0x44 0x6C 0x6C 0x7C 0x38 0x7C 0xFE 0xFE 0xFE 0x6C 0x28 0x28 0x6C 0x00
+: npc-7 0x18 0x3C 0x24 0x7E 0xC3 0xEF 0xEF 0xEF 0xAF 0xC3 0xFD 0x7E 0x3C 0x3C 0x7E 0x00
+
+# 16x16 portraits
+: portrait-0
+ 0x0F 0xFE 0x1F 0xBF 0x3F 0x9C 0x7F 0x0C 0x7F 0xDE 0x7E 0x06 0xFD 0x9E 0xFC 0x8E
+ 0xF8 0x06 0xF8 0x26 0xD8 0x06 0x5C 0xF7 0x3C 0x0E 0x19 0xF8 0x2D 0xDE 0x76 0x3F
+: portrait-1
+ 0x1F 0x7C 0x7F 0xF8 0xFF 0xF8 0xF0 0xDC 0xB0 0x1E 0x39 0xDE 0x39 0xFC 0x3C 0x96
+ 0x1C 0x16 0x2C 0x36 0x2E 0x7E 0x30 0x7E 0x3F 0x7C 0x1C 0xF6 0x3F 0xEF 0x6F 0xFF
+: portrait-2
+ 0x0F 0xF0 0x38 0x7C 0x30 0x1E 0x30 0x1E 0x30 0x1E 0x3F 0x1E 0xFF 0xEE 0xFF 0xFE
+ 0x37 0xBD 0x1F 0xF9 0x1F 0x19 0x1B 0x06 0x18 0x06 0x0F 0x8C 0x0E 0x3E 0x1F 0xFE
+: portrait-3
+ 0x1F 0xE0 0x3F 0xF0 0x7E 0x08 0x78 0x76 0x78 0xE1 0x3C 0x61 0x66 0x1F 0x47 0x0E
+ 0x41 0x86 0x2C 0x1C 0x3E 0x3C 0x1F 0x8C 0x33 0xCC 0x7C 0xF8 0xFE 0x34 0xFF 0x8E
+: portrait-4
+ 0x3F 0xC0 0x7F 0xF0 0x7F 0xF8 0xF6 0xEC 0xE3 0x74 0xF6 0x74 0xF7 0xB6 0xD2 0x3A
+ 0xC0 0x1A 0xD8 0x5B 0xCC 0x7D 0xE0 0x5D 0xEE 0xDF 0xF1 0xFE 0x7F 0xBE 0x78 0x7A
+: portrait-5
+ 0x3F 0xC0 0xFF 0xF8 0x7F 0xE8 0x3F 0x34 0x34 0x2C 0x3B 0xA8 0x1B 0xDC 0x62 0x38
+ 0x40 0x7C 0x78 0x3C 0x18 0x7C 0x17 0x7E 0x17 0xFF 0x0F 0xDF 0x1C 0x3F 0x3B 0xCF
+: portrait-6
+ 0x1F 0xF8 0x1F 0xF4 0x2F 0xFA 0x77 0xFE 0x70 0xFF 0xF0 0x3F 0xFE 0xFF 0xD4 0x57
+ 0xD1 0x17 0xF9 0x3F 0xF9 0x3F 0xE8 0x2F 0x7B 0xBE 0x38 0x3C 0x4F 0xF2 0xFB 0x9F
+: portrait-7
+ 0x1F 0xF0 0x2F 0xE8 0x23 0x8C 0x5C 0x74 0x54 0x66 0x48 0x4A 0x6F 0xD6 0x47 0x8E
+ 0x2F 0xEC 0x30 0x1F 0x7F 0xFB 0x5F 0xF3 0xCF 0xCF 0xE0 0x1D 0xBC 0xFB 0xFC 0xFF
+
+: npc-title-0 # "Mme. Genta"
+ 0xFE 0x17 0x2D 0xFC 0x0C 0x26 0x1E 0x3E 0xF9 0x06 0x1E 0x27 0x2D 0x1A 0xFF
+: npc-title-1 # "Mr. Carmine"
+ 0xFE 0x14 0x2D 0xFC 0x0C 0x2B 0x3E 0xF9 0x02 0x1A 0x2B 0x26 0x22 0x27 0x1E 0xFF
+: npc-title-2 # "Sir Feldgrau"
+ 0xFE 0x11 0x2D 0xFC 0x12 0x22 0x2B 0xF9 0x05 0x1E 0x25 0x1D 0x20 0x2B 0x1A 0x2E 0xFF
+: npc-title-3 # "Prof. Puce"
+ 0xFE 0x16 0x2D 0xFC 0x0F 0x2B 0x28 0x1F 0x3E 0xF9 0x0F 0x2E 0x1C 0x1E 0xFF
+: npc-title-4 # "Lady Veridian"
+ 0xFE 0x0D 0x2D 0xFC 0x0B 0x1A 0x1D 0x32 0xF9 0x15 0x1E 0x2B 0x22 0x1D 0x22 0x1A 0x27 0xFF
+: npc-title-5 # "Cmdr. Coriander" (longest)
+ 0xFE 0x05 0x2D 0xFC 0x02 0x26 0x1D 0x2B 0x3E 0xF9 0x02 0x28 0x2B 0x22 0x1A 0x27 0x1D 0x1E 0x2B 0xFF
+: npc-title-6 # "Dr. Coquelicot"
+ 0xFE 0x08 0x2D 0xFC 0x03 0x2B 0x3E 0xF9 0x02 0x28 0x2A 0x2E 0x1E 0x25 0x22 0x1C 0x28 0x2D 0xFF
+: npc-title-7 # "Lord Zaffre"
+ 0xFE 0x11 0x2D 0xFC 0x0B 0x28 0x2B 0x1D 0xF9 0xF9 0x19 0x1A 0x1F 0x1F 0x2B 0x1E 0xFF
+
+: npc-name-0 0x0C 0x26 0x1E 0x3E 0xF9 0x06 0x1E 0x27 0x2D 0x1A 0xFF
+: npc-name-1 0x0C 0x2B 0x3E 0xF9 0x02 0x1A 0x2B 0x26 0x22 0x27 0x1E 0xFF
+: npc-name-2 0x12 0x22 0x2B 0xF9 0x05 0x1E 0x25 0x1D 0x20 0x2B 0x1A 0x2E 0xFF
+: npc-name-3 0x0F 0x2B 0x28 0x1F 0x3E 0xF9 0x0F 0x2E 0x1C 0x1E 0xFF
+: npc-name-4 0x0B 0x1A 0x1D 0x32 0xF9 0x15 0x1E 0x2B 0x22 0x1D 0x22 0x1A 0x27 0xFF
+: npc-name-5 0x02 0x26 0x1D 0x2B 0x3E 0xF9 0x02 0x28 0x2B 0x22 0x1A 0x27 0x1D 0x1E 0x2B 0xFF
+: npc-name-6 0x03 0x2B 0x3E 0xF9 0x02 0x28 0x2A 0x2E 0x1E 0x25 0x22 0x1C 0x28 0x2D 0xFF
+: npc-name-7 0x0B 0x28 0x2B 0x1D 0xF9 0xF9 0x19 0x1A 0x1F 0x1F 0x2B 0x1E 0xFF
+
+: npc-scoff-0 # "Still snooping about, I see? What was I doing?"
+ 0xFE 0x00 0x0D 0x44 0x12 0x2D 0x22 0x25 0x25 0xF9 0x2C 0x27 0x28 0x28 0x29 0x22
+ 0x27 0x20 0xF9 0x1A 0x1B 0x28 0x2E 0x2D 0xF9 0xFE 0x00 0x17 0x08 0xF9 0x2C 0x1E
+ 0x1E 0x40 0xF9 0x06 0x28 0x28 0x1D 0xF9 0x25 0x2E 0x1C 0x24 0x3E 0xF9 0xF9 0xFE
+ 0x00 0x2B 0x3E 0x3E 0x3E 0x16 0x21 0x1A 0x2D 0xF9 0x30 0x1A 0x2C 0xF9 0x08 0xF9
+ 0x1D 0x28 0x22 0x27 0x20 0x40 0x44 0xFF
+: npc-scoff-1 # "I suppose you want to know my wereabouts this evening?"
+ 0xFE 0x00 0x0F 0x44 0x08 0xF9 0x2C 0x2E 0x29 0x29 0x28 0x2C 0x1E 0xF9 0x32 0x28
+ 0x2E 0xF9 0x30 0x1A 0x27 0x2D 0xF9 0xFE 0x00 0x19 0x2D 0x28 0xF9 0x24 0x27 0x28
+ 0x30 0xF9 0x26 0x32 0xF9 0x30 0x1E 0x2B 0x1E 0x1A 0x1B 0x28 0x2E 0x2D 0x2C 0xF9
+ 0xFE 0x00 0x23 0x2D 0x21 0x22 0x2C 0xF9 0x1E 0x2F 0x1E 0x27 0x22 0x27 0x20 0x40
+ 0x44 0xFF
+: npc-scoff-2 # "Oh, come now. You can't believe I was responsible for this?"
+ 0xFE 0x04 0x0C 0x44 0x0E 0x21 0x41 0xF9 0x1C 0x28 0x26 0x1E 0xF9 0x27 0x28 0x30
+ 0x3F 0xF9 0xFE 0x04 0x16 0x18 0x28 0x2E 0xF9 0x1C 0x1A 0x27 0x42 0x2D 0xF9 0x1B
+ 0x1E 0x25 0x22 0x1E 0x2F 0x1E 0xF9 0x08 0xF9 0xFE 0x04 0x20 0x30 0x1A 0x2C 0xF9
+ 0x2B 0x1E 0x2C 0x29 0x28 0x27 0x2C 0x22 0x1B 0x25 0x1E 0xF9 0x1F 0x28 0x2B 0xF9
+ 0xFE 0x04 0x2A 0x1A 0x27 0x32 0xF9 0x28 0x1F 0xF9 0x2D 0x21 0x22 0x2C 0x40 0x44
+ 0xFF
+: npc-scoff-3 # "I'll have you know I was quite occupied during this vile business."
+ 0xFE 0x01 0x0C 0x44 0x08 0x42 0x25 0x25 0xF9 0x21 0x1A 0x2F 0x1E 0xF9 0x32 0x28
+ 0x2E 0xF9 0x24 0x27 0x28 0x30 0x41 0xF9 0xFE 0x01 0x16 0x08 0xF9 0x30 0x1A 0x2C
+ 0xF9 0x2A 0x2E 0x22 0x2D 0x1E 0xF9 0x28 0x1C 0x1C 0x2E 0x29 0x22 0x1E 0x1D 0xF9
+ 0xFE 0x01 0x20 0x1D 0x2E 0x2B 0x22 0x27 0x20 0xF9 0x1A 0x25 0x25 0xF9 0x28 0x1F
+ 0xF9 0x2D 0x21 0x22 0x2C 0xF9 0xFE 0x01 0x2A 0x2F 0x22 0x25 0x1E 0xF9 0x1B 0x2E
+ 0x2C 0x22 0x27 0x1E 0x2C 0x2C 0x3F 0x44 0xFF
+: npc-scoff-4 # "Regarding tonight, I assure you I have nothing to hide."
+ 0xFE 0x05 0x11 0x44 0x11 0x1E 0x20 0x1A 0x2B 0x1D 0x22 0x27 0x20 0xF9 0x2D 0x28
+ 0x27 0x22 0x20 0x21 0x2D 0x41 0xF9 0xFE 0x05 0x1B 0x08 0xF9 0x1A 0x2C 0x2C 0x2E
+ 0x2B 0x1E 0xF9 0x32 0x28 0x2E 0xF9 0x08 0xF9 0x21 0x1A 0x2F 0x1E 0xF9 0xFE 0x05
+ 0x25 0x27 0x28 0x2D 0x21 0x22 0x27 0x20 0xF9 0x2D 0x28 0xF9 0x21 0x22 0x1D 0x1E
+ 0x3F 0x44 0xFF
+: npc-scoff-5 # "Where was I all evening? Hrm. Fine, I'll tell you."
+ 0xFE 0x00 0x0A 0x44 0x16 0x21 0x1E 0x2B 0x1E 0xF9 0x30 0x1A 0x2C 0xF9 0x08 0xF9
+ 0x1A 0x25 0x25 0xF9 0xFE 0x00 0x14 0x1E 0x2F 0x1E 0x27 0x22 0x27 0x20 0x40 0xF9
+ 0x07 0x2E 0x26 0x29 0x21 0x3E 0xF9 0xFE 0x00 0x28 0x05 0x22 0x27 0x1E 0x41 0xF9
+ 0x08 0x42 0x25 0x25 0xF9 0x2D 0x1E 0x25 0x25 0xF9 0x32 0x28 0x2E 0x3E 0x44 0xFF
+: npc-scoff-6 # "No beating around the bush, then? I think I know what you want to hear."
+ 0xFE 0x04 0x0A 0x44 0x0D 0x28 0xF9 0x1B 0x1E 0x1A 0x2D 0x22 0x27 0x20 0xF9 0x1A
+ 0x2B 0x28 0x2E 0x27 0x1D 0xFE 0x04 0x14 0x2D 0x21 0x1E 0xF9 0x1B 0x2E 0x2C 0x21
+ 0x41 0xF9 0x2D 0x21 0x1E 0x27 0x40 0xFE 0x04 0x1E 0x08 0xF9 0x2D 0x21 0x22 0x27
+ 0x24 0xF9 0x08 0xF9 0x24 0x27 0x28 0x30 0xF9 0x30 0x21 0x1A 0x2D 0xFE 0x04 0x28
+ 0x32 0x28 0x2E 0xF9 0x30 0x1A 0x27 0x2D 0xF9 0x2D 0x28 0xF9 0x21 0x1E 0x1A 0x2B
+ 0x3E 0x44 0xFF
+: npc-scoff-7 # "I dearly hope you can solve this, old chum. As for myself, well..."
+ 0xFE 0x08 0x03 0x44 0x08 0xF9 0x1D 0x1E 0x1A 0x2B 0x25 0x32 0xF9 0x21 0x28 0x29
+ 0x1E 0xF9 0x32 0x28 0x2E 0xF9 0xFE 0x08 0x0D 0x1C 0x1A 0x27 0xF9 0x2C 0x28 0x25
+ 0x2F 0x1E 0xF9 0x2D 0x21 0x22 0x2C 0x41 0xF9 0xFE 0x08 0x17 0x28 0x25 0x1D 0xF9
+ 0x1C 0x21 0x2E 0x26 0x3E 0xF9 0xFE 0x08 0x2B 0x00 0x2C 0xF9 0x1F 0x28 0x2B 0xF9
+ 0x26 0x32 0x2C 0x1E 0x25 0x1F 0x41 0xF9 0xFE 0x08 0x35 0x30 0x1E 0x25 0x25 0x3E
+ 0x3E 0x3E 0x44 0xFF
+: npc-scoff-8 # "Hm? Did you want something? Oh, of course, of course."
+ 0xFE 0x0D 0x08 0x44 0x07 0x26 0x40 0xF9 0x03 0x22 0x1D 0xF9 0x32 0x28 0x2E 0xF9
+ 0x30 0x1A 0x27 0x2D 0xFE 0x0D 0x12 0x2C 0x28 0x26 0x1E 0x2D 0x21 0x22 0x27 0x20
+ 0x40 0xFE 0x0D 0x1C 0xFE 0x0D 0x26 0x0E 0x21 0x41 0xF9 0x28 0x1F 0xF9 0x1C 0x28
+ 0x2E 0x2B 0x2C 0x1E 0x41 0xFE 0x0D 0x30 0x28 0x1F 0xF9 0x1C 0x28 0x2E 0x2B 0x2C
+ 0x1E 0x3E 0x44 0xFF
+
+: room-name-0 0x25 0x22 0x1B 0x2B 0x1A 0x2B 0x32 0xFF
+: room-name-1 0x1B 0x1A 0x2D 0x21 0x2B 0x28 0x28 0x26 0xFF
+: room-name-2 0x1B 0x1E 0x1D 0x2B 0x28 0x28 0x26 0xFF
+: room-name-3 0x1D 0x2B 0x1A 0x30 0x22 0x27 0x20 0xF9 0x2B 0x28 0x28 0x26 0xFF
+: room-name-4 0x29 0x1A 0x2B 0x25 0x28 0x2B 0xFF
+: room-name-5 0x1D 0x22 0x27 0x22 0x27 0x20 0xF9 0x2B 0x28 0x28 0x26 0xFF
+: room-name-6 0x2C 0x2D 0x2E 0x1D 0x32 0xFF
+: room-name-7 0x1C 0x28 0x27 0x2C 0x1E 0x2B 0x2F 0x1A 0x2D 0x28 0x2B 0x32 0xFF
+: room-name-8 0x24 0x22 0x2D 0x1C 0x21 0x1E 0x27 0xFF
+: room-name-9 0x29 0x1A 0x27 0x2D 0x2B 0x32 0xFF
+
+: npc-was-alone # $1 is location.
+ 0xFE 0x0E 0x10 0x44 0x08 0xF9 0x30 0x1A 0x2C 0xF9 0x1A 0x25 0x28 0x27 0x1E 0xF9 0x1A
+ 0x25 0x25 0xF9 0xFE 0x0E 0x1A 0x1E 0x2F 0x1E 0x27 0x22 0x27 0x20 0x41 0xF9 0x22 0x27
+ 0xF9 0x2D 0x21 0x1E 0xF9 0xFE 0x0E 0x24 0xFA 0x3E 0x44 0xFF
+
+: npc-was-with-in # $0 is partner, $1 is location.
+ 0xFE 0x00 0x11 0x44 0x08 0xF9 0x30 0x1A 0x2C 0xF9 0x30 0x22 0x2D 0x21 0xF9 0xFE 0x00
+ 0x1B 0xFB 0x41 0xF9 0xFE 0x00 0x25 0x22 0x27 0xF9 0x2D 0x21 0x1E 0xF9 0xFA 0x3E 0x44
+ 0xFF
+
+: npc-portrait-table
+ pointer portrait-0
+ pointer portrait-1
+ pointer portrait-2
+ pointer portrait-3
+ pointer portrait-4
+ pointer portrait-5
+ pointer portrait-6
+ pointer portrait-7
+
+: npc-title-table
+ pointer npc-title-0
+ pointer npc-title-1
+ pointer npc-title-2
+ pointer npc-title-3
+ pointer npc-title-4
+ pointer npc-title-5
+ pointer npc-title-6
+ pointer npc-title-7
+
+: npc-name-table
+ pointer npc-name-0
+ pointer npc-name-1
+ pointer npc-name-2
+ pointer npc-name-3
+ pointer npc-name-4
+ pointer npc-name-5
+ pointer npc-name-6
+ pointer npc-name-7
+
+: npc-scoff-table
+ pointer npc-scoff-0
+ pointer npc-scoff-1
+ pointer npc-scoff-2
+ pointer npc-scoff-3
+ pointer npc-scoff-4
+ pointer npc-scoff-5
+ pointer npc-scoff-6
+ pointer npc-scoff-7
+
+: room-name-table
+ pointer room-name-0
+ pointer room-name-1
+ pointer room-name-2
+ pointer room-name-3
+ pointer room-name-4
+ pointer room-name-5
+ pointer room-name-6
+ pointer room-name-7
+ pointer room-name-8
+ pointer room-name-9
+
+to-code
+
+: introduce-npc
+ # (pass the npc index in v2...)
+ clear
+ i := long npc-portrait-table
+ portrait
+ print-from-table v2 npc-title-table
+ dialog-pause
+;
+
+: describe-npc
+ thicken-plot SUSPICION_TALK_NPC
+ introduce-npc
+
+ i := long npc-scoff-index
+ i += v2
+ load v0
+ print-from-table v0 npc-scoff-table
+ dialog-pause
+
+ # find alibi slot w/ our npc
+ v0 := 0
+ loop
+ i := long npc-alibis
+ i += v0
+ load v1 - v1
+ while v1 != v2
+ v0 += 1
+ again
+
+ if v0 == 6 begin
+ # lone NPC
+ i := long npc-alibi-r4
+ load v0
+ fmt-from-table v0 room-name-table print-string1-read
+ print npc-was-alone
+ jump dialog-pause
+ end
+
+ if v0 == 7 begin
+ thicken-plot SUSPICION_TALK_MURDERER
+ # murderer
+ i := long npc-alibi-r5
+ load v0
+ fmt-from-table v0 room-name-table print-string1-read
+ i := long npc-alibis
+ load v0
+ else
+ # normal alibi
+ v1 := v0
+ vf := 0b110
+ v1 &= vf # v1 is now the base [0,2,4] alibi slot index.
+ i := long npc-alibi-rooms
+ v1 >>= v1 # v1 is now an alibi room index
+ i += v1
+ load v1 - v1 # v1 is now the alibi room for the current slot.
+ fmt-from-table v1 room-name-table print-string1-read
+
+ i := long npc-alibis
+ vf := 1
+ v0 ^= vf # v0 is now the slot index of the partner NPC.
+ i += v0
+ load v0 # v0 is now the id of the partner NPC.
+ end
+ fmt-from-table v0 npc-name-table print-string0-read
+ print npc-was-with-in
+ jump dialog-pause
+
+###########################################
+#
+# Decorations and Passages
+#
+###########################################
+
+to-data
+
+: d1-library # "Countless volumes on the history of the Orient. Fascinating."
+ 0xFE 0x0A 0x08 0x02 0x28 0x2E 0x27 0x2D 0x25 0x1E 0x2C 0x2C 0xF9 0x2F 0x28 0x25
+ 0x2E 0x26 0x1E 0x2C 0xF9 0xFE 0x0A 0x12 0x28 0x27 0xF9 0x2D 0x21 0x1E 0xF9 0x21
+ 0x22 0x2C 0x2D 0x28 0x2B 0x32 0xF9 0x28 0x1F 0xF9 0xFE 0x0A 0x1C 0x2D 0x21 0x1E
+ 0xF9 0x0E 0x2B 0x22 0x1E 0x27 0x2D 0x3E 0xF9 0xFE 0x0A 0x2E 0x05 0x1A 0x2C 0x1C
+ 0x22 0x27 0x1A 0x2D 0x22 0x27 0x20 0x3E 0xFF
+: d2-library # "Philosophy, Logic, Game theory... Mosaic tiling? How Curious."
+ 0xFE 0x0A 0x08 0x0F 0x21 0x22 0x25 0x28 0x2C 0x28 0x29 0x21 0x32 0x41 0xF9 0x0B
+ 0x28 0x20 0x22 0x1C 0x41 0xF9 0xFE 0x0A 0x12 0x06 0x1A 0x26 0x1E 0xF9 0x2D 0x21
+ 0x1E 0x28 0x2B 0x32 0x3E 0x3E 0x3E 0xF9 0xFE 0x0A 0x1C 0x0C 0x28 0x2C 0x1A 0x22
+ 0x1C 0xF9 0x2D 0x22 0x25 0x22 0x27 0x20 0x40 0xF9 0xFE 0x0A 0x2E 0x07 0x28 0x30
+ 0xF9 0x02 0x2E 0x2B 0x22 0x28 0x2E 0x2C 0x3E 0xFF
+
+: d1-bathroom # "Spare towels, dirty clothes. Nothing terribly out of the ordinary."
+ 0xFE 0x04 0x0C 0x12 0x29 0x1A 0x2B 0x1E 0xF9 0x2D 0x28 0x30 0x1E 0x25 0x2C 0x41
+ 0xFE 0x04 0x16 0x1D 0x22 0x2B 0x2D 0x32 0xF9 0x1C 0x25 0x28 0x2D 0x21 0x1E 0x2C
+ 0x3E 0xFE 0x04 0x20 0x0D 0x28 0x2D 0x21 0x22 0x27 0x20 0xF9 0x2D 0x1E 0x2B 0x2B
+ 0x22 0x1B 0x25 0x32 0xFE 0x04 0x2A 0x28 0x2E 0x2D 0xF9 0x28 0x1F 0xF9 0x2D 0x21
+ 0x1E 0xF9 0x28 0x2B 0x1D 0x22 0x27 0x1A 0x2B 0x32 0x3E 0xFF
+: d2-bathroom # "The bathmat feels damp to the touch. Someone has showered here recently."
+ 0xFE 0x02 0x0C 0x13 0x21 0x1E 0xF9 0x1B 0x1A 0x2D 0x21 0x26 0x1A 0x2D 0xF9 0x1F
+ 0x1E 0x1E 0x25 0x2C 0xFE 0x02 0x16 0x1D 0x1A 0x26 0x29 0xF9 0x2D 0x28 0xF9 0x2D
+ 0x21 0x1E 0xF9 0x2D 0x28 0x2E 0x1C 0x21 0x3E 0xFE 0x02 0x20 0x12 0x28 0x26 0x1E
+ 0x28 0x27 0x1E 0xF9 0x21 0x1A 0x2C 0xF9 0x2C 0x21 0x28 0x30 0x1E 0x2B 0x1E 0x1D
+ 0xFE 0x02 0x2A 0x21 0x1E 0x2B 0x1E 0xF9 0x2B 0x1E 0x1C 0x1E 0x27 0x2D 0x25 0x32
+ 0x3E 0x3E 0x3E 0xFF
+
+: d1-bedroom # "The covers are warm to the touch. How scandalous!"
+ 0xFE 0x02 0x0A 0x13 0x21 0x1E 0xF9 0x1C 0x28 0x2F 0x1E 0x2B 0x2C 0xF9 0x1A 0x2B
+ 0x1E 0xF9 0x2C 0x2D 0x22 0x25 0x25 0xF9 0xFE 0x02 0x14 0x30 0x1A 0x2B 0x26 0xF9
+ 0x2D 0x28 0xF9 0x2D 0x21 0x1E 0xF9 0x2D 0x28 0x2E 0x1C 0x21 0x3E 0xF9 0xFE 0x02
+ 0x23 0x08 0x2D 0xF9 0x2C 0x1E 0x1E 0x26 0x2C 0xF9 0x2D 0x21 0x1E 0xF9 0x20 0x2E
+ 0x1E 0x2C 0x2D 0x2C 0xF9 0xFE 0x02 0x2D 0x21 0x1A 0x2F 0x1E 0xF9 0x1B 0x1E 0x1E
+ 0x27 0x3E 0x3E 0x3E 0xF9 0x1B 0x2E 0x2C 0x32 0x3E 0x3E 0x3E 0xFF
+: d2-bedroom # "On the desk is an open decanter of a particularly peaty scotch."
+ 0xFE 0x05 0x08 0x0E 0x27 0xF9 0x2D 0x21 0x1E 0xF9 0x1D 0x1E 0x2C 0x24 0xF9 0x22
+ 0x2C 0xF9 0x1A 0x27 0xFE 0x05 0x12 0x28 0x29 0x1E 0x27 0xF9 0x1D 0x1E 0x1C 0x1A
+ 0x27 0x2D 0x1E 0x2B 0xF9 0x28 0x1F 0xF9 0x1A 0xFE 0x05 0x1C 0x29 0x1A 0x2B 0x2D
+ 0x22 0x1C 0x2E 0x25 0x1A 0x2B 0x25 0x32 0xF9 0x29 0x1E 0x1A 0x2D 0x32 0xFE 0x05
+ 0x26 0x2C 0x1C 0x28 0x2D 0x1C 0x21 0x3E 0xF9 0x03 0x28 0x27 0x42 0x2D 0xF9 0x26
+ 0x22 0x27 0x1D 0xFE 0x05 0x30 0x22 0x1F 0xF9 0x08 0xF9 0x1D 0x28 0x3E 0x3E 0x3E
+ 0xFF
+
+: d1-drawing-room # "A crude sketch of a naked woman. The eyes have been carefully cut out. Most unsettling."
+ 0xFE 0x06 0x02 0x00 0xF9 0x1C 0x2B 0x2E 0x1D 0x1E 0xF9 0x2C 0x24 0x1E 0x2D 0x1C
+ 0x21 0xF9 0x28 0x1F 0xF9 0x1A 0xF9 0xFE 0x06 0x0C 0x27 0x1A 0x24 0x1E 0x1D 0xF9
+ 0x30 0x28 0x26 0x1A 0x27 0x3E 0xF9 0xFE 0x06 0x16 0x13 0x21 0x1E 0xF9 0x1E 0x32
+ 0x1E 0x2C 0xF9 0x21 0x1A 0x2F 0x1E 0xF9 0x1B 0x1E 0x1E 0x27 0xF9 0xFE 0x06 0x20
+ 0x1C 0x1A 0x2B 0x1E 0x1F 0x2E 0x25 0x25 0x32 0xF9 0x1C 0x2E 0x2D 0xF9 0x28 0x2E
+ 0x2D 0x3E 0xF9 0xFE 0x06 0x34 0x0C 0x28 0x2C 0x2D 0xF9 0x2E 0x27 0x2C 0x1E 0x2D
+ 0x2D 0x25 0x22 0x27 0x20 0x3E 0xFF
+: d2-drawing-room # "A sketch of a featureless black cube. Quite modern."
+ 0xFE 0x16 0x06 0x00 0xF9 0x2C 0x24 0x1E 0x2D 0x1C 0x21 0xF9 0x28 0x1F 0xF9 0x1A
+ 0xF9 0xFE 0x16 0x10 0x1F 0x1E 0x1A 0x2D 0x2E 0x2B 0x1E 0x25 0x1E 0x2C 0x2C 0xF9
+ 0xFE 0x16 0x1A 0x1B 0x25 0x1A 0x1C 0x24 0xF9 0x1C 0x2E 0x1B 0x1E 0x3E 0xF9 0xFE
+ 0x16 0x2E 0x10 0x2E 0x22 0x2D 0x1E 0xF9 0x26 0x28 0x1D 0x1E 0x2B 0x27 0x3E 0xFF
+
+# editor's note: yes, I know that a "drawing room" is not typically
+# dedicated to literal drawings and sketches. It's a joke!
+
+: d1-parlor # "A half-written party invitation. The recipient is hastily scratched out."
+ 0xFE 0x00 0x04 0x00 0xF9 0x21 0x1A 0x25 0x1F 0x43 0x30 0x2B 0x22 0x2D 0x2D 0x1E
+ 0x27 0xF9 0x29 0x1A 0x2B 0x2D 0x32 0xF9 0xF9 0xFE 0x00 0x0E 0x22 0x27 0x2F 0x22
+ 0x2D 0x1A 0x2D 0x22 0x28 0x27 0x3E 0xF9 0xF9 0xFE 0x00 0x1C 0x13 0x21 0x1E 0xF9
+ 0x2B 0x1E 0x1C 0x22 0x29 0x22 0x1E 0x27 0x2D 0x42 0x2C 0xF9 0xF9 0xFE 0x00 0x26
+ 0x27 0x1A 0x26 0x1E 0xF9 0x21 0x1A 0x2C 0xF9 0x1B 0x1E 0x1E 0x27 0xF9 0x21 0x1A
+ 0x2C 0x2D 0x22 0x25 0x32 0xF9 0xF9 0xFE 0x00 0x30 0x2C 0x1C 0x2B 0x1A 0x2D 0x1C
+ 0x21 0x1E 0x1D 0xF9 0x28 0x2E 0x2D 0x3E 0x3E 0x3E 0xFF
+: d2-parlor # "Well-worn armchairs of the finest mink leather. A tad ostentatious."
+ 0xFE 0x05 0x0C 0x16 0x1E 0x25 0x25 0x43 0x30 0x28 0x2B 0x27 0xF9 0x1A 0x2B 0x26
+ 0x1C 0x21 0x1A 0x22 0x2B 0x2C 0xFE 0x05 0x16 0x28 0x1F 0xF9 0x2D 0x21 0x1E 0xF9
+ 0x1F 0x22 0x27 0x1E 0x2C 0x2D 0xF9 0x26 0x22 0x27 0x24 0xFE 0x05 0x20 0x25 0x1E
+ 0x1A 0x2D 0x21 0x1E 0x2B 0x3E 0xF9 0x00 0xF9 0x2D 0x1A 0x1D 0xFE 0x05 0x2A 0x28
+ 0x2C 0x2D 0x1E 0x27 0x2D 0x1A 0x2D 0x22 0x28 0x2E 0x2C 0x41 0xF9 0x27 0x28 0x40
+ 0xFF
+
+: d1-dining-room # "What remains of dinner. I daresay I've lost my appetite."
+ 0xFE 0x04 0x08 0x16 0x21 0x1A 0x2D 0xF9 0x2B 0x1E 0x26 0x1A 0x22 0x27 0x2C 0xF9
+ 0xF9 0xFE 0x04 0x12 0x28 0x1F 0xF9 0x1D 0x22 0x27 0x27 0x1E 0x2B 0x3E 0xF9 0xF9
+ 0xFE 0x04 0x26 0x08 0xF9 0x1D 0x1A 0x2B 0x1E 0x2C 0x1A 0x32 0xF9 0x08 0x42 0x2F
+ 0x1E 0xF9 0x25 0x28 0x2C 0x2D 0xF9 0xF9 0xFE 0x04 0x30 0x26 0x32 0xF9 0x1A 0x29
+ 0x29 0x1E 0x2D 0x22 0x2D 0x1E 0x3E 0xFF
+: d2-dining-room # "A sterling silver tea set. Someone seems to have made off with the sugar."
+ 0xFE 0x08 0x02 0x00 0xF9 0x2C 0x2D 0x1E 0x2B 0x25 0x22 0x27 0x20 0xF9 0x2C 0x22
+ 0x25 0x2F 0x1E 0x2B 0xF9 0xFE 0x08 0x0C 0x2D 0x1E 0x1A 0xF9 0x2C 0x1E 0x2D 0x3E
+ 0xF9 0xFE 0x08 0x20 0x12 0x28 0x26 0x1E 0x28 0x27 0x1E 0xF9 0x2C 0x1E 0x1E 0x26
+ 0x2C 0xF9 0x2D 0x28 0xF9 0xFE 0x08 0x2A 0x21 0x1A 0x2F 0x1E 0xF9 0x26 0x1A 0x1D
+ 0x1E 0xF9 0x28 0x1F 0x1F 0xF9 0x30 0x22 0x2D 0x21 0xF9 0xFE 0x08 0x34 0x2D 0x21
+ 0x1E 0xF9 0x2C 0x2E 0x20 0x1A 0x2B 0x3E 0xFF
+
+: d1-study # "A leatherbound volume describing ancient torture techniques. Ghastly stuff."
+ 0xFE 0x00 0x0A 0x00 0xF9 0x25 0x1E 0x1A 0x2D 0x21 0x1E 0x2B 0x1B 0x28 0x2E 0x27
+ 0x1D 0xF9 0x2F 0x28 0x25 0x2E 0x26 0x1E 0xFE 0x00 0x14 0x1D 0x1E 0x2C 0x1C 0x2B
+ 0x22 0x1B 0x22 0x27 0x20 0xF9 0x1A 0x27 0x1C 0x22 0x1E 0x27 0x2D 0xFE 0x00 0x1E
+ 0x2D 0x28 0x2B 0x2D 0x2E 0x2B 0x1E 0xF9 0x2D 0x1E 0x1C 0x21 0x27 0x22 0x2A 0x2E
+ 0x1E 0x2C 0x3E 0xFE 0x00 0x28 0x06 0x21 0x1A 0x2C 0x2D 0x25 0x32 0xF9 0x2C 0x2D
+ 0x2E 0x1F 0x1F 0x3E 0xFF
+: d2-study # "Our host's beloved collection of mystery novels. Darkly ironic, given tonight's festivities."
+ 0xFE 0x00 0x03 0x0E 0x2E 0x2B 0xF9 0x21 0x28 0x2C 0x2D 0x42 0x2C 0xF9 0x1B 0x1E
+ 0x25 0x28 0x2F 0x1E 0x1D 0xFE 0x00 0x0D 0x1C 0x28 0x25 0x25 0x1E 0x1C 0x2D 0x22
+ 0x28 0x27 0xF9 0x28 0x1F 0xF9 0x26 0x32 0x2C 0x2D 0x1E 0x2B 0x32 0xFE 0x00 0x17
+ 0x27 0x28 0x2F 0x1E 0x25 0x2C 0x3E 0xFE 0x00 0x21 0x03 0x1A 0x2B 0x24 0x25 0x32
+ 0xF9 0x22 0x2B 0x28 0x27 0x22 0x1C 0x41 0xF9 0x20 0x22 0x2F 0x1E 0x27 0xFE 0x00
+ 0x2B 0x2D 0x21 0x1E 0xF9 0x1E 0x2F 0x1E 0x27 0x22 0x27 0x20 0x42 0x2C 0xFE 0x00
+ 0x35 0x44 0x1F 0x1E 0x2C 0x2D 0x22 0x2F 0x22 0x2D 0x22 0x1E 0x2C 0x44 0x3E 0xFF
+
+: d1-conservatory # "Rain patters against the dark windowpanes."
+ 0xFE 0x00 0x07 0x11 0x1A 0x22 0x27 0xF9 0x29 0x1A 0x2D 0x2D 0x1E 0x2B 0x2C 0xF9
+ 0x1A 0x20 0x1A 0x22 0x27 0x2C 0x2D 0xF9 0xFE 0x00 0x11 0x2D 0x21 0x1E 0xF9 0x1D
+ 0x1A 0x2B 0x24 0xF9 0x30 0x22 0x27 0x1D 0x28 0x30 0x29 0x1A 0x27 0x1E 0x2C 0x3E
+ 0xF9 0xFE 0x00 0x25 0x08 0x2D 0x42 0x2C 0xF9 0x2C 0x2D 0x22 0x25 0x25 0xF9 0x2C
+ 0x2D 0x28 0x2B 0x26 0x22 0x27 0x20 0xF9 0xFE 0x00 0x2F 0x21 0x1A 0x2B 0x1D 0xF9
+ 0x28 0x2E 0x2D 0x2C 0x22 0x1D 0x1E 0x3E 0xFF
+: d2-conservatory # "The loose soil in this pot suggests recent excavation."
+ 0xFE 0x03 0x11 0x13 0x21 0x1E 0xF9 0x25 0x28 0x28 0x2C 0x1E 0xF9 0x2C 0x28 0x22
+ 0x25 0xF9 0x22 0x27 0xFE 0x03 0x1B 0x2D 0x21 0x22 0x2C 0xF9 0x29 0x28 0x2D 0xF9
+ 0x2C 0x2E 0x20 0x20 0x1E 0x2C 0x2D 0x2C 0xFE 0x03 0x25 0x2B 0x1E 0x1C 0x1E 0x27
+ 0x2D 0xF9 0x1E 0x31 0x1C 0x1A 0x2F 0x1A 0x2D 0x22 0x28 0x27 0x3E 0x3E 0x3E 0xFF
+
+: d1-kitchen # "A slew of recipe books. The master of the house was quite the gourmand."
+ 0xFE 0x03 0x0C 0x00 0xF9 0x2C 0x25 0x1E 0x30 0xF9 0x28 0x1F 0xF9 0x2B 0x1E 0x1C
+ 0x22 0x29 0x1E 0xFE 0x03 0x16 0x1B 0x28 0x28 0x24 0x2C 0x3E 0xF9 0x13 0x21 0x1E
+ 0xF9 0x26 0x1A 0x2C 0x2D 0x1E 0x2B 0xF9 0x28 0x1F 0xFE 0x03 0x20 0x2D 0x21 0x1E
+ 0xF9 0x21 0x28 0x2E 0x2C 0x1E 0xF9 0x30 0x1A 0x2C 0xF9 0x2A 0x2E 0x22 0x2D 0x1E
+ 0xFE 0x03 0x2A 0x2D 0x21 0x1E 0xF9 0x20 0x28 0x2E 0x2B 0x26 0x1A 0x27 0x1D 0x3E
+ 0x3E 0x3E 0xFF
+: d2-kitchen # "The washbasin's faucet has a slow drip, which echoes through the room."
+ 0xFE 0x06 0x0C 0x13 0x21 0x1E 0xF9 0x30 0x1A 0x2C 0x21 0x1B 0x1A 0x2C 0x22 0x27
+ 0x42 0x2C 0xFE 0x06 0x16 0x1F 0x1A 0x2E 0x1C 0x1E 0x2D 0xF9 0x21 0x1A 0x2C 0xF9
+ 0x1A 0xF9 0x2C 0x25 0x28 0x30 0xFE 0x06 0x20 0x1D 0x2B 0x22 0x29 0x41 0xF9 0x30
+ 0x21 0x22 0x1C 0x21 0xF9 0x1E 0x1C 0x21 0x28 0x1E 0x2C 0xFE 0x06 0x2A 0x2D 0x21
+ 0x2B 0x28 0x2E 0x20 0x21 0xF9 0x2D 0x21 0x1E 0xF9 0x2B 0x28 0x28 0x26 0x3E 0xFF
+
+: d1-pantry # "There are enough tinned provisions on these shelves to feed a small army."
+ 0xFE 0x02 0x0C 0x13 0x21 0x1E 0x2B 0x1E 0xF9 0x1A 0x2B 0x1E 0xF9 0x1E 0x27 0x28
+ 0x2E 0x20 0x21 0xFE 0x02 0x16 0x2D 0x22 0x27 0x27 0x1E 0x1D 0xF9 0x29 0x2B 0x28
+ 0x2F 0x22 0x2C 0x22 0x28 0x27 0x2C 0xF9 0x28 0x27 0xFE 0x02 0x20 0x2D 0x21 0x1E
+ 0x2C 0x1E 0xF9 0x2C 0x21 0x1E 0x25 0x2F 0x1E 0x2C 0xF9 0x2D 0x28 0xFE 0x02 0x2A
+ 0x1F 0x1E 0x1E 0x1D 0xF9 0x1A 0xF9 0x2C 0x26 0x1A 0x25 0x25 0xF9 0x1A 0x2B 0x26
+ 0x32 0x3E 0xFF
+: d2-pantry # "Judging by color and odor, whatever is in these jars should stay there."
+ 0xFE 0x08 0x07 0x09 0x2E 0x1D 0x20 0x22 0x27 0x20 0xF9 0x1B 0x32 0xF9 0x1C 0x28
+ 0x25 0x28 0x2B 0xF9 0xFE 0x08 0x11 0x1A 0x27 0x1D 0xF9 0x28 0x1D 0x28 0x2B 0x41
+ 0xF9 0x30 0x21 0x1A 0x2D 0x1E 0x2F 0x1E 0x2B 0xF9 0xFE 0x08 0x1B 0x22 0x2C 0xF9
+ 0x22 0x27 0xF9 0x2D 0x21 0x1E 0x2C 0x1E 0xF9 0x23 0x1A 0x2B 0x2C 0xF9 0xFE 0x08
+ 0x25 0x2C 0x21 0x28 0x2E 0x25 0x1D 0xF9 0x2C 0x2D 0x1A 0x32 0xF9 0x2D 0x21 0x1E
+ 0x2B 0x1E 0xF9 0xFE 0x08 0x2F 0x22 0x27 0x1D 0x1E 0x1F 0x22 0x27 0x22 0x2D 0x1E
+ 0x25 0x32 0x3E 0xFF
+
+: d1-secret # "Cobwebbed boxes full of knick-knacks and bric-a-brac."
+ 0xFE 0x00 0x07 0x02 0x28 0x1B 0x30 0x1E 0x1B 0x1B 0x1E 0x1D 0xF9 0x1B 0x28 0x31
+ 0x1E 0x2C 0xF9 0x1F 0x2E 0x25 0x25 0xFE 0x00 0x11 0x28 0x1F 0xF9 0x24 0x27 0x22
+ 0x1C 0x24 0x43 0x24 0x27 0x1A 0x1C 0x24 0x2C 0x41 0xF9 0xFE 0x00 0x1B 0x1C 0x2E
+ 0x2B 0x22 0x28 0x2C 0x41 0xF9 0x28 0x1B 0x23 0x1E 0x2D 0xF9 0x1D 0x42 0x1A 0x2B
+ 0x2D 0x41 0xFE 0x00 0x25 0x1B 0x1A 0x2E 0x1B 0x25 0x1E 0x2C 0x41 0xF9 0x1C 0x2E
+ 0x2B 0x22 0x28 0x2C 0x22 0x2D 0x22 0x1E 0x2C 0x41 0xFE 0x00 0x2F 0x1A 0x27 0x1D
+ 0xF9 0x1B 0x2B 0x22 0x1C 0x43 0x1A 0x43 0x1B 0x2B 0x1A 0x1C 0x3E 0xFF
+: d2-secret # "Scrapes in the dust suggest these boxes have been moved recently."
+ 0xFE 0x04 0x0C 0x12 0x1C 0x2B 0x1A 0x29 0x1E 0x2C 0xF9 0x22 0x27 0xF9 0x2D 0x21
+ 0x1E 0xF9 0x1D 0x2E 0x2C 0x2D 0xFE 0x04 0x16 0x2C 0x2E 0x20 0x20 0x1E 0x2C 0x2D
+ 0xF9 0x2D 0x21 0x1E 0x2C 0x1E 0xF9 0x1B 0x28 0x31 0x1E 0x2C 0xFE 0x04 0x20 0x21
+ 0x1A 0x2F 0x1E 0xF9 0x1B 0x1E 0x1E 0x27 0xF9 0x26 0x28 0x2F 0x1E 0x1D 0xFE 0x04
+ 0x2A 0x2B 0x1E 0x1C 0x1E 0x27 0x2D 0x25 0x32 0x3E 0x3E 0x3E 0xFF
+
+: secret-passage-entrance
+ 0xFE 0x04 0x02 0x16 0x21 0x1A 0x2D 0x42 0x2C 0xF9 0x2D 0x21 0x22 0x2C 0x40 0xFE
+ 0x04 0x0C 0x00 0xF9 0x1C 0x28 0x27 0x1C 0x1E 0x1A 0x25 0x1E 0x1D 0xF9 0x1D 0x28
+ 0x28 0x2B 0xF9 0x22 0x2C 0xFE 0x04 0x16 0x2C 0x25 0x22 0x20 0x21 0x2D 0x25 0x32
+ 0xF9 0x1A 0x23 0x1A 0x2B 0x3F 0xFE 0x04 0x20 0x12 0x2D 0x1E 0x29 0xF9 0x22 0x27
+ 0x2C 0x22 0x1D 0x1E 0x40 0xFF
+: secret-room-entrance
+ 0xFE 0x00 0x02 0x04 0x2E 0x2B 0x1E 0x24 0x1A 0x3F 0xF9 0x00 0xF9 0x2D 0x2B 0x1A
+ 0x29 0xF9 0x1D 0x28 0x28 0x2B 0xFE 0x00 0x0C 0x22 0x2C 0xF9 0x21 0x22 0x1D 0x1D
+ 0x1E 0x27 0xF9 0x22 0x27 0xF9 0x2D 0x21 0x1E 0xFE 0x00 0x16 0x1F 0x25 0x28 0x28
+ 0x2B 0xF9 0x21 0x1E 0x2B 0x1E 0x3F 0xFE 0x00 0x20 0x12 0x21 0x28 0x2E 0x25 0x1D
+ 0xF9 0x08 0xF9 0x22 0x27 0x2F 0x1E 0x2C 0x2D 0x22 0x20 0x1A 0x2D 0x1E 0x40 0xFF
+: how-did-i-get-here
+ 0xFE 0x08 0x14 0x07 0x28 0x30 0xF9 0x28 0x27 0xF9 0x1E 0x1A 0x2B 0x2D 0x21 0xFE
+ 0x08 0x1E 0x1D 0x22 0x1D 0xF9 0x08 0xF9 0x1E 0x27 0x1D 0xF9 0x2E 0x29 0xF9 0x21
+ 0x1E 0x2B 0x1E 0x40 0xFF
+
+: room-deco-table
+ pointer d1-library
+ pointer d2-library
+ pointer d1-bathroom
+ pointer d2-bathroom
+ pointer d1-bedroom
+ pointer d2-bedroom
+ pointer d1-drawing-room
+ pointer d2-drawing-room
+ pointer d1-parlor
+ pointer d2-parlor
+ pointer d1-dining-room
+ pointer d2-dining-room
+ pointer d1-study
+ pointer d2-study
+ pointer d1-conservatory
+ pointer d2-conservatory
+ pointer d1-kitchen
+ pointer d2-kitchen
+ pointer d1-pantry
+ pointer d2-pantry
+ pointer d1-secret
+ pointer d2-secret
+
+: secret-room-player-pos-stash
+ 0x00 0x00
+
+to-code
+
+: describe-deco
+ # pass the decoration index {0,1} in v2.
+ thicken-plot SUSPICION_EXAMINE_DECO
+ clear
+ if v2 == 0 begin
+ i := long has-passage-room
+ load v0
+ if v0 == current-room begin
+ print secret-passage-entrance
+ dialog-yes-no
+ if v2 == 0 then return
+ i := long has-body-room
+ load v0
+ current-room := v0
+
+ # gotta unpack the player's
+ # start position for the new room:
+ dialog-fill-screen
+ plane 0
+ draw-room
+ i := long room-stash-start
+ load player-x - player-y
+ plane 1
+
+ wait 60
+ clear
+ print how-did-i-get-here
+ dialog-pause
+ jump draw-room-title
+ end
+ else
+ i := long has-secret-room
+ load v0
+ if v0 == current-room begin
+ print secret-room-entrance
+ dialog-yes-no
+ if v2 == 0 then return
+ i := long secret-room-player-pos-stash
+ save player-x - player-y
+ current-room := 10
+ room
+ i := long has-secret-room
+ load v0
+ i := long secret-room-player-pos-stash
+ load player-x - player-y
+ current-room := v0
+ jump draw-room-title
+ end
+ end
+ i := long room-deco-table
+ v0 := current-room
+ v0 <<= v0
+ v0 <<= v0
+ v0 += v2
+ v0 += v2
+ i += v0
+ load v1
+ print-text
+ jump dialog-pause
+
+###########################################
+#
+# Rooms
+#
+###########################################
+
+:const TILE_EMPTY 0x00
+:const TILE_SOLID 0x08
+:const TILE_EXIT_UP 0xA0
+:const TILE_EXIT_RT 0xA8
+:const TILE_EXIT_DN 0xB0
+:const TILE_EXIT_LF 0xB8
+:const TILE_NPC_1 0xC0
+:const TILE_NPC_2 0xC8
+:const TILE_DECO_1 0xD0
+:const TILE_DECO_2 0xD8
+:const TILE_START 0xE0
+:const TILE_BODY 0xE8
+:const TILE_ITEM 0xF0
+:const TILE_QUESTION 0xF8
+
+to-data
+
+: player-left-0 0x38 0x7C 0x3C 0x3C 0x1E 0x3C 0x3C 0x1C 0x18 0x3C 0x3C 0x3C 0x1C 0x08 0x08
+: player-left-1 0x00 0x38 0x7C 0x3C 0x3C 0x1E 0x3C 0x3C 0x1C 0x38 0x3C 0x3C 0x1E 0x24 0x24
+: player-right-0 mirror-8x15 player-left-0
+: player-right-1 mirror-8x15 player-left-1
+
+: tileset # 8x8 tiles
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0x95 0x95 0x95 0xFF 0x51 0x51 0xFF 0xFF
+ 0x00 0x7E 0x00 0x7A 0x7A 0x7E 0x00 0x00
+ 0xD5 0x00 0xDD 0x10 0xC0 0x10 0xC0 0x00
+ 0x55 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x56 0x00 0x06 0x00 0x06 0x00 0x06 0x00
+ 0x00 0x44 0x50 0x14 0x42 0x54 0x3C 0x08
+ 0xC0 0x00 0xC0 0x00 0xC0 0x00 0xD5 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x55 0x00
+ 0x06 0x00 0x06 0x10 0x36 0x00 0x56 0x00
+ 0xFF 0xFF 0x81 0x00 0x00 0x81 0xFF 0xDB
+ 0xFF 0xFF 0x80 0xBF 0xA0 0xA0 0xE0 0x00
+ 0xFF 0xFF 0x01 0xFD 0x05 0x05 0x07 0x00
+ 0xE0 0xE0 0xE0 0xE0 0xFE 0xFE 0xFE 0x00
+ 0x07 0x07 0x07 0x07 0x7F 0x7F 0x7F 0x00
+ 0x7E 0x7E 0x42 0x42 0x42 0x00 0x00 0x00
+ 0xFF 0xFF 0x00 0xFF 0x00 0x00 0x00 0x00
+ 0xFF 0x83 0xBB 0xB9 0xB9 0x81 0xE1 0xFF
+ 0x40 0x08 0x00 0x88 0x02 0x00 0x10 0x00
+ 0x00 0x18 0x3C 0x7E 0x18 0x18 0x18 0x00
+ 0x00 0x08 0x0C 0x7E 0x7E 0x0C 0x08 0x00
+ 0x00 0x18 0x18 0x18 0x7E 0x3C 0x18 0x00
+ 0x00 0x10 0x30 0x7E 0x7E 0x30 0x10 0x00
+ 0x00 0x74 0x54 0x54 0x74 0x44 0x44 0x00
+ 0x00 0x76 0x52 0x56 0x74 0x44 0x46 0x00
+ 0x00 0x64 0x54 0x54 0x54 0x54 0x64 0x00
+ 0x00 0x66 0x52 0x56 0x54 0x54 0x66 0x00
+ 0x00 0x3E 0x40 0x7E 0x02 0x02 0x7C 0x00
+ 0x00 0x7C 0x42 0x7C 0x42 0x42 0x7C 0x00
+ 0x00 0x7E 0x08 0x08 0x08 0x08 0x7E 0x00
+ 0x3C 0x7E 0x66 0x0E 0x1C 0x18 0x00 0x18
+
+: map-library
+ 0x08 0x38 0x00 0x00 0x00 0x00 0x98 0x00
+ 0x08 0x00 0x00 0x98 0x00 0x38 0x00 0x00
+ 0x08 0x38 0x00 0x00 0x00 0x00 0x98 0x00
+ 0x08 0x38 0x38 0x38 0x00 0x38 0x38 0x38
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x10 0x00 0x00 0x00 0x00 0x10 0x08
+ 0x08 0x10 0xF0 0x00 0x00 0xD0 0x10 0x08
+ 0x08 0x10 0x00 0x00 0x00 0x00 0x10 0x08
+ 0x08 0xC8 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x10 0x00 0x00 0x00 0x00 0x10 0x08
+ 0x08 0x10 0xE8 0x00 0x00 0xD8 0x10 0x08
+ 0x08 0x10 0x00 0x00 0x00 0x00 0x10 0x08
+ 0x08 0x10 0x00 0x00 0xE0 0x00 0x10 0x08
+ 0x08 0xC0 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x08 0x08 0x08 0xA8 0x08 0x08
+
+: map-bathroom
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x98 0x98 0x98 0x98 0x98 0x20 0x08
+ 0x08 0x98 0x98 0x98 0x98 0x98 0x30 0x08
+ 0x08 0x00 0xD8 0x20 0x40 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x30 0x50 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0xC0 0x00 0x08
+ 0x08 0x08 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x58 0xF0 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x00 0x00 0x00 0xE8 0x00 0x08
+ 0x08 0xC8 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0xE0 0x00 0x00 0xD0 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x18 0x08
+ 0x08 0x08 0x08 0xA8 0x08 0x08 0x08 0x08
+
+: map-bedroom
+ 0x08 0x08 0x08 0xB8 0x08 0x08 0x08 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x70 0x80 0x08
+ 0x08 0xC0 0x00 0x00 0x00 0xF0 0x00 0x08
+ 0x08 0x90 0x60 0xD8 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x68 0x00 0x00 0xC8 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0xE0 0x00 0xB0
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0xD0 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x20 0x20 0x60 0x00 0x00 0x08
+ 0x08 0x08 0x28 0x28 0x88 0x00 0x00 0x08
+ 0x08 0x08 0x50 0x30 0x68 0xE8 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+
+: map-drawing-room
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x00 0x70 0x80 0x00 0xE8 0x00 0x08
+ 0x08 0xC0 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x60 0x00 0x00 0x00 0x90 0x08
+ 0x08 0x90 0x88 0xD0 0x00 0x00 0x08 0x08
+ 0x08 0x08 0x68 0x00 0x00 0x00 0x08 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0xE0 0x00 0xB0
+ 0x08 0x90 0x60 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x90 0x88 0xF0 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x68 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0xD8 0x00 0xC8 0x00 0x08
+ 0x08 0x00 0x08 0x90 0x60 0x78 0x80 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+
+: map-parlor
+ 0x00 0x38 0x00 0x00 0x00 0x98 0x00 0x38
+ 0x00 0x00 0x00 0x00 0x38 0x00 0x00 0x00
+ 0x00 0x98 0x00 0x00 0x00 0x00 0x00 0x98
+ 0x38 0x38 0x38 0x00 0x98 0x38 0x38 0x38
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x70 0x80 0xC0 0x00 0x00 0x00 0x08
+ 0x08 0xD8 0x00 0x00 0x00 0x00 0xD0 0x08
+ 0x08 0x78 0x80 0x00 0x00 0x90 0x60 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x08 0x68 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0xF0 0x08
+ 0x08 0x00 0xE8 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0xE0 0x00 0x00 0xC8 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0xA8 0x08 0x08 0x08 0x08 0x08
+
+: map-dining-room
+ 0x08 0xB8 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x70 0x80 0x08
+ 0x08 0x00 0xE0 0x00 0x00 0x00 0xF0 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0xC8 0x00 0xD0 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x70 0x70 0x70 0x80 0x00 0x08
+ 0x08 0x00 0x58 0x08 0x08 0x60 0x00 0x08
+ 0x08 0x00 0x08 0x08 0x58 0x68 0x00 0x08
+ 0x08 0x00 0x78 0x78 0x78 0x80 0x00 0x08
+ 0x08 0x00 0x00 0xC0 0x00 0x00 0x00 0x08
+ 0x08 0xE8 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0xD8 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x08 0x58 0x60 0x00 0x00 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0xA8 0x08
+
+: map-study
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x10 0x00 0x10 0x10 0x10 0x10 0x08
+ 0x08 0x10 0xC0 0x00 0xD0 0x00 0x00 0x08
+ 0x08 0x10 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x10 0x00 0x00 0x70 0x80 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0xA0 0x00 0x00 0xE0 0x00 0x00 0x20 0x08
+ 0x08 0xE8 0x00 0x00 0x00 0xF0 0x30 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x10 0x00 0x00 0x78 0x80 0x00 0x08
+ 0x08 0x10 0xD8 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x10 0x00 0x00 0x00 0xC8 0x00 0x08
+ 0x08 0x10 0x00 0x10 0x10 0x10 0x10 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+
+: map-conservatory
+ 0x08 0x08 0x08 0x08 0xB8 0x08 0x08 0x08
+ 0x08 0xC0 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0xE0 0x00 0x00 0x08
+ 0x88 0x00 0x38 0x18 0x00 0x38 0x18 0x08
+ 0x88 0x00 0x38 0x18 0x00 0x00 0x00 0x08
+ 0x88 0xD0 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x88 0x00 0x00 0x00 0x00 0x38 0x18 0x08
+ 0x08 0x00 0x38 0x18 0x00 0xD8 0x18 0x08
+ 0x08 0x00 0x38 0x18 0x00 0x00 0x00 0x08
+ 0x88 0xE8 0x00 0x00 0x00 0x38 0x18 0x08
+ 0x88 0x00 0x00 0x00 0x00 0x38 0x18 0x08
+ 0x88 0x00 0x00 0x98 0x00 0x00 0x00 0x08
+ 0x88 0x00 0xF0 0x18 0x00 0xC8 0x00 0x08
+ 0x08 0x00 0x38 0x18 0x00 0x38 0x18 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+
+: map-kitchen
+ 0x08 0x08 0x08 0x08 0xB8 0x08 0x08 0x08
+ 0x08 0x10 0x00 0x00 0x00 0x00 0x10 0x08
+ 0x08 0x10 0xD0 0x00 0xE0 0x00 0x10 0x08
+ 0x08 0x08 0x60 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x88 0x00 0x00 0xE8 0x00 0x08
+ 0x08 0x08 0x88 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x68 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0xC0 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x60 0x00 0x00 0xC8 0x00 0x08
+ 0x08 0x58 0x88 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x58 0x88 0xD8 0x00 0x00 0x00 0xB0
+ 0x08 0x08 0x68 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x10 0x08 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x10 0x08 0x00 0x00 0x00 0x38 0x08
+ 0x08 0x10 0x10 0x60 0x00 0xF0 0x38 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+
+: map-pantry
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x00 0x00 0xC0 0x00 0x00 0x18 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0xF0 0x08
+ 0x08 0x18 0x18 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x90 0x08 0x80 0x00 0x08 0x90 0x08
+ 0x08 0x00 0xD0 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x08 0x80 0x00 0x00 0x08 0x08 0x08
+ 0x08 0x00 0x00 0x00 0x00 0xC8 0x00 0x08
+ 0xA0 0x00 0xE0 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0xE8 0x00 0x08 0x80 0xD8 0x18 0x08
+ 0x08 0x00 0x00 0x08 0x80 0x00 0x18 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+
+: map-secret
+ # note: no NPCs or body!
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x18 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x18 0xD0 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x98 0x00 0x00 0x00 0xE0 0x00 0xB0
+ 0x08 0x18 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x98 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x98 0x08
+ 0x08 0x98 0xF0 0x00 0x00 0xD8 0x18 0x08
+ 0x08 0x98 0x98 0x00 0x98 0x18 0x18 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08
+
+: map-dining-room-alt
+ # no NPCs or special tiles except start.
+ 0x08 0xB8 0x08 0x08 0x08 0x08 0x08 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x70 0x80 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0xE0 0x00 0x08
+ 0x08 0x00 0x70 0x70 0x70 0x80 0x00 0x08
+ 0x08 0x00 0x08 0x08 0x08 0x60 0x00 0x08
+ 0x08 0x00 0x08 0x08 0x08 0x68 0x00 0x08
+ 0x08 0x00 0x78 0x78 0x78 0x80 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x08
+ 0x08 0x00 0x08 0x58 0x60 0x00 0x00 0x08
+ 0x08 0x08 0x08 0x08 0x08 0x08 0xA8 0x08
+
+: room-map-table
+ pointer map-library
+ pointer map-bathroom
+ pointer map-bedroom
+ pointer map-drawing-room
+ pointer map-parlor
+ pointer map-dining-room
+ pointer map-study
+ pointer map-conservatory
+ pointer map-kitchen
+ pointer map-pantry
+ pointer map-secret
+ pointer map-dining-room-alt
+
+: title-library 0xFE 0x24 0x18 0xFC 0x0B 0x22 0x1B 0x2B 0x1A 0x2B 0x32 0xFF
+: title-bathroom 0xFE 0x21 0x18 0xFC 0x01 0x1A 0x2D 0x21 0x2B 0x28 0x28 0x26 0xFF
+: title-bedroom 0xFE 0x25 0x18 0xFC 0x01 0x1E 0x1D 0x2B 0x28 0x28 0x26 0xFF
+: title-drawing-room 0xFE 0x12 0x18 0xFC 0x03 0x2B 0x1A 0x30 0x22 0x27 0x20 0xF9 0x11 0x28 0x28 0x26 0xFF
+: title-parlor 0xFE 0x29 0x18 0xFC 0x0F 0x1A 0x2B 0x25 0x28 0x2B 0xFF
+: title-dining-room 0xFE 0x15 0x18 0xFC 0x03 0x22 0x27 0x22 0x27 0x20 0xF9 0x11 0x28 0x28 0x26 0xFF
+: title-study 0xFE 0x2B 0x18 0xFC 0x12 0x2D 0x2E 0x1D 0x32 0xFF
+: title-conservatory 0xFE 0x11 0x18 0xFC 0x02 0x28 0x27 0x2C 0x1E 0x2B 0x2F 0x1A 0x2D 0x28 0x2B 0x32 0xFF
+: title-kitchen 0xFE 0x25 0x18 0xFC 0x0A 0x22 0x2D 0x1C 0x21 0x1E 0x27 0xFF
+: title-pantry 0xFE 0x28 0x18 0xFC 0x0F 0x1A 0x27 0x2D 0x2B 0x32 0xFF
+: title-secret-room 0xFE 0x14 0x18 0xFC 0x12 0x1E 0x1C 0x2B 0x1E 0x2D 0xF9 0x11 0x28 0x28 0x26 0xFF
+: title-secret-passage 0xFE 0x08 0x18 0xFC 0x12 0x1E 0x1C 0x2B 0x1E 0x2D 0xF9 0x0F 0x1A 0x2C 0x2C 0x1A 0x20 0x1E 0xFF
+
+: room-title-table
+ pointer title-library
+ pointer title-bathroom
+ pointer title-bedroom
+ pointer title-drawing-room
+ pointer title-parlor
+ pointer title-dining-room
+ pointer title-study
+ pointer title-conservatory
+ pointer title-kitchen
+ pointer title-pantry
+ pointer title-secret-room
+ pointer title-secret-passage
+
+: special-room-exits
+ # -1 just dumps you to the main map
+ # UP, RT, DN, LF to match TILE_EXIT indices
+ -1 -1 -1 -1 # 0 library
+ -1 2 -1 -1 # 1 bathroom
+ -1 -1 -1 1 # 2 bedroom
+ -1 -1 -1 -1 # 3 drawing-room
+ -1 -1 -1 -1 # 4 parlor
+ -1 -1 -1 -1 # 5 dining-room 5
+ -1 -1 -1 -1 # 6 study
+ -1 -1 -1 -1 # 7 conservatory
+ -1 -1 9 -1 # 8 kitchen
+ 8 -1 -1 -1 # 9 pantry
+ -1 -1 -1 -1 # 10 secret-room
+
+: room-stash
+ 0 0 # exit-N
+ 0 0 # exit E
+ 0 0 # exit S
+ 0 0 # exit W
+: room-stash-points-of-interest
+ 0 0 # npc 1
+ 0 0 # npc 2
+ 0 0 # deco 1
+ 0 0 # deco 2
+: room-stash-start 0 0 # start
+ 0 0 # body
+ 0 0 # item
+
+: blink-masks
+: blink-npc 0x7E 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x7E
+: blink-item 0x92 0x44 0x00 0x82 0x00 0x44 0x92 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+: blink-body 0x00 0x70 0x78 0x38 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+: blink-table 0 0 15 15 -1 30 15
+
+to-code
+
+:macro abs-diff A B RESULT {
+ if A < B begin
+ RESULT := B
+ RESULT -= A
+ else
+ RESULT := A
+ RESULT -= B
+ end
+}
+
+: room-poi-find
+ # grab coords of the current object:
+ i := long room-stash-points-of-interest
+ i += v4
+ i += v4
+ load v1
+
+ # calculate ABS of manhattan distance to object:
+ prev-x := player-x
+ prev-y := player-y
+ if v4 >= 2 begin
+ # adjust player hitbox to the feet
+ # for anything but NPC positions:
+ prev-x += 4
+ prev-y += 8
+ end
+ abs-diff v0 prev-x v2
+ abs-diff v1 prev-y v3
+ v3 += v2
+
+ # ignore NPCs that aren't in the room.
+ i := long npc-positions
+ i += current-room
+ i += current-room
+ i += v4
+ load v2 - v2
+ if v4 == 0 begin
+ if v2 == -1 then v3 := 0xFF
+ end
+ if v4 == 1 begin
+ if v2 == -1 then v3 := 0xFF
+ end
+
+ # ignore body if it's not in this room.
+ if v4 == 5 begin
+ i := long has-body-room
+ load v2 - v2
+ if v2 != current-room then v3 := 0xFF
+ end
+
+ # ignore item if it's not here.
+ if v4 == 6 begin
+ i := long item-positions
+ i += current-room
+ load v2 - v2
+ if v2 == -1 then v3 := 0xFF
+ end
+;
+
+: room-poi-walk
+ # first pass: if objects are close enough,
+ # just activate them directly.
+ v4 := 0
+ loop
+ room-poi-find
+
+ if v3 < 10 begin
+ if v4 == 0 begin
+ i := long npc-positions
+ : room-npc
+ i += current-room
+ i += current-room
+ load v2 - v2
+ describe-npc
+ jump poi-done
+ end
+ if v4 == 1 begin
+ :calc npc-slot-2 { npc-positions + 1 }
+ i := long npc-slot-2
+ jump room-npc
+ end
+ if v4 == 2 begin v2 := 0 describe-deco jump poi-done end
+ if v4 == 3 begin v2 := 1 describe-deco jump poi-done end
+ if v4 == 5 begin describe-body jump poi-done end
+ if v4 == 6 begin
+ i := long item-positions
+ i += current-room
+ load v2 - v2
+ describe-item
+ end
+ : poi-done
+ if current-room == GAME_OVER then return
+ draw-room
+ ve := 0
+ jump draw-player
+ end
+
+ v4 += 1
+ if v4 == 4 then v4 += 1 # skip over start position
+ if v4 != 7 then
+ again
+
+ # second pass: if objects are near-ish,
+ # blink them.
+ v4 := 0
+ loop
+ room-poi-find
+
+ # if the object is further, blink it.
+ if v3 < 40 begin
+ i := long blink-table
+ i += v4
+ load v2 - v2
+ i := long blink-masks
+ i += v2
+ v2 := 6
+ loop
+ sprite v0 v1 15
+ wait 5
+ v2 += -1
+ if v2 != 0 then
+ again
+ end
+
+ v4 += 1
+ if v4 == 4 then v4 += 1 # skip over start position
+ if v4 != 7 then
+ again
+;
+
+: room-load indirect room-write ;
+
+: room-draw-npc
+ i += current-room
+ i += current-room
+ load v4 - v4
+ if v4 == -1 then return # no npc here
+ i := long npc-sprites
+ v4 <<= v4 # * 2
+ v4 <<= v4 # * 4
+ v4 <<= v4 # * 8
+ v4 <<= v4 # * 16
+ i += v4
+ sprite v1 v2 15
+;
+
+: room-unpack
+ if v0 == TILE_BODY begin
+ i := long has-body-room
+ load v4 - v4
+ if v4 == current-room begin
+ i := long body-sprite
+ sprite v1 v2 0
+ end
+ end
+ if v0 == TILE_NPC_1 begin
+ i := long npc-positions
+ room-draw-npc
+ end
+ if v0 == TILE_NPC_2 begin
+ :calc second-slot { npc-positions + 1 }
+ i := long second-slot
+ room-draw-npc
+ end
+
+ # in the general case, all we want to do
+ # is store the x/y coords of the item
+ # in a convenient lookup table for later:
+ i := long room-stash
+ :calc special-tile-offset { 0xFF & - TILE_EXIT_UP }
+ v0 += special-tile-offset
+ v0 >>= v0 # v0 had stride 8...
+ v0 >>= v0 # v0 now has stride 2
+ i += v0
+ save v1 - v2
+ v0 := TILE_EMPTY
+;
+
+: draw-player
+ # pass frame offset { 0, 15 } in ve...
+ i := long player-left-0
+ if player-face == 1 then i := long player-right-0
+ i += ve
+ sprite player-x player-y 15
+;
+
+: draw-room-title
+ dialog-fill-screen
+ print-from-table current-room room-title-table
+ wait 60
+ clear
+;
+
+: draw-room
+ i := long room-map-table
+ i += current-room
+ i += current-room
+ load v1
+ i := room-write
+ save v1
+
+ v1 := 0 # x
+ v2 := 0 # y
+ v3 := 0 # index
+ loop
+ room-load
+ i += v3
+ load v0
+
+ if v0 >= TILE_NPC_1 then room-unpack
+ i := long tileset
+ i += v0
+ sprite v1 v2 8
+
+ v3 += 1
+ v2 += 8
+ if v2 == 64 then v1 += 8
+ if v2 == 64 then v2 := 0
+ if v1 != 128 then
+ again
+;
+
+: room
+ draw-room-title
+ draw-room
+
+ # the murderer is stalking the player!
+ i := long interstitial-progress
+ load v0
+ if v0 == 2 begin
+ i := long npc-positions
+ i += current-room
+ i += current-room
+ load v1
+ i := long npc-murderer
+ load v2 - v2
+ # kill the player if they're alone
+ # with a stalker murderer...
+ if v2 == v0 begin
+ if v1 == -1 then murder-player
+ if current-room == GAME_OVER then return
+ end
+ if v2 == v1 begin
+ if v1 == -1 then murder-player
+ if current-room == GAME_OVER then return
+ end
+ end
+
+ # walk/explore mode:
+ player-face := 0
+ i := long room-stash-start
+ load player-x - player-y
+ ve := 0
+ draw-player
+
+ loop
+ ve := 0
+ draw-player
+ prev-x := player-x
+ prev-y := player-y
+ vf := OCTO_KEY_D if vf key begin player-x += 4 player-face := 1 end
+ vf := OCTO_KEY_A if vf key begin player-x += -4 player-face := 0 end
+ vf := OCTO_KEY_S if vf key then player-y += 4
+ vf := OCTO_KEY_W if vf key then player-y += -4
+
+ room-load
+ # index at feet is:
+ # (py+8)/8 + (px/8)*8
+ v0 := player-y
+ v0 += 8
+ v0 >>= v0
+ v0 >>= v0
+ v0 >>= v0
+ i += v0
+ v0 := player-x
+ vf := 0b11111000
+ v0 &= vf
+ i += v0
+ load v0
+
+ if v0 == 0x88 then v0 := TILE_SOLID # (conservatory north windows)
+ if v0 == TILE_SOLID begin
+ player-x := prev-x
+ player-y := prev-y
+ end
+
+ while v0 != TILE_EXIT_UP
+ while v0 != TILE_EXIT_DN
+ while v0 != TILE_EXIT_LF
+ while v0 != TILE_EXIT_RT
+
+ if player-x != prev-x begin
+ # step animation:
+ player-x := prev-x
+ if player-face == 1 then player-x += 2
+ if player-face == 0 then player-x += -2
+ ve := 15
+ draw-player
+ wait 2
+ draw-player
+ if player-face == 1 then player-x += 2
+ if player-face == 0 then player-x += -2
+ end
+
+ ve := 0
+ draw-player
+
+ vf := OCTO_KEY_E if vf key then room-poi-walk
+ if current-room == GAME_OVER then return
+
+ sync
+ again
+ clear
+
+ i := long special-room-exits
+ i += current-room # stride 4...
+ i += current-room
+ i += current-room
+ i += current-room
+ :calc exit-offset { 0xFF & - TILE_EXIT_UP }
+ v0 += exit-offset
+ v0 >>= v0 # stride 8 -> stride 4
+ v0 >>= v0 # stride 4 -> stride 2
+ v0 >>= v0 # stride 2 -> stride 1
+ i += v0
+ load v0
+ if v0 != -1 begin
+ current-room := v0
+ jump room
+ end
+
+ # a brief pause before returning
+ # to the main map helps debounce
+ # held down keypresses:
+ wait 10
+;
+
+###########################################
+#
+# Interstitials
+#
+###########################################
+
+to-data
+
+: growing-suspicious
+ 0xFE 0x00 0x11 0xFD 0x13 0x21 0x1E 0xF9 0x26 0x2E 0x2B 0x1D 0x1E 0x2B 0x1E 0x2B
+ 0xF9 0x22 0x2C 0xFE 0x00 0x1B 0x20 0x2B 0x28 0x30 0x22 0x27 0x20 0xF9 0x2C 0x2E
+ 0x2C 0x29 0x22 0x1C 0x22 0x28 0x2E 0x2C 0xFE 0x00 0x25 0x28 0x1F 0xF9 0x2D 0x21
+ 0x1E 0xF9 0x22 0x27 0x2F 0x1E 0x2C 0x2D 0x22 0x20 0x1A 0x2D 0x22 0x28 0x27 0x3E
+ 0xFF
+: begun-stalking
+ 0xFE 0x06 0x17 0xFD 0x13 0x21 0x1E 0xF9 0x26 0x2E 0x2B 0x1D 0x1E 0x2B 0x1E 0x2B
+ 0xF9 0x21 0x1A 0x2C 0xFE 0x06 0x21 0x1B 0x1E 0x20 0x2E 0x27 0xF9 0x2C 0x2D 0x1A
+ 0x25 0x24 0x22 0x27 0x20 0xF9 0x32 0x28 0x2E 0x3F 0xFF
+: footsteps-in-distance
+ 0xFE 0x07 0x10 0x18 0x28 0x2E 0xF9 0x21 0x1E 0x1A 0x2B 0xF9 0x1F 0x28 0x28 0x2D
+ 0x2C 0x2D 0x1E 0x29 0x2C 0xFE 0x07 0x1A 0x2C 0x28 0x26 0x1E 0x30 0x21 0x1E 0x2B
+ 0x1E 0xF9 0x22 0x27 0xF9 0x2D 0x21 0x1E 0xFE 0x07 0x24 0x1D 0x22 0x2C 0x2D 0x1A
+ 0x27 0x1C 0x1E 0x3E 0x3E 0x3E 0xFF
+
+: notice-0 # Thunder rumbles in the distance.
+ 0xFE 0x06 0x17 0x13 0x21 0x2E 0x27 0x1D 0x1E 0x2B 0xF9 0x2B 0x2E 0x26 0x1B 0x25
+ 0x1E 0x2C 0xF9 0x22 0x27 0xFE 0x06 0x21 0x2D 0x21 0x1E 0xF9 0x1D 0x22 0x2C 0x2D
+ 0x1A 0x27 0x1C 0x1E 0x3E 0x3E 0x3E 0xFF
+: notice-1 # You feel as though you're being watched.
+ 0xFE 0x14 0x13 0x18 0x28 0x2E 0xF9 0x1F 0x1E 0x1E 0x25 0xF9 0x1A 0x2C 0xFE 0x14
+ 0x1D 0x2D 0x21 0x28 0x2E 0x20 0x21 0xF9 0x32 0x28 0x2E 0x42 0x2B 0x1E 0xFE 0x14
+ 0x27 0x1B 0x1E 0x22 0x27 0x20 0xF9 0x30 0x1A 0x2D 0x1C 0x21 0x1E 0x1D 0x3E 0xFF
+: notice-2 # You feel a sudden chill.
+ 0xFE 0x17 0x16 0x18 0x28 0x2E 0xF9 0x1F 0x1E 0x1E 0x25 0xF9 0x1A 0xFE 0x17 0x20
+ 0x2C 0x2E 0x1D 0x1D 0x1E 0x27 0xF9 0x1C 0x21 0x22 0x25 0x25 0x3E 0xFF
+: notice-3 # The door slams loudly behind you.
+ 0xFE 0x08 0x16 0x13 0x21 0x1E 0xF9 0x1D 0x28 0x28 0x2B 0xF9 0x2C 0x25 0x1A 0x26
+ 0x2C 0xFE 0x08 0x20 0x25 0x28 0x2E 0x1D 0x25 0x32 0xF9 0x1B 0x1E 0x21 0x22 0x27
+ 0x1D 0xF9 0x32 0x28 0x2E 0x3E 0xFF
+: notice-4 # The lights overhead flicker momentarily.
+ 0xFE 0x03 0x18 0x13 0x21 0x1E 0xF9 0x25 0x22 0x20 0x21 0x2D 0x2C 0xF9 0x28 0x2F
+ 0x1E 0x2B 0x21 0x1E 0x1A 0x1D 0xFE 0x03 0x22 0x1F 0x25 0x22 0x1C 0x24 0x1E 0x2B
+ 0xF9 0x26 0x28 0x26 0x1E 0x27 0x2D 0x1A 0x2B 0x22 0x25 0x32 0x3E 0xFF
+: notice-5 # You shakily wipe a bead of sweat from your brow. This case is getting to you.
+ 0xFE 0x03 0x0D 0x18 0x28 0x2E 0xF9 0x2C 0x21 0x1A 0x24 0x22 0x25 0x32 0xF9 0x30
+ 0x22 0x29 0x1E 0xF9 0x1A 0xFE 0x03 0x17 0x1B 0x1E 0x1A 0x1D 0xF9 0x28 0x1F 0xF9
+ 0x2C 0x30 0x1E 0x1A 0x2D 0xF9 0x1F 0x2B 0x28 0x26 0xFE 0x03 0x21 0x32 0x28 0x2E
+ 0x2B 0xF9 0x1B 0x2B 0x28 0x30 0x3E 0xF9 0x13 0x21 0x22 0x2C 0xF9 0x1C 0x1A 0x2C
+ 0x1E 0xFE 0x03 0x2B 0x22 0x2C 0xF9 0x20 0x1E 0x2D 0x2D 0x22 0x27 0x20 0xF9 0x2D
+ 0x28 0xF9 0x32 0x28 0x2E 0x3E 0xFF
+: notice-6 # Lightning cracks, flooding the nearby windows with light.
+ 0xFE 0x05 0x11 0x0B 0x22 0x20 0x21 0x2D 0x27 0x22 0x27 0x20 0xF9 0x1C 0x2B 0x1A
+ 0x1C 0x24 0x2C 0x41 0xFE 0x05 0x1B 0x1F 0x25 0x28 0x28 0x1D 0x22 0x27 0x20 0xF9
+ 0x2D 0x21 0x1E 0xF9 0x27 0x1E 0x1A 0x2B 0x1B 0x32 0xFE 0x05 0x25 0x30 0x22 0x27
+ 0x1D 0x28 0x30 0x2C 0xF9 0x30 0x22 0x2D 0x21 0xF9 0x25 0x22 0x20 0x21 0x2D 0x3E
+ 0xFF
+: notice-7 # The floorboards creak loudly under your feet.
+ 0xFE 0x09 0x13 0x13 0x21 0x1E 0xF9 0x1F 0x25 0x28 0x28 0x2B 0x1B 0x28 0x1A 0x2B
+ 0x1D 0x2C 0xFE 0x09 0x1D 0x1C 0x2B 0x1E 0x1A 0x24 0xF9 0x25 0x28 0x2E 0x1D 0x25
+ 0x32 0xF9 0x2E 0x27 0x1D 0x1E 0x2B 0xFE 0x09 0x27 0x32 0x28 0x2E 0x2B 0xF9 0x1F
+ 0x1E 0x1E 0x2D 0x3E 0xFF
+
+: interstitial-notice-table
+ pointer notice-0
+ pointer notice-1
+ pointer notice-2
+ pointer notice-3
+ pointer notice-4
+ pointer notice-5
+ pointer notice-6
+ pointer notice-7
+
+: interstitial-progress 0x00
+
+to-code
+
+: move-npcs
+ v1 := 9 random-upto-v1 # source room in v0.
+ v1 := random 1 # source slot in v1.
+
+ # make sure we have a source NPC.
+ i := long npc-positions
+ i += v0
+ i += v0
+ i += v1
+ load v2 - v2
+ if v2 == -1 then return
+
+ # find an adjacent, distinct room.
+ i := long room-nav
+ i += v0
+ i += v0
+ i += v0
+ i += v0
+ v2 := random 0b11
+ i += v2
+ load v2 - v2
+ if v2 == v0 then return # dest room in v2.
+
+ # make sure we have an empty destination.
+ v3 := 0
+ i := long npc-positions
+ i += v2
+ i += v2
+ load v4 - v4
+ if v4 != -1 then v3 := 1
+ i += v3
+ load v4 - v4
+ if v4 != -1 then return
+ # dest slot is in v3.
+
+ # remove NPC from source slot
+ i := long npc-positions
+ i += v0
+ i += v0
+ i += v1
+ load v4 - v4 # npc id is in v4.
+ vf := -1
+ save vf - vf
+
+ # place NPC in destination slot
+ i := long npc-positions
+ i += v2
+ i += v2
+ i += v3
+ save v4 - v4
+
+ dialog-fill-screen
+ print footsteps-in-distance
+ jump dialog-pause
+
+: describe-ambience
+ i := long interstitial-progress
+ load v0
+ if v0 == 0 begin
+ if suspicion-level > 40 begin
+ i := long interstitial-progress
+ v0 := 1
+ save v0
+ dialog-fill-screen
+ print growing-suspicious
+ jump dialog-pause
+ end
+ end
+ if v0 == 1 begin
+ if suspicion-level > 100 begin
+ i := long interstitial-progress
+ v0 := 2
+ save v0
+ dialog-fill-screen
+ print begun-stalking
+ jump dialog-pause
+ end
+ end
+
+ move-npcs
+
+ v0 := random 0xFF
+ if v0 > 128 then return
+ v2 := random 0b111
+ dialog-fill-screen
+ print-from-table v2 interstitial-notice-table
+ jump dialog-pause
+
+###########################################
+#
+# Fencing
+#
+###########################################
+
+to-data
+
+: fencing-background
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x33 0x18 0x33 0x18 0x33 0x18 0x33 0x18 0x33 0x18
+ 0x33 0x18 0x33 0x38 0x33 0x38 0x33 0x30 0x27 0x30 0x27 0x30 0x66 0x60 0x6E 0x60
+ 0x4C 0xC0 0x4C 0xC0 0x99 0x80 0x1B 0x00 0x36 0x00 0x6C 0x00 0xF8 0x00 0xE0 0x00
+ 0xE0 0x00 0x70 0x00 0x70 0x00 0x70 0x00 0x78 0x00 0x78 0x00 0x78 0x00 0x5C 0x00
+ 0x5C 0x00 0x5C 0x00 0x5C 0x00 0x5E 0x00 0x5E 0x00 0x5A 0x00 0x5B 0x00 0x5B 0x00
+ 0x5B 0x00 0x5B 0x00 0x5B 0x00 0x5B 0x00 0x5B 0x00 0x5B 0x00 0x5B 0x00 0x5B 0x00
+ 0x5B 0x00 0x5B 0x00 0x5B 0x00 0x5B 0x00 0x5B 0x00 0x5B 0x00 0x5B 0x00 0xFF 0xFF
+ 0xFF 0xFF 0xF2 0x72 0xE1 0x21 0xE0 0x20 0xF0 0x70 0xF8 0xF8 0xFD 0xFD 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF
+ 0xFF 0xFF 0x72 0x7F 0x21 0x3F 0x20 0x3F 0x70 0x7F 0xF8 0xFF 0xFD 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x02 0x10 0x0A 0x14 0x2A 0x94 0x2A 0x94 0x08 0x84
+ 0x72 0x25 0x1F 0x9B 0x07 0xFF 0x01 0xFF 0x02 0x2F 0x00 0x22 0x00 0x02 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x08 0x40 0x28 0x50 0x29 0x54 0x29 0x54 0x21 0x10
+ 0xA4 0x4E 0xD9 0xF8 0xFF 0xE0 0xFF 0x80 0xF4 0x40 0x44 0x00 0x40 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF
+ 0xFF 0xFF 0xFE 0x4E 0xFC 0x24 0xFC 0x04 0xFE 0x0E 0xFF 0x1F 0xFF 0xBF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x18 0xCC 0x18 0xCC 0x18 0xCC 0x18 0xCC 0x18 0xCC
+ 0x18 0xCC 0x1C 0xCC 0x1C 0xCC 0x0C 0xCC 0x0C 0xE4 0x0C 0xE4 0x06 0x66 0x06 0x76
+ 0x03 0x32 0x03 0x32 0x01 0x99 0x00 0xD8 0x00 0x6C 0x00 0x36 0x00 0x1F 0x00 0x07
+ 0x00 0x07 0x00 0x0E 0x00 0x0E 0x00 0x0E 0x00 0x1E 0x00 0x1E 0x00 0x1E 0x00 0x3A
+ 0x00 0x3A 0x00 0x3A 0x00 0x3A 0x00 0x7A 0x00 0x7A 0x00 0x5A 0x00 0xDA 0x00 0xDA
+ 0x00 0xDA 0x00 0xDA 0x00 0xDA 0x00 0xDA 0x00 0xDA 0x00 0xDA 0x00 0xDA 0x00 0xDA
+ 0x00 0xDA 0x00 0xDA 0x00 0xDA 0x00 0xDA 0x00 0xDA 0x00 0xDA 0x00 0xDA 0xFF 0xFF
+ 0xFF 0xFF 0x4E 0x4F 0x24 0x27 0x04 0x07 0x0E 0x0F 0x1F 0x1F 0xBF 0xBF 0xFF 0xFF
+
+: fencing-heart
+ 0xC8 0xF8 0x70 0x20
+
+: player-sword
+ 0xFF 0xF8 0xC0 0x08 0xFF 0xF0 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+: enemy-sword
+ 0x1F 0xFF 0x10 0x03 0x0F 0xFF 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+: player-top-neutral
+ 0x03 0xE0 0x07 0xF0 0x07 0xE8 0x07 0xA0 0x07 0xA0 0x0F 0xE0 0x3F 0xE0 0x1D 0xC0
+ 0x01 0x80 0x0F 0xE0 0x13 0xF0 0x13 0xF8 0x27 0xEE 0x27 0xFF 0x11 0x03 0x08 0xFC
+: player-top-stagger
+ 0x07 0xD0 0x4F 0xE0 0x0F 0xC0 0x2E 0x40 0x1F 0xC0 0x7F 0xC0 0x3F 0xC0 0x97 0x80
+ 0x03 0x00 0x1F 0xF0 0x27 0xFC 0x27 0xFF 0x4F 0xC7 0x4F 0xF8 0x22 0x06 0x11 0xF8
+: player-top-attack
+ 0x03 0xE0 0x07 0xF8 0x07 0xE0 0x07 0xA0 0x0F 0xA0 0x3F 0xE0 0x1F 0xE0 0x0D 0xC0
+ 0x01 0x83 0x03 0xFD 0x04 0x03 0x1C 0x1C 0x3F 0xE0 0x7F 0xE0 0x77 0xE0 0x37 0xE0
+: player-top-block
+ 0x03 0xE0 0x07 0xF8 0x07 0xE2 0x07 0xA2 0x0F 0xA5 0x3F 0xE5 0x1F 0xE5 0x0D 0xC5
+ 0x01 0x85 0x03 0xCA 0x04 0x6E 0x04 0x72 0x06 0x36 0x07 0x04 0x03 0xC8 0x07 0xF0
+: player-bottom-neutral
+ 0x0F 0xF0 0x0F 0xF0 0x1F 0xF8 0x1F 0xF8 0x3F 0xF8 0x3F 0xFC 0x7F 0xFC 0x1F 0xF8
+ 0x0E 0x70 0x0E 0x70 0x0C 0x60 0x18 0x60 0x18 0xC0 0x18 0xC0 0x1E 0xF0 0x00 0x00
+: player-bottom-step
+ 0x0F 0xF0 0x0F 0xF8 0x0F 0xFC 0x1F 0xFC 0x7F 0xFC 0x3F 0xFE 0x1F 0xFE 0x0F 0xFC
+ 0x0E 0x38 0x1C 0x18 0x38 0x1C 0x70 0x1C 0xE0 0x0C 0x60 0x0C 0x30 0x0F 0x00 0x00
+: player-bottom-stagger
+ 0x0F 0xF0 0x0F 0xF0 0x1F 0xF8 0x3F 0xF8 0x3F 0xF8 0xFF 0xF8 0x7F 0xF8 0x1F 0xF0
+ 0x0E 0x70 0x0E 0x78 0x07 0x38 0x03 0x19 0x03 0xFF 0x03 0xCE 0x01 0x84 0x00 0x00
+
+: enemy-0-top-neutral
+ 0x07 0xC0 0x0F 0xE0 0x1F 0xE0 0x06 0xF0 0x05 0xF0 0x07 0xF0 0x07 0xF0 0x03 0xA0
+ 0x01 0x80 0x07 0xF0 0x0F 0xC8 0x1F 0xC8 0x77 0xE4 0xFF 0xE4 0xC0 0x88 0x3F 0x10
+: enemy-0-top-tell
+ 0x07 0xC0 0x0F 0xE0 0x1F 0xE0 0x04 0xF0 0x05 0xF0 0x07 0xF0 0x07 0xF0 0x03 0xA0
+ 0x01 0x80 0x07 0xF0 0x0F 0xC8 0x1F 0xE4 0x37 0xF2 0x7F 0xF2 0x20 0x44 0x1F 0x98
+: enemy-0-top-stagger
+ 0x03 0xE4 0x07 0xF0 0x0F 0xF8 0x02 0x7A 0x02 0x78 0x03 0xF8 0x03 0xF8 0x01 0xD2
+ 0x00 0xC0 0x0F 0xF8 0x3F 0xE4 0xFF 0xE4 0xE3 0xF2 0x1F 0xF2 0x60 0x44 0x1F 0x8C
+: enemy-0-top-attack
+ 0x07 0xC0 0x0F 0xE0 0x1F 0xF0 0x06 0xF0 0x05 0xF0 0x07 0xF0 0x07 0xF0 0x03 0xA0
+ 0xC1 0x80 0xBF 0xC0 0xC0 0x20 0x38 0x38 0x07 0xFC 0x07 0xFE 0x07 0xEE 0x07 0xEC
+: enemy-0-top-block
+ 0x07 0xC0 0x0F 0xE0 0x5F 0xF0 0x45 0xF0 0xA5 0xF0 0xA7 0xF0 0xA7 0xF0 0xA3 0xA0
+ 0xA1 0x80 0x53 0xC0 0x76 0x20 0x4E 0x20 0x6C 0x60 0x20 0xE0 0x13 0xC0 0x0F 0xE0
+: enemy-0-bottom-neutral
+ 0x07 0xE0 0x0F 0xE0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x1F 0xF0 0x1F 0xF8 0x1F 0xF8
+ 0x1F 0xF8 0x1F 0xFC 0x1F 0xFC 0x06 0x18 0x03 0x18 0x03 0x18 0x0F 0x78 0x00 0x00
+: enemy-0-bottom-step
+ 0x07 0xE0 0x07 0xE0 0x0F 0xF0 0x0F 0xF0 0x1F 0xF0 0x1F 0xF8 0x1F 0xFC 0x1F 0xFE
+ 0x1F 0xFC 0x1F 0xFC 0x3F 0x9C 0x38 0x0E 0x30 0x07 0x30 0x06 0xF0 0x0C 0x00 0x00
+: enemy-0-bottom-stagger
+ 0x01 0xF8 0x03 0xF8 0x03 0xF8 0x03 0xF8 0x07 0xF8 0x0F 0xF8 0x1F 0xF8 0x7F 0xF8
+ 0x3F 0xF8 0x1F 0xF0 0x1F 0xF0 0x98 0xF0 0xFF 0xC0 0x73 0xC0 0x21 0x80 0x00 0x00
+
+: enemy-1-top-neutral
+ 0x07 0xD0 0x0F 0xF0 0x0F 0xF0 0x06 0xF0 0x05 0xF0 0x0F 0xF0 0x0F 0xF0 0x07 0xE0
+ 0x01 0xC0 0x07 0xF0 0x0F 0xC8 0x1F 0xC8 0x77 0xE4 0xFF 0xE4 0xC0 0x88 0x3F 0x10
+: enemy-1-top-tell
+ 0x07 0xD0 0x0F 0xF0 0x0F 0xF0 0x04 0xF0 0x05 0xF0 0x0F 0xF0 0x0F 0xF0 0x07 0xE0
+ 0x01 0xC0 0x07 0xF0 0x0F 0xC8 0x1F 0xE4 0x37 0xF2 0x7F 0xF2 0x20 0x44 0x1F 0x98
+: enemy-1-top-stagger
+ 0x03 0xEA 0x07 0xF8 0x07 0xF8 0x02 0x78 0x02 0x7B 0x07 0xF8 0x07 0xF8 0x03 0xF2
+ 0x00 0xE0 0x0F 0xF8 0x3F 0xE4 0xFF 0xE4 0xE3 0xF2 0x1F 0xF2 0x60 0x44 0x1F 0x8C
+: enemy-1-top-attack
+ 0x07 0xD0 0x0F 0xF0 0x0F 0xF0 0x06 0xF0 0x05 0xF0 0x0F 0xF0 0x0F 0xF0 0x07 0xE0
+ 0xF1 0xC0 0x8F 0xC0 0xC0 0x20 0x20 0x38 0x1F 0xFC 0x07 0xFE 0x07 0xFE 0x07 0xF4
+: enemy-1-top-block
+ 0x07 0xD0 0x0F 0xF0 0x4F 0xF0 0x45 0xF0 0xA5 0xF0 0xAF 0xF0 0xAF 0xF0 0xA7 0xE0
+ 0xA1 0xC0 0x53 0xC0 0x76 0x20 0x4C 0x20 0x68 0x20 0x20 0x60 0x11 0xC0 0x0F 0xE0
+: enemy-1-bottom-neutral
+ 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF8 0x0F 0xF8 0x0F 0x78
+ 0x0F 0x78 0x0F 0x78 0x07 0x38 0x07 0x9C 0x03 0x9C 0x03 0x9C 0x0F 0xFC 0x00 0x00
+: enemy-1-bottom-step
+ 0x07 0xF0 0x07 0xF0 0x0F 0xF0 0x0F 0xE0 0x1F 0xE0 0x1F 0xF0 0x1F 0xF0 0x1E 0xF8
+ 0x1E 0x7C 0x1C 0x3E 0x3C 0x1E 0x3C 0x0F 0x38 0x07 0x38 0x06 0xF8 0x0C 0x00 0x00
+: enemy-1-bottom-stagger
+ 0x03 0xFC 0x03 0xFC 0x03 0xFC 0x03 0xFC 0x07 0xFC 0x07 0xFC 0x07 0xFC 0x0F 0xFC
+ 0x0F 0x78 0x1F 0x78 0x1C 0xF0 0x9C 0xE0 0xFF 0xE0 0x73 0xC0 0x21 0x80 0x00 0x00
+
+: enemy-2-top-neutral
+ 0x07 0xC0 0x07 0xE0 0x07 0xE0 0x1F 0xE0 0x05 0xE0 0x07 0xE0 0x07 0xC0 0x03 0x80
+ 0x01 0x80 0x07 0xF0 0x0F 0xC8 0x1F 0xC8 0x77 0xE4 0xFF 0xE4 0xC0 0x88 0x3F 0x10
+: enemy-2-top-tell
+ 0x07 0xC0 0x07 0xE0 0x07 0xE0 0x1F 0xE0 0x05 0xE0 0x07 0xE0 0x07 0xC0 0x03 0x80
+ 0x01 0x80 0x07 0xF0 0x0F 0xC8 0x1F 0xE4 0x37 0xF2 0x7F 0xF2 0x20 0x44 0x1F 0x98
+: enemy-2-top-stagger
+ 0x03 0xE4 0x03 0xF0 0x03 0xF0 0x0F 0xF6 0x02 0xF0 0x03 0xF0 0x03 0xE4 0x01 0xC0
+ 0x00 0xC0 0x0F 0xF8 0x3F 0xE4 0xFF 0xE4 0xE3 0xF2 0x1F 0xF2 0x60 0x44 0x1F 0x8C
+: enemy-2-top-attack
+ 0x07 0xC0 0x07 0xE0 0x07 0xE0 0x1F 0xE0 0x07 0xE0 0x07 0xE0 0x07 0xC0 0x03 0x80
+ 0xC1 0x80 0xBF 0xC0 0xC0 0x20 0x38 0x38 0x07 0xFC 0x07 0xFE 0x07 0xEE 0x07 0xEC
+: enemy-2-top-block
+ 0x07 0xC0 0x07 0xE0 0x47 0xE0 0x5F 0xE0 0xA5 0xE0 0xA7 0xE0 0xA7 0xC0 0xA3 0x80
+ 0xA1 0x80 0x53 0xC0 0x76 0x20 0x4E 0x20 0x6C 0x60 0x20 0xE0 0x13 0xC0 0x0F 0xE0
+: enemy-2-bottom-neutral
+ 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0E 0xF0 0x0E 0x70
+ 0x0E 0x70 0x0E 0x70 0x06 0x30 0x06 0x18 0x03 0x18 0x03 0x18 0x0F 0x78 0x00 0x00
+: enemy-2-bottom-step
+ 0x07 0xE0 0x07 0xE0 0x0F 0xE0 0x0F 0xE0 0x1F 0xF0 0x1F 0xF0 0x1E 0xF0 0x1C 0xF0
+ 0x1C 0x78 0x18 0x38 0x38 0x1C 0x38 0x0E 0x30 0x07 0x30 0x06 0xF0 0x0C 0x00 0x00
+: enemy-2-bottom-stagger
+ 0x03 0xFC 0x03 0xFC 0x03 0xF8 0x03 0xF8 0x07 0xF8 0x07 0xF8 0x07 0x78 0x0F 0x78
+ 0x0E 0x70 0x1E 0x70 0x1C 0xE0 0x98 0xC0 0xFF 0xC0 0x73 0xC0 0x21 0x80 0x00 0x00
+
+: enemy-3-top-neutral
+ 0x07 0xC0 0x07 0xE0 0x0F 0xE0 0x1C 0xE0 0x1F 0xE0 0x07 0xE0 0x07 0xC0 0x03 0xE0
+ 0x01 0xE0 0x07 0xF0 0x0F 0xC8 0x1F 0xC8 0x77 0xE4 0xFF 0xE4 0xC0 0x88 0x3F 0x10
+: enemy-3-top-tell
+ 0x07 0xC0 0x07 0xE0 0x0F 0xE0 0x1C 0xE0 0x1F 0xE0 0x07 0xE0 0x07 0xC0 0x03 0xE0
+ 0x01 0xE0 0x07 0xF0 0x0F 0xC8 0x1F 0xE4 0x37 0xF2 0x7F 0xF2 0x20 0x44 0x1F 0x98
+: enemy-3-top-stagger
+ 0x03 0xE4 0x03 0xF0 0x07 0xF0 0x0E 0x76 0x0F 0xF0 0x03 0xF0 0x03 0xE4 0x01 0xF0
+ 0x00 0xF0 0x0F 0xF8 0x3F 0xE4 0xFF 0xE4 0xE3 0xF2 0x1F 0xF2 0x60 0x44 0x1F 0x8C
+: enemy-3-top-attack
+ 0x07 0xC0 0x07 0xE0 0x0F 0xE0 0x1C 0xE0 0x1F 0xE0 0x07 0xE0 0x07 0xC0 0x03 0xE0
+ 0xC1 0xE0 0xBF 0xE0 0xC0 0x20 0x38 0x38 0x07 0xFC 0x07 0xFE 0x07 0xEE 0x07 0xEC
+: enemy-3-top-block
+ 0x07 0xC0 0x07 0xE0 0x4F 0xE0 0x5C 0xE0 0xBF 0xE0 0xA7 0xE0 0xA7 0xC0 0xA3 0xE0
+ 0xA1 0xE0 0x53 0xE0 0x76 0x20 0x4E 0x20 0x6C 0x60 0x20 0xE0 0x13 0xC0 0x0F 0xE0
+: enemy-3-bottom-neutral
+ 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0E 0xF0 0x0E 0x70
+ 0x0E 0x70 0x0E 0x70 0x06 0x30 0x06 0x18 0x03 0x18 0x03 0x18 0x0F 0x78 0x00 0x00
+: enemy-3-bottom-step
+ 0x07 0xE0 0x07 0xE0 0x0F 0xE0 0x0F 0xE0 0x1F 0xF0 0x1F 0xF0 0x1E 0xF0 0x1C 0xF0
+ 0x1C 0x78 0x18 0x38 0x38 0x1C 0x38 0x0E 0x30 0x07 0x30 0x06 0xF0 0x0C 0x00 0x00
+: enemy-3-bottom-stagger
+ 0x03 0xFC 0x03 0xFC 0x03 0xF8 0x03 0xF8 0x07 0xF8 0x07 0xF8 0x07 0x78 0x0F 0x78
+ 0x0E 0x70 0x1E 0x70 0x1C 0xE0 0x98 0xC0 0xFF 0xC0 0x73 0xC0 0x21 0x80 0x00 0x00
+
+: enemy-4-top-neutral
+ 0x07 0xC0 0x0F 0xE0 0x0F 0xE0 0x0E 0xF0 0x0D 0xF0 0x1F 0xF8 0x1F 0xF8 0x1B 0xF8
+ 0x01 0x80 0x07 0xF0 0x0F 0xC8 0x1F 0xC8 0x77 0xE4 0xFF 0xE4 0xC0 0x88 0x3F 0x10
+: enemy-4-top-tell
+ 0x07 0xC0 0x0F 0xE0 0x0F 0xE0 0x0C 0xF0 0x0D 0xF0 0x1F 0xF8 0x1F 0xF8 0x1B 0xF8
+ 0x01 0x80 0x07 0xF0 0x0F 0xC8 0x1F 0xE4 0x37 0xF2 0x7F 0xF2 0x20 0x44 0x1F 0x98
+: enemy-4-top-stagger
+ 0x03 0xE4 0x07 0xF0 0x07 0xF0 0x06 0x79 0x06 0x78 0x0F 0xFC 0x0F 0xFD 0x0D 0xFC
+ 0x00 0xC0 0x0F 0xF8 0x3F 0xE4 0xFF 0xE4 0xE3 0xF2 0x1F 0xF2 0x60 0x44 0x1F 0x8C
+: enemy-4-top-attack
+ 0x07 0xC0 0x0F 0xE0 0x0F 0xE0 0x0E 0xF0 0x0D 0xF0 0x1F 0xF8 0x1F 0xF8 0x1B 0xF8
+ 0xC1 0x80 0xBF 0xC0 0xC0 0x20 0x38 0x38 0x07 0xFC 0x07 0xFE 0x07 0xEE 0x07 0xEC
+: enemy-4-top-block
+ 0x07 0xC0 0x0F 0xE0 0x4F 0xE0 0x4E 0xF0 0xAD 0xF0 0xBF 0xF8 0xBF 0xF8 0xBB 0xF8
+ 0xA1 0x80 0x53 0xC0 0x76 0x20 0x4E 0x20 0x6C 0x60 0x20 0xE0 0x13 0xC0 0x0F 0xE0
+: enemy-4-bottom-neutral
+ 0x07 0xF0 0x07 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF8
+ 0x0F 0xF8 0x0F 0xF8 0x0F 0xF8 0x0F 0xFC 0x0F 0xFE 0x03 0x18 0x0F 0x78 0x00 0x00
+: enemy-4-bottom-step
+ 0x07 0xE0 0x07 0xE0 0x0F 0xE0 0x0F 0xE0 0x1F 0xF0 0x1F 0xF0 0x1F 0xF8 0x1F 0xF8
+ 0x1F 0xFC 0x1F 0xFE 0x3F 0xFE 0x3F 0xFE 0x3F 0xFF 0x30 0x06 0xF0 0x0C 0x00 0x00
+: enemy-4-bottom-stagger
+ 0x03 0xFC 0x03 0xFC 0x03 0xF8 0x03 0xF8 0x07 0xF8 0x07 0xF8 0x0F 0xF8 0x0F 0xF8
+ 0x1F 0xF8 0x7F 0xF8 0x3F 0xF8 0x98 0xF0 0xFF 0xD0 0x73 0xC0 0x21 0x80 0x00 0x00
+
+: enemy-5-top-neutral
+ 0x1F 0xC0 0x0F 0xE0 0x0F 0xE0 0x06 0xE0 0x0D 0xE0 0x0F 0xE0 0x07 0xE0 0x07 0xC0
+ 0x01 0xC0 0x07 0xF0 0x0F 0xC8 0x1F 0xC8 0x77 0xE4 0xFF 0xE4 0xC0 0x88 0x3F 0x10
+: enemy-5-top-tell
+ 0x1F 0xC0 0x0F 0xE0 0x0F 0xE0 0x04 0xE0 0x0D 0xE0 0x0F 0xE0 0x07 0xE0 0x07 0xC0
+ 0x01 0xC0 0x07 0xF0 0x0F 0xC8 0x1F 0xE4 0x37 0xF2 0x7F 0xF2 0x20 0x44 0x1F 0x98
+: enemy-5-top-stagger
+ 0x0F 0xE4 0x07 0xF0 0x07 0xF0 0x02 0x70 0x06 0x76 0x07 0xF0 0x03 0xF0 0x03 0xE4
+ 0x00 0xE0 0x0F 0xF8 0x3F 0xE4 0xFF 0xE4 0xE3 0xF2 0x1F 0xF2 0x60 0x44 0x1F 0x8C
+: enemy-5-top-attack
+ 0x1F 0xC0 0x0F 0xE0 0x07 0xE0 0x06 0xE0 0x0D 0xE0 0x0F 0xE0 0x07 0xE0 0x07 0xC0
+ 0xF1 0xC0 0x8F 0xC0 0xC0 0x20 0x20 0x38 0x1F 0xFC 0x07 0xFE 0x07 0xFE 0x07 0xF4
+: enemy-5-top-block
+ 0x1F 0xC0 0x0F 0xE0 0x4F 0xE0 0x45 0xE0 0xAD 0xE0 0xAF 0xE0 0xA7 0xE0 0xA7 0xC0
+ 0xA1 0xC0 0x53 0xC0 0x76 0x20 0x4C 0x20 0x68 0x20 0x20 0x60 0x11 0xC0 0x0F 0xE0
+: enemy-5-bottom-neutral
+ 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF0 0x0F 0xF8 0x0F 0xF8 0x0F 0x78
+ 0x0F 0x78 0x0F 0x78 0x07 0x38 0x07 0x9C 0x03 0x9C 0x03 0x9C 0x0F 0xFC 0x00 0x00
+: enemy-5-bottom-step
+ 0x07 0xF0 0x07 0xF0 0x0F 0xF0 0x0F 0xE0 0x1F 0xE0 0x1F 0xF0 0x1F 0xF0 0x1E 0xF8
+ 0x1E 0x7C 0x1C 0x3E 0x3C 0x1E 0x3C 0x0F 0x38 0x07 0x38 0x06 0xF8 0x0C 0x00 0x00
+: enemy-5-bottom-stagger
+ 0x03 0xFC 0x03 0xFC 0x03 0xFC 0x03 0xFC 0x07 0xFC 0x07 0xFC 0x07 0xFC 0x0F 0xFC
+ 0x0F 0x78 0x1F 0x78 0x1C 0xF0 0x9C 0xE0 0xFF 0xE0 0x73 0xC0 0x21 0x80 0x00 0x00
+
+: enemy-6-top-neutral
+ 0x07 0xC0 0x0F 0xE0 0x0F 0xE0 0x06 0xF0 0x05 0xF0 0x07 0xF0 0x07 0xF0 0x03 0xF0
+ 0x01 0x80 0x07 0xF0 0x0F 0xC8 0x1F 0xC8 0x77 0xE4 0xFF 0xE4 0xC0 0x88 0x3F 0x10
+: enemy-6-top-tell
+ 0x07 0xC0 0x0F 0xE0 0x0F 0xE0 0x04 0xF0 0x05 0xF0 0x07 0xF0 0x07 0xF0 0x03 0xF0
+ 0x01 0x80 0x07 0xF0 0x0F 0xC8 0x1F 0xE4 0x37 0xF2 0x7F 0xF2 0x20 0x44 0x1F 0x98
+: enemy-6-top-stagger
+ 0x03 0xE2 0x07 0xF0 0x07 0xF8 0x02 0x7B 0x02 0x78 0x03 0xF8 0x03 0xFA 0x01 0xF8
+ 0x00 0xC0 0x0F 0xF8 0x3F 0xE4 0xFF 0xE4 0xE3 0xF2 0x1F 0xF2 0x60 0x44 0x1F 0x8C
+: enemy-6-top-attack
+ 0x07 0xC0 0x0F 0xE0 0x0F 0xE0 0x06 0xF0 0x05 0xF0 0x07 0xF0 0x07 0xF0 0x03 0xF0
+ 0xC1 0x80 0xBF 0xC0 0xC0 0x20 0x38 0x38 0x07 0xFC 0x07 0xFE 0x07 0xEE 0x07 0xEC
+: enemy-6-top-block
+ 0x07 0xC0 0x0F 0xE0 0x47 0xE0 0x45 0xF0 0xA5 0xF0 0xA7 0xF0 0xA7 0xF0 0xA3 0xF0
+ 0xA1 0x80 0x53 0xC0 0x76 0x20 0x4E 0x20 0x6C 0x60 0x20 0xE0 0x13 0xC0 0x0F 0xE0
+: enemy-6-bottom-neutral
+ 0x0F 0xF0 0x0F 0xF0 0x0F 0xF8 0x1F 0xF8 0x1F 0xFC 0x0F 0xF0 0x0E 0x70 0x06 0x70
+ 0x06 0x70 0x06 0x30 0x02 0x10 0x02 0x18 0x03 0x18 0x03 0x18 0x0F 0x78 0x00 0x00
+: enemy-6-bottom-step
+ 0x0F 0xE0 0x1F 0xF0 0x1F 0xF0 0x3F 0xF0 0x3F 0xF8 0x0F 0xE0 0x1C 0xE0 0x1C 0x70
+ 0x18 0x38 0x18 0x18 0x30 0x0C 0x30 0x06 0x30 0x07 0x30 0x06 0xF0 0x0C 0x00 0x00
+: enemy-6-bottom-stagger
+ 0x07 0xFC 0x1F 0xFC 0x3F 0xF8 0x0F 0xF8 0x03 0xF8 0x07 0xF8 0x07 0x70 0x06 0x30
+ 0x0E 0x30 0x0C 0x60 0x18 0x60 0x90 0xC0 0xFF 0xC0 0x73 0xC0 0x21 0x80 0x00 0x00
+
+: enemy-7-top-neutral
+ 0x03 0xC0 0x07 0xE0 0x07 0xE0 0x06 0xE0 0x0D 0xE0 0x0F 0xE0 0x07 0xE0 0x03 0xE0
+ 0x07 0xF0 0x0F 0x88 0x1F 0x84 0x3F 0xC4 0x77 0xE2 0xFF 0xE2 0xC0 0x84 0x3F 0x08
+: enemy-7-top-tell
+ 0x03 0xC0 0x07 0xE0 0x07 0xE0 0x04 0xE0 0x0D 0xE0 0x0F 0xE0 0x07 0xE0 0x03 0xE0
+ 0x03 0xF8 0x07 0xC4 0x0F 0xC2 0x1F 0xE2 0x3F 0xF1 0x7F 0xE1 0x20 0x42 0x1F 0x8C
+: enemy-7-top-stagger
+ 0x01 0xE4 0x03 0xF0 0x03 0xF0 0x06 0x76 0x06 0x70 0x03 0xF0 0x03 0xF4 0x01 0xF0
+ 0x01 0xF8 0x0F 0xC4 0x3F 0xC4 0xFF 0xC4 0xE3 0xE2 0x1F 0xE2 0x60 0x44 0x1F 0x8C
+: enemy-7-top-attack
+ 0x03 0xC0 0x07 0xE0 0x07 0xE0 0x06 0xE0 0x0D 0xE0 0x0F 0xE0 0x07 0xE0 0x03 0xE0
+ 0xC1 0xF0 0xBF 0xF0 0x80 0x38 0x40 0x3C 0x38 0x3E 0x07 0xFF 0x07 0xFF 0x07 0xEE
+: enemy-7-top-block
+ 0x03 0xC0 0x07 0xE0 0x47 0xE0 0x45 0xE0 0xAD 0xE0 0xAF 0xE0 0xA7 0xE0 0xA3 0xE0
+ 0xA1 0xE0 0x53 0x30 0x76 0x10 0x4E 0x10 0x64 0x30 0x20 0x60 0x10 0xE0 0x0F 0xE0
+: enemy-7-bottom-neutral
+ 0x0F 0xF0 0x0F 0xF8 0x0F 0xF8 0x0F 0xF8 0x0F 0xF8 0x0F 0xF8 0x0F 0xF8 0x0F 0x7C
+ 0x0F 0x7C 0x0F 0xFC 0x07 0xBC 0x07 0x9E 0x03 0xDE 0x03 0xDE 0x0F 0xFE 0x00 0x00
+: enemy-7-bottom-step
+ 0x07 0xF0 0x07 0xF0 0x0F 0xF0 0x0F 0xF8 0x1F 0xF8 0x1F 0xF8 0x1F 0xF8 0x1F 0xFC
+ 0x1E 0x7C 0x1E 0x3E 0x3C 0x1F 0x3C 0x0F 0x3C 0x07 0x3C 0x06 0xFC 0x0C 0x00 0x00
+: enemy-7-bottom-stagger
+ 0x03 0xFE 0x03 0xFE 0x03 0xFE 0x03 0xFE 0x07 0xFE 0x07 0xFC 0x07 0xFC 0x0F 0xFC
+ 0x0F 0x78 0x1F 0x78 0x1E 0xF0 0xBC 0xF0 0xFF 0xF0 0x73 0xE0 0x21 0xC0 0x00 0x00
+
+to-code
+
+:const MODE_NEUTRAL 0
+:const MODE_WALK_RT 1
+:const MODE_WALK_LF 2
+:const MODE_BLOCK 3
+:const MODE_TELL 4
+:const MODE_ATTACK 5
+:const MODE_STAGGER 6
+
+#note: can't touch vc as it contains suspicion level. everything else is fair game.
+:alias player-health vb # 20, 12, 4 (-8 per step)
+:alias enemy-health va # 103, 111, 119 (+8 per step)
+:alias player-fence-x v9
+:alias enemy-fence-x v8
+:alias player-mode v7
+:alias player-timer v6
+:alias enemy-mode v5
+:alias enemy-timer v4
+
+: draw-player-and-enemy
+ # top halves
+ v0 := 24
+ i := long player-top-neutral
+ if player-mode == MODE_ATTACK then i := long player-top-attack
+ if player-mode == MODE_STAGGER then i := long player-top-stagger
+ if player-mode == MODE_BLOCK then i := long player-top-block
+ sprite player-fence-x v0 0
+ if player-mode == MODE_ATTACK begin
+ v1 := 31
+ v2 := player-fence-x
+ v2 += 16
+ i := long player-sword
+ sprite v2 v1 0
+ end
+
+ indirect enemy-top-neutral-slot
+ if enemy-mode == MODE_ATTACK then indirect enemy-top-attack-slot
+ if enemy-mode == MODE_STAGGER then indirect enemy-top-stagger-slot
+ if enemy-mode == MODE_TELL then indirect enemy-top-tell-slot
+ if enemy-mode == MODE_BLOCK then indirect enemy-top-block-slot
+ sprite enemy-fence-x v0 0
+ if enemy-mode == MODE_ATTACK begin
+ v1 := 32
+ v2 := enemy-fence-x
+ v2 += -16
+ i := long enemy-sword
+ sprite v2 v1 0
+ end
+
+ # bottom halves
+ v0 += 16
+ i := long player-bottom-neutral
+ vf := 0
+ if player-mode == MODE_WALK_LF then vf := 1
+ if player-mode == MODE_WALK_RT then vf := 1
+ vf &= player-timer
+ if vf != 0 then i := long player-bottom-step
+ if player-mode == MODE_STAGGER then i := long player-bottom-stagger
+ sprite player-fence-x v0 0
+ indirect enemy-bottom-neutral-slot
+ vf := 0
+ if enemy-mode == MODE_WALK_LF then vf := 1
+ if enemy-mode == MODE_WALK_RT then vf := 1
+ vf &= enemy-timer
+ if vf != 0 then indirect enemy-bottom-step-slot
+ if enemy-mode == MODE_STAGGER then indirect enemy-bottom-stagger-slot
+ sprite enemy-fence-x v0 0
+;
+
+to-data
+
+: fencing-enemy-0 # { target-pointer, patch-ptr }
+ pointer enemy-top-neutral-slot pointer enemy-0-top-neutral
+ pointer enemy-top-tell-slot pointer enemy-0-top-tell
+ pointer enemy-top-stagger-slot pointer enemy-0-top-stagger
+ pointer enemy-top-attack-slot pointer enemy-0-top-attack
+ pointer enemy-top-block-slot pointer enemy-0-top-block
+ pointer enemy-bottom-neutral-slot pointer enemy-0-bottom-neutral
+ pointer enemy-bottom-step-slot pointer enemy-0-bottom-step
+ pointer enemy-bottom-stagger-slot pointer enemy-0-bottom-stagger
+: fencing-enemy-1
+ pointer enemy-top-neutral-slot pointer enemy-1-top-neutral
+ pointer enemy-top-tell-slot pointer enemy-1-top-tell
+ pointer enemy-top-stagger-slot pointer enemy-1-top-stagger
+ pointer enemy-top-attack-slot pointer enemy-1-top-attack
+ pointer enemy-top-block-slot pointer enemy-1-top-block
+ pointer enemy-bottom-neutral-slot pointer enemy-1-bottom-neutral
+ pointer enemy-bottom-step-slot pointer enemy-1-bottom-step
+ pointer enemy-bottom-stagger-slot pointer enemy-1-bottom-stagger
+: fencing-enemy-2
+ pointer enemy-top-neutral-slot pointer enemy-2-top-neutral
+ pointer enemy-top-tell-slot pointer enemy-2-top-tell
+ pointer enemy-top-stagger-slot pointer enemy-2-top-stagger
+ pointer enemy-top-attack-slot pointer enemy-2-top-attack
+ pointer enemy-top-block-slot pointer enemy-2-top-block
+ pointer enemy-bottom-neutral-slot pointer enemy-2-bottom-neutral
+ pointer enemy-bottom-step-slot pointer enemy-2-bottom-step
+ pointer enemy-bottom-stagger-slot pointer enemy-2-bottom-stagger
+: fencing-enemy-3
+ pointer enemy-top-neutral-slot pointer enemy-3-top-neutral
+ pointer enemy-top-tell-slot pointer enemy-3-top-tell
+ pointer enemy-top-stagger-slot pointer enemy-3-top-stagger
+ pointer enemy-top-attack-slot pointer enemy-3-top-attack
+ pointer enemy-top-block-slot pointer enemy-3-top-block
+ pointer enemy-bottom-neutral-slot pointer enemy-3-bottom-neutral
+ pointer enemy-bottom-step-slot pointer enemy-3-bottom-step
+ pointer enemy-bottom-stagger-slot pointer enemy-3-bottom-stagger
+: fencing-enemy-4
+ pointer enemy-top-neutral-slot pointer enemy-4-top-neutral
+ pointer enemy-top-tell-slot pointer enemy-4-top-tell
+ pointer enemy-top-stagger-slot pointer enemy-4-top-stagger
+ pointer enemy-top-attack-slot pointer enemy-4-top-attack
+ pointer enemy-top-block-slot pointer enemy-4-top-block
+ pointer enemy-bottom-neutral-slot pointer enemy-4-bottom-neutral
+ pointer enemy-bottom-step-slot pointer enemy-4-bottom-step
+ pointer enemy-bottom-stagger-slot pointer enemy-4-bottom-stagger
+: fencing-enemy-5
+ pointer enemy-top-neutral-slot pointer enemy-5-top-neutral
+ pointer enemy-top-tell-slot pointer enemy-5-top-tell
+ pointer enemy-top-stagger-slot pointer enemy-5-top-stagger
+ pointer enemy-top-attack-slot pointer enemy-5-top-attack
+ pointer enemy-top-block-slot pointer enemy-5-top-block
+ pointer enemy-bottom-neutral-slot pointer enemy-5-bottom-neutral
+ pointer enemy-bottom-step-slot pointer enemy-5-bottom-step
+ pointer enemy-bottom-stagger-slot pointer enemy-5-bottom-stagger
+: fencing-enemy-6
+ pointer enemy-top-neutral-slot pointer enemy-6-top-neutral
+ pointer enemy-top-tell-slot pointer enemy-6-top-tell
+ pointer enemy-top-stagger-slot pointer enemy-6-top-stagger
+ pointer enemy-top-attack-slot pointer enemy-6-top-attack
+ pointer enemy-top-block-slot pointer enemy-6-top-block
+ pointer enemy-bottom-neutral-slot pointer enemy-6-bottom-neutral
+ pointer enemy-bottom-step-slot pointer enemy-6-bottom-step
+ pointer enemy-bottom-stagger-slot pointer enemy-6-bottom-stagger
+: fencing-enemy-7
+ pointer enemy-top-neutral-slot pointer enemy-7-top-neutral
+ pointer enemy-top-tell-slot pointer enemy-7-top-tell
+ pointer enemy-top-stagger-slot pointer enemy-7-top-stagger
+ pointer enemy-top-attack-slot pointer enemy-7-top-attack
+ pointer enemy-top-block-slot pointer enemy-7-top-block
+ pointer enemy-bottom-neutral-slot pointer enemy-7-bottom-neutral
+ pointer enemy-bottom-step-slot pointer enemy-7-bottom-step
+ pointer enemy-bottom-stagger-slot pointer enemy-7-bottom-stagger
+
+: fencing-enemy-table
+ pointer fencing-enemy-0
+ pointer fencing-enemy-1
+ pointer fencing-enemy-2
+ pointer fencing-enemy-3
+ pointer fencing-enemy-4
+ pointer fencing-enemy-5
+ pointer fencing-enemy-6
+ pointer fencing-enemy-7
+
+: engarde
+ 0xFE 0x1C 0x19 0xFC 0x04 0x27 0xF9 0x06 0x1A 0x2B 0x1D 0x1E 0x3F 0xFF
+
+: fight-start-1 # The fiend grabs a sword from the wall. They intend to go down fighting!
+ 0xFE 0x03 0x08 0x13 0x21 0x1E 0xF9 0x1F 0x22 0x1E 0x27 0x1D 0xF9 0x20 0x2B 0x1A
+ 0x1B 0x2C 0xF9 0x1A 0xF9 0xF9 0xFE 0x03 0x12 0x2C 0x30 0x28 0x2B 0x1D 0xF9 0x1F
+ 0x2B 0x28 0x26 0xF9 0x2D 0x21 0x1E 0xF9 0x30 0x1A 0x25 0x25 0x3E 0xF9 0xF9 0xFE
+ 0x03 0x26 0x13 0x21 0x1E 0x32 0xF9 0x22 0x27 0x2D 0x1E 0x27 0x1D 0xF9 0x2D 0x28
+ 0xF9 0x20 0x28 0xF9 0xF9 0xFE 0x03 0x30 0x1D 0x28 0x30 0x27 0xF9 0x1F 0x22 0x20
+ 0x21 0x2D 0x22 0x27 0x20 0x3F 0xFF
+: fight-start-2 # I pray those fencing lessons won't have gone to waste...
+ 0xFE 0x01 0x11 0x08 0xF9 0x29 0x2B 0x1A 0x32 0xF9 0x2D 0x21 0x28 0x2C 0x1E 0xF9
+ 0x1F 0x1E 0x27 0x1C 0x22 0x27 0x20 0xFE 0x01 0x1B 0x25 0x1E 0x2C 0x2C 0x28 0x27
+ 0x2C 0xF9 0x30 0x28 0x27 0x42 0x2D 0xF9 0x21 0x1A 0x2F 0x1E 0xFE 0x01 0x25 0x20
+ 0x28 0x27 0x1E 0xF9 0x2D 0x28 0xF9 0x30 0x1A 0x2C 0x2D 0x1E 0x3E 0x3E 0x3E 0xFF
+: dispatched # With a few deft blows, the villain is soundly trounced.
+ 0xFE 0x03 0x11 0x16 0x22 0x2D 0x21 0xF9 0x1A 0xF9 0x1F 0x1E 0x30 0xF9 0x1D 0x1E
+ 0x1F 0x2D 0xFE 0x03 0x1B 0x1B 0x25 0x28 0x30 0x2C 0x41 0xF9 0x2D 0x21 0x1E 0xF9
+ 0x2F 0x22 0x25 0x25 0x1A 0x22 0x27 0xFE 0x03 0x25 0x22 0x2C 0xF9 0x2C 0x28 0x2E
+ 0x27 0x1D 0x25 0x32 0xF9 0x2D 0x2B 0x28 0x2E 0x27 0x1C 0x1E 0x1D 0x3E 0xFF
+: failed-fight-1 # Blast, the villain got the best of me and made their escape!
+ 0xFE 0x25 0x07 0xFC 0x01 0x25 0x1A 0x2C 0x2D 0x3F 0xF9 0xFE 0x05 0x1B 0xFC 0x13
+ 0x21 0x1E 0xF9 0x2F 0x22 0x25 0x25 0x1A 0x22 0x27 0xF9 0x20 0x28 0x2D 0xF9 0x2D
+ 0x21 0x1E 0xF9 0xFE 0x05 0x25 0x1B 0x1E 0x2C 0x2D 0xF9 0x28 0x1F 0xF9 0x26 0x1E
+ 0xF9 0x1A 0x27 0x1D 0xF9 0x26 0x1A 0x1D 0x1E 0xF9 0xFE 0x05 0x2F 0x2D 0x21 0x1E
+ 0x22 0x2B 0xF9 0x1E 0x2C 0x1C 0x1A 0x29 0x1E 0x3F 0xFF
+: failed-fight-2 # Jolly good showing, anyway. I can hear the hounds calling for them already...
+ 0xFE 0x05 0x05 0x09 0x28 0x25 0x25 0x32 0xF9 0x20 0x28 0x28 0x1D 0xF9 0x2C 0x21
+ 0x28 0x30 0x22 0x27 0x20 0x41 0xF9 0xFE 0x05 0x0F 0x1A 0x27 0x32 0x30 0x1A 0x32
+ 0x3E 0xF9 0x08 0xF9 0x1C 0x1A 0x27 0xF9 0x21 0x1E 0x1A 0x2B 0xF9 0xFE 0x05 0x19
+ 0x2D 0x21 0x1E 0xF9 0x21 0x28 0x2E 0x27 0x1D 0x2C 0xF9 0x1C 0x1A 0x25 0x25 0x22
+ 0x27 0x20 0xF9 0xFE 0x05 0x23 0x1F 0x28 0x2B 0xF9 0x2D 0x21 0x1E 0x26 0xF9 0x1A
+ 0x25 0x2B 0x1E 0x1A 0x1D 0x32 0x3E 0x3E 0x3E 0xF9 0xFE 0x24 0x31 0x13 0x07 0x04
+ 0xF9 0x04 0x0D 0x03 0xFF
+
+: fencing-registers
+ 20 103
+: fencing-registers-round
+ 32 80 0 0 0 0
+
+: fencing-ai # 8 entries { mode, timer }
+ :byte MODE_WALK_LF 10
+ :byte MODE_WALK_LF 10
+ :byte MODE_WALK_LF 10
+ :byte MODE_WALK_LF 10
+ :byte MODE_WALK_RT 6
+ :byte MODE_WALK_RT 6
+ :byte MODE_BLOCK 4
+ :byte MODE_BLOCK 4
+ :byte MODE_BLOCK 4
+
+to-code
+
+: fencing-minigame
+ clear
+ print fight-start-1
+ dialog-pause
+ print fight-start-2
+ dialog-pause
+
+ # rewrite all the enemy graphics based
+ # on who is currently the villain...
+ i := long npc-murderer
+ load v0
+ i := long fencing-enemy-table
+ i += v0
+ i += v0
+ load v1
+ i := fencing-graphics-rewrite
+ save v1
+ v4 := 0
+ loop
+ indirect fencing-graphics-rewrite
+ i += v4
+ i += v4
+ i += v4
+ i += v4
+ load v3
+ i := fencing-graphics-dest
+ save v0 - v1
+ indirect fencing-graphics-dest
+ save v2 - v3
+ v4 += 1
+ if v4 != 8 then
+ again
+
+ i := long fencing-registers
+ load vb - v4
+ i := long fencing-background
+ full-screen-blit
+ print engarde
+ wait 60
+ print engarde
+
+ draw-player-and-enemy
+ loop
+ draw-player-and-enemy
+
+ # player controls and state machine
+ if player-mode == MODE_NEUTRAL begin
+ vf := OCTO_KEY_A
+ if vf key begin
+ player-mode := MODE_WALK_LF
+ player-timer := 3
+ end
+ vf := OCTO_KEY_D
+ if vf key begin
+ player-mode := MODE_WALK_RT
+ player-timer := 3
+ end
+ vf := OCTO_KEY_W
+ if vf key begin
+ player-mode := MODE_BLOCK
+ player-timer := 10
+ end
+ vf := OCTO_KEY_E
+ if vf key begin
+ player-mode := MODE_ATTACK
+ player-timer := 6
+ # did the player hit the enemy?
+ if enemy-mode != MODE_BLOCK begin
+ v0 := enemy-fence-x
+ v0 -= player-fence-x
+ if v0 < 24 begin
+ enemy-mode := MODE_STAGGER
+ enemy-timer := 30
+ player-timer := 31
+ end
+ end
+ end
+ end
+ if player-fence-x > 0 begin
+ if player-mode == MODE_WALK_LF then player-fence-x += -1
+ end
+ if player-fence-x < 112 begin
+ if player-mode == MODE_WALK_RT then player-fence-x += 1
+ end
+ if player-mode != MODE_NEUTRAL begin
+ player-timer += -1
+ if player-timer == 0 begin
+ if player-mode == MODE_STAGGER begin
+ # done staggering, subtract health.
+ i := long fencing-heart
+ v0 := 58
+ sprite player-health v0 4
+ player-health += -8
+ if player-health == -4 then jump fencing-lose
+ i := long fencing-registers-round
+ load player-fence-x - enemy-timer
+ else
+ player-mode := MODE_NEUTRAL
+ end
+ end
+ end
+
+ # enemy AI
+ if enemy-mode == MODE_NEUTRAL begin
+ v0 := enemy-fence-x
+ v0 -= player-fence-x
+ if v0 < 26 begin
+ # if player is close, attack!
+ enemy-mode := MODE_TELL
+ enemy-timer := 4
+ else
+ # otherwise, pick randomly
+ i := long fencing-ai
+ v0 := random 0b1110
+ i += v0
+ load enemy-mode - enemy-timer
+ end
+ else
+ enemy-timer += -1
+ if enemy-timer == 0 begin
+ if enemy-mode == MODE_STAGGER begin
+ # done staggering, subtract health.
+ i := long fencing-heart
+ v0 := 58
+ sprite enemy-health v0 4
+ enemy-health += 8
+ if enemy-health == 127 then jump fencing-win
+ i := long fencing-registers-round
+ load player-fence-x - enemy-timer
+ end
+ if enemy-mode == MODE_TELL begin
+ enemy-mode := MODE_ATTACK
+ enemy-timer := 6
+ # did the enemy hit the player?
+ if player-mode != MODE_BLOCK begin
+ v0 := enemy-fence-x
+ v0 -= player-fence-x
+ if v0 < 24 begin
+ player-mode := MODE_STAGGER
+ player-timer := 30
+ enemy-timer := 31
+ end
+ end
+ else
+ enemy-mode := MODE_NEUTRAL
+ end
+ end
+ end
+ if enemy-fence-x > 0 begin
+ if enemy-mode == MODE_WALK_LF then enemy-fence-x += -1
+ end
+ if enemy-fence-x < 112 begin
+ if enemy-mode == MODE_WALK_RT then enemy-fence-x += 1
+ end
+
+ draw-player-and-enemy
+ wait 3
+ again
+
+: fencing-win
+ clear
+ print dispatched
+ dialog-pause
+ print good-end-2
+ dialog-pause
+ print good-end-3
+ jump show-rank
+
+: fencing-lose
+ clear
+ print failed-fight-1
+ dialog-pause
+ print failed-fight-2
+ jump show-rank
+
+###########################################
+#
+# Main Map
+#
+###########################################
+
+to-data
+
+:const FLOORPLAN_X 16
+:const FLOORPLAN_Y 2
+:calc FLOORPLAN_END_X { FLOORPLAN_X + 16 * 6 }
+:calc FLOORPLAN_END_Y { FLOORPLAN_Y + 16 * 3 }
+
+: empty-floorplan # 6x3 16x16 sprites, tblr
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x7F 0xFF 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00
+ 0x0F 0xFF 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00
+ 0xF8 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00
+ 0xFF 0xFF 0x04 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x04 0x00
+ 0x04 0x00 0x1C 0x00 0x20 0x00 0x10 0x00 0x08 0x00 0x04 0x00 0x04 0x00 0x04 0x00
+ 0xFF 0xFF 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00
+ 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00 0x08 0x00
+ 0xF7 0x7F 0x10 0x40 0x10 0x40 0x10 0x40 0x10 0x40 0x10 0x40 0x10 0x70 0x10 0x08
+ 0x10 0x10 0x10 0x20 0x10 0x40 0x10 0x40 0x10 0x40 0x10 0x40 0x10 0x40 0x10 0x40
+ 0xFF 0xFE 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02
+ 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02
+ 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00
+ 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x7F 0xFF 0x40 0x00 0x40 0x00
+ 0x08 0x00 0x08 0x00 0x08 0x00 0x0F 0xFF 0x08 0x00 0x08 0x00 0x10 0x00 0x20 0x00
+ 0x40 0x7F 0x38 0x40 0x08 0x40 0x08 0x20 0x08 0x10 0xF8 0x08 0x08 0x70 0x08 0x40
+ 0x04 0x08 0x04 0x14 0x04 0x24 0xFF 0xC7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0xFF 0xFF 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x04
+ 0x08 0x10 0x08 0x28 0x08 0x48 0xFF 0x8F 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0xC7 0xFF 0x24 0x04 0x14 0x04 0x08 0x04 0x00 0x04 0x00 0x07 0x00 0x04 0x00 0x07
+ 0x10 0x7F 0x10 0x40 0x10 0x40 0xF0 0x40 0x00 0x40 0x00 0x40 0x00 0x70 0x00 0x08
+ 0xF0 0x10 0x10 0x20 0x10 0x40 0x10 0x40 0x10 0x40 0xF0 0x40 0x10 0x40 0xF0 0x40
+ 0xFF 0xFE 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02
+ 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02
+ 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00
+ 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x7F 0xFF
+ 0x08 0x40 0x08 0x40 0x08 0x40 0x10 0x40 0x20 0x40 0x40 0x40 0x38 0x40 0x08 0x40
+ 0x08 0x40 0x08 0x40 0x08 0x40 0x08 0x40 0x08 0x40 0x08 0x7F 0x08 0x40 0xFB 0x40
+ 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x04 0x00 0x07 0x00 0x04 0x00 0x04
+ 0x00 0x08 0x00 0x10 0x00 0x20 0x00 0x1C 0x00 0x04 0xFF 0xFF 0x00 0x00 0x00 0x00
+ 0x00 0x04 0x00 0x07 0x00 0x04 0x00 0x07 0x00 0x04 0xFF 0xFD 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00
+ 0x10 0x7F 0xF0 0x40 0x10 0x40 0xF0 0x40 0x10 0x40 0xD0 0x40 0x00 0x40 0x00 0x40
+ 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0x00 0x40 0xFF 0xC0 0x00 0x40 0x00 0x7F
+ 0xF1 0xFE 0x09 0x02 0x05 0x02 0x02 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02
+ 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0x00 0x02 0xFF 0xFE
+
+: room-masks # 16x16 sprites
+ 0xFF 0xFC 0xFF 0xFC 0xFF 0xFC 0xFF 0xFC 0xFF 0xFC 0xFF 0xFC 0xFF 0xFC 0xFF 0xFC # library
+ 0xFF 0xFC 0xFF 0xF8 0xFF 0xF0 0xFF 0xE0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xE0
+ 0xFF 0xF0 0xFF 0xF0 0xFF 0xF0 0xFF 0xF0 0xFF 0xE0 0xFF 0xC0 0xFF 0x80 0xFF 0x80 # bathroom
+ 0xFF 0x80 0xFF 0xC0 0xFF 0xE0 0xFF 0xF0 0xFF 0xF0 0xFF 0xF0 0x00 0x00 0x00 0x00
+ 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 # bedroom
+ 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xE3 0xC0 0xC1 0xC0 0x80 0xC0 0x00 0x00 0x00 0x00
+ 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 # drawing room (same as bedroom)
+ 0xFF 0xC0 0xFF 0xC0 0xFF 0xC0 0xE3 0xC0 0xC1 0xC0 0x80 0xC0 0x00 0x00 0x00 0x00
+ 0xFF 0xFC 0xFF 0xFC 0xFF 0xF8 0xFF 0xF0 0xFF 0xE0 0xFF 0xE0 0xFF 0xE0 0xFF 0xF0 # parlor
+ 0xFF 0xF8 0xFF 0xFC 0xFF 0xFC 0xFF 0xFC 0xFF 0xFC 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x3F 0xFE 0x1F 0xFE 0x0F 0xFE 0x0F 0xFE 0x1F 0xFE 0x3F 0xFE 0x7F 0xFE 0xFF 0xFE # dining room
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFC 0xFF 0xF8 0xFF 0xF0 0xFF 0xE0
+ 0x80 0xE0 0xC0 0xE0 0xE1 0xE0 0xFF 0xE0 0xFF 0xE0 0xFF 0xE0 0xFF 0xE0 0xFF 0xE0 # study
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0xFF 0xFF 0x7F 0xFF 0x3F 0xFF 0x1F 0xFF 0x1F 0xFF 0x1F 0xFF 0x3F 0xFF 0x7F 0xFF # conservatory
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0xFF 0xFF 0x7F 0xFF 0x3F 0xFF 0x1F 0xFF 0x1F 0xFF 0x1F 0xFF 0x3F 0xFF 0x7F 0xFF # kitchen (same as conservatory)
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0xFE 0x03 0xFF 0x03 0xFF 0x87 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF # pantry
+ 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+: mask-positions # [x,y]
+ 20 13 # 0 - library
+ 39 5 # 1 - bathroom
+ 56 5 # 2 - bedroom
+ 71 5 # 3 - drawing room
+ 20 34 # 4 - parlor
+ 44 29 # 5 - dining room
+ 64 29 # 6 - study
+ 92 5 # 7 - conservatory
+ 92 21 # 8 - kitchen
+ 92 37 # 9 - pantry
+
+: room-nav # [n,e,s,w]
+ 1 1 4 0 # 0
+ 1 2 5 0 # 1
+ 2 3 5 1 # 2
+ 3 7 6 2 # 3
+ 0 5 4 4 # 4
+ 2 6 5 4 # 5
+ 3 9 6 5 # 6
+ 7 7 8 3 # 7
+ 7 8 9 6 # 8
+ 8 9 9 6 # 9
+ 10 10 10 10 # 10 (secret room)
+
+: label-library 0xFE 0x28 0x35 0x0B 0x22 0x1B 0x2B 0x1A 0x2B 0x32 0xFF
+: label-bathroom 0xFE 0x26 0x35 0x01 0x1A 0x2D 0x21 0x2B 0x28 0x28 0x26 0xFF
+: label-bedroom 0xFE 0x26 0x35 0x01 0x1E 0x1D 0x2B 0x28 0x28 0x26 0xFF
+: label-drawing-room 0xFE 0x1B 0x35 0x03 0x2B 0x1A 0x30 0x22 0x27 0x20 0xF9 0x11 0x28 0x28 0x26 0xFF
+: label-parlor 0xFE 0x2D 0x35 0x0F 0x1A 0x2B 0x25 0x28 0x2B 0xFF
+: label-dining-room 0xFE 0x1E 0x35 0x03 0x22 0x27 0x22 0x27 0x20 0xF9 0x11 0x28 0x28 0x26 0xFF
+: label-study 0xFE 0x2F 0x35 0x12 0x2D 0x2E 0x1D 0x32 0xFF
+: label-conservatory 0xFE 0x1B 0x35 0x02 0x28 0x27 0x2C 0x1E 0x2B 0x2F 0x1A 0x2D 0x28 0x2B 0x32 0xFF
+: label-kitchen 0xFE 0x29 0x35 0x0A 0x22 0x2D 0x1C 0x21 0x1E 0x27 0xFF
+: label-pantry 0xFE 0x2C 0x35 0x0F 0x1A 0x27 0x2D 0x2B 0x32 0xFF
+
+: room-label-table
+ pointer label-library
+ pointer label-bathroom
+ pointer label-bedroom
+ pointer label-drawing-room
+ pointer label-parlor
+ pointer label-dining-room
+ pointer label-study
+ pointer label-conservatory
+ pointer label-kitchen
+ pointer label-pantry
+
+to-code
+
+: draw-map-label
+ i := long room-label-table
+ i += current-room
+ i += current-room
+ load v1
+ jump print-text # TCO
+
+:macro main-map {
+ # draw the building's floorplan
+ clear
+ i := long empty-floorplan
+ v0 := FLOORPLAN_X
+ v1 := FLOORPLAN_Y
+ v2 := 32
+ loop
+ sprite v0 v1 0
+ i += v2
+ v0 += 16
+ if v0 == FLOORPLAN_END_X then v1 += 16
+ if v0 == FLOORPLAN_END_X then v0 := FLOORPLAN_X
+ if v1 != FLOORPLAN_END_Y then
+ again
+
+ # choose a new room
+ loop
+ draw-map-label
+
+ i := long mask-positions
+ i += current-room
+ i += current-room
+ load v1 - v2
+
+ i := long room-masks
+ v0 := current-room
+ vf := 32
+ loop
+ while v0 != 0
+ v0 += -1
+ i += vf
+ again
+ sprite v1 v2 0
+
+ v4 := key
+ sprite v1 v2 0
+ draw-map-label
+
+ i := long room-nav
+ i += current-room
+ i += current-room
+ i += current-room
+ i += current-room
+ load v3
+
+ if v4 == OCTO_KEY_W then current-room := v0
+ if v4 == OCTO_KEY_D then current-room := v1
+ if v4 == OCTO_KEY_S then current-room := v2
+ if v4 == OCTO_KEY_A then current-room := v3
+
+ if v4 != OCTO_KEY_E then
+ again
+}
+
+###########################################
+#
+# Entrypoint
+#
+###########################################
+
+: main
+ hires
+ loop
+ intro-sequence
+ loop
+ main-map
+ thicken-plot SUSPICION_ENTER_ROOM
+ room
+ while current-room != GAME_OVER
+ describe-ambience
+ again
+ again
diff --git a/resources/octoroms/outlaw.8o b/resources/octoroms/outlaw.8o
new file mode 100644
index 0000000..fcf6aad
--- /dev/null
+++ b/resources/octoroms/outlaw.8o
@@ -0,0 +1,295 @@
+###########################################
+#
+# Outlaw
+#
+# An adaptation of the Atari 2600 game
+# "Outlaw" for the Chip8.
+#
+# Move with ASWD and fire with E.
+# Holding a direction while firing will
+# fire an angled shot, which ricochets.
+# Plays fairly well at 30 cycles/frame
+#
+###########################################
+
+: cactus 0x38 0x38 0x3B 0x1B 0x1B 0x1B 0xDF 0xDE 0xD8 0xD8 0xF8 0x78 0x18 0x1C 0x0C
+: left 0x00 0x00 0x70 0xF8 0x70 0x67 0x7C 0x60 0x60 0x78 0x28 0xEC
+: lwalk1 0x18 0x3E 0x1C 0x18 0x7E 0x99 0x99 0x99 0x5A 0x3C 0x66 0xC3
+: lwalk2 0x18 0x3E 0x1C 0x18 0x7E 0x99 0x99 0x99 0x5A 0x3C 0x24 0x36
+: right 0x00 0x00 0x06 0x1F 0x0E 0xE6 0x3E 0x06 0x06 0x1E 0x14 0x37
+: rwalk1 0x18 0x7C 0x38 0x18 0x7E 0x99 0x99 0x99 0x5A 0x3C 0x66 0xC3
+: rwalk2 0x18 0x7C 0x38 0x18 0x7E 0x99 0x99 0x99 0x5A 0x3C 0x24 0x6C
+: edge 0xFF 0xFF
+: bullet 0x80
+
+###########################################
+#
+# Register map
+#
+###########################################
+
+:alias leftx ve
+:alias lefty vd
+:alias leftframe vc
+:alias leftbx vb
+:alias leftby va
+:alias leftfire v9
+
+:alias rightx v8
+:alias righty v7
+:alias rightframe v6
+:alias rightbx v5
+:alias rightby v4
+:alias rightfire v3
+
+:const FIRE_ST 1
+:const FIRE_UP 2
+:const FIRE_DN 3
+
+# v0-v2 and vf are left as temporaries
+
+###########################################
+#
+# Bullets
+#
+###########################################
+
+: fire-left-bullet
+ v0 := 6
+ if v0 -key then return
+ leftfire := FIRE_ST
+ v0 := 5
+ if v0 key then leftfire := FIRE_UP
+ v0 := 8
+ if v0 key then leftfire := FIRE_DN
+
+ # spawn bullet
+ leftbx := leftx
+ leftbx += 9
+ leftby := lefty
+ leftby += 5
+
+ # draw initial bullet frame
+ i := bullet
+ sprite leftbx leftby 1
+;
+
+: move-left-bullet
+ sprite leftbx leftby 1
+ leftbx += 1
+ if leftfire == FIRE_UP then leftby += -1
+ if leftfire == FIRE_DN then leftby += 1
+
+ # bounce off top and bottom edge
+ if leftby == 1 then leftfire := FIRE_DN
+ if leftby == 30 then leftfire := FIRE_UP
+
+ # despawn on right edge
+ if leftbx == 63 then leftfire := 0
+ if leftfire == 0 then return
+ sprite leftbx leftby 1
+
+ # collide with obstacles
+ if vf == 0 then return
+ leftfire := 0
+;
+
+: fire-right-bullet
+ if rightfire != 0 then return
+ rightfire := FIRE_ST
+ v0 := random 0b11
+ if v0 == 1 then rightfire := FIRE_UP
+ if v0 == 2 then rightfire := FIRE_DN
+
+ # spawn bullet
+ rightbx := rightx
+ rightbx += -1
+ rightby := righty
+ rightby += 5
+
+ # draw initial bullet frame
+ i := bullet
+ sprite rightbx rightby 1
+;
+
+: move-right-bullet
+ sprite rightbx rightby 1
+ rightbx += -1
+ if rightfire == FIRE_UP then rightby += -1
+ if rightfire == FIRE_DN then rightby += 1
+
+ # bounce off top and bottom edge
+ if rightby == 1 then rightfire := FIRE_DN
+ if rightby == 30 then rightfire := FIRE_UP
+
+ # despawn on right edge
+ if rightbx == 0 then rightfire := 0
+ if rightfire == 0 then return
+ sprite rightbx rightby 1
+
+ # collide with obstacles
+ if vf == 0 then return
+ rightfire := 0
+;
+
+###########################################
+#
+# Gunslingers
+#
+###########################################
+
+: move-left-player
+ v1 := leftx
+ v2 := lefty
+
+ # process movement keys
+ v0 := 7
+ if v0 key then v1 += -1
+ v0 := 9
+ if v0 key then v1 += 1
+ v0 := 5
+ if v0 key then v2 += -1
+ v0 := 8
+ if v0 key then v2 += 1
+
+ # animate player movement
+ v0 := 0
+ if v1 != leftx then v0 := 24
+ if v2 != lefty then v0 := 24
+ if leftframe == 24 then v0 := 12
+ vf := 6
+ if vf key then v0 := 0
+
+ # clamp position within left side
+ if v1 == 0 then v1 := 1
+ if v1 == 21 then v1 := 20
+ if v2 == 0 then v2 := 1
+ if v2 == 18 then v2 := 17
+
+ # update position
+ i := left
+ i += leftframe
+ sprite leftx lefty 12
+ leftx := v1
+ lefty := v2
+ leftframe := v0
+ i := left
+ i += leftframe
+ sprite leftx lefty 12
+;
+
+: brain-table
+ v1 += -1 return
+ v1 += 1 return
+ v2 += -1 return # vertical movement is 2x as likely
+ v2 += -1 return # as horizontal movement
+ v2 += 1 return
+ v2 += 1 return
+ v0 := v0 return # do nothing
+ fire-right-bullet return
+: do-brain
+ v0 := random 0b11100 # entries are 4 bytes each
+ jump0 brain-table
+
+: move-right-player
+ v1 := rightx
+ v2 := righty
+
+ # process (random) movement
+ do-brain
+
+ # animate movement
+ v0 := 0
+ if v1 != rightx then v0 := 24
+ if v2 != righty then v0 := 24
+ if rightframe == 24 then v0 := 12
+
+ # clamp position within right side
+ if v1 == 35 then v1 := 36
+ if v1 == 56 then v1 := 55
+ if v2 == 0 then v2 := 1
+ if v2 == 18 then v2 := 17
+
+ # update position
+ i := right
+ i += rightframe
+ sprite rightx righty 12
+ rightx := v1
+ righty := v2
+ rightframe := v0
+ i := right
+ i += rightframe
+ sprite rightx righty 12
+;
+
+###########################################
+#
+# Main
+#
+###########################################
+
+: rightdead
+ sprite rightx righty 12
+ i := bullet
+ sprite leftbx leftby 1
+
+: gameover
+ v0 := 32
+ buzzer := v0
+ delay := v0
+ loop
+ v0 := delay
+ if v0 != 0 then
+ again
+ jump main
+
+: leftdead
+ sprite leftx lefty 12
+ i := bullet
+ sprite rightbx rightby 1
+ jump gameover
+
+: main
+ # initialize state
+ leftx := 5
+ lefty := 10
+ leftframe := 0
+ rightx := 51
+ righty := 10
+ rightframe := 0
+ leftfire := 0
+ rightfire := 0
+
+ # draw background
+ clear
+ i := cactus
+ v0 := 28
+ v1 := 9
+ sprite v0 v1 15
+
+ i := edge
+ v0 := 0
+ v1 := 31
+ loop
+ sprite v0 v1 2
+ v0 += 8
+ if v0 != 64 then
+ again
+
+ i := left
+ sprite leftx lefty 12
+ i := right
+ sprite rightx righty 12
+
+ # main game loop
+ loop
+ move-left-player
+ if vf != 0 then jump leftdead
+ move-right-player
+ if vf != 0 then jump rightdead
+
+ i := bullet
+ if leftfire != 0 then move-left-bullet
+ if leftfire == 0 then fire-left-bullet
+ if rightfire != 0 then move-right-bullet
+ again
diff --git a/resources/octoroms/sinusoid.8o b/resources/octoroms/sinusoid.8o
new file mode 100644
index 0000000..2d20452
--- /dev/null
+++ b/resources/octoroms/sinusoid.8o
@@ -0,0 +1,39 @@
+###########################################
+#
+# Sinusoid
+#
+# Demonstrate :calc and :macro by computing
+# a sinusoidal lookup table at compile time.
+#
+# John Earnest
+#
+###########################################
+
+: main
+ hires
+ loop
+ scroll-down 2
+ i := table
+ i += v2
+ load v1
+ i := thin
+ sprite v0 v3 2
+ i := thick
+ sprite v1 v3 2
+ v2 += 2
+ again
+
+: thin 0x0F 0x0F
+: thick 0xFF 0xFF
+
+: table
+ :macro S2 {
+ :calc rads { ( HERE - table ) * ( 2 * PI ) / 256 }
+ :byte { 56 + 38 * sin rads }
+ :byte { 56 + 16 * cos rads }
+ }
+
+ # expand S2 2*8*8 times for a 256-byte table:
+ :macro S1 { S2 S2 S2 S2 S2 S2 S2 S2 }
+ :macro S0 { S1 S1 S1 S1 S1 S1 S1 S1 }
+ S0 S0
diff --git a/resources/octoroms/slippery.8o b/resources/octoroms/slippery.8o
new file mode 100644
index 0000000..55064d3
--- /dev/null
+++ b/resources/octoroms/slippery.8o
@@ -0,0 +1,805 @@
+###########################################
+#
+# Slippery Slope
+#
+# built for Octojam 2018.
+# level editor: http://beyondloom.com/tools/slipedit.html
+#
+# John Earnest
+#
+###########################################
+
+:const MODE-INPUT 0
+:const MODE-MOVE 1
+:const MODE-NEXT 2
+:const MODE-RESET 3
+
+:const FINAL_LEVEL 0xA
+
+# v0-v2 are reserved as temporaries.
+:alias px vD # player x position (tiles)
+:alias py vC # player y position (tiles)
+:alias pdir vB # player direction ( E N W S )
+:alias pmode vA # see MODE-XXX
+:alias gx v9 # goal x position
+:alias gy v8 # goal y position
+:alias gf v7 # goal frame (0,5,10,15)
+:alias level v6 # current level no.
+
+:alias move-dx v5 # x amount player moved this step
+:alias move-dy v4 # y amount player moved this step
+:alias move-dir v3 # temporary direction
+
+:alias load-ti vD # tile index
+:alias load-tx vC # x position
+:alias load-ty vB # y position
+
+:alias copy-off vE # when unpacking, offset
+:alias copy-lvl vD # when unpacking, level index
+
+###########################################
+#
+# Utilities
+#
+###########################################
+
+:macro temp-begin { :calc there { HERE } :org 0 }
+:macro temp-end { :org there }
+:macro xor_ { :byte { ( @ a + HERE - to ) ^ @ b + HERE - to } }
+:macro xor A B { :calc to { HERE } :calc a { A } :calc b { B }
+ xor_ xor_ xor_ xor_ xor_ }
+
+:macro sync N {
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+ vf := N
+ delay := vf
+}
+:macro copy-2 SRC DST {
+ i := SRC load v1
+ i := DST save v1
+}
+:macro times-5 SRC DST {
+ DST := SRC
+ DST <<= DST
+ DST <<= DST
+ DST += SRC
+}
+:macro times-6 SRC DST {
+ DST := SRC
+ DST += SRC
+ DST += SRC
+ DST += DST
+}
+:macro to-tile SX SY DX DY {
+ times-5 SX DX DX += 2
+ times-5 SY DY DY += 1
+}
+
+###########################################
+#
+# Step Counter
+#
+###########################################
+
+: step-total 0 0 # high 2 digits, low 2 digits
+: step-level 0 0
+: step-bcd 0 0 0
+
+:macro steps-clear {
+ v0 := 0
+ v1 := 0
+ i := step-total
+ save v1
+ i := step-level
+ save v1
+}
+
+:macro steps-inc {
+ i := step-level
+ load v1
+ v1 += 1
+ if v1 == 100 begin
+ v0 += 1
+ v1 := 0
+ end
+ if v0 == 100 begin
+ # cap at 9999
+ v1 := 99
+ v0 := 99
+ end
+ i := step-level
+ save v1
+}
+
+:macro steps-reset-level { copy-2 step-total step-level }
+:macro steps-next-level { copy-2 step-level step-total }
+
+:macro steps-show-digits SRC {
+ i := SRC
+ load v0
+ i := step-bcd
+ bcd v0
+ load v2
+ i := hex v1
+ sprite v3 v4 5
+ v3 += 5
+ i := hex v2
+ sprite v3 v4 5
+ v3 += 5
+}
+:macro steps-show { # at v3,v4
+ steps-show-digits step-level
+ :calc low-digits { 1 + step-level }
+ steps-show-digits low-digits
+}
+
+###########################################
+#
+# Level Representation
+#
+###########################################
+
+:const board-rows 6
+:const board-cols 12
+:calc board-size { board-rows * board-cols }
+: board
+ 00 00 00 00 00 00
+ 00 00 00 05 00 00
+ 00 00 05 00 05 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+
+: goal-position 10 03 # (x tiles, y tiles)
+: start-position 02 03 # (x tiles, y tiles)
+
+: level-unpack-stash
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+
+:macro level-base-addr {
+ v0 := copy-lvl
+ i := level-data
+ loop
+ while v0 != 1
+ v0 += -1
+ vf := 76
+ i += vf
+ again
+}
+: level-unpack
+ # we need most of the registers for bulk copying,
+ # so stash the entire register file in a buffer:
+ vf := level
+ i := level-unpack-stash
+ save vf
+
+ # copy tile data
+ copy-off := 0 # source/dest offset
+ copy-lvl := vf # level index
+ loop
+ level-base-addr
+ i += copy-off
+ load vb
+
+ i := board
+ i += copy-off
+ save vb
+
+ copy-off += 12
+ if copy-off != 72 then
+ again
+
+ # copy goal/start positions
+ level-base-addr
+ i += copy-off
+ load v3
+ i := goal-position
+ save v3
+
+ i := level-unpack-stash
+ load vf
+;
+
+###########################################
+#
+# Text
+#
+###########################################
+
+: let-A 0xF0 0xB0 0xF0 0xB0
+: let-D 0xE0 0xB0 0xB0 0xE0
+: let-E 0xF0 0xE0 0xC0 0xF0
+: let-G 0xF0 0xC0 0xD0 0xF0
+: let-H 0xB0 0xF0 0xB0 0xB0
+: let-I 0xC0 0x00 0xC0 0xC0
+: let-L 0xC0 0xC0 0xC0 0xE0
+: let-M 0xD8 0xE8 0xC8 0xC8
+: let-N 0xE0 0xF0 0xD0 0xD0
+: let-O 0xF0 0xB0 0xB0 0xF0
+: let-P 0xF0 0xB0 0xF0 0x80
+: let-R 0xF0 0xF0 0xA0 0x90
+: let-S 0xF0 0xE0 0x30 0xF0
+: let-T 0xF0 0x60 0x60 0x60
+: let-V 0xB0 0xB0 0xB0 0x60
+: let-Y 0xB0 0xF0 0x30 0xF0
+
+:stringmode print " " {
+ v0 += 4
+}
+:stringmode print "ADEGHILMNOPRSTVY" {
+ :calc S { let-A + VALUE * 4 }
+ i := S
+ sprite v0 v1 4
+ # compute the width of each glyph statically:
+ :calc p { ( @ S ) | ( @ 1 + S ) | ( @ 2 + S ) | ( @ 3 + S ) }
+ :calc w { 9 - ( log p & - p ) / log 2 }
+ v0 += w
+}
+
+:macro draw-title {
+ v0 := 14
+ v1 := 4
+ print "SLIPPERY"
+ v0 := 20
+ v1 := 24
+ print "SLOPE"
+}
+
+:macro draw-gameover {
+ v0 := 15
+ v1 := 12
+ print "THE END"
+ v3 := 8
+ v4 := 20
+ steps-show
+ v0 := v3
+ v1 := v4
+ print " STEPS"
+}
+
+:macro draw-levelno {
+ v0 := 17
+ v1 := 12
+ print "LEVEL "
+ i := hex level
+ sprite v0 v1 5
+}
+
+###########################################
+#
+# Player And Movement
+#
+###########################################
+
+: stand 0x20 0x70 0x70 0x30 0x20
+ 0x20 0x70 0x70 0x70 0x50
+ 0x20 0x70 0x70 0x60 0x20
+ 0x20 0x70 0xF8 0x70 0x50
+: slide 0x20 0x38 0x30 0x38 0x48
+ 0x20 0xF0 0x78 0xB0 0x90
+ 0x20 0xE0 0x60 0xE0 0x90
+ 0x20 0xF8 0x70 0x70 0x48
+
+:macro draw-player {
+ times-5 pdir v0
+ i += v0
+ to-tile px py v0 v1
+ sprite v0 v1 5
+}
+:macro draw-stand { i := stand draw-player }
+:macro draw-slide { i := slide draw-player }
+
+: deltas 1 0 0 -1 -1 0 0 1
+: dirs-\ 3 2 1 0
+: dirs-/ 1 0 3 2
+
+:macro at-edge REG DELTA VAL {
+ if REG == VAL begin
+ REG -= DELTA
+ pmode := MODE-INPUT
+ end
+}
+:macro get-current-tile {
+ times-6 px v0
+ v0 += py
+ i := board
+ i += v0
+ load v0
+ v1 := v0
+}
+:macro set-current-tile {
+ times-6 px v0
+ v0 += py
+ i := board
+ i += v0
+ v0 := v1
+ save v0
+}
+:macro draw-current-tile {
+ i := tiles
+ i += v1
+ times-5 px v0
+ times-5 py v1
+ v0 += 2
+ v1 += 1
+ sprite v0 v1 5
+}
+:macro change-dir TABLE {
+ i := TABLE
+ i += pdir
+ load v0
+ pdir := v0
+}
+:macro move-player {
+ loop
+ # move in facing direction
+ i := deltas
+ i += pdir
+ i += pdir
+
+ load v1
+ move-dx := v0
+ move-dy := v1
+ px += move-dx
+ py += move-dy
+
+ # out of bounds?
+ at-edge px move-dx -1
+ at-edge px move-dx 12
+ at-edge py move-dy -1
+ at-edge py move-dy 6
+
+ # at goal?
+ to-tile px py v0 v1
+ v0 ^= gx
+ v1 ^= gy
+ v0 |= v1
+ if v0 == 0 then pmode := MODE-NEXT
+
+ # check current cell (into v1)
+ get-current-tile
+
+ if v1 == tile-wall begin
+ px -= move-dx
+ py -= move-dy
+ pmode := MODE-INPUT
+ end
+
+ if v1 == tile-hole begin
+ pmode := MODE-RESET
+ end
+
+ if v1 == tile-mir-\ begin
+ change-dir dirs-\
+ end
+
+ if v1 == tile-mir-/ begin
+ change-dir dirs-/
+ end
+
+ if v1 == tile-flip-\ begin
+ change-dir dirs-\
+ draw-current-tile
+ v1 := tile-flop-/
+ set-current-tile
+ draw-current-tile
+ v1 := 0 # avoid fallthrough!
+ end
+
+ if v1 == tile-flop-/ begin
+ change-dir dirs-/
+ draw-current-tile
+ v1 := tile-flip-\
+ set-current-tile
+ draw-current-tile
+ v1 := 0 # avoid fallthrough!
+ end
+
+ if v1 >= tile-ground begin
+ draw-current-tile
+ pmode := MODE-INPUT
+ end
+
+ while pmode == MODE-MOVE
+ draw-slide
+ animate-goal
+ sync 5
+ draw-slide
+ again
+}
+
+:macro poll-key KEY DIR {
+ vf := KEY if vf key begin
+ pmode := MODE-MOVE
+ move-dir := DIR
+ end
+}
+:macro poll-player {
+ poll-key OCTO_KEY_D 0
+ poll-key OCTO_KEY_W 1
+ poll-key OCTO_KEY_A 2
+ poll-key OCTO_KEY_S 3
+ if pmode == MODE-MOVE begin
+ steps-inc
+ draw-stand
+ get-current-tile
+ if v1 >= tile-ground begin
+ draw-current-tile
+ end
+ pdir := move-dir
+ move-player
+ draw-stand
+ end
+ if level != 0 begin
+ vf := OCTO_KEY_E
+ if vf key then pmode := MODE-RESET
+ end
+}
+
+###########################################
+#
+# Level Display
+#
+###########################################
+
+: line 0xFF # top and bottom edge
+: corner 0x80 # 1px
+: wall 0xC0 0xC0 0xC0 0xC0 0xC0 # 2x15 pixels
+ 0xC0 0xC0 0xC0 0xC0 0xC0
+ 0xC0 0xC0 0xC0 0xC0 0xC0
+
+:macro draw-border {
+ i := wall
+ v0 := 0
+ v1 := 62
+ v2 := 1
+ sprite v0 v2 15
+ sprite v1 v2 15
+ v2 := 16
+ sprite v0 v2 15
+ sprite v1 v2 15
+
+ i := line
+ v1 := 0
+ v2 := 31
+ loop
+ sprite v0 v1 1
+ sprite v0 v2 1
+ v0 += 8
+ if v0 != 64 then
+ again
+
+ i := corner
+ v0 := 2
+ v1 := 61
+ v2 := 1
+ sprite v0 v2 1
+ sprite v1 v2 1
+ v2 := 30
+ sprite v0 v2 1
+ sprite v1 v2 1
+}
+
+temp-begin
+: tgoal-1 0x50 0x80 0x08 0x80 0x50
+: tgoal-2 0x50 0x88 0x00 0x88 0x20
+: tgoal-3 0x50 0x08 0x80 0x08 0x50
+temp-end
+: goal-0 0x20 0x88 0x00 0x88 0x50
+: goal-1 xor goal-0 tgoal-1
+: goal-2 xor tgoal-1 tgoal-2
+: goal-3 xor tgoal-2 tgoal-3
+: goal-4 xor tgoal-3 goal-0
+
+:macro init-goal {
+ gf := 0
+ i := goal-0
+ sprite gx gy 5
+}
+
+:macro animate-goal {
+ i := goal-1
+ i += gf
+ sprite gx gy 5
+ gf += 5
+ if gf == 20 then gf := 0
+}
+
+: tiles 0x00 0x00 0x00 0x00 0x00 # empty space
+ 0xF8 0xF8 0xF8 0xF8 0xF8 :const tile-wall 5
+ 0x88 0x50 0x20 0x50 0x88 :const tile-hole 10
+ 0x80 0x40 0x20 0x10 0x08 :const tile-mir-\ 15
+ 0x08 0x10 0x20 0x40 0x80 :const tile-mir-/ 20
+ 0x80 0x60 0x50 0x30 0x08 :const tile-flip-\ 25
+ 0x08 0x30 0x70 0x60 0x80 :const tile-flop-/ 30
+ 0x50 0x88 0x88 0x88 0x70 :const tile-ground 35
+ 0x58 0x80 0x80 0x80 0x78
+ 0x88 0x88 0x88 0x88 0x70
+ 0x80 0x80 0x80 0x80 0x78
+ 0x50 0x08 0x08 0x08 0xF0
+ 0x58 0x00 0x00 0x00 0xF8
+ 0x08 0x08 0x08 0x08 0xF0
+ 0x00 0x00 0x00 0x00 0xF8
+ 0x50 0x88 0x88 0x88 0x88
+ 0x58 0x80 0x80 0x80 0x80
+ 0x88 0x88 0x88 0x88 0x88
+ 0x80 0x80 0x80 0x80 0x80
+ 0x50 0x08 0x08 0x08 0x08
+ 0x58 0x00 0x00 0x00 0x00
+ 0x08 0x08 0x08 0x08 0x08
+ 0x00 0x00 0x00 0x00 0x00
+
+:macro load-tile REG Y {
+ i := tiles
+ i += REG
+ load-ty := Y
+ sprite load-tx load-ty 5
+}
+: load-level
+ clear
+ draw-border
+ load-ti := 0
+ load-tx := 2
+ loop
+ i := board
+ i += load-ti
+ load v5
+
+ load-tile v0 1
+ load-tile v1 6
+ load-tile v2 11
+ load-tile v3 16
+ load-tile v4 21
+ load-tile v5 26
+
+ load-tx += 5
+ load-ti += board-rows
+ if load-ti != board-size then
+ again
+ i := goal-position
+ load v3
+ to-tile v0 v1 gx gy
+ init-goal
+
+ px := v2
+ py := v3
+ pdir := 3
+ pmode := MODE-INPUT
+ draw-stand
+;
+
+:macro try-key KEY {
+ vf := KEY
+ while vf -key
+}
+:macro wait-key {
+ v4 := random 3
+ i := deltas
+ i += v4
+ i += v4
+ load v1
+ v0 += v0
+ v2 := random 63
+ v3 := random 31
+ i := slide
+ i += v4
+ i += v4
+ i += v4
+ i += v4
+ i += v4
+ sprite v2 v3 5
+ loop
+ sprite v2 v3 5
+ v2 += v0
+ v3 += v1
+ sprite v2 v3 5
+ try-key OCTO_KEY_E
+ try-key OCTO_KEY_A
+ try-key OCTO_KEY_S
+ try-key OCTO_KEY_W
+ try-key OCTO_KEY_D
+ again
+}
+
+###########################################
+#
+# Main
+#
+###########################################
+
+: main
+ load-level
+ draw-title
+
+ loop
+ loop
+ poll-player
+ while pmode == MODE-INPUT
+ animate-goal
+ sync 5
+ again
+
+ if pmode == MODE-RESET begin
+ steps-reset-level
+ level-unpack
+ load-level
+ end
+
+ if pmode == MODE-NEXT begin
+ clear
+ if level == FINAL_LEVEL begin
+ level := 1
+ draw-gameover
+ steps-clear
+ steps-inc # start screen takes 1 move
+ else
+ level += 1
+ draw-levelno
+ end
+ steps-next-level
+ level-unpack
+ wait-key
+ load-level
+ end
+ again
+
+###########################################
+#
+# Level Data
+#
+###########################################
+
+: level-data
+: level-1 # just walls and sliding
+ 05 05 05 00 00 00
+ 05 00 00 00 00 00
+ 05 00 00 00 00 00
+ 00 00 05 00 00 05
+ 00 00 05 00 00 05
+ 05 05 05 00 00 05
+ 00 00 00 00 00 05
+ 00 00 00 05 05 05
+ 00 00 00 00 00 05
+ 05 00 00 00 00 00
+ 05 00 00 00 05 00
+ 05 05 05 05 05 00
+ 10 03 04 01
+: level-2 # more walls and sliding
+ 00 00 00 00 00 05
+ 00 05 00 00 00 05
+ 05 00 00 00 00 05
+ 05 00 00 00 05 05
+ 05 05 00 00 05 05
+ 00 00 00 00 00 05
+ 05 05 00 00 00 05
+ 05 00 00 00 00 05
+ 05 00 00 00 05 05
+ 05 00 00 00 00 05
+ 00 00 00 00 00 05
+ 00 00 00 00 05 05
+ 05 00 01 04
+
+: level-3 # introduce mirrors
+ 05 05 05 05 05 05
+ 05 05 00 00 00 05
+ 05 05 00 00 00 05
+ 05 00 00 00 00 05
+ 05 00 00 20 00 00
+ 05 00 00 00 00 00
+ 05 05 00 00 00 05
+ 05 05 05 00 05 05
+ 05 05 00 00 00 05
+ 05 05 00 00 00 05
+ 05 05 00 00 00 05
+ 05 05 05 05 05 05
+ 09 03 02 03
+: level-4 # many mirrors
+ 00 00 00 00 00 00
+ 00 00 00 00 00 20
+ 15 00 00 00 00 00
+ 00 05 00 00 00 00
+ 00 00 00 00 15 00
+ 00 20 00 00 00 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ 15 00 00 05 00 20
+ 00 15 00 00 00 00
+ 00 00 00 00 00 00
+ 00 00 00 15 00 00
+ 10 03 01 01
+: level-5 # mild mirror maze
+ 00 20 00 00 15 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 20
+ 15 00 00 00 00 00
+ 20 00 00 00 00 00
+ 15 00 15 00 00 20
+ 20 20 15 20 00 15
+ 15 00 00 00 00 20
+ 20 20 00 00 00 15
+ 15 00 00 15 00 20
+ 10 20 15 00 00 15
+ 00 20 15 20 15 20
+ 11 00 00 02
+
+: level-6 # introduce ground
+ 10 10 10 10 10 10
+ 10 00 00 00 00 10
+ 10 00 00 00 00 10
+ 10 00 00 00 00 10
+ 10 00 00 00 00 10
+ 10 00 80 50 00 10
+ 10 00 95 65 00 10
+ 10 00 00 00 00 10
+ 10 00 00 00 00 10
+ 10 00 00 00 00 10
+ 10 00 00 00 00 10
+ 10 10 10 10 10 10
+ 09 03 02 02
+: level-7 # good clean fun with ground + mirrors
+ 00 00 00 55 00 00
+ 00 00 00 00 00 05
+ 05 15 00 00 00 00
+ 00 00 00 20 00 75
+ 00 00 00 00 00 00
+ 05 05 05 00 00 00
+ 05 05 05 00 20 00
+ 05 20 00 00 20 00
+ 00 00 00 15 00 00
+ 00 00 00 00 00 00
+ 00 00 00 00 00 00
+ 90 50 00 10 00 20
+ 03 00 08 00
+
+: level-8 # introduce flip-flops
+ 05 05 05 05 05 05
+ 05 05 05 05 05 05
+ 05 05 05 00 05 05
+ 05 05 05 00 05 05
+ 05 00 00 30 00 00
+ 05 05 00 00 05 05
+ 05 05 00 00 05 05
+ 00 00 25 00 00 05
+ 05 05 00 05 05 05
+ 05 05 00 05 05 05
+ 05 05 05 05 05 05
+ 05 05 05 05 05 05
+ 09 02 02 03
+: level-9 # mirror maze with a few flip-flops
+ 15 20 00 00 20 15
+ 00 00 15 00 35 00
+ 20 00 00 00 00 20
+ 00 15 00 00 00 15
+ 00 30 00 20 00 00
+ 45 00 00 00 00 20
+ 00 20 15 20 00 00
+ 00 00 00 00 25 00
+ 20 00 20 00 00 20
+ 00 00 00 20 35 00
+ 00 00 00 00 15 00
+ 20 15 00 20 00 20
+ 02 02 10 01
+: level-A # many flip-flops
+ 00 00 10 05 05 00
+ 00 00 00 10 05 00
+ 05 05 00 00 00 00
+ 50 00 25 00 00 20
+ 70 00 30 00 00 15
+ 70 00 30 30 00 20
+ 70 00 30 30 00 15
+ 70 00 25 00 00 20
+ 70 00 30 25 00 15
+ 65 00 30 00 00 20
+ 00 00 05 00 00 00
+ 00 00 05 00 00 00
+ 00 05 11 01
diff --git a/resources/octoroms/snowdaze.8o b/resources/octoroms/snowdaze.8o
new file mode 100644
index 0000000..a9029dd
--- /dev/null
+++ b/resources/octoroms/snowdaze.8o
@@ -0,0 +1,382 @@
+###########################################
+#
+# Snow Daze
+#
+# Clear all the snow off your driveway
+# before you're late for work!
+# Press ASWD to move around the driveway
+# and press E to shovel snow behind you.
+# Runs best at 100 cycles/frame.
+#
+# Created for the 2015 CCP Game Jam
+# Theme: Winter
+#
+###########################################
+
+:alias px va
+:alias py vb
+:alias dir vc
+:alias frame vd
+:alias timer v9
+:alias timerx v8
+:alias endflag v7
+
+:const LEFT 45
+:const RIGHT 0
+:const TICK_TIME 8
+:const SNOWFALL 8
+
+: draw-image
+ v0 := 0
+ v1 := 0
+ v2 := 32
+ loop
+ sprite v0 v1 0
+ i += v2
+ v0 += 16
+ if v0 == 128 begin
+ v0 := 0
+ v1 += 16
+ end
+ if v1 != 64 then
+ again
+;
+
+: draw-player
+ i := person-r
+ i += dir
+ i += frame
+ sprite px py 15
+;
+
+: do-snow
+ i := snow
+ v3 := 0
+ loop
+ v1 := random 0b111111
+ v1 += 32
+ v2 := random 0b11111
+ v2 += 16
+ sprite v1 v2 15
+ v0 := 5 wait
+ v3 += 1
+ if v3 != SNOWFALL then
+ again
+;
+
+: do-scoop
+ frame := 15
+ draw-player
+ v0 := 5 gamewait
+ draw-player
+ frame := 30
+ draw-player
+ v0 := 5 gamewait
+ draw-player
+ frame := 0
+
+ v1 := py
+ i := snow
+
+ :macro scoop DELTA1 DELTA2 {
+ v0 := px
+ v0 += DELTA1
+ sprite v0 v1 15
+ if vf == 0 begin
+ sprite v0 v1 15
+ else
+ v0 += DELTA2
+ sprite v0 v1 15
+ if vf == 1 then sprite v0 v1 15
+ end
+ }
+ if dir == LEFT begin
+ scoop -8 16
+ else
+ scoop 8 -16
+ end
+;
+
+: move-player
+ vf := 5 if vf key begin
+ if py != 10 then py += -1
+ end
+ vf := 8 if vf key begin
+ if py != 0x31 then py += 1
+ end
+ vf := 7 if vf key begin
+ px += -1
+ dir := LEFT
+ end
+ vf := 9 if vf key begin
+ px += 1
+ dir := RIGHT
+ end
+ vf := 6 if vf key then do-scoop
+;
+
+: countdown
+ if timer == 0 begin
+ timer := TICK_TIME
+ vf := 4
+ i := timeslice
+ sprite timerx vf 3
+ timerx += -1
+ if timerx == 3 then endflag := 1
+ else
+ timer += -1
+ end
+;
+
+: gamewait
+ loop
+ sync
+ countdown
+ v0 += -1
+ if v0 != 0 then
+ again
+;
+
+: wait
+ loop
+ sync
+ v0 += -1
+ if v0 != 0 then
+ again
+;
+
+: game-over
+ draw-player
+ v0 := 20 wait
+
+ v1 := 32
+ v2 := 15
+ v3 := 0
+ i := block
+ loop
+ sprite v1 v2 0
+ if vf != 0 then v3 += 1
+ v0 := 5 wait
+ sprite v1 v2 0
+ v1 += 16
+ if v1 == 96 begin
+ v1 := 32
+ v2 += 16
+ end
+ if v2 != 63 then
+ again
+ i := bighex v3
+ v1 := 60
+ v2 := 14
+ sprite v1 v2 10
+
+ v0 := 20 wait
+ v0 := key
+ setup
+;
+
+: sync
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+ vf := 2
+ delay := vf
+;
+
+: intro-num
+ i := bighex v0
+ v1 := 60
+ v2 := 14
+ sprite v1 v2 10
+ v0 := 10 wait
+ sprite v1 v2 10
+;
+
+: setup
+ clear
+ i := background
+ draw-image
+ timerx := 123
+ timer := 0
+ px := 64
+ py := 32
+ endflag := 0
+ draw-player
+ do-snow
+ v0 := 10 wait
+ v0 := 3 intro-num
+ v0 := 2 intro-num
+ v0 := 1 intro-num
+;
+
+: main
+ hires
+ setup
+ loop
+ draw-player
+ move-player
+ draw-player
+ countdown
+ if endflag == 1 then game-over
+ sync
+ again
+
+: timeslice
+ 0x80 0x80 0x80
+: block
+ 0x00 0x00 0x7F 0xFE 0x7F 0xFE 0x7F 0xFE
+ 0x7F 0xFE 0x7F 0xFE 0x7F 0xFE 0x7F 0xFE
+ 0x7F 0xFE 0x7F 0xFE 0x7F 0xFE 0x7F 0xFE
+ 0x7F 0xFE 0x7F 0xFE 0x7F 0xFE 0x00 0x00
+: snow
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x7E 0x7E 0x7E 0x00
+: person-r
+ 0x38 0x38 0x28 0x3C 0x78 0x70 0x78 0x78
+ 0xF8 0xF8 0xFB 0x7F 0x4F 0x4B 0x6C
+: scoop1-r
+ 0x00 0x1C 0x1C 0x14 0x1E 0x7C 0xF8 0xF8
+ 0xFC 0xFC 0xFC 0x7F 0x4F 0x4F 0x6F
+: scoop2-r
+ 0xC0 0xCE 0x4E 0x6A 0x7F 0xFE 0x78 0x7E
+ 0x7C 0xF8 0xF8 0xF8 0x38 0x48 0x6C
+: person-l
+ 0x1C 0x1C 0x14 0x3C 0x1E 0x0E 0x1E 0x1E
+ 0x1F 0x1F 0xDF 0xFE 0xF2 0xD2 0x36
+: scoop1-l
+ 0x00 0x38 0x38 0x28 0x78 0x3E 0x1F 0x1F
+ 0x3F 0x3F 0x3F 0xFE 0xF2 0xF2 0xF6
+: scoop2-l
+ 0x03 0x73 0x72 0x56 0xFE 0x7F 0x1E 0x7E
+ 0x3E 0x1F 0x1F 0x1F 0x1C 0x12 0x36
+
+: background # (1024 bytes)
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xE0 0x00
+ 0xEF 0xFF 0xEF 0xFF 0xEF 0xFF 0xE0 0x00
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x7F 0xFF 0x7F 0xFF 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00
+ 0xFF 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x07
+ 0xFF 0xF7 0xFF 0xF7 0xFF 0xF7 0x00 0x07
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFC 0xFF 0xFE
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFE
+ 0xFF 0xFF 0xFF 0xFF 0xF8 0xFB 0x00 0x00
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE 0xFF 0xFE
+ 0xFF 0xFC 0xFF 0xFC 0xFF 0xEE 0xFF 0xFE
+ 0xFF 0xFE 0xFF 0xFE 0xFF 0xFC 0xFF 0xF8
+ 0xFF 0xB8 0xFF 0xF0 0x8F 0xC0 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x5F 0xFF 0x7F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0x3F 0xFF 0x5F 0xFF 0x7F 0xFF 0x7F 0xFF
+ 0x3F 0xFF 0x3E 0xFF 0x3F 0xFF 0x1F 0xFF
+ 0x0F 0xDF 0x01 0xFF 0x00 0x3D 0x00 0x00
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x7F
+ 0xFF 0xFF 0xFF 0xFF 0xFE 0x1F 0x00 0x00
diff --git a/resources/octoroms/sweetcopter.8o b/resources/octoroms/sweetcopter.8o
new file mode 100644
index 0000000..6b49060
--- /dev/null
+++ b/resources/octoroms/sweetcopter.8o
@@ -0,0 +1,188 @@
+###########################################
+#
+# Sw8 Copter
+#
+# An adaptation of "Swing Copters"
+# for the Super Chip8.
+# Runs best at 100 cycles/frame.
+#
+# Press E to start a game and then
+# press E to switch directions.
+# Avoid obstacles!
+#
+###########################################
+
+:alias score v7
+:alias collide v8
+:alias player-key v9
+:alias player-x va
+:alias player-y vb
+:alias player-frame vc
+:alias player-dir vd
+
+:const H_SPEED 3
+:const BUTTON 6
+
+: sync
+ loop
+ vf := delay
+ if vf != 0 then
+ again
+ vf := 2
+ delay := vf
+;
+
+: girders
+ i := girder
+ v0 := 88
+ v1 := 8
+ loop
+ sprite v0 v1 0
+ v0 += 16
+ if v0 != 168 then
+ again
+;
+
+: draw-copter
+ i := face-l
+ if player-dir == H_SPEED then i := face-r
+ collide := 0
+ sprite player-x player-y 0
+ collide |= vf
+ i := blade-0
+ if player-frame == 1 then i := blade-1
+ v0 := 16
+ v0 =- player-y
+ sprite player-x v0 0
+ collide |= vf
+;
+
+: wait-release
+ if v0 key then return
+ player-key := 0
+;
+
+: switch-dir
+ v0 := BUTTON
+ if player-key != 0 then jump wait-release
+ if v0 -key then return
+ player-key := 1
+
+ # (~x)+1 = -x
+ v0 := 0xFF
+ player-dir ^= v0
+ player-dir += 1
+;
+
+: move-up
+ player-y += -1
+ v0 := 63
+ v0 &= player-y
+ if v0 == 0 then score += 1
+;
+
+: move-copter
+ v0 := 1
+ player-frame ^= v0
+ move-up
+ v0 := random 1
+ if v0 == 1 then move-up
+ player-x += player-dir
+ switch-dir
+;
+
+: wait-key
+ v0 := 56
+ v1 := 18
+ i := tapbubble
+ sprite v0 v1 0
+ vf := BUTTON
+ loop
+ if vf -key then
+ again
+ sprite v0 v1 0
+;
+
+: game-over
+ vf := 16
+ buzzer := vf
+ delay := vf
+ loop
+ draw-copter
+ scroll-left
+ draw-copter
+ draw-copter
+ scroll-right
+ draw-copter
+ draw-copter
+ scroll-right
+ draw-copter
+ draw-copter
+ scroll-left
+ draw-copter
+ vf := delay
+ if vf != 0 then
+ again
+
+: show-score
+ i := bcd-buffer
+ bcd score
+ load v2
+ i := bighex v1
+ v0 := 55
+ v1 := 30
+ sprite v0 v1 10
+ i := bighex v2
+ v0 += 9
+ sprite v0 v1 10
+ vf := BUTTON
+ loop
+ if vf -key then
+ again
+ clear
+
+: main
+ hires
+ player-x := 56
+ player-y := 48
+ player-dir := H_SPEED
+ player-key := 0
+ score := 0
+
+ girders
+ draw-copter
+ wait-key
+
+ loop
+ draw-copter
+ move-copter
+ draw-copter
+ if collide != 0 then jump game-over
+ sync
+ again
+
+: bcd-buffer 0 0 0
+
+: blade-0
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x01 0x80 0x02 0x40 0x7A 0x5E 0x89 0x91 0xF0 0x0F 0x01 0x80 0x01 0x80 0x00 0x00
+
+: blade-1
+ 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x01 0x80 0x02 0x40 0x0E 0xF0 0x31 0x0C 0x3E 0xFC 0x01 0x80 0x01 0x80 0x00 0x00
+
+: face-r
+ 0x07 0xC0 0x18 0x30 0x20 0x08 0x23 0xB8 0x44 0x44 0x48 0x8A 0x48 0x02 0x48 0x02
+ 0x34 0x44 0x23 0xB8 0x30 0x08 0x4B 0xF4 0x48 0x14 0x30 0x18 0x11 0x20 0x0F 0xC0
+
+: face-l
+ 0x03 0xE0 0x0C 0x18 0x10 0x04 0x1D 0xC4 0x22 0x22 0x51 0x12 0x40 0x12 0x40 0x12
+ 0x22 0x2C 0x1D 0xC4 0x10 0x0C 0x2F 0x92 0x28 0x12 0x18 0x0C 0x04 0x88 0x03 0xF0
+
+: girder
+ 0xFF 0xFF 0x00 0x00 0xC3 0xC3 0x7E 0x7E 0x3C 0x3C 0x3C 0x3C 0x3C 0x3C 0x7E 0x7E
+ 0xFF 0xFF 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+: tapbubble
+ 0x3F 0xFC 0x7F 0xFE 0xFF 0xFF 0xF0 0x0F 0xF0 0x0F 0xF3 0xFF 0xF0 0x7F 0xF0 0x7F
+ 0xF3 0xFF 0xF0 0x0F 0xF0 0x0F 0xFF 0xFF 0x7F 0xFE 0x3F 0xFC 0x00 0xC0 0x00 0x80
diff --git a/resources/octoroms/tank.8o b/resources/octoroms/tank.8o
new file mode 100644
index 0000000..354acb0
--- /dev/null
+++ b/resources/octoroms/tank.8o
@@ -0,0 +1,39 @@
+###########################################
+#
+# Tank
+#
+# Classic Chip8 program translated from
+# VIPer Volume 1 Issue 1 (June 1978), pg 12-14
+# https://github.com/mattmikolay/viper/blob/master/volume1/issue1.pdf
+#
+# Press 2/E/S/Q to move the tank.
+#
+###########################################
+
+: tankup
+ 0x10 0x54 0x7c 0x6c 0x7c 0x7c
+: tankdown
+ 0x44 0x7c 0x7c 0x6c 0x7c 0x54 0x10
+: tankleft
+ 0x0 0xfc 0x78 0x6e 0x78 0xfc
+: tankright
+ 0x0 0x3f 0x1e 0x76 0x1e 0x3f 0x0
+
+: up v2 += -1 i := tankup ;
+: down v2 += 1 i := tankdown ;
+: right v1 += -1 i := tankright ;
+: left v1 += 1 i := tankleft ;
+
+: main
+ v1 := 0x20
+ v2 := 0x10
+ i := tankup
+ loop
+ sprite v1 v2 7
+ v0 := key
+ sprite v1 v2 7
+ if v0 == 2 then up
+ if v0 == 4 then right
+ if v0 == 6 then left
+ if v0 == 8 then down
+ again
diff --git a/resources/octoroms/tests/testbigfont.8o b/resources/octoroms/tests/testbigfont.8o
new file mode 100644
index 0000000..eb9468d
--- /dev/null
+++ b/resources/octoroms/tests/testbigfont.8o
@@ -0,0 +1,23 @@
+###########################################
+#
+# Big Font
+#
+# High resolution font test program.
+# Draw the complete SuperChip high res
+# hex character set.
+#
+###########################################
+
+: main
+ hires
+ loop
+ i := bighex v0
+ sprite v1 v2 10
+ v1 += 9
+ v0 += 1
+ if v0 == 8 then v1 := 0
+ if v0 == 8 then v2 += 11
+ if v0 != 16 then
+ again
+
+ loop again
diff --git a/resources/octoroms/tests/testbranch.8o b/resources/octoroms/tests/testbranch.8o
new file mode 100644
index 0000000..e4e648a
--- /dev/null
+++ b/resources/octoroms/tests/testbranch.8o
@@ -0,0 +1,72 @@
+###########################################
+#
+# Branch Test
+#
+# Tests the if...begin...else...then
+# control structure.
+# Should print "51368"
+#
+###########################################
+
+: main
+ # simple taken branch
+ v0 := 1
+ v1 := 3
+ if v1 == 3 begin
+ v0 := 2
+ v0 += 3
+ end
+ print0
+
+ # simple not taken branch
+ v0 := 1
+ v1 := 3
+ if v1 != 3 begin
+ v0 := 2
+ v0 += 3
+ end
+ print0
+
+ # else taken
+ v0 := 2
+ v1 := 3
+ if v1 != v0 begin
+ v0 := 1
+ v0 += 2
+ else
+ v0 := 2
+ v0 += 4
+ end
+ print0
+
+ # else not taken
+ v0 := 2
+ v1 := 3
+ if v1 < v0 begin
+ v0 := 1
+ v0 += 2
+ else
+ v0 := 2
+ v0 += 4
+ end
+ print0
+
+ # nesting
+ v0 := 1
+ if v0 != 0 begin
+ if v0 != 2 begin
+ v0 := 7
+ else
+ v0 := 3
+ end
+ v0 += 1
+ end
+ print0
+
+ loop again
+
+: print0
+ i := hex v0
+ sprite va vb 5
+ va += 5
+;
diff --git a/resources/octoroms/tests/testcollide.8o b/resources/octoroms/tests/testcollide.8o
new file mode 100644
index 0000000..4c0c521
--- /dev/null
+++ b/resources/octoroms/tests/testcollide.8o
@@ -0,0 +1,32 @@
+###########################################
+#
+# Collision Detection Test
+#
+# Draws two overlapping sprites and
+# displays the contents of the vF register.
+# Should print "1".
+#
+###########################################
+
+: square
+ 0b11110000
+ 0b11110000
+ 0b11110000
+ 0b11110000
+
+: main
+ v0 := 20
+ v1 := 10
+ i := square
+
+ sprite v0 v1 4
+ v0 += 2
+ v1 += 2
+ sprite v0 v1 4
+
+ v2 := vf
+ v0 += 10
+ i := hex v2
+ sprite v0 v1 5
+
+ loop again
diff --git a/resources/octoroms/tests/testcompare.8o b/resources/octoroms/tests/testcompare.8o
new file mode 100644
index 0000000..a3f1b98
--- /dev/null
+++ b/resources/octoroms/tests/testcompare.8o
@@ -0,0 +1,70 @@
+###########################################
+#
+# Numeric Comparison Tests
+#
+# Exercises all combinations of
+# register-to-register and register-to-constant
+# comparisons with the synthetic pseudo-ops
+# < > <= and >=
+#
+###########################################
+
+: print
+ i := hex v0
+ sprite va vb 5
+ va += 5
+ v0 := 0
+;
+
+: newline
+ va := 0
+ vb += 6
+;
+
+: greater? if v1 > v2 then v0 := 1 print ;
+: greater5? if v1 > 5 then v0 := 1 print ;
+: less? if v1 < v2 then v0 := 1 print ;
+: less5? if v1 < 5 then v0 := 1 print ;
+: greatere? if v1 >= v2 then v0 := 1 print ;
+: greatere5? if v1 >= 5 then v0 := 1 print ;
+: lesse? if v1 <= v2 then v0 := 1 print ;
+: lesse5? if v1 <= 5 then v0 := 1 print ;
+
+: main
+ # 001001
+ v1 := 3 v2 := 5 greater?
+ v1 := 3 v2 := 3 greater?
+ v1 := 5 v2 := 3 greater?
+ v1 := 3 greater5?
+ v1 := 5 greater5?
+ v1 := 7 greater5?
+ newline
+
+ # 100100
+ v1 := 3 v2 := 5 less?
+ v1 := 3 v2 := 3 less?
+ v1 := 5 v2 := 3 less?
+ v1 := 3 less5?
+ v1 := 5 less5?
+ v1 := 7 less5?
+ newline
+
+ # 011011
+ v1 := 3 v2 := 5 greatere?
+ v1 := 3 v2 := 3 greatere?
+ v1 := 5 v2 := 3 greatere?
+ v1 := 3 greatere5?
+ v1 := 5 greatere5?
+ v1 := 7 greatere5?
+ newline
+
+ # 110110
+ v1 := 3 v2 := 5 lesse?
+ v1 := 3 v2 := 3 lesse?
+ v1 := 5 v2 := 3 lesse?
+ v1 := 3 lesse5?
+ v1 := 5 lesse5?
+ v1 := 7 lesse5?
+
+ loop again
+
diff --git a/resources/octoroms/tests/testloop.8o b/resources/octoroms/tests/testloop.8o
new file mode 100644
index 0000000..689efdd
--- /dev/null
+++ b/resources/octoroms/tests/testloop.8o
@@ -0,0 +1,66 @@
+###########################################
+#
+# Loop Construct Tests
+#
+# Tests boundary conditions using `if`
+# and `while` with simple and synthetic
+# comparison operators.
+# Should draw 5 vertical strips of 6 boxes.
+#
+###########################################
+
+: main
+ # while with pseudo-op
+ v0 := 0
+ v1 := 0
+ loop
+ while v1 < 30
+ sprite v0 v1 5
+ v1 += 5
+ again
+
+ # while with simple comparison
+ v0 := 5
+ v1 := 0
+ loop
+ while v1 != 30
+ sprite v0 v1 5
+ v1 += 5
+ again
+
+ # if with pseudo-op
+ v0 := 10
+ v1 := 0
+ loop
+ if v1 >= 30 then jump done1
+ sprite v0 v1 5
+ v1 += 5
+ again
+ : done1
+
+ # if with simple comparison
+ v0 := 15
+ v1 := 0
+ loop
+ if v1 == 30 then jump done2
+ sprite v0 v1 5
+ v1 += 5
+ again
+ : done2
+
+ # nested loops with while
+ v0 := 20
+ v1 := 0
+ loop
+ while v1 != 25
+ sprite v0 v1 5
+ v1 += 5
+ v2 := 5
+ loop
+ v2 += -1
+ if v2 != 0 then
+ again
+ again
+ sprite v0 v1 5
+
+ loop again
diff --git a/resources/octoroms/tests/testnext.8o b/resources/octoroms/tests/testnext.8o
new file mode 100644
index 0000000..2742c71
--- /dev/null
+++ b/resources/octoroms/tests/testnext.8o
@@ -0,0 +1,34 @@
+###########################################
+#
+# Next Test
+#
+# Demonstrates the use of the :next
+# operative to refer to the second byte
+# of an instruction. Mainly used for
+# writing self-modifying code.
+#
+###########################################
+
+: lt v0 := -1 i := vx save v0 ;
+: rt v0 := 1 i := vx save v0 ;
+
+: up v0 := -1 i := vy save v0 ;
+: dn v0 := 1 i := vy save v0 ;
+
+: main
+ va := 2
+ vb := 8
+
+ loop
+ clear
+ i := hex vc
+ sprite va vb 5
+
+ :next vx va += 1
+ :next vy vb += 1
+
+ if va == 0 then rt
+ if va == 59 then lt
+ if vb == 0 then dn
+ if vb == 26 then up
+ again
diff --git a/resources/octoroms/tests/testquirks.8o b/resources/octoroms/tests/testquirks.8o
new file mode 100644
index 0000000..af6a1da
--- /dev/null
+++ b/resources/octoroms/tests/testquirks.8o
@@ -0,0 +1,66 @@
+###########################################
+#
+# Quirks Mode Tests
+#
+# Examines the behavior of operations
+# which have multiple interpretations
+# in the wild and demonstrates the
+# distinction between Octo's defaults
+# and quirks modes for each instruction.
+#
+###########################################
+
+: buffer 0 3
+
+: main
+ # Perform a shift where source and dest
+ # differ. The correct behavior will draw a 1
+ # while in-place vx modification will draw 2.
+ v0 := 4
+ v1 := 2
+ v0 >>= v1
+ i := hex v0
+ va := 5
+ sprite va va 5
+
+ # perform a repeated store and load.
+ # the correct behavior should print 3 due to i autoincrements.
+ # quirks mode will print 4.
+ i := buffer
+ v0 := 4
+ save v0
+ v0 := 9 # just clobber v0 in case
+ load v0
+ i := hex v0
+ vb := 12
+ sprite va vb 5
+
+ # perform an arithmetic operation which stores to vf.
+ # the correct behavior should write the carry flag last, resulting in 0.
+ # quirks mode will print 6.
+ vf := 3
+ vf <<= vf
+ i := hex vf
+ vb := 19
+ sprite va vb 5
+
+ # try a jump0 instruction.
+ # the correct behavior should respect the value of v0 as an offset, resulting in 7
+ # quirks mode will respect v2 instead (in this case), resulting in 8.
+ v0 := 8
+ v2 := 4
+ jump0 jump-table
+
+: jump-table
+ 0x00 0x00 0x00 0x00
+ vf := 8 jump show-jump0-result
+ vf := 7 jump show-jump0-result
+ 0x00 0x00 0x00 0x00
+
+: show-jump0-result
+ i := hex vf
+ va := 10
+ vb := 5
+ sprite va vb 5
+
+ loop again
diff --git a/resources/octoroms/tests/testunpack.8o b/resources/octoroms/tests/testunpack.8o
new file mode 100644
index 0000000..4fd1535
--- /dev/null
+++ b/resources/octoroms/tests/testunpack.8o
@@ -0,0 +1,69 @@
+###########################################
+#
+# Unpacking Tests
+#
+# Uses the :unpack operative to perform
+# self-modification and rewrite an
+# i := NNN instruction.
+#
+###########################################
+
+: font
+ 0x20 0x20 0x20 0x00 0x20 0xF0 0x10 0x70 0x00 0x40 0xE0 0x90 0xE0 0x90 0x90 0x90
+ 0xE0 0x90 0xE0 0x90 0xE0 0x80 0x80 0xD0 0xB0 0xB0 0x90 0x90 0x90 0x90 0x60 0x60
+ 0x90 0x90 0x90 0x60 0x90 0xD0 0xB0 0x90 0x90 0x90 0x50 0x20 0x40 0x00 0x00 0x00
+ 0x00 0x00 0x60 0x90 0xF0 0x90 0x90 0xF0 0x90 0x90 0xF0 0x80 0xE0 0x80 0x80 0x80
+ 0x80 0xF0 0x80 0xE0 0x80 0xF0 0x40 0x40 0x40 0xF0 0x20 0x40 0x80 0xF0 0x20 0x20
+ 0x20 0xC0 0x60 0x90 0x80 0x90 0x60 0x90 0x90 0xB0 0x70 0x80 0xB0 0x90 0x70 0x80
+ 0x60 0x10 0xE0 0x90 0xA0 0xC0 0xA0 0x90 0x90 0xB0 0xB0 0xD0 0xF0 0x40 0x40 0x40
+ 0x40 0x90 0x90 0x70 0x10 0xE0
+
+# draw and linewrap a string at an address given
+# in the v0-v1 registers. The string will consist
+# of a sequence of offsets into the font data:
+
+: display-string
+ i := string-addr
+ save v1
+ v1 := 2 # character x
+ v2 := 1 # character y
+ v3 := 0 # byte index
+ # v4 contains string length
+ loop
+ : string-addr 0 0
+ i += v3
+ load v0
+ i := font
+ i += v0
+ sprite v1 v2 5
+ v1 += 5
+ if v1 == 62 then v2 += 6
+ if v1 == 62 then v1 := 2
+ v3 += 1
+ if v3 != v4 then
+ again
+;
+
+: demo-string
+ 0x71 0x1F 0x1A 0x2D 0x35 0x41 0x32 0x0A 0x2D 0x2D 0x2D 0x2D 0x32 0x2D 0x0A 0x1A
+ 0x17 0x10 0x3D 0x41 0x2D 0x45 0x24 0x2D 0x6C 0x35 0x41 0x2D 0x0C 0x45 0x5E 0x6C
+ 0x32 0x24 0x52 0x41
+:const demo-string-length 36
+
+# these constants demonstrate all the valid opcodes
+# which can be built with the high nybble in :unpack
+:const JUMP 0x1 # jump