diff --git a/.gitignore b/.gitignore index 11456a0..938a539 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target log george.o +.DS_Store diff --git a/Cargo.lock b/Cargo.lock index bf7457c..a0a0ef6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,60 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + [[package]] name = "autocfg" version = "1.1.0" @@ -78,6 +132,52 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "cty" version = "0.2.2" @@ -99,6 +199,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.8" @@ -214,9 +320,35 @@ dependencies = [ name = "georgeemu" version = "0.1.0" dependencies = [ + "anyhow", "bdf", "bitvec", + "clap", "minifb", + "serde", + "toml", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", ] [[package]] @@ -460,24 +592,33 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "slab" version = "0.4.9" @@ -493,6 +634,12 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.48" @@ -543,12 +690,52 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "version-compare" version = "0.1.1" @@ -858,6 +1045,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 9f17eb9..816c740 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.81" bdf = "0.6.0" bitvec = "1.0.1" +clap = { version = "4.5.4", features = ["derive"] } minifb = "0.25.0" +serde = { version = "1.0.197", features = ["serde_derive", "derive"] } +toml = "0.8.12" diff --git a/README.md b/README.md index e25932e..2ecd381 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,15 @@ plenty of inspiration & guidance taken from [emulator_6502](https://docs.rs/emul see [the george wiki](https://git.augustkline.com/august/george/wiki) for how george works, why she exists, who she is, etc. the george emulator contains 3 main structs: `Cpu`, `Mem`, and `MemMappedDevice`. `Cpu` represents a 65c02 processor, with functions for interacting with memory and executing instructions. `Mem` is a collection of `MemMappedDevice`'s, which each hold the data for some address space. `Mem` can add `MemMappedDevice`'s, and read from and write to them. A `MemMappedDevice` can have multiple banks at the same address space, and can translate 'global' addresses to 'local' ones (e.g. 0xFFFF in the system's [memory map](https://git.augustkline.com/august/george-hardware/src/branch/main/memory.md) corresponds to 0x1FFF in the ROM). + +## font generation + +george uses a modified version of [cozette](https://github.com/slavfox/Cozette) for her main font. the modified version has 8 pixel wide characters for use with the character generator rom, and has a limit of 256 (0xFF) characters. the toolchain to generate a rom binary is still pretty clunky, and someday i might get around to streamlining it, but for now to make any changes: + +- open `./src/Cozette.sfd` in fontforge +- make your edits + - to reorder glyphs, edit `./src/georgeencoding.txt` and load it with "Encoding -> Load Encoding -> (select georgeencoding.txt, name it whatever) -> Reencode -> (name of encoding)" + - ensure that there are exactly 256 characters in the font +- generate a bdf font with "File -> Generate Fonts... -> (name of the font, select bdf in the options panel)" +- open the generated bdf file in a text editor and change the line `FONTBOUNDINGBOX 11 13 0 -3` to `FONTBOUNDINGBOX 8 13 0 -3` +- now open the font in [bdf view](https://emurenmrz.github.io/bdf_view/), and export a single-row png diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..95aaea3 --- /dev/null +++ b/config.toml @@ -0,0 +1,2 @@ +char_rom = "./src/roms/cozette.rom" +rom = "./src/roms/george.rom" diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..0696295 --- /dev/null +++ b/run.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +vasm6502_oldstyle ./src/george.asm -dotdir -wdc02 -ldots -Fbin -o ./src/george.rom; +cargo run; +# hexdump -C ./cpu_dump.bin; diff --git a/src/cozette.bdf b/src/cozette.bdf deleted file mode 100644 index b38374d..0000000 --- a/src/cozette.bdf +++ /dev/null @@ -1,2943 +0,0 @@ -STARTFONT 2.1 -FONT -slavfox-Cozette-Medium-R-Normal--13-120-75-75-M-60-george-0 -SIZE 12 75 75 -FONTBOUNDINGBOX 11 13 0 -3 -COMMENT "Generated by fontforge, http://fontforge.sourceforge.net" -COMMENT "(c) 2020-2023 Slavfox" -STARTPROPERTIES 40 -FOUNDRY "slavfox" -FAMILY_NAME "Cozette" -WEIGHT_NAME "Medium" -SLANT "R" -SETWIDTH_NAME "Normal" -ADD_STYLE_NAME "" -PIXEL_SIZE 13 -POINT_SIZE 120 -RESOLUTION_X 75 -RESOLUTION_Y 75 -SPACING "M" -AVERAGE_WIDTH 60 -CHARSET_REGISTRY "george" -CHARSET_ENCODING "0" -FONTNAME_REGISTRY "" -FONT_NAME "Cozette" -FACE_NAME "Cozette" -COPYRIGHT "(c) 2020-2023 Slavfox" -FONT_VERSION "1.232" -FONT_ASCENT 10 -FONT_DESCENT 3 -UNDERLINE_POSITION -19 -UNDERLINE_THICKNESS 13 -X_HEIGHT 5 -CAP_HEIGHT 7 -DEFAULT_CHAR 0 -RAW_ASCENT 769 -RAW_DESCENT 230 -NORM_SPACE 6 -RELATIVE_WEIGHT 50 -RELATIVE_SETWIDTH 50 -SUPERSCRIPT_X 0 -SUPERSCRIPT_Y 2 -SUPERSCRIPT_SIZE 2 -SUBSCRIPT_X 0 -SUBSCRIPT_Y 2 -SUBSCRIPT_SIZE 2 -FIGURE_WIDTH 6 -AVG_LOWERCASE_WIDTH 60 -AVG_UPPERCASE_WIDTH 60 -ENDPROPERTIES -CHARS 190 -STARTCHAR uni0295 -ENCODING 0 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -80 -80 -60 -20 -20 -20 -ENDCHAR -STARTCHAR uni0097 -ENCODING 1 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 3 2 2 -BITMAP -40 -E0 -40 -ENDCHAR -STARTCHAR uni1D25 -ENCODING 2 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -70 -70 -70 -20 -20 -D8 -ENDCHAR -STARTCHAR uni0294 -ENCODING 3 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -08 -08 -30 -20 -20 -20 -ENDCHAR -STARTCHAR uni2661 -ENCODING 4 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 7 0 0 -BITMAP -6C -92 -82 -82 -44 -28 -10 -ENDCHAR -STARTCHAR heart -ENCODING 5 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 7 0 0 -BITMAP -6C -FE -FE -FE -7C -38 -10 -ENDCHAR -STARTCHAR uni2B50 -ENCODING 6 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 1 -BITMAP -20 -20 -F8 -50 -70 -88 -ENDCHAR -STARTCHAR uni272D -ENCODING 7 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 1 -BITMAP -20 -20 -F8 -70 -70 -88 -ENDCHAR -STARTCHAR uniF005 -ENCODING 8 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 8 0 0 -BITMAP -10 -10 -38 -FE -7C -38 -6C -44 -ENDCHAR -STARTCHAR uni2726 -ENCODING 9 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 5 1 1 -BITMAP -20 -70 -F8 -70 -20 -ENDCHAR -STARTCHAR uni2728 -ENCODING 10 -SWIDTH 922 0 -DWIDTH 12 0 -BBX 10 10 1 -1 -BITMAP -4000 -E000 -4200 -0200 -0700 -1FC0 -0700 -2200 -7200 -2000 -ENDCHAR -STARTCHAR female -ENCODING 11 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 -2 -BITMAP -70 -88 -88 -88 -70 -20 -70 -20 -ENDCHAR -STARTCHAR male -ENCODING 12 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 6 0 0 -BITMAP -1C -0C -74 -90 -90 -60 -ENDCHAR -STARTCHAR uni26A2 -ENCODING 13 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 7 0 0 -BITMAP -78 -B4 -B4 -78 -48 -FC -48 -ENDCHAR -STARTCHAR uni26A3 -ENCODING 14 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 8 0 0 -BITMAP -1C -0C -74 -9E -B6 -6A -48 -30 -ENDCHAR -STARTCHAR uni26A5 -ENCODING 15 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 9 0 -1 -BITMAP -1C -0C -74 -94 -90 -60 -20 -70 -20 -ENDCHAR -STARTCHAR uni2669 -ENCODING 16 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 2 7 2 0 -BITMAP -40 -40 -40 -40 -40 -C0 -C0 -ENDCHAR -STARTCHAR musicalnote -ENCODING 17 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 7 2 0 -BITMAP -60 -50 -40 -40 -40 -C0 -C0 -ENDCHAR -STARTCHAR musicalnotedbl -ENCODING 18 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 8 0 0 -BITMAP -70 -4C -44 -44 -44 -C4 -CC -0C -ENDCHAR -STARTCHAR uni266C -ENCODING 19 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 7 0 0 -BITMAP -7C -44 -7C -44 -44 -CC -CC -ENDCHAR -STARTCHAR space -ENCODING 20 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 1 1 6 -1 -BITMAP -00 -ENDCHAR -STARTCHAR exclam -ENCODING 33 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 1 8 3 0 -BITMAP -80 -80 -80 -80 -80 -80 -00 -80 -ENDCHAR -STARTCHAR quotedbl -ENCODING 34 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 3 2 6 -BITMAP -A0 -A0 -A0 -ENDCHAR -STARTCHAR numbersign -ENCODING 35 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -50 -50 -F8 -50 -50 -F8 -50 -50 -ENDCHAR -STARTCHAR dollar -ENCODING 36 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 10 1 -1 -BITMAP -20 -70 -A8 -A0 -70 -28 -28 -A8 -70 -20 -ENDCHAR -STARTCHAR percent -ENCODING 37 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 0 -BITMAP -40 -A0 -48 -10 -20 -40 -90 -28 -10 -ENDCHAR -STARTCHAR ampersand -ENCODING 38 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 0 -BITMAP -20 -50 -50 -20 -68 -90 -90 -90 -68 -ENDCHAR -STARTCHAR quotesingle -ENCODING 39 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 1 3 3 6 -BITMAP -80 -80 -80 -ENDCHAR -STARTCHAR parenleft -ENCODING 40 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 11 2 -2 -BITMAP -20 -40 -40 -80 -80 -80 -80 -80 -40 -40 -20 -ENDCHAR -STARTCHAR parenright -ENCODING 41 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 11 2 -2 -BITMAP -80 -40 -40 -20 -20 -20 -20 -20 -40 -40 -80 -ENDCHAR -STARTCHAR asterisk -ENCODING 42 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 5 1 1 -BITMAP -50 -20 -F8 -20 -50 -ENDCHAR -STARTCHAR plus -ENCODING 43 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 5 1 1 -BITMAP -20 -20 -F8 -20 -20 -ENDCHAR -STARTCHAR comma -ENCODING 44 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 2 4 2 -2 -BITMAP -C0 -C0 -40 -80 -ENDCHAR -STARTCHAR hyphen -ENCODING 45 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 1 1 3 -BITMAP -F8 -ENDCHAR -STARTCHAR period -ENCODING 46 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 2 2 2 0 -BITMAP -C0 -C0 -ENDCHAR -STARTCHAR slash -ENCODING 47 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 10 1 -1 -BITMAP -08 -08 -10 -10 -20 -20 -40 -40 -80 -80 -ENDCHAR -STARTCHAR zero -ENCODING 48 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -88 -A8 -A8 -88 -88 -70 -ENDCHAR -STARTCHAR one -ENCODING 49 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -20 -60 -A0 -20 -20 -20 -20 -F8 -ENDCHAR -STARTCHAR two -ENCODING 50 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -08 -10 -20 -40 -80 -F8 -ENDCHAR -STARTCHAR three -ENCODING 51 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -08 -30 -08 -08 -88 -70 -ENDCHAR -STARTCHAR four -ENCODING 52 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 8 1 0 -BITMAP -08 -18 -28 -48 -88 -FC -08 -08 -ENDCHAR -STARTCHAR five -ENCODING 53 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -F8 -80 -80 -F0 -08 -08 -88 -70 -ENDCHAR -STARTCHAR six -ENCODING 54 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -30 -40 -80 -F0 -88 -88 -88 -70 -ENDCHAR -STARTCHAR seven -ENCODING 55 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -F8 -08 -10 -10 -20 -20 -40 -40 -ENDCHAR -STARTCHAR eight -ENCODING 56 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -88 -70 -88 -88 -88 -70 -ENDCHAR -STARTCHAR nine -ENCODING 57 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -88 -88 -78 -08 -10 -60 -ENDCHAR -STARTCHAR colon -ENCODING 58 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 2 6 2 0 -BITMAP -C0 -C0 -00 -00 -C0 -C0 -ENDCHAR -STARTCHAR semicolon -ENCODING 59 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 2 8 2 -2 -BITMAP -C0 -C0 -00 -00 -C0 -C0 -40 -80 -ENDCHAR -STARTCHAR less -ENCODING 60 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 7 2 0 -BITMAP -10 -20 -40 -80 -40 -20 -10 -ENDCHAR -STARTCHAR equal -ENCODING 61 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 3 1 2 -BITMAP -F8 -00 -F8 -ENDCHAR -STARTCHAR greater -ENCODING 62 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 7 1 0 -BITMAP -80 -40 -20 -10 -20 -40 -80 -ENDCHAR -STARTCHAR question -ENCODING 63 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -08 -10 -20 -20 -00 -20 -ENDCHAR -STARTCHAR at -ENCODING 64 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -88 -B8 -A8 -B8 -80 -78 -ENDCHAR -STARTCHAR A -ENCODING 65 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -88 -88 -F8 -88 -88 -88 -ENDCHAR -STARTCHAR B -ENCODING 66 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -F0 -88 -88 -F0 -88 -88 -88 -F0 -ENDCHAR -STARTCHAR C -ENCODING 67 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -80 -80 -80 -80 -88 -70 -ENDCHAR -STARTCHAR D -ENCODING 68 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -E0 -90 -88 -88 -88 -88 -90 -E0 -ENDCHAR -STARTCHAR E -ENCODING 69 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -F8 -80 -80 -F0 -80 -80 -80 -F8 -ENDCHAR -STARTCHAR F -ENCODING 70 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -F8 -80 -80 -F0 -80 -80 -80 -80 -ENDCHAR -STARTCHAR G -ENCODING 71 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -80 -80 -98 -88 -88 -70 -ENDCHAR -STARTCHAR H -ENCODING 72 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -88 -88 -88 -F8 -88 -88 -88 -88 -ENDCHAR -STARTCHAR I -ENCODING 73 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 8 2 0 -BITMAP -E0 -40 -40 -40 -40 -40 -40 -E0 -ENDCHAR -STARTCHAR J -ENCODING 74 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -38 -08 -08 -08 -08 -88 -88 -70 -ENDCHAR -STARTCHAR K -ENCODING 75 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -88 -90 -A0 -E0 -90 -90 -88 -88 -ENDCHAR -STARTCHAR L -ENCODING 76 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -80 -80 -80 -80 -80 -80 -80 -F8 -ENDCHAR -STARTCHAR M -ENCODING 77 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -88 -D8 -A8 -A8 -88 -88 -88 -88 -ENDCHAR -STARTCHAR N -ENCODING 78 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -88 -C8 -C8 -A8 -A8 -98 -98 -88 -ENDCHAR -STARTCHAR O -ENCODING 79 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -88 -88 -88 -88 -88 -70 -ENDCHAR -STARTCHAR P -ENCODING 80 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -F0 -88 -88 -88 -F0 -80 -80 -80 -ENDCHAR -STARTCHAR Q -ENCODING 81 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 -1 -BITMAP -70 -88 -88 -88 -88 -88 -90 -68 -08 -ENDCHAR -STARTCHAR R -ENCODING 82 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -F0 -88 -88 -F0 -90 -88 -88 -88 -ENDCHAR -STARTCHAR S -ENCODING 83 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -70 -88 -80 -70 -08 -08 -88 -70 -ENDCHAR -STARTCHAR T -ENCODING 84 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -F8 -20 -20 -20 -20 -20 -20 -20 -ENDCHAR -STARTCHAR U -ENCODING 85 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -88 -88 -88 -88 -88 -88 -88 -70 -ENDCHAR -STARTCHAR V -ENCODING 86 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -88 -88 -88 -50 -50 -50 -20 -20 -ENDCHAR -STARTCHAR W -ENCODING 87 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -88 -88 -88 -A8 -A8 -70 -50 -50 -ENDCHAR -STARTCHAR X -ENCODING 88 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -88 -88 -50 -20 -20 -50 -88 -88 -ENDCHAR -STARTCHAR Y -ENCODING 89 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -88 -88 -88 -50 -20 -20 -20 -20 -ENDCHAR -STARTCHAR Z -ENCODING 90 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -F8 -10 -20 -20 -40 -40 -80 -F8 -ENDCHAR -STARTCHAR bracketleft -ENCODING 91 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 11 2 -2 -BITMAP -E0 -80 -80 -80 -80 -80 -80 -80 -80 -80 -E0 -ENDCHAR -STARTCHAR backslash -ENCODING 92 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 10 1 -1 -BITMAP -80 -80 -40 -40 -20 -20 -10 -10 -08 -08 -ENDCHAR -STARTCHAR bracketright -ENCODING 93 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 11 2 -2 -BITMAP -E0 -20 -20 -20 -20 -20 -20 -20 -20 -20 -E0 -ENDCHAR -STARTCHAR asciicircum -ENCODING 94 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 3 1 7 -BITMAP -20 -50 -88 -ENDCHAR -STARTCHAR underscore -ENCODING 95 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 1 1 -1 -BITMAP -F8 -ENDCHAR -STARTCHAR grave -ENCODING 96 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 2 2 2 7 -BITMAP -80 -40 -ENDCHAR -STARTCHAR a -ENCODING 97 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -78 -88 -88 -88 -98 -68 -ENDCHAR -STARTCHAR b -ENCODING 98 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 0 -BITMAP -80 -80 -80 -F0 -88 -88 -88 -88 -F0 -ENDCHAR -STARTCHAR c -ENCODING 99 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -70 -88 -80 -80 -88 -70 -ENDCHAR -STARTCHAR d -ENCODING 100 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 0 -BITMAP -08 -08 -08 -78 -88 -88 -88 -88 -78 -ENDCHAR -STARTCHAR e -ENCODING 101 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -70 -88 -F8 -80 -88 -70 -ENDCHAR -STARTCHAR f -ENCODING 102 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 0 -BITMAP -38 -40 -40 -F0 -40 -40 -40 -40 -40 -ENDCHAR -STARTCHAR g -ENCODING 103 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 -3 -BITMAP -78 -88 -88 -88 -88 -78 -08 -08 -70 -ENDCHAR -STARTCHAR h -ENCODING 104 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 0 -BITMAP -80 -80 -80 -F0 -88 -88 -88 -88 -88 -ENDCHAR -STARTCHAR i -ENCODING 105 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 8 2 0 -BITMAP -40 -00 -C0 -40 -40 -40 -40 -30 -ENDCHAR -STARTCHAR j -ENCODING 106 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 10 2 -2 -BITMAP -20 -00 -60 -20 -20 -20 -20 -20 -A0 -40 -ENDCHAR -STARTCHAR k -ENCODING 107 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 0 -BITMAP -80 -80 -80 -88 -90 -A0 -E0 -90 -88 -ENDCHAR -STARTCHAR l -ENCODING 108 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 9 2 0 -BITMAP -C0 -40 -40 -40 -40 -40 -40 -40 -60 -ENDCHAR -STARTCHAR m -ENCODING 109 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -D0 -A8 -A8 -A8 -A8 -A8 -ENDCHAR -STARTCHAR n -ENCODING 110 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -F0 -88 -88 -88 -88 -88 -ENDCHAR -STARTCHAR o -ENCODING 111 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -70 -88 -88 -88 -88 -70 -ENDCHAR -STARTCHAR p -ENCODING 112 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 -3 -BITMAP -F0 -88 -88 -88 -88 -F0 -80 -80 -80 -ENDCHAR -STARTCHAR q -ENCODING 113 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 9 1 -3 -BITMAP -78 -88 -88 -88 -88 -78 -08 -08 -0C -ENDCHAR -STARTCHAR r -ENCODING 114 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -F0 -88 -80 -80 -80 -80 -ENDCHAR -STARTCHAR s -ENCODING 115 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -78 -80 -70 -08 -08 -F0 -ENDCHAR -STARTCHAR t -ENCODING 116 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 1 0 -BITMAP -40 -40 -F0 -40 -40 -40 -40 -38 -ENDCHAR -STARTCHAR u -ENCODING 117 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -88 -88 -88 -88 -88 -78 -ENDCHAR -STARTCHAR v -ENCODING 118 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -88 -88 -50 -50 -20 -20 -ENDCHAR -STARTCHAR w -ENCODING 119 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -88 -88 -A8 -A8 -50 -50 -ENDCHAR -STARTCHAR x -ENCODING 120 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -88 -50 -20 -20 -50 -88 -ENDCHAR -STARTCHAR y -ENCODING 121 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 9 1 -3 -BITMAP -88 -88 -88 -88 -88 -78 -08 -08 -70 -ENDCHAR -STARTCHAR z -ENCODING 122 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 6 1 0 -BITMAP -F8 -10 -20 -40 -80 -F8 -ENDCHAR -STARTCHAR braceleft -ENCODING 123 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 11 1 -2 -BITMAP -18 -20 -20 -20 -20 -C0 -20 -20 -20 -20 -18 -ENDCHAR -STARTCHAR bar -ENCODING 124 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 1 11 3 -2 -BITMAP -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -ENDCHAR -STARTCHAR braceright -ENCODING 125 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 11 1 -2 -BITMAP -C0 -20 -20 -20 -20 -18 -20 -20 -20 -20 -C0 -ENDCHAR -STARTCHAR asciitilde -ENCODING 126 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 3 1 2 -BITMAP -48 -A8 -90 -ENDCHAR -STARTCHAR SF100000 -ENCODING 127 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 1 0 3 -BITMAP -FE -ENDCHAR -STARTCHAR SF110000 -ENCODING 128 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 1 13 3 -3 -BITMAP -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -ENDCHAR -STARTCHAR SF010000 -ENCODING 129 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 7 3 -3 -BITMAP -F0 -80 -80 -80 -80 -80 -80 -ENDCHAR -STARTCHAR SF020000 -ENCODING 130 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 7 3 3 -BITMAP -80 -80 -80 -80 -80 -80 -F0 -ENDCHAR -STARTCHAR SF080000 -ENCODING 131 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 13 3 -3 -BITMAP -80 -80 -80 -80 -80 -80 -F0 -80 -80 -80 -80 -80 -80 -ENDCHAR -STARTCHAR SF090000 -ENCODING 132 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 13 0 -3 -BITMAP -10 -10 -10 -10 -10 -10 -F0 -10 -10 -10 -10 -10 -10 -ENDCHAR -STARTCHAR SF060000 -ENCODING 133 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 7 0 -3 -BITMAP -FE -10 -10 -10 -10 -10 -10 -ENDCHAR -STARTCHAR SF070000 -ENCODING 134 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 7 0 3 -BITMAP -10 -10 -10 -10 -10 -10 -FE -ENDCHAR -STARTCHAR SF050000 -ENCODING 135 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -10 -10 -10 -10 -10 -10 -FE -10 -10 -10 -10 -10 -10 -ENDCHAR -STARTCHAR uni256D -ENCODING 136 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 7 3 -3 -BITMAP -70 -80 -80 -80 -80 -80 -80 -ENDCHAR -STARTCHAR uni256E -ENCODING 137 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 7 0 -3 -BITMAP -E0 -10 -10 -10 -10 -10 -10 -ENDCHAR -STARTCHAR uni256F -ENCODING 138 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 7 0 3 -BITMAP -10 -10 -10 -10 -10 -10 -E0 -ENDCHAR -STARTCHAR uni2570 -ENCODING 139 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 7 3 3 -BITMAP -80 -80 -80 -80 -80 -80 -70 -ENDCHAR -STARTCHAR uni2571 -ENCODING 140 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -02 -02 -04 -04 -08 -08 -10 -20 -20 -40 -40 -80 -80 -ENDCHAR -STARTCHAR uni2572 -ENCODING 141 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -80 -80 -40 -40 -20 -20 -10 -08 -08 -04 -04 -02 -02 -ENDCHAR -STARTCHAR uni2573 -ENCODING 142 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -82 -82 -44 -44 -28 -28 -10 -28 -28 -44 -44 -82 -82 -ENDCHAR -STARTCHAR SF430000 -ENCODING 143 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 3 0 2 -BITMAP -FE -00 -FE -ENDCHAR -STARTCHAR SF240000 -ENCODING 144 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 13 2 -3 -BITMAP -A0 -A0 -A0 -A0 -A0 -A0 -A0 -A0 -A0 -A0 -A0 -A0 -A0 -ENDCHAR -STARTCHAR SF390000 -ENCODING 145 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 2 -3 -BITMAP -F8 -80 -B8 -A0 -A0 -A0 -A0 -A0 -ENDCHAR -STARTCHAR SF250000 -ENCODING 146 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 0 -3 -BITMAP -F8 -08 -E8 -28 -28 -28 -28 -28 -ENDCHAR -STARTCHAR SF380000 -ENCODING 147 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 2 2 -BITMAP -A0 -A0 -A0 -A0 -A0 -B8 -80 -F8 -ENDCHAR -STARTCHAR SF260000 -ENCODING 148 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 8 0 2 -BITMAP -28 -28 -28 -28 -28 -E8 -08 -F8 -ENDCHAR -STARTCHAR SF420000 -ENCODING 149 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 13 2 -3 -BITMAP -A0 -A0 -A0 -A0 -A0 -B8 -80 -B8 -A0 -A0 -A0 -A0 -A0 -ENDCHAR -STARTCHAR SF230000 -ENCODING 150 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 13 0 -3 -BITMAP -28 -28 -28 -28 -28 -E8 -08 -E8 -28 -28 -28 -28 -28 -ENDCHAR -STARTCHAR SF410000 -ENCODING 151 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 8 0 -3 -BITMAP -FE -00 -EE -28 -28 -28 -28 -28 -ENDCHAR -STARTCHAR SF400000 -ENCODING 152 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 8 0 2 -BITMAP -28 -28 -28 -28 -28 -EE -00 -FE -ENDCHAR -STARTCHAR SF440000 -ENCODING 153 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -28 -28 -28 -28 -28 -EE -00 -EE -28 -28 -28 -28 -28 -ENDCHAR -STARTCHAR uniE0B0 -ENCODING 159 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -80 -C0 -E0 -F0 -F8 -FC -FE -FC -F8 -F0 -E0 -C0 -80 -ENDCHAR -STARTCHAR uniE0B2 -ENCODING 160 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -02 -06 -0E -1E -3E -7E -FE -7E -3E -1E -0E -06 -02 -ENDCHAR -STARTCHAR uniE0B4 -ENCODING 161 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -E0 -F8 -FC -FC -FE -FE -FE -FE -FE -FC -FC -F8 -E0 -ENDCHAR -STARTCHAR uniE0B6 -ENCODING 162 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -0E -3E -7E -7E -FE -FE -FE -FE -FE -7E -7E -3E -0E -ENDCHAR -STARTCHAR uniE0B8 -ENCODING 163 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -80 -80 -C0 -C0 -E0 -E0 -F0 -F0 -F0 -F8 -F8 -FC -FC -ENDCHAR -STARTCHAR uniE0BA -ENCODING 164 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -04 -04 -0C -0C -1C -1C -3C -3C -3C -7C -7C -FC -FC -ENDCHAR -STARTCHAR uniE0BC -ENCODING 165 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -FC -FC -F8 -F8 -F0 -F0 -E0 -E0 -E0 -C0 -C0 -80 -80 -ENDCHAR -STARTCHAR uniE0BE -ENCODING 166 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -FC -FC -7C -7C -3C -3C -1C -1C -1C -0C -0C -04 -04 -ENDCHAR -STARTCHAR uni2581 -ENCODING 167 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 2 0 -3 -BITMAP -FE -FE -ENDCHAR -STARTCHAR uni2582 -ENCODING 168 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 4 0 -3 -BITMAP -FE -FE -FE -FE -ENDCHAR -STARTCHAR uni2583 -ENCODING 169 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 5 0 -3 -BITMAP -FE -FE -FE -FE -FE -ENDCHAR -STARTCHAR dnblock -ENCODING 170 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 7 0 -3 -BITMAP -FE -FE -FE -FE -FE -FE -FE -ENDCHAR -STARTCHAR uni2585 -ENCODING 171 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 9 0 -3 -BITMAP -FE -FE -FE -FE -FE -FE -FE -FE -FE -ENDCHAR -STARTCHAR uni2586 -ENCODING 172 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 11 0 -3 -BITMAP -FE -FE -FE -FE -FE -FE -FE -FE -FE -FE -FE -ENDCHAR -STARTCHAR uni2587 -ENCODING 173 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 12 0 -3 -BITMAP -FE -FE -FE -FE -FE -FE -FE -FE -FE -FE -FE -FE -ENDCHAR -STARTCHAR block -ENCODING 174 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -FE -FE -FE -FE -FE -FE -FE -FE -FE -FE -FE -FE -FE -ENDCHAR -STARTCHAR uni2589 -ENCODING 175 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -FC -FC -FC -FC -FC -FC -FC -FC -FC -FC -FC -FC -FC -ENDCHAR -STARTCHAR uni258A -ENCODING 176 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 13 0 -3 -BITMAP -F8 -F8 -F8 -F8 -F8 -F8 -F8 -F8 -F8 -F8 -F8 -F8 -F8 -ENDCHAR -STARTCHAR uni258B -ENCODING 177 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 13 0 -3 -BITMAP -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -ENDCHAR -STARTCHAR lfblock -ENCODING 178 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 4 13 0 -3 -BITMAP -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -F0 -ENDCHAR -STARTCHAR uni258D -ENCODING 179 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 13 0 -3 -BITMAP -E0 -E0 -E0 -E0 -E0 -E0 -E0 -E0 -E0 -E0 -E0 -E0 -E0 -ENDCHAR -STARTCHAR uni258E -ENCODING 180 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 2 13 0 -3 -BITMAP -C0 -C0 -C0 -C0 -C0 -C0 -C0 -C0 -C0 -C0 -C0 -C0 -C0 -ENDCHAR -STARTCHAR uni258F -ENCODING 181 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 1 13 0 -3 -BITMAP -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -80 -ENDCHAR -STARTCHAR ltshade -ENCODING 182 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -88 -22 -88 -22 -88 -22 -88 -22 -88 -22 -88 -22 -88 -ENDCHAR -STARTCHAR shade -ENCODING 183 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -AA -54 -AA -54 -AA -54 -AA -54 -AA -54 -AA -54 -AA -ENDCHAR -STARTCHAR dkshade -ENCODING 184 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 7 13 0 -3 -BITMAP -BA -EE -BA -EE -BA -EE -BA -EE -BA -EE -BA -EE -BA -ENDCHAR -STARTCHAR uni2596 -ENCODING 185 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 6 0 -3 -BITMAP -EC -FE -F1 -EC -FF -F3 -ENDCHAR -STARTCHAR uni2597 -ENCODING 186 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 6 3 -3 -BITMAP -EC -F6 -F2 -F7 -FA -F6 -ENDCHAR -STARTCHAR uni2598 -ENCODING 187 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 7 0 3 -BITMAP -EC -F6 -F2 -F7 -FA -F6 -FF -ENDCHAR -STARTCHAR uni2599 -ENCODING 188 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -E2 -E2 -E2 -E0 -E2 -E0 -E3 -FD -FC -FF -FF -FF -FF -ENDCHAR -STARTCHAR uni259A -ENCODING 189 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -E1 -E1 -E0 -E0 -E2 -E3 -E3 -1E -1E -1F -1D -1E -1C -ENDCHAR -STARTCHAR uni259B -ENCODING 190 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -FD -FD -FC -FC -FE -FF -FF -E2 -E2 -E3 -E1 -E2 -E0 -ENDCHAR -STARTCHAR uni259C -ENCODING 191 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -FE -FE -FE -FC -FE -FC -FF -1D -1C -1F -1C -1F -1C -ENDCHAR -STARTCHAR uni259D -ENCODING 192 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 7 3 3 -BITMAP -F2 -FE -F1 -F8 -FE -F3 -FF -ENDCHAR -STARTCHAR uni259E -ENCODING 193 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -1D -1D -1C -1C -1E -1F -1F -E2 -E2 -E3 -E1 -E2 -E0 -ENDCHAR -STARTCHAR uni259F -ENCODING 194 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 13 0 -3 -BITMAP -1C -1E -1F -1E -1E -1C -1F -FE -FF -FF -FC -FF -FC -ENDCHAR -STARTCHAR arrowleft -ENCODING 195 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 5 1 1 -BITMAP -20 -40 -F8 -40 -20 -ENDCHAR -STARTCHAR arrowup -ENCODING 196 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 7 1 0 -BITMAP -20 -70 -A8 -20 -20 -20 -20 -ENDCHAR -STARTCHAR arrowright -ENCODING 197 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 5 1 1 -BITMAP -20 -10 -F8 -10 -20 -ENDCHAR -STARTCHAR arrowdown -ENCODING 198 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 5 7 1 0 -BITMAP -20 -20 -20 -20 -A8 -70 -20 -ENDCHAR -STARTCHAR uni2B60 -ENCODING 199 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 3 1 2 -BITMAP -40 -FC -40 -ENDCHAR -STARTCHAR uni2B61 -ENCODING 200 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 6 2 1 -BITMAP -40 -E0 -40 -40 -40 -40 -ENDCHAR -STARTCHAR uni2B62 -ENCODING 201 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 3 1 2 -BITMAP -08 -FC -08 -ENDCHAR -STARTCHAR uni2B63 -ENCODING 202 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 3 6 2 1 -BITMAP -40 -40 -40 -40 -E0 -40 -ENDCHAR -STARTCHAR uni2B80 -ENCODING 203 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 6 1 1 -BITMAP -40 -FC -40 -08 -FC -08 -ENDCHAR -STARTCHAR uni2B81 -ENCODING 204 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 6 1 1 -BITMAP -48 -E8 -48 -48 -5C -48 -ENDCHAR -STARTCHAR uni2B82 -ENCODING 205 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 6 1 1 -BITMAP -08 -FC -08 -40 -FC -40 -ENDCHAR -STARTCHAR uni2B83 -ENCODING 206 -SWIDTH 500 0 -DWIDTH 6 0 -BBX 6 6 1 1 -BITMAP -48 -5C -48 -48 -E8 -48 -ENDCHAR -ENDFONT diff --git a/src/cozette.bin b/src/cozette.bin deleted file mode 100644 index df64451..0000000 Binary files a/src/cozette.bin and /dev/null differ diff --git a/src/cpu.rs b/src/cpu.rs index e76b87e..d1ac36d 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,19 +1,23 @@ -use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError}; +// use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError}; use crate::instructions::{get_instruction, Instruction}; use crate::memory::Mem; use crate::types::{Byte, Word}; use std::path::PathBuf; -use std::process::exit; +use std::str::FromStr; +use std::sync::mpsc::Receiver; +use std::sync::Arc; use std::sync::Mutex; +use std::thread::sleep; use std::time::Duration; -use std::{str::FromStr, sync::Arc, thread::sleep}; + +use anyhow::{bail, Result}; #[derive(Clone, Copy)] pub enum StatusFlag { Negative = 0b1000_0000, Overflow = 0b0100_0000, Brk = 0b0011_0000, - BrkIrq = 0b0010_0000, + //BrkIrq = 0b0010_0000, Decimal = 0b0000_1000, IrqDisable = 0b0000_0100, Zero = 0b000_0010, @@ -27,7 +31,7 @@ pub struct Cpu { pub pc: Word, // Program Counter pub s: Byte, // Stack Pointer pub p: Byte, // Status Register - pub irq: bool, + pub irq: Receiver, pub nmi: bool, pub memory: Arc>, pub pending_cycles: usize, @@ -35,7 +39,7 @@ pub struct Cpu { } impl Cpu { - pub fn new(memory: Arc>) -> Self { + pub fn new(memory: Arc>, irq: Receiver) -> Self { Cpu { a: 0x00, x: 0x00, @@ -43,30 +47,29 @@ impl Cpu { pc: 0x0000, s: 0xFF, p: 0b0010_0100, - irq: false, + irq, nmi: false, memory, pending_cycles: 0, cycle_count: 0, } } - pub fn reset(&mut self) -> Result<(), ExecutionError> { + pub fn reset(&mut self) -> Result<()> { let reset_vector_pointer = self.read_word(0xFFFC)?; self.pc = reset_vector_pointer; - self.pending_cycles = 8; + self.pending_cycles = 0; Ok(()) } - pub fn read(&self, address: Word) -> Result { + pub fn read(&self, address: Word) -> Result { let memory = match self.memory.lock() { Ok(read) => read, Err(_) => { - println!("Couldn't acquire read lock on memory in cpu thread"); - return Err(MemoryError::NoDataAtAddress); - } // TODO: upgrade this error type to a `GeorgeError` and make an errorkind for thread lock errors + bail!("Couldn't acquire lock on memory in cpu thread") + } }; Ok(memory.read(address)) } - pub fn read_word(&self, address: Word) -> Result { + pub fn read_word(&self, address: Word) -> Result { let low_byte = self.read(address)?; let high_byte = self.read(address + 0x1)?; Ok((high_byte as u16) << 8 | (low_byte as u16)) @@ -77,27 +80,27 @@ impl Cpu { 0x0100 + self.s as u16 } - pub fn push_stack(&mut self, data: Byte) -> Result<(), ExecutionError> { - self.s -= 0x1; + pub fn push_stack(&mut self, data: Byte) -> Result<()> { + self.s = self.s.wrapping_sub(0x1); self.write(self.stack_addr(), data)?; Ok(()) } - pub fn push_stack_word(&mut self, address: Word) -> Result<(), ExecutionError> { - self.s -= 0x1; + pub fn push_stack_word(&mut self, address: Word) -> Result<()> { + self.s = self.s.wrapping_sub(0x1); self.write(self.stack_addr(), address.to_le_bytes()[1])?; // Upper byte first - self.s -= 0x1; + self.s = self.s.wrapping_sub(0x1); self.write(self.stack_addr(), address.to_le_bytes()[0])?; // Lower byte second Ok(()) } - pub fn pop_stack(&mut self) -> Result { + pub fn pop_stack(&mut self) -> Result { let byte = self.read(self.stack_addr())?; self.s = self.s.wrapping_add(0x1); Ok(byte) } - pub fn pop_stack_word(&mut self) -> Result { + pub fn pop_stack_word(&mut self) -> Result { let low_byte = self.pop_stack()?; let high_byte = self.pop_stack()?; let word = ((high_byte as Word) << 8) + low_byte as u16; @@ -114,17 +117,17 @@ impl Cpu { pub fn get_flag(&self, flag: StatusFlag) -> bool { (self.p & flag as Byte) > 0 } + pub fn is_negative(&self, value: Byte) -> bool { value & 0b1000_0000 == 0b1000_0000 } - pub fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> { + pub fn write(&mut self, address: Word, data: Byte) -> Result<()> { let mut memory = match self.memory.lock() { Ok(write) => write, Err(_) => { - println!("Couldn't acquire write lock on memory in cpu thread"); - return Err(MemoryError::NoDataAtAddress); - } // TODO: upgrade this error type to a `GeorgeError` and make an errorkind for thread lock errors + bail!("Couldn't acquire write lock on memory in cpu thread") + } }; memory.write(address, data); Ok(()) @@ -141,135 +144,71 @@ impl Cpu { unimplemented!() } - // fn handle_interrupt(&mut self) -> Result<(), ExecutionError> { - // match self.get_flag(StatusFlag::IrqDisable) { - // // Check that interrupts aren't disabled - // true => Ok(self.execute()), - // false => { - // if self.irq { - // let irq_vector = 0xFFFE; - // match self.read_word(irq_vector) { - // Err(error) => Err(ExecutionError::MemoryError(error)), - // Ok(address) => { - // let isr_address = address; - // match self.read(isr_address) { - // Ok(_opcode) => Ok(self.execute()), - // Err(error) => Err(ExecutionError::MemoryError(error)), - // } - // } - // } - // } else { - // Ok(()) - // } - // } - // } - // } - pub fn cycle(&mut self) { - sleep(Duration::from_nanos(100)); - if self.pending_cycles == 0 { - if self.nmi || (self.irq && !self.get_flag(StatusFlag::IrqDisable)) { - let _ = self.push_stack_word(self.pc); - self.set_flag(StatusFlag::BrkIrq, true); - let _ = self.push_stack(self.p); - self.set_flag(StatusFlag::IrqDisable, true); - if self.nmi { - match self.read_word(0xFFFA) { - Ok(word) => self.pc = word, - Err(error) => { - let george_error = GeorgeError { - kind: GeorgeErrorKind::Memory(error), - desc: String::from_str("Couldn't read NMI vector").unwrap(), - }; - println!("{:?}", george_error.desc); - } - }; - self.pending_cycles = 8; - } else { - match self.read_word(0xFFFE) { - Ok(word) => self.pc = word, - Err(error) => { - let george_error = GeorgeError { - kind: GeorgeErrorKind::Memory(error), - desc: String::from_str("Couldn't read IRQ vector").unwrap(), - }; - println!("{:?}", george_error.desc); - } - }; - self.pending_cycles = 7 - } - self.nmi = false; - self.irq = false; - } - } else { - let opcode = match self.read(self.pc) { - Ok(byte) => byte, - Err(error) => { - let george_error = GeorgeError { - desc: format!("Failed to read from memory at address {:#06x}!", self.pc), - kind: GeorgeErrorKind::Memory(error), - }; - println!("{:?}", george_error.desc); - return; - } - }; - let instruction = get_instruction(opcode); - match instruction { - Instruction::Valid(valid_instruction) => { - //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!(""); - self.pc += 1; - match valid_instruction.opcode.call(self) { - Ok(_) => { - self.pending_cycles += valid_instruction.cycles as usize; - } - Err(error) => { - let george_error = GeorgeError{ - desc: format!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}", - self.pc, valid_instruction).to_string(), kind: error}; - println!("{:?}", george_error.desc); - } - }; - } - Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode { - 0x02 => { - let memory = match self.memory.lock() { - Ok(read) => read, - Err(_) => { - println!("Couldn't acquire read lock on memory in cpu thread"); - let george_error = GeorgeError { - kind: GeorgeErrorKind::Memory(MemoryError::Unwritable), - desc: "Couldn't acquire read lock on memory in cpu thread" - .to_string(), - }; - println!("{:?}", george_error.desc); - return; - } - }; + pub fn interrupt(&mut self) { + self.push_stack_word(self.pc).unwrap(); + self.push_stack(self.p).unwrap(); + self.set_flag(StatusFlag::IrqDisable, true); + self.pc = self.read_word(0xFFFE).unwrap(); + } - 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); - memory - .dump(PathBuf::from_str("./cpu_dump.bin").unwrap()) - .unwrap(); - exit(1); + pub fn cycle(&mut self) { + while self.pending_cycles != 0 { + // roughly cycle-accurate timing + sleep(Duration::from_nanos(100)); + self.pending_cycles -= 1; + } + if !self.get_flag(StatusFlag::IrqDisable) && self.irq.recv().unwrap() { + self.interrupt(); + } + let opcode = match self.read(self.pc) { + Ok(byte) => byte, + Err(_) => { + println!("Failed to read from memory at address {:#06x}!", self.pc); + return; + } + }; + let instruction = get_instruction(opcode); + match instruction { + Instruction::Valid(valid_instruction) => { + //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!(""); + self.pc += 1; + match valid_instruction.opcode.call(self) { + Ok(_) => { + self.pending_cycles += valid_instruction.cycles as usize; } - _ => { - let george_error = GeorgeError { - kind: GeorgeErrorKind::Execution(ExecutionError::InvalidInstruction), - desc: format!( + Err(_) => { + println!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}", self.pc, valid_instruction); + } + }; + } + Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode { + 0x02 => { + let memory = match self.memory.lock() { + Ok(read) => read, + Err(_) => { + println!("Couldn't acquire read lock on memory in cpu thread"); + return; + } + }; + + 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.try_recv().unwrap_or_default(), nmi = self.nmi); + memory + .dump(PathBuf::from_str("./cpu_dump.bin").unwrap()) + .unwrap(); + } + _ => { + println!( "An invalid instruction with opcode {:#04x} was called at address {:#06x}", invalid_instruction.opcode, self.pc - ), - }; - println!("{:?}", george_error.desc); - } - }, - } + ); + } + }, } - self.pending_cycles -= 1; self.cycle_count += 1; } diff --git a/src/george b/src/george deleted file mode 100644 index cd0e6b5..0000000 Binary files a/src/george and /dev/null differ diff --git a/src/george.asm b/src/george.asm deleted file mode 100644 index 82bf8d3..0000000 --- a/src/george.asm +++ /dev/null @@ -1,240 +0,0 @@ -.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 - -kb_row0 = $4400 ; keyboard hardware register, there are 5 more but i can just increment from here - -.segment "ROM" - -reset: - ldx #0; initialize data stack pointer -;lda #$80 -;sta $200 -;adc $200 -;breakpoint - -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 - -main: - ;jsr draw - jsr keyboard - jmp main - -keyboard: - ldy #0 ; loop through each row - @loop: - lda kb_row0, y - beq @skip ; if row has no key pressed, skip checking which key - jsr key_pressed - @skip: - iny - cpy #5 - bne @loop - rts - -key_pressed: ; a is loaded with the row byte - sty key_row ; store character row - inc cursor - pha - phy - ldy #0 - @find_col: ; test each row bit, store column if key pressed - lsr ; test bit 7 - bcs store_col ; if set, go store character column - 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 - -do_something_w_key: ; we've stored the character position, now let's - lda keymap, y - ldy cursor - sta $6000, y - pla - ply - rts - -keymap: - .byte "?outrew?" - .byte "?piygsq?" - .byte "a??khvd?" - .byte "42ljbfz?" - .byte "31?mncx?" - .byte "?????ssm" - -draw: -; lda #%01000000 -; bit $4400 -; beq @1 -; push_coords #0, #0 -; push_char #$77 -; jsr draw_char -; @1: -; lda #%00100000 -; bit $4400 -; beq @2 -; push_coords #0, #0 -; push_char #$65 -; jsr draw_char -; @2: - 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 - -; --- Data Stack --- ; -; on this channel we love garth wilson: https://wilsonminesco.com/stacks/StackOps.ASM -; data stack is built up of 2-byte cells - - -; TODO: this is broken, jumping here does nothing to the stack and skips several instructions, could be an emulator problem tho -push_lit: ; this bad boy lets you inline a literal (low byte first) right after `jsr push_lit` and put it on the stack, once again, on this channel we love garth wilson - push2 - phx - tsx - txa - tay - plx - - lda $102, y - sta 0, x - clc - adc #2 - sta $102, y - - lda $103, y - sta 1, x - adc #0 - sta $103, y - -fetch: - lda (0, x) - pha - inc 0, x - bne @1 - inc 1, x -@1: lda (0, x) - bra put - push - -put: - sta 1, x - pla - sta 0, x - rts - -plus: ; add: (n1 n2 -- n1+n2) - clc - lda 0, x - adc 2, x - sta 2, x - lda 1, x - adc 3, x - sta 3, x - pop - rts - - -mult: ; multiply: (n1 n2 -- n1*n2), frankly, i don't know how this works, but TODO: will try to figure it out later - phy - stz n - ldy #0 -@1: lsr 3, x - ror 2, x - bcc @2 - clc - lda n - adc 0, x - sta n - tya - adc 1, x - tay -@2: asl 0, x - rol 1, x - lda 2, x - ora 3, x - bne @1 - lda n - sta 2, x - sty 3, x - pop - ply - rts - - -.segment "VECTOR" -.word reset - diff --git a/src/george.config b/src/george.config deleted file mode 100644 index d0ad24e..0000000 --- a/src/george.config +++ /dev/null @@ -1,10 +0,0 @@ -MEMORY { - RAM: start = $0000, size = $8000, type = ro, fill = yes; - ROM: start = $8000, size = $7FFC, type = ro, fill = yes; - VECTOR: start = $FFFC, size = $4, type = ro, fill = yes; - } - -SEGMENTS { - ROM: load = "ROM", type = ro; - VECTOR: load = "VECTOR", type = ro; - } diff --git a/src/instructions.rs b/src/instructions.rs index b1b3631..bb40e09 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -1,11 +1,12 @@ #![allow(clippy::upper_case_acronyms)] use crate::cpu::{Cpu, StatusFlag}; -use crate::error::{AddressingModeError, ExecutionError, GeorgeErrorKind}; use crate::types::{Byte, Word}; use std::collections::HashMap; +use anyhow::{bail, Result}; + #[derive(Clone, Debug)] pub enum Instruction { Valid(ValidInstruction), @@ -470,7 +471,7 @@ pub fn get_instruction(opcode: u8) -> Instruction { ( 0x40, Instruction::Valid(ValidInstruction { - opcode: Opcode::RTI(AddressingMode::Stack), + opcode: Opcode::RTI(AddressingMode::Implied), cycles: 6, }), ), @@ -1827,10 +1828,12 @@ enum AddressingModeValue { } impl TryFrom for Word { - type Error = AddressingModeError; - fn try_from(value: AddressingModeValue) -> Result { + type Error = anyhow::Error; + fn try_from(value: AddressingModeValue) -> Result { match value { - AddressingModeValue::Implied => Err(AddressingModeError::IncompatibleAddrMode), + AddressingModeValue::Implied => bail!( + "An addressing mode value without an address was attempted to turn into a u16" + ), AddressingModeValue::RelativeTest(_zero_page, offset) => Ok(offset as Word), AddressingModeValue::Relative(inner_value) => Ok(inner_value as Word), AddressingModeValue::Absolute(inner_value) => Ok(inner_value), @@ -1839,10 +1842,7 @@ impl TryFrom for Word { } } -fn get_address( - mode: &AddressingMode, - cpu: &mut Cpu, -) -> Result { +fn get_address(mode: &AddressingMode, cpu: &mut Cpu) -> Result { fn accumulator(cpu: &mut Cpu) -> AddressingModeValue { let byte = cpu.a; AddressingModeValue::Accumulator(byte) @@ -1856,30 +1856,30 @@ fn get_address( fn immediate(cpu: &mut Cpu) -> AddressingModeValue { // # let address: Word = cpu.pc; - cpu.pc += 1; + cpu.pc = cpu.pc.wrapping_add(1); AddressingModeValue::Absolute(address) } - fn absolute_a(cpu: &mut Cpu) -> Result { + fn absolute_a(cpu: &mut Cpu) -> Result { // a let address = cpu.read_word(cpu.pc)?; - cpu.pc += 2; + cpu.pc = cpu.pc.wrapping_add(2); Ok(AddressingModeValue::Absolute(address)) } - fn zero_page(cpu: &mut Cpu) -> Result { + fn zero_page(cpu: &mut Cpu) -> Result { // zp let address = cpu.read(cpu.pc)?; - cpu.pc += 1; + cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::Absolute(address as Word)) } fn absolute_indexed_with_x( // a, y cpu: &mut Cpu, - ) -> Result { + ) -> Result { let word = cpu.read_word(cpu.pc)?; - cpu.pc += 2; + cpu.pc = cpu.pc.wrapping_add(2); let address: Word = word + cpu.x as Word; Ok(AddressingModeValue::Absolute(address)) } @@ -1887,9 +1887,9 @@ fn get_address( fn absolute_indexed_with_y( // a, y cpu: &mut Cpu, - ) -> Result { + ) -> Result { let word = cpu.read_word(cpu.pc)?; - cpu.pc += 2; + cpu.pc = cpu.pc.wrapping_add(2); let address: Word = word + cpu.y as Word; Ok(AddressingModeValue::Absolute(address)) } @@ -1897,9 +1897,9 @@ fn get_address( fn zero_page_indexed_with_x( // zp, x cpu: &mut Cpu, - ) -> Result { + ) -> Result { let byte = cpu.read(cpu.pc)?; - cpu.pc += 1; + cpu.pc = cpu.pc.wrapping_add(1); let address: Word = (byte.wrapping_add(cpu.x)) as Word; Ok(AddressingModeValue::Absolute(address)) } @@ -1907,9 +1907,9 @@ fn get_address( fn zero_page_indexed_with_y( // zp, y cpu: &mut Cpu, - ) -> Result { + ) -> Result { let byte = cpu.read(cpu.pc)?; - cpu.pc += 1; + cpu.pc = cpu.pc.wrapping_add(1); let address: Word = (byte + cpu.y) as Word; Ok(AddressingModeValue::Absolute(address)) } @@ -1917,70 +1917,70 @@ fn get_address( fn absolute_indirect( // (a) cpu: &mut Cpu, - ) -> Result { + ) -> Result { let word = cpu.read_word(cpu.pc)?; let address = cpu.read_word(word)?; - cpu.pc += 2; + cpu.pc = cpu.pc.wrapping_add(2); Ok(AddressingModeValue::Absolute(address)) } fn absolute_indexed_indirect( // (a, x), only used with the JMP instruction cpu: &mut Cpu, - ) -> Result { + ) -> Result { let word = cpu.read_word(cpu.pc)?; let address = cpu.read_word(word + cpu.x as Word)?; - cpu.pc += 2; + cpu.pc = cpu.pc.wrapping_add(2); Ok(AddressingModeValue::Absolute(address)) } fn zero_page_indirect( // (zp) cpu: &mut Cpu, - ) -> Result { + ) -> Result { let byte: Byte = cpu.read(cpu.pc)?; let address: Word = cpu.read_word(byte as Word)?; - cpu.pc += 1; + cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::Absolute(address as Word)) } fn zero_page_indexed_indirect( // (zp, x) cpu: &mut Cpu, - ) -> Result { + ) -> Result { let byte = cpu.read(cpu.pc)?; - let address = cpu.read_word((byte + cpu.x) as Word)?; // Anytime you see something like `byte as Word`, it's using the byte as a zero-page address - cpu.pc += 1; + let address = cpu.read_word((byte.wrapping_add(cpu.x)) as Word)?; // Anytime you see something like `byte as Word`, it's using the byte as a zero-page address + cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::Absolute(address)) } fn zero_page_indirect_indexed_with_y( // (zp), y cpu: &mut Cpu, - ) -> Result { + ) -> Result { let byte: Byte = cpu.read(cpu.pc)?; - let address: Word = cpu.read_word(byte as Word)?; - cpu.pc += 1; - Ok(AddressingModeValue::Absolute(address + cpu.y as Word)) + let address: Word = cpu.read_word(byte.wrapping_add(cpu.y) as Word)?; + cpu.pc = cpu.pc.wrapping_add(1); + Ok(AddressingModeValue::Absolute(address)) } fn relative( // r cpu: &mut Cpu, - ) -> Result { + ) -> Result { let byte: Byte = cpu.read(cpu.pc)?; - cpu.pc += 1; + cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::Relative(byte)) } fn relative_test( // r cpu: &mut Cpu, - ) -> Result { + ) -> Result { let byte: Byte = cpu.read(cpu.pc)?; - cpu.pc += 1; + cpu.pc = cpu.pc.wrapping_add(1); let address = cpu.read(cpu.pc)?; - cpu.pc += 1; + cpu.pc = cpu.pc.wrapping_add(1); Ok(AddressingModeValue::RelativeTest(byte, address)) } @@ -2004,19 +2004,14 @@ fn get_address( } } -fn signed_byte_to_word(value: Byte) -> Word { - let mut value = Word::from(value); - if value & 0x80 > 0 { - value |= 0xff00; +fn branch(cpu: &mut Cpu, condition: bool, value: AddressingModeValue) -> Result<()> { + fn signed_byte_to_word(value: Byte) -> Word { + let mut value = Word::from(value); + if value & 0x80 > 0 { + value |= 0xff00; + } + value } - value -} - -fn branch( - cpu: &mut Cpu, - condition: bool, - value: AddressingModeValue, -) -> Result<(), AddressingModeError> { match value { AddressingModeValue::Relative(address) => { let address = signed_byte_to_word(address).wrapping_add(cpu.pc); @@ -2042,12 +2037,12 @@ fn branch( } Ok(()) } - _ => Err(AddressingModeError::IncompatibleAddrMode), + _ => bail!("Tried to branch but received an invalid addressing mode value"), } } impl Opcode { - pub fn call(&self, cpu: &mut Cpu) -> Result<(), GeorgeErrorKind> { + pub fn call(&self, cpu: &mut Cpu) -> Result<()> { match self { Opcode::ADC(mode) => match mode { AddressingMode::Immediate @@ -2063,7 +2058,6 @@ impl Opcode { let byte = cpu.read(address.try_into()?)?; let carry = cpu.get_flag(StatusFlag::Carry); let result = cpu.a as Word + byte as Word + carry as Word; - println!("{result:#04x}"); cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value())); cpu.set_flag(StatusFlag::Zero, result as Byte == 0); cpu.set_flag( @@ -2074,12 +2068,9 @@ impl Opcode { ); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as Byte)); cpu.a = result as Byte; - println!("{:#04x}", cpu.a); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::AND(mode) => match mode { AddressingMode::Immediate @@ -2097,9 +2088,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::ASL(mode) => { fn asl(cpu: &mut Cpu, value: Byte) -> Byte { @@ -2124,9 +2113,7 @@ impl Opcode { cpu.write(address.try_into()?, result)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) } } Opcode::BBR0(mode) => match mode { @@ -2140,15 +2127,11 @@ impl Opcode { branch(cpu, test_byte & 0b0000_0001 != 0b0000_0001, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBR1(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2159,15 +2142,11 @@ impl Opcode { branch(cpu, test_byte & 0b0000_0010 != 0b0000_0010, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBR2(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2178,15 +2157,11 @@ impl Opcode { branch(cpu, test_byte & 0b0000_0100 != 0b0000_0100, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBR3(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2197,15 +2172,11 @@ impl Opcode { branch(cpu, test_byte & 0b0000_1000 != 0b0000_1000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBR4(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2216,15 +2187,11 @@ impl Opcode { branch(cpu, test_byte & 0b0001_0000 != 0b0001_0000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBR5(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2235,15 +2202,11 @@ impl Opcode { branch(cpu, test_byte & 0b0010_0000 != 0b0010_0000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBR6(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2254,15 +2217,11 @@ impl Opcode { branch(cpu, test_byte & 0b0100_0000 != 0b0100_0000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBR7(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2273,15 +2232,11 @@ impl Opcode { branch(cpu, test_byte & 0b1000_0000 != 0b1000_0000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBS0(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2292,15 +2247,11 @@ impl Opcode { branch(cpu, test_byte & 0b0000_0001 == 0b0000_0001, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBS1(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2311,15 +2262,11 @@ impl Opcode { branch(cpu, test_byte & 0b0000_0010 == 0b0000_0010, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBS2(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2330,15 +2277,11 @@ impl Opcode { branch(cpu, test_byte & 0b0000_0100 == 0b0000_0100, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBS3(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2349,15 +2292,11 @@ impl Opcode { branch(cpu, test_byte & 0b0000_1000 == 0b0000_1000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBS4(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2368,15 +2307,11 @@ impl Opcode { branch(cpu, test_byte & 0b0001_0000 == 0b0001_0000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBS5(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2387,15 +2322,11 @@ impl Opcode { branch(cpu, test_byte & 0b0010_0000 == 0b0010_0000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBS6(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2406,15 +2337,11 @@ impl Opcode { branch(cpu, test_byte & 0b0100_0000 == 0b0100_0000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BBS7(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { @@ -2425,15 +2352,11 @@ impl Opcode { branch(cpu, test_byte & 0b1000_0000 == 0b1000_0000, value)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, - Err(error) => Err(GeorgeErrorKind::Execution(error)), + Err(error) => bail!("Tried to get address, but {error:#?}"), }, - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BCC(mode) => match mode { AddressingMode::ProgramCounterRelative => { @@ -2441,9 +2364,7 @@ impl Opcode { branch(cpu, !cpu.get_flag(StatusFlag::Carry), address)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BCS(mode) => match mode { AddressingMode::ProgramCounterRelative => { @@ -2451,9 +2372,7 @@ impl Opcode { branch(cpu, cpu.get_flag(StatusFlag::Carry), address)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BEQ(mode) => match mode { AddressingMode::ProgramCounterRelative => { @@ -2461,9 +2380,7 @@ impl Opcode { branch(cpu, cpu.get_flag(StatusFlag::Zero), address)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BIT(mode) => match mode { AddressingMode::Immediate @@ -2484,9 +2401,7 @@ impl Opcode { ); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BMI(mode) => match mode { AddressingMode::ProgramCounterRelative => { @@ -2494,9 +2409,7 @@ impl Opcode { branch(cpu, cpu.get_flag(StatusFlag::Negative), address)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BNE(mode) => match mode { AddressingMode::ProgramCounterRelative => { @@ -2504,9 +2417,7 @@ impl Opcode { branch(cpu, !cpu.get_flag(StatusFlag::Zero), address)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BPL(mode) => match mode { AddressingMode::ProgramCounterRelative => { @@ -2514,9 +2425,7 @@ impl Opcode { branch(cpu, !cpu.get_flag(StatusFlag::Negative), address)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BRA(mode) => match mode { AddressingMode::ProgramCounterRelative => { @@ -2524,9 +2433,7 @@ impl Opcode { branch(cpu, true, address)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BRK(mode) => match mode { AddressingMode::Implied => { @@ -2534,9 +2441,7 @@ impl Opcode { Ok(()) //panic!("Interrupts unimplemented!"); } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BVC(mode) => match mode { AddressingMode::ProgramCounterRelative => { @@ -2544,9 +2449,7 @@ impl Opcode { branch(cpu, !cpu.get_flag(StatusFlag::Overflow), address)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::BVS(mode) => match mode { AddressingMode::ProgramCounterRelative => { @@ -2554,45 +2457,35 @@ impl Opcode { branch(cpu, cpu.get_flag(StatusFlag::Overflow), address)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::CLC(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Carry, false); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::CLD(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Decimal, false); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::CLI(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::IrqDisable, false); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::CLV(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Overflow, false); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::CMP(mode) => match mode { AddressingMode::Immediate @@ -2611,9 +2504,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.a <= byte); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::CPX(mode) => match mode { AddressingMode::Immediate @@ -2626,9 +2517,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x - byte)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::CPY(mode) => match mode { AddressingMode::Immediate @@ -2636,15 +2525,12 @@ impl Opcode { | AddressingMode::AbsoluteA => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; - println!("{byte:#04x}"); cpu.set_flag(StatusFlag::Carry, cpu.y >= byte); cpu.set_flag(StatusFlag::Zero, cpu.y == byte); cpu.set_flag(StatusFlag::Negative, cpu.y <= byte); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::DEC(mode) => match mode { AddressingMode::ZeroPage @@ -2652,12 +2538,14 @@ impl Opcode { | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX => { let address = get_address(mode, cpu)?; - cpu.write(address.try_into()?, cpu.read(address.try_into()?)? - 1)?; + let byte = cpu.read(address.try_into()?)?; + let dec_byte = byte.wrapping_sub(1); + cpu.write(address.try_into()?, dec_byte)?; + cpu.set_flag(StatusFlag::Zero, dec_byte == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(dec_byte)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::DEX(mode) => match mode { AddressingMode::Implied => { @@ -2666,9 +2554,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::DEY(mode) => match mode { AddressingMode::Implied => { @@ -2677,9 +2563,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::EOR(mode) => match mode { AddressingMode::Immediate @@ -2697,9 +2581,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::INC(mode) => match mode { AddressingMode::Accumulator => { @@ -2719,9 +2601,7 @@ impl Opcode { cpu.write(address.try_into()?, byte.wrapping_add(1))?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::INX(mode) => match mode { AddressingMode::Implied => { @@ -2730,9 +2610,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::INY(mode) => match mode { AddressingMode::Implied => { @@ -2741,9 +2619,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::JMP(mode) => match mode { AddressingMode::AbsoluteA @@ -2753,9 +2629,7 @@ impl Opcode { cpu.pc = address.try_into()?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::JSR(mode) => match mode { AddressingMode::AbsoluteA => { @@ -2765,9 +2639,7 @@ impl Opcode { cpu.pc = address.try_into()?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::LDA(mode) => match mode { AddressingMode::AbsoluteA @@ -2786,9 +2658,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Zero, cpu.a == 0); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::LDX(mode) => match mode { AddressingMode::AbsoluteA @@ -2803,9 +2673,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Zero, cpu.x == 0); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::LDY(mode) => match mode { AddressingMode::AbsoluteA @@ -2820,9 +2688,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Zero, cpu.y == 0); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::LSR(mode) => { fn lsr(cpu: &mut Cpu, value: Byte) -> Byte { @@ -2847,13 +2713,11 @@ impl Opcode { cpu.write(address.try_into()?, result)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) } } Opcode::NOP(_mode) => { - cpu.pc += 1; + cpu.pc = cpu.pc.wrapping_add(1); Ok(()) } Opcode::ORA(mode) => match mode { @@ -2872,81 +2736,63 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::PHA(mode) => match mode { AddressingMode::Stack => { cpu.push_stack(cpu.a)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::PHP(mode) => match mode { AddressingMode::Stack => { cpu.push_stack(cpu.p)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::PHX(mode) => match mode { AddressingMode::Stack => { cpu.push_stack(cpu.x)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::PHY(mode) => match mode { AddressingMode::Stack => { cpu.push_stack(cpu.y)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::PLA(mode) => match mode { AddressingMode::Stack => { cpu.a = cpu.pop_stack()?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::PLP(mode) => match mode { AddressingMode::Stack => { cpu.p = cpu.pop_stack()?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::PLX(mode) => match mode { AddressingMode::Stack => { cpu.x = cpu.pop_stack()?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::PLY(mode) => match mode { AddressingMode::Stack => { cpu.y = cpu.pop_stack()?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::RMB0(mode) => match mode { AddressingMode::ZeroPage => { @@ -2956,9 +2802,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::RMB1(mode) => match mode { AddressingMode::ZeroPage => { @@ -2968,9 +2812,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::RMB2(mode) => match mode { AddressingMode::ZeroPage => { @@ -2980,9 +2822,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::RMB3(mode) => match mode { AddressingMode::ZeroPage => { @@ -2992,9 +2832,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::RMB4(mode) => match mode { AddressingMode::ZeroPage => { @@ -3004,9 +2842,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::RMB5(mode) => match mode { AddressingMode::ZeroPage => { @@ -3016,9 +2852,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::RMB6(mode) => match mode { AddressingMode::ZeroPage => { @@ -3028,9 +2862,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::RMB7(mode) => match mode { AddressingMode::ZeroPage => { @@ -3040,9 +2872,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::ROL(mode) => { fn rol(cpu: &mut Cpu, value: Byte) -> Byte { @@ -3068,9 +2898,7 @@ impl Opcode { cpu.write(address.try_into()?, result)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) } } Opcode::ROR(mode) => { @@ -3097,20 +2925,16 @@ impl Opcode { cpu.write(address.try_into()?, result)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) } } Opcode::RTI(mode) => match mode { AddressingMode::Implied => { - cpu.s = cpu.pop_stack()?; cpu.p = cpu.pop_stack()?; + cpu.pc = cpu.pop_stack_word()?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::RTS(mode) => match mode { AddressingMode::Stack => { @@ -3118,9 +2942,7 @@ impl Opcode { cpu.pc = return_address + 3; // Go back to where we jsr'ed, skipping the operand Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SBC(mode) => match mode { AddressingMode::Immediate @@ -3147,36 +2969,28 @@ impl Opcode { cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as Byte)); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SEC(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Carry, true); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SED(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Decimal, true); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SEI(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::IrqDisable, true); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SMB0(mode) => match mode { AddressingMode::ZeroPage => { @@ -3186,9 +3000,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SMB1(mode) => match mode { AddressingMode::ZeroPage => { @@ -3198,9 +3010,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SMB2(mode) => match mode { AddressingMode::ZeroPage => { @@ -3210,9 +3020,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SMB3(mode) => match mode { AddressingMode::ZeroPage => { @@ -3222,9 +3030,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SMB4(mode) => match mode { AddressingMode::ZeroPage => { @@ -3234,9 +3040,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SMB5(mode) => match mode { AddressingMode::ZeroPage => { @@ -3246,9 +3050,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SMB6(mode) => match mode { AddressingMode::ZeroPage => { @@ -3258,9 +3060,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::SMB7(mode) => match mode { AddressingMode::ZeroPage => { @@ -3270,9 +3070,7 @@ impl Opcode { cpu.write(address.try_into()?, reset_byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::STA(mode) => match mode { AddressingMode::AbsoluteA @@ -3287,18 +3085,14 @@ impl Opcode { cpu.write(address.try_into()?, cpu.a)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::STP(mode) => match mode { AddressingMode::Implied => { cpu.stop(); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::STX(mode) => match mode { AddressingMode::AbsoluteA @@ -3308,9 +3102,7 @@ impl Opcode { cpu.write(address.try_into()?, cpu.x)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::STY(mode) => match mode { AddressingMode::AbsoluteA @@ -3320,9 +3112,7 @@ impl Opcode { cpu.write(address.try_into()?, cpu.y)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::STZ(mode) => match mode { AddressingMode::AbsoluteA @@ -3333,9 +3123,7 @@ impl Opcode { cpu.write(address.try_into()?, 0)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::TAX(mode) => match mode { AddressingMode::Implied => { @@ -3344,9 +3132,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Zero, cpu.x == 0); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::TAY(mode) => match mode { AddressingMode::Implied => { @@ -3355,9 +3141,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Zero, cpu.y == 0); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::TRB(mode) => match mode { // Still not really sure when you would @@ -3370,9 +3154,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::TSB(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::ZeroPage => { @@ -3381,9 +3163,7 @@ impl Opcode { cpu.write(address.try_into()?, cpu.a | byte)?; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::TSX(mode) => match mode { AddressingMode::Implied => { @@ -3392,9 +3172,7 @@ impl Opcode { cpu.set_flag(StatusFlag::Zero, cpu.x == 0); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::TXA(mode) => match mode { AddressingMode::Implied => { @@ -3403,18 +3181,14 @@ impl Opcode { cpu.set_flag(StatusFlag::Zero, cpu.a == 0); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::TXS(mode) => match mode { AddressingMode::Implied => { cpu.s = cpu.x; Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::TYA(mode) => match mode { AddressingMode::Implied => { @@ -3423,18 +3197,14 @@ impl Opcode { cpu.set_flag(StatusFlag::Zero, cpu.a == 0); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, Opcode::WAI(mode) => match mode { AddressingMode::Implied => { cpu.wait_for_interrupt(); Ok(()) } - _ => Err(GeorgeErrorKind::AddrMode( - AddressingModeError::IncompatibleAddrMode, - )), + _ => bail!("An incompatible addressing mode was used for instruction {self:#?} at address {:#04x}", cpu.pc) }, } } diff --git a/src/keyboard.rs b/src/keyboard.rs new file mode 100644 index 0000000..f7839c6 --- /dev/null +++ b/src/keyboard.rs @@ -0,0 +1,92 @@ +use minifb::{InputCallback, Key}; +use std::{ + path::PathBuf, + process::exit, + str::FromStr, + sync::{Arc, Mutex}, +}; + +use crate::memory::Mem; + +pub struct Keyboard { + memory: Arc>, +} + +impl Keyboard { + pub fn new(memory: Arc>) -> Self { + Self { memory } + } +} + +impl InputCallback for Keyboard { + fn add_char(&mut self, _uni_char: u32) {} + fn set_key_state(&mut self, key: Key, _state: bool) { + let mut row0 = 0; + let mut row1 = 0; + let mut row2 = 0; + let mut row3 = 0; + let mut row4 = 0; + let mut row5 = 0; + + match key { + Key::Escape => row0 ^= 0b1000_0000, + Key::W => row0 ^= 0b0100_0000, + Key::E => row0 ^= 0b0010_0000, + Key::R => row0 ^= 0b0001_0000, + Key::T => row0 ^= 0b0000_1000, + Key::U => row0 ^= 0b0000_0100, + Key::O => row0 ^= 0b0000_0010, + Key::Backspace => row0 ^= 0b0000_0001, + Key::Tab => row1 ^= 0b1000_0000, + Key::Q => row1 ^= 0b0100_0000, + Key::S => row1 ^= 0b0010_0000, + Key::G => row1 ^= 0b0001_0000, + Key::Y => row1 ^= 0b0000_1000, + Key::I => row1 ^= 0b0000_0100, + Key::P => row1 ^= 0b0000_0010, + Key::Enter => row1 ^= 0b0000_0001, + Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000, + Key::D => row2 ^= 0b0100_0000, + Key::V => row2 ^= 0b0010_0000, + Key::H => row2 ^= 0b0001_0000, + Key::K => row2 ^= 0b0000_1000, + Key::Apostrophe => row2 ^= 0b0000_0100, + Key::Slash => row2 ^= 0b0000_0010, + Key::A => row2 ^= 0b0000_0001, + Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000, + Key::Z => row3 ^= 0b0100_0000, + Key::F => row3 ^= 0b0010_0000, + Key::B => row3 ^= 0b0001_0000, + Key::J => row3 ^= 0b0000_1000, + Key::L => row3 ^= 0b0000_0100, + Key::Key2 => row3 ^= 0b0000_0010, + Key::Key4 => row3 ^= 0b0000_0001, + Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000, + Key::X => row4 ^= 0b0100_0000, + Key::C => row4 ^= 0b0010_0000, + Key::N => row4 ^= 0b0001_0000, + Key::M => row4 ^= 0b0000_1000, + Key::Comma => row4 ^= 0b0000_0100, + Key::Key1 => row4 ^= 0b0000_0010, + Key::Key3 => row4 ^= 0b0000_0001, + Key::LeftSuper => row5 ^= 0b1000_0000, + Key::Space => row5 ^= 0b0100_0000, + Key::RightSuper => row5 ^= 0b0010_0000, + _ => {} + }; + + match &mut self.memory.lock() { + Ok(memory) => { + memory.write(0x4400, row0); + memory.write(0x4401, row1); + memory.write(0x4402, row2); + memory.write(0x4403, row3); + memory.write(0x4404, row4); + memory.write(0x4405, row5); + } + Err(_error) => { + println!("couldnt get a lock on memory while saving keyboard registers"); + } + } + } +} diff --git a/src/main.rs b/src/main.rs index a0b5c8b..1ef59c2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,27 +9,58 @@ mod types; mod video; use crate::cpu::Cpu; +use crate::keyboard::Keyboard; use crate::memory::Mem; use crate::video::Crtc; +use clap::Parser; +use minifb::{Scale, ScaleMode, Window, WindowOptions}; +use serde::{Deserialize, Serialize}; + +use std::fs::File; +use std::io::Read; +use std::process::exit; use std::str::FromStr; -use std::sync::Mutex; -use std::thread::sleep; -use std::time::Duration; +use std::sync::{mpsc, Mutex}; use std::{path::PathBuf, sync::Arc, thread}; +use toml::Table; + +//#[derive(Parser)] +//struct Cli { +// // Load a rom onto the system rom +// #[arg(short, required = true)] +// rom: Option, +//} + +#[derive(Serialize, Deserialize, Debug)] +struct Config { + char_rom: Option, + rom: String, +} + fn main() { - let mut memory = Mem::new(); - let binary = match std::fs::File::open(PathBuf::from( - "/Users/kline/projects/winter/george-emu/src/george", - )) { - Ok(file) => file, - Err(error) => panic!("Couldn't open binary file! {:?}", error), + let config: Config = match File::open("./config.toml") { + Ok(mut file) => { + let mut string = String::new(); + file.read_to_string(&mut string).unwrap(); + toml::from_str(string.as_str()).unwrap() + } + Err(_) => return, }; - if let Err(error) = memory.read_from_bin(binary) { + println!("{config:#?}"); + let mut memory = Mem::new(); + let rom = match std::fs::File::open(config.rom) { + Ok(file) => file, + Err(error) => panic!("Couldn't open main rom! {:?}", error), + }; + if let Err(error) = memory.load_rom(rom) { println!("{:?}", error); }; + let (interrupt_tx, interrupt_rx) = mpsc::channel(); + let (window_tx, window_rx) = mpsc::channel(); + memory .dump(PathBuf::from_str("./coredump.bin").unwrap()) .unwrap(); @@ -37,12 +68,45 @@ fn main() { let shared_memory = Arc::new(Mutex::new(memory)); let cpu_memory = shared_memory.clone(); let display_memory = shared_memory.clone(); + let keyboard_memory = shared_memory.clone(); thread::spawn(move || { - let mut cpu = Cpu::new(cpu_memory); + let mut cpu = Cpu::new(cpu_memory, interrupt_rx); cpu.reset().unwrap(); cpu.execute(); }); - let mut screen = Crtc::new(display_memory); - screen.run(); + + thread::spawn(move || { + let mut screen = Crtc::new( + display_memory, + config.char_rom.as_ref(), + interrupt_tx, + window_tx, + ); + screen.run(); + }); + + let mut window = Window::new( + "ʕ·ᴥ·ʔ-☆", + 512, + 380, + WindowOptions { + resize: true, + borderless: true, + title: true, + transparency: false, + scale: Scale::X2, + scale_mode: ScaleMode::AspectRatioStretch, + topmost: false, + none: true, + }, + ) + .unwrap(); + + window.set_input_callback(Box::new(Keyboard::new(keyboard_memory))); + + while window.is_open() { + let buffer = window_rx.recv().unwrap(); + window.update_with_buffer(&buffer, 512, 380).unwrap(); + } } diff --git a/src/memory.rs b/src/memory.rs index adc564e..66e3e5b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -30,17 +30,30 @@ impl Mem { self.data[address as usize] = data; } - pub fn read_from_bin(&mut self, f: File) -> Result<(), MemoryError> { - let bytes = f.bytes(); + pub fn load_rom(&mut self, rom: File) -> Result<(), MemoryError> { + let bytes = rom.bytes(); for (address, byte) in bytes.enumerate() { match byte { - Ok(value) => self.write(address as Word, value), + Ok(value) => self.write(address as Word + 0x8000, value), Err(_) => { - println!("couldn't write byte {:#04x}", address); + println!("Loading rom: couldn't write byte {:#04x}", address); return Err(MemoryError::Unwritable); } } } Ok(()) } + //pub fn read_from_bin(&mut self, f: File) -> Result<(), MemoryError> { + // let bytes = f.bytes(); + // for (address, byte) in bytes.enumerate() { + // match byte { + // Ok(value) => self.write(address as Word, value), + // Err(_) => { + // println!("couldn't write byte {:#04x}", address); + // return Err(MemoryError::Unwritable); + // } + // } + // } + // Ok(()) + //} } diff --git a/src/roms/cozette.rom b/src/roms/cozette.rom new file mode 100644 index 0000000..9ce58aa Binary files /dev/null and b/src/roms/cozette.rom differ diff --git a/src/roms/george.asm b/src/roms/george.asm new file mode 100644 index 0000000..c24a7a7 --- /dev/null +++ b/src/roms/george.asm @@ -0,0 +1,164 @@ +; .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, there are 5 more but i can just increment from here +kb_row_cache = $203 ; cache + + .org $8000 + +reset: + sei + ldx #0; initialize data stack pointer + +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 draw + jmp main + +keyboard: + ldy #0 + .loop: ; loop through each row + lda kb_row, y + beq .skip ; 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: + iny + cpy #5 + bne .loop + rts + +key_down: ; a is loaded with the row byte + sty key_row ; store character row + inc cursor + pha + phy + 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 + +do_something_w_key: ; we've stored the character position, now let's + lda keymap, y + ldy cursor + sta $6000, y + ply + pla + 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 diff --git a/src/roms/george.rom b/src/roms/george.rom new file mode 100644 index 0000000..a72c0ec Binary files /dev/null and b/src/roms/george.rom differ diff --git a/src/roms/keyboard.asm b/src/roms/keyboard.asm new file mode 100644 index 0000000..0797a58 --- /dev/null +++ b/src/roms/keyboard.asm @@ -0,0 +1,142 @@ +; .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, there are 5 more but i can just increment from here +kb_row_cache = $203 ; cache + + .org $8000 + +reset: + sei + ldx #0; initialize data stack pointer + +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 draw + jmp main + +keyboard: + ldy #5 + .loop: ; loop through each row + lda kb_row, y + bne key_down; if row has key pressed, go to key_down subroutine + dey + bne .loop + rts + +key_down: ; a is loaded with the row byte + sty key_row ; store character row + inc cursor + pha + phy + 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 + +do_something_w_key: ; we've stored the character position, now let's + lda keymap, y + ldy cursor + sta $6000, y + ply + pla + rts + +keymap_0: + .byte "?outrew?" +keymap_1: + .byte "?piygsq?" +keymap_2: + .byte "a??khvd?" +keymap_3: + .byte "42ljbfz?" +keymap_4: + .byte "31?mncx?" +keymap_5: + .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 diff --git a/src/roms/macro.inc b/src/roms/macro.inc new file mode 100644 index 0000000..4690248 --- /dev/null +++ b/src/roms/macro.inc @@ -0,0 +1,62 @@ + .macro breakpoint ; $02 isn't a valid instruction, the emulator will see this and halt, dump memory contents + .byte $02 + .endm + + .macro pop ; drops a data stack cell + inx + inx + .endm + + .macro pop2 ; drops 2 data stack cells + inx + inx + inx + inx + .endm + + .macro push ; push a data stack cell + dex + dex + .endm + + .macro push2 ; push 2 data stack cells + dex + dex + dex + dex + .endm + + .macro push_char, char ; pushes an ascii character code onto the stack + lda \char + push + sta 0, x ; char low byte + stz 1, x ; char high byte + .endm + + .macro push_coords, coord_x, coord_y ; push a set of (x,y) coordinates onto the data stack + lda \coord_x + push + sta 0, x ; low byte + stz 1,x ; high byte is zero + lda \coord_y + push + sta 0,x ; same here + stz 1,x + .endm + + .macro to_r ; pop the top of the stack off and save it in the return (hardware) stack: (n -- ) + lda 1, x + pha + lda 0, x + pha + pop + .endm + + .macro from_r ; pop the top of the return stack off and put it on the data stack: ( -- n) + push + pla + sta 0, x + pla + sta 1, x + .endm + diff --git a/src/roms/math.inc b/src/roms/math.inc new file mode 100644 index 0000000..5ad4b1f --- /dev/null +++ b/src/roms/math.inc @@ -0,0 +1,81 @@ +; --- Data Stack --- ; +; on this channel we love garth wilson: https://wilsonminesco.com/stacks/StackOps.ASM +; data stack is built up of 2-byte cells + + +; TODO: this is broken, jumping here does nothing to the stack and skips several instructions, could be an emulator problem tho +; push_lit: ; this bad boy lets you inline a literal (low byte first) right after `jsr push_lit` and put it on the stack, once again, on this channel we love garth wilson +; push2 +; phx +; tsx +; txa +; tay +; plx + +; lda $102, y +; sta 0, x +; clc +; adc #2 +; sta $102, y + +; lda $103, y +; sta 1, x +; adc #0 +; sta $103, y + +; fetch: +; lda (0, x) +; pha +; inc 0, x +; bne .1 +; inc 1, x +; .1: +; lda (0, x) +; bra put +; push + +; put: +; sta 1, x +; pla +; sta 0, x +; rts + +plus: ; add: (n1 n2 -- n1+n2) + clc + lda 0, x + adc 2, x + sta 2, x + lda 1, x + adc 3, x + sta 3, x + pop + rts + + +mult: ; multiply: (n1 n2 -- n1*n2), frankly, i don't know how this works, but TODO: will try to figure it out later + phy + stz n + ldy #0 + .1: + lsr 3, x + ror 2, x + bcc .2 + clc + lda n + adc 0, x + sta n + tya + adc 1, x + tay + .2: + asl 0, x + rol 1, x + lda 2, x + ora 3, x + bne .1 + lda n + sta 2, x + sty 3, x + pop + ply + rts diff --git a/src/video.rs b/src/video.rs index 611f724..9f62d74 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,9 +1,12 @@ use crate::Mem; -use minifb::{Key, Scale, ScaleMode, Window, WindowOptions}; use std::{ fs::File, io::Read, - sync::{Arc, Mutex}, + path::Path, + sync::{ + mpsc::{Sender, SyncSender}, + Arc, Mutex, + }, thread::sleep, time::{Duration, Instant}, }; @@ -14,41 +17,44 @@ const BG_COLOR: u32 = 0x110500; pub struct Crtc { memory: Arc>, buffer: Vec, - window: minifb::Window, char_rom: Vec, + interrupt: Sender, + window: Sender>, } -pub fn get_char_bin(path: &str) -> Vec { - let mut file = File::open(path).unwrap(); - let mut buffer = vec![0; 0xFFFF]; - file.read_exact(&mut buffer).unwrap(); - buffer +pub fn get_char_bin

(char_rom: Option

) -> Vec +where + P: AsRef, +{ + 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 + } + None => include_bytes!("./roms/cozette.rom").to_vec(), + } } impl Crtc { - pub fn new(memory: Arc>) -> Self { - let window = Window::new( - "ʕ·ᴥ·ʔ-☆", - 512, - 380, - WindowOptions { - resize: true, - borderless: true, - title: true, - transparency: false, - scale: Scale::X2, - scale_mode: ScaleMode::AspectRatioStretch, - topmost: false, - none: true, - }, - ) - .unwrap(); - let char_rom = get_char_bin("./src/cozette.bin"); + pub fn new

( + memory: Arc>, + char_rom: Option

, + interrupt: Sender, + window: Sender>, + ) -> Self + where + P: AsRef, + { + let char_rom = get_char_bin(char_rom); Self { memory, buffer: vec![0; 512 * 380], window, char_rom, + interrupt, } } fn draw(&mut self) { @@ -59,127 +65,38 @@ impl Crtc { return; } }; - let hw_ctrl = memory.read(0x4000); - match hw_ctrl & 0b0000_1000 == 0b0000_1000 { - false => { - // 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; - for char_row in 0..29 { - for char_col in 0..64 { - let ascii = memory.read(0x6000 + i); - i += 1; - for row in 0..13 { - let byte = self.char_rom[ascii as usize + (row * 0x101)]; - for i in (0..8).rev() { - let buffer_index = - ((char_row) * 13 + (row)) * 512 + (char_col * 8 + i); - if (byte << i) & 0x80 == 0x80 { - self.buffer[buffer_index] = FG_COLOR; - } else { - self.buffer[buffer_index] = BG_COLOR; - } - } + // 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; + for char_row in 0..29 { + for char_col in 0..64 { + let ascii = memory.read(0x6000 + i); + i += 1; + for row in 0..13 { + let byte = self.char_rom[ascii as usize + (row * 0x100)]; + for i in (0..8).rev() { + let buffer_index = ((char_row) * 13 + (row)) * 512 + (char_col * 8 + i); + if (byte << i) & 0x80 == 0x80 { + self.buffer[buffer_index] = FG_COLOR; + } else { + self.buffer[buffer_index] = BG_COLOR; } } } } - true => { - for addr in 0x6000..0xBF00 { - // TODO: eventually this will access memory in the weird interleaved way the hardware is - // designed, but this is easiest to implement for now - let byte = memory.read(addr); - for i in 0..8 { - match byte & 0x80 >> i == 0 { - true => self.buffer[(addr - 0x6000) as usize * 8 + i] = BG_COLOR, - false => self.buffer[(addr - 0x6000) as usize * 8 + i] = FG_COLOR, - } - } - } - } - }; + } - self.window - .update_with_buffer(&self.buffer, 512, 380) - .unwrap(); + let buffer = self.buffer.to_owned(); + + self.window.send(buffer).unwrap(); } pub fn run(&mut self) { - let frame_duration = Duration::from_millis(16); - let mut previous_draw = Instant::now(); - loop { - let mut row0 = 0; - let mut row1 = 0; - let mut row2 = 0; - let mut row3 = 0; - let mut row4 = 0; - let mut row5 = 0; - - self.window.get_keys().iter().for_each(|key| match key { - Key::Escape => row0 ^= 0b1000_0000, - Key::W => row0 ^= 0b0100_0000, - Key::E => row0 ^= 0b0010_0000, - Key::R => row0 ^= 0b0001_0000, - Key::T => row0 ^= 0b0000_1000, - Key::U => row0 ^= 0b0000_0100, - Key::O => row0 ^= 0b0000_0010, - Key::Backspace => row0 ^= 0b0000_0001, - Key::Tab => row1 ^= 0b1000_0000, - Key::Q => row1 ^= 0b0100_0000, - Key::S => row1 ^= 0b0010_0000, - Key::G => row1 ^= 0b0001_0000, - Key::Y => row1 ^= 0b0000_1000, - Key::I => row1 ^= 0b0000_0100, - Key::P => row1 ^= 0b0000_0010, - Key::Enter => row1 ^= 0b0000_0001, - Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000, - Key::D => row2 ^= 0b0100_0000, - Key::V => row2 ^= 0b0010_0000, - Key::H => row2 ^= 0b0001_0000, - Key::K => row2 ^= 0b0000_1000, - Key::Apostrophe => row2 ^= 0b0000_0100, - Key::Slash => row2 ^= 0b0000_0010, - Key::A => row2 ^= 0b0000_0001, - Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000, - Key::Z => row3 ^= 0b0100_0000, - Key::F => row3 ^= 0b0010_0000, - Key::B => row3 ^= 0b0001_0000, - Key::J => row3 ^= 0b0000_1000, - Key::L => row3 ^= 0b0000_0100, - Key::Key2 => row3 ^= 0b0000_0010, - Key::Key4 => row3 ^= 0b0000_0001, - Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000, - Key::X => row4 ^= 0b0100_0000, - Key::C => row4 ^= 0b0010_0000, - Key::N => row4 ^= 0b0001_0000, - Key::M => row4 ^= 0b0000_1000, - Key::Comma => row4 ^= 0b0000_0100, - Key::Key1 => row4 ^= 0b0000_0010, - Key::Key3 => row4 ^= 0b0000_0001, - Key::LeftSuper => row5 ^= 0b1000_0000, - Key::Space => row5 ^= 0b0100_0000, - Key::RightSuper => row5 ^= 0b0010_0000, - _ => {} - }); - - match &mut self.memory.lock() { - Ok(memory) => { - memory.write(0x4400, row0); - memory.write(0x4401, row1); - memory.write(0x4402, row2); - memory.write(0x4403, row3); - memory.write(0x4404, row4); - memory.write(0x4405, row5); - } - Err(error) => println!("{error}"), - } - let now = Instant::now(); - if now - previous_draw > frame_duration { - self.draw(); - previous_draw = now; - } - sleep(Duration::from_millis(1)); + self.interrupt.send(false).unwrap(); + sleep(Duration::from_millis(16)); + self.draw(); + self.interrupt.send(true).unwrap(); } } }