Compare commits
9 Commits
c2aef4f249
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a37caf8b2 | |||
| 3de2c63fe0 | |||
| 3856d81c47 | |||
| a621ec235a | |||
| f464dbfaf1 | |||
| 7d0b66f418 | |||
| 6e55faa2c0 | |||
| 952b79cf91 | |||
| 8ac0cbc57b |
@@ -1,6 +1,7 @@
|
||||
/target
|
||||
log
|
||||
george.o
|
||||
Cargo.lock
|
||||
.DS_Store
|
||||
*.bin
|
||||
/result
|
||||
|
||||
Generated
-1075
File diff suppressed because it is too large
Load Diff
+19
-6
@@ -2,16 +2,29 @@
|
||||
name = "georgeemu"
|
||||
version = "0.1.0"
|
||||
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
|
||||
|
||||
[build-dependencies]
|
||||
bdf-parser = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.81"
|
||||
bdf = "0.6.0"
|
||||
crossterm = "0.27.0"
|
||||
minifb = "0.27.0"
|
||||
# minifb = "0.25.0"
|
||||
# ratatui = "0.26.3"
|
||||
minifb = { git = "https://github.com/emoon/rust_minifb" }
|
||||
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"
|
||||
toml = "0.8.12"
|
||||
toml = { version = "0.8.12" }
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
+556
-549
File diff suppressed because it is too large
Load Diff
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#! /bin/sh
|
||||
|
||||
rm *.bdf*
|
||||
File diff suppressed because it is too large
Load Diff
+3903
File diff suppressed because it is too large
Load Diff
+3903
File diff suppressed because it is too large
Load Diff
@@ -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 # ⚥
|
||||
Executable
+5
@@ -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
@@ -1,3 +1,3 @@
|
||||
char_rom = "./src/roms/cozette.rom"
|
||||
rom = "./src/roms/george.rom"
|
||||
rom = "./src/roms/keyboard_sys.rom"
|
||||
screen = "Window"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.org $00
|
||||
|
||||
.byte $80
|
||||
|
||||
.org $8000
|
||||
|
||||
reset:
|
||||
bbr7 $00, reset
|
||||
|
||||
.org $fffc
|
||||
.word reset
|
||||
Binary file not shown.
Binary file not shown.
+238
@@ -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
|
||||
Binary file not shown.
+189
@@ -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.
Binary file not shown.
@@ -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.
Binary file not shown.
+173
@@ -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
|
||||
Binary file not shown.
@@ -7,6 +7,6 @@ fi
|
||||
|
||||
set -e
|
||||
|
||||
vasm6502_oldstyle ./src/roms/$1.asm -dotdir -wdc02 -ldots -Fbin -o ./src/roms/$1.rom;
|
||||
cargo run -- rom "./src/roms/$1.rom";
|
||||
vasm6502_oldstyle roms/$1.asm -dotdir -wdc02 -ldots -Fbin -o roms/$1.rom;
|
||||
cargo run -- rom "roms/$1.rom";
|
||||
# hexdump -C ./cpu_dump.bin;
|
||||
|
||||
@@ -7,7 +7,18 @@ use std::{
|
||||
|
||||
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)]
|
||||
struct ConfigBuilder {
|
||||
@@ -27,7 +38,7 @@ impl ConfigBuilder {
|
||||
|
||||
fn build(self) -> Config {
|
||||
let rom = match self.rom {
|
||||
Some(rom) => rom,
|
||||
Some(rom) => Some(rom),
|
||||
None => {
|
||||
println!("no rom was provided :(");
|
||||
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>) {
|
||||
let executable: String = env::args().next().unwrap();
|
||||
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!(" screen, -s, --screen <type>: use the \"terminal\" or \"window\" display type");
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -101,6 +110,7 @@ pub fn get_input() -> Config {
|
||||
}
|
||||
Err(_) => {
|
||||
println!("couldn't find a `george.toml` in the current directory!");
|
||||
println!("\nuse `{executable} --help` to see all config options");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
@@ -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
@@ -2,11 +2,11 @@ use crate::instructions::get_instruction;
|
||||
use crate::memory::{MemHandle, MemoryReader, MemoryWriter};
|
||||
use std::fmt::Display;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::thread::sleep;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::time::Duration;
|
||||
|
||||
use termion::cursor::Goto;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum StatusFlag {
|
||||
Negative = 0b1000_0000,
|
||||
@@ -19,9 +19,10 @@ pub enum StatusFlag {
|
||||
Carry = 0b0000_0001,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CpuController(Sender<CpuControl>);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CpuControl {
|
||||
Irq,
|
||||
Nmi,
|
||||
@@ -35,22 +36,23 @@ impl CpuController {
|
||||
Self(sender)
|
||||
}
|
||||
pub fn irq(&self) {
|
||||
self.0.send(CpuControl::Irq);
|
||||
let _ = self.0.send(CpuControl::Irq);
|
||||
}
|
||||
pub fn nmi(&self) {
|
||||
self.0.send(CpuControl::Nmi);
|
||||
let _ = self.0.send(CpuControl::Nmi);
|
||||
}
|
||||
pub fn toggle(&self) {
|
||||
self.0.send(CpuControl::Toggle);
|
||||
let _ = self.0.send(CpuControl::Toggle);
|
||||
}
|
||||
pub fn cycle(&self) {
|
||||
self.0.send(CpuControl::Cycle);
|
||||
let _ = self.0.send(CpuControl::Cycle);
|
||||
}
|
||||
pub fn data(&self) {
|
||||
self.0.send(CpuControl::Data);
|
||||
let _ = self.0.send(CpuControl::Data);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CpuReceiver(Receiver<CpuControl>);
|
||||
impl CpuReceiver {
|
||||
pub fn new(receiver: Receiver<CpuControl>) -> Self {
|
||||
@@ -58,6 +60,7 @@ impl CpuReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cpu {
|
||||
pub a: u8, // Accumulator Register
|
||||
pub x: u8, // X Register
|
||||
@@ -69,10 +72,10 @@ pub struct Cpu {
|
||||
pub nmi: bool,
|
||||
pub memory: MemHandle,
|
||||
pub pending_cycles: usize,
|
||||
pub debug: bool,
|
||||
receiver: Option<CpuReceiver>,
|
||||
stopped: bool,
|
||||
cycle: bool, // cycle_count: usize,
|
||||
// state_tx: Sender<CpuState>,
|
||||
cycle: bool,
|
||||
}
|
||||
|
||||
impl Display for Cpu {
|
||||
@@ -94,22 +97,28 @@ impl MemoryWriter for Cpu {
|
||||
|
||||
impl Cpu {
|
||||
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 {
|
||||
a: 0x00,
|
||||
x: 0x00,
|
||||
y: 0x00,
|
||||
pc: 0x0000,
|
||||
pc,
|
||||
s: 0xFF,
|
||||
p: 0b0010_0100,
|
||||
irq: false,
|
||||
nmi: false,
|
||||
receiver: None,
|
||||
memory,
|
||||
debug: false,
|
||||
stopped: false,
|
||||
pending_cycles: 0,
|
||||
cycle: false, // cycle_count: 0,
|
||||
// state_tx,
|
||||
cycle: false,
|
||||
}
|
||||
}
|
||||
pub fn with_receiver(mut self, receiver: CpuReceiver) -> Self {
|
||||
@@ -179,13 +188,6 @@ impl Cpu {
|
||||
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) {
|
||||
unimplemented!()
|
||||
}
|
||||
@@ -198,6 +200,10 @@ impl Cpu {
|
||||
self.pc = self.read_word(0xFFFE);
|
||||
}
|
||||
|
||||
pub fn toggle_stopped(&mut self) {
|
||||
self.stopped = !self.stopped;
|
||||
}
|
||||
|
||||
fn receive_control(&mut self) {
|
||||
match &self.receiver {
|
||||
Some(receiver) => {
|
||||
@@ -213,7 +219,8 @@ impl Cpu {
|
||||
self.stopped = !self.stopped;
|
||||
}
|
||||
CpuControl::Cycle => self.cycle = true,
|
||||
CpuControl::Data => {} // self
|
||||
CpuControl::Data => {
|
||||
// self
|
||||
// .state_tx
|
||||
// .send(CpuState {
|
||||
// a: self.a.clone(), // Accumulator Register
|
||||
@@ -228,6 +235,7 @@ impl Cpu {
|
||||
// .unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
@@ -241,6 +249,8 @@ impl Cpu {
|
||||
}
|
||||
self.cycle = false;
|
||||
|
||||
// can't sleep in wasm
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
while self.pending_cycles != 0 {
|
||||
// roughly cycle-accurate timing
|
||||
sleep(Duration::from_nanos(500));
|
||||
@@ -252,45 +262,35 @@ impl Cpu {
|
||||
let opcode = self.read(self.pc);
|
||||
let instruction = get_instruction(opcode);
|
||||
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) {
|
||||
self.stopped = true;
|
||||
}
|
||||
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);
|
||||
// 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)
|
||||
// );
|
||||
if self.debug {
|
||||
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!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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
@@ -1,10 +1,9 @@
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
use core::panic;
|
||||
use std::process::exit;
|
||||
|
||||
use termion::cursor::Goto;
|
||||
use termion::{clear, color};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use termion::{clear, color, cursor::Goto};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use web_sys::console;
|
||||
|
||||
use crate::cpu::{Cpu, StatusFlag};
|
||||
use crate::memory::{MemoryReader, MemoryWriter};
|
||||
@@ -23,47 +22,13 @@ pub struct Instruction<'a> {
|
||||
|
||||
impl Instruction<'_> {
|
||||
pub fn call(&self, cpu: &mut Cpu) {
|
||||
// TODO: add flag to print these
|
||||
// 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
|
||||
// );
|
||||
// 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)),
|
||||
// );
|
||||
if cpu.debug {
|
||||
self.debug(cpu);
|
||||
}
|
||||
|
||||
cpu.pc = cpu.pc.wrapping_add(1); // read instruction byte
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
match self.instr_fn {
|
||||
// existence of instr_fn means this is a valid instruction
|
||||
Some(instr_fn) => {
|
||||
@@ -81,16 +46,119 @@ impl Instruction<'_> {
|
||||
} // None for address_fn implies it's implied (lol)/stack
|
||||
}
|
||||
}
|
||||
None => {
|
||||
panic!(
|
||||
"An invalid instruction was called at {:04x}, with opcode {:02x}",
|
||||
None =>
|
||||
{
|
||||
#[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.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
|
||||
// 6502 instruction, but otherwise we should panic prolly, for now i'm just
|
||||
// gonna panic
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
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: u16 = address.wrapping_add(cpu.y as u16);
|
||||
cpu.pc = cpu.pc.wrapping_add(1);
|
||||
println!("{}indirect addr: {:#04x}", Goto(1, 39), address);
|
||||
address
|
||||
}
|
||||
|
||||
@@ -255,7 +322,7 @@ fn branch(cpu: &mut Cpu, condition: bool, address: u16) {
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -264,7 +331,7 @@ fn adc(cpu: &mut Cpu, address: Option<u16>) {
|
||||
let byte = cpu.read(address);
|
||||
let carry = cpu.get_flag(StatusFlag::Carry);
|
||||
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::Overflow,
|
||||
@@ -786,7 +853,7 @@ fn sbc(cpu: &mut Cpu, address: Option<u16>) {
|
||||
let byte = cpu.read(address);
|
||||
let carry = cpu.get_flag(StatusFlag::Carry);
|
||||
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::Overflow,
|
||||
@@ -948,7 +1015,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 7,
|
||||
name: "brk",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ora),
|
||||
@@ -958,18 +1025,18 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
addr_mode: "zero_page_indexed_indirect",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(brkpt),
|
||||
instr_fn: Some(breakpoint),
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "brkpt",
|
||||
addr_mode: "none",
|
||||
name: "breakpoint",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
name: "end",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(tsb),
|
||||
@@ -1004,7 +1071,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "php",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ora),
|
||||
@@ -1025,7 +1092,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(tsb),
|
||||
@@ -1081,7 +1148,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(trb),
|
||||
@@ -1116,7 +1183,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "clc",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ora),
|
||||
@@ -1137,7 +1204,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(trb),
|
||||
@@ -1186,14 +1253,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1228,7 +1295,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 4,
|
||||
name: "plp",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(and),
|
||||
@@ -1249,7 +1316,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1305,7 +1372,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1340,7 +1407,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "sec",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(and),
|
||||
@@ -1361,7 +1428,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1396,7 +1463,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 6,
|
||||
name: "rti",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1410,21 +1477,21 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1452,7 +1519,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "pha",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1473,7 +1540,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(jmp),
|
||||
@@ -1529,14 +1596,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1564,7 +1631,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "cli",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1578,21 +1645,21 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "phy",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1620,7 +1687,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 6,
|
||||
name: "rts",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(adc),
|
||||
@@ -1634,14 +1701,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(stz),
|
||||
@@ -1676,7 +1743,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 4,
|
||||
name: "pla",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(adc),
|
||||
@@ -1697,7 +1764,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(jmp),
|
||||
@@ -1753,7 +1820,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(stz),
|
||||
@@ -1788,7 +1855,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "sei",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(adc),
|
||||
@@ -1802,14 +1869,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 4,
|
||||
name: "ply",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(jmp),
|
||||
@@ -1858,14 +1925,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sty),
|
||||
@@ -1900,7 +1967,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "dey",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1914,14 +1981,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "txa",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sty),
|
||||
@@ -1977,7 +2044,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sty),
|
||||
@@ -2012,7 +2079,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "tya",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sta),
|
||||
@@ -2026,14 +2093,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "txs",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(stz),
|
||||
@@ -2089,7 +2156,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ldy),
|
||||
@@ -2124,7 +2191,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "tay",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(lda),
|
||||
@@ -2138,14 +2205,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "tax",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ldy),
|
||||
@@ -2201,7 +2268,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ldy),
|
||||
@@ -2236,7 +2303,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "clv",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(lda),
|
||||
@@ -2250,14 +2317,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "tsx",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ldy),
|
||||
@@ -2306,14 +2373,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cpy),
|
||||
@@ -2348,7 +2415,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "iny",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cmp),
|
||||
@@ -2362,14 +2429,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "dex",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(wai),
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "wai",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cpy),
|
||||
@@ -2425,14 +2492,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cmp),
|
||||
@@ -2460,7 +2527,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "cld",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cmp),
|
||||
@@ -2474,21 +2541,21 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "phx",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(stp),
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "stp",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cmp),
|
||||
@@ -2530,14 +2597,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cpx),
|
||||
@@ -2572,7 +2639,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "inx",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sbc),
|
||||
@@ -2586,14 +2653,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "nop",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cpx),
|
||||
@@ -2649,14 +2716,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sbc),
|
||||
@@ -2684,7 +2751,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "sed",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sbc),
|
||||
@@ -2698,21 +2765,21 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 4,
|
||||
name: "plx",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sbc),
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use minifb::InputCallback;
|
||||
use minifb::Key as MKey;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use termion::event::Key;
|
||||
|
||||
use crate::memory::{MemHandle, MemoryWriter};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Keyboard {
|
||||
memory: MemHandle,
|
||||
}
|
||||
@@ -13,6 +16,7 @@ impl Keyboard {
|
||||
Self { memory }
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn clear_keys(&self) {
|
||||
self.memory.write(0x4400, 0x00);
|
||||
self.memory.write(0x4401, 0x00);
|
||||
@@ -22,6 +26,7 @@ impl Keyboard {
|
||||
self.memory.write(0x4405, 0x00);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn read_keys(&self, key: Key) {
|
||||
let mut row0 = 0;
|
||||
let mut row1 = 0;
|
||||
|
||||
+204
@@ -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
@@ -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
@@ -1,10 +1,6 @@
|
||||
use anyhow::{bail, Result};
|
||||
use core::panic;
|
||||
use std::cell::RefCell;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::exit;
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fs::File, io::Read};
|
||||
@@ -16,17 +12,17 @@ impl MemHandle {
|
||||
Self(Arc::new(Mutex::new(memory)))
|
||||
}
|
||||
pub fn read(&self, address: u16) -> u8 {
|
||||
let memory = self.0.lock().unwrap();
|
||||
let memory = self.0.try_lock().unwrap();
|
||||
memory.read(address)
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn dump(&self) {
|
||||
pub fn dump(&self) -> [u8; 0x10000] {
|
||||
let memory = self.0.lock().unwrap();
|
||||
let _ = memory.dump(PathBuf::from_str("./cpu_dump.bin").unwrap());
|
||||
memory.dump()
|
||||
}
|
||||
pub fn poke(&self, address: u16) {
|
||||
let memory = self.0.lock().unwrap();
|
||||
@@ -51,18 +47,26 @@ pub struct Mem([u8; 0x10000]);
|
||||
|
||||
impl Default for Mem {
|
||||
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 {
|
||||
pub fn new() -> Self {
|
||||
Mem::default()
|
||||
Self([0; 0x10000])
|
||||
}
|
||||
pub fn dump(&self, path: PathBuf) -> io::Result<()> {
|
||||
let mut outfile = File::create(path)?;
|
||||
outfile.write_all(&self.0)?;
|
||||
Ok(())
|
||||
pub fn dump(&self) -> [u8; 0x10000] {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn read(&self, address: u16) -> u8 {
|
||||
@@ -97,16 +101,22 @@ impl Mem {
|
||||
pub fn poke(&self, address: u16) {
|
||||
println!("{:02x}", self.read(address));
|
||||
}
|
||||
//pub fn read_from_bin(&mut self, f: File) -> Result<()> {
|
||||
// let bytes = f.bytes();
|
||||
// for (address, byte) in bytes.enumerate() {
|
||||
// match byte {
|
||||
// Ok(value) => self.write(address as u16, value),
|
||||
// Err(_) => {
|
||||
// bail!("couldn't write byte {:#04x}", address);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
//}
|
||||
pub fn load_bytes(&mut self, bytes: &[u8]) {
|
||||
for (address, byte) in bytes.iter().enumerate() {
|
||||
self.write(address as u16 + 0x8000, *byte)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_from_bin(&mut self, f: File) -> Result<()> {
|
||||
let bytes = f.bytes();
|
||||
for (address, byte) in bytes.enumerate() {
|
||||
match byte {
|
||||
Ok(value) => self.write(address as u16 + 0x8000, value),
|
||||
Err(_) => {
|
||||
bail!("couldn't write byte {:#04x}", address);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod native;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod wasm;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
use std::{u16, u8};
|
||||
|
||||
pub type Byte = u8;
|
||||
pub type Word = u16;
|
||||
+83
-163
@@ -1,133 +1,52 @@
|
||||
use minifb::{Scale, ScaleMode, Window, WindowOptions};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::io::{self, Write};
|
||||
use std::{env, fs::File, io::Read, path::Path};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use termion::{
|
||||
color::{self, Bg, Color, Fg},
|
||||
color::{self, Bg, Fg},
|
||||
cursor::Goto,
|
||||
screen::IntoAlternateScreen,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
cli::Config,
|
||||
cpu::CpuController,
|
||||
keyboard::Keyboard,
|
||||
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;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
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 HEIGHT: usize = 380;
|
||||
|
||||
pub fn get_char_bin<P>(char_rom: Option<P>) -> [u8; 0x8000]
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
match char_rom {
|
||||
Some(path) => {
|
||||
let mut file = File::open(path).unwrap();
|
||||
let mut bin = vec![0; 0x8000];
|
||||
file.read_exact(&mut bin).unwrap();
|
||||
// println!("reading char rom");
|
||||
bin.try_into().unwrap()
|
||||
}
|
||||
None => *include_bytes!("./roms/cozette.rom"),
|
||||
}
|
||||
}
|
||||
// pub fn get_char_bin<P>(char_rom: Option<P>) -> [u8; 0x8000]
|
||||
// where
|
||||
// P: AsRef<Path>,
|
||||
// {
|
||||
// match char_rom {
|
||||
// Some(path) => {
|
||||
// let mut file = File::open(path).unwrap();
|
||||
// let mut bin = vec![0; 0x8000];
|
||||
// file.read_exact(&mut bin).unwrap();
|
||||
// // println!("reading char rom");
|
||||
// bin.try_into().unwrap()
|
||||
// }
|
||||
// None => *include_bytes!("./roms/cozette.rom"),
|
||||
// }
|
||||
// }
|
||||
|
||||
trait Renderer {
|
||||
fn render(&mut self) {}
|
||||
}
|
||||
|
||||
#[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] = [
|
||||
const CHAR_ROM: &[u8; 0x8000] = include_bytes!(concat!(env!("OUT_DIR"), "/cozette.rom"));
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
const ASCII_LOOKUP: [&str; 256] = [
|
||||
" ", "░", "▒", "▓", "♡", "♥", "⭐", "✭", "", "✦", "✨", "♀", "♂", "⚢", "⚣", "⚥", "♩", "♪",
|
||||
"♫", "♬", "ﱝ", "", "", "", "奄", "奔", "婢", "ﱜ", "ﱛ", "", "", "", " ", "!", "\"", "#",
|
||||
"$", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6",
|
||||
@@ -144,15 +63,55 @@ const ASCII_LOOPUP: [&str; 256] = [
|
||||
"", "", "", "", "🎁", "", "", "", "", "⚐", "⚑", "", "", "",
|
||||
];
|
||||
|
||||
impl Renderer for TerminalRenderer {
|
||||
fn render(&mut self) {
|
||||
#[derive(Debug)]
|
||||
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 i = 0;
|
||||
for char_row in 0..29 {
|
||||
for char_col in 0..64 {
|
||||
let ascii = self.read(0x6000 + i);
|
||||
i += 1;
|
||||
let char = ASCII_LOOPUP[ascii as usize];
|
||||
let char = ASCII_LOOKUP[ascii as usize];
|
||||
let _ = write!(
|
||||
// FG_COLOR = 0xFFCC00
|
||||
// BG_COLOR = 0x110500
|
||||
@@ -165,53 +124,14 @@ 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.renderer.render();
|
||||
}
|
||||
|
||||
// pub fn run(&mut self) {
|
||||
// loop {
|
||||
// // sleep(Duration::from_millis(16));
|
||||
// self.draw();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
impl MemoryReader for Renderer {
|
||||
fn read(&self, address: u16) -> u8 {
|
||||
self.memory.read(address)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user