Compare commits

...

9 Commits

47 changed files with 14573 additions and 2462 deletions
+1
View File
@@ -1,6 +1,7 @@
/target /target
log log
george.o george.o
Cargo.lock
.DS_Store .DS_Store
*.bin *.bin
/result /result
Generated
-1075
View File
File diff suppressed because it is too large Load Diff
+19 -6
View File
@@ -2,16 +2,29 @@
name = "georgeemu" name = "georgeemu"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
build = "build.rs"
[[target.'cfg(not(target_arch = "wasm32"))'.bin]]
path = "src/bin/main.rs"
name = "george"
[target.'cfg(target_arch = "wasm32")'.lib]
crate-type = ["cdylib", "rlib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
bdf-parser = "0.1.0"
[dependencies] [dependencies]
anyhow = "1.0.81" anyhow = "1.0.81"
bdf = "0.6.0" minifb = { git = "https://github.com/emoon/rust_minifb" }
crossterm = "0.27.0"
minifb = "0.27.0"
# minifb = "0.25.0"
# ratatui = "0.26.3"
serde = { version = "1.0.197", features = ["serde_derive", "derive"] } serde = { version = "1.0.197", features = ["serde_derive", "derive"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.7"
web-sys = "0.3.70"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
termion = "4.0.2" termion = "4.0.2"
toml = "0.8.12" toml = { version = "0.8.12" }
+83
View File
@@ -0,0 +1,83 @@
use std::{
env,
fs::File,
io::{stdin, stdout, IsTerminal, Read, Write},
ops::Neg,
os::unix::process::CommandExt,
path::Path,
process::{exit, Command},
};
// takes all charaters in bdf and returns a vec of each character row byte in order, normalized to
// width & height of the font (only works with 8 or fewer pixel wide fonts, should work for any height)
fn bdf_to_bitmap(mut bdf: File) -> [u8; 0x8000] {
let mut bdf_font_bytes = Vec::new();
bdf.read_to_end(&mut bdf_font_bytes).unwrap();
let bdf_font = bdf_parser::BdfFont::parse(&bdf_font_bytes).unwrap();
let mut bdf_vec = vec![];
for glyph in bdf_font.glyphs.iter() {
let glyph_offset_x = glyph.bounding_box.offset.x;
let glyph_offset_y = glyph.bounding_box.offset.y;
let glyph_height = glyph.bounding_box.size.y;
let font_height = bdf_font.metadata.bounding_box.size.y;
let font_offset_y = bdf_font.metadata.bounding_box.offset.y;
let top_space = font_height + font_offset_y - glyph_height - glyph_offset_y;
for _ in 0..top_space {
bdf_vec.push(0x00);
}
for bitmap in glyph.bitmap.iter() {
bdf_vec.push(bitmap >> glyph_offset_x);
}
let bottom_space = font_offset_y.neg() + glyph_offset_y;
for _ in 0..bottom_space {
bdf_vec.push(0x00);
}
}
let height = bdf_font.metadata.bounding_box.size.y as usize;
reorder_bitmap(&bdf_vec, height)
}
// takes an vec of ordered characters and translates them for use with the character rom
// TODO: make this work for any arbitrary char rom pin format using some kinda interface
fn reorder_bitmap(bitmap: &[u8], font_height: usize) -> [u8; 0x8000] {
let mut rom = [0; 0x8000]; // create vec the size of character rom
for row in 0..font_height {
for ascii_address in 0..u8::MAX {
// first 8 bits of address pick character
// next 5 bits pick row
// TODO: final 2 pick character set
let byte = bitmap[ascii_address as usize * font_height + row];
let rom_index: u16 = ((row as u16) << 8) + ascii_address as u16;
rom[rom_index as usize] = byte;
}
}
rom
}
fn rom_from_file<P>(path: P) -> [u8; 0x8000]
where
P: AsRef<Path>,
{
let file = File::open(path).unwrap();
bdf_to_bitmap(file)
}
fn main() {
let mut regen_font = Command::new("regen-font.sh");
let mut cleanup = Command::new("cleanup.sh");
regen_font.exec();
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("cozette.rom");
let cozette_rom_bytes = rom_from_file("build/cozette.bdf");
let mut cozette_rom = File::create(dest_path).unwrap();
cozette_rom.write_all(&cozette_rom_bytes).unwrap();
cleanup.exec();
}
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
#! /bin/sh
rm *.bdf*
File diff suppressed because it is too large Load Diff
+3903
View File
File diff suppressed because it is too large Load Diff
+3903
View File
File diff suppressed because it is too large Load Diff
+523
View File
@@ -0,0 +1,523 @@
# this is georgescii, august's version of extended ascii for george <3
# we're limited to 255 characters
# format: ascii byte, unicode hex, # unicode name
#
# format: three tab-separated columns
# column #1 is the ISO/IEC 8859-1 code (in hex as 0xXX)
# column #2 is the Unicode (in hex as 0xXXXX)
# column #3 the Unicode name (follows a comment sign, '#')
# 0x00 0x00 #
# 0x01 0x2591 # ░
# 0x02 0x2592 # ▒
# 0x03 0x2593 # ▓
# 0x04 0x2661 # ♡
# 0x05 0x2665 # ♥
# 0x06 0x2B50 # ⭐
# 0x07 0x272D # ✭
# 0x08 0xF005 # 
# 0x09 0x2726 # ✦
# 0x0a 0x2728 # ✨
# 0x0b 0x2640 # ♀
# 0x0c 0x2642 # ♂
# 0x0d 0x26A2 # ⚢
# 0x0E 0x26A3 # ⚣
# 0x0F 0x26A5 # ⚥
# 0x10 0x2669 # ♩
# 0x11 0x266A # ♪
# 0x12 0x266B # ♫
# 0x13 0x266C # ♬
# 0x14 0xFC5D # ﱝ
# 0x15 0xF026 # 
# 0x16 0xF027 # 
# 0x17 0xF028 # 
# 0x18 0xFA7E # 奄
# 0x19 0xFA7F # 奔
# 0x1A 0xFA80 # 婢
# 0x1B 0xFC5C # ﱜ
# 0x1C 0xFC5B # ﱛ
# 0x1D 0xF0AC # 
# 0x1E 0xF04B # 
# 0x1F 0xF04D # 
# 0x20 0x0020 #
# 0x21 0x0021 # !
# 0x22 0x0022 # "
# 0x23 0x0023 # #
# 0x24 0x0024 # $
# 0x25 0x0025 # %
# 0x26 0x0026 # &
# 0x27 0x0027 # '
# 0x28 0x0028 # (
# 0x29 0x0029 # )
# 0x2A 0x002A # *
# 0x2B 0x002B # +
# 0x2C 0x002C # ,
# 0x2D 0x002D # -
# 0x2E 0x002E # .
# 0x2F 0x002F # /
# 0x30 0x0030 # 0
# 0x31 0x0031 # 1
# 0x32 0x0032 # 2
# 0x33 0x0033 # 3
# 0x34 0x0034 # 4
# 0x35 0x0035 # 5
# 0x36 0x0036 # 6
# 0x37 0x0037 # 7
# 0x38 0x0038 # 8
# 0x39 0x0039 # 9
# 0x3A 0x003A # :
# 0x3B 0x003B # ;
# 0x3C 0x003C # <
# 0x3D 0x003D # =
# 0x3E 0x003E # >
# 0x3F 0x003F # ?
# 0x40 0x0040 # @
# 0x41 0x0041 # A
# 0x42 0x0042 # B
# 0x43 0x0043 # C
# 0x44 0x0044 # D
# 0x45 0x0045 # E
# 0x46 0x0046 # F
# 0x47 0x0047 # G
# 0x48 0x0048 # H
# 0x49 0x0049 # I
# 0x4A 0x004A # J
# 0x4B 0x004B # K
# 0x4C 0x004C # L
# 0x4D 0x004D # M
# 0x4E 0x004E # N
# 0x4F 0x004F # O
# 0x50 0x0050 # P
# 0x51 0x0051 # Q
# 0x52 0x0052 # R
# 0x53 0x0053 # S
# 0x54 0x0054 # T
# 0x55 0x0055 # U
# 0x56 0x0056 # V
# 0x57 0x0057 # W
# 0x58 0x0058 # X
# 0x59 0x0059 # Y
# 0x5A 0x005A # Z
# 0x5B 0x005B # [
# 0x5C 0x005C # \
# 0x5D 0x005D # ]
# 0x5E 0x005E # ^
# 0x5F 0x005F # _
# 0x60 0x0060 # `
# 0x61 0x0061 # a
# 0x62 0x0062 # b
# 0x63 0x0063 # c
# 0x64 0x0064 # d
# 0x65 0x0065 # e
# 0x66 0x0066 # f
# 0x67 0x0067 # g
# 0x68 0x0068 # h
# 0x69 0x0069 # i
# 0x6A 0x006A # j
# 0x6B 0x006B # k
# 0x6C 0x006C # l
# 0x6D 0x006D # m
# 0x6E 0x006E # n
# 0x6F 0x006F # o
# 0x70 0x0070 # p
# 0x71 0x0071 # q
# 0x72 0x0072 # r
# 0x73 0x0073 # s
# 0x74 0x0074 # t
# 0x75 0x0075 # u
# 0x76 0x0076 # v
# 0x77 0x0077 # w
# 0x78 0x0078 # x
# 0x79 0x0079 # y
# 0x7A 0x007A # z
# 0x7B 0x007B # {
# 0x7C 0x007C # |
# 0x7D 0x007D # }
# 0x7E 0x007E # ~
# 0x7F 0x2500 # ─
# 0x80 0x2502 # │
# 0x81 0x250C # ┌
# 0x82 0x2514 # └
# 0x83 0x251C # ├
# 0x84 0x2524 # ┤
# 0x85 0x252C # ┬
# 0x86 0x2534 # ┴
# 0x87 0x253C # ┼
# 0x88 0x256D # ╭
# 0x89 0x256E # ╮
# 0x8A 0x256F # ╯
# 0x8B 0x2570 # ╰
# 0x8C 0x2571 #
# 0x8D 0x2572 # ╲
# 0x8E 0x2573 #
# 0x8F 0x2550 # ═
# 0x90 0x2551 # ║
# 0x91 0x2554 # ╔
# 0x92 0x2557 # ╗
# 0x93 0x255a # ╚
# 0x94 0x255D # ╝
# 0x95 0x2560 # ╠
# 0x96 0x2563 # ╣
# 0x97 0x2566 # ╦
# 0x98 0x2569 # ╩
# 0x99 0x256C # ╬
# 0x9A 0xF04E # 
# 0x9B 0xF050 # 
# 0x9C 0xF051 # 
# 0x9D 0xF052 # 
# 0x9E 0xF048 # 
# 0x9F 0xE0B0 # 
# 0xA0 0xE0B2 # 
# 0xA1 0xE0B4 # 
# 0xA2 0xE0B6 # 
# 0xA3 0xE0B8 # 
# 0xA4 0xE0BA # 
# 0xA5 0xE0BC # 
# 0xA6 0xE0BE # 
# 0xA7 0x2581 # ▁
# 0xA8 0x2582 # ▂
# 0xA9 0x2583 # ▃
# 0xAA 0x2584 # ▄
# 0xAB 0x2585 # ▅
# 0xAC 0x2586 # ▆
# 0xAD 0x2587 # ▇
# 0xAE 0x2588 # █
# 0xAF 0x2589 # ▉
# 0xB0 0x258A # ▊
# 0xB1 0x258B # ▋
# 0xB2 0x258C # ▌
# 0xB3 0x258D # ▍
# 0xB4 0x258E # ▎
# 0xB5 0x258F # ▏
# 0xB6 0x0295 # ʕ
# 0xB7 0x00B7 # ·
# 0xB8 0x1D25 # ᴥ
# 0xB9 0x0294 # ʔ
# 0xBA 0x2596 # ▖
# 0xBB 0x2597 # ▗
# 0xBC 0x2598 # ▘
# 0xBD 0x2599 # ▙
# 0xBE 0x259A # ▚
# 0xBF 0x259B # ▛
# 0xC0 0x259C # ▜
# 0xC1 0x259D # ▝
# 0xC2 0x259E # ▞
# 0xC3 0x259F # ▟
# 0xC4 0x2190 # ←
# 0xC5 0x2191 # ↑
# 0xC6 0x2192 # →
# 0xC7 0x2193 # ↓
# 0xC8 0x2B60 # ⭠
# 0xC9 0x2B61 # ⭡
# 0xCA 0x2B62 # ⭢
# 0xCB 0x2B63 # ⭣
# 0xCC 0x2B80 # ⮀
# 0xCD 0x2B81 # ⮁
# 0xCE 0x2B82 # ⮂
# 0xCF 0x2B83 # ⮃
# 0xD0 0xF049 # 
# 0xD1 0xF04A # 
# 0xD2 0x23F3 # ⏳
# 0xD3 0xF07B # 
# 0xD4 0xF07C # 
# 0xD5 0xF114 # 
# 0xD6 0xF115 # 
# 0xD7 0xF250 # 
# 0xD8 0xF251 # 
# 0xD9 0xF253 # 
# 0xDA 0xF254 # 
# 0xDB 0xF461 # 
# 0xDC 0xF016 # 
# 0xDD 0xF401 # 
# 0xDE 0x1F52E # 🔮
# 0xDF 0xF2DB # 
# 0xE0 0xF008 # 
# 0xE1 0x25C7 # ◇
# 0xE2 0x25C8 # ◈
# 0xE3 0x1F311 # 🌑
# 0xE4 0x1F312 # 🌒
# 0xE5 0x1F313 # 🌓
# 0xE6 0x1F314 # 🌔
# 0xE7 0x1F315 # 🌕
# 0xE8 0x1F316 # 🌖
# 0xE9 0x1F317 # 🌗
# 0xEA 0x1F318 # 🌘
# 0xEB 0xF04C # 
# 0xEC 0x2714 # ✔
# 0xED 0x2718 # ✘
# 0xEE 0x25C6 # ◆
# 0xEF 0xF15D # 
# 0xF0 0xF15E # 
# 0xF1 0xF071 # 
# 0xF2 0xF449 # 
# 0xF3 0xF529 # 
# 0xF4 0xF658 # 
# 0xF5 0xF659 # 
# 0xF6 0x1f381 # 🎁
# 0xF7 0xf05a # 
# 0xF8 0xf06a # 
# 0xF9 0xf834 # 
# 0xFA 0xf835 # 
# 0xFB 0x2690 # ⚐
# 0xFC 0x2691 # ⚑
# 0xFD 0xf8d7 # 
# 0xFE 0xf0e7 # 
# 0xFF 0xf7d9 # 
0x00 0x00 #
0x01 0x0295 # ʕ
0x02 0x00B7 # ·
0x03 0x1D25 # ᴥ
0x04 0x0294 # ʔ
0x05 0x2661 # ♡
0x06 0x2665 # ♥
0x07 0x2726 # ✦
0x08 0x25C7 # ◇
0x09 0x25C6 # ◆
0x0a 0x272D # ✭
0x0b 0xF005 # 
0x0c 0x2728 # ✨
0x0d 0x000d # CR
0x0e 0x2518 # ┘
0x0f 0x2514 # └
0x10 0x250c # ┌
0x11 0x2510 # ┐
0x12 0x2500 # ─
0x13 0x2502 # │
0x14 0x2524 # ┤
0x15 0x2534 # ┴
0x16 0x251C # ├
0x17 0x252C # ┬
0x18 0x253C # ┼
0x19 0x2571 #
0x1a 0x2572 # ╲
0x1b 0x2573 #
0x1c 0x2591 # ░
0x1d 0x2592 # ▒
0x1e 0x2593 # ▓
0x1f 0x2588 # █
0x20 0x0020 #
0x21 0x0021 # !
0x22 0x0022 # "
0x23 0x0023 # #
0x24 0x0024 # $
0x25 0x0025 # %
0x26 0x0026 # &
0x27 0x0027 # '
0x28 0x0028 # (
0x29 0x0029 # )
0x2a 0x002A # *
0x2b 0x002B # +
0x2c 0x002C # ,
0x2d 0x002D # -
0x2e 0x002E # .
0x2f 0x002F # /
0x30 0x0030 # 0
0x31 0x0031 # 1
0x32 0x0032 # 2
0x33 0x0033 # 3
0x34 0x0034 # 4
0x35 0x0035 # 5
0x36 0x0036 # 6
0x37 0x0037 # 7
0x38 0x0038 # 8
0x39 0x0039 # 9
0x3a 0x003A # :
0x3b 0x003B # ;
0x3c 0x003C # <
0x3d 0x003D # =
0x3e 0x003E # >
0x3f 0x003F # ?
0x40 0x0040 # @
0x41 0x0041 # A
0x42 0x0042 # B
0x43 0x0043 # C
0x44 0x0044 # D
0x45 0x0045 # E
0x46 0x0046 # F
0x47 0x0047 # G
0x48 0x0048 # H
0x49 0x0049 # I
0x4a 0x004A # J
0x4b 0x004B # K
0x4c 0x004C # L
0x4d 0x004D # M
0x4e 0x004E # N
0x4f 0x004F # O
0x50 0x0050 # P
0x51 0x0051 # Q
0x52 0x0052 # R
0x53 0x0053 # S
0x54 0x0054 # T
0x55 0x0055 # U
0x56 0x0056 # V
0x57 0x0057 # W
0x58 0x0058 # X
0x59 0x0059 # Y
0x5a 0x005A # Z
0x5b 0x005B # [
0x5c 0x005C # \
0x5d 0x005D # ]
0x5e 0x005E # ^
0x5f 0x005F # _
0x60 0x0060 # `
0x61 0x0061 # a
0x62 0x0062 # b
0x63 0x0063 # c
0x64 0x0064 # d
0x65 0x0065 # e
0x66 0x0066 # f
0x67 0x0067 # g
0x68 0x0068 # h
0x69 0x0069 # i
0x6a 0x006A # j
0x6b 0x006B # k
0x6c 0x006C # l
0x6d 0x006D # m
0x6e 0x006E # n
0x6f 0x006F # o
0x70 0x0070 # p
0x71 0x0071 # q
0x72 0x0072 # r
0x73 0x0073 # s
0x74 0x0074 # t
0x75 0x0075 # u
0x76 0x0076 # v
0x77 0x0077 # w
0x78 0x0078 # x
0x79 0x0079 # y
0x7a 0x007A # z
0x7b 0x007B # {
0x7c 0x007C # |
0x7d 0x007D # }
0x7e 0x007E # ~
0x7f 0x256F # ╯
0x80 0x2570 # ╰
0x81 0x256D # ╭
0x82 0x256E # ╮
0x83 0x255D # ╝
0x84 0x255a # ╚
0x85 0x2554 # ╔
0x86 0x2557 # ╗
0x87 0x2550 # ═
0x88 0x2551 # ║
0x89 0x2563 # ╣
0x8a 0x2569 # ╩
0x8b 0x2560 # ╠
0x8c 0x2566 # ╦
0x8d 0x256C # ╬
0x8e 0xE0B8 # 
0x8f 0xE0BA # 
0x90 0xE0BC # 
0x91 0xE0BE # 
0x92 0xE0B2 # 
0x93 0xE0B0 # 
0x94 0xE0B6 # 
0x95 0xE0B4 # 
0x96 0x2596 # ▖
0x97 0x2597 # ▗
0x98 0x2598 # ▘
0x99 0x2599 # ▙
0x9a 0x259A # ▚
0x9b 0x259B # ▛
0x9c 0x259C # ▜
0x9d 0x259D # ▝
0x9e 0x259E # ▞
0x9f 0x259F # ▟
0xa0 0x2581 # ▁
0xa1 0x2582 # ▂
0xa2 0x2583 # ▃
0xa3 0x2584 # ▄
0xa4 0x2585 # ▅
0xa5 0x2586 # ▆
0xa6 0x2587 # ▇
0xa7 0x2589 # ▉
0xa8 0x258A # ▊
0xa9 0x258B # ▋
0xaa 0x258C # ▌
0xab 0x258D # ▍
0xac 0x258E # ▎
0xad 0x258F # ▏
0xae 0x1F311 # 🌑
0xaf 0x1F312 # 🌒
0xb0 0x1F313 # 🌓
0xb1 0x1F314 # 🌔
0xb2 0x1F315 # 🌕
0xb3 0x1F316 # 🌖
0xb4 0x1F317 # 🌗
0xb5 0x1F318 # 🌘
0xb6 0xF254 # 
0xb7 0xF251 # 
0xb8 0x23F3 # ⏳
0xb9 0xF253 # 
0xba 0xF250 # 
0xbb 0x2190 # ←
0xbc 0x2191 # ↑
0xbd 0x2192 # →
0xbe 0x2193 # ↓
0xbf 0x2B60 # ⭠
0xc0 0x2B61 # ⭡
0xc1 0x2B62 # ⭢
0xc2 0x2B63 # ⭣
0xc3 0x2B80 # ⮀
0xc4 0x2B81 # ⮁
0xc5 0x2B82 # ⮂
0xc6 0x2B83 # ⮃
0xc7 0xF049 # 
0xc8 0xF04A # 
0xc9 0xF048 # 
0xca 0xF04B # 
0xcb 0xF04C # 
0xcc 0xF04D # 
0xcd 0xF052 # 
0xce 0xF051 # 
0xcf 0xF04E # 
0xd0 0xF050 # 
0xd1 0xFC5D # ﱝ
0xd2 0xF026 # 
0xd3 0xF027 # 
0xd4 0xF028 # 
0xd5 0xFA80 # 婢
0xd6 0xFC5C # ﱜ
0xd7 0xFC5B # ﱛ
0xd8 0x2669 # ♩
0xd9 0x266A # ♪
0xda 0x266B # ♫
0xdb 0x266C # ♬
0xdc 0xF016 # 
0xdd 0xF07B # 
0xde 0xF07C # 
0xdf 0xF114 # 
0xe0 0xF115 # 
0xe1 0xF15D # 
0xe2 0xF15E # 
0xe3 0xF529 # 
0xe4 0xF071 # 
0xe5 0xF449 # 
0xe6 0xf05a # 
0xe7 0xF659 # 
0xe8 0xF658 # 
0xe9 0xf835 # 
0xea 0xf834 # 
0xeb 0x2690 # ⚐
0xec 0x2691 # ⚑
0xed 0xf7d9 # 
0xee 0x2714 # ✔
0xef 0x2718 # ✘
0xf0 0xf0e7 # 
0xf1 0xF2DB # 
0xf2 0xF008 # 
0xf3 0xF461 # 
0xf4 0x2B50 # ⭐
0xf5 0xF401 # 
0xf6 0x1F52E # 🔮
0xf7 0x1f381 # 🎁
0xf8 0xf8d7 # 
0xf9 0xF0AC # 
0xfa 0x25C8 # ◈
0xfb 0x2640 # ♀
0xfc 0x2642 # ♂
0xfd 0x26A2 # ⚢
0xfe 0x26A3 # ⚣
0xff 0x26A5 # ⚥
+5
View File
@@ -0,0 +1,5 @@
#! /bin/sh
fontforge -lang=ff -c 'Open($1); LoadEncodingFile($2, "george"); Reencode("george"); Generate($3)' Cozette.sfd georgeencoding.txt cozette.bdf
sed -i'' -e 's/FONTBOUNDINGBOX 11 13 0 -3/FONTBOUNDINGBOX 8 13 0 -3/' *.bdf
mv cozette-13.bdf cozette.bdf
+1 -1
View File
@@ -1,3 +1,3 @@
char_rom = "./src/roms/cozette.rom" char_rom = "./src/roms/cozette.rom"
rom = "./src/roms/george.rom" rom = "./src/roms/keyboard_sys.rom"
screen = "Window" screen = "Window"
+11
View File
@@ -0,0 +1,11 @@
.org $00
.byte $80
.org $8000
reset:
bbr7 $00, reset
.org $fffc
.word reset
BIN
View File
Binary file not shown.
Binary file not shown.
+238
View File
@@ -0,0 +1,238 @@
; .setcpu "65C02"
.include "./macro.inc"
.org $8000
n = $01 ; temporary storage for data stack operations
temp = $20 ; scratchpad page
cursor = $300
cursor_x = cursor
cursor_y = cursor + 1
rand_index = $200
reset:
sei
ldx #0; initialize data stack pointer
init:
cli
main:
jsr print
jsr rand_draw
jmp main
newline: ; sets cursor to start of next line
stz cursor_x
lda cursor_y
cmp #28
bne .end
stz cursor_y
rts
.end:
inc cursor_y
rts
text:
.byte 1,2,3,2,4
.asciiz "- george loves u <3"
random_y:
.byte 25,12,0,20,4,25,5,13
.byte 20, 1, 1, 22, 12, 19, 19, 19
.byte 9, 0, 4, 18, 13, 14, 4, 16
.byte 8, 17, 21, 14, 23, 21, 9, 0
.byte 14, 10, 14, 2, 26, 18, 15, 23
.byte 12, 2, 5, 4, 25, 20, 27, 4
.byte 28, 21, 3, 22, 11, 25, 2, 25
.byte 13, 17, 17, 24, 8, 8, 20, 21
.byte 11, 24, 27, 25, 8, 12, 7, 0
.byte 27, 12, 19, 27, 10, 3, 19, 2
.byte 2, 23, 22, 5, 26, 28, 4, 16
.byte 18, 7, 10, 9, 6, 19, 9, 2
.byte 14, 8, 14, 18, 18, 2, 13, 0
.byte 15, 26, 3, 23, 17, 12, 18, 11
.byte 4, 16, 17, 22, 9, 25, 3, 15
.byte 28, 3, 6, 14, 25, 5, 21, 8
.byte 15, 18, 15, 5, 28, 6, 15, 4
.byte 10, 1, 16, 24, 6, 9, 22, 3
.byte 17, 18, 10, 19, 27, 11, 22, 16
.byte 22, 17, 15, 6, 23, 11, 11, 11
.byte 4, 15, 5, 25, 19, 1, 8, 26
.byte 21, 20, 17, 27, 11, 3, 11, 20
.byte 15, 28, 0, 6, 14, 23, 20, 21
.byte 17, 20, 16, 15, 19, 6, 21, 19
.byte 15, 27, 1, 22, 7, 0, 5, 2
.byte 14, 24, 15, 4, 20, 16, 1, 14
.byte 4, 16, 4, 8, 13, 26, 3, 9
.byte 12, 25, 5, 0, 7, 17, 14, 20
.byte 2, 26, 2, 27, 18, 23, 5, 8
.byte 4, 21, 10, 11, 28, 22, 6, 6
.byte 10, 13, 23, 12, 20, 28, 20, 1
.byte 27, 19, 25, 6, 1, 10, 1
random_x:
.byte 42, 59, 11, 5, 18, 0, 26, 1
.byte 61, 16, 1, 51, 36, 47, 23, 1
.byte 16, 50, 46, 4, 55, 31, 15, 2
.byte 45, 21, 59, 53, 15, 43, 0, 2
.byte 64, 31, 38, 41, 25, 12, 12, 3
.byte 30, 13, 64, 44, 21, 8, 48, 3
.byte 46, 1, 2, 33, 4, 32, 59, 28
.byte 4, 24, 58, 53, 21, 41, 30, 2
.byte 56, 53, 31, 10, 42, 12, 9, 54
.byte 14, 14, 24, 29, 43, 60, 54, 26
.byte 5, 53, 17, 55, 27, 46, 31, 3
.byte 26, 44, 63, 30, 10, 34, 62, 48
.byte 42, 47, 51, 7, 55, 32, 14, 21
.byte 15, 26, 52, 37, 48, 0, 13, 2
.byte 50, 20, 35, 32, 8, 41, 2, 24
.byte 18, 9, 52, 22, 52, 12, 19, 32
.byte 29, 46, 34, 58, 54, 51, 43, 57
.byte 62, 10, 12, 57, 36, 39, 4, 30
.byte 38, 9, 30, 32, 33, 57, 3, 25
.byte 21, 36, 59, 30, 19, 39, 9, 60
.byte 34, 50, 52, 37, 34, 42, 3, 33
.byte 40, 19, 2, 26, 10, 38, 46, 30
.byte 3, 1, 19, 16, 26, 58, 42, 49
.byte 63, 1, 63, 41, 0, 21, 41, 19
.byte 21, 45, 44, 52, 20, 5, 11, 64
.byte 1, 62, 16, 5, 5, 8, 58, 56
.byte 16, 26, 6, 37, 19, 16, 25, 29
.byte 64, 59, 16, 6, 41, 28, 8, 51
.byte 54, 5, 19, 28, 13, 38, 52, 35
.byte 42, 13, 34, 33, 61, 61, 7, 27
.byte 38, 33, 9, 57, 10, 30, 8, 4
.byte 46, 3, 39, 46, 62, 20, 48, 7
; increments the cursor line by line, looping to (0, 0) after (63, 28)
inc_cursor:
lda cursor_x
cmp #63
beq .newline
inc cursor_x
rts
.newline:
lda cursor_y
cmp #28
beq .newscreen
stz cursor_x
inc cursor_y
rts
.newscreen:
stz cursor_y
stz cursor_x
rts
; zeroes out the display, resets cursor to 0,0
clear:
lda #0
ldy #0
.loop:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne .loop
stz cursor
stz cursor + 1
rts
; prints string from cursor position, stopping at end of string or at 256 chars, whichever comes first
; $6000 + (64*Y) + X
; THIS WILL WRITE OUT OF BOUNDS IF THE CURSOR IS OUT OF BOUNDS/STRING IS TOO LONG
; TODO: figure out a simple way of writing arbitrary length strings
print:
jsr cursor_addr
ldy #0
; y_overflow = temp + 5
.loop:
lda text, y
beq .end
sta (temp), y
iny
bra .loop
.end:
rts
; calculates real vram address from cursor (x, y)
cursor_addr:
stz temp
stz temp + 1
lda cursor_y
beq .add_x ; if y's zero just add x
.y_mult:
; multiply by 64
clc
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
sta temp
.add_x:
clc
lda cursor_x
adc temp
sta temp
lda #0
adc temp + 1
sta temp + 1
clc
lda #$60
adc temp + 1
sta temp + 1
rts
rand_draw:
ldx rand_index
lda random_x, x
sta cursor_x
lda random_y, x
sta cursor_y
jsr cursor_addr
inc rand_index
rts
isr: ; interrupt service routine
pha
phx
phy
; jsr irq
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
BIN
View File
Binary file not shown.
+189
View File
@@ -0,0 +1,189 @@
; .setcpu "65C02"
.include "./macro.inc"
; okay so rn i wanna set up a very basic system init, and write a few subroutines to draw characters at x,y coordinates
n = $01 ; temporary storage for data stack operations
key_row = $200 ; used for character lookup when key pressed
key_col = $201
cursor = $202
char_buffer = $300 ; 256 byte character buffer
kb_row = $4400 ; keyboard hardware register
kb_row_cache = $203 ; cache
.org $8000
reset:
sei
ldx #0; initialize data stack pointer
initdisplay:
lda #0
ldy #0
cleardisplay:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne cleardisplay
cli
print_test:
lda #0
sta key_row
lda #5
sta key_col
push_coords #5, #5
main:
; jsr printtext
; key_zero:
; stz keyboard_cache, x
; dex
; bpl key_zero
; fim:
; cli
; bra fim
jsr print
; jsr print
stp
jmp main
; keyboard: ; reads keyboard registers and stores the column and row of the first key found
; ; TODO: make this routine store up to 8 indices (for 8 key rollover)
; ldy #0
; .check_row: ; loop through each row
; lda kb_row, y
; beq .skip_row ; if row has no key pressed, skip checking which key
; ; jmp key_down
; sta kb_row_cache, y ; if key pressed, cache it
; lda kb_row, y
; cmp kb_row_cache, y ; has key changed?
; beq key_down
; .skip_row:
; iny
; cpy #5
; bne .check_row
; rts
; key_down: ; a is loaded with the row byte
; phy
; sty key_row ; store character row
; ldy #0
; .find_col: ; test each row bit, store column if key pressed
; lsr ; test bit 7
; bcs store_col ; if unset, don't go store character columnb
; .skip:
; iny
; cpy #8
; bne .find_col ; loop until we've checked each bit
; rts
; store_col:
; sty key_col
; jsr print
; rts
print: ; x y -- prints the key indexed with key_col and key_row at position x, y
keymap_index:
push
lda key_col
stz 1, x
sta 0, x
push
lda #8
stz 1, x
sta 0, x
push
lda key_row
stz 1, x
sta 0, x
jsr mult
jsr plus
lda 0, x
tay
lda keymap, y
push
sta 0, x
stz 1, x
jsr draw_char
rts
keymap:
.byte "?outrew?"
.byte "?piygsq?"
.byte "a??khvd?"
.byte "42ljbfz?"
.byte "31?mncx?"
.byte "????? m"
draw:
; push_coords #0, #0
; push_char #$00
; jsr draw_char
rts
draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
lda 0, x ; load a with character to draw
pop ; and pop it off the stack
jsr get_char_address ; calculate where to put the character in memory
sta (0, x) ; store a at the address pointed to on the stack
rts
get_char_address: ; gets vram address for a character at (x, y),
; (n1: x n2: y -- n: $6000 + x + (64 * y))
;jsr push_lit ; push 64 onto stack, low byte first
;.byte 64
;.byte 0
pha
lda #64
push ; doing this instead until `push_lit` is fixed
sta 0, x
stz 1, x
jsr mult ; multiply 64 with y (n2)
jsr plus ; add result with x (n1)
;jsr push_lit ; push vram address onto the stack
;.byte $00
;.byte $60
lda #$60
push
sta 1, x
stz 0, x
jsr plus ; add vram start address to result
pla
rts
fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
jsr get_char_address
isr: ; interrupt service routine
pha
phx
phy
; jsr keyboard
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
Binary file not shown.
BIN
View File
Binary file not shown.
+215
View File
@@ -0,0 +1,215 @@
; .setcpu "65C02"
.include "./macro.inc"
.org $8000
n = $01 ; temporary storage for data stack operations
temp = $20 ; scratchpad page
str_ptr = $30
cursor = $300
cursor_x = cursor
cursor_y = cursor + 1
char_buf = $302
char_buf_index = char_buf + 8
reset:
sei
ldx #0; initialize data stack pointer
init:
lda #$31
sta str_ptr
lda #$80
sta str_ptr + 1
jsr clear
lda #0
sta cursor_x
lda #0
sta cursor_y
cli
main:
jsr print
jmp main
newline: ; sets cursor to start of next line
stz cursor_x
lda cursor_y
cmp #28
bne .end
stz cursor_y
rts
.end:
inc cursor_y
rts
text:
.asciiz "hello <3"
; increments the cursor line by line, looping to (0, 0) after (63, 28)
inc_cursor:
lda cursor_x
cmp #63
beq .newline
inc cursor_x
rts
.newline:
lda cursor_y
cmp #28
beq .newscreen
stz cursor_x
inc cursor_y
rts
.newscreen:
stz cursor_y
stz cursor_x
rts
; zeroes out the display, resets cursor to 0,0
clear:
lda #0
ldy #0
.loop:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne .loop
stz cursor
stz cursor + 1
rts
; prints string from cursor position, stopping at end of string or at 256 chars, whichever comes first
; $6000 + (64*Y) + X
; THIS WILL WRITE OUT OF BOUNDS IF THE CURSOR IS OUT OF BOUNDS/STRING IS TOO LONG
; TODO: figure out a simple way of writing arbitrary length strings
; and
print:
jsr cursor_addr
ldy #0
; y_overflow = temp + 5
.loop:
lda (str_ptr), y
beq .end
sta (temp), y
iny
bra .loop
.end:
rts
; calculates real vram address from cursor (x, y)
cursor_addr:
stz temp
stz temp + 1
lda cursor_y
beq .add_x ; if y's zero just add x
.y_mult:
; multiply by 64
clc
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
sta temp
.add_x:
clc
lda cursor_x
adc temp
sta temp
lda #0
adc temp + 1
sta temp + 1
clc
lda #$60
adc temp + 1
sta temp + 1
rts
; print_text:
; lda text,y
; beq .end
; sta $6000, y
; iny
; bra print_text
; .end:
; ldy #0
; rts
; draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
; lda 0, x ; load a with character to draw
; pop ; and pop it off the stack
; jsr get_char_address ; calculate where to put the character in memory
; sta (0, x) ; store a at the address pointed to on the stack
; rts
; get_char_address: ; gets vram address for a character at (x, y),
; ; (n1: x n2: y -- n: $6000 + x + (64 * y))
; ;jsr push_lit ; push 64 onto stack, low byte first
; ;.byte 64
; ;.byte 0
; pha
; lda #64
; push ; doing this instead until `push_lit` is fixed
; sta 0, x
; stz 1, x
; jsr mult ; multiply 64 with y (n2)
; jsr plus ; add result with x (n1)
; ;jsr push_lit ; push vram address onto the stack
; ;.byte $00
; ;.byte $60
; lda #$60
; push
; sta 1, x
; stz 0, x
; jsr plus ; add vram start address to result
; pla
; rts
; fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
; jsr get_char_address
isr: ; interrupt service routine
pha
phx
phy
; jsr irq
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
Binary file not shown.
View File
BIN
View File
Binary file not shown.
+173
View File
@@ -0,0 +1,173 @@
; .setcpu "65C02"
.include "./macro.inc"
; okay so rn i wanna set up a very basic system init, and write a few subroutines to draw characters at x,y coordinates
n = $01 ; temporary storage for data stack operations
key_row = $200 ; used for character lookup when key pressed
key_col = $201
cursor = $202
char_buffer = $300 ; 256 byte character buffer
kb_row = $4400 ; keyboard hardware register
kb_row_cache = $203 ; cache
.org $8000
reset:
sei
ldx #0; initialize data stack pointer
jmp main
initdisplay:
lda #20
ldy #0
cleardisplay:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne cleardisplay
cli
main:
; jsr keyboard
; key_zero:
; stz keyboard_cache, x
; dex
; bpl key_zero
; fim:
; cli
; bra fim
; jsr kitty_keys
lda #9
sta $6000
jmp main
not_keyboard:
ldy #0
.check_row: ; loop through each row
lda kb_row, y
beq .skip_row ; if row has no key pressed, skip checking which key
sta kb_row_cache, y ; if key pressed, cache it
lda kb_row, y
cmp kb_row_cache, y ; has key changed?
beq key_down
.skip_row:
iny
cpy #5
bne .check_row
rts
key_down: ; a is loaded with the row byte
phy
sty key_row ; store character row
ldy #0
.find_col: ; test each row bit, store column if key pressed
lsr ; test bit 7
bcs store_col ; if unset, don't go store character columnb
.skip:
iny
cpy #8
bne .find_col ; loop until we've checked each bit
store_col:
sty key_col
keymap_index:
push
lda key_col
stz 1, x
sta 0, x
push
lda #8
stz 1, x
sta 0, x
push
lda key_row
stz 1, x
sta 0, x
jsr mult
jsr plus
lda 0, x
tay
print: ; we've stored the character position, now let's
lda keymap, y
ldy cursor
sta $6000, y
inc cursor
ply
rts
keymap:
.byte "?outrew?"
.byte "?piygsq?"
.byte "a??khvd?"
.byte "42ljbfz?"
.byte "31?mncx?"
.byte "????? m"
; draw:
; ; push_coords #0, #0
; ; push_char #$00
; ; jsr draw_char
; rts
; draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
; lda 0, x ; load a with character to draw
; pop ; and pop it off the stack
; jsr get_char_address ; calculate where to put the character in memory
; sta (0, x) ; store a at the address pointed to on the stack
; rts
; get_char_address: ; gets vram address for a character at (x, y),
; ; (n1: x n2: y -- n: $6000 + x + (64 * y))
; ;jsr push_lit ; push 64 onto stack, low byte first
; ;.byte 64
; ;.byte 0
; pha
; lda #64
; push ; doing this instead until `push_lit` is fixed
; sta 0, x
; stz 1, x
; jsr mult ; multiply 64 with y (n2)
; jsr plus ; add result with x (n1)
; ;jsr push_lit ; push vram address onto the stack
; ;.byte $00
; ;.byte $60
; lda #$60
; push
; sta 1, x
; stz 0, x
; jsr plus ; add vram start address to result
; pla
; rts
; fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
; jsr get_char_address
isr: ; interrupt service routine
pha
phx
phy
; jsr irq
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
BIN
View File
Binary file not shown.
+2 -2
View File
@@ -7,6 +7,6 @@ fi
set -e set -e
vasm6502_oldstyle ./src/roms/$1.asm -dotdir -wdc02 -ldots -Fbin -o ./src/roms/$1.rom; vasm6502_oldstyle roms/$1.asm -dotdir -wdc02 -ldots -Fbin -o roms/$1.rom;
cargo run -- rom "./src/roms/$1.rom"; cargo run -- rom "roms/$1.rom";
# hexdump -C ./cpu_dump.bin; # hexdump -C ./cpu_dump.bin;
+19 -9
View File
@@ -7,7 +7,18 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::video::ScreenType; #[derive(Serialize, Deserialize, Default, Debug)]
pub enum ScreenType {
Terminal,
#[default]
Window,
}
pub struct Config {
pub rom: Option<String>,
pub screen: ScreenType,
pub char_rom: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct ConfigBuilder { struct ConfigBuilder {
@@ -27,7 +38,7 @@ impl ConfigBuilder {
fn build(self) -> Config { fn build(self) -> Config {
let rom = match self.rom { let rom = match self.rom {
Some(rom) => rom, Some(rom) => Some(rom),
None => { None => {
println!("no rom was provided :("); println!("no rom was provided :(");
exit(1); exit(1);
@@ -44,12 +55,6 @@ impl ConfigBuilder {
} }
} }
pub struct Config {
pub rom: String,
pub screen: ScreenType,
pub char_rom: Option<String>,
}
fn help(command: Option<String>) { fn help(command: Option<String>) {
let executable: String = env::args().next().unwrap(); let executable: String = env::args().next().unwrap();
if let Some(command) = command { if let Some(command) = command {
@@ -81,7 +86,11 @@ fn help(command: Option<String>) {
println!(" rom, -r, --rom <path>: load a rom/binary from path"); println!(" rom, -r, --rom <path>: load a rom/binary from path");
println!(" screen, -s, --screen <type>: use the \"terminal\" or \"window\" display type"); println!(" screen, -s, --screen <type>: use the \"terminal\" or \"window\" display type");
println!("\nconfiguration:"); println!("\nconfiguration:");
println!(" george-emu searches for a `george.toml` in the current directory.\n in `george.toml` you can specify a path for the character rom using the key `char_rom` and the main rom/binary with the key `rom`"); println!(" george-emu searches for a `george.toml` in the current directory.\n in `george.toml` you can specify a path for the character rom\n using the key `char_rom` and the main rom/binary with the key `rom`");
println!("\ndebugging:");
println!(" you can pipe in a rom for george to evaluate.");
println!(" she'll read it until she reaches a breakpoint (`0x02`) or `stp` instruction,");
println!(" then will dump her memory to stdout <3");
exit(0); exit(0);
} }
} }
@@ -101,6 +110,7 @@ pub fn get_input() -> Config {
} }
Err(_) => { Err(_) => {
println!("couldn't find a `george.toml` in the current directory!"); println!("couldn't find a `george.toml` in the current directory!");
println!("\nuse `{executable} --help` to see all config options");
exit(1); exit(1);
} }
}; };
+76
View File
@@ -0,0 +1,76 @@
use std::io::{self, stdin, IsTerminal};
#[cfg(not(target_arch = "wasm32"))]
mod cli;
#[cfg(not(target_arch = "wasm32"))]
use cli::get_input;
use georgeemu::memory::{Mem, MemHandle};
#[cfg(not(target_arch = "wasm32"))]
use georgeemu::GeorgeEmu;
#[cfg(not(target_arch = "wasm32"))]
use minifb::{Scale, ScaleMode, Window, WindowOptions};
use std::{fs::File, io::Read};
#[cfg(not(target_arch = "wasm32"))]
fn main() -> io::Result<()> {
use std::io::{stdout, Write};
use georgeemu::cpu::Cpu;
let mut input = stdin();
if !input.is_terminal() {
let mut bytes = [0u8; 0x8000];
input.read_exact(&mut bytes).unwrap();
let memory = make_memory(&bytes);
let mut cpu = Cpu::new(memory);
let final_output = cpu.run_to_end();
let _ = stdout().write_all(&final_output);
Ok(())
} else {
let window = Window::new(
"ʕ·ᴥ·ʔ-☆",
512,
380,
WindowOptions {
resize: true,
borderless: true,
title: true,
transparency: false,
scale: Scale::FitScreen,
scale_mode: ScaleMode::AspectRatioStretch,
topmost: false,
none: true,
},
)
.unwrap();
let config = get_input();
let rom = match config.rom {
Some(path) => {
let mut file = File::open(path).unwrap();
let mut bin = vec![0; 0x8000];
file.read_exact(&mut bin).unwrap();
bin.try_into().unwrap()
}
None => *include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/roms/george.rom")),
};
let mut emu = GeorgeEmu::builder().rom(rom).window(window).build();
emu.run();
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
fn make_memory(bytes: &[u8]) -> MemHandle {
let mut memory = Mem::new();
memory.load_bytes(bytes);
MemHandle::new(memory)
}
#[cfg(target_arch = "wasm32")]
fn main() {}
+56 -56
View File
@@ -2,11 +2,11 @@ use crate::instructions::get_instruction;
use crate::memory::{MemHandle, MemoryReader, MemoryWriter}; use crate::memory::{MemHandle, MemoryReader, MemoryWriter};
use std::fmt::Display; use std::fmt::Display;
use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::mpsc::{channel, Receiver, Sender};
#[cfg(not(target_arch = "wasm32"))]
use std::thread::sleep; use std::thread::sleep;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Duration; use std::time::Duration;
use termion::cursor::Goto;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum StatusFlag { pub enum StatusFlag {
Negative = 0b1000_0000, Negative = 0b1000_0000,
@@ -19,9 +19,10 @@ pub enum StatusFlag {
Carry = 0b0000_0001, Carry = 0b0000_0001,
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct CpuController(Sender<CpuControl>); pub struct CpuController(Sender<CpuControl>);
#[derive(Clone, Copy)]
pub enum CpuControl { pub enum CpuControl {
Irq, Irq,
Nmi, Nmi,
@@ -35,22 +36,23 @@ impl CpuController {
Self(sender) Self(sender)
} }
pub fn irq(&self) { pub fn irq(&self) {
self.0.send(CpuControl::Irq); let _ = self.0.send(CpuControl::Irq);
} }
pub fn nmi(&self) { pub fn nmi(&self) {
self.0.send(CpuControl::Nmi); let _ = self.0.send(CpuControl::Nmi);
} }
pub fn toggle(&self) { pub fn toggle(&self) {
self.0.send(CpuControl::Toggle); let _ = self.0.send(CpuControl::Toggle);
} }
pub fn cycle(&self) { pub fn cycle(&self) {
self.0.send(CpuControl::Cycle); let _ = self.0.send(CpuControl::Cycle);
} }
pub fn data(&self) { pub fn data(&self) {
self.0.send(CpuControl::Data); let _ = self.0.send(CpuControl::Data);
} }
} }
#[derive(Debug)]
pub struct CpuReceiver(Receiver<CpuControl>); pub struct CpuReceiver(Receiver<CpuControl>);
impl CpuReceiver { impl CpuReceiver {
pub fn new(receiver: Receiver<CpuControl>) -> Self { pub fn new(receiver: Receiver<CpuControl>) -> Self {
@@ -58,6 +60,7 @@ impl CpuReceiver {
} }
} }
#[derive(Debug)]
pub struct Cpu { pub struct Cpu {
pub a: u8, // Accumulator Register pub a: u8, // Accumulator Register
pub x: u8, // X Register pub x: u8, // X Register
@@ -69,10 +72,10 @@ pub struct Cpu {
pub nmi: bool, pub nmi: bool,
pub memory: MemHandle, pub memory: MemHandle,
pub pending_cycles: usize, pub pending_cycles: usize,
pub debug: bool,
receiver: Option<CpuReceiver>, receiver: Option<CpuReceiver>,
stopped: bool, stopped: bool,
cycle: bool, // cycle_count: usize, cycle: bool,
// state_tx: Sender<CpuState>,
} }
impl Display for Cpu { impl Display for Cpu {
@@ -94,22 +97,28 @@ impl MemoryWriter for Cpu {
impl Cpu { impl Cpu {
pub fn new(memory: MemHandle) -> Self { pub fn new(memory: MemHandle) -> Self {
// pub fn new(memory: MemHandle) -> Self { // reset the cpu on initialization so we don't
// scream and cry for two days trying to figure
// out why george isn't working <3
let low_byte = memory.read(0xFFFC);
let high_byte = memory.read(0xFFFD);
let pc = (high_byte as u16) << 8 | (low_byte as u16);
Cpu { Cpu {
a: 0x00, a: 0x00,
x: 0x00, x: 0x00,
y: 0x00, y: 0x00,
pc: 0x0000, pc,
s: 0xFF, s: 0xFF,
p: 0b0010_0100, p: 0b0010_0100,
irq: false, irq: false,
nmi: false, nmi: false,
receiver: None, receiver: None,
memory, memory,
debug: false,
stopped: false, stopped: false,
pending_cycles: 0, pending_cycles: 0,
cycle: false, // cycle_count: 0, cycle: false,
// state_tx,
} }
} }
pub fn with_receiver(mut self, receiver: CpuReceiver) -> Self { pub fn with_receiver(mut self, receiver: CpuReceiver) -> Self {
@@ -179,13 +188,6 @@ impl Cpu {
value & 0b1000_0000 == 0b1000_0000 value & 0b1000_0000 == 0b1000_0000
} }
// pub fn execute(&mut self) {
// self.cycle();
// while self.pending_cycles != 0 {
// self.cycle();
// }
// }
pub fn wait_for_interrupt(&self) { pub fn wait_for_interrupt(&self) {
unimplemented!() unimplemented!()
} }
@@ -198,6 +200,10 @@ impl Cpu {
self.pc = self.read_word(0xFFFE); self.pc = self.read_word(0xFFFE);
} }
pub fn toggle_stopped(&mut self) {
self.stopped = !self.stopped;
}
fn receive_control(&mut self) { fn receive_control(&mut self) {
match &self.receiver { match &self.receiver {
Some(receiver) => { Some(receiver) => {
@@ -213,7 +219,8 @@ impl Cpu {
self.stopped = !self.stopped; self.stopped = !self.stopped;
} }
CpuControl::Cycle => self.cycle = true, CpuControl::Cycle => self.cycle = true,
CpuControl::Data => {} // self CpuControl::Data => {
// self
// .state_tx // .state_tx
// .send(CpuState { // .send(CpuState {
// a: self.a.clone(), // Accumulator Register // a: self.a.clone(), // Accumulator Register
@@ -228,6 +235,7 @@ impl Cpu {
// .unwrap(), // .unwrap(),
} }
} }
}
None => {} None => {}
} }
} }
@@ -241,6 +249,8 @@ impl Cpu {
} }
self.cycle = false; self.cycle = false;
// can't sleep in wasm
#[cfg(not(target_arch = "wasm32"))]
while self.pending_cycles != 0 { while self.pending_cycles != 0 {
// roughly cycle-accurate timing // roughly cycle-accurate timing
sleep(Duration::from_nanos(500)); sleep(Duration::from_nanos(500));
@@ -252,45 +262,35 @@ impl Cpu {
let opcode = self.read(self.pc); let opcode = self.read(self.pc);
let instruction = get_instruction(opcode); let instruction = get_instruction(opcode);
instruction.call(self); instruction.call(self);
// println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi);
// println!(
// "Instruction: {:?}, {:#04x}",
// valid_instruction.opcode, opcode
// );
// println!("");
} }
pub fn stop(&mut self) { pub fn stop(&mut self) {
self.stopped = true; self.stopped = true;
} }
pub fn breakpoint(&mut self) { pub fn breakpoint(&mut self) {
// println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi); if self.debug {
// println!(
// "Instruction: {:?}, {:#04x}",
// valid_instruction.opcode, opcode
// );
// println!("");
// println!(
// "{}{:#04x}: {:#02x}, {:#04x}: {:#02x}",
// Goto(1, 35),
// 0x20,
// self.read(0x20),
// 0x21,
// self.read(0x21)
// );
// println!(
// "{}str_ptr {:#04x}: {:#02x}, {:#04x}: {:#02x}",
// Goto(1, 36),
// 0x30,
// self.read(0x30),
// 0x31,
// self.read(0x31)
// );
// println!(
// "{}cursor - x: {:#02x}, y: {:#02x}",
// Goto(1, 37),
// self.read(0x300),
// self.read(0x301)
// );
self.stop(); self.stop();
} }
} }
fn quick_cycle(&mut self) {
if !self.get_flag(StatusFlag::IrqDisable) && self.irq {
self.interrupt();
}
let opcode = self.read(self.pc);
let instruction = get_instruction(opcode);
instruction.call(self);
}
pub fn run_to_end(&mut self) -> [u8; 0x10000] {
self.quick_cycle();
while self.pending_cycles != 0 {
let opcode = self.read(self.pc);
let instruction = get_instruction(opcode);
match instruction.name {
"stp" | "breakpoint" => return self.memory.dump(),
_ => self.quick_cycle(),
}
}
unreachable!()
}
}
-67
View File
@@ -1,67 +0,0 @@
#[derive(Debug)]
pub enum GeorgeErrorKind {
Memory(MemoryError),
Execution(ExecutionError),
AddrMode(AddressingModeError),
Mapping(MappingError),
}
impl From<MemoryError> for GeorgeErrorKind {
fn from(value: MemoryError) -> Self {
Self::Memory(value)
}
}
impl From<AddressingModeError> for GeorgeErrorKind {
fn from(value: AddressingModeError) -> Self {
Self::AddrMode(value)
}
}
impl From<ExecutionError> for GeorgeErrorKind {
fn from(value: ExecutionError) -> Self {
Self::Execution(value)
}
}
#[derive(Debug)]
pub enum ExecutionError {
InvalidInstruction,
InterruptsDisabled,
StackOverflow,
MemoryError(MemoryError),
}
#[derive(Debug)]
pub enum AddressingModeError {
IncompatibleAddrMode,
}
impl From<ExecutionError> for AddressingModeError {
fn from(_value: ExecutionError) -> Self {
Self::IncompatibleAddrMode
}
}
impl From<MemoryError> for ExecutionError {
fn from(error: MemoryError) -> Self {
ExecutionError::MemoryError(error)
}
}
#[derive(Debug)]
pub struct GeorgeError {
pub desc: String,
pub kind: GeorgeErrorKind,
}
#[derive(Debug)]
pub enum MemoryError {
Unmapped,
NoDataAtAddress,
Unwritable,
}
#[derive(Debug)]
pub enum MappingError {
RegionOccupied,
InvalidPage,
}
-265
View File
@@ -1,265 +0,0 @@
# this is august's version of extended ascii for george <3
# we're limited to 255 characters
# format: ascii byte, unicode hex, # unicode name
#
# format: three tab-separated columns
# column #1 is the ISO/IEC 8859-1 code (in hex as 0xXX)
# column #2 is the Unicode (in hex as 0xXXXX)
# column #3 the Unicode name (follows a comment sign, '#')
0x00 0x00 #
0x01 0x2591 # ░
0x02 0x2592 # ▒
0x03 0x2593 # ▓
0x04 0x2661 # ♡
0x05 0x2665 # ♥
0x06 0x2B50 # ⭐
0x07 0x272D # ✭
0x08 0xF005 # 
0x09 0x2726 # ✦
0x0a 0x2728 # ✨
0x0b 0x2640 # ♀
0x0c 0x2642 # ♂
0x0d 0x26A2 # ⚢
0x0E 0x26A3 # ⚣
0x0F 0x26A5 # ⚥
0x10 0x2669 # ♩
0x11 0x266A # ♪
0x12 0x266B # ♫
0x13 0x266C # ♬
0x14 0xFC5D # ﱝ
0x15 0xF026 # 
0x16 0xF027 # 
0x17 0xF028 # 
0x18 0xFA7E # 奄
0x19 0xFA7F # 奔
0x1A 0xFA80 # 婢
0x1B 0xFC5C # ﱜ
0x1C 0xFC5B # ﱛ
0x1D 0xF0AC # 
0x1E 0xF04B # 
0x1F 0xF04D # 
0x20 0x0020 #
0x21 0x0021 # !
0x22 0x0022 # "
0x23 0x0023 # #
0x24 0x0024 # $
0x25 0x0025 # %
0x26 0x0026 # &
0x27 0x0027 # '
0x28 0x0028 # (
0x29 0x0029 # )
0x2A 0x002A # *
0x2B 0x002B # +
0x2C 0x002C # ,
0x2D 0x002D # -
0x2E 0x002E # .
0x2F 0x002F # /
0x30 0x0030 # 0
0x31 0x0031 # 1
0x32 0x0032 # 2
0x33 0x0033 # 3
0x34 0x0034 # 4
0x35 0x0035 # 5
0x36 0x0036 # 6
0x37 0x0037 # 7
0x38 0x0038 # 8
0x39 0x0039 # 9
0x3A 0x003A # :
0x3B 0x003B # ;
0x3C 0x003C # <
0x3D 0x003D # =
0x3E 0x003E # >
0x3F 0x003F # ?
0x40 0x0040 # @
0x41 0x0041 # A
0x42 0x0042 # B
0x43 0x0043 # C
0x44 0x0044 # D
0x45 0x0045 # E
0x46 0x0046 # F
0x47 0x0047 # G
0x48 0x0048 # H
0x49 0x0049 # I
0x4A 0x004A # J
0x4B 0x004B # K
0x4C 0x004C # L
0x4D 0x004D # M
0x4E 0x004E # N
0x4F 0x004F # O
0x50 0x0050 # P
0x51 0x0051 # Q
0x52 0x0052 # R
0x53 0x0053 # S
0x54 0x0054 # T
0x55 0x0055 # U
0x56 0x0056 # V
0x57 0x0057 # W
0x58 0x0058 # X
0x59 0x0059 # Y
0x5A 0x005A # Z
0x5B 0x005B # [
0x5C 0x005C # \
0x5D 0x005D # ]
0x5E 0x005E # ^
0x5F 0x005F # _
0x60 0x0060 # `
0x61 0x0061 # a
0x62 0x0062 # b
0x63 0x0063 # c
0x64 0x0064 # d
0x65 0x0065 # e
0x66 0x0066 # f
0x67 0x0067 # g
0x68 0x0068 # h
0x69 0x0069 # i
0x6A 0x006A # j
0x6B 0x006B # k
0x6C 0x006C # l
0x6D 0x006D # m
0x6E 0x006E # n
0x6F 0x006F # o
0x70 0x0070 # p
0x71 0x0071 # q
0x72 0x0072 # r
0x73 0x0073 # s
0x74 0x0074 # t
0x75 0x0075 # u
0x76 0x0076 # v
0x77 0x0077 # w
0x78 0x0078 # x
0x79 0x0079 # y
0x7A 0x007A # z
0x7B 0x007B # {
0x7C 0x007C # |
0x7D 0x007D # }
0x7E 0x007E # ~
0x7F 0x2500 # ─
0x80 0x2502 # │
0x81 0x250C # ┌
0x82 0x2514 # └
0x83 0x251C # ├
0x84 0x2524 # ┤
0x85 0x252C # ┬
0x86 0x2534 # ┴
0x87 0x253C # ┼
0x88 0x256D # ╭
0x89 0x256E # ╮
0x8A 0x256F # ╯
0x8B 0x2570 # ╰
0x8C 0x2571 #
0x8D 0x2572 # ╲
0x8E 0x2573 #
0x8F 0x2550 # ═
0x90 0x2551 # ║
0x91 0x2554 # ╔
0x92 0x2557 # ╗
0x93 0x255a # ╚
0x94 0x255D # ╝
0x95 0x2560 # ╠
0x96 0x2563 # ╣
0x97 0x2566 # ╦
0x98 0x2569 # ╩
0x99 0x256C # ╬
0x9A 0xF04E # 
0x9B 0xF050 # 
0x9C 0xF051 # 
0x9D 0xF052 # 
0x9E 0xF048 # 
0x9F 0xE0B0 # 
0xA0 0xE0B2 # 
0xA1 0xE0B4 # 
0xA2 0xE0B6 # 
0xA3 0xE0B8 # 
0xA4 0xE0BA # 
0xA5 0xE0BC # 
0xA6 0xE0BE # 
0xA7 0x2581 # ▁
0xA8 0x2582 # ▂
0xA9 0x2583 # ▃
0xAA 0x2584 # ▄
0xAB 0x2585 # ▅
0xAC 0x2586 # ▆
0xAD 0x2587 # ▇
0xAE 0x2588 # █
0xAF 0x2589 # ▉
0xB0 0x258A # ▊
0xB1 0x258B # ▋
0xB2 0x258C # ▌
0xB3 0x258D # ▍
0xB4 0x258E # ▎
0xB5 0x258F # ▏
0xB6 0x0295 # ʕ
0xB7 0x00B7 # ·
0xB8 0x1D25 # ᴥ
0xB9 0x0294 # ʔ
0xBA 0x2596 # ▖
0xBB 0x2597 # ▗
0xBC 0x2598 # ▘
0xBD 0x2599 # ▙
0xBE 0x259A # ▚
0xBF 0x259B # ▛
0xC0 0x259C # ▜
0xC1 0x259D # ▝
0xC2 0x259E # ▞
0xC3 0x259F # ▟
0xC4 0x2190 # ←
0xC5 0x2191 # ↑
0xC6 0x2192 # →
0xC7 0x2193 # ↓
0xC8 0x2B60 # ⭠
0xC9 0x2B61 # ⭡
0xCA 0x2B62 # ⭢
0xCB 0x2B63 # ⭣
0xCC 0x2B80 # ⮀
0xCD 0x2B81 # ⮁
0xCE 0x2B82 # ⮂
0xCF 0x2B83 # ⮃
0xD0 0xF049 # 
0xD1 0xF04A # 
0xD2 0x23F3 # ⏳
0xD3 0xF07B # 
0xD4 0xF07C # 
0xD5 0xF114 # 
0xD6 0xF115 # 
0xD7 0xF250 # 
0xD8 0xF251 # 
0xD9 0xF253 # 
0xDA 0xF254 # 
0xDB 0xF461 # 
0xDC 0xF016 # 
0xDD 0xF401 # 
0xDE 0x1F52E # 🔮
0xDF 0xF2DB # 
0xE0 0xF008 # 
0xE1 0x25C7 # ◇
0xE2 0x25C8 # ◈
0xE3 0x1F311 # 🌑
0xE4 0x1F312 # 🌒
0xE5 0x1F313 # 🌓
0xE6 0x1F314 # 🌔
0xE7 0x1F315 # 🌕
0xE8 0x1F316 # 🌖
0xE9 0x1F317 # 🌗
0xEA 0x1F318 # 🌘
0xEB 0xF04C # 
0xEC 0x2714 # ✔
0xED 0x2718 # ✘
0xEE 0x25C6 # ◆
0xEF 0xF15D # 
0xF0 0xF15E # 
0xF1 0xF071 # 
0xF2 0xF449 # 
0xF3 0xF529 # 
0xF4 0xF658 # 
0xF5 0xF659 # 
0xF6 0x1f381 # 🎁
0xF7 0xf05a # 
0xF8 0xf06a # 
0xF9 0xf834 # 
0xFA 0xf835 # 
0xFB 0x2690 # ⚐
0xFC 0x2691 # ⚑
0xFD 0xf8d7 # 
0xFE 0xf0e7 # 
0xFF 0xf7d9 # 
+201 -134
View File
@@ -1,10 +1,9 @@
#![allow(clippy::upper_case_acronyms)] #![allow(clippy::upper_case_acronyms)]
use core::panic; #[cfg(not(target_arch = "wasm32"))]
use std::process::exit; use termion::{clear, color, cursor::Goto};
#[cfg(target_arch = "wasm32")]
use termion::cursor::Goto; use web_sys::console;
use termion::{clear, color};
use crate::cpu::{Cpu, StatusFlag}; use crate::cpu::{Cpu, StatusFlag};
use crate::memory::{MemoryReader, MemoryWriter}; use crate::memory::{MemoryReader, MemoryWriter};
@@ -23,47 +22,13 @@ pub struct Instruction<'a> {
impl Instruction<'_> { impl Instruction<'_> {
pub fn call(&self, cpu: &mut Cpu) { pub fn call(&self, cpu: &mut Cpu) {
// TODO: add flag to print these if cpu.debug {
// print!("{}{}a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", Goto(0, 31),clear::UntilNewline, a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi); self.debug(cpu);
// print!( }
// "{}instruction: {:?}, pc: {:#04x}",
// Goto(0, 31),
// self.name,
// cpu.pc
// );
// print!(
// "{}{}instruction: {:?}, mode: {:?}",
// Goto(0, 32),
// clear::UntilNewline,
// self.name,
// self.addr_mode
// );
// print!(
// "{}{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {}{}{:02x}{}{} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
// Goto(0, 33),
// cpu.read(cpu.pc.wrapping_sub(8)),
// cpu.read(cpu.pc.wrapping_sub(7)),
// cpu.read(cpu.pc.wrapping_sub(6)),
// cpu.read(cpu.pc.wrapping_sub(5)),
// cpu.read(cpu.pc.wrapping_sub(4)),
// cpu.read(cpu.pc.wrapping_sub(3)),
// cpu.read(cpu.pc.wrapping_sub(2)),
// cpu.read(cpu.pc.wrapping_sub(1)),
// color::Bg(color::Rgb(0xFF, 0xCC, 0x00)),
// color::Fg(color::Rgb(0x11, 0x05, 0x00)),
// cpu.read(cpu.pc),
// color::Fg(color::Rgb(0xFF, 0xCC, 0x00)),
// color::Bg(color::Rgb(0x11, 0x05, 0x00)),
// cpu.read(cpu.pc.wrapping_add(1)),
// cpu.read(cpu.pc.wrapping_add(2)),
// cpu.read(cpu.pc.wrapping_add(3)),
// cpu.read(cpu.pc.wrapping_add(4)),
// cpu.read(cpu.pc.wrapping_add(5)),
// cpu.read(cpu.pc.wrapping_add(6)),
// cpu.read(cpu.pc.wrapping_add(7)),
// cpu.read(cpu.pc.wrapping_add(8)),
// );
cpu.pc = cpu.pc.wrapping_add(1); // read instruction byte cpu.pc = cpu.pc.wrapping_add(1); // read instruction byte
#[allow(clippy::single_match)]
match self.instr_fn { match self.instr_fn {
// existence of instr_fn means this is a valid instruction // existence of instr_fn means this is a valid instruction
Some(instr_fn) => { Some(instr_fn) => {
@@ -81,16 +46,119 @@ impl Instruction<'_> {
} // None for address_fn implies it's implied (lol)/stack } // None for address_fn implies it's implied (lol)/stack
} }
} }
None => { None =>
panic!( {
"An invalid instruction was called at {:04x}, with opcode {:02x}", #[cfg(not(target_arch = "wasm32"))]
if cpu.debug {
println!(
"{}An invalid instruction was called at {:04x}, with opcode {:02x}",
Goto(0, 35),
cpu.pc, cpu.pc,
cpu.read(cpu.pc) cpu.read(cpu.pc)
);
cpu.breakpoint();
}
}
}
}
fn debug(&self, cpu: &Cpu) {
#[cfg(not(target_arch = "wasm32"))]
{
// cpu state
print!("{}{}a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", Goto(0, 31),clear::UntilNewline, a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi);
print!(
"{}instruction: {:?}, pc: {:#04x}",
Goto(0, 31),
self.name,
cpu.pc
);
// current instruction and addressing mode
print!(
"{}{}instruction: {:?}, mode: {:?}",
Goto(0, 32),
clear::UntilNewline,
self.name,
self.addr_mode
);
// local memory
print!(
"{}{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {}{}{:02x}{}{} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
Goto(0, 33),
cpu.read(cpu.pc.wrapping_sub(8)),
cpu.read(cpu.pc.wrapping_sub(7)),
cpu.read(cpu.pc.wrapping_sub(6)),
cpu.read(cpu.pc.wrapping_sub(5)),
cpu.read(cpu.pc.wrapping_sub(4)),
cpu.read(cpu.pc.wrapping_sub(3)),
cpu.read(cpu.pc.wrapping_sub(2)),
cpu.read(cpu.pc.wrapping_sub(1)),
color::Bg(color::Rgb(0xFF, 0xCC, 0x00)),
color::Fg(color::Rgb(0x11, 0x05, 0x00)),
cpu.read(cpu.pc),
color::Fg(color::Rgb(0xFF, 0xCC, 0x00)),
color::Bg(color::Rgb(0x11, 0x05, 0x00)),
cpu.read(cpu.pc.wrapping_add(1)),
cpu.read(cpu.pc.wrapping_add(2)),
cpu.read(cpu.pc.wrapping_add(3)),
cpu.read(cpu.pc.wrapping_add(4)),
cpu.read(cpu.pc.wrapping_add(5)),
cpu.read(cpu.pc.wrapping_add(6)),
cpu.read(cpu.pc.wrapping_add(7)),
cpu.read(cpu.pc.wrapping_add(8)),
);
// scratchpad
print!(
"{} {:02x} {:02x} {:02x}",
Goto(0, 34),
cpu.read(0x200),
cpu.read(0x201),
cpu.read(0x202)
) )
} // idk if we should panic here, if we don't it's undefined behavior, which }
// might actually be what we want in the cases where it's an undocumented #[cfg(target_arch = "wasm32")]
// 6502 instruction, but otherwise we should panic prolly, for now i'm just {
// gonna panic let cpu_state = format!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi);
console::log_1(&cpu_state.into());
let curr_instr = format!("instruction: {:?}, mode: {:?}", self.name, self.addr_mode);
console::log_1(&curr_instr.into());
let local_mem = format!(
"{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} %c{:02x}%c {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
cpu.read(cpu.pc.wrapping_sub(8)),
cpu.read(cpu.pc.wrapping_sub(7)),
cpu.read(cpu.pc.wrapping_sub(6)),
cpu.read(cpu.pc.wrapping_sub(5)),
cpu.read(cpu.pc.wrapping_sub(4)),
cpu.read(cpu.pc.wrapping_sub(3)),
cpu.read(cpu.pc.wrapping_sub(2)),
cpu.read(cpu.pc.wrapping_sub(1)),
cpu.read(cpu.pc),
cpu.read(cpu.pc.wrapping_add(1)),
cpu.read(cpu.pc.wrapping_add(2)),
cpu.read(cpu.pc.wrapping_add(3)),
cpu.read(cpu.pc.wrapping_add(4)),
cpu.read(cpu.pc.wrapping_add(5)),
cpu.read(cpu.pc.wrapping_add(6)),
cpu.read(cpu.pc.wrapping_add(7)),
cpu.read(cpu.pc.wrapping_add(8)),
);
console::log_3(
&local_mem.into(),
&"background: black; color: white".into(),
&"background: unset; color: unset".into(),
);
let scratchpad = format!(
"scratchpad: {:02x} {:02x} {:02x}",
cpu.read(0x200),
cpu.read(0x201),
cpu.read(0x202)
);
console::log_1(&scratchpad.into());
} }
} }
} }
@@ -209,7 +277,6 @@ fn zero_page_indirect_indexed_with_y(
let address = cpu.read_word(zp as u16); let address = cpu.read_word(zp as u16);
let address: u16 = address.wrapping_add(cpu.y as u16); let address: u16 = address.wrapping_add(cpu.y as u16);
cpu.pc = cpu.pc.wrapping_add(1); cpu.pc = cpu.pc.wrapping_add(1);
println!("{}indirect addr: {:#04x}", Goto(1, 39), address);
address address
} }
@@ -255,7 +322,7 @@ fn branch(cpu: &mut Cpu, condition: bool, address: u16) {
cpu.pc = cpu.pc.wrapping_add(1); cpu.pc = cpu.pc.wrapping_add(1);
} }
fn brkpt(cpu: &mut Cpu, address: Option<u16>) { fn breakpoint(cpu: &mut Cpu, _address: Option<u16>) {
cpu.breakpoint() cpu.breakpoint()
} }
@@ -264,7 +331,7 @@ fn adc(cpu: &mut Cpu, address: Option<u16>) {
let byte = cpu.read(address); let byte = cpu.read(address);
let carry = cpu.get_flag(StatusFlag::Carry); let carry = cpu.get_flag(StatusFlag::Carry);
let result = cpu.a as u16 + byte as u16 + carry as u16; let result = cpu.a as u16 + byte as u16 + carry as u16;
cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::MAX));
cpu.set_flag(StatusFlag::Zero, result as u8 == 0); cpu.set_flag(StatusFlag::Zero, result as u8 == 0);
cpu.set_flag( cpu.set_flag(
StatusFlag::Overflow, StatusFlag::Overflow,
@@ -786,7 +853,7 @@ fn sbc(cpu: &mut Cpu, address: Option<u16>) {
let byte = cpu.read(address); let byte = cpu.read(address);
let carry = cpu.get_flag(StatusFlag::Carry); let carry = cpu.get_flag(StatusFlag::Carry);
let result = cpu.a as u16 + byte as u16 + !carry as u16; let result = cpu.a as u16 + byte as u16 + !carry as u16;
cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::MAX));
cpu.set_flag(StatusFlag::Zero, result as u8 == 0); cpu.set_flag(StatusFlag::Zero, result as u8 == 0);
cpu.set_flag( cpu.set_flag(
StatusFlag::Overflow, StatusFlag::Overflow,
@@ -948,7 +1015,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 7, cycles: 7,
name: "brk", name: "brk",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(ora), instr_fn: Some(ora),
@@ -958,18 +1025,18 @@ pub const OPCODES: [Instruction; 256] = [
addr_mode: "zero_page_indexed_indirect", addr_mode: "zero_page_indexed_indirect",
}, },
Instruction { Instruction {
instr_fn: Some(brkpt), instr_fn: Some(breakpoint),
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "brkpt", name: "breakpoint",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "end",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(tsb), instr_fn: Some(tsb),
@@ -1004,7 +1071,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 3, cycles: 3,
name: "php", name: "php",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(ora), instr_fn: Some(ora),
@@ -1025,7 +1092,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(tsb), instr_fn: Some(tsb),
@@ -1081,7 +1148,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(trb), instr_fn: Some(trb),
@@ -1116,7 +1183,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "clc", name: "clc",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(ora), instr_fn: Some(ora),
@@ -1137,7 +1204,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(trb), instr_fn: Some(trb),
@@ -1186,14 +1253,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(bit), instr_fn: Some(bit),
@@ -1228,7 +1295,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 4, cycles: 4,
name: "plp", name: "plp",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(and), instr_fn: Some(and),
@@ -1249,7 +1316,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(bit), instr_fn: Some(bit),
@@ -1305,7 +1372,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(bit), instr_fn: Some(bit),
@@ -1340,7 +1407,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "sec", name: "sec",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(and), instr_fn: Some(and),
@@ -1361,7 +1428,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(bit), instr_fn: Some(bit),
@@ -1396,7 +1463,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 6, cycles: 6,
name: "rti", name: "rti",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(eor), instr_fn: Some(eor),
@@ -1410,21 +1477,21 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(eor), instr_fn: Some(eor),
@@ -1452,7 +1519,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 3, cycles: 3,
name: "pha", name: "pha",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(eor), instr_fn: Some(eor),
@@ -1473,7 +1540,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(jmp), instr_fn: Some(jmp),
@@ -1529,14 +1596,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(eor), instr_fn: Some(eor),
@@ -1564,7 +1631,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "cli", name: "cli",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(eor), instr_fn: Some(eor),
@@ -1578,21 +1645,21 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 3, cycles: 3,
name: "phy", name: "phy",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(eor), instr_fn: Some(eor),
@@ -1620,7 +1687,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 6, cycles: 6,
name: "rts", name: "rts",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(adc), instr_fn: Some(adc),
@@ -1634,14 +1701,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(stz), instr_fn: Some(stz),
@@ -1676,7 +1743,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 4, cycles: 4,
name: "pla", name: "pla",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(adc), instr_fn: Some(adc),
@@ -1697,7 +1764,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(jmp), instr_fn: Some(jmp),
@@ -1753,7 +1820,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(stz), instr_fn: Some(stz),
@@ -1788,7 +1855,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "sei", name: "sei",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(adc), instr_fn: Some(adc),
@@ -1802,14 +1869,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 4, cycles: 4,
name: "ply", name: "ply",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(jmp), instr_fn: Some(jmp),
@@ -1858,14 +1925,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(sty), instr_fn: Some(sty),
@@ -1900,7 +1967,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "dey", name: "dey",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(bit), instr_fn: Some(bit),
@@ -1914,14 +1981,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "txa", name: "txa",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(sty), instr_fn: Some(sty),
@@ -1977,7 +2044,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(sty), instr_fn: Some(sty),
@@ -2012,7 +2079,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "tya", name: "tya",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(sta), instr_fn: Some(sta),
@@ -2026,14 +2093,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "txs", name: "txs",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(stz), instr_fn: Some(stz),
@@ -2089,7 +2156,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(ldy), instr_fn: Some(ldy),
@@ -2124,7 +2191,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "tay", name: "tay",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(lda), instr_fn: Some(lda),
@@ -2138,14 +2205,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "tax", name: "tax",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(ldy), instr_fn: Some(ldy),
@@ -2201,7 +2268,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(ldy), instr_fn: Some(ldy),
@@ -2236,7 +2303,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "clv", name: "clv",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(lda), instr_fn: Some(lda),
@@ -2250,14 +2317,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "tsx", name: "tsx",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(ldy), instr_fn: Some(ldy),
@@ -2306,14 +2373,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(cpy), instr_fn: Some(cpy),
@@ -2348,7 +2415,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "iny", name: "iny",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(cmp), instr_fn: Some(cmp),
@@ -2362,14 +2429,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "dex", name: "dex",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(wai), instr_fn: Some(wai),
address_fn: None, address_fn: None,
cycles: 3, cycles: 3,
name: "wai", name: "wai",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(cpy), instr_fn: Some(cpy),
@@ -2425,14 +2492,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(cmp), instr_fn: Some(cmp),
@@ -2460,7 +2527,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "cld", name: "cld",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(cmp), instr_fn: Some(cmp),
@@ -2474,21 +2541,21 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 3, cycles: 3,
name: "phx", name: "phx",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(stp), instr_fn: Some(stp),
address_fn: None, address_fn: None,
cycles: 3, cycles: 3,
name: "stp", name: "stp",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(cmp), instr_fn: Some(cmp),
@@ -2530,14 +2597,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(cpx), instr_fn: Some(cpx),
@@ -2572,7 +2639,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "inx", name: "inx",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(sbc), instr_fn: Some(sbc),
@@ -2586,14 +2653,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "nop", name: "nop",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(cpx), instr_fn: Some(cpx),
@@ -2649,14 +2716,14 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(sbc), instr_fn: Some(sbc),
@@ -2684,7 +2751,7 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 2, cycles: 2,
name: "sed", name: "sed",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(sbc), instr_fn: Some(sbc),
@@ -2698,21 +2765,21 @@ pub const OPCODES: [Instruction; 256] = [
address_fn: None, address_fn: None,
cycles: 4, cycles: 4,
name: "plx", name: "plx",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: None, instr_fn: None,
address_fn: None, address_fn: None,
cycles: 0, cycles: 0,
name: "none", name: "none",
addr_mode: "none", addr_mode: "implied",
}, },
Instruction { Instruction {
instr_fn: Some(sbc), instr_fn: Some(sbc),
+5
View File
@@ -1,9 +1,12 @@
use minifb::InputCallback; use minifb::InputCallback;
use minifb::Key as MKey; use minifb::Key as MKey;
#[cfg(not(target_arch = "wasm32"))]
use termion::event::Key; use termion::event::Key;
use crate::memory::{MemHandle, MemoryWriter}; use crate::memory::{MemHandle, MemoryWriter};
#[derive(Debug, Clone)]
pub struct Keyboard { pub struct Keyboard {
memory: MemHandle, memory: MemHandle,
} }
@@ -13,6 +16,7 @@ impl Keyboard {
Self { memory } Self { memory }
} }
#[cfg(not(target_arch = "wasm32"))]
pub fn clear_keys(&self) { pub fn clear_keys(&self) {
self.memory.write(0x4400, 0x00); self.memory.write(0x4400, 0x00);
self.memory.write(0x4401, 0x00); self.memory.write(0x4401, 0x00);
@@ -22,6 +26,7 @@ impl Keyboard {
self.memory.write(0x4405, 0x00); self.memory.write(0x4405, 0x00);
} }
#[cfg(not(target_arch = "wasm32"))]
pub fn read_keys(&self, key: Key) { pub fn read_keys(&self, key: Key) {
let mut row0 = 0; let mut row0 = 0;
let mut row1 = 0; let mut row1 = 0;
+204
View File
@@ -0,0 +1,204 @@
pub mod cpu;
mod instructions;
mod keyboard;
pub mod memory;
mod platform;
pub mod video;
use crate::cpu::Cpu;
use crate::keyboard::Keyboard;
use crate::memory::Mem;
use crate::video::Renderer;
use cpu::CpuController;
use memory::MemHandle;
use minifb::Window;
#[cfg(not(target_arch = "wasm32"))]
use platform::native as imp;
#[cfg(target_arch = "wasm32")]
use platform::wasm as imp;
pub struct GeorgeEmu(imp::GeorgeEmu);
impl GeorgeEmu {
pub fn builder() -> GeorgeEmuBuilder<NoRom, NoWindow> {
GeorgeEmuBuilder::new()
}
pub fn draw(&mut self) {
self.0.draw()
}
pub fn cycle(&mut self) {
self.0.cycle()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn run(&mut self) {
self.0.run()
}
}
pub struct SomeRom();
pub struct NoRom();
pub struct SomeWindow(Window);
pub struct NoWindow();
pub struct GeorgeEmuBuilder<Rom, Window> {
rom: Rom,
cpu: Option<Cpu>,
keyboard: Option<Keyboard>,
renderer: Option<Renderer>,
cpu_controller: Option<CpuController>,
memory_handle: Option<MemHandle>,
window: Window,
}
impl GeorgeEmuBuilder<NoRom, NoWindow> {
fn new() -> Self {
GeorgeEmuBuilder {
rom: NoRom(),
cpu: None,
keyboard: None,
renderer: None,
cpu_controller: None,
memory_handle: None,
window: NoWindow(),
}
}
pub fn rom(self, rom: [u8; 0x8000]) -> GeorgeEmuBuilder<SomeRom, NoWindow> {
let mut memory = Mem::new();
memory.load_bytes(&rom);
let memory_handle = MemHandle::new(memory);
let (cpu, cpu_controller) = Cpu::new_with_control(memory_handle.clone());
let Self {
renderer,
keyboard,
window,
..
} = self;
GeorgeEmuBuilder {
cpu: Some(cpu),
rom: SomeRom(),
keyboard,
renderer,
cpu_controller: Some(cpu_controller),
memory_handle: Some(memory_handle),
window,
}
}
}
impl GeorgeEmuBuilder<SomeRom, NoWindow> {
/// Adds a minifb window to the emulator
pub fn window(self, mut window: Window) -> GeorgeEmuBuilder<SomeRom, SomeWindow> {
let Self {
rom,
cpu,
keyboard,
ref cpu_controller,
ref memory_handle,
..
} = self;
let renderer = Renderer::new(
self.cpu_controller.clone().unwrap(),
self.memory_handle.clone().unwrap(),
);
window.set_input_callback(Box::new(Keyboard::new(
self.memory_handle.as_ref().unwrap().clone(),
)));
GeorgeEmuBuilder {
cpu,
rom,
keyboard,
renderer: Some(renderer),
cpu_controller: cpu_controller.clone(),
memory_handle: memory_handle.clone(),
window: SomeWindow(window),
}
}
/// Enables debug printing and breakpoint triggering
pub fn debug(mut self) -> Self {
self.cpu.as_mut().unwrap().debug = true;
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn build(self) -> GeorgeEmu {
let keyboard = Keyboard::new(self.memory_handle.clone().unwrap());
GeorgeEmu(imp::GeorgeEmu::new(
self.cpu.unwrap(),
self.renderer.unwrap(),
Some(keyboard),
None,
))
}
}
impl GeorgeEmuBuilder<NoRom, SomeWindow> {
pub fn rom(self, rom: [u8; 0x8000]) -> GeorgeEmuBuilder<SomeRom, SomeWindow> {
let mut memory = Mem::new();
memory.load_bytes(&rom);
let memory_handle = MemHandle::new(memory);
let (cpu, cpu_controller) = Cpu::new_with_control(memory_handle.clone());
let Self {
renderer,
keyboard,
window,
..
} = self;
GeorgeEmuBuilder {
cpu: Some(cpu),
rom: SomeRom(),
keyboard,
renderer,
cpu_controller: Some(cpu_controller),
memory_handle: Some(memory_handle),
window,
}
}
/// Enables debug printing and breakpoint triggering
pub fn debug(mut self) -> Self {
self.cpu.as_mut().unwrap().debug = true;
self
}
}
impl GeorgeEmuBuilder<SomeRom, SomeWindow> {
/// Enables debug printing and breakpoint triggering
pub fn debug(mut self) -> Self {
self.cpu.as_mut().unwrap().debug = true;
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn build(self) -> GeorgeEmu {
let keyboard = Keyboard::new(self.memory_handle.clone().unwrap());
GeorgeEmu(imp::GeorgeEmu::new(
self.cpu.unwrap(),
self.renderer.unwrap(),
None,
Some(self.window.0),
))
}
#[cfg(target_arch = "wasm32")]
pub fn build(self) -> GeorgeEmu {
GeorgeEmu(imp::GeorgeEmu::new(
self.cpu.unwrap(),
self.renderer.unwrap(),
self.window.0,
))
}
}
-74
View File
@@ -1,74 +0,0 @@
mod cli;
mod cpu;
mod error;
mod instructions;
mod keyboard;
mod memory;
mod types;
mod video;
use crate::cpu::Cpu;
use crate::keyboard::Keyboard;
use crate::memory::Mem;
use crate::video::Screen;
use cli::get_input;
use crossterm::cursor;
use memory::MemHandle;
use std::io::stdout;
use std::thread::{self, sleep};
use std::time::Duration;
use termion::cursor::Goto;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::{async_stdin, clear};
fn main() {
let _stdout = stdout().into_raw_mode();
let config = get_input();
let mut memory = Mem::new();
let _ = memory.load_rom(&config.rom);
let shared_memory = MemHandle::new(memory);
let screen_memory = shared_memory.clone();
let cpu_memory = shared_memory.clone();
let keyboard_memory = shared_memory.clone();
let keyboard = Keyboard::new(keyboard_memory);
let (mut cpu, cpu_controller) = Cpu::new_with_control(cpu_memory);
cpu.reset();
thread::spawn(move || loop {
cpu.cycle();
});
let screen_remote = cpu_controller.clone();
let mut screen = Screen::new(&config, screen_remote, screen_memory);
let mut stdin = async_stdin().keys();
print!("{}{}", cursor::Hide, clear::All,);
loop {
screen.draw();
if let Some(Ok(key)) = stdin.next() {
keyboard.read_keys(key);
match key {
Key::Char('q') => {
break;
}
Key::Char('`') => cpu_controller.toggle(),
Key::Char('\n') => cpu_controller.cycle(),
Key::Char('i') => cpu_controller.irq(),
_ => {}
}
} else {
keyboard.clear_keys();
}
sleep(Duration::from_millis(16));
}
print!("{}{}{}", clear::All, cursor::Show, Goto(1, 1));
}
+36 -26
View File
@@ -1,10 +1,6 @@
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use core::panic;
use std::cell::RefCell;
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::exit;
use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{fs::File, io::Read}; use std::{fs::File, io::Read};
@@ -16,17 +12,17 @@ impl MemHandle {
Self(Arc::new(Mutex::new(memory))) Self(Arc::new(Mutex::new(memory)))
} }
pub fn read(&self, address: u16) -> u8 { pub fn read(&self, address: u16) -> u8 {
let memory = self.0.lock().unwrap(); let memory = self.0.try_lock().unwrap();
memory.read(address) memory.read(address)
} }
pub fn write(&self, address: u16, data: u8) { pub fn write(&self, address: u16, data: u8) {
let mut memory = self.0.lock().unwrap(); let mut memory = self.0.try_lock().unwrap();
memory.write(address, data); memory.write(address, data);
} }
pub fn dump(&self) { pub fn dump(&self) -> [u8; 0x10000] {
let memory = self.0.lock().unwrap(); let memory = self.0.lock().unwrap();
let _ = memory.dump(PathBuf::from_str("./cpu_dump.bin").unwrap()); memory.dump()
} }
pub fn poke(&self, address: u16) { pub fn poke(&self, address: u16) {
let memory = self.0.lock().unwrap(); let memory = self.0.lock().unwrap();
@@ -51,18 +47,26 @@ pub struct Mem([u8; 0x10000]);
impl Default for Mem { impl Default for Mem {
fn default() -> Self { fn default() -> Self {
Self([0; 0x10000]) let bytes = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/roms/george.rom"));
let padding = [0; 0x8000];
let mem: [u8; 0x10000] = {
let mut rom: [u8; 0x10000] = [0; 0x10000];
let (one, two) = rom.split_at_mut(padding.len());
one.copy_from_slice(&padding);
two.copy_from_slice(bytes);
rom
};
Self(mem)
} }
} }
impl Mem { impl Mem {
pub fn new() -> Self { pub fn new() -> Self {
Mem::default() Self([0; 0x10000])
} }
pub fn dump(&self, path: PathBuf) -> io::Result<()> { pub fn dump(&self) -> [u8; 0x10000] {
let mut outfile = File::create(path)?; self.0
outfile.write_all(&self.0)?;
Ok(())
} }
pub fn read(&self, address: u16) -> u8 { pub fn read(&self, address: u16) -> u8 {
@@ -97,16 +101,22 @@ impl Mem {
pub fn poke(&self, address: u16) { pub fn poke(&self, address: u16) {
println!("{:02x}", self.read(address)); println!("{:02x}", self.read(address));
} }
//pub fn read_from_bin(&mut self, f: File) -> Result<()> { pub fn load_bytes(&mut self, bytes: &[u8]) {
// let bytes = f.bytes(); for (address, byte) in bytes.iter().enumerate() {
// for (address, byte) in bytes.enumerate() { self.write(address as u16 + 0x8000, *byte)
// match byte { }
// Ok(value) => self.write(address as u16, value), }
// Err(_) => {
// bail!("couldn't write byte {:#04x}", address); pub fn read_from_bin(&mut self, f: File) -> Result<()> {
// } let bytes = f.bytes();
// } for (address, byte) in bytes.enumerate() {
// } match byte {
// Ok(()) Ok(value) => self.write(address as u16 + 0x8000, value),
//} Err(_) => {
bail!("couldn't write byte {:#04x}", address);
}
}
}
Ok(())
}
} }
+4
View File
@@ -0,0 +1,4 @@
#[cfg(not(target_arch = "wasm32"))]
pub mod native;
#[cfg(target_arch = "wasm32")]
pub mod wasm;
+101
View File
@@ -0,0 +1,101 @@
use minifb::Window;
use std::{
io::stdout,
process::exit,
thread::sleep,
time::{Duration, Instant},
};
use termion::{
async_stdin, clear,
cursor::{self, Goto},
event::Key,
input::TermRead,
raw::IntoRawMode,
};
use crate::{cpu::Cpu, keyboard::Keyboard, video::Renderer};
#[derive(Debug)]
pub struct GeorgeEmu {
cpu: Cpu,
renderer: Renderer,
input: Option<Keyboard>,
window: Option<Window>,
}
impl GeorgeEmu {
pub fn new(
mut cpu: Cpu,
renderer: Renderer,
input: Option<Keyboard>,
window: Option<Window>,
) -> Self {
cpu.reset();
Self {
cpu,
input,
renderer,
window,
}
}
pub fn draw(&mut self) {
self.renderer.render(self.window.as_mut())
}
pub fn cycle(&mut self) {
self.cpu.cycle()
}
pub fn run(&mut self) {
if self.window.is_none() {
let _ = stdout().into_raw_mode();
print!("{}{}", cursor::Hide, clear::All,);
}
let clock_interval = Duration::from_nanos(500);
let frame_interval = Duration::from_millis(16);
let mut next_clock = Instant::now() + clock_interval;
let mut next_frame = Instant::now() + frame_interval;
loop {
let now = Instant::now();
if now >= next_clock {
self.cpu.cycle();
next_clock += clock_interval;
}
if now >= next_frame {
if self.window.is_none() {
self.handle_input();
}
self.draw();
next_frame += frame_interval;
}
let next_run = std::cmp::min(next_clock, next_frame);
let sleep_dur = next_run - now;
sleep(sleep_dur)
}
}
fn handle_input(&mut self) {
let mut stdin = async_stdin().keys();
if let Some(Ok(key)) = stdin.next() {
match key {
Key::Char('q') => {
print!("{}{}{}", clear::All, cursor::Show, Goto(1, 1));
exit(0);
}
Key::Char('`') => self.cpu.toggle_stopped(),
Key::Char('\n') => self.cpu.cycle(),
Key::Char('i') => self.cpu.interrupt(),
_ => {
self.input.as_mut().unwrap().read_keys(key);
}
}
} else {
self.input.as_mut().unwrap().clear_keys();
}
}
}
+29
View File
@@ -0,0 +1,29 @@
use minifb::Window;
use crate::{cpu::Cpu, video::Renderer};
#[derive(Debug)]
pub struct GeorgeEmu {
cpu: Cpu,
renderer: Renderer,
window: Window,
}
impl GeorgeEmu {
pub fn new(mut cpu: Cpu, renderer: Renderer, window: Window) -> Self {
cpu.reset();
Self {
cpu,
renderer,
window,
}
}
pub fn draw(&mut self) {
self.renderer.render(Some(&mut self.window));
}
pub fn cycle(&mut self) {
self.cpu.cycle()
}
}
-4
View File
@@ -1,4 +0,0 @@
use std::{u16, u8};
pub type Byte = u8;
pub type Word = u16;
+82 -162
View File
@@ -1,133 +1,52 @@
use minifb::{Scale, ScaleMode, Window, WindowOptions}; #[cfg(not(target_arch = "wasm32"))]
use serde::{Deserialize, Serialize}; use std::io::{self, Write};
use std::{env, fs::File, io::Read, path::Path};
#[cfg(not(target_arch = "wasm32"))]
use termion::{ use termion::{
color::{self, Bg, Color, Fg}, color::{self, Bg, Fg},
cursor::Goto, cursor::Goto,
screen::IntoAlternateScreen,
}; };
use crate::{ use crate::{
cli::Config,
cpu::CpuController, cpu::CpuController,
keyboard::Keyboard,
memory::{MemHandle, MemoryReader}, memory::{MemHandle, MemoryReader},
types::{Byte, Word},
};
use std::{
fs::File,
io::{self, Read, Write},
path::Path,
process::exit,
thread::sleep,
time::{Duration, Instant},
}; };
use minifb::Window;
#[cfg(not(target_arch = "wasm32"))]
const FG_COLOR: u32 = 0xFFCC00; const FG_COLOR: u32 = 0xFFCC00;
#[cfg(not(target_arch = "wasm32"))]
const BG_COLOR: u32 = 0x110500; const BG_COLOR: u32 = 0x110500;
// Wasm colors are ABGR
#[cfg(target_arch = "wasm32")]
const FG_COLOR: u32 = 0xFF00CCFF;
#[cfg(target_arch = "wasm32")]
const BG_COLOR: u32 = 0xFF000511;
const WIDTH: usize = 512; const WIDTH: usize = 512;
const HEIGHT: usize = 380; const HEIGHT: usize = 380;
pub fn get_char_bin<P>(char_rom: Option<P>) -> [u8; 0x8000] // pub fn get_char_bin<P>(char_rom: Option<P>) -> [u8; 0x8000]
where // where
P: AsRef<Path>, // P: AsRef<Path>,
{ // {
match char_rom { // match char_rom {
Some(path) => { // Some(path) => {
let mut file = File::open(path).unwrap(); // let mut file = File::open(path).unwrap();
let mut bin = vec![0; 0x8000]; // let mut bin = vec![0; 0x8000];
file.read_exact(&mut bin).unwrap(); // file.read_exact(&mut bin).unwrap();
// println!("reading char rom"); // // println!("reading char rom");
bin.try_into().unwrap() // bin.try_into().unwrap()
} // }
None => *include_bytes!("./roms/cozette.rom"), // None => *include_bytes!("./roms/cozette.rom"),
} // }
} // }
trait Renderer { const CHAR_ROM: &[u8; 0x8000] = include_bytes!(concat!(env!("OUT_DIR"), "/cozette.rom"));
fn render(&mut self) {} #[cfg(not(target_arch = "wasm32"))]
} const ASCII_LOOKUP: [&str; 256] = [
#[derive(Serialize, Deserialize, Debug, Default)]
pub enum ScreenType {
Window,
#[default]
Terminal,
}
pub struct WindowRenderer {
char_rom: [u8; 0x8000],
window: Window,
memory: MemHandle,
}
impl WindowRenderer {
pub fn new<P>(memory: MemHandle, char_rom: Option<P>, window: Window) -> Self
where
P: AsRef<Path>,
{
let char_rom = get_char_bin(char_rom);
Self {
memory,
window,
char_rom,
}
}
}
impl Renderer for WindowRenderer {
fn render(&mut self) {
// the rest of this function is arcane wizardry
// based on the specifics of george's weird
// display and characters... don't fuck around w it
let mut i = 0;
let mut buffer = [0; 512 * 380];
for char_row in 0..29 {
for char_col in 0..64 {
let ascii = self.read(0x6000 + i);
i += 1;
for row in 0..13 {
let byte = self.char_rom[ascii as usize + (row * 0x100)];
for bit_index in (0..8).rev() {
let buffer_index =
((char_row) * 13 + (row)) * 512 + (char_col * 8 + bit_index);
if (byte << bit_index) & 0x80 == 0x80 {
buffer[buffer_index] = FG_COLOR;
} else {
buffer[buffer_index] = BG_COLOR;
}
}
}
}
}
self.window
.update_with_buffer(&buffer, WIDTH, HEIGHT)
.unwrap();
}
}
impl MemoryReader for WindowRenderer {
fn read(&self, address: Word) -> Byte {
self.memory.read(address)
}
}
pub struct TerminalRenderer {
memory: MemHandle,
}
impl TerminalRenderer {
pub fn new(memory: MemHandle) -> Self {
Self { memory }
}
}
impl MemoryReader for TerminalRenderer {
fn read(&self, address: Word) -> Byte {
self.memory.read(address)
}
}
const ASCII_LOOPUP: [&str; 256] = [
" ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "奔", "", "", "", "", "", "", " ", "!", "\"", "#", "", "", "", "", "", "", "", "奔", "", "", "", "", "", "", " ", "!", "\"", "#",
"$", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "$", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6",
@@ -144,15 +63,55 @@ const ASCII_LOOPUP: [&str; 256] = [
"", "", "", "", "🎁", "", "", "", "", "", "", "", "", "", "", "", "", "", "🎁", "", "", "", "", "", "", "", "", "",
]; ];
impl Renderer for TerminalRenderer { #[derive(Debug)]
fn render(&mut self) { pub struct Renderer {
memory: MemHandle,
controller: CpuController,
}
impl Renderer {
pub fn new(controller: CpuController, memory: MemHandle) -> Self {
Self { memory, controller }
}
pub fn render(&self, window: Option<&mut Window>) {
match window {
Some(window) => {
// the rest of this function is arcane wizardry
// based on the specifics of george's weird
// display and characters... don't fuck around w it
let mut i = 0;
let mut buffer = [0; WIDTH * HEIGHT];
for char_row in 0..29 {
for char_col in 0..64 {
let ascii = self.read(0x6000 + i);
i += 1;
for row in 0..13 {
let byte = CHAR_ROM[ascii as usize + (row * 0x100)];
for bit_index in (0..8).rev() {
let buffer_index =
((char_row) * 13 + (row)) * 512 + (char_col * 8 + bit_index);
if (byte << bit_index) & 0x80 == 0x80 {
buffer[buffer_index] = FG_COLOR;
} else {
buffer[buffer_index] = BG_COLOR;
}
}
}
}
}
window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap();
}
None => {
#[cfg(not(target_arch = "wasm32"))]
{
let mut stdout = io::stdout(); let mut stdout = io::stdout();
let mut i = 0; let mut i = 0;
for char_row in 0..29 { for char_row in 0..29 {
for char_col in 0..64 { for char_col in 0..64 {
let ascii = self.read(0x6000 + i); let ascii = self.read(0x6000 + i);
i += 1; i += 1;
let char = ASCII_LOOPUP[ascii as usize]; let char = ASCII_LOOKUP[ascii as usize];
let _ = write!( let _ = write!(
// FG_COLOR = 0xFFCC00 // FG_COLOR = 0xFFCC00
// BG_COLOR = 0x110500 // BG_COLOR = 0x110500
@@ -166,52 +125,13 @@ impl Renderer for TerminalRenderer {
} }
} }
} }
pub struct Screen {
renderer: Box<dyn Renderer>,
controller: CpuController,
} }
impl Screen {
pub fn new(config: &Config, controller: CpuController, memory: MemHandle) -> Self {
let renderer: Box<dyn Renderer> = match config.screen {
ScreenType::Window => {
let mut window = Window::new(
"ʕ·ᴥ·ʔ-☆",
512,
380,
WindowOptions {
resize: true,
borderless: true,
title: true,
transparency: false,
scale: Scale::FitScreen,
scale_mode: ScaleMode::AspectRatioStretch,
topmost: false,
none: true,
},
)
.unwrap();
window.set_input_callback(Box::new(Keyboard::new(memory.clone())));
Box::new(WindowRenderer::new(memory, config.char_rom.clone(), window))
}
ScreenType::Terminal => Box::new(TerminalRenderer::new(memory)),
};
Self {
renderer,
controller,
}
}
pub fn draw(&mut self) {
self.controller.irq(); self.controller.irq();
self.renderer.render(); }
} }
// pub fn run(&mut self) { impl MemoryReader for Renderer {
// loop { fn read(&self, address: u16) -> u8 {
// // sleep(Duration::from_millis(16)); self.memory.read(address)
// self.draw(); }
// }
// }
} }