From fb9f811160f4e6f000b950f5d7a9e9001a280459 Mon Sep 17 00:00:00 2001 From: Gospodin Medvedev Date: Sun, 12 Mar 2023 15:55:16 +0100 Subject: [PATCH] Inital Commit --- Cargo.lock | 522 ++++++++++++ Cargo.toml | 16 + README.md | 4 + SDL2.lib | Bin 0 -> 174154 bytes emulator/Cargo.toml | 33 + emulator/cpudiag.asm | 811 +++++++++++++++++++ emulator/cpudiag.bin | Bin 0 -> 1453 bytes emulator/cpudiag.lst | 981 +++++++++++++++++++++++ emulator/src/emulator.rs | 1352 ++++++++++++++++++++++++++++++++ emulator/src/emulator_state.rs | 81 ++ emulator/src/lib.rs | 18 + emulator/src/memory.rs | 89 +++ emulator/src/opcodes.rs | 543 +++++++++++++ emulator/src/state.rs | 186 +++++ emulator/src/test.rs | 30 + emulator/src/test2.rs | 9 + screenshot.png | Bin 0 -> 13130 bytes space-invaders.rom | Bin 0 -> 8192 bytes src/emulator2.rs | 76 ++ src/main.rs | 13 + src/sdl2.rs | 196 +++++ 21 files changed, 4960 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 SDL2.lib create mode 100644 emulator/Cargo.toml create mode 100644 emulator/cpudiag.asm create mode 100644 emulator/cpudiag.bin create mode 100644 emulator/cpudiag.lst create mode 100644 emulator/src/emulator.rs create mode 100644 emulator/src/emulator_state.rs create mode 100644 emulator/src/lib.rs create mode 100644 emulator/src/memory.rs create mode 100644 emulator/src/opcodes.rs create mode 100644 emulator/src/state.rs create mode 100644 emulator/src/test.rs create mode 100644 emulator/src/test2.rs create mode 100644 screenshot.png create mode 100644 space-invaders.rom create mode 100644 src/emulator2.rs create mode 100644 src/main.rs create mode 100644 src/sdl2.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ed71a6a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,522 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" +dependencies = [ + "cfg-if 0.1.10", + "wasm-bindgen", +] + +[[package]] +name = "emulator" +version = "0.1.0" +dependencies = [ + "console_error_panic_hook", + "lazy_static", + "log", + "once_cell", + "wasm-bindgen", + "wasm-bindgen-test", + "wasm-timer", + "wee_alloc", +] + +[[package]] +name = "futures" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" + +[[package]] +name = "futures-executor" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" + +[[package]] +name = "futures-macro" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +dependencies = [ + "autocfg", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" + +[[package]] +name = "futures-task" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" + +[[package]] +name = "futures-util" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +dependencies = [ + "autocfg", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "instant" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sdl2" +version = "0.34.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deecbc3fa9460acff5a1e563e05cb5f31bba0aa0c214bb49a43db8159176d54b" +dependencies = [ + "bitflags", + "lazy_static", + "libc", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.34.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a29aa21f175b5a41a6e26da572d5e5d1ee5660d35f9f9d0913e8a802098f74" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "version-compare", +] + +[[package]] +name = "slab" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "space-invaders" +version = "0.1.0" +dependencies = [ + "emulator", + "lazy_static", + "once_cell", + "sdl2", +] + +[[package]] +name = "syn" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version-compare" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96f1aa7971fdf61ef0f353602102dbea75a56e225ed036c1e3740564b91e6b7e" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6006f79628dfeb96a86d4db51fbf1344cd7fd8408f06fc9aa3c84913a4789688" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7c26a21 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "space-invaders" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[workspace] +members = [ "emulator"] + +[dependencies] +once_cell = "1.8.0" +lazy_static = "1.4.0" +sdl2 = "0.34.5" +emulator = { path = "emulator" } + diff --git a/README.md b/README.md new file mode 100644 index 0000000..2732753 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# space-invade.rs: Biterrfied edition +## I resized cells and changed colors. +![Screenshot](screenshot.png) +[Original source code](https://github.com/cbeust/space-invade.rs) diff --git a/SDL2.lib b/SDL2.lib new file mode 100644 index 0000000000000000000000000000000000000000..539324dc221ecc96603b40830b22c99552e81eba GIT binary patch literal 174154 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 G9RL4(dW^OJ literal 0 HcmV?d00001 diff --git a/emulator/Cargo.toml b/emulator/Cargo.toml new file mode 100644 index 0000000..34bc825 --- /dev/null +++ b/emulator/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "emulator" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +once_cell = "1.8.0" +lazy_static = "1.4.0" +log = "0.4" +wasm-bindgen = "0.2.63" +wasm-timer = "0.2.5" + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.6", optional = true } + +# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size +# compared to the default allocator's ~10K. It is slower than the default +# allocator, however. +# +# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. +wee_alloc = { version = "0.4.5", optional = true } + +[dev-dependencies] +wasm-bindgen-test = "0.3.13" + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" diff --git a/emulator/cpudiag.asm b/emulator/cpudiag.asm new file mode 100644 index 0000000..50e06a4 --- /dev/null +++ b/emulator/cpudiag.asm @@ -0,0 +1,811 @@ +;*********************************************************************** +; MICROCOSM ASSOCIATES 8080/8085 CPU DIAGNOSTIC VERSION 1.0 (C) 1980 +;*********************************************************************** +; +;DONATED TO THE "SIG/M" CP/M USER'S GROUP BY: +;KELLY SMITH, MICROCOSM ASSOCIATES +;3055 WACO AVENUE +;SIMI VALLEY, CALIFORNIA, 93065 +;(805) 527-9321 (MODEM, CP/M-NET (TM)) +;(805) 527-0518 (VERBAL) +; +; +; +; +; +; + + ORG 00100H +; +; +; + JMP CPU ;JUMP TO 8080 CPU DIAGNOSTIC +; +; +; + DB 'MICROCOSM ASSOCIATES 8080/8085 CPU DIAGNOSTIC' + DB ' VERSION 1.0 (C) 1980' +; +; +; +BDOS EQU 00005H ;BDOS ENTRY TO CP/M +WBOOT EQU 00000H ;RE-ENTRY TO CP/M WARM BOOT +; +; +; +;MESSAGE OUTPUT ROUTINE +; +MSG: PUSH D ;EXILE D REG. + XCHG ;SWAP H&L REGS. TO D&E REGS. + MVI C,9 ;LET BDOS KNOW WE WANT TO SEND A MESSAGE + CALL BDOS + POP D ;BACK FROM EXILE + RET +; +; +; +;CHARACTER OUTPUT ROUTINE +; +PCHAR: MVI C,2 + CALL BDOS + RET +; +; +; +BYTEO: PUSH PSW + CALL BYTO1 + MOV E,A + CALL PCHAR + POP PSW + CALL BYTO2 + MOV E,A + JMP PCHAR +BYTO1: RRC + RRC + RRC + RRC +BYTO2: ANI 0FH + CPI 0AH + JM BYTO3 + ADI 7 +BYTO3: ADI 30H + RET +; +; +; +;************************************************************ +; MESSAGE TABLE FOR OPERATIONAL CPU TEST +;************************************************************ +; +OKCPU: DB 0CH,0DH,0AH,' CPU IS OPERATIONAL$' +; +NGCPU: DB 0CH,0DH,0AH,' CPU HAS FAILED! ERROR EXIT=$' +; +; +; +;************************************************************ +; 8080/8085 CPU TEST/DIAGNOSTIC +;************************************************************ +; +;NOTE: (1) PROGRAM ASSUMES "CALL",AND "LXI SP" INSTRUCTIONS WORK! +; +; (2) INSTRUCTIONS NOT TESTED ARE "HLT","DI","EI","RIM","SIM", +; AND "RST 0" THRU "RST 7" +; +; +; +;TEST JUMP INSTRUCTIONS AND FLAGS +; +CPU: LXI SP,STACK ;SET THE STACK POINTER + ANI 0 ;INITIALIZE A REG. AND CLEAR ALL FLAGS + JZ J010 ;TEST "JZ" + CALL CPUER +J010: JNC J020 ;TEST "JNC" + CALL CPUER +J020: JPE J030 ;TEST "JPE" + CALL CPUER +J030: JP J040 ;TEST "JP" + CALL CPUER +J040: JNZ J050 ;TEST "JNZ" + JC J050 ;TEST "JC" + JPO J050 ;TEST "JPO" + JM J050 ;TEST "JM" + JMP J060 ;TEST "JMP" (IT'S A LITTLE LATE,BUT WHAT THE HELL! +J050: CALL CPUER +J060: ADI 6 ;A=6,C=0,P=1,S=0,Z=0 + JNZ J070 ;TEST "JNZ" + CALL CPUER +J070: JC J080 ;TEST "JC" + JPO J080 ;TEST "JPO" + JP J090 ;TEST "JP" +J080: CALL CPUER +J090: ADI 070H ;A=76H,C=0,P=0,S=0,Z=0 + JPO J100 ;TEST "JPO" + CALL CPUER +J100: JM J110 ;TEST "JM" + JZ J110 ;TEST "JZ" + JNC J120 ;TEST "JNC" +J110: CALL CPUER +J120: ADI 081H ;A=F7H,C=0,P=0,S=1,Z=0 + JM J130 ;TEST "JM" + CALL CPUER +J130: JZ J140 ;TEST "JZ" + JC J140 ;TEST "JC" + JPO J150 ;TEST "JPO" +J140: CALL CPUER +J150: ADI 0FEH ;A=F5H,C=1,P=1,S=1,Z=0 + JC J160 ;TEST "JC" + CALL CPUER +J160: JZ J170 ;TEST "JZ" + JPO J170 ;TEST "JPO" + JM AIMM ;TEST "JM" +J170: CALL CPUER +; +; +; +;TEST ACCUMULATOR IMMEDIATE INSTRUCTIONS +; +AIMM: CPI 0 ;A=F5H,C=0,Z=0 + JC CPIE ;TEST "CPI" FOR RE-SET CARRY + JZ CPIE ;TEST "CPI" FOR RE-SET ZERO + CPI 0F5H ;A=F5H,C=0,Z=1 + JC CPIE ;TEST "CPI" FOR RE-SET CARRY ("ADI") + JNZ CPIE ;TEST "CPI" FOR RE-SET ZERO + CPI 0FFH ;A=F5H,C=1,Z=0 + JZ CPIE ;TEST "CPI" FOR RE-SET ZERO + JC ACII ;TEST "CPI" FOR SET CARRY +CPIE: CALL CPUER +ACII: ACI 00AH ;A=F5H+0AH+CARRY(1)=0,C=1 + ACI 00AH ;A=0+0AH+CARRY(0)=0BH,C=0 + CPI 00BH + JZ SUII ;TEST "ACI" + CALL CPUER +SUII: SUI 00CH ;A=FFH,C=0 + SUI 00FH ;A=F0H,C=1 + CPI 0F0H + JZ SBII ;TEST "SUI" + CALL CPUER +SBII: SBI 0F1H ;A=F0H-0F1H-CARRY(0)=FFH,C=1 + SBI 00EH ;A=FFH-OEH-CARRY(1)=F0H,C=0 + CPI 0F0H + JZ ANII ;TEST "SBI" + CALL CPUER +ANII: ANI 055H ;A=F0H55H=50H,C=0,P=1,S=0,Z=0 + CPI 050H + JZ ORII ;TEST "ANI" + CALL CPUER +ORII: ORI 03AH ;A=50H3AH=7AH,C=0,P=0,S=0,Z=0 + CPI 07AH + JZ XRII ;TEST "ORI" + CALL CPUER +XRII: XRI 00FH ;A=7AH0FH=75H,C=0,P=0,S=0,Z=0 + CPI 075H + JZ C010 ;TEST "XRI" + CALL CPUER +; +; +; +;TEST CALLS AND RETURNS +; +C010: ANI 000H ;A=0,C=0,P=1,S=0,Z=1 + CC CPUER ;TEST "CC" + CPO CPUER ;TEST "CPO" + CM CPUER ;TEST "CM" + CNZ CPUER ;TEST "CNZ" + CPI 000H + JZ C020 ;A=0,C=0,P=0,S=0,Z=1 + CALL CPUER +C020: SUI 077H ;A=89H,C=1,P=0,S=1,Z=0 + CNC CPUER ;TEST "CNC" + CPE CPUER ;TEST "CPE" + CP CPUER ;TEST "CP" + CZ CPUER ;TEST "CZ" + CPI 089H + JZ C030 ;TEST FOR "CALLS" TAKING BRANCH + CALL CPUER +C030: ANI 0FFH ;SET FLAGS BACK! + CPO CPOI ;TEST "CPO" + CPI 0D9H + JZ MOVI ;TEST "CALL" SEQUENCE SUCCESS + CALL CPUER +CPOI: RPE ;TEST "RPE" + ADI 010H ;A=99H,C=0,P=0,S=1,Z=0 + CPE CPEI ;TEST "CPE" + ADI 002H ;A=D9H,C=0,P=0,S=1,Z=0 + RPO ;TEST "RPO" + CALL CPUER +CPEI: RPO ;TEST "RPO" + ADI 020H ;A=B9H,C=0,P=0,S=1,Z=0 + CM CMI ;TEST "CM" + ADI 004H ;A=D7H,C=0,P=1,S=1,Z=0 + RPE ;TEST "RPE" + CALL CPUER +CMI: RP ;TEST "RP" + ADI 080H ;A=39H,C=1,P=1,S=0,Z=0 + CP TCPI ;TEST "CP" + ADI 080H ;A=D3H,C=0,P=0,S=1,Z=0 + RM ;TEST "RM" + CALL CPUER +TCPI: RM ;TEST "RM" + ADI 040H ;A=79H,C=0,P=0,S=0,Z=0 + CNC CNCI ;TEST "CNC" + ADI 040H ;A=53H,C=0,P=1,S=0,Z=0 + RP ;TEST "RP" + CALL CPUER +CNCI: RC ;TEST "RC" + ADI 08FH ;A=08H,C=1,P=0,S=0,Z=0 + CC CCI ;TEST "CC" + SUI 002H ;A=13H,C=0,P=0,S=0,Z=0 + RNC ;TEST "RNC" + CALL CPUER +CCI: RNC ;TEST "RNC" + ADI 0F7H ;A=FFH,C=0,P=1,S=1,Z=0 + CNZ CNZI ;TEST "CNZ" + ADI 0FEH ;A=15H,C=1,P=0,S=0,Z=0 + RC ;TEST "RC" + CALL CPUER +CNZI: RZ ;TEST "RZ" + ADI 001H ;A=00H,C=1,P=1,S=0,Z=1 + CZ CZI ;TEST "CZ" + ADI 0D0H ;A=17H,C=1,P=1,S=0,Z=0 + RNZ ;TEST "RNZ" + CALL CPUER +CZI: RNZ ;TEST "RNZ" + ADI 047H ;A=47H,C=0,P=1,S=0,Z=0 + CPI 047H ;A=47H,C=0,P=1,S=0,Z=1 + RZ ;TEST "RZ" + CALL CPUER +; +; +; +;TEST "MOV","INR",AND "DCR" INSTRUCTIONS +; +MOVI: MVI A,077H + INR A + MOV B,A + INR B + MOV C,B + DCR C + MOV D,C + MOV E,D + MOV H,E + MOV L,H + MOV A,L ;TEST "MOV" A,L,H,E,D,C,B,A + DCR A + MOV C,A + MOV E,C + MOV L,E + MOV B,L + MOV D,B + MOV H,D + MOV A,H ;TEST "MOV" A,H,D,B,L,E,C,A + MOV D,A + INR D + MOV L,D + MOV C,L + INR C + MOV H,C + MOV B,H + DCR B + MOV E,B + MOV A,E ;TEST "MOV" A,E,B,H,C,L,D,A + MOV E,A + INR E + MOV B,E + MOV H,B + INR H + MOV C,H + MOV L,C + MOV D,L + DCR D + MOV A,D ;TEST "MOV" A,D,L,C,H,B,E,A + MOV H,A + DCR H + MOV D,H + MOV B,D + MOV L,B + INR L + MOV E,L + DCR E + MOV C,E + MOV A,C ;TEST "MOV" A,C,E,L,B,D,H,A + MOV L,A + DCR L + MOV H,L + MOV E,H + MOV D,E + MOV C,D + MOV B,C + MOV A,B + CPI 077H + CNZ CPUER ;TEST "MOV" A,B,C,D,E,H,L,A +; +; +; +;TEST ARITHMETIC AND LOGIC INSTRUCTIONS +; + XRA A + MVI B,001H + MVI C,003H + MVI D,007H + MVI E,00FH + MVI H,01FH + MVI L,03FH + ADD B + ADD C + ADD D + ADD E + ADD H + ADD L + ADD A + CPI 0F0H + CNZ CPUER ;TEST "ADD" B,C,D,E,H,L,A + SUB B + SUB C + SUB D + SUB E + SUB H + SUB L + CPI 078H + CNZ CPUER ;TEST "SUB" B,C,D,E,H,L + SUB A + CNZ CPUER ;TEST "SUB" A + MVI A,080H + ADD A + MVI B,001H + MVI C,002H + MVI D,003H + MVI E,004H + MVI H,005H + MVI L,006H + ADC B + MVI B,080H + ADD B + ADD B + ADC C + ADD B + ADD B + ADC D + ADD B + ADD B + ADC E + ADD B + ADD B + ADC H + ADD B + ADD B + ADC L + ADD B + ADD B + ADC A + CPI 037H + CNZ CPUER ;TEST "ADC" B,C,D,E,H,L,A + MVI A,080H + ADD A + MVI B,001H + SBB B + MVI B,0FFH + ADD B + SBB C + ADD B + SBB D + ADD B + SBB E + ADD B + SBB H + ADD B + SBB L + CPI 0E0H + CNZ CPUER ;TEST "SBB" B,C,D,E,H,L + MVI A,080H + ADD A + SBB A + CPI 0FFH + CNZ CPUER ;TEST "SBB" A + MVI A,0FFH + MVI B,0FEH + MVI C,0FCH + MVI D,0EFH + MVI E,07FH + MVI H,0F4H + MVI L,0BFH + ANA A + ANA C + ANA D + ANA E + ANA H + ANA L + ANA A + CPI 024H + CNZ CPUER ;TEST "ANA" B,C,D,E,H,L,A + XRA A + MVI B,001H + MVI C,002H + MVI D,004H + MVI E,008H + MVI H,010H + MVI L,020H + ORA B + ORA C + ORA D + ORA E + ORA H + ORA L + ORA A + CPI 03FH + CNZ CPUER ;TEST "ORA" B,C,D,E,H,L,A + MVI A,000H + MVI H,08FH + MVI L,04FH + XRA B + XRA C + XRA D + XRA E + XRA H + XRA L + CPI 0CFH + CNZ CPUER ;TEST "XRA" B,C,D,E,H,L + XRA A + CNZ CPUER ;TEST "XRA" A + MVI B,044H + MVI C,045H + MVI D,046H + MVI E,047H + MVI H,(TEMP0 / 0FFH) ;HIGH BYTE OF TEST MEMORY LOCATION + MVI L,(TEMP0 AND 0FFH) ;LOW BYTE OF TEST MEMORY LOCATION + MOV M,B + MVI B,000H + MOV B,M + MVI A,044H + CMP B + CNZ CPUER ;TEST "MOV" M,B AND B,M + MOV M,D + MVI D,000H + MOV D,M + MVI A,046H + CMP D + CNZ CPUER ;TEST "MOV" M,D AND D,M + MOV M,E + MVI E,000H + MOV E,M + MVI A,047H + CMP E + CNZ CPUER ;TEST "MOV" M,E AND E,M + MOV M,H + MVI H,(TEMP0 / 0FFH) + MVI L,(TEMP0 AND 0FFH) + MOV H,M + MVI A,(TEMP0 / 0FFH) + CMP H + CNZ CPUER ;TEST "MOV" M,H AND H,M + MOV M,L + MVI H,(TEMP0 / 0FFH) + MVI L,(TEMP0 AND 0FFH) + MOV L,M + MVI A,(TEMP0 AND 0FFH) + CMP L + CNZ CPUER ;TEST "MOV" M,L AND L,M + MVI H,(TEMP0 / 0FFH) + MVI L,(TEMP0 AND 0FFH) + MVI A,032H + MOV M,A + CMP M + CNZ CPUER ;TEST "MOV" M,A + ADD M + CPI 064H + CNZ CPUER ;TEST "ADD" M + XRA A + MOV A,M + CPI 032H + CNZ CPUER ;TEST "MOV" A,M + MVI H,(TEMP0 / 0FFH) + MVI L,(TEMP0 AND 0FFH) + MOV A,M + SUB M + CNZ CPUER ;TEST "SUB" M + MVI A,080H + ADD A + ADC M + CPI 033H + CNZ CPUER ;TEST "ADC" M + MVI A,080H + ADD A + SBB M + CPI 0CDH + CNZ CPUER ;TEST "SBB" M + ANA M + CNZ CPUER ;TEST "ANA" M + MVI A,025H + ORA M + CPI 037H + CNZ CPUER ;TEST "ORA" M + XRA M + CPI 005H + CNZ CPUER ;TEST "XRA" M + MVI M,055H + INR M + DCR M + ADD M + CPI 05AH + CNZ CPUER ;TEST "INR","DCR",AND "MVI" M + LXI B,12FFH + LXI D,12FFH + LXI H,12FFH + INX B + INX D + INX H + MVI A,013H + CMP B + CNZ CPUER ;TEST "LXI" AND "INX" B + CMP D + CNZ CPUER ;TEST "LXI" AND "INX" D + CMP H + CNZ CPUER ;TEST "LXI" AND "INX" H + MVI A,000H + CMP C + CNZ CPUER ;TEST "LXI" AND "INX" B + CMP E + CNZ CPUER ;TEST "LXI" AND "INX" D + CMP L + CNZ CPUER ;TEST "LXI" AND "INX" H + DCX B + DCX D + DCX H + MVI A,012H + CMP B + CNZ CPUER ;TEST "DCX" B + CMP D + CNZ CPUER ;TEST "DCX" D + CMP H + CNZ CPUER ;TEST "DCX" H + MVI A,0FFH + CMP C + CNZ CPUER ;TEST "DCX" B + CMP E + CNZ CPUER ;TEST "DCX" D + CMP L + CNZ CPUER ;TEST "DCX" H + STA TEMP0 + XRA A + LDA TEMP0 + CPI 0FFH + CNZ CPUER ;TEST "LDA" AND "STA" + LHLD TEMPP + SHLD TEMP0 + LDA TEMPP + MOV B,A + LDA TEMP0 + CMP B + CNZ CPUER ;TEST "LHLD" AND "SHLD" + LDA TEMPP+1 + MOV B,A + LDA TEMP0+1 + CMP B + CNZ CPUER ;TEST "LHLD" AND "SHLD" + MVI A,0AAH + STA TEMP0 + MOV B,H + MOV C,L + XRA A + LDAX B + CPI 0AAH + CNZ CPUER ;TEST "LDAX" B + INR A + STAX B + LDA TEMP0 + CPI 0ABH + CNZ CPUER ;TEST "STAX" B + MVI A,077H + STA TEMP0 + LHLD TEMPP + LXI D,00000H + XCHG + XRA A + LDAX D + CPI 077H + CNZ CPUER ;TEST "LDAX" D AND "XCHG" + XRA A + ADD H + ADD L + CNZ CPUER ;TEST "XCHG" + MVI A,0CCH + STAX D + LDA TEMP0 + CPI 0CCH + STAX D + LDA TEMP0 + CPI 0CCH + CNZ CPUER ;TEST "STAX" D + LXI H,07777H + DAD H + MVI A,0EEH + CMP H + CNZ CPUER ;TEST "DAD" H + CMP L + CNZ CPUER ;TEST "DAD" H + LXI H,05555H + LXI B,0FFFFH + DAD B + MVI A,055H + CNC CPUER ;TEST "DAD" B + CMP H + CNZ CPUER ;TEST "DAD" B + MVI A,054H + CMP L + CNZ CPUER ;TEST "DAD" B + LXI H,0AAAAH + LXI D,03333H + DAD D + MVI A,0DDH + CMP H + CNZ CPUER ;TEST "DAD" D + CMP L + CNZ CPUER ;TEST "DAD" B + STC + CNC CPUER ;TEST "STC" + CMC + CC CPUER ;TEST "CMC + MVI A,0AAH + CMA + CPI 055H + CNZ CPUER ;TEST "CMA" + ORA A ;RE-SET AUXILIARY CARRY + DAA + CPI 055H + CNZ CPUER ;TEST "DAA" + MVI A,088H + ADD A + DAA + CPI 076H + CNZ CPUER ;TEST "DAA" + XRA A + MVI A,0AAH + DAA + CNC CPUER ;TEST "DAA" + CPI 010H + CNZ CPUER ;TEST "DAA" + XRA A + MVI A,09AH + DAA + CNC CPUER ;TEST "DAA" + CNZ CPUER ;TEST "DAA" + STC + MVI A,042H + RLC + CC CPUER ;TEST "RLC" FOR RE-SET CARRY + RLC + CNC CPUER ;TEST "RLC" FOR SET CARRY + CPI 009H + CNZ CPUER ;TEST "RLC" FOR ROTATION + RRC + CNC CPUER ;TEST "RRC" FOR SET CARRY + RRC + CPI 042H + CNZ CPUER ;TEST "RRC" FOR ROTATION + RAL + RAL + CNC CPUER ;TEST "RAL" FOR SET CARRY + CPI 008H + CNZ CPUER ;TEST "RAL" FOR ROTATION + RAR + RAR + CC CPUER ;TEST "RAR" FOR RE-SET CARRY + CPI 002H + CNZ CPUER ;TEST "RAR" FOR ROTATION + LXI B,01234H + LXI D,0AAAAH + LXI H,05555H + XRA A + PUSH B + PUSH D + PUSH H + PUSH PSW + LXI B,00000H + LXI D,00000H + LXI H,00000H + MVI A,0C0H + ADI 0F0H + POP PSW + POP H + POP D + POP B + CC CPUER ;TEST "PUSH PSW" AND "POP PSW" + CNZ CPUER ;TEST "PUSH PSW" AND "POP PSW" + CPO CPUER ;TEST "PUSH PSW" AND "POP PSW" + CM CPUER ;TEST "PUSH PSW" AND "POP PSW" + MVI A,012H + CMP B + CNZ CPUER ;TEST "PUSH B" AND "POP B" + MVI A,034H + CMP C + CNZ CPUER ;TEST "PUSH B" AND "POP B" + MVI A,0AAH + CMP D + CNZ CPUER ;TEST "PUSH D" AND "POP D" + CMP E + CNZ CPUER ;TEST "PUSH D" AND "POP D" + MVI A,055H + CMP H + CNZ CPUER ;TEST "PUSH H" AND "POP H" + CMP L + CNZ CPUER ;TEST "PUSH H" AND "POP H" + LXI H,00000H + DAD SP + SHLD SAVSTK ;SAVE THE "OLD" STACK-POINTER! + LXI SP,TEMP4 + DCX SP + DCX SP + INX SP + DCX SP + MVI A,055H + STA TEMP2 + CMA + STA TEMP3 + POP B + CMP B + CNZ CPUER ;TEST "LXI","DAD","INX",AND "DCX" SP + CMA + CMP C + CNZ CPUER ;TEST "LXI","DAD","INX", AND "DCX" SP + LXI H,TEMP4 + SPHL + LXI H,07733H + DCX SP + DCX SP + XTHL + LDA TEMP3 + CPI 077H + CNZ CPUER ;TEST "SPHL" AND "XTHL" + LDA TEMP2 + CPI 033H + CNZ CPUER ;TEST "SPHL" AND "XTHL" + MVI A,055H + CMP L + CNZ CPUER ;TEST "SPHL" AND "XTHL" + CMA + CMP H + CNZ CPUER ;TEST "SPHL" AND "XTHL" + LHLD SAVSTK ;RESTORE THE "OLD" STACK-POINTER + SPHL + LXI H,CPUOK + PCHL ;TEST "PCHL" +; +; +; +CPUER: LXI H,NGCPU ;OUTPUT "CPU HAS FAILED ERROR EXIT=" TO CONSOLE + CALL MSG + XTHL + MOV A,H + CALL BYTEO ;SHOW ERROR EXIT ADDRESS HIGH BYTE + MOV A,L + CALL BYTEO ;SHOW ERROR EXIT ADDRESS LOW BYTE + JMP WBOOT ;EXIT TO CP/M WARM BOOT +; +; +; +CPUOK: LXI H,OKCPU ;OUTPUT "CPU IS OPERATIONAL" TO CONSOLE + CALL MSG + JMP WBOOT ;EXIT TO CP/M WARM BOOT +; +; +; +TEMPP: DW TEMP0 ;POINTER USED TO TEST "LHLD","SHLD", + ; AND "LDAX" INSTRUCTIONS +; +TEMP0: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +TEMP1: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +TEMP2 DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +TEMP3: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +TEMP4: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +SAVSTK: DS 2 ;TEMPORARY STACK-POINTER STORAGE LOCATION +; +; +; +STACK EQU TEMPP+256 ;DE-BUG STACK POINTER STORAGE AREA +; +; +; + END +; +; +; diff --git a/emulator/cpudiag.bin b/emulator/cpudiag.bin new file mode 100644 index 0000000000000000000000000000000000000000..447f8836516030883fb9c5b0db1b8aafaedcdb0e GIT binary patch literal 1453 zcmY+D3s6*57{~9u3kO(n7ZzKmhs#~VC8?IDfr}noc9(SvSlox1lhMOUt0_xu&BU&z z;$}tfhh|NM3oO}ImiDkhusg8B8afKJV-BsvSU9veV8$@#_MMB&)Xv=V{m%dUfA@ap zyL&rY*(!&{<+M26RlM2lc3K=}kIl`OJDk;g zzA=x#$ughMUs#fNW>}RHNn}oTsW=E-DZKRqVjjUm7JE~bV{D#y-Iq@VbCW(1n zsWX-GT^(-TSz~jVJ#asBW!AL3)a>Rf%#KQ%RnOa8E~ksPt#Ei2XXPJE8e+nS*@z#V zI!f0++v$3#ldkQ3Y_yM!^|2FuY)6zTB5IG(7#(I~!|bIISz8yoOyh*ah9&kC$5G$5 z2_=o;3@)0%#k8_UqB>d2=3?30L=I)bM9aBwIY-7JwnLO)6SdLQ3FQeTnGs$_xi_^} zP2%BIl+TTxQvt7~Jme)c;q{a*o5<#H9iuv&>mP8uX({KRQky72Z$PUf*dy zy68t^e$)-BKYU2`N`q}2`6WC@0hSA5+Qn{8J~fidel~P|CT#vJyMl=pEYsCv$Z!^?#-%P>%C_4!}C1lYi?OJ=Z!|F9^aOL3mLRUJ`@`Qao*Z2T8)a!d_vY@Sd=r3{1%%5Q&0>h-y;%m+m>knDNJ^ z4?p_2@jw&Ff(&hi(VykfRW@yf z&Tc@)z&gZKVC&bQKBZ-rCur4b<`wMn{1+QhpoOv@N~Y%SuuT_QKy; zZrUTO!C-n};kEeJXQZEc_mKmajQzkdO1>1HPY literal 0 HcmV?d00001 diff --git a/emulator/cpudiag.lst b/emulator/cpudiag.lst new file mode 100644 index 0000000..7aedf14 --- /dev/null +++ b/emulator/cpudiag.lst @@ -0,0 +1,981 @@ +;*********************************************************************** + ; MICROCOSM ASSOCIATES 8080/8085 CPU DIAGNOSTIC VERSION 1.0 (C) 1980 + ;*********************************************************************** + ; + ;DONATED TO THE "SIG/M" CP/M USER'S GROUP BY: + ;KELLY SMITH, MICROCOSM ASSOCIATES + ;3055 WACO AVENUE + ;SIMI VALLEY, CALIFORNIA, 93065 + ;(805) 527-9321 (MODEM, CP/M-NET (TM)) + ;(805) 527-0518 (VERBAL) + ; + ; + ; + ; + ; + ; + + ORG 00100H + ; + ; + ; +0100 C3 AB 01 JMP CPU ;JUMP TO 8080 CPU DIAGNOSTIC + ; + ; + ; +0103 4D 49 43 52 DB 'MICROCOSM ASSOCIATES 8080/8085 CPU DIAGNOSTIC' + . . . +012F 43 +0130 20 56 45 52 DB ' VERSION 1.0 (C) 1980' + . . . +0144 30 + ; + ; + ; + BDOS EQU 00005H ;BDOS ENTRY TO CP/M + WBOOT EQU 00000H ;RE-ENTRY TO CP/M WARM BOOT + ; + ; + ; + ;MESSAGE OUTPUT ROUTINE + ; +0145 D5 MSG: PUSH D ;EXILE D REG. +0146 EB XCHG ;SWAP H&L REGS. TO D&E REGS. +0147 0E 09 MVI C,9 ;LET BDOS KNOW WE WANT TO SEND A MESSAGE +0149 CD 05 00 CALL BDOS +014C D1 POP D ;BACK FROM EXILE +014D C9 RET + ; + ; + ; + ;CHARACTER OUTPUT ROUTINE + ; +014E 0E 02 PCHAR: MVI C,2 +0150 CD 05 00 CALL BDOS +0153 C9 RET + ; + ; + ; +0154 F5 BYTEO: PUSH PSW +0155 CD 64 01 CALL BYTO1 +0158 5F MOV E,A +0159 CD 4E 01 CALL PCHAR +015C F1 POP PSW +015D CD 68 01 CALL BYTO2 +0160 5F MOV E,A +0161 C3 4E 01 JMP PCHAR +0164 0F BYTO1: RRC +0165 0F RRC +0166 0F RRC +0167 0F RRC +0168 E6 0F BYTO2: ANI 0FH +016A FE 0A CPI 0AH +016C FA 71 01 JM BYTO3 +016F C6 07 ADI 7 +0171 C6 30 BYTO3: ADI 30H +0173 C9 RET + ; + ; + ; + ;************************************************************ + ; MESSAGE TABLE FOR OPERATIONAL CPU TEST + ;************************************************************ + ; +0174 0C 0D 0A 20 OKCPU: DB 0CH,0DH,0AH,' CPU IS OPERATIONAL$' + . . . +0188 41 4C 24 + ; +018B 0C 0D 0A 20 NGCPU: DB 0CH,0DH,0AH,' CPU HAS FAILED! ERROR EXIT=$' + . . . +01A7 49 54 3D 24 + ; + ; + ; + ;************************************************************ + ; 8080/8085 CPU TEST/DIAGNOSTIC + ;************************************************************ + ; + ;NOTE: (1) PROGRAM ASSUMES "CALL",AND "LXI SP" INSTRUCTIONS WORK! + ; + ; (2) INSTRUCTIONS NOT TESTED ARE "HLT","DI","EI","RIM","SIM", + ; AND "RST 0" THRU "RST 7" + ; + ; + ; + ;TEST JUMP INSTRUCTIONS AND FLAGS + ; +01AB 31 AD 06 CPU: LXI SP,STACK ;SET THE STACK POINTER +01AE E6 00 ANI 0 ;INITIALIZE A REG. AND CLEAR ALL FLAGS +01B0 CA B6 01 JZ J010 ;TEST "JZ" +01B3 CD 89 06 CALL CPUER +01B6 D2 BC 01 J010: JNC J020 ;TEST "JNC" +01B9 CD 89 06 CALL CPUER +01BC EA C2 01 J020: JPE J030 ;TEST "JPE" +01BF CD 89 06 CALL CPUER +01C2 F2 C8 01 J030: JP J040 ;TEST "JP" +01C5 CD 89 06 CALL CPUER +01C8 C2 D7 01 J040: JNZ J050 ;TEST "JNZ" +01CB DA D7 01 JC J050 ;TEST "JC" +01CE E2 D7 01 JPO J050 ;TEST "JPO" +01D1 FA D7 01 JM J050 ;TEST "JM" +01D4 C3 DA 01 JMP J060 ;TEST "JMP" (IT'S A LITTLE LATE,BUT WHAT THE HELL! +01D7 CD 89 06 J050: CALL CPUER +01DA C6 06 J060: ADI 6 ;A=6,C=0,P=1,S=0,Z=0 +01DC C2 E2 01 JNZ J070 ;TEST "JNZ" +01DF CD 89 06 CALL CPUER +01E2 DA EB 01 J070: JC J080 ;TEST "JC" +01E5 E2 EB 01 JPO J080 ;TEST "JPO" +01E8 F2 EE 01 JP J090 ;TEST "JP" +01EB CD 89 06 J080: CALL CPUER +01EE C6 70 J090: ADI 070H ;A=76H,C=0,P=0,S=0,Z=0 +01F0 E2 F6 01 JPO J100 ;TEST "JPO" +01F3 CD 89 06 CALL CPUER +01F6 FA FF 01 J100: JM J110 ;TEST "JM" +01F9 CA FF 01 JZ J110 ;TEST "JZ" +01FC D2 02 02 JNC J120 ;TEST "JNC" +01FF CD 89 06 J110: CALL CPUER +0202 C6 81 J120: ADI 081H ;A=F7H,C=0,P=0,S=1,Z=0 +0204 FA 0A 02 JM J130 ;TEST "JM" +0207 CD 89 06 CALL CPUER +020A CA 13 02 J130: JZ J140 ;TEST "JZ" +020D DA 13 02 JC J140 ;TEST "JC" +0210 E2 16 02 JPO J150 ;TEST "JPO" +0213 CD 89 06 J140: CALL CPUER +0216 C6 FE J150: ADI 0FEH ;A=F5H,C=1,P=1,S=1,Z=0 +0218 DA 1E 02 JC J160 ;TEST "JC" +021B CD 89 06 CALL CPUER +021E CA 27 02 J160: JZ J170 ;TEST "JZ" +0221 E2 27 02 JPO J170 ;TEST "JPO" +0224 FA 2A 02 JM AIMM ;TEST "JM" +0227 CD 89 06 J170: CALL CPUER + ; + ; + ; + ;TEST ACCUMULATOR IMMEDIATE INSTRUCTIONS + ; +022A FE 00 AIMM: CPI 0 ;A=F5H,C=0,Z=0 +022C DA 42 02 JC CPIE ;TEST "CPI" FOR RE-SET CARRY +022F CA 42 02 JZ CPIE ;TEST "CPI" FOR RE-SET ZERO +0232 FE F5 CPI 0F5H ;A=F5H,C=0,Z=1 +0234 DA 42 02 JC CPIE ;TEST "CPI" FOR RE-SET CARRY ("ADI") +0237 C2 42 02 JNZ CPIE ;TEST "CPI" FOR RE-SET ZERO +023A FE FF CPI 0FFH ;A=F5H,C=1,Z=0 +023C CA 42 02 JZ CPIE ;TEST "CPI" FOR RE-SET ZERO +023F DA 45 02 JC ACII ;TEST "CPI" FOR SET CARRY +0242 CD 89 06 CPIE: CALL CPUER +0245 CE 0A ACII: ACI 00AH ;A=F5H+0AH+CARRY(1)=0,C=1 +0247 CE 0A ACI 00AH ;A=0+0AH+CARRY(0)=0BH,C=0 +0249 FE 0B CPI 00BH +024B CA 51 02 JZ SUII ;TEST "ACI" +024E CD 89 06 CALL CPUER +0251 D6 0C SUII: SUI 00CH ;A=FFH,C=0 +0253 D6 0F SUI 00FH ;A=F0H,C=1 +0255 FE F0 CPI 0F0H +0257 CA 5D 02 JZ SBII ;TEST "SUI" +025A CD 89 06 CALL CPUER +025D DE F1 SBII: SBI 0F1H ;A=F0H-0F1H-CARRY(0)=FFH,C=1 +025F DE 0E SBI 00EH ;A=FFH-OEH-CARRY(1)=F0H,C=0 +0261 FE F0 CPI 0F0H +0263 CA 69 02 JZ ANII ;TEST "SBI" +0266 CD 89 06 CALL CPUER +0269 E6 55 ANII: ANI 055H ;A=F0H55H=50H,C=0,P=1,S=0,Z=0 +026B FE 50 CPI 050H +026D CA 73 02 JZ ORII ;TEST "ANI" +0270 CD 89 06 CALL CPUER +0273 F6 3A ORII: ORI 03AH ;A=50H3AH=7AH,C=0,P=0,S=0,Z=0 +0275 FE 7A CPI 07AH +0277 CA 7D 02 JZ XRII ;TEST "ORI" +027A CD 89 06 CALL CPUER +027D EE 0F XRII: XRI 00FH ;A=7AH0FH=75H,C=0,P=0,S=0,Z=0 +027F FE 75 CPI 075H +0281 CA 87 02 JZ C010 ;TEST "XRI" +0284 CD 89 06 CALL CPUER + ; + ; + ; + ;TEST CALLS AND RETURNS + ; +0287 E6 00 C010: ANI 000H ;A=0,C=0,P=1,S=0,Z=1 +0289 DC 89 06 CC CPUER ;TEST "CC" +028C E4 89 06 CPO CPUER ;TEST "CPO" +028F FC 89 06 CM CPUER ;TEST "CM" +0292 C4 89 06 CNZ CPUER ;TEST "CNZ" +0295 FE 00 CPI 000H +0297 CA 9D 02 JZ C020 ;A=0,C=0,P=0,S=0,Z=1 +029A CD 89 06 CALL CPUER +029D D6 77 C020: SUI 077H ;A=89H,C=1,P=0,S=1,Z=0 +029F D4 89 06 CNC CPUER ;TEST "CNC" +02A2 EC 89 06 CPE CPUER ;TEST "CPE" +02A5 F4 89 06 CP CPUER ;TEST "CP" +02A8 CC 89 06 CZ CPUER ;TEST "CZ" +02AB FE 89 CPI 089H +02AD CA B3 02 JZ C030 ;TEST FOR "CALLS" TAKING BRANCH +02B0 CD 89 06 CALL CPUER +02B3 E6 FF C030: ANI 0FFH ;SET FLAGS BACK! +02B5 E4 C0 02 CPO CPOI ;TEST "CPO" +02B8 FE D9 CPI 0D9H +02BA CA 1D 03 JZ MOVI ;TEST "CALL" SEQUENCE SUCCESS +02BD CD 89 06 CALL CPUER +02C0 E8 CPOI: RPE ;TEST "RPE" +02C1 C6 10 ADI 010H ;A=99H,C=0,P=0,S=1,Z=0 +02C3 EC CC 02 CPE CPEI ;TEST "CPE" +02C6 C6 02 ADI 002H ;A=D9H,C=0,P=0,S=1,Z=0 +02C8 E0 RPO ;TEST "RPO" +02C9 CD 89 06 CALL CPUER +02CC E0 CPEI: RPO ;TEST "RPO" +02CD C6 20 ADI 020H ;A=B9H,C=0,P=0,S=1,Z=0 +02CF FC D8 02 CM CMI ;TEST "CM" +02D2 C6 04 ADI 004H ;A=D7H,C=0,P=1,S=1,Z=0 +02D4 E8 RPE ;TEST "RPE" +02D5 CD 89 06 CALL CPUER +02D8 F0 CMI: RP ;TEST "RP" +02D9 C6 80 ADI 080H ;A=39H,C=1,P=1,S=0,Z=0 +02DB F4 E4 02 CP TCPI ;TEST "CP" +02DE C6 80 ADI 080H ;A=D3H,C=0,P=0,S=1,Z=0 +02E0 F8 RM ;TEST "RM" +02E1 CD 89 06 CALL CPUER +02E4 F8 TCPI: RM ;TEST "RM" +02E5 C6 40 ADI 040H ;A=79H,C=0,P=0,S=0,Z=0 +02E7 D4 F0 02 CNC CNCI ;TEST "CNC" +02EA C6 40 ADI 040H ;A=53H,C=0,P=1,S=0,Z=0 +02EC F0 RP ;TEST "RP" +02ED CD 89 06 CALL CPUER +02F0 D8 CNCI: RC ;TEST "RC" +02F1 C6 8F ADI 08FH ;A=08H,C=1,P=0,S=0,Z=0 +02F3 DC FC 02 CC CCI ;TEST "CC" +02F6 D6 02 SUI 002H ;A=13H,C=0,P=0,S=0,Z=0 +02F8 D0 RNC ;TEST "RNC" +02F9 CD 89 06 CALL CPUER +02FC D0 CCI: RNC ;TEST "RNC" +02FD C6 F7 ADI 0F7H ;A=FFH,C=0,P=1,S=1,Z=0 +02FF C4 08 03 CNZ CNZI ;TEST "CNZ" +0302 C6 FE ADI 0FEH ;A=15H,C=1,P=0,S=0,Z=0 +0304 D8 RC ;TEST "RC" +0305 CD 89 06 CALL CPUER +0308 C8 CNZI: RZ ;TEST "RZ" +0309 C6 01 ADI 001H ;A=00H,C=1,P=1,S=0,Z=1 +030B CC 14 03 CZ CZI ;TEST "CZ" +030E C6 D0 ADI 0D0H ;A=17H,C=1,P=1,S=0,Z=0 +0310 C0 RNZ ;TEST "RNZ" +0311 CD 89 06 CALL CPUER +0314 C0 CZI: RNZ ;TEST "RNZ" +0315 C6 47 ADI 047H ;A=47H,C=0,P=1,S=0,Z=0 +0317 FE 47 CPI 047H ;A=47H,C=0,P=1,S=0,Z=1 +0319 C8 RZ ;TEST "RZ" +031A CD 89 06 CALL CPUER + ; + ; + ; + ;TEST "MOV","INR",AND "DCR" INSTRUCTIONS + ; +031D 3E 77 MOVI: MVI A,077H +031F 3C INR A +0320 47 MOV B,A +0321 04 INR B +0322 48 MOV C,B +0323 0D DCR C +0324 51 MOV D,C +0325 5A MOV E,D +0326 63 MOV H,E +0327 6C MOV L,H +0328 7D MOV A,L ;TEST "MOV" A,L,H,E,D,C,B,A +0329 3D DCR A +032A 4F MOV C,A +032B 59 MOV E,C +032C 6B MOV L,E +032D 45 MOV B,L +032E 50 MOV D,B +032F 62 MOV H,D +0330 7C MOV A,H ;TEST "MOV" A,H,D,B,L,E,C,A +0331 57 MOV D,A +0332 14 INR D +0333 6A MOV L,D +0334 4D MOV C,L +0335 0C INR C +0336 61 MOV H,C +0337 44 MOV B,H +0338 05 DCR B +0339 58 MOV E,B +033A 7B MOV A,E ;TEST "MOV" A,E,B,H,C,L,D,A +033B 5F MOV E,A +033C 1C INR E +033D 43 MOV B,E +033E 60 MOV H,B +033F 24 INR H +0340 4C MOV C,H +0341 69 MOV L,C +0342 55 MOV D,L +0343 15 DCR D +0344 7A MOV A,D ;TEST "MOV" A,D,L,C,H,B,E,A +0345 67 MOV H,A +0346 25 DCR H +0347 54 MOV D,H +0348 42 MOV B,D +0349 68 MOV L,B +034A 2C INR L +034B 5D MOV E,L +034C 1D DCR E +034D 4B MOV C,E +034E 79 MOV A,C ;TEST "MOV" A,C,E,L,B,D,H,A +034F 6F MOV L,A +0350 2D DCR L +0351 65 MOV H,L +0352 5C MOV E,H +0353 53 MOV D,E +0354 4A MOV C,D +0355 41 MOV B,C +0356 78 MOV A,B +0357 FE 77 CPI 077H +0359 C4 89 06 CNZ CPUER ;TEST "MOV" A,B,C,D,E,H,L,A + ; + ; + ; + ;TEST ARITHMETIC AND LOGIC INSTRUCTIONS + ; +035C AF XRA A +035D 06 01 MVI B,001H +035F 0E 03 MVI C,003H +0361 16 07 MVI D,007H +0363 1E 0F MVI E,00FH +0365 26 1F MVI H,01FH +0367 2E 3F MVI L,03FH +0369 80 ADD B +036A 81 ADD C +036B 82 ADD D +036C 83 ADD E +036D 84 ADD H +036E 85 ADD L +036F 87 ADD A +0370 FE F0 CPI 0F0H +0372 C4 89 06 CNZ CPUER ;TEST "ADD" B,C,D,E,H,L,A +0375 90 SUB B +0376 91 SUB C +0377 92 SUB D +0378 93 SUB E +0379 94 SUB H +037A 95 SUB L +037B FE 78 CPI 078H +037D C4 89 06 CNZ CPUER ;TEST "SUB" B,C,D,E,H,L +0380 97 SUB A +0381 C4 89 06 CNZ CPUER ;TEST "SUB" A +0384 3E 80 MVI A,080H +0386 87 ADD A +0387 06 01 MVI B,001H +0389 0E 02 MVI C,002H +038B 16 03 MVI D,003H +038D 1E 04 MVI E,004H +038F 26 05 MVI H,005H +0391 2E 06 MVI L,006H +0393 88 ADC B +0394 06 80 MVI B,080H +0396 80 ADD B +0397 80 ADD B +0398 89 ADC C +0399 80 ADD B +039A 80 ADD B +039B 8A ADC D +039C 80 ADD B +039D 80 ADD B +039E 8B ADC E +039F 80 ADD B +03A0 80 ADD B +03A1 8C ADC H +03A2 80 ADD B +03A3 80 ADD B +03A4 8D ADC L +03A5 80 ADD B +03A6 80 ADD B +03A7 8F ADC A +03A8 FE 37 CPI 037H +03AA C4 89 06 CNZ CPUER ;TEST "ADC" B,C,D,E,H,L,A +03AD 3E 80 MVI A,080H +03AF 87 ADD A +03B0 06 01 MVI B,001H +03B2 98 SBB B +03B3 06 FF MVI B,0FFH +03B5 80 ADD B +03B6 99 SBB C +03B7 80 ADD B +03B8 9A SBB D +03B9 80 ADD B +03BA 9B SBB E +03BB 80 ADD B +03BC 9C SBB H +03BD 80 ADD B +03BE 9D SBB L +03BF FE E0 CPI 0E0H +03C1 C4 89 06 CNZ CPUER ;TEST "SBB" B,C,D,E,H,L +03C4 3E 80 MVI A,080H +03C6 87 ADD A +03C7 9F SBB A +03C8 FE FF CPI 0FFH +03CA C4 89 06 CNZ CPUER ;TEST "SBB" A +03CD 3E FF MVI A,0FFH +03CF 06 FE MVI B,0FEH +03D1 0E FC MVI C,0FCH +03D3 16 EF MVI D,0EFH +03D5 1E 7F MVI E,07FH +03D7 26 F4 MVI H,0F4H +03D9 2E BF MVI L,0BFH +03DB A7 ANA A +03DC A1 ANA C +03DD A2 ANA D +03DE A3 ANA E +03DF A4 ANA H +03E0 A5 ANA L +03E1 A7 ANA A +03E2 FE 24 CPI 024H +03E4 C4 89 06 CNZ CPUER ;TEST "ANA" B,C,D,E,H,L,A +03E7 AF XRA A +03E8 06 01 MVI B,001H +03EA 0E 02 MVI C,002H +03EC 16 04 MVI D,004H +03EE 1E 08 MVI E,008H +03F0 26 10 MVI H,010H +03F2 2E 20 MVI L,020H +03F4 B0 ORA B +03F5 B1 ORA C +03F6 B2 ORA D +03F7 B3 ORA E +03F8 B4 ORA H +03F9 B5 ORA L +03FA B7 ORA A +03FB FE 3F CPI 03FH +03FD C4 89 06 CNZ CPUER ;TEST "ORA" B,C,D,E,H,L,A +0400 3E 00 MVI A,000H +0402 26 8F MVI H,08FH +0404 2E 4F MVI L,04FH +0406 A8 XRA B +0407 A9 XRA C +0408 AA XRA D +0409 AB XRA E +040A AC XRA H +040B AD XRA L +040C FE CF CPI 0CFH +040E C4 89 06 CNZ CPUER ;TEST "XRA" B,C,D,E,H,L +0411 AF XRA A +0412 C4 89 06 CNZ CPUER ;TEST "XRA" A +0415 06 44 MVI B,044H +0417 0E 45 MVI C,045H +0419 16 46 MVI D,046H +041B 1E 47 MVI E,047H +041D 26 06 MVI H,(TEMP0 / 0FFH) ;HIGH BYTE OF TEST MEMORY LOCATION +041F 2E A6 MVI L,(TEMP0 AND 0FFH) ;LOW BYTE OF TEST MEMORY LOCATION +0421 70 MOV M,B +0422 06 00 MVI B,000H +0424 46 MOV B,M +0425 3E 44 MVI A,044H +0427 B8 CMP B +0428 C4 89 06 CNZ CPUER ;TEST "MOV" M,B AND B,M +042B 72 MOV M,D +042C 16 00 MVI D,000H +042E 56 MOV D,M +042F 3E 46 MVI A,046H +0431 BA CMP D +0432 C4 89 06 CNZ CPUER ;TEST "MOV" M,D AND D,M +0435 73 MOV M,E +0436 1E 00 MVI E,000H +0438 5E MOV E,M +0439 3E 47 MVI A,047H +043B BB CMP E +043C C4 89 06 CNZ CPUER ;TEST "MOV" M,E AND E,M +043F 74 MOV M,H +0440 26 06 MVI H,(TEMP0 / 0FFH) +0442 2E A6 MVI L,(TEMP0 AND 0FFH) +0444 66 MOV H,M +0445 3E 06 MVI A,(TEMP0 / 0FFH) +0447 BC CMP H +0448 C4 89 06 CNZ CPUER ;TEST "MOV" M,H AND H,M +044B 75 MOV M,L +044C 26 06 MVI H,(TEMP0 / 0FFH) +044E 2E A6 MVI L,(TEMP0 AND 0FFH) +0450 6E MOV L,M +0451 3E A6 MVI A,(TEMP0 AND 0FFH) +0453 BD CMP L +0454 C4 89 06 CNZ CPUER ;TEST "MOV" M,L AND L,M +0457 26 06 MVI H,(TEMP0 / 0FFH) +0459 2E A6 MVI L,(TEMP0 AND 0FFH) +045B 3E 32 MVI A,032H +045D 77 MOV M,A +045E BE CMP M +045F C4 89 06 CNZ CPUER ;TEST "MOV" M,A +0462 86 ADD M +0463 FE 64 CPI 064H +0465 C4 89 06 CNZ CPUER ;TEST "ADD" M +0468 AF XRA A +0469 7E MOV A,M +046A FE 32 CPI 032H +046C C4 89 06 CNZ CPUER ;TEST "MOV" A,M +046F 26 06 MVI H,(TEMP0 / 0FFH) +0471 2E A6 MVI L,(TEMP0 AND 0FFH) +0473 7E MOV A,M +0474 96 SUB M +0475 C4 89 06 CNZ CPUER ;TEST "SUB" M +0478 3E 80 MVI A,080H +047A 87 ADD A +047B 8E ADC M +047C FE 33 CPI 033H +047E C4 89 06 CNZ CPUER ;TEST "ADC" M +0481 3E 80 MVI A,080H +0483 87 ADD A +0484 9E SBB M +0485 FE CD CPI 0CDH +0487 C4 89 06 CNZ CPUER ;TEST "SBB" M +048A A6 ANA M +048B C4 89 06 CNZ CPUER ;TEST "ANA" M +048E 3E 25 MVI A,025H +0490 B6 ORA M +0491 FE 37 CPI 037H +0493 C4 89 06 CNZ CPUER ;TEST "ORA" M +0496 AE XRA M +0497 FE 05 CPI 005H +0499 C4 89 06 CNZ CPUER ;TEST "XRA" M +049C 36 55 MVI M,055H +049E 34 INR M +049F 35 DCR M +04A0 86 ADD M +04A1 FE 5A CPI 05AH +04A3 C4 89 06 CNZ CPUER ;TEST "INR","DCR",AND "MVI" M +04A6 01 FF 12 LXI B,12FFH +04A9 11 FF 12 LXI D,12FFH +04AC 21 FF 12 LXI H,12FFH +04AF 03 INX B +04B0 13 INX D +04B1 23 INX H +04B2 3E 13 MVI A,013H +04B4 B8 CMP B +04B5 C4 89 06 CNZ CPUER ;TEST "LXI" AND "INX" B +04B8 BA CMP D +04B9 C4 89 06 CNZ CPUER ;TEST "LXI" AND "INX" D +04BC BC CMP H +04BD C4 89 06 CNZ CPUER ;TEST "LXI" AND "INX" H +04C0 3E 00 MVI A,000H +04C2 B9 CMP C +04C3 C4 89 06 CNZ CPUER ;TEST "LXI" AND "INX" B +04C6 BB CMP E +04C7 C4 89 06 CNZ CPUER ;TEST "LXI" AND "INX" D +04CA BD CMP L +04CB C4 89 06 CNZ CPUER ;TEST "LXI" AND "INX" H +04CE 0B DCX B +04CF 1B DCX D +04D0 2B DCX H +04D1 3E 12 MVI A,012H +04D3 B8 CMP B +04D4 C4 89 06 CNZ CPUER ;TEST "DCX" B +04D7 BA CMP D +04D8 C4 89 06 CNZ CPUER ;TEST "DCX" D +04DB BC CMP H +04DC C4 89 06 CNZ CPUER ;TEST "DCX" H +04DF 3E FF MVI A,0FFH +04E1 B9 CMP C +04E2 C4 89 06 CNZ CPUER ;TEST "DCX" B +04E5 BB CMP E +04E6 C4 89 06 CNZ CPUER ;TEST "DCX" D +04E9 BD CMP L +04EA C4 89 06 CNZ CPUER ;TEST "DCX" H +04ED 32 A6 06 STA TEMP0 +04F0 AF XRA A +04F1 3A A6 06 LDA TEMP0 +04F4 FE FF CPI 0FFH +04F6 C4 89 06 CNZ CPUER ;TEST "LDA" AND "STA" +04F9 2A A4 06 LHLD TEMPP +04FC 22 A6 06 SHLD TEMP0 +04FF 3A A4 06 LDA TEMPP +0502 47 MOV B,A +0503 3A A6 06 LDA TEMP0 +0506 B8 CMP B +0507 C4 89 06 CNZ CPUER ;TEST "LHLD" AND "SHLD" +050A 3A A5 06 LDA TEMPP+1 +050D 47 MOV B,A +050E 3A A7 06 LDA TEMP0+1 +0511 B8 CMP B +0512 C4 89 06 CNZ CPUER ;TEST "LHLD" AND "SHLD" +0515 3E AA MVI A,0AAH +0517 32 A6 06 STA TEMP0 +051A 44 MOV B,H +051B 4D MOV C,L +051C AF XRA A +051D 0A LDAX B +051E FE AA CPI 0AAH +0520 C4 89 06 CNZ CPUER ;TEST "LDAX" B +0523 3C INR A +0524 02 STAX B +0525 3A A6 06 LDA TEMP0 +0528 FE AB CPI 0ABH +052A C4 89 06 CNZ CPUER ;TEST "STAX" B +052D 3E 77 MVI A,077H +052F 32 A6 06 STA TEMP0 +0532 2A A4 06 LHLD TEMPP +0535 11 00 00 LXI D,00000H +0538 EB XCHG +0539 AF XRA A +053A 1A LDAX D +053B FE 77 CPI 077H +053D C4 89 06 CNZ CPUER ;TEST "LDAX" D AND "XCHG" +0540 AF XRA A +0541 84 ADD H +0542 85 ADD L +0543 C4 89 06 CNZ CPUER ;TEST "XCHG" +0546 3E CC MVI A,0CCH +0548 12 STAX D +0549 3A A6 06 LDA TEMP0 +054C FE CC CPI 0CCH +054E 12 STAX D +054F 3A A6 06 LDA TEMP0 +0552 FE CC CPI 0CCH +0554 C4 89 06 CNZ CPUER ;TEST "STAX" D +0557 21 77 77 LXI H,07777H +055A 29 DAD H +055B 3E EE MVI A,0EEH +055D BC CMP H +055E C4 89 06 CNZ CPUER ;TEST "DAD" H +0561 BD CMP L +0562 C4 89 06 CNZ CPUER ;TEST "DAD" H +0565 21 55 55 LXI H,05555H +0568 01 FF FF LXI B,0FFFFH +056B 09 DAD B +056C 3E 55 MVI A,055H +056E D4 89 06 CNC CPUER ;TEST "DAD" B +0571 BC CMP H +0572 C4 89 06 CNZ CPUER ;TEST "DAD" B +0575 3E 54 MVI A,054H +0577 BD CMP L +0578 C4 89 06 CNZ CPUER ;TEST "DAD" B +057B 21 AA AA LXI H,0AAAAH +057E 11 33 33 LXI D,03333H +0581 19 DAD D +0582 3E DD MVI A,0DDH +0584 BC CMP H +0585 C4 89 06 CNZ CPUER ;TEST "DAD" D +0588 BD CMP L +0589 C4 89 06 CNZ CPUER ;TEST "DAD" B +058C 37 STC +058D D4 89 06 CNC CPUER ;TEST "STC" +0590 3F CMC +0591 DC 89 06 CC CPUER ;TEST "CMC +0594 3E AA MVI A,0AAH +0596 2F CMA +0597 FE 55 CPI 055H +0599 C4 89 06 CNZ CPUER ;TEST "CMA" +059C B7 ORA A ;RE-SET AUXILIARY CARRY +059D 27 DAA +059E FE 55 CPI 055H +05A0 C4 89 06 CNZ CPUER ;TEST "DAA" +05A3 3E 88 MVI A,088H +05A5 87 ADD A +05A6 27 DAA +05A7 FE 76 CPI 076H +05A9 C4 89 06 CNZ CPUER ;TEST "DAA" +05AC AF XRA A +05AD 3E AA MVI A,0AAH +05AF 27 DAA +05B0 D4 89 06 CNC CPUER ;TEST "DAA" +05B3 FE 10 CPI 010H +05B5 C4 89 06 CNZ CPUER ;TEST "DAA" +05B8 AF XRA A +05B9 3E 9A MVI A,09AH +05BB 27 DAA +05BC D4 89 06 CNC CPUER ;TEST "DAA" +05BF C4 89 06 CNZ CPUER ;TEST "DAA" +05C2 37 STC +05C3 3E 42 MVI A,042H +05C5 07 RLC +05C6 DC 89 06 CC CPUER ;TEST "RLC" FOR RE-SET CARRY +05C9 07 RLC +05CA D4 89 06 CNC CPUER ;TEST "RLC" FOR SET CARRY +05CD FE 09 CPI 009H +05CF C4 89 06 CNZ CPUER ;TEST "RLC" FOR ROTATION +05D2 0F RRC +05D3 D4 89 06 CNC CPUER ;TEST "RRC" FOR SET CARRY +05D6 0F RRC +05D7 FE 42 CPI 042H +05D9 C4 89 06 CNZ CPUER ;TEST "RRC" FOR ROTATION +05DC 17 RAL +05DD 17 RAL +05DE D4 89 06 CNC CPUER ;TEST "RAL" FOR SET CARRY +05E1 FE 08 CPI 008H +05E3 C4 89 06 CNZ CPUER ;TEST "RAL" FOR ROTATION +05E6 1F RAR +05E7 1F RAR +05E8 DC 89 06 CC CPUER ;TEST "RAR" FOR RE-SET CARRY +05EB FE 02 CPI 002H +05ED C4 89 06 CNZ CPUER ;TEST "RAR" FOR ROTATION +05F0 01 34 12 LXI B,01234H +05F3 11 AA AA LXI D,0AAAAH +05F6 21 55 55 LXI H,05555H +05F9 AF XRA A +05FA C5 PUSH B +05FB D5 PUSH D +05FC E5 PUSH H +05FD F5 PUSH PSW +05FE 01 00 00 LXI B,00000H +0601 11 00 00 LXI D,00000H +0604 21 00 00 LXI H,00000H +0607 3E C0 MVI A,0C0H +0609 C6 F0 ADI 0F0H +060B F1 POP PSW +060C E1 POP H +060D D1 POP D +060E C1 POP B +060F DC 89 06 CC CPUER ;TEST "PUSH PSW" AND "POP PSW" +0612 C4 89 06 CNZ CPUER ;TEST "PUSH PSW" AND "POP PSW" +0615 E4 89 06 CPO CPUER ;TEST "PUSH PSW" AND "POP PSW" +0618 FC 89 06 CM CPUER ;TEST "PUSH PSW" AND "POP PSW" +061B 3E 12 MVI A,012H +061D B8 CMP B +061E C4 89 06 CNZ CPUER ;TEST "PUSH B" AND "POP B" +0621 3E 34 MVI A,034H +0623 B9 CMP C +0624 C4 89 06 CNZ CPUER ;TEST "PUSH B" AND "POP B" +0627 3E AA MVI A,0AAH +0629 BA CMP D +062A C4 89 06 CNZ CPUER ;TEST "PUSH D" AND "POP D" +062D BB CMP E +062E C4 89 06 CNZ CPUER ;TEST "PUSH D" AND "POP D" +0631 3E 55 MVI A,055H +0633 BC CMP H +0634 C4 89 06 CNZ CPUER ;TEST "PUSH H" AND "POP H" +0637 BD CMP L +0638 C4 89 06 CNZ CPUER ;TEST "PUSH H" AND "POP H" +063B 21 00 00 LXI H,00000H +063E 39 DAD SP +063F 22 AB 06 SHLD SAVSTK ;SAVE THE "OLD" STACK-POINTER! +0642 31 AA 06 LXI SP,TEMP4 +0645 3B DCX SP +0646 3B DCX SP +0647 33 INX SP +0648 3B DCX SP +0649 3E 55 MVI A,055H +064B 32 A8 06 STA TEMP2 +064E 2F CMA +064F 32 A9 06 STA TEMP3 +0652 C1 POP B +0653 B8 CMP B +0654 C4 89 06 CNZ CPUER ;TEST "LXI","DAD","INX",AND "DCX" SP +0657 2F CMA +0658 B9 CMP C +0659 C4 89 06 CNZ CPUER ;TEST "LXI","DAD","INX", AND "DCX" SP +065C 21 AA 06 LXI H,TEMP4 +065F F9 SPHL +0660 21 33 77 LXI H,07733H +0663 3B DCX SP +0664 3B DCX SP +0665 E3 XTHL +0666 3A A9 06 LDA TEMP3 +0669 FE 77 CPI 077H +066B C4 89 06 CNZ CPUER ;TEST "SPHL" AND "XTHL" +066E 3A A8 06 LDA TEMP2 +0671 FE 33 CPI 033H +0673 C4 89 06 CNZ CPUER ;TEST "SPHL" AND "XTHL" +0676 3E 55 MVI A,055H +0678 BD CMP L +0679 C4 89 06 CNZ CPUER ;TEST "SPHL" AND "XTHL" +067C 2F CMA +067D BC CMP H +067E C4 89 06 CNZ CPUER ;TEST "SPHL" AND "XTHL" +0681 2A AB 06 LHLD SAVSTK ;RESTORE THE "OLD" STACK-POINTER +0684 F9 SPHL +0685 21 9B 06 LXI H,CPUOK +0688 E9 PCHL ;TEST "PCHL" + ; + ; + ; +0689 21 8B 01 CPUER: LXI H,NGCPU ;OUTPUT "CPU HAS FAILED ERROR EXIT=" TO CONSOLE +068C CD 45 01 CALL MSG +068F E3 XTHL +0690 7C MOV A,H +0691 CD 54 01 CALL BYTEO ;SHOW ERROR EXIT ADDRESS HIGH BYTE +0694 7D MOV A,L +0695 CD 54 01 CALL BYTEO ;SHOW ERROR EXIT ADDRESS LOW BYTE +0698 C3 00 00 JMP WBOOT ;EXIT TO CP/M WARM BOOT + ; + ; + ; +069B 21 74 01 CPUOK: LXI H,OKCPU ;OUTPUT "CPU IS OPERATIONAL" TO CONSOLE +069E CD 45 01 CALL MSG +06A1 C3 00 00 JMP WBOOT ;EXIT TO CP/M WARM BOOT + ; + ; + ; +06A4 A6 06 TEMPP: DW TEMP0 ;POINTER USED TO TEST "LHLD","SHLD", + ; AND "LDAX" INSTRUCTIONS + ; +06A6 00 TEMP0: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +06A7 00 TEMP1: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +06A8 00 TEMP2 DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +06A9 00 TEMP3: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +06AA 00 TEMP4: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS +06AB 00 00 SAVSTK: DS 2 ;TEMPORARY STACK-POINTER STORAGE LOCATION + ; + ; + ; + STACK EQU TEMPP+256 ;DE-BUG STACK POINTER STORAGE AREA + ; + ; + ; + END + ; + ; + ; + +Labels: +acii 0245aimm 022Aanii 0269bdos 0005 +byteo 0154byto1 0164byto2 0168byto3 0171 +c010 0287c020 029Dc030 02B3cci 02FC +cmi 02D8cnci 02F0cnzi 0308cpei 02CC +cpie 0242cpoi 02C0cpu 01ABcpuer 0689 +cpuok 069Bczi 0314end 06ADj010 01B6 +j020 01BCj030 01C2j040 01C8j050 01D7 +j060 01DAj070 01E2j080 01EBj090 01EE +j100 01F6j110 01FFj120 0202j130 020A +j140 0213j150 0216j160 021Ej170 0227 +movi 031Dmsg 0145ngcpu 018Bokcpu 0174 +orii 0273pchar 014Esavstk 06ABsbii 025D +stack 06ADsuii 0251tcpi 02E4temp0 06A6 +temp1 06A7temp2 06A8temp3 06A9temp4 06AA +tempp 06A4wboot 0000xrii 027D + +Memory dump: +0100: C3 AB 01 4D 49 43 52 4F-43 4F 53 4D 20 41 53 53 ...MICROCOSM.ASS +0110: 4F 43 49 41 54 45 53 20-38 30 38 30 2F 38 30 38 OCIATES.8080/808 +0120: 35 20 43 50 55 20 44 49-41 47 4E 4F 53 54 49 43 5.CPU.DIAGNOSTIC +0130: 20 56 45 52 53 49 4F 4E-20 31 2E 30 20 28 43 29 .VERSION.1.0.(C) +0140: 20 31 39 38 30 D5 EB 0E-09 CD 05 00 D1 C9 0E 02 .1980........... +0150: CD 05 00 C9 F5 CD 64 01-5F CD 4E 01 F1 CD 68 01 ......d._.N...h. +0160: 5F C3 4E 01 0F 0F 0F 0F-E6 0F FE 0A FA 71 01 C6 _.N..........q.. +0170: 07 C6 30 C9 0C 0D 0A 20-43 50 55 20 49 53 20 4F ..0.....CPU.IS.O +0180: 50 45 52 41 54 49 4F 4E-41 4C 24 0C 0D 0A 20 43 PERATIONAL$....C +0190: 50 55 20 48 41 53 20 46-41 49 4C 45 44 21 20 45 PU.HAS.FAILED!.E +01A0: 52 52 4F 52 20 45 58 49-54 3D 24 31 AD 06 E6 00 RROR.EXIT=$1.... +01B0: CA B6 01 CD 89 06 D2 BC-01 CD 89 06 EA C2 01 CD ................ +01C0: 89 06 F2 C8 01 CD 89 06-C2 D7 01 DA D7 01 E2 D7 ................ +01D0: 01 FA D7 01 C3 DA 01 CD-89 06 C6 06 C2 E2 01 CD ................ +01E0: 89 06 DA EB 01 E2 EB 01-F2 EE 01 CD 89 06 C6 70 ...............p +01F0: E2 F6 01 CD 89 06 FA FF-01 CA FF 01 D2 02 02 CD ................ +0200: 89 06 C6 81 FA 0A 02 CD-89 06 CA 13 02 DA 13 02 ................ +0210: E2 16 02 CD 89 06 C6 FE-DA 1E 02 CD 89 06 CA 27 ...............' +0220: 02 E2 27 02 FA 2A 02 CD-89 06 FE 00 DA 42 02 CA ..'..*.......B.. +0230: 42 02 FE F5 DA 42 02 C2-42 02 FE FF CA 42 02 DA B....B..B....B.. +0240: 45 02 CD 89 06 CE 0A CE-0A FE 0B CA 51 02 CD 89 E...........Q... +0250: 06 D6 0C D6 0F FE F0 CA-5D 02 CD 89 06 DE F1 DE ........]....... +0260: 0E FE F0 CA 69 02 CD 89-06 E6 55 FE 50 CA 73 02 ....i.....U.P.s. +0270: CD 89 06 F6 3A FE 7A CA-7D 02 CD 89 06 EE 0F FE ....:.z.}....... +0280: 75 CA 87 02 CD 89 06 E6-00 DC 89 06 E4 89 06 FC u............... +0290: 89 06 C4 89 06 FE 00 CA-9D 02 CD 89 06 D6 77 D4 ..............w. +02A0: 89 06 EC 89 06 F4 89 06-CC 89 06 FE 89 CA B3 02 ................ +02B0: CD 89 06 E6 FF E4 C0 02-FE D9 CA 1D 03 CD 89 06 ................ +02C0: E8 C6 10 EC CC 02 C6 02-E0 CD 89 06 E0 C6 20 FC ................ +02D0: D8 02 C6 04 E8 CD 89 06-F0 C6 80 F4 E4 02 C6 80 ................ +02E0: F8 CD 89 06 F8 C6 40 D4-F0 02 C6 40 F0 CD 89 06 ......@....@.... +02F0: D8 C6 8F DC FC 02 D6 02-D0 CD 89 06 D0 C6 F7 C4 ................ +0300: 08 03 C6 FE D8 CD 89 06-C8 C6 01 CC 14 03 C6 D0 ................ +0310: C0 CD 89 06 C0 C6 47 FE-47 C8 CD 89 06 3E 77 3C ......G.G....>w< +0320: 47 04 48 0D 51 5A 63 6C-7D 3D 4F 59 6B 45 50 62 G.H.QZcl}=OYkEPb +0330: 7C 57 14 6A 4D 0C 61 44-05 58 7B 5F 1C 43 60 24 |W.jM.aD.X{_.C`$ +0340: 4C 69 55 15 7A 67 25 54-42 68 2C 5D 1D 4B 79 6F LiU.zg%TBh,].Kyo +0350: 2D 65 5C 53 4A 41 78 FE-77 C4 89 06 AF 06 01 0E -e\SJAx.w....... +0360: 03 16 07 1E 0F 26 1F 2E-3F 80 81 82 83 84 85 87 .....&..?....... +0370: FE F0 C4 89 06 90 91 92-93 94 95 FE 78 C4 89 06 ............x... +0380: 97 C4 89 06 3E 80 87 06-01 0E 02 16 03 1E 04 26 ....>..........& +0390: 05 2E 06 88 06 80 80 80-89 80 80 8A 80 80 8B 80 ................ +03A0: 80 8C 80 80 8D 80 80 8F-FE 37 C4 89 06 3E 80 87 .........7...>.. +03B0: 06 01 98 06 FF 80 99 80-9A 80 9B 80 9C 80 9D FE ................ +03C0: E0 C4 89 06 3E 80 87 9F-FE FF C4 89 06 3E FF 06 ....>........>.. +03D0: FE 0E FC 16 EF 1E 7F 26-F4 2E BF A7 A1 A2 A3 A4 .......&........ +03E0: A5 A7 FE 24 C4 89 06 AF-06 01 0E 02 16 04 1E 08 ...$............ +03F0: 26 10 2E 20 B0 B1 B2 B3-B4 B5 B7 FE 3F C4 89 06 &...........?... +0400: 3E 00 26 8F 2E 4F A8 A9-AA AB AC AD FE CF C4 89 >.&..O.......... +0410: 06 AF C4 89 06 06 44 0E-45 16 46 1E 47 26 06 2E ......D.E.F.G&.. +0420: A6 70 06 00 46 3E 44 B8-C4 89 06 72 16 00 56 3E .p..F>D....r..V> +0430: 46 BA C4 89 06 73 1E 00-5E 3E 47 BB C4 89 06 74 F....s..^>G....t +0440: 26 06 2E A6 66 3E 06 BC-C4 89 06 75 26 06 2E A6 &...f>.....u&... +0450: 6E 3E A6 BD C4 89 06 26-06 2E A6 3E 32 77 BE C4 n>.....&...>2w.. +0460: 89 06 86 FE 64 C4 89 06-AF 7E FE 32 C4 89 06 26 ....d....~.2...& +0470: 06 2E A6 7E 96 C4 89 06-3E 80 87 8E FE 33 C4 89 ...~....>....3.. +0480: 06 3E 80 87 9E FE CD C4-89 06 A6 C4 89 06 3E 25 .>............>% +0490: B6 FE 37 C4 89 06 AE FE-05 C4 89 06 36 55 34 35 ..7.........6U45 +04A0: 86 FE 5A C4 89 06 01 FF-12 11 FF 12 21 FF 12 03 ..Z.........!... +04B0: 13 23 3E 13 B8 C4 89 06-BA C4 89 06 BC C4 89 06 .#>............. +04C0: 3E 00 B9 C4 89 06 BB C4-89 06 BD C4 89 06 0B 1B >............... +04D0: 2B 3E 12 B8 C4 89 06 BA-C4 89 06 BC C4 89 06 3E +>.............> +04E0: FF B9 C4 89 06 BB C4 89-06 BD C4 89 06 32 A6 06 .............2.. +04F0: AF 3A A6 06 FE FF C4 89-06 2A A4 06 22 A6 06 3A .:.......*.."..: +0500: A4 06 47 3A A6 06 B8 C4-89 06 3A A5 06 47 3A A7 ..G:......:..G:. +0510: 06 B8 C4 89 06 3E AA 32-A6 06 44 4D AF 0A FE AA .....>.2..DM.... +0520: C4 89 06 3C 02 3A A6 06-FE AB C4 89 06 3E 77 32 ...<.:.......>w2 +0530: A6 06 2A A4 06 11 00 00-EB AF 1A FE 77 C4 89 06 ..*.........w... +0540: AF 84 85 C4 89 06 3E CC-12 3A A6 06 FE CC 12 3A ......>..:.....: +0550: A6 06 FE CC C4 89 06 21-77 77 29 3E EE BC C4 89 .......!ww)>.... +0560: 06 BD C4 89 06 21 55 55-01 FF FF 09 3E 55 D4 89 .....!UU....>U.. +0570: 06 BC C4 89 06 3E 54 BD-C4 89 06 21 AA AA 11 33 .....>T....!...3 +0580: 33 19 3E DD BC C4 89 06-BD C4 89 06 37 D4 89 06 3.>.........7... +0590: 3F DC 89 06 3E AA 2F FE-55 C4 89 06 B7 27 FE 55 ?...>./.U....'.U +05A0: C4 89 06 3E 88 87 27 FE-76 C4 89 06 AF 3E AA 27 ...>..'.v....>.' +05B0: D4 89 06 FE 10 C4 89 06-AF 3E 9A 27 D4 89 06 C4 .........>.'.... +05C0: 89 06 37 3E 42 07 DC 89-06 07 D4 89 06 FE 09 C4 ..7>B........... +05D0: 89 06 0F D4 89 06 0F FE-42 C4 89 06 17 17 D4 89 ........B....... +05E0: 06 FE 08 C4 89 06 1F 1F-DC 89 06 FE 02 C4 89 06 ................ +05F0: 01 34 12 11 AA AA 21 55-55 AF C5 D5 E5 F5 01 00 .4....!UU....... +0600: 00 11 00 00 21 00 00 3E-C0 C6 F0 F1 E1 D1 C1 DC ....!..>........ +0610: 89 06 C4 89 06 E4 89 06-FC 89 06 3E 12 B8 C4 89 ...........>.... +0620: 06 3E 34 B9 C4 89 06 3E-AA BA C4 89 06 BB C4 89 .>4....>........ +0630: 06 3E 55 BC C4 89 06 BD-C4 89 06 21 00 00 39 22 .>U........!..9" +0640: AB 06 31 AA 06 3B 3B 33-3B 3E 55 32 A8 06 2F 32 ..1..;;3;>U2../2 +0650: A9 06 C1 B8 C4 89 06 2F-B9 C4 89 06 21 AA 06 F9 ......./....!... +0660: 21 33 77 3B 3B E3 3A A9-06 FE 77 C4 89 06 3A A8 !3w;;.:...w...:. +0670: 06 FE 33 C4 89 06 3E 55-BD C4 89 06 2F BC C4 89 ..3...>U..../... +0680: 06 2A AB 06 F9 21 9B 06-E9 21 8B 01 CD 45 01 E3 .*...!...!...E.. +0690: 7C CD 54 01 7D CD 54 01-C3 00 00 21 74 01 CD 45 |.T.}.T....!t..E +06A0: 01 C3 00 00 A6 06 00 00-00 00 00 00 00 ................ + +Intel HEX: +cat >test.hex <> = OnceCell::new(); + +// lazy_static! { +// pub(crate) static ref SHARED_STATE: Mutex = Mutex::new(SharedState::new()); +// } + +#[cfg(target_arch = "wasm32")] +pub fn graphic_memory() -> Vec { + let memory = crate::memory::STATIC_MEMORY.read().unwrap(); + memory[0x2400..0x2400 + crate::memory::GRAPHIC_MEMORY_SIZE].to_vec() +} + +#[derive(PartialEq)] +pub enum StepStatus { + Continue, + Paused, + Success(String), + Failure(String) +} + +pub struct StepResult { + pub status: StepStatus, + pub cycles: u8, +} + +#[wasm_bindgen] +pub struct Emulator { + memory: Box, + state: Option, + shift_register: u16, + shift_register_offset: u8, + output_buffer: Vec, +} + +pub const WIDTH: u16 = 224; +pub const HEIGHT: u16 = 256; + +#[wasm_bindgen(js_namespace = console)] +pub fn start_emulator_wasm() { + println!("Starting emulator wasm"); +} + +#[wasm_bindgen] +pub fn spawn_emulator() { + let mut emulator = Emulator::new_space_invaders(); + let time_per_frame_ms = 16; + loop { + let start = SystemTime::now(); + // Run one frame + let cycles = emulator.run_one_frame(false); + let elapsed = start.elapsed().unwrap().as_millis(); + + // Wait until we reach 16ms before running the next frame. + // TODO: I'm not 100% sure the event pump is being invoked on a 16ms cadence, + // which might explain why my game is going a bit too fast. I should actually + // rewrite this logic to guarantee that it runs every 16ms + if elapsed < time_per_frame_ms { + std::thread::sleep(Duration::from_millis((time_per_frame_ms - elapsed) as u64)); + } + let after_sleep = start.elapsed().unwrap().as_micros(); + if false { + println!("Actual time frame: {}ms, after sleep: {} ms, cycles: {}", + elapsed, + after_sleep, + cycles); + } + + SHARED_STATE.get().unwrap().lock().unwrap() + .set_megahertz(cycles as f64 / after_sleep as f64); + } +} + +pub fn spawn_emulator_thread() { + // + // Spawn the game logic in a separate thread. This logic will communicate with the + // main thread (and therefore, the actual graphics on your screen) via the `listener` + // object that this function receives in parameter. + // + thread::spawn(move || { + spawn_emulator(); + }); +} + +#[wasm_bindgen] +extern { + // Use `js_namespace` here to bind `console.log(..)` instead of just + // `log(..)` + #[cfg(target_arch = "wasm32")] + #[wasm_bindgen(js_namespace = console)] + fn log(s: &str); +} + + +// #[cfg(target_arch = "wasm32")] +// pub fn log(s: &str) { +// javascript_log(s); +// } + +#[cfg(not(target_arch = "wasm32"))] +pub fn log(s: &str) { + println!("{}", s); +} + +impl Emulator { + + pub fn new_space_invaders() -> Emulator { + let mut memory = Memory::new(); + #[cfg(not(target_arch = "wasm32"))] + memory.read_file("space-invaders.rom", 0); + + #[cfg(target_arch = "wasm32")] + log("Warning: need to read the rom file in WASM mode"); + + Emulator::new(Box::new(memory), 0) + } + + pub fn new(memory: Box, pc: usize) -> Emulator { + Emulator { memory, + shift_register: 0, + shift_register_offset: 0, + state: Some(State::new(pc)), + output_buffer: Vec::new(), + } + } + + pub fn start_emulator() -> &'static Mutex { + SHARED_STATE.set(Mutex::new(SharedState::new())); + spawn_emulator_thread(); + &SHARED_STATE.get().unwrap() + } + + pub fn run_one_frame(&mut self, verbose: bool) -> u64 { + let mut total_cycles: u64 = 0; + let cycle_max = 33_000; + while total_cycles < cycle_max / 2 { + total_cycles += self.step(verbose).cycles as u64; + } + self.interrupt(1); + + while total_cycles < cycle_max { + total_cycles += self.step(verbose).cycles as u64; + } + self.interrupt(2); + + total_cycles + } + + pub fn step(&mut self, verbose: bool) -> StepResult { + let shared = SHARED_STATE.get().unwrap(); + if shared.lock().unwrap().is_paused() { + return StepResult { status: StepStatus::Paused, cycles: 0 }; + } + + let state = &mut self.state.as_mut().unwrap(); + let op: u8 = self.memory.read(state.pc); + let opcode = OPCODES.get(&op).expect(format!("Couldn't find opcode {:02x} at pc {:04x}", + op, state.pc).as_str()); + let mut pc_was_assigned = false; + let byte1 = self.memory.read(state.pc + 1); + let byte2 = self.memory.read(state.pc + 2); + let word = Memory::to_word(byte1, byte2); + let cycles; + + unsafe { + if VERBOSE_DISASSEMBLE || (VERBOSE_DISASSEMBLE_SECTION && + (DISASSEMBLE_SECTION_START..=DISASSEMBLE_SECTION_END).contains(&state.pc)) { + let line1 = self.memory.disassemble(&opcode, state.pc); + println!("{} {:10}", line1.0, state.disassemble()); + } + } + + let mut test_status = StepResult { status: StepStatus::Continue, cycles: 0}; + + match op { + opcodes::NOP => { + cycles = 4; + }, + opcodes::INX_B => { + state.c = ((state.c as u16) + 1) as u8; + if state.c == 0 { + state.b = ((state.b as u16) + 1) as u8; + } + cycles = 5; + }, + opcodes::INX_D => { + state.e = ((state.e as u16) + 1) as u8; + if state.e == 0 { + state.d = ((state.d as u16) + 1) as u8; + } + cycles = 5; + }, + opcodes::INX_SP => { + if state.sp == 0xffff { + state.sp = 0; + } else { + state.sp += 1; + } + cycles = 5; + }, + opcodes::INX_H => { + state.l = ((state.l as u16) + 1) as u8; + if state.l == 0 { + state.h = ((state.h as u16) + 1) as u8; + } + cycles = 5; + }, + opcodes::DCX_B => { + state.c = ((state.c as i8) - 1) as u8; + if state.c == 0xff { + state.b = ((state.b as i8) - 1) as u8; + } + cycles = 5; + }, + opcodes::DCX_D => { + state.e = ((state.e as i8) - 1) as u8; + if state.e == 0xff { + state.d = ((state.d as i8) - 1) as u8; + } + cycles = 5; + }, + opcodes::DCX_H => { + let m = state.m() as i32 - 1; + state.h = (m >> 8) as u8; + state.l = m as u8; + cycles = 5; + }, + opcodes::DCX_SP => { + if state.sp == 0 { + state.sp = 0xffff as usize; + } else { + state.sp -= 1; + } + cycles = 5; + }, + opcodes::DAD_B => { + state.add_hl(state.c, state.b); + cycles = 10; + }, + opcodes::DAD_D => { + state.add_hl(state.e, state.d); + cycles = 10; + }, + opcodes::DAD_H => { + // not tested by cpudiag + state.add_hl(state.l, state.h); + cycles = 10; + }, + opcodes::DAD_SP => { + state.add_hl(state.sp as u8, state.sp as u8 - 1); + cycles = 10; + }, + opcodes::RAL => { + // high order bit + let hob = (state.psw.a & 0x80) >> 7; + let carry_value = Psw::to_u8(state.psw.carry) as u8; + state.psw.a = (state.psw.a << 1) | carry_value; + state.psw.carry = if hob == 1 { true } else { false }; + cycles = 4; + }, + opcodes::RAR => { + // low order bit + let lob = state.psw.a & 1; + let carry_value = Psw::to_u8(state.psw.carry) as u8; + state.psw.a = state.psw.a >> 1 | (carry_value << 7); + state.psw.carry = if lob == 1 { true } else { false }; + cycles = 4; + }, + opcodes::RRC => { + // low order bit + let lob = state.psw.a & 1; + state.psw.carry = lob != 0; + state.psw.a = (lob << 7) | state.psw.a >> 1; + cycles = 4; + }, + opcodes::RLC => { + // high order bit + let hob = (state.psw.a & 0x80) >> 7; + state.psw.carry = hob != 0; + state.psw.a = (state.psw.a << 1) | hob; + cycles = 4; + }, + opcodes::LDAX_B => { + state.psw.a = self.memory.read_word(state.c, state.b); + cycles = 7; + }, + opcodes::LDAX_D => { + state.psw.a = self.memory.read_word(state.e, state.d); + cycles = 7; + }, + opcodes::STAX_B => { + self.memory.write_word(state.c, state.b, state.psw.a); + cycles = 7; + }, + opcodes::STAX_D => { + self.memory.write_word(state.e, state.d, state.psw.a); + cycles = 7; + }, + opcodes::LHLD => { + state.l = self.memory.read(word); + state.h = self.memory.read(word + 1); + cycles = 16; + }, + opcodes::SHLD => { + self.memory.write(word, state.l); + self.memory.write(word + 1, state.h); + cycles = 16; + }, + opcodes::LDA => { + state.psw.a = self.memory.read(word); + cycles = 13; + }, + opcodes::STA => { + self.memory.write(word, state.psw.a); + cycles = 13; + }, + opcodes::MVI_A => { + state.psw.a = byte1; + cycles = 7; + }, + opcodes::MVI_B => { + state.b = byte1; + cycles = 7; + }, + opcodes::MVI_C => { + state.c = byte1; + cycles = 7; + }, + opcodes::MVI_D => { + state.d = byte1; + cycles = 7; + }, + opcodes::MVI_E => { + state.e = byte1; + cycles = 7; + }, + opcodes::MVI_H => { + state.h = byte1; + cycles = 7; + }, + opcodes::MVI_L => { + state.l = byte1; + cycles = 7; + }, + opcodes::MVI_M => { + self.memory.write(state.m(), byte1); + cycles = 10; + }, + opcodes::INR_A => { + state.psw.a = state.inr(state.psw.a); + cycles = 5; + }, + opcodes::INR_B => { + state.b = state.inr(state.b); + cycles = 5; + }, + opcodes::INR_C => { + state.c = state.inr(state.c); + cycles = 5; + }, + opcodes::INR_D => { + state.d = state.inr(state.d); + cycles = 5; + }, + opcodes::INR_E => { + state.e = state.inr(state.e); + cycles = 5; + }, + opcodes::INR_H => { + state.h = state.inr(state.h); + cycles = 5; + }, + opcodes::INR_L => { + state.l = state.inr(state.l); + cycles = 5; + }, + opcodes::INR_M => { + self.memory.write(state.m(), state.inr(self.memory.read(state.m()))); + cycles = 10; + }, + opcodes::DCR_A => { + state.psw.a = state.dec(state.psw.a); + cycles = 5; + }, + opcodes::DCR_B => { + state.b = state.dec(state.b); + cycles = 5; + }, + opcodes::DCR_C => { + state.c = state.dec(state.c); + cycles = 5; + }, + opcodes::DCR_D => { + state.d = state.dec(state.d); + cycles = 5; + }, + opcodes::DCR_E => { + state.e = state.dec(state.e); + cycles = 5; + }, + opcodes::DCR_H => { + state.h = state.dec(state.h); + cycles = 5; + }, + opcodes::DCR_L => { + state.l = state.dec(state.l); + cycles = 5; + }, + opcodes::DCR_M => { + self.memory.write(state.m(), state.dec(self.memory.read(state.m()))); + cycles = 10; + }, + opcodes::MOV_B_A => { + state.b = state.psw.a; + cycles = 5; + }, + opcodes::MOV_B_B => { + cycles = 5; + }, + opcodes::MOV_B_C => { + state.b = state.c; + cycles = 5; + }, + opcodes::MOV_B_D => { + state.b = state.d; + cycles = 5; + }, + opcodes::MOV_B_E => { + state.b = state.e; + cycles = 5; + }, + opcodes::MOV_B_H => { + state.b = state.h; + cycles = 5; + }, + opcodes::MOV_B_L => { + state.b = state.l; + cycles = 5; + }, + opcodes::MOV_A_M => { + state.psw.a = self.memory.read(state.m()); + cycles = 7; + }, + opcodes::MOV_B_M => { + state.b = self.memory.read(state.m()); + cycles = 7; + }, + opcodes::MOV_C_M => { + state.c = self.memory.read(state.m()); + cycles = 7; + }, + opcodes::MOV_D_M => { + state.d = self.memory.read(state.m()); + cycles = 7; + }, + opcodes::MOV_E_M => { + state.e = self.memory.read(state.m()); + cycles = 7; + }, + opcodes::MOV_H_M => { + state.h = self.memory.read(state.m()); + cycles = 7; + }, + opcodes::MOV_L_M => { + state.l = self.memory.read(state.m()); + cycles = 7; + }, + opcodes::MOV_C_A => { + state.c = state.psw.a; + cycles = 5; + }, + opcodes::MOV_C_B => { + state.c = state.b; + cycles = 5; + }, + opcodes::MOV_C_C => { + cycles = 5; + }, + opcodes::MOV_C_D => { + state.c = state.d; + cycles = 5; + }, + opcodes::MOV_C_E => { + state.c = state.e; + cycles = 5; + }, + opcodes::MOV_C_H => { + state.c = state.h; + cycles = 5; + }, + opcodes::MOV_C_L => { + state.c = state.l; + cycles = 5; + }, + opcodes::MOV_D_A => { + state.d = state.psw.a; + cycles = 5; + }, + opcodes::MOV_D_B => { + state.d = state.b; + cycles = 5; + }, + opcodes::MOV_D_C => { + state.d = state.c; + cycles = 5; + }, + opcodes::MOV_D_D => { + cycles = 5; + }, + opcodes::MOV_D_E => { + state.d = state.e; + cycles = 5; + }, + opcodes::MOV_D_H => { + state.d = state.h; + cycles = 5; + }, + opcodes::MOV_D_L => { + state.d = state.l; + cycles = 5; + }, + opcodes::MOV_E_A => { + // not tested by cpudiag, was bogus + state.e = state.psw.a; + cycles = 5; + }, + opcodes::MOV_E_B => { + state.e = state.b; + cycles = 5; + }, + opcodes::MOV_E_C => { + state.e = state.c; + cycles = 5; + }, + opcodes::MOV_E_D => { + state.e = state.d; + cycles = 5; + }, + opcodes::MOV_E_E => { + cycles = 5; + }, + opcodes::MOV_E_H => { + state.e = state.h; + cycles = 5; + }, + opcodes::MOV_E_L => { + state.e = state.l; + cycles = 5; + }, + opcodes::MOV_H_A => { + state.h = state.psw.a; + cycles = 5; + }, + opcodes::MOV_H_B => { + state.h = state.b; + cycles = 5; + }, + opcodes::MOV_H_C => { + state.h = state.c; + cycles = 5; + }, + opcodes::MOV_H_D => { + state.h = state.d; + cycles = 5; + }, + opcodes::MOV_H_E => { + state.h = state.e; + cycles = 5; + }, + opcodes::MOV_H_H => { + cycles = 5; + }, + opcodes::MOV_H_L => { + state.h = state.l; + cycles = 5; + }, + opcodes::MOV_L_A => { + state.l = state.psw.a; + cycles = 5; + }, + opcodes::MOV_M_B => { + self.memory.write(state.m(), state.b); + cycles = 7; + }, + opcodes::MOV_M_C => { + self.memory.write(state.m(), state.c); + cycles = 7; + }, + opcodes::MOV_M_D => { + self.memory.write(state.m(), state.d); + cycles = 7; + }, + opcodes::MOV_M_E => { + self.memory.write(state.m(), state.e); + cycles = 7; + }, + opcodes::MOV_M_H => { + self.memory.write(state.m(), state.h); + cycles = 7; + }, + opcodes::MOV_M_L => { + self.memory.write(state.m(), state.l); + cycles = 7; + }, + opcodes::MOV_M_A => { + // self.memory.disassemble_instructions(state.pc, 10); + self.memory.write(state.m(), state.psw.a); + cycles = 7; + }, + opcodes::MOV_L_B => { + state.l = state.b; + cycles = 5; + }, + opcodes::MOV_L_C => { + state.l = state.c; + cycles = 5; + }, + opcodes::MOV_L_D => { + state.l = state.d; + cycles = 5; + }, + opcodes::MOV_L_E => { + state.l = state.e; + cycles = 5; + }, + opcodes::MOV_L_H => { + state.l = state.h; + cycles = 5; + }, + opcodes::MOV_L_L => { + cycles = 5; + }, + opcodes::LXI_B => { + state.c = byte1; + state.b = byte2; + cycles = 10; + }, + opcodes::LXI_D => { + state.e = byte1; + state.d = byte2; + cycles = 10; + }, + opcodes::LXI_H => { + state.l = byte1; + state.h = byte2; + cycles = 10; + }, + opcodes::MOV_A_B => { + state.psw.a = state.b; + cycles = 5; + }, + opcodes::MOV_A_C => { + state.psw.a = state.c; + cycles = 10; + }, + opcodes::MOV_A_D => { + state.psw.a = state.d; + cycles = 10; + }, + opcodes::MOV_A_E => { + state.psw.a = state.e; + cycles = 10; + }, + opcodes::MOV_A_H => { + state.psw.a = state.h; + cycles = 10; + }, + opcodes::MOV_A_L => { + state.psw.a = state.l; + cycles = 10; + }, + opcodes::LXI_SP => { + state.sp = word; + cycles = 10; + }, + opcodes::SUI => { + let value = state.psw.a as i16 - byte1 as i16; + state.psw.a = value as u8; + state.set_arithmetic_flags(value); + cycles = 7; + }, + opcodes::SBI => { + let value = state.psw.a as i16 - (byte1 as i16 + state.psw.carry as i16); + state.psw.a = value as u8; + state.set_arithmetic_flags(value); + cycles = 7; + }, + opcodes::ADD_A => { + state.add(state.psw.a, 0); + cycles = 4; + } + opcodes::ADD_B => { + state.add(state.b, 0); + cycles = 4; + } + opcodes::ADD_C => { + state.add(state.c, 0); + cycles = 4; + } + opcodes::ADD_D => { + state.add(state.d, 0); + cycles = 4; + } + opcodes::ADD_E => { + state.add(state.e, 0); + cycles = 4; + } + opcodes::ADD_H => { + state.add(state.h, 0); + cycles = 4; + } + opcodes::ADD_L => { + state.add(state.l, 0); + cycles = 4; + } + opcodes::ADD_M => { + state.add(self.memory.read(state.m()), 0); + cycles = 7; + } + opcodes::ADC_A => { + state.add(state.psw.a, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::ADC_B => { + state.add(state.b, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::ADC_C => { + state.add(state.c, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::ADC_D => { + state.add(state.d, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::ADC_E => { + state.add(state.e, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::ADC_H => { + state.add(state.h, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::ADC_L => { + state.add(state.l, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::ADC_M => { + state.add(self.memory.read(state.m()), Psw::to_u8(state.psw.carry)); + cycles = 7; + } + opcodes::SUB_A => { + state.sub(state.psw.a, 0); + cycles = 4; + } + opcodes::SUB_B => { + state.sub(state.b, 0); + cycles = 4; + } + opcodes::SUB_C => { + state.sub(state.c, 0); + cycles = 4; + } + opcodes::SUB_D => { + state.sub(state.d, 0); + cycles = 4; + } + opcodes::SUB_E => { + state.sub(state.e, 0); + cycles = 4; + } + opcodes::SUB_H => { + state.sub(state.h, 0); + cycles = 4; + } + opcodes::SUB_L => { + state.sub(state.l, 0); + cycles = 4; + } + opcodes::SUB_M => { + state.sub(self.memory.read(state.m()), 0); + cycles = 7; + } + opcodes::SBB_A => { + state.sub(state.psw.a, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::SBB_B => { + state.sub(state.b, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::SBB_C => { + state.sub(state.c, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::SBB_D => { + state.sub(state.d, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::SBB_E => { + state.sub(state.e, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::SBB_H => { + state.sub(state.h, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::SBB_L => { + state.sub(state.l, Psw::to_u8(state.psw.carry)); + cycles = 4; + } + opcodes::SBB_M => { + state.sub(self.memory.read(state.m()), Psw::to_u8(state.psw.carry)); + cycles = 7; + } + opcodes::ADI => { + let value = state.psw.a as i16 + byte1 as i16; + state.psw.a = value as u8; + state.set_arithmetic_flags(value); + cycles = 7; + }, + opcodes::ACI => { + let value = state.psw.a as i16 + byte1 as i16 + Psw::to_u8(state.psw.carry) as i16; + state.psw.a = value as u8; + state.set_arithmetic_flags(value); + cycles = 7; + }, + opcodes::JMP => { + if word == 0 { + let output: String = self.output_buffer.clone().into_iter().collect(); + println!("{}", output); + } else { + state.pc = word; + pc_was_assigned = true; + } + cycles = 10; + }, + opcodes::RPO => { + pc_was_assigned = state.ret(&mut self.memory, ! state.psw.parity); + cycles = if pc_was_assigned { 11 } else { 5 }; + }, + opcodes::RPE => { + pc_was_assigned = state.ret(&mut self.memory, state.psw.parity); + cycles = if pc_was_assigned { 11 } else { 5 }; + }, + opcodes::RNC => { + pc_was_assigned = state.ret(&mut self.memory, ! state.psw.carry); + cycles = if pc_was_assigned { 11 } else { 5 }; + }, + opcodes::RC => { + pc_was_assigned = state.ret(&mut self.memory, state.psw.carry); + cycles = if pc_was_assigned { 11 } else { 5 }; + }, + opcodes::RP => { + pc_was_assigned = state.ret(&mut self.memory, ! state.psw.sign); + cycles = if pc_was_assigned { 11 } else { 5 }; + }, + opcodes::RM => { + pc_was_assigned = state.ret(&mut self.memory, state.psw.sign); + cycles = if pc_was_assigned { 11 } else { 5 }; + }, + opcodes::RZ => { + pc_was_assigned = state.ret(&mut self.memory, state.psw.zero); + cycles = if pc_was_assigned { 11 } else { 5 }; + }, + opcodes::RNZ => { + pc_was_assigned = state.ret(&mut self.memory, ! state.psw.zero); + cycles = if pc_was_assigned { 11 } else { 5 }; + }, + opcodes::RET => { + pc_was_assigned = state.ret(&mut self.memory, true); + cycles = 11; + }, + opcodes::POP_B => { + state.c = self.memory.read(state.sp); + state.b = self.memory.read(state.sp + 1); + state.sp += 2; + cycles = 10; + }, + opcodes::POP_D => { + state.e = self.memory.read(state.sp); + state.d = self.memory.read(state.sp + 1); + state.sp += 2; + cycles = 10; + }, + opcodes::POP_H => { + state.l = self.memory.read(state.sp); + state.h = self.memory.read(state.sp + 1); + state.sp += 2; + cycles = 10; + }, + opcodes::PUSH_B => { + self.memory.write(state.sp - 1, state.b); + self.memory.write(state.sp - 2, state.c); + state.sp -= 2; + cycles = 11; + }, + opcodes::PUSH_D => { + self.memory.write(state.sp - 1, state.d); + self.memory.write(state.sp - 2, state.e); + state.sp -= 2; + cycles = 11; + }, + opcodes::PUSH_H => { + self.memory.write(state.sp - 1, state.h); + self.memory.write(state.sp - 2, state.l); + state.sp -= 2; + cycles = 11; + }, + opcodes::CC => { + if state.psw.carry { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + cycles = if pc_was_assigned { 11 } else { 17 }; + }, + opcodes::CPO => { + if ! state.psw.parity { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + cycles = if pc_was_assigned { 11 } else { 17 }; + }, + opcodes::CPE => { + if state.psw.parity { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + cycles = if pc_was_assigned { 11 } else { 17 }; + }, + opcodes::CM => { + if state.psw.sign { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + cycles = if pc_was_assigned { 11 } else { 17 }; + }, + opcodes::CP => { + if ! state.psw.sign { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + cycles = if pc_was_assigned { 11 } else { 17 }; + }, + opcodes::CNZ => { + if ! state.psw.zero { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + cycles = if pc_was_assigned { 11 } else { 17 }; + }, + opcodes::CZ => { + if state.psw.zero { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + cycles = if pc_was_assigned { 11 } else { 17 }; + }, + opcodes::CNC => { + if ! state.psw.carry { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + cycles = if pc_was_assigned { 11 } else { 17 }; + }, + opcodes::STC => { + state.psw.carry = true; + cycles = 4; + }, + opcodes::CMC => { + state.psw.carry = ! state.psw.carry; + cycles = 4; + }, + opcodes::CMA => { + state.psw.a ^= 0xff; + cycles = 4; + }, + opcodes::DAA => { + let mut a: u16 = state.psw.a as u16; + // least significant bits + let lsb = a & 0x0f; + if lsb > 9 || state.psw.auxiliary_carry { + a += 6; + state.psw.auxiliary_carry = (lsb + 6) > 0xf; + }; + // most significant bits + let mut msb = (a & 0xf0) >> 4; + if (msb > 9) || state.psw.carry { msb += 6; } + a = (msb << 4) | (a & 0xf); + state.psw.auxiliary_carry = (msb + 6) > 0xf; + state.set_arithmetic_flags(a as i16); + state.psw.a = a as u8; + + cycles = 4; + }, + opcodes::CALL => { + cycles = 17; + if cfg!(test) { + // In test mode, this function returns 1 for successful test, 2 for failure + if word == 5 && state.c == 9 { + // print message at address word(D, E) + let ind = Memory::to_word(state.e, state.d); + if ind == 0x174 { + test_status.status = StepStatus::Success("Success".into()); + } else if ind == 0x18b { + let sp = state.sp + 4; + let pc = Memory::to_word(self.memory.read(sp), self.memory.read(sp+1)) + - 6; + for i in 0..20 { + println!("sp + {}: {:02x}", i, self.memory.read(sp + i)); + } + let s = format!("Failure at pc {:04x}", pc); + test_status.status = StepStatus::Failure(s); + } else { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + } else { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + } else { + state.call(&mut self.memory, word); + pc_was_assigned = true; + } + }, + opcodes::ANI => { + let value = state.psw.a & byte1; + state.psw.a = value; + state.set_logic_flags(value.into()); + cycles = 7; + }, + opcodes::ORI => { + let value = state.psw.a | byte1; + state.psw.a = value; + state.set_logic_flags(value.into()); + cycles = 7; + }, + opcodes::XRI => { + state.psw.a = state.xra(byte1); + cycles = 7; + }, + opcodes::XRA_A => { + state.psw.a = state.xra(state.psw.a); + cycles = 4; + }, + opcodes::XRA_B => { + state.psw.a = state.xra(state.b); + cycles = 4; + }, + opcodes::XRA_C => { + state.psw.a = state.xra(state.c); + cycles = 4; + }, + opcodes::XRA_D => { + state.psw.a = state.xra(state.d); + cycles = 4; + }, + opcodes::XRA_E => { + state.psw.a = state.xra(state.e); + cycles = 4; + }, + opcodes::XRA_H => { + state.psw.a = state.xra(state.h); + cycles = 4; + }, + opcodes::XRA_L => { + state.psw.a = state.xra(state.l); + cycles = 4; + }, + opcodes::XRA_M => { + state.psw.a = state.xra(self.memory.read(state.m())); + cycles = 7; + }, + opcodes::ANA_A => { + state.psw.a = state.and(state.psw.a); + cycles = 4; + }, + opcodes::ANA_B => { + state.psw.a = state.and(state.b); + cycles = 4; + }, + opcodes::ANA_C => { + state.psw.a = state.and(state.c); + cycles = 4; + }, + opcodes::ANA_D => { + state.psw.a = state.and(state.d); + cycles = 4; + }, + opcodes::ANA_E => { + state.psw.a = state.and(state.e); + cycles = 4; + }, + opcodes::ANA_H => { + state.psw.a = state.and(state.h); + cycles = 4; + }, + opcodes::ANA_L => { + state.psw.a = state.and(state.l); + cycles = 4; + }, + opcodes::ANA_M => { + state.psw.a = state.and(self.memory.read(state.m())); + cycles = 7; + }, + opcodes::ORA_A => { + state.psw.a = state.or(state.psw.a); + cycles = 4; + }, + opcodes::ORA_B => { + state.psw.a = state.or(state.b); + cycles = 4; + }, + opcodes::ORA_C => { + state.psw.a = state.or(state.c); + cycles = 4; + }, + opcodes::ORA_D => { + state.psw.a = state.or(state.d); + cycles = 4; + }, + opcodes::ORA_E => { + state.psw.a = state.or(state.e); + cycles = 4; + }, + opcodes::ORA_H => { + state.psw.a = state.or(state.h); + cycles = 4; + }, + opcodes::ORA_L => { + state.psw.a = state.or(state.l); + cycles = 4; + }, + opcodes::ORA_M => { + state.psw.a = state.or(self.memory.read(state.m())); + cycles = 7; + }, + opcodes::XTHL => { + let l = self.memory.read(state.sp); + self.memory.write(state.sp, state.l); + state.l = l; + let h = self.memory.read(state.sp + 1); + self.memory.write(state.sp + 1, state.h); + state.h = h; + cycles = 18; + }, + opcodes::JPO => { + pc_was_assigned = state.jump_if_flag(word, ! state.psw.parity); + cycles = 10; + }, + opcodes::JPE => { + pc_was_assigned = state.jump_if_flag(word, state.psw.parity); + cycles = 10; + }, + opcodes::JNZ => { + pc_was_assigned = state.jump_if_flag(word, ! state.psw.zero); + cycles = 10; + }, + opcodes::JZ => { + pc_was_assigned = state.jump_if_flag(word, state.psw.zero); + cycles = 10; + }, + opcodes::JP => { + pc_was_assigned = state.jump_if_flag(word, ! state.psw.sign); + cycles = 10; + }, + opcodes::JM => { + pc_was_assigned = state.jump_if_flag(word, state.psw.sign); + cycles = 10; + }, + opcodes::JC => { + pc_was_assigned = state.jump_if_flag(word, state.psw.carry); + cycles = 10; + }, + opcodes::JNC => { + pc_was_assigned = state.jump_if_flag(word, ! state.psw.carry); + cycles = 10; + }, + opcodes::XCHG => { + let h = state.h; + state.h = state.d; + state.d = h; + let l = state.l; + state.l = state.e; + state.e = l; + cycles = 4; + }, + opcodes::PUSH_PSW => { + self.memory.write(state.sp - 1, state.psw.a); + self.memory.write(state.sp - 2, (state.psw.value() & 0xff) as u8); + state.sp -= 2; + cycles = 11; + }, + opcodes::POP_PSW => { + state.psw.a = self.memory.read(state.sp + 1); + state.psw.set_flags(self.memory.read(state.sp)); + state.sp += 2; + cycles = 10; + }, + opcodes::CPI => { + state.cmp(byte1); + cycles = 7; + }, + opcodes::CMP_B => { + state.cmp(state.b); + cycles = 4; // not sure, couldn't find it in the reference + }, + opcodes::CMP_C => { + state.cmp(state.c); + cycles = 4; // not sure, couldn't find it in the reference + }, + opcodes::CMP_D => { + state.cmp(state.d); + cycles = 4; // not sure, couldn't find it in the reference + }, + opcodes::CMP_E => { + state.cmp(state.e); + cycles = 4; // not sure, couldn't find it in the reference + }, + opcodes::CMP_H => { + state.cmp(state.h); + cycles = 4; // not sure, couldn't find it in the reference + }, + opcodes::CMP_L => { + state.cmp(state.l); + cycles = 4; // not sure, couldn't find it in the reference + }, + opcodes::CMP_M => { + state.cmp(self.memory.read(state.m())); + cycles = 7; + }, + opcodes::CMP_A => { + state.cmp(state.psw.a); + cycles = 4; // not sure, couldn't find it in the reference + }, + opcodes::SPHL => { + state.sp = ((state.h as u16) << 8) as usize | state.l as usize; + cycles = 5; + }, + opcodes::PCHL => { + state.pc = ((state.h as u16) << 8) as usize | state.l as usize; + pc_was_assigned = true; + cycles = 5; + }, + opcodes::EI => { + state.enable_interrupts = true; + cycles = 4; + } + opcodes::DI => { + state.enable_interrupts = false; + cycles = 4; + } + opcodes::OUT => { + match byte1 { + 2 => { + self.shift_register_offset = state.psw.a & 0x7; + }, + 3 => { + // sound + }, + 4 => { + self.shift_register = ((state.psw.a as u16) << 8) + | (self.shift_register >> 8) + }, + 5 => { + // sound + }, + 6 => { + // watch dog + } + _ => { + println!("Unsupported OUT port: {}", byte1); + } + } + cycles = 10; + } + opcodes::IN => { + match byte1 { + 1 => { + state.psw.a = SHARED_STATE.get().unwrap().lock().unwrap().get_in_1(); + }, + 2 => { + state.psw.a = SHARED_STATE.get().unwrap().lock().unwrap().get_in_2(); + }, + 3 => { + let shift_amount = 8 - self.shift_register_offset; + state.psw.a = (self.shift_register >> shift_amount) as u8; + }, + _ => { + panic!("Unsupported IN port: {}", byte1); + } + } + cycles = 10; + } + opcodes::RST_1 => { + state.call(&mut self.memory, 1 * 8); + pc_was_assigned = true; + cycles = 10; + } + opcodes::RST_2 => { + state.call(&mut self.memory, 2 * 8); + pc_was_assigned = true; + cycles = 10; + } + opcodes::RST_7 => { + state.call(&mut self.memory, 7 * 8); + pc_was_assigned = true; + cycles = 10; + } + _ => panic!("Don't know how to run opcode: {:02x} at {:04x}", op, state.pc), + } + + if ! pc_was_assigned { + state.pc += opcode.size; + } + + if cycles == 0 { + panic!("Cycles not assigned"); + } + + if test_status.status != StepStatus::Continue { + test_status + } else { + StepResult { status: StepStatus::Continue, cycles } + } + } + + fn interrupt(&mut self, interrupt_number: u8) { + if self.state.as_ref().unwrap().enable_interrupts { + // log_time(format!("Interrupt {}", interrupt_number).as_str()); + let state = self.state.as_mut().unwrap(); + self.memory.write(state.sp - 1, ((state.pc as u16 & 0xff00) >> 8) as u8); + self.memory.write(state.sp - 2, (state.pc as u16 & 0xff) as u8); + state.sp -= 2; + // Interrupt 0 goes to $0, 1 to $08, 2 to $10, etc... + state.pc = (interrupt_number as usize) << 3; + } + } +} diff --git a/emulator/src/emulator_state.rs b/emulator/src/emulator_state.rs new file mode 100644 index 0000000..3ab7b81 --- /dev/null +++ b/emulator/src/emulator_state.rs @@ -0,0 +1,81 @@ +use crate::memory; +use crate::memory::GRAPHIC_MEMORY_SIZE; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct SharedState { + display: bool, + megahertz: f64, + in_1: u8, + in_2: u8, + is_paused: bool, + +} + +#[wasm_bindgen] +impl SharedState { + pub fn new() -> SharedState { + SharedState { + display: false, + megahertz: 2.0, + in_1: 8, // bit 3 is always 1 + in_2: 0, + is_paused: false, + } + } +} + +impl SharedState { + #[cfg(not(target_arch = "wasm32"))] + pub fn graphic_memory(&self) -> [u8; GRAPHIC_MEMORY_SIZE] { + let mut result: [u8; GRAPHIC_MEMORY_SIZE] = [0; GRAPHIC_MEMORY_SIZE]; + let memory = memory::STATIC_MEMORY.read().unwrap(); + result.clone_from_slice(&memory[0x2400..0x2400 + GRAPHIC_MEMORY_SIZE]); + result + } + + fn set_vbl(&mut self, value: bool) { + self.display = value; + } + + fn is_vbl(&self) -> bool { self.display } + + pub fn set_megahertz(&mut self, mhz: f64) { + self.megahertz = mhz; + } + + pub fn get_megahertz(&self) -> f64 { + self.megahertz + } + + pub fn set_bit_in_1(&mut self, bit: u8, value: bool) { + let mask = 1 << bit; + if value { + self.in_1 |= mask; + } else { + self.in_1 &= ! mask; + } + } + + pub fn get_in_1(&self) -> u8 { + self.in_1 + } + + pub fn set_bit_in_2(&mut self, bit: u8, value: bool) { + let mask = 1 << bit; + if value { + self.in_2 |= mask; + } else { + self.in_2 &= ! mask; + } + } + + pub fn get_in_2(&self) -> u8 { + self.in_2 + } + + pub fn is_paused(&self) -> bool { self.is_paused } + pub fn pause(&mut self) { self.is_paused = true; } + pub fn unpause(&mut self) { self.is_paused = false; } +} diff --git a/emulator/src/lib.rs b/emulator/src/lib.rs new file mode 100644 index 0000000..7ab093b --- /dev/null +++ b/emulator/src/lib.rs @@ -0,0 +1,18 @@ +pub mod emulator; +pub mod memory; +pub mod state; +pub mod emulator_state; +pub mod opcodes; +mod test; + +const VERBOSE: bool = false; +static mut VERBOSE_DISASSEMBLE: bool = false; +const VERBOSE_GRAPHIC: bool = true; +const VERBOSE_DISASSEMBLE_SECTION: bool = false; +const DISASSEMBLE_SECTION_START: usize = 0x1439; +const DISASSEMBLE_SECTION_END: usize = 0x1447; +// const DISASSEMBLE_SECTION_START: usize = 0x1439; +// const DISASSEMBLE_SECTION_END: usize = 0x1447; +const VERBOSE_MEMORY: bool = false; + + diff --git a/emulator/src/memory.rs b/emulator/src/memory.rs new file mode 100644 index 0000000..849e39a --- /dev/null +++ b/emulator/src/memory.rs @@ -0,0 +1,89 @@ +use std::fs::File; +use std::io::Read; +use crate::state::State; +use crate::opcodes::Opcode; +use std::sync::{Mutex, RwLock}; +use lazy_static::lazy_static; +use crate::emulator_state::SharedState; + +const MEMORY_SIZE: usize = 0x10000; +pub const SCREEN_WIDTH: usize = 0x20; // 0x20 bytes (256 pixels) +pub const SCREEN_HEIGHT: usize = 0xe0; +pub const GRAPHIC_MEMORY_SIZE: usize = SCREEN_WIDTH * SCREEN_HEIGHT; + +pub trait GraphicRenderer: Send { + fn draw(&mut self, x: u8, y: u8, value: u8); + fn color(&self, x: u8, y: u8) -> u8; + fn display(&self); +} + +static _STATIC_MEMORY: [u8; MEMORY_SIZE] = [0; MEMORY_SIZE]; + +lazy_static! { + pub(crate) static ref STATIC_MEMORY: RwLock<[u8; MEMORY_SIZE]> = RwLock::new(_STATIC_MEMORY); +} + +pub struct Memory { + pub verbose: bool, +} + +impl Memory { + pub fn new() -> Self { + Memory { + verbose: false, + } + } + + pub(crate) fn set_verbose(&mut self, v: bool) { + self.verbose = v; + } + + pub fn read_file(&mut self, file_name: &str, start: usize) { + let mut file = File::open(file_name).expect("Couldn't open file"); + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer).expect("Couldn't read file"); + + let mut state = State::default(); + state.pc = start; + + let mut i: usize = 0; + for byte in buffer { + self.write(i + state.pc, byte); + i += 1; + } + } + + pub(crate) fn write(&mut self, address: usize, value: u8) { + STATIC_MEMORY.write().unwrap()[address] = value; + if self.verbose { + println!(" mem[{:04x}={:02X}]", address, value ); + } + } + + pub(crate) fn read(&self, i: usize) -> u8 { + STATIC_MEMORY.read().unwrap()[i] + } + + pub(crate) fn read_word(&self, b0: u8, b1: u8) -> u8 { + STATIC_MEMORY.read().unwrap()[Memory::to_word(b0, b1)] + } + + pub(crate) fn write_word(&mut self, b0: u8, b1: u8, value: u8) { + let address = Memory::to_word(b0, b1); + STATIC_MEMORY.write().unwrap()[address] = value; + } + + pub(crate) fn disassemble(&self, opcode: &Opcode, pc: usize) -> (String, usize) { + let formatted_opcode = match opcode.size { + 1 => opcode.display1(), + 2 => opcode.display2(self.read(pc + 1)), + _ => opcode.display3(self.read(pc + 1), self.read(pc + 2)), + }; + let result = format!("{:04x}: {}", pc, formatted_opcode); + (result, opcode.size) + } + + pub(crate) fn to_word(b1: u8, b2: u8) -> usize { + return ((b2 as u16) << 8 | b1 as u16) as usize; + } +} diff --git a/emulator/src/opcodes.rs b/emulator/src/opcodes.rs new file mode 100644 index 0000000..8e59ae5 --- /dev/null +++ b/emulator/src/opcodes.rs @@ -0,0 +1,543 @@ +use std::collections::HashMap; +use crate::memory::Memory; +use lazy_static::lazy_static; + +pub struct Opcode { + opcode: u8, + pub(crate) size: usize, + name: &'static str +} + +impl Opcode { + fn new(opcode: u8, size: usize, name: &'static str) -> Opcode { + Opcode { opcode, size, name } + } + + pub(crate) fn display1(&self) -> String { + let s = self.name; + format!("{:02x} {:14}", self.opcode, s) + } + + pub(crate) fn display2(&self, byte1: u8) -> String { + let s = format!("{} {:02x}", self.name, byte1); + format!("{:02x} {:02x} {:14}", self.opcode, byte1, s) + } + + pub(crate) fn display3(&self, byte1: u8, byte2: u8) -> String { + let s = format!("{} {:04x}", self.name, Memory::to_word(byte1, byte2)); + format!("{:02x} {:02x} {:02x} {:14}", self.opcode, byte1, byte2, s) + } +} + +pub const NOP: u8 = 0x00; +pub const LXI_B: u8 = 0x01; +pub const STAX_B: u8 = 0x02; +pub const INX_B: u8 = 0x03; +pub const INR_B: u8 = 0x04; +pub const DCR_B: u8 = 0x05; +pub const MVI_B: u8 = 0x06; +pub const RLC: u8 = 0x07; +pub const DAD_B: u8 = 0x09; +pub const LDAX_B: u8 = 0x0a; +pub const DCX_B: u8 = 0x0b; +pub const RRC: u8 = 0x0f; +pub const LXI_D: u8 = 0x11; +pub const STAX_D: u8 = 0x12; +pub const INX_D: u8 = 0x13; +pub const INR_C: u8 = 0x0c; +pub const DCR_C: u8 = 0x0d; +pub const MVI_C: u8 = 0x0e; +pub const INR_D: u8 = 0x14; +pub const DCR_D: u8 = 0x15; +pub const MVI_D: u8 = 0x16; +pub const RAL: u8 = 0x17; +pub const DAD_D: u8 = 0x19; +pub const LDAX_D: u8 = 0x1a; +pub const DCX_D: u8 = 0x1b; +pub const INR_E: u8 = 0x1c; +pub const DCR_E: u8 = 0x1d; +pub const MVI_E: u8 = 0x1e; +pub const RAR: u8 = 0x1f; +pub const LXI_H: u8 = 0x21; +pub const SHLD: u8 = 0x22; +pub const INX_H: u8 = 0x23; +pub const INR_H: u8 = 0x24; +pub const DCR_H: u8 = 0x25; +pub const MVI_H: u8 = 0x26; +pub const DAA: u8 = 0x27; +pub const DAD_H: u8 = 0x29; +pub const LHLD: u8 = 0x2a; +pub const DCX_H: u8 = 0x2b; +pub const INR_L: u8 = 0x2c; +pub const DCR_L: u8 = 0x2d; +pub const MVI_L: u8 = 0x2e; +pub const CMA: u8 = 0x2f; +pub const LXI_SP: u8 = 0x31; +pub const STA: u8 = 0x32; +pub const INX_SP: u8 = 0x33; +pub const INR_M: u8 = 0x34; +pub const DCR_M: u8 = 0x35; +pub const MVI_M: u8 = 0x36; +pub const STC: u8 = 0x37; +pub const DAD_SP: u8 = 0x39; +pub const LDA: u8 = 0x3a; +pub const DCX_SP: u8 = 0x3b; +pub const INR_A: u8 = 0x3c; +pub const DCR_A: u8 = 0x3d; +pub const MVI_A: u8 = 0x3e; +pub const CMC: u8 = 0x3f; + +pub const MOV_B_B: u8 = 0x40; +pub const MOV_B_C: u8 = 0x41; +pub const MOV_B_D: u8 = 0x42; +pub const MOV_B_E: u8 = 0x43; +pub const MOV_B_H: u8 = 0x44; +pub const MOV_B_L: u8 = 0x45; +pub const MOV_B_M: u8 = 0x46; +pub const MOV_B_A: u8 = 0x47; + +pub const MOV_C_B: u8 = 0x48; +pub const MOV_C_C: u8 = 0x49; +pub const MOV_C_D: u8 = 0x4a; +pub const MOV_C_E: u8 = 0x4b; +pub const MOV_C_H: u8 = 0x4c; +pub const MOV_C_L: u8 = 0x4d; +pub const MOV_C_M: u8 = 0x4e; +pub const MOV_C_A: u8 = 0x4f; + +pub const MOV_D_B: u8 = 0x50; +pub const MOV_D_C: u8 = 0x51; +pub const MOV_D_D: u8 = 0x52; +pub const MOV_D_E: u8 = 0x53; +pub const MOV_D_H: u8 = 0x54; +pub const MOV_D_L: u8 = 0x55; +pub const MOV_D_M: u8 = 0x56; +pub const MOV_D_A: u8 = 0x57; + +pub const MOV_E_B: u8 = 0x58; +pub const MOV_E_C: u8 = 0x59; +pub const MOV_E_D: u8 = 0x5a; +pub const MOV_E_E: u8 = 0x5b; +pub const MOV_E_H: u8 = 0x5c; +pub const MOV_E_L: u8 = 0x5d; +pub const MOV_E_M: u8 = 0x5e; +pub const MOV_E_A: u8 = 0x5f; + +pub const MOV_H_B: u8 = 0x60; +pub const MOV_H_C: u8 = 0x61; +pub const MOV_H_D: u8 = 0x62; +pub const MOV_H_E: u8 = 0x63; +pub const MOV_H_H: u8 = 0x64; +pub const MOV_H_L: u8 = 0x65; +pub const MOV_H_M: u8 = 0x66; +pub const MOV_H_A: u8 = 0x67; + +pub const MOV_L_B: u8 = 0x68; +pub const MOV_L_C: u8 = 0x69; +pub const MOV_L_D: u8 = 0x6a; +pub const MOV_L_E: u8 = 0x6b; +pub const MOV_L_H: u8 = 0x6c; +pub const MOV_L_L: u8 = 0x6d; +pub const MOV_L_M: u8 = 0x6e; +pub const MOV_L_A: u8 = 0x6f; + +pub const MOV_M_B: u8 = 0x70; +pub const MOV_M_C: u8 = 0x71; +pub const MOV_M_D: u8 = 0x72; +pub const MOV_M_E: u8 = 0x73; +pub const MOV_M_H: u8 = 0x74; +pub const MOV_M_L: u8 = 0x75; +pub const MOV_M_A: u8 = 0x77; + +pub const MOV_A_B: u8 = 0x78; +pub const MOV_A_C: u8 = 0x79; +pub const MOV_A_D: u8 = 0x7a; +pub const MOV_A_E: u8 = 0x7b; +pub const MOV_A_H: u8 = 0x7c; +pub const MOV_A_L: u8 = 0x7d; +pub const MOV_A_M: u8 = 0x7e; + +pub const ADD_B: u8 = 0x80; +pub const ADD_C: u8 = 0x81; +pub const ADD_D: u8 = 0x82; +pub const ADD_E: u8 = 0x83; +pub const ADD_H: u8 = 0x84; +pub const ADD_L: u8 = 0x85; +pub const ADD_M: u8 = 0x86; +pub const ADD_A: u8 = 0x87; + +pub const ADC_B: u8 = 0x88; +pub const ADC_C: u8 = 0x89; +pub const ADC_D: u8 = 0x8a; +pub const ADC_E: u8 = 0x8b; +pub const ADC_H: u8 = 0x8c; +pub const ADC_L: u8 = 0x8d; +pub const ADC_M: u8 = 0x8e; +pub const ADC_A: u8 = 0x8f; + +pub const SUB_B: u8 = 0x90; +pub const SUB_C: u8 = 0x91; +pub const SUB_D: u8 = 0x92; +pub const SUB_E: u8 = 0x93; +pub const SUB_H: u8 = 0x94; +pub const SUB_L: u8 = 0x95; +pub const SUB_M: u8 = 0x96; +pub const SUB_A: u8 = 0x97; + +pub const SBB_B: u8 = 0x98; +pub const SBB_C: u8 = 0x99; +pub const SBB_D: u8 = 0x9a; +pub const SBB_E: u8 = 0x9b; +pub const SBB_H: u8 = 0x9c; +pub const SBB_L: u8 = 0x9d; +pub const SBB_M: u8 = 0x9e; +pub const SBB_A: u8 = 0x9f; + +pub const ORA_B: u8 = 0xb0; +pub const ORA_C: u8 = 0xb1; +pub const ORA_D: u8 = 0xb2; +pub const ORA_E: u8 = 0xb3; +pub const ORA_H: u8 = 0xb4; +pub const ORA_L: u8 = 0xb5; +pub const ORA_M: u8 = 0xb6; +pub const ORA_A: u8 = 0xb7; + +pub const CMP_B: u8 = 0xb8; +pub const CMP_C: u8 = 0xb9; +pub const CMP_D: u8 = 0xba; +pub const CMP_E: u8 = 0xbb; +pub const CMP_H: u8 = 0xbc; +pub const CMP_L: u8 = 0xbd; +pub const CMP_M: u8 = 0xbe; +pub const CMP_A: u8 = 0xbf; + +pub const ANA_B: u8 = 0xa0; +pub const ANA_C: u8 = 0xa1; +pub const ANA_D: u8 = 0xa2; +pub const ANA_E: u8 = 0xa3; +pub const ANA_H: u8 = 0xa4; +pub const ANA_L: u8 = 0xa5; +pub const ANA_M: u8 = 0xa6; +pub const ANA_A: u8 = 0xa7; + +pub const XRA_B: u8 = 0xa8; +pub const XRA_C: u8 = 0xa9; +pub const XRA_D: u8 = 0xaa; +pub const XRA_E: u8 = 0xab; +pub const XRA_H: u8 = 0xac; +pub const XRA_L: u8 = 0xad; +pub const XRA_M: u8 = 0xae; +pub const XRA_A: u8 = 0xaf; + +pub const RNZ: u8 = 0xc0; +pub const POP_B: u8 = 0xc1; +pub const JNZ: u8 = 0xc2; +pub const JMP: u8 = 0xc3; +pub const CNZ: u8 = 0xc4; +pub const PUSH_B: u8 = 0xc5; +pub const ADI: u8 = 0xc6; +pub const RZ: u8 = 0xc8; +pub const RET: u8 = 0xc9; +pub const JZ: u8 = 0xca; +pub const CZ: u8 = 0xcc; +pub const CALL: u8 = 0xcd; +pub const ACI: u8 = 0xce; +pub const RST_1: u8 = 0xcf; +pub const RNC: u8 = 0xd0; +pub const POP_D: u8 = 0xd1; +pub const JNC: u8 = 0xd2; +pub const OUT: u8 = 0xd3; +pub const CNC: u8 = 0xd4; +pub const PUSH_D: u8 = 0xd5; +pub const SUI: u8 = 0xd6; +pub const RST_2: u8 = 0xd7; +pub const RC: u8 = 0xd8; +pub const JC: u8 = 0xda; +pub const IN: u8 = 0xdb; +pub const CC: u8 = 0xdc; +pub const SBI: u8 = 0xde; +pub const RPO: u8 = 0xe0; +pub const POP_H: u8 = 0xe1; +pub const JPO: u8 = 0xe2; +pub const XTHL: u8 = 0xe3; +pub const CPO: u8 = 0xe4; +pub const PUSH_H: u8 = 0xe5; +pub const ANI: u8 = 0xe6; +pub const RPE: u8 = 0xe8; +pub const PCHL: u8 = 0xe9; +pub const JPE: u8 = 0xea; +pub const XCHG: u8 = 0xeb; +pub const CPE: u8 = 0xec; +pub const XRI: u8 = 0xee; +pub const RP: u8 = 0xf0; +pub const POP_PSW: u8 = 0xf1; +pub const JP: u8 = 0xf2; +pub const DI: u8 = 0xf3; +pub const CP: u8 = 0xf4; +pub const PUSH_PSW: u8 = 0xf5; +pub const ORI: u8 = 0xf6; +pub const RM: u8 = 0xf8; +pub const SPHL: u8 = 0xf9; +pub const JM: u8 = 0xfa; +pub const EI: u8 = 0xfb; +pub const CM: u8 = 0xfc; +pub const CPI: u8 = 0xfe; +pub const RST_7: u8 = 0xff; + +fn init_opcodes() -> HashMap { + // Opcode, size, disassembly name, cycles (appendix B of the ref manual) + let ops: Vec<(u8, usize, &str)> = vec![ + (NOP, 1, "NOP"), + (LXI_B, 3, "LD BC,"), + (STAX_B, 1, "STA (BC)"), + (INX_B, 1, "INC (BC)"), + (INR_B, 1, "INR B"), + (DCR_B, 1, "DEC B"), + (MVI_B, 2, "LD B,"), + (RLC, 1, "RLC"), + (DAD_B, 1, "ADD HL,BC"), + (LDAX_B, 1, "LD A,(BC)"), + (DCX_B, 1, "DEC (BC)"), + (INR_C, 1, "INR C"), + (DCR_C, 1, "DEC C"), + (MVI_C, 2, "LD C,"), + (RRC, 1, "RRC"), + (LXI_D, 3, "LD DE,"), + (STAX_D, 1, "STA (DE)"), + (INX_D, 1, "INC (DE)"), + (INR_D, 1, "INR D"), + (DCR_D, 1, "DEC D"), + (MVI_D, 2, "LD D,"), + (RAL, 1, "RAL"), + (DAD_D, 1, "ADD HL,DE"), + (LDAX_D, 1, "LD A,(DE)"), + (DCX_D, 1, "DC (DE)"), + (INR_E, 1, "INR E"), + (DCR_E, 1, "DEC E"), + (MVI_E, 2, "LD E,"), + (RAR, 1, "RAR"), + (LXI_H, 3, "LD HL,"), + (SHLD, 3, "SHLD"), + (INX_H, 1, "INC (HL)"), + (INR_H, 1, "INR H"), + (DCR_H, 1, "DEC HL"), + (MVI_H, 2, "MOV H,"), + (DAA, 1, "DAA"), + (DAD_H, 1, "ADD HL,HL"), + (LHLD, 3, "LHLD"), + (DCX_H, 1, "DEC (HL)"), + (INR_L, 1, "INR L"), + (DCR_L, 1, "DEC L"), + (MVI_L, 2, "LD L,"), + (CMA, 1, "CMA"), + (LXI_SP, 3, "LD SP,"), + (STA, 3, "STA"), + (INX_SP, 1, "INC (SP)"), + (INR_M, 1, "INC (HL)"), + (DCR_M, 1, "DEC (HL)"), + (MVI_M, 2, "MV (HL),"), + (STC, 1, "STC"), + (DAD_SP, 1, "ADD HL,SP"), + (0x3a, 3, "LDA"), + (DCX_SP, 1, "DEC (SP)"), + (INR_A, 1, "INR A"), + (DCR_A, 1, "DEC A"), + (MVI_A, 2, "LD A,"), + (CMC, 1, "CMC"), + (MOV_B_C, 1, "MOV B,C"), + (MOV_B_D, 1, "MOV B,D"), + (MOV_B_E, 1, "MOV B,E"), + (MOV_B_H, 1, "MOV B,H"), + (MOV_B_L, 1, "MOV B,L"), + (MOV_B_M, 1, "MOV B,M"), + (MOV_B_A, 1, "MOV B,A"), + (MOV_C_B, 1, "MOV C,B"), + (MOV_C_C, 1, "MOV C,C"), + (MOV_C_D, 1, "MOV C,D"), + (MOV_C_E, 1, "MOV C,E"), + (MOV_C_H, 1, "MOV C,H"), + (MOV_C_L, 1, "MOV C,L"), + (MOV_C_M, 1, "MOV C,M"), + (MOV_C_A, 1, "MOV C,A"), + (MOV_D_B, 1, "MOV D,B"), + (MOV_D_C, 1, "MOV D,C"), + (MOV_D_D, 1, "MOV D,D"), + (MOV_D_E, 1, "MOV D,E"), + (MOV_D_H, 1, "MOV D,H"), + (MOV_D_L, 1, "MOV D,L"), + (MOV_D_M, 1, "MOV D,M"), + (MOV_D_A, 1, "MOV D,A"), + (MOV_E_B, 1, "MOV E,B"), + (MOV_E_C, 1, "MOV E,C"), + (MOV_E_D, 1, "MOV E,D"), + (MOV_E_E, 1, "MOV E,E"), + (MOV_E_H, 1, "MOV E,H"), + (MOV_E_L, 1, "MOV E,L"), + (MOV_E_M, 1, "MOV E,M"), + (MOV_E_A, 1, "MOV E,A"), + (MOV_H_B, 1, "MOV H,B"), + (MOV_H_C, 1, "MOV H,C"), + (MOV_H_D, 1, "MOV H,D"), + (MOV_H_E, 1, "MOV H,E"), + (MOV_H_H, 1, "MOV H,H"), + (MOV_H_L, 1, "MOV H,L"), + (MOV_H_M, 1, "MOV H,M"), + (MOV_H_A, 1, "MOV H,A"), + (MOV_L_B, 1, "MOV L,B"), + (MOV_L_C, 1, "MOV L,C"), + (MOV_L_D, 1, "MOV L,D"), + (MOV_L_E, 1, "MOV L,E"), + (MOV_L_H, 1, "MOV L,H"), + (MOV_L_L, 1, "MOV L,L"), + (MOV_L_M, 1, "MOV L,M"), + (MOV_L_A, 1, "MOV L,A"), + (MOV_M_B, 1, "LD (HL),B"), + (MOV_M_C, 1, "LD (HL),C"), + (MOV_M_D, 1, "LD (HL),D"), + (MOV_M_E, 1, "LD (HL),E"), + (MOV_M_H, 1, "LD (HL),H"), + (MOV_M_L, 1, "LD (HL),L"), + (MOV_M_A, 1, "LD (HL),A"), + (MOV_A_B, 1, "MOV A,B"), + (MOV_A_C, 1, "MOV A,C"), + (MOV_A_D, 1, "MOV A,D"), + (MOV_A_E, 1, "MOV A,E"), + (MOV_A_H, 1, "MOV A,H"), + (MOV_A_L, 1, "MOV A,L"), + (MOV_A_M, 1, "MOV A,(HL)"), + (ADD_B, 1, "ADD B"), + (ADD_C, 1, "ADD C"), + (ADD_D, 1, "ADD D"), + (ADD_E, 1, "ADD E"), + (ADD_H, 1, "ADD H"), + (ADD_L, 1, "ADD L"), + (ADD_M, 1, "ADD M"), + (ADD_A, 1, "ADD A"), + (ADC_B, 1, "ADC B"), + (ADC_C, 1, "ADC C"), + (ADC_D, 1, "ADC D"), + (ADC_E, 1, "ADC E"), + (ADC_H, 1, "ADC H"), + (ADC_L, 1, "ADC L"), + (ADC_M, 1, "ADC M"), + (ADC_A, 1, "ADC A"), + (SUB_B, 1, "SUB B"), + (SUB_C, 1, "SUB C"), + (SUB_D, 1, "SUB D"), + (SUB_E, 1, "SUB E"), + (SUB_H, 1, "SUB H"), + (SUB_L, 1, "SUB L"), + (SUB_M, 1, "SUB M"), + (SUB_A, 1, "SUB A"), + (SBB_B, 1, "SBB B"), + (SBB_C, 1, "SBB C"), + (SBB_D, 1, "SBB D"), + (SBB_E, 1, "SBB E"), + (SBB_H, 1, "SBB H"), + (SBB_L, 1, "SBB L"), + (SBB_M, 1, "SBB M"), + (SBB_A, 1, "SBB A"), + (ANA_B, 1, "AND B"), + (ANA_C, 1, "AND C"), + (ANA_D, 1, "AND D"), + (ANA_E, 1, "AND E"), + (ANA_H, 1, "AND H"), + (ANA_L, 1, "AND L"), + (ANA_M, 1, "AND M"), + (ANA_A, 1, "AND A"), + (ORA_B, 1, "ORA B"), + (ORA_C, 1, "ORA C"), + (ORA_D, 1, "ORA D"), + (ORA_E, 1, "ORA E"), + (ORA_H, 1, "ORA H"), + (ORA_L, 1, "ORA L"), + (ORA_M, 1, "ORA M"), + (ORA_A, 1, "ORA A"), + (CMP_B, 1, "CMP B"), + (CMP_C, 1, "CMP C"), + (CMP_D, 1, "CMP D"), + (CMP_E, 1, "CMP E"), + (CMP_H, 1, "CMP H"), + (CMP_L, 1, "CMP L"), + (CMP_M, 1, "CMP M"), + (CMP_A, 1, "CMP A"), + (XRA_B, 1, "XRA B"), + (XRA_C, 1, "XRA C"), + (XRA_D, 1, "XRA D"), + (XRA_E, 1, "XRA E"), + (XRA_H, 1, "XRA H"), + (XRA_L, 1, "XRA L"), + (XRA_M, 1, "XRA M"), + (XRA_A, 1, "XRA A"), + (RNZ, 1, "RNZ"), + (POP_B, 1, "POP BC"), + (JNZ, 3, "JNZ"), + (JMP, 3, "JMP"), + (CNZ, 3, "CNZ"), + (PUSH_B, 1, "PUSH BC"), + (ADI, 2, "ADI"), + (RZ, 1, "RZ"), + (RET, 1, "RET"), + (JZ, 3, "JZ"), + (CZ, 3, "CZ"), + (CALL, 3, "CALL"), + (ACI, 2, "ACI"), + (RNC, 1, "RNC"), + (POP_D, 1, "POP DE"), + (JNC, 3, "JNC"), + (OUT, 2, "OUT"), + (CNC, 3, "CNC"), + (PUSH_D, 1, "PUSH DE"), + (SUI, 2, "SUI"), + (RC, 1, "RC"), + (JC, 3, "JC"), + (IN, 2, "IN"), + (CC, 3, "CC"), + (SBI, 2, "SBI"), + (RPO, 1, "RPO"), + (POP_H, 1, "POP HL"), + (JPO, 3, "JPO"), + (XTHL, 1, "XTHL"), + (CPO, 3, "CPO"), + (PUSH_H, 1, "PUSH HL"), + (ANI, 2, "ANI"), + (RPE, 1, "RPE"), + (PCHL, 1, "PCHL"), + (JPE, 3, "JPE"), + (XCHG, 1, "EX DE,HL"), + (CPE, 3, "CPE"), + (XRI, 2, "XRI"), + (RP, 1, "RP"), + (POP_PSW, 1, "POP PSW"), + (JP, 3, "JP"), + (CP, 3, "CP"), + (PUSH_PSW, 1, "PUSH PSW"), + (ORI, 2, "ORI"), + (RM, 1, "RM"), + (SPHL, 1, "SPHL"), + (JM, 3, "JM"), + (CM, 3, "CM"), + (EI, 1, "EI"), + (DI, 1, "DI"), + (CPI, 2, "CPI"), + (RST_1, 2, "RST 1"), + (RST_2, 2, "RST 2"), + (RST_7, 2, "RST 7"), + ]; + let mut result: HashMap = HashMap::new(); + for op in ops { + if result.get(&op.0).is_some() { + panic!("REPEATED OPCODE {:02x}", op.0); + } + result.insert(op.0, Opcode::new(op.0, op.1, op.2)); + } + result +} + +lazy_static! { + pub(crate) static ref OPCODES: HashMap = init_opcodes(); +} + +// fn insert(map: &mut HashMap, opcode: u8, size: usize, name: &'static str) { +// map.insert(opcode, Opcode::new(opcode, size, name)); +// } diff --git a/emulator/src/state.rs b/emulator/src/state.rs new file mode 100644 index 0000000..9f3d007 --- /dev/null +++ b/emulator/src/state.rs @@ -0,0 +1,186 @@ +use crate::memory::Memory; + +#[derive(Default)] +pub(crate) struct Psw { + pub a: u8, + pub sign: bool, + pub zero: bool, + pub auxiliary_carry: bool, + pub parity: bool, + pub carry: bool, +} + +/* + * PSW is: A for the high byte and the flags for the low byte, in order, + * sign, zero, auxiliary carry, parity, carry: + * + * Bit: 7 6 5 4 3 2 1 0 + * Flag: S Z 0 AC 0 P 1 C + */ +impl Psw { + pub(crate) fn to_u8(f: bool) -> u16 { if f { 1 } else { 0 } } + pub(crate) fn to_bool(v: u8) -> bool { if v == 0 { false } else { true }} + + pub(crate) fn set_flags(&mut self, value: u8) { + self.sign = Psw::to_bool(value & (1 << 7)); + self.zero = Psw::to_bool(value & (1 << 6)); + self.auxiliary_carry = Psw::to_bool(value & (1 << 4)); + self.parity = Psw::to_bool(value & (1 << 2)); + self.carry = Psw::to_bool(value & 1); + } + + pub(crate) fn value(&self) -> u16 { + (self.a as u16) << 8 | + Psw::to_u8(self.sign) << 7 | + Psw::to_u8(self.zero) << 6 | + Psw::to_u8(self.auxiliary_carry) << 4 | + Psw::to_u8(self.parity) << 2 | + 1 << 1 | + Psw::to_u8(self.carry) + } + + pub(crate) fn disassemble(&self) -> String { + format!("[C={} P={} S={} Z={}]", + Psw::to_u8(self.carry), + Psw::to_u8(self.parity), + Psw::to_u8(self.sign), + Psw::to_u8(self.zero)) + } +} + +#[derive(Default)] +pub(crate) struct State { + pub b: u8, + pub c: u8, + pub d: u8, + pub e: u8, + pub h: u8, + pub l: u8, + pub psw: Psw, + pub pc: usize, + pub sp: usize, + pub enable_interrupts: bool, +} + +impl State { + pub(crate) fn new(pc: usize) -> State { + State { + pc, + .. Default::default() + } + } + + pub fn m(&self) -> usize { + Memory::to_word(self.l, self.h) + } + + pub fn disassemble(&self) -> String { + format!("a:{:02x} b:{:02x} c:{:02x} d:{:02x} e:{:02x} lh:{:04x} pc:{:04x} sp:{:04x} {}", + self.psw.a, self.b, self.c, self.d, self.e, ((self.h as u16) << 8) | self.l as u16, + self.pc, self.sp, + self.psw.disassemble()) + } + + pub fn set_logic_flags(&mut self, value: i16) { + self.psw.zero = value == 0; + self.psw.sign = 0x80 == (value & 0x80); + self.psw.parity = (value & 0xff).count_ones() % 2 == 0; + self.psw.carry = false; + // state.psw.auxiliary_carry = (state.psw.a < byte1); + } + + pub fn set_arithmetic_flags(&mut self, value: i16) { + self.psw.zero = (value & 0xff) == 0; + self.psw.sign = 0x80 == (value & 0x80); + self.psw.parity = (value & 0xff).count_ones() % 2 == 0; + self.psw.carry = value < 0 || value > 0xff; + self.psw.auxiliary_carry = self.psw.carry; + } + + pub fn jump_if_flag(&mut self, word: usize, flag: bool) -> bool { + if flag { + self.pc = word; + true + } else { + false + } + } + + pub fn call(&mut self, memory: &mut Memory, target_pc: usize) { + let ret = self.pc + 3; + memory.write(self.sp - 1, ((ret >> 8) as u8) & 0xff); + memory.write(self.sp - 2, (ret & 0xff) as u8); + self.sp -= 2; + self.pc = target_pc; + } + + pub fn ret(&mut self, memory: &Memory, flag: bool) -> bool { + if flag { + self.pc = Memory::to_word(memory.read(self.sp), memory.read(self.sp + 1)); + self.sp += 2; + } + flag + } + + pub fn dec(&mut self, n: u8) -> u8 { + let value = if n == 0 { + self.psw.carry = true; + self.psw.auxiliary_carry = true; + 0xff + } else { + n - 1 + }; + self.set_arithmetic_flags(value as i16); + value + } + + pub fn xra(&mut self, value: u8) -> u8 { + let value = self.psw.a ^ value; + self.set_arithmetic_flags(value as i16); + value + } + + pub fn and(&mut self, value: u8) -> u8 { + let value = self.psw.a & value; + self.set_arithmetic_flags(value as i16); + value + } + + pub fn or(&mut self, value: u8) -> u8 { + let value = self.psw.a | value; + self.set_arithmetic_flags(value as i16); + value + } + + pub fn add(&mut self, value: u8, carry: u16) { + let value = self.psw.a as u16 + value as u16 + carry; + self.set_arithmetic_flags(value as i16); + self.psw.a = value as u8 & 0xff; + } + + pub fn sub(&mut self, value: u8, carry: u16) { + let value = self.psw.a as i16 - value as i16 - carry as i16; + self.set_arithmetic_flags(value as i16); + self.psw.a = value as u8 & 0xff; + } + + pub fn cmp(&mut self, n: u8) { + let value: i16 = self.psw.a as i16 - n as i16; + self.set_arithmetic_flags(value); + } + + pub fn inr(&mut self, n: u8) -> u8 { + let value = if n == 0xff { 0 } else { n + 1 }; + self.set_arithmetic_flags(value as i16); + value + } + + pub fn add_hl(&mut self, b0: u8, b1: u8) { + let hl = Memory::to_word(self.l, self.h) as u32; + let v = Memory::to_word(b0, b1) as u32; + let result: u32 = hl + v; + self.psw.carry = result > 0xffff; + self.h = ((result & 0xff00) >> 8) as u8; + self.l = (result & 0xff) as u8; + } +} diff --git a/emulator/src/test.rs b/emulator/src/test.rs new file mode 100644 index 0000000..da8dc8f --- /dev/null +++ b/emulator/src/test.rs @@ -0,0 +1,30 @@ +#[cfg(test)] +mod test { + use crate::memory::Memory; + use crate::emulator::{Emulator, StepResult, StepStatus}; + + #[test] + fn run_cpu_diag() { + let mut memory = Memory::new(); + let start = 0x100; + memory.read_file("cpudiag.bin", start); + let mut computer = Emulator::new(Box::new(memory), start as usize); + let mut result = StepResult { status: StepStatus::Continue, cycles: 0 }; + unsafe { + while result.status == StepStatus::Continue { + result = computer.step(true); + } + } + match result.status { + StepStatus::Success(s) => { + println!("Success: {}", s) + }, + StepStatus::Failure(s) => { + panic!(s); + }, + _ => { + println!("Something went wrong"); + } + } + } +} diff --git a/emulator/src/test2.rs b/emulator/src/test2.rs new file mode 100644 index 0000000..18e2d52 --- /dev/null +++ b/emulator/src/test2.rs @@ -0,0 +1,9 @@ + +struct Foo {} + +fn main() { + let foo = Foo{}; + thread::spawn(move || { + println!("Here is foo {}", foo); + }) +} \ No newline at end of file diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6e3763fe93e7dc4c1898f222f0cf3acb84474d35 GIT binary patch literal 13130 zcmeHtX;f2J+jgu~>qrrm8LU9Lh|lI-{<{&AN|()etvzHt_36~JNxW?@9Vy<>%NZ( zXY8!DZc^F=gTc0*`oZEH47L^ngMBIY)p~Gcq~kkh@aM~r6Q|C91^&f+br}cVD~4KL z2z5aDghpHn_J;Wep#r^iLcD^#y@NvhP@(L#Br`Bb2O4A^?0qTp3My##`743mFo&?9 z-Fk<2+g=LZefYb>M|OXA#Q3P8@zG-bU@0%Mi*lyS%An z?2OcPS9qh|B<~2XPUtH+z<82oc9q3l1A~=qy60NvQugEb=i?UG+R^DdWjF6S=*Ox8 zA9cZ;BQ6w$755OcyVk;BKl~p2WNV*%G?@F<`Eb7)CkFx;bbYQBy^|W`sAbpnvtU>HG_vKu9GdjD!zY6=d+VNTN4o3MSz;0cFkWZ4XP$mS+t-QkkFKNzfMKiM_$n#@+J)3{N*-es~p}B96JI#Zlm^L9Xsm2j= zD)JsNXSXPaGfuC({3{JBequONhH4o`kyPb0#PINuuztaWE><=ZMT|mcmbx{x>eSFw zHmF=X9y8y(fxOt{-(RY2%6s%ZMTgIzsD#+8sO@+`N>Me@h~p$pd#!N$wd0B%p4y>j zs}O1BUXJ5t5ws+`1reoChzIJxV}QH1RRp>7u?< zTZ4=7lBL&?(iQ%bii*SQWrzpDhXeZu{0VRdz=8j5Mx|(rvcbX0cHw^Ll=hyasb>b& zUkRu<#k_AYJG&n4tJHJRH(POerZS>&{*T(w+*L@*%o18Ew{fxTBWItFg&E9{?n@FGi$u3 zX?2s{4zoj5X?-Owls|56-urlx^UT@X{=mm+>Pi zRI}xP=l=bNh%2*qywc!2Tv)%GW^0PFJOH;`PkxhI@XB5*v+dw#*WzSHQWJkvd+7NU zxtF)naNsUld-lX{TOmCwEKCG3+Up*9$<@LlD`K<`J>M)<&2SCAGSj$dr|lYXYn$qL z`w=QGZbj7E7_~UQ!bb>tl}?vn-~DMdZjTi?G;;f%8_vI2%b2ZsO4fRswQEJhP5J)D z`c@~Gz@6%`OJ^H-L(}M2`w%!8gm~T|4c+|nI!+Zqwd`D4-CcV#;kP-MIpIKHjoTC3 zOoQ^~^~x=5EW*@1PB>xs{&$p>Mau)<99b(BbYvD*vQE#Z{Z3dZw{fvEWrerwX<=ff7M!_v z`SM%?C0X&vc&bU9i&&y)ycGYeP!y0A&3dmFCl=svhRm!=Mwhuv0t6xV$g!KoX(kbK zwEIROKE*Yo(VR|`o+Nb-QI>E_W^WaC(C;zPnRx7EzJy8 zb_Ukaf{SptasD-nmRSDEAqbDL%e<848@F2Or%RQ=T2PH57Q+9gyG4f)X;0EFokS49 zzQm?AYf}^ILYODPDVSBoW*U@$WTN6EEBpk&9+dg9myG<(cuR|x+*@kfnOPycr@Xqc z_*`_GPEF}G)#GZu_}C!Vh>@!95|^6V$%o&Zb#qf4X^2R>(vwfFW5z|~rH*-L9Us)N z5n$T!2t;9_nKEf*b{5Slc-jJvk6SRiev6?-_`s!G3W&IvfQnZdoE0`!SHzu`mz7n= ziqT8c{m1g{Vvnd{#rA-v6|Gdc9flxDl9uryorCCwSDKbZP9=<2nv|1(=e_1=b+WSP zL2s$tEw$($Y#tufYE6zh9L=9oMF?L_Xvbo^f7+n-HD<2P*Ct!E8fU3wO_{xgg{f%j zPaJOQO%W2k3?UIjl>u;}RX1m?+w|==)A{Sr!Pw-8ozFYQ@*EZk?IJ!MD>WSZh2bR; zN9kSp>-T}s9$Usj8&)MVu=*kmE1g$>N85m1OC54+4ge?yjZ|w%=1GrlU+}c_j}>z$ zNy8~>aE&0M5UE`iztc!6W>dT~R-Or%cOC3Xxx!#-Ouk6mV&Tyt9tcmf~q`T!2YZvrk8^3n;LF?0K$S6Y)5(W3XhExiS(lQlsjkAV<^j9|B1^o`Zbx6Q0RN0~2@zg}UBH-i(G zN7xi_R%-C*B^jha~s zP`Xx9&9t|f%#@8Qu_`4Fc19(Fml%S3)8nw|KD{8CKtR@wQv~Cj#}?1Ksg}>sg<|4FkQWAUYO!OhCRWN?8F={}MS*(>x#-QO7YSZIMy2bjEp?>17Cst_* zfb9GW2#y`mtmsrHF3ru^O`Sm4=TYrVB4j8aBxI0yPLN4KlXoH#j6x<`0H4ds>LY!J ztKEtOlZTesBrM?TB?S}SD?qWXF~T;F#Hzlla)6o9*^6|5LG;_7Hnf0~XdU#$D}%$7 zJL1!hyQLoYt(_^yg0wHwsbA-VMszD7RAN@G1q zE4(BY3umpXV_^Y6?U1C)QGwS_wvGckR1uj>jpJ3K1!K3!sU~w@tD1x-CnhFN^%i(p z^tzrZ1OojOA187^MC#Th)&edkfJ-SFOyVut~W65^G8OgCkzF zT6A}%FLmJB%dLq>2esIx?%$?IuNFN#rVC;tao&<8@2=n>ZYG*dO9hNH|CKZ1*V=>@ z5D*2(=ZHCJGGqsPxN6O)XgZyEqT(^dHKY3prNtdHt0*fA#6HcxR46>%;W+2q??P}; zi4%-fnT7L5_$?TAQw?oa#_;W<`}+P)XmK6Yk=&Jgtar%Aky^8Fs0JkulplTm{P`B3 z>?AV5yMP&TW2-?jZYN%@?Ff)n&)O6v{R4;Ur~Vk{+jl6Z+TaQc3ol@1tDe~7N6E9S z1z;xiC)*#$sTzmkz#8{~?0svngxEsD_?ddf#JG+Qh3juE7ILayn2FUQGrtSit0#RCO2(2gqq=U5l}KaCo1DEe#U% z>RqQ94E%VbcCH>z0cz*iXoUj8nGB4c24Ki!XDPlYX<)6A4d6Y~m8Bz`r5439X=Mv2IuvrJFhA4K8}`CGLt(7(O(s;yEY z>SvDoA{q+&m`*(JZPqTM>9?)aRTUd%Dy)}SZ?u7?Dzky*YFzq@e!8^5HuE9LCxgF0 z5|v^>7^=*U0nsoNO|3z{`_-yt6vN)>As#2LI{xtlO$uEq;a8|HE{NZ0B3mrw-Bdtu zTw*4VxIF=S11)?9!lS>Pkalt!uK_I1sFg64;6IzoUv9$_`i%i&=x{l>cx>n1J?>H7o>?&qT4YYVa?saMVPcbIsJCZN zzCB)6R#ul}`VE8}S$z*YR?J8r0M#LMbZ%oZNRqH4uv{wfNLi#x-9ZLv#>=RmHO+9k z%o(@$NJqUXbaaj3SaugW776guy5gCLzt;7c-q_&bxTOgY-QGQWN=cHTf#5bB2CHZT z1x?v{Flq}QxOM-YeA@t!`)x7B8o@rDVyetZw}$YPu)d3$d#;v#X<=cLV`{W#4=_L! zASG_5+}<7z05W-IP#K_&DGIp48Vz@9Tr-)y^3N4gu<^9_IO#aY=wg&|vG7 zOSe>Ip}j-a%0uwSp{)z|rz-$SLpBpwR7P?SR5nd^6m85XsUE5q7-p=bZ_#$1er%~< zK3MKGtF3Ge!Ck}~dD4du9$oW1YcVhZ@SHY5d=b)CqJ&GZ!kg*yUDBh?kds*#azL6K z9I6gfmX(G0QC5~flHdu=&(7-e-*Bh=fs&4t932I|>i*%0lqt|@;N0p5yhrG(CP}zDz}zQw z6Flh8N=pwBqneWy_1RxU0{O9+?kn+nntgTj-H)%@Xc~4|3XIm2r0j@MBgmcQL`@D( zfr5?}+EW(!J}<*lFfY4WD4E|H{W{G|2E5Y9*Vj-iplb8_+HIh>ak535V3RysJ!}wE|GqU6_>-$m7k?0BF&MeL~unR(# zC*V-^Q@YJ1fb*bsra~Q*BcPyGI^x@z4B~DAl?S8I(;ZN$SyD4<>>m9-Z`=gsghWyZ zaoi$7v&^A{B7&NVJ)9dF${y7L=yQpA+I}v6OPz*VA+xJy8T|JA5?m)CUz{sJ+5xjqZp5s+^n6r=i=4< zn9O-Yh}#F;@KS-glWybu<4DjL@dC-2V!qADu+Ia#;x}A9S=jalDU+Zaq@aY4sW416 z6Fq~hPJ8AtLLcc}#`LdNCnd`qJFQXw>ci8-smDoPOH+E?ps<@73R|q5|09Vp52k-C@;=SpMS$PjiCGfYJlsHx9SZ%n_7886=*v${_!; zQ4SOV9giK0oPY~MmM%F1G!Q{}Zh2L97$n`;X8c|CV7}O+m$(N4K?AUQz?9Yw_&rG( zpQ$9t$>!my=|-COxDE6XbrjJ^U&Nc0LpAB^KrVVbGxA{hJG~e%8%MlI)n$KY2~~Lt zhV!+8ae01<;t`*gyo_Dr#N{|7lNScSz`&p!A;%Xvk)8nMF61>rLEjE4GXv@-c9pY< z9?4MG%l6pTB#^{U`gV39B%HDRJK_{TFDV7i8`!(&fam<%BLAD*Lj>dh1VbEIWLlnjVKiQ8pf}r>J3$`gCR~0=snCL3iC>te&U#u3>Ma zt>!W--yCX~q1ZO5z#iWBXo3|xNjiUO1lx@Tj<1LM2T+Z@Zu2fs zOqdP#wyk-j_%#N0`#9dL$tydoFToeTV%?7S;1iC*9!)$O2x^#Z>;x^>-HJ#aXsB-A z4w~GsjR{Z}^%pT~9rOo)60~H0SZ%O=yE>>40H-7qdih%c?yK?*Fxc&6aC^{z{rmF| zCuxD&vnM=dz%9O69eI1;->3WM3_mlS?|xkC=E>1faUBme!$>zK3i|?XZfCTPl6pBx z(aV#iU4IT|BKjpMv5jv^{?F@k6x5WEg=uWfA?2o#jO!|(n0*+D-kG^U?Yj!YBs5<) ztZ~u|>jmB&Q_)oVF1Xk)q8P6y4GO z;2{4#gO0hJrr@2<0`bw}6A5UZFr#WloYz`r3|)F2aC4U#Vr$VEPujqu{;`|UshM&g z1LENb90N8hnzN7El+qX*l8GH$X@k3mgW>&0Z|1wdxa#CL&d_Oyty^};Ik6W-r!hT7 zz@oV$_=O!xQOlDC(u(B_Ok=#${>89D z*ut?|V9}E;tB(eL`2df(J`q&++?Lup5J?c+)d+J{e8ElPgLj-x&TKf5zJuN|qnuN$kY~5l;1GY{{6q%Pw z?^=Ark4%AM&DBYDTF?PY@>2V|hL1(8VoH0IL=%D|blv(d^kB)$87A1e<&2`-G$czf z(TSXsfDgU-y8KAnSD)AsmJ0F>#$~?#PfI(Pwv*qx*$#Rm^QpJ?(FNWlYIM=u`hTvC z)qa#x?{b=a^3Qk%NNLMG6+2HS&*CvyzgJKWQJWAcl#hb<4S%L&Y||vFdl3w8r$1)i zx>x+8WEUKvP5tE9O8_w-)uG&pPVK<^$AQWJzEW)baqiG6``f`HI`Eh{=jsy)<-!oE zjLyZ}mH}}c^XdO(^`)z~*KuhU>qJAdudeOdC-@kLW97Sl8TPNZfb}ayVHS_Eg}M9w z=h`Q}7Yu>r_=%4p{5Su9kShOt>i&>yKg`z8Tm2lFja5Ow>U5dzcsMATP$T4mJk1md zCgx=R^<4Qwz|{H5T%R~_w=Oo8j|$r4ow^1*ZvO=Lz+pHM{RZ;t@;ONHf|+Y~BR)k6 z8(suMx$0C5WCnJy?Ndavrpy&+>v1{lqu`nMb7I&OuxT04{{?tt^Ks^a)xqvP0mftP zajy{Xe|KGf!ZEO;);Bdq)nR?1AAk98S^U){x5Y5?zpo(bc07z;(`EC?6!AN`Y7R-Rz@u3sfxKTC1qyn{I``Ka zRM4VsgOmlf@X6bDCI9UYfjKeKtfOd0Nbdo|NX;0EvvqjJ1W^DccmTRAQX}=&(im?du#e(Do@}S=*^%BP zVB8*E`Z!P@lFw(g`X{II=e7TLXW_H6{IH$!&*^`B2JmM9|F5>yXKngVX>I<0m)h5* z^ikCrjjt@$>?k=Um1YbFuHAe>!RIY6D~zi()RM`u{A!grzhCVwukz`atM+I&)MoyA z6`4P;3eD=)J`aX{j>h%R*x3D_oGJ7obr|f$DD@RdYLml%E_Cr#r?QwNZgoG_e~EiP zkjKRiiMMG&k48SEjY05)OYO&oCm~_X8P+!H@MIeAITIn0q)i5nz_C?tq}U=9ed01y zN`DwV6AbM5byd~-zDw-w#6Stg5PDGdA}ybC>3>mKHXOG5?VKw9jY-URJuLnrOQ>oGJ&5}- zw?#3tq|j}!vVp-gO=Lt&>D=2C;)-J>Gi|i|;fhFIh%$LtM38>!n-!JN{oZ}F)Bi_C bUHPK>=2jb`&*n_9BG@TQJB!j2KmPn*B|yZe literal 0 HcmV?d00001 diff --git a/space-invaders.rom b/space-invaders.rom new file mode 100644 index 0000000000000000000000000000000000000000..606ec01945d665881793becbde201b7292947a9e GIT binary patch literal 8192 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 literal 0 HcmV?d00001 diff --git a/src/emulator2.rs b/src/emulator2.rs new file mode 100644 index 0000000..975c18f --- /dev/null +++ b/src/emulator2.rs @@ -0,0 +1,76 @@ + +const MEMORY_SIZE: usize = 260; + +use lazy_static::lazy_static; +use std::sync::RwLock; +use std::sync::Mutex; +use std::time::Duration; +use std::thread; +use emulator::opcodes::STA; +use crate::log_time; +use once_cell::sync::Lazy; + +struct State { + memory: Vec, + input_1: u8, +} + +static SHARED_STATE: Lazy> = Lazy::new(|| { + RwLock::new(State { + memory: vec![0; MEMORY_SIZE], + input_1: 0, + }) +}); + +trait Emulator { + fn run_one_frame(&self); + fn write_memory(&mut self, address: usize, value: u8); + fn memory(&self) -> Vec; + fn set_input_1(&mut self, value: u8); + fn input_1(&self) -> u8; +} +struct Runner{} + +impl Emulator for Runner { + fn run_one_frame(&self) { + println!("Running one frame"); + std::thread::sleep(Duration::from_millis(500)); + } + + fn write_memory(&mut self, address: usize, value: u8) { + SHARED_STATE.write().unwrap().memory[address] = value; + } + + fn memory(&self) -> Vec { + SHARED_STATE.read().unwrap().memory.to_vec() + } + + fn set_input_1(&mut self, value: u8) { + SHARED_STATE.write().unwrap().input_1 = value; + } + + fn input_1(&self) -> u8 { + SHARED_STATE.read().unwrap().input_1 + } +} + +pub(crate) fn main() { + let mut e2 = Runner{}; + let mut e3 = Runner{}; + let t = thread::spawn(move || { + let mut i = 0; + loop { + e2.run_one_frame(); + println!("Writing {}, input_1: {}", i, e2.input_1()); + e2.write_memory(0, i); + i += 1; + } + }); + loop { + let value = e3.memory()[0]; + let s = format!("============= Memory: {:02x}", value); + e3.set_input_1(value); + log_time(&s); + thread::sleep(Duration::from_millis(500)); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..edbca7b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,13 @@ +use emulator::emulator_state::SharedState; + +use std::time::{UNIX_EPOCH, SystemTime}; + +mod sdl2; + +fn main() -> Result<(), String> { + sdl2::sdl2() +} + +pub fn log_time(s: &str) { + println!("{} {}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() % 100000, s); +} diff --git a/src/sdl2.rs b/src/sdl2.rs new file mode 100644 index 0000000..9a702a0 --- /dev/null +++ b/src/sdl2.rs @@ -0,0 +1,196 @@ +use std::time::{Duration, SystemTime}; +use sdl2::event::Event; +use sdl2::pixels::Color; +use sdl2::keyboard::Keycode; +use sdl2::rect::Rect; +use emulator::memory::Memory; +use std::thread; +use std::sync::Mutex; +use emulator::emulator::{Emulator, WIDTH, HEIGHT}; +use emulator::emulator_state::SharedState; + +// I replaced GREEN with blue color and WHITE with red color. +// I also made RECTANGLE_SIZE double of it's original value cuz window was too small in my opinion. -Biterr +const RECTANGLE_SIZE: u32 = 4; +const WHITE: Color = Color::RGB(128,0,0); +const BLACK: Color = Color::RGB(0, 0, 0); +const RED: Color = Color::RGB(255, 0, 0); +const GREEN: Color = Color::RGB(0, 0, 255); + +pub fn sdl2() -> Result<(), String> { + let sdl_context = sdl2::init()?; + let video_subsystem = sdl_context.video()?; + let window = video_subsystem + .window("", WIDTH as u32 * RECTANGLE_SIZE, HEIGHT as u32 * RECTANGLE_SIZE) + .position_centered() + .resizable() + .build() + .map_err(|e| e.to_string())?; + + let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?; + + canvas.clear(); + canvas.present(); + + // + // Spawn the game logic in a separate thread. This logic will communicate with the + // main thread (and therefore, the actual graphics on your screen) via the `SHARED_STATE` + // object returned by the start of the emilator. + // + let shared_state = Emulator::start_emulator(); + // Hehe + println!("Recolorised and resized by Biterr"); + + canvas.clear(); + canvas.present(); + + // Only update the title every one second or so + let mut last_title_update = SystemTime::now(); + + // Main game loop + let mut event_pump = sdl_context.event_pump()?; + 'running: loop { + for event in event_pump.poll_iter() { + + // + // Read the keyboard + // + match event { + Event::Quit { .. } | Event::KeyDown { keycode: Some(Keycode::Escape), .. } + => break 'running, + // Pause / unpause ('p') + Event::KeyDown { keycode: Some(Keycode::P), .. } => { + let mut l = shared_state.lock().unwrap(); + if l.is_paused() { + l.unpause(); + } else { + l.pause(); + } + }, + + // Insert coin + Event::KeyDown { keycode: Some(Keycode::C), .. } => { + shared_state.lock().unwrap().set_bit_in_1(0, true); + }, + Event::KeyUp { keycode: Some(Keycode::C), .. } => { + shared_state.lock().unwrap().set_bit_in_1(0, false); + }, + // Start 2 players + Event::KeyDown { keycode: Some(Keycode::Num2), .. } => { + shared_state.lock().unwrap().set_bit_in_1(1, true); + }, + Event::KeyUp { keycode: Some(Keycode::Num2), .. } => { + shared_state.lock().unwrap().set_bit_in_1(1, false); + }, + // Start 1 player + Event::KeyDown { keycode: Some(Keycode::Num1), .. } => { + shared_state.lock().unwrap().set_bit_in_1(2, true); + }, + Event::KeyUp { keycode: Some(Keycode::Num1), .. } => { + shared_state.lock().unwrap().set_bit_in_1(2, false); + }, + // Player 1 shot + Event::KeyDown { keycode: Some(Keycode::Space), .. } => { + if shared_state.lock().unwrap().is_paused() { + shared_state.lock().unwrap().unpause(); + } else { + shared_state.lock().unwrap().set_bit_in_1(4, true); + } + }, + Event::KeyUp { keycode: Some(Keycode::Space), .. } => { + shared_state.lock().unwrap().set_bit_in_1(4, false); + }, + // Player 1 move left + Event::KeyDown { keycode: Some(Keycode::Left), .. } => { + shared_state.lock().unwrap().set_bit_in_1(5, true); + }, + Event::KeyUp { keycode: Some(Keycode::Left), .. } => { + shared_state.lock().unwrap().set_bit_in_1(5, false); + }, + // Player 1 move right + Event::KeyDown { keycode: Some(Keycode::Right), .. } => { + shared_state.lock().unwrap().set_bit_in_1(6, true); + }, + Event::KeyUp { keycode: Some(Keycode::Right), .. } => { + shared_state.lock().unwrap().set_bit_in_1(6, false); + }, + + // Player 2 shot ('s') + Event::KeyDown { keycode: Some(Keycode::S), .. } => { + shared_state.lock().unwrap().set_bit_in_2(4, true); + }, + Event::KeyUp { keycode: Some(Keycode::S), .. } => { + shared_state.lock().unwrap().set_bit_in_2(4, false); + }, + // Player 2 move left ('a') + Event::KeyDown { keycode: Some(Keycode::A), .. } => { + shared_state.lock().unwrap().set_bit_in_2(5, true); + }, + Event::KeyUp { keycode: Some(Keycode::A), .. } => { + shared_state.lock().unwrap().set_bit_in_2(5, false); + }, + // Player 2 move right ('d') + Event::KeyDown { keycode: Some(Keycode::D), .. } => { + shared_state.lock().unwrap().set_bit_in_2(6, true); + }, + Event::KeyUp { keycode: Some(Keycode::D), .. } => { + shared_state.lock().unwrap().set_bit_in_2(6, false); + }, + // If the emulator is paused, any key will unpause it + Event::KeyDown { .. } => { + if shared_state.lock().unwrap().is_paused() { + shared_state.lock().unwrap().unpause(); + } + } + _ => { + } + } + } + + // ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 30)); + + canvas.clear(); + + // + // Draw the graphic + // Simply map the listener's frame buffer (updated by the main logic in a separate thread) + // to the SDL canvas + // + let graphic_memory = shared_state.lock().unwrap().graphic_memory(); + let mut i: usize = 0; + for ix in 0..WIDTH { + for iy in (0..HEIGHT).step_by(8) { + let mut byte = graphic_memory[i]; + i += 1; + for b in 0..8 { + let x: i32 = ix as i32 * RECTANGLE_SIZE as i32; + let y: i32 = (HEIGHT as i32 - (iy as i32+ b)) * RECTANGLE_SIZE as i32; + let color = if byte & 1 == 0 { BLACK } else { + if iy > 200 && iy < 220 { RED } + else if iy < 80 { GREEN } + else { WHITE } + }; + byte >>= 1; + + canvas.set_draw_color(color); + canvas.fill_rect(Rect::new(x, y, RECTANGLE_SIZE as u32, RECTANGLE_SIZE as u32)) + .unwrap(); + } + } + } + + if last_title_update.elapsed().unwrap().gt(&Duration::from_millis(1000)) { + let paused = if shared_state.lock().unwrap().is_paused() { " - Paused" } else { "" }; + canvas.window_mut().set_title( + format!("Space Invaders in Rust - Hacked by Biterr - {:.2} Mhz{}", + shared_state.lock().unwrap().get_megahertz(), + paused) + .as_str()).unwrap(); + last_title_update = SystemTime::now(); + } + + canvas.present(); + } + + Ok(()) +}