├── .gitignore ├── mos6502 ├── .gitignore ├── data │ ├── testincl.hex │ └── 6502_functional_test.hex ├── Cargo.toml └── src │ └── lib.rs ├── bluepet ├── src │ ├── lib.rs │ ├── bin │ │ └── add_file.rs │ ├── main.rs │ ├── keyboard │ │ └── mod.rs │ └── storage │ │ └── mod.rs ├── Bluepillpinout.gif ├── data │ └── pet2001-logo.prg ├── char_rom │ └── characters-2.901447-10.bin ├── memory.x ├── .cargo │ └── config ├── Cargo.toml ├── build.rs ├── .gdbinit └── Embed.toml ├── pet ├── rom │ ├── rom-b-c000.bin │ ├── rom-b-d000.bin │ ├── rom-e-e000.bin │ ├── rom-k-f000.bin │ └── char │ │ └── characters-2.901447-10.bin ├── Cargo.toml └── src │ ├── lib.rs │ └── io │ └── mod.rs ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .vscode -------------------------------------------------------------------------------- /mos6502/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /bluepet/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub mod storage; 4 | -------------------------------------------------------------------------------- /pet/rom/rom-b-c000.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bluepet/main/pet/rom/rom-b-c000.bin -------------------------------------------------------------------------------- /pet/rom/rom-b-d000.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bluepet/main/pet/rom/rom-b-d000.bin -------------------------------------------------------------------------------- /pet/rom/rom-e-e000.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bluepet/main/pet/rom/rom-e-e000.bin -------------------------------------------------------------------------------- /pet/rom/rom-k-f000.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bluepet/main/pet/rom/rom-k-f000.bin -------------------------------------------------------------------------------- /bluepet/Bluepillpinout.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bluepet/main/bluepet/Bluepillpinout.gif -------------------------------------------------------------------------------- /mos6502/data/testincl.hex: -------------------------------------------------------------------------------- 1 | :18000000696E74656C2D68657820696E636C7564652066696C650D0A7F 2 | :00000001FF 3 | -------------------------------------------------------------------------------- /bluepet/data/pet2001-logo.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bluepet/main/bluepet/data/pet2001-logo.prg -------------------------------------------------------------------------------- /pet/rom/char/characters-2.901447-10.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bluepet/main/pet/rom/char/characters-2.901447-10.bin -------------------------------------------------------------------------------- /bluepet/char_rom/characters-2.901447-10.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/bluepet/main/bluepet/char_rom/characters-2.901447-10.bin -------------------------------------------------------------------------------- /bluepet/memory.x: -------------------------------------------------------------------------------- 1 | /* Linker script for the STM32F103C8T6 */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 128K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 20K 6 | } -------------------------------------------------------------------------------- /bluepet/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7m-none-eabi] 2 | runner = 'probe-run --chip STM32F103CB' 3 | rustflags = [ 4 | "-C", "link-arg=-Tlink.x" 5 | ] 6 | [build] 7 | target = "thumbv7m-none-eabi" 8 | -------------------------------------------------------------------------------- /mos6502/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mos6502" 3 | version = "0.1.0" 4 | authors = ["bjoern "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /pet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pet" 3 | version = "0.1.0" 4 | authors = ["bjoern "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | mos6502 = { path = "../mos6502" } 11 | -------------------------------------------------------------------------------- /bluepet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bluepet" 3 | version = "0.1.0" 4 | authors = ["bjoern "] 5 | edition = "2018" 6 | 7 | default-run = "main" 8 | 9 | [[bin]] 10 | name = "main" 11 | path = "./src/main.rs" 12 | 13 | 14 | [dependencies] 15 | cortex-m = "0.6.3" 16 | cortex-m-rt = "0.6.13" 17 | stm32f1xx-hal = { version = "0.6.1", features = ["rt", "stm32f103", "medium"] } 18 | embedded-hal = "0.2.4" 19 | rtt-target = { version = "0.2.2", features = ["cortex-m"] } 20 | panic-halt = "0.2.0" 21 | spi-memory = "0.2.0" 22 | pet = { path = "d:/projects/bluepet/pet" } 23 | mos6502 = { path = "d:/projects/bluepet/mos6502" } 24 | -------------------------------------------------------------------------------- /bluepet/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Put the linker script somewhere the linker can find it 8 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 9 | File::create(out.join("memory.x")) 10 | .unwrap() 11 | .write_all(include_bytes!("memory.x")) 12 | .unwrap(); 13 | println!("cargo:rustc-link-search={}", out.display()); 14 | 15 | // Only re-run the build script when memory.x is changed, 16 | // instead of when any part of the source code changes. 17 | println!("cargo:rerun-if-changed=memory.x"); 18 | } 19 | -------------------------------------------------------------------------------- /bluepet/.gdbinit: -------------------------------------------------------------------------------- 1 | target remote :3333 2 | 3 | # print demangled symbols by default 4 | set print asm-demangle on 5 | 6 | #monitor arm semihosting enable 7 | 8 | # # send captured ITM to the file itm.fifo 9 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 10 | # # 8000000 must match the core clock frequency 11 | # monitor tpiu config internal itm.fifo uart off 8000000 12 | 13 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 14 | # # 2000000 is the frequency of the SWO pin 15 | # monitor tpiu config external uart off 8000000 2000000 16 | 17 | # # enable ITM port 0 18 | # monitor itm port 0 on 19 | 20 | load 21 | continue 22 | #step -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Björn Quentin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bluepet/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.probe] 2 | # USB vendor ID 3 | # usb_vid = "1337" 4 | # USB product ID 5 | # usb_pid = "1337" 6 | # Serial number 7 | # serial = "12345678" 8 | # The protocol to be used for communicating with the target. 9 | protocol = "Swd" 10 | # The speed in kHz of the data link to the target. 11 | # speed = 1337 12 | 13 | [default.flashing] 14 | # Whether or not the target should be flashed. 15 | enabled = true 16 | # Whether or not the target should be halted after reset. 17 | # DEPRECATED, moved to reset section 18 | halt_afterwards = false 19 | # Whether or not bytes erased but not rewritten with data from the ELF 20 | # should be restored with their contents before erasing. 21 | restore_unwritten_bytes = false 22 | # The path where an SVG of the assembled flash layout should be written to. 23 | # flash_layout_output_path = "out.svg" 24 | 25 | [default.reset] 26 | # Whether or not the target should be reset. 27 | # When flashing is enabled as well, the target will be reset after flashing. 28 | enabled = true 29 | # Whether or not the target should be halted after reset. 30 | halt_afterwards = false 31 | 32 | [default.general] 33 | # The chip name of the chip to be debugged. 34 | chip = "STM32F103CB" 35 | # A list of chip descriptions to be loaded during runtime. 36 | chip_descriptions = [] 37 | # The default log level to be used. Possible values are one of: 38 | # "OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" 39 | log_level = "WARN" 40 | 41 | [default.rtt] 42 | # Whether or not an RTTUI should be opened after flashing. 43 | # This is exclusive and cannot be used with GDB at the moment. 44 | enabled = false 45 | # A list of channel associations to be displayed. If left empty, all channels are displayed. 46 | channels = [ 47 | # { up = 0, down = 0, name = "name", format = "String" } 48 | ] 49 | # The duration in ms for which the logger should retry to attach to RTT. 50 | timeout = 3000 51 | # Whether timestamps in the RTTUI are enabled 52 | show_timestamps = true 53 | # Whether to save rtt history buffer on exit. 54 | log_enabled = false 55 | # Where to save rtt history buffer relative to manifest path. 56 | log_path = "./logs" 57 | 58 | [default.gdb] 59 | # Whether or not a GDB server should be opened after flashing. 60 | # This is exclusive and cannot be used with RTT at the moment. 61 | enabled = true 62 | # The connection string in host:port format wher the GDB server will open a socket. 63 | gdb_connection_string = "0.0.0.0:3333" 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PET 2001 Emulator on STM32F103 BluePill 2 | 3 | ## What? 4 | 5 | This is a PET 2001 emulator running on a BluePill dev board. Written in Rust (and some inline assembly). 6 | Video is displayed via composite video. The emulated disk drive uses GD25Q64CSIG spi flash. 7 | 8 | It's totally a just for fun personal project. 9 | 10 | The emulation is probably not perfect and definitely pretty slow. As you can see in the video creating a nice replica of the original machine also was a non-goal. My original goal was to test the video output - everything else was just to have something interesting to display. 11 | 12 | Here is a video of it in action: (click to open it on YouTube) 13 | 14 | [![Video](http://img.youtube.com/vi/QxffeJKS6hY/0.jpg)](http://www.youtube.com/watch?v=QxffeJKS6hY "Video") 15 | 16 | If you want to build the code you have to build it with the release profile - it won't work in debug mode. 17 | 18 | ## Pins used 19 | 20 | - PB8 Video to video via 680 ohms resitor 21 | - PB9 Video-Sync to video via 330 ohms resistor 22 | 23 | - PA0 Keyboard Select 1 24 | - PA1 Keyboard Select 2 25 | - PA2 Keyboard Select 3 26 | - PA3 Keyboard Select 4 27 | - PA4 Keyboard Select 5 28 | - PA5 Keyboard Select 6 29 | - PA6 Keyboard Select 7 30 | - PA7 Keyboard Select 8 31 | 32 | - PB0 Keyboard Input 1 33 | - PB1 Keyboard Input 2 34 | - PB10 Keyboard Input 3 35 | - PB11 Keyboard Input 4 36 | - PA8 Keyboard Input 5 37 | - PA10 Keyboard Input 6 38 | - PA11 Keyboard Input 7 39 | - PA15 Keyboard Input 8 40 | - PB3 Keyboard Input 9 41 | - PB4 Keyboard Shift 42 | 43 | SPI FLASH 44 | - SCK = PB13 = 6 45 | - MISO = PB14 = 2 46 | - MOSI = PB15 = 5 47 | - GND = 4 48 | - VCC 3.3V = 8 49 | - HOLD -> VCC -> 7 50 | - WP -> VCC -> 3 51 | - CS = PA9 = 1 52 | 53 | ## Video 54 | 55 | I used these sites to learn about this: 56 | - http://www.batsocks.co.uk/readme/video_timing.htm 57 | - https://github.com/eprive/STM32Lvideo 58 | - https://github.com/eprive/STM32Lvideo/wiki 59 | 60 | PB9 is controlled by TIM4 61 | 62 | TIM1 is used as a "shock absorber" as outlined in https://github.com/abelykh0/VGA-demo-on-bluepill 63 | 64 | ## Disk Emulation 65 | 66 | This uses the SPI flash to save / load programs. In bluepet/src/bin/add_files.rs is some code to add a PET program. 67 | Unfortunately there isn't enough free memory to use the full 4k blocks of the flash so I "waste" 3/4 of each block unfortunately. 68 | 69 | ## Keyboard 70 | 71 | It's a usual matrix keyboard with 8 select lines and 9 data lines plus the shift keys (both connected to the same pin). I totally mixed up wires during soldering so the keyboard mapping in the code is somewhat odd. 72 | 73 | ## Code Organization 74 | 75 | There are three crates 76 | - mos6502 - the CPU emulation 77 | - pet - other hardware emulation plus some trait definitions for the hardware emulation 78 | - bluepet - the actual emulator using the other crates - implements the traits from the pet crate to use the given hardware 79 | -------------------------------------------------------------------------------- /bluepet/src/bin/add_file.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | use rtt_target::{rprintln, rtt_init_print}; 7 | 8 | use cortex_m_rt::entry; 9 | 10 | use bluepet::storage::FlashStorage; 11 | use pet::io::Storage; 12 | 13 | use stm32f1xx_hal::spi::{Mode, Phase, Polarity, Spi}; 14 | use stm32f1xx_hal::{pac, prelude::*}; 15 | 16 | use embedded_hal::digital::v2::OutputPin; 17 | use embedded_hal::spi::MODE_0; 18 | 19 | use spi_memory::prelude::*; 20 | use spi_memory::series25::Flash; 21 | 22 | const FILEDATA: &[u8; 1992] = include_bytes!("../../data/pet2001-logo.prg"); 23 | 24 | #[entry] 25 | fn main() -> ! { 26 | rtt_init_print!(); 27 | 28 | // Get access to the core peripherals from the cortex-m crate 29 | let _cp = cortex_m::Peripherals::take().unwrap(); 30 | // Get access to the device specific peripherals from the peripheral access crate 31 | let dp = pac::Peripherals::take().unwrap(); 32 | 33 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 34 | // HAL structs 35 | let mut flash = dp.FLASH.constrain(); 36 | let mut rcc = dp.RCC.constrain(); 37 | 38 | let clocks = rcc 39 | .cfgr 40 | .use_hse(8.mhz()) 41 | .sysclk(72.mhz()) 42 | .pclk1(36.mhz()) 43 | .pclk2(72.mhz()) 44 | .freeze(&mut flash.acr); 45 | 46 | let mut afio = dp.AFIO.constrain(&mut rcc.apb2); 47 | let mut gpioa = dp.GPIOA.split(&mut rcc.apb2); 48 | let mut gpiob = dp.GPIOB.split(&mut rcc.apb2); 49 | let (_pa15,_pb33,_pb4b4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4); 50 | 51 | // configure SPI for SPI Flash 52 | let cs = { 53 | let mut cs = gpioa.pa9.into_push_pull_output(&mut gpioa.crh); 54 | cs.set_high().unwrap(); // deselect 55 | cs 56 | }; 57 | 58 | let pins = ( 59 | gpiob.pb13.into_alternate_push_pull(&mut gpiob.crh), 60 | gpiob.pb14.into_floating_input(&mut gpiob.crh), 61 | gpiob.pb15.into_alternate_push_pull(&mut gpiob.crh), 62 | ); 63 | 64 | let spi = Spi::spi2(dp.SPI2, pins, MODE_0, 4.mhz(), clocks, &mut rcc.apb1); 65 | let mut spi_flash = Flash::init(spi, cs).unwrap(); 66 | 67 | spi_flash.erase_all().unwrap_or_default(); 68 | let mut file_storage = FlashStorage::new(spi_flash); 69 | 70 | 71 | let filename = "HELLO"; 72 | 73 | file_storage.start_filename(); 74 | 75 | for c in filename.chars() { 76 | file_storage.next_filename_byte(c as u8); 77 | } 78 | 79 | file_storage.start_save(); 80 | 81 | let mut i = 0; 82 | for b in FILEDATA { 83 | file_storage.save_data_byte(i, *b); 84 | i += 1; 85 | } 86 | file_storage.end_save(); 87 | 88 | rprintln!("done. writen file as {}", filename); 89 | 90 | file_storage.ensure_directory(); 91 | for entry in &file_storage.directory { 92 | rprintln!("{:?}", entry); 93 | } 94 | loop {} 95 | } 96 | -------------------------------------------------------------------------------- /bluepet/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | 5 | use panic_halt as _; 6 | use rtt_target::{rprintln, rtt_init_print}; 7 | 8 | use cortex_m_rt::entry; 9 | use mos6502::Cpu; 10 | use pet::Ram; 11 | 12 | mod keyboard; 13 | mod storage; 14 | mod video; 15 | 16 | use storage::FlashStorage; 17 | 18 | use stm32f1xx_hal::spi::{Mode, Phase, Polarity, Spi}; 19 | use stm32f1xx_hal::{pac, prelude::*}; 20 | 21 | use embedded_hal::digital::v2::OutputPin; 22 | use embedded_hal::spi::MODE_0; 23 | 24 | use spi_memory::series25::Flash; 25 | 26 | #[entry] 27 | fn main() -> ! { 28 | rtt_init_print!(); 29 | 30 | // Get access to the core peripherals from the cortex-m crate 31 | let mut cp = cortex_m::Peripherals::take().unwrap(); 32 | // Get access to the device specific peripherals from the peripheral access crate 33 | let dp = pac::Peripherals::take().unwrap(); 34 | 35 | // Take ownership over the raw flash and rcc devices and convert them into the corresponding 36 | // HAL structs 37 | let mut flash = dp.FLASH.constrain(); 38 | let mut rcc = dp.RCC.constrain(); 39 | 40 | let clocks = rcc 41 | .cfgr 42 | .use_hse(8.mhz()) 43 | .sysclk(72.mhz()) 44 | .pclk1(36.mhz()) 45 | .pclk2(72.mhz()) 46 | .freeze(&mut flash.acr); 47 | 48 | let mut afio = dp.AFIO.constrain(&mut rcc.apb2); 49 | let mut gpioa = dp.GPIOA.split(&mut rcc.apb2); 50 | let mut gpiob = dp.GPIOB.split(&mut rcc.apb2); 51 | let (pa15, pb3, pb4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4); 52 | 53 | // configure video pins 54 | let _pb8 = gpiob.pb8.into_push_pull_output(&mut gpiob.crh); 55 | let _pb9 = gpiob.pb9.into_alternate_push_pull(&mut gpiob.crh); // timer controlled 56 | 57 | // configure keyboard pins 58 | let mut pa0 = gpioa.pa0.into_push_pull_output(&mut gpioa.crl); 59 | let mut pa1 = gpioa.pa1.into_push_pull_output(&mut gpioa.crl); 60 | let mut pa2 = gpioa.pa2.into_push_pull_output(&mut gpioa.crl); 61 | let mut pa3 = gpioa.pa3.into_push_pull_output(&mut gpioa.crl); 62 | let mut pa4 = gpioa.pa4.into_push_pull_output(&mut gpioa.crl); 63 | let mut pa5 = gpioa.pa5.into_push_pull_output(&mut gpioa.crl); 64 | let mut pa6 = gpioa.pa6.into_push_pull_output(&mut gpioa.crl); 65 | let mut pa7 = gpioa.pa7.into_push_pull_output(&mut gpioa.crl); 66 | 67 | let pb0 = gpiob.pb0.into_pull_down_input(&mut gpiob.crl); 68 | let pb1 = gpiob.pb1.into_pull_down_input(&mut gpiob.crl); 69 | let pb10 = gpiob.pb10.into_pull_down_input(&mut gpiob.crh); 70 | let pb11 = gpiob.pb11.into_pull_down_input(&mut gpiob.crh); 71 | let pa8 = gpioa.pa8.into_pull_down_input(&mut gpioa.crh); 72 | let pa10 = gpioa.pa10.into_pull_down_input(&mut gpioa.crh); 73 | let pa11 = gpioa.pa11.into_pull_down_input(&mut gpioa.crh); 74 | let pa15 = pa15.into_pull_down_input(&mut gpioa.crh); 75 | let pb3 = pb3.into_pull_down_input(&mut gpiob.crl); 76 | 77 | // PB4 = shift 78 | let pb4 = pb4.into_pull_down_input(&mut gpiob.crl); 79 | 80 | // configure SPI for SPI Flash 81 | let cs = { 82 | let mut cs = gpioa.pa9.into_push_pull_output(&mut gpioa.crh); 83 | cs.set_high().unwrap(); // deselect 84 | cs 85 | }; 86 | 87 | let pins = ( 88 | gpiob.pb13.into_alternate_push_pull(&mut gpiob.crh), 89 | gpiob.pb14.into_floating_input(&mut gpiob.crh), 90 | gpiob.pb15.into_alternate_push_pull(&mut gpiob.crh), 91 | ); 92 | 93 | let spi = Spi::spi2(dp.SPI2, pins, MODE_0, 4.mhz(), clocks, &mut rcc.apb1); 94 | 95 | // for testing use the onboard led 96 | let mut gpioc = dp.GPIOC.split(&mut rcc.apb2); 97 | let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); 98 | 99 | let spi_flash = Flash::init(spi, cs).unwrap(); 100 | 101 | //spi_flash.erase_all(); 102 | let mut file_storage = FlashStorage::new(spi_flash); 103 | 104 | // prepare video stuff 105 | video::init_video(&mut cp, dp.TIM4, dp.TIM1); 106 | 107 | // init the emulator stuff 108 | let mut ram = [0u8; 8192]; 109 | 110 | let mem = unsafe { Ram::new(&mut ram, &mut video::VID_RAM, &mut file_storage) }; 111 | let mut cpu = Cpu::new(&mem); 112 | cpu.reset(); 113 | 114 | // emulation 115 | let mut cycle_cnt: u64 = 0; 116 | let mut tick_cntr = 0u32; 117 | 118 | let mut keyboard_cnt = 0; 119 | 120 | led.set_high().unwrap_or_default(); // on board LED off 121 | 122 | // start video output 123 | video::start_video(); 124 | 125 | loop { 126 | if cycle_cnt == 0 { 127 | cycle_cnt = cpu.step(); 128 | cycle_cnt -= 1; 129 | } else { 130 | cycle_cnt -= 1; 131 | } 132 | 133 | tick_cntr += 1; 134 | if tick_cntr >= 1000 { 135 | tick_cntr = 0; 136 | let trigger_irq = mem.io.borrow_mut().tick(); 137 | if trigger_irq { 138 | cpu.trigger_irq(); 139 | } 140 | } 141 | 142 | keyboard_cnt += 1; 143 | if keyboard_cnt > 20830 { 144 | keyboard_cnt = 0; 145 | 146 | keyboard::handle_keyboard( 147 | &mut pa0, 148 | &mut pa1, 149 | &mut pa2, 150 | &mut pa3, 151 | &mut pa4, 152 | &mut pa5, 153 | &mut pa6, 154 | &mut pa7, 155 | &pb0, 156 | &pb1, 157 | &pb10, 158 | &pb11, 159 | &pa8, 160 | &pa10, 161 | &pa11, 162 | &pa15, 163 | &pb3, 164 | &pb4, 165 | &mut (mem.io.borrow_mut().keyboard), 166 | ); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /bluepet/src/keyboard/mod.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::v2::InputPin; 2 | use embedded_hal::digital::v2::OutputPin; 3 | 4 | pub fn handle_keyboard( 5 | out0: &mut O0, 6 | out1: &mut O1, 7 | out2: &mut O2, 8 | out3: &mut O3, 9 | out4: &mut O4, 10 | out5: &mut O5, 11 | out6: &mut O6, 12 | out7: &mut O7, 13 | in0: &I0, 14 | in1: &I1, 15 | in2: &I2, 16 | in3: &I3, 17 | in4: &I4, 18 | in5: &I5, 19 | in6: &I6, 20 | in7: &I7, 21 | in8: &I8, 22 | 23 | shift: &SHIFT, 24 | 25 | keyboard: &mut pet::io::Keyboard, 26 | ) where 27 | O0: OutputPin, 28 | O1: OutputPin, 29 | O2: OutputPin, 30 | O3: OutputPin, 31 | O4: OutputPin, 32 | O5: OutputPin, 33 | O6: OutputPin, 34 | O7: OutputPin, 35 | I0: InputPin, 36 | I1: InputPin, 37 | I2: InputPin, 38 | I3: InputPin, 39 | I4: InputPin, 40 | I5: InputPin, 41 | I6: InputPin, 42 | I7: InputPin, 43 | I8: InputPin, 44 | 45 | SHIFT: InputPin, 46 | { 47 | let keycode = check_keyboard( 48 | out0, out1, out2, out3, out4, out5, out6, out7, in0, in1, in2, in3, in4, in5, in6, in7, in8, 49 | ); 50 | 51 | let row_col = convert_keycode(keycode); 52 | 53 | if row_col == 0xff { 54 | for i in 0..0x9f { 55 | keyboard.key_up(i); 56 | } 57 | } 58 | 59 | if row_col != 0xff { 60 | keyboard.key_down(row_col); 61 | } 62 | 63 | // 85 = shift left 64 | if shift.is_high().unwrap_or_default() { 65 | keyboard.key_down(0x85); 66 | } else { 67 | keyboard.key_up(0x85); 68 | } 69 | } 70 | 71 | fn convert_keycode(hw_code: u8) -> u8 { 72 | match hw_code { 73 | 1 => 0x00, 74 | 2 => 0x10, 75 | 3 => 0x01, 76 | 4 => 0x11, 77 | 5 => 0x02, 78 | 6 => 0x12, 79 | 7 => 0x03, 80 | 8 => 0x13, 81 | 9 => 0x04, 82 | 10 => 0x14, 83 | 11 => 0x05, 84 | 12 => 0x06, 85 | 13 => 0x07, 86 | 14 => 0x16, 87 | 15 => 0x36, 88 | 16 => 0x17, 89 | 17 => 0x37, 90 | 18 => 0x27, 91 | 19 => 0x26, 92 | 20 => 0x25, 93 | 21 => 0x34, 94 | 22 => 0x24, 95 | 23 => 0x33, 96 | 24 => 0x31, 97 | 25 => 0x23, 98 | 26 => 0x32, 99 | 27 => 0x22, 100 | 28 => 0x21, 101 | 29 => 0x30, 102 | 30 => 0x20, 103 | 31 => 0x57, 104 | 32 => 0x47, 105 | 33 => 0x44, 106 | 34 => 0x56, 107 | 35 => 0x46, 108 | 36 => 0x54, 109 | 37 => 0x53, 110 | 38 => 0x43, 111 | 39 => 0x52, 112 | 40 => 0x42, 113 | 41 => 0x51, 114 | 42 => 0x77, 115 | 43 => 0x41, 116 | 44 => 0x50, 117 | 45 => 0x40, 118 | 46 => 0x67, 119 | 47 => 0x76, 120 | 48 => 0x66, 121 | 49 => 0x65, 122 | 50 => 0x74, 123 | 51 => 0x63, 124 | 52 => 0x64, 125 | 53 => 0x73, 126 | 54 => 0x72, 127 | 55 => 0x62, 128 | 56 => 0x71, 129 | 57 => 0x61, 130 | 58 => 0x70, 131 | 59 => 0x97, 132 | 60 => 0x86, 133 | 61 => 0x87, 134 | 62 => 0x96, 135 | 63 => 0x60, 136 | 64 => 0x94, 137 | 65 => 0x84, 138 | 66 => 0x93, 139 | 67 => 0x92, 140 | 68 => 0x91, 141 | 69 => 0x84, 142 | 70 => 0x81, 143 | 71 => 0x90, 144 | 72 => 0x82, 145 | _ => 0xff, 146 | } 147 | } 148 | 149 | fn check_keyboard( 150 | out0: &mut O0, 151 | out1: &mut O1, 152 | out2: &mut O2, 153 | out3: &mut O3, 154 | out4: &mut O4, 155 | out5: &mut O5, 156 | out6: &mut O6, 157 | out7: &mut O7, 158 | in0: &I0, 159 | in1: &I1, 160 | in2: &I2, 161 | in3: &I3, 162 | in4: &I4, 163 | in5: &I5, 164 | in6: &I6, 165 | in7: &I7, 166 | in8: &I8, 167 | ) -> u8 168 | where 169 | O0: OutputPin, 170 | O1: OutputPin, 171 | O2: OutputPin, 172 | O3: OutputPin, 173 | O4: OutputPin, 174 | O5: OutputPin, 175 | O6: OutputPin, 176 | O7: OutputPin, 177 | I0: InputPin, 178 | I1: InputPin, 179 | I2: InputPin, 180 | I3: InputPin, 181 | I4: InputPin, 182 | I5: InputPin, 183 | I6: InputPin, 184 | I7: InputPin, 185 | I8: InputPin, 186 | { 187 | let mut res = 0; 188 | 189 | out0.set_high().unwrap_or_default(); 190 | out1.set_low().unwrap_or_default(); 191 | out2.set_low().unwrap_or_default(); 192 | out3.set_low().unwrap_or_default(); 193 | out4.set_low().unwrap_or_default(); 194 | out5.set_low().unwrap_or_default(); 195 | out6.set_low().unwrap_or_default(); 196 | out7.set_low().unwrap_or_default(); 197 | if in0.is_high().unwrap_or_default() { 198 | res = 1; 199 | } 200 | if in1.is_high().unwrap_or_default() { 201 | res = 2; 202 | } 203 | if in2.is_high().unwrap_or_default() { 204 | res = 3; 205 | } 206 | if in3.is_high().unwrap_or_default() { 207 | res = 4; 208 | } 209 | if in4.is_high().unwrap_or_default() { 210 | res = 5; 211 | } 212 | if in5.is_high().unwrap_or_default() { 213 | res = 6; 214 | } 215 | if in6.is_high().unwrap_or_default() { 216 | res = 7; 217 | } 218 | if in7.is_high().unwrap_or_default() { 219 | res = 8; 220 | } 221 | if in8.is_high().unwrap_or_default() { 222 | res = 9; 223 | } 224 | out0.set_low().unwrap_or_default(); 225 | 226 | out1.set_high().unwrap_or_default(); 227 | out0.set_low().unwrap_or_default(); 228 | out2.set_low().unwrap_or_default(); 229 | out3.set_low().unwrap_or_default(); 230 | out4.set_low().unwrap_or_default(); 231 | out5.set_low().unwrap_or_default(); 232 | out6.set_low().unwrap_or_default(); 233 | out7.set_low().unwrap_or_default(); 234 | if in0.is_high().unwrap_or_default() { 235 | res = 10; 236 | } 237 | if in1.is_high().unwrap_or_default() { 238 | res = 11; 239 | } 240 | if in2.is_high().unwrap_or_default() { 241 | res = 12; 242 | } 243 | if in3.is_high().unwrap_or_default() { 244 | res = 13; 245 | } 246 | if in4.is_high().unwrap_or_default() { 247 | res = 14; 248 | } 249 | if in5.is_high().unwrap_or_default() { 250 | res = 15; 251 | } 252 | if in6.is_high().unwrap_or_default() { 253 | res = 16; 254 | } 255 | if in7.is_high().unwrap_or_default() { 256 | res = 17; 257 | } 258 | if in8.is_high().unwrap_or_default() { 259 | res = 18; 260 | } 261 | out1.set_low().unwrap_or_default(); 262 | 263 | out2.set_high().unwrap_or_default(); 264 | out0.set_low().unwrap_or_default(); 265 | out1.set_low().unwrap_or_default(); 266 | out3.set_low().unwrap_or_default(); 267 | out4.set_low().unwrap_or_default(); 268 | out5.set_low().unwrap_or_default(); 269 | out6.set_low().unwrap_or_default(); 270 | out7.set_low().unwrap_or_default(); 271 | if in0.is_high().unwrap_or_default() { 272 | res = 19; 273 | } 274 | if in1.is_high().unwrap_or_default() { 275 | res = 20; 276 | } 277 | if in2.is_high().unwrap_or_default() { 278 | res = 21; 279 | } 280 | if in3.is_high().unwrap_or_default() { 281 | res = 22; 282 | } 283 | if in4.is_high().unwrap_or_default() { 284 | res = 23; 285 | } 286 | if in5.is_high().unwrap_or_default() { 287 | res = 24; 288 | } 289 | if in6.is_high().unwrap_or_default() { 290 | res = 25; 291 | } 292 | if in7.is_high().unwrap_or_default() { 293 | res = 26; 294 | } 295 | if in8.is_high().unwrap_or_default() { 296 | res = 27; 297 | } 298 | out2.set_low().unwrap_or_default(); 299 | 300 | out3.set_high().unwrap_or_default(); 301 | out0.set_low().unwrap_or_default(); 302 | out2.set_low().unwrap_or_default(); 303 | out1.set_low().unwrap_or_default(); 304 | out4.set_low().unwrap_or_default(); 305 | out5.set_low().unwrap_or_default(); 306 | out6.set_low().unwrap_or_default(); 307 | out7.set_low().unwrap_or_default(); 308 | if in0.is_high().unwrap_or_default() { 309 | res = 28; 310 | } 311 | if in1.is_high().unwrap_or_default() { 312 | res = 29; 313 | } 314 | if in2.is_high().unwrap_or_default() { 315 | res = 30; 316 | } 317 | if in3.is_high().unwrap_or_default() { 318 | res = 31; 319 | } 320 | if in4.is_high().unwrap_or_default() { 321 | res = 32; 322 | } 323 | if in5.is_high().unwrap_or_default() { 324 | res = 33; 325 | } 326 | if in6.is_high().unwrap_or_default() { 327 | res = 34; 328 | } 329 | if in7.is_high().unwrap_or_default() { 330 | res = 35; 331 | } 332 | if in8.is_high().unwrap_or_default() { 333 | res = 36; 334 | } 335 | out3.set_low().unwrap_or_default(); 336 | 337 | out4.set_high().unwrap_or_default(); 338 | out0.set_low().unwrap_or_default(); 339 | out2.set_low().unwrap_or_default(); 340 | out3.set_low().unwrap_or_default(); 341 | out1.set_low().unwrap_or_default(); 342 | out5.set_low().unwrap_or_default(); 343 | out6.set_low().unwrap_or_default(); 344 | out7.set_low().unwrap_or_default(); 345 | if in0.is_high().unwrap_or_default() { 346 | res = 37; 347 | } 348 | if in1.is_high().unwrap_or_default() { 349 | res = 38; 350 | } 351 | if in2.is_high().unwrap_or_default() { 352 | res = 39; 353 | } 354 | if in3.is_high().unwrap_or_default() { 355 | res = 40; 356 | } 357 | if in4.is_high().unwrap_or_default() { 358 | res = 41; 359 | } 360 | if in5.is_high().unwrap_or_default() { 361 | res = 42; 362 | } 363 | if in6.is_high().unwrap_or_default() { 364 | res = 43; 365 | } 366 | if in7.is_high().unwrap_or_default() { 367 | res = 44; 368 | } 369 | if in8.is_high().unwrap_or_default() { 370 | res = 45; 371 | } 372 | out4.set_low().unwrap_or_default(); 373 | 374 | out5.set_high().unwrap_or_default(); 375 | out0.set_low().unwrap_or_default(); 376 | out2.set_low().unwrap_or_default(); 377 | out3.set_low().unwrap_or_default(); 378 | out4.set_low().unwrap_or_default(); 379 | out1.set_low().unwrap_or_default(); 380 | out6.set_low().unwrap_or_default(); 381 | out7.set_low().unwrap_or_default(); 382 | if in0.is_high().unwrap_or_default() { 383 | res = 46; 384 | } 385 | if in1.is_high().unwrap_or_default() { 386 | res = 47; 387 | } 388 | if in2.is_high().unwrap_or_default() { 389 | res = 48; 390 | } 391 | if in3.is_high().unwrap_or_default() { 392 | res = 49; 393 | } 394 | if in4.is_high().unwrap_or_default() { 395 | res = 50; 396 | } 397 | if in5.is_high().unwrap_or_default() { 398 | res = 51; 399 | } 400 | if in6.is_high().unwrap_or_default() { 401 | res = 52; 402 | } 403 | if in7.is_high().unwrap_or_default() { 404 | res = 53; 405 | } 406 | if in8.is_high().unwrap_or_default() { 407 | res = 54; 408 | } 409 | out5.set_low().unwrap_or_default(); 410 | 411 | out6.set_high().unwrap_or_default(); 412 | out0.set_low().unwrap_or_default(); 413 | out2.set_low().unwrap_or_default(); 414 | out3.set_low().unwrap_or_default(); 415 | out4.set_low().unwrap_or_default(); 416 | out5.set_low().unwrap_or_default(); 417 | out1.set_low().unwrap_or_default(); 418 | out7.set_low().unwrap_or_default(); 419 | if in0.is_high().unwrap_or_default() { 420 | res = 55; 421 | } 422 | if in1.is_high().unwrap_or_default() { 423 | res = 56; 424 | } 425 | if in2.is_high().unwrap_or_default() { 426 | res = 57; 427 | } 428 | if in3.is_high().unwrap_or_default() { 429 | res = 58; 430 | } 431 | if in4.is_high().unwrap_or_default() { 432 | res = 59; 433 | } 434 | if in5.is_high().unwrap_or_default() { 435 | res = 60; 436 | } 437 | if in6.is_high().unwrap_or_default() { 438 | res = 61; 439 | } 440 | if in7.is_high().unwrap_or_default() { 441 | res = 62; 442 | } 443 | if in8.is_high().unwrap_or_default() { 444 | res = 63; 445 | } 446 | out6.set_low().unwrap_or_default(); 447 | 448 | out7.set_high().unwrap_or_default(); 449 | out0.set_low().unwrap_or_default(); 450 | out2.set_low().unwrap_or_default(); 451 | out3.set_low().unwrap_or_default(); 452 | out4.set_low().unwrap_or_default(); 453 | out5.set_low().unwrap_or_default(); 454 | out6.set_low().unwrap_or_default(); 455 | out1.set_low().unwrap_or_default(); 456 | if in0.is_high().unwrap_or_default() { 457 | res = 64; 458 | } 459 | if in1.is_high().unwrap_or_default() { 460 | res = 65; 461 | } 462 | if in2.is_high().unwrap_or_default() { 463 | res = 66; 464 | } 465 | if in3.is_high().unwrap_or_default() { 466 | res = 67; 467 | } 468 | if in4.is_high().unwrap_or_default() { 469 | res = 68; 470 | } 471 | if in5.is_high().unwrap_or_default() { 472 | res = 69; 473 | } 474 | if in6.is_high().unwrap_or_default() { 475 | res = 70; 476 | } 477 | if in7.is_high().unwrap_or_default() { 478 | res = 71; 479 | } 480 | if in8.is_high().unwrap_or_default() { 481 | res = 72; 482 | } 483 | out7.set_low().unwrap_or_default(); 484 | 485 | res 486 | } 487 | -------------------------------------------------------------------------------- /bluepet/src/storage/mod.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Index; 2 | 3 | use embedded_hal::blocking::spi::Transfer; 4 | use spi_memory::prelude::*; 5 | use spi_memory::series25::Flash; 6 | 7 | use embedded_hal::digital::v2::OutputPin; 8 | 9 | #[derive(Debug, Copy, Clone)] 10 | struct Filename { 11 | filename: [u8; 16], 12 | } 13 | 14 | impl Filename { 15 | fn empty() -> Filename { 16 | Filename { 17 | filename: [0u8; 16], 18 | } 19 | } 20 | 21 | fn new(name: [u8; 16]) -> Filename { 22 | Filename { filename: name } 23 | } 24 | 25 | fn byte_at(&self, index: usize) -> u8 { 26 | self.filename[index] 27 | } 28 | } 29 | 30 | impl PartialEq for Filename { 31 | fn eq(&self, other: &Self) -> bool { 32 | for i in 0usize..16usize { 33 | if self.filename[i] != other.filename[i] { 34 | return false; 35 | } 36 | } 37 | 38 | true 39 | } 40 | } 41 | 42 | impl Index for Filename { 43 | type Output = u8; 44 | 45 | fn index(&self, index: usize) -> &u8 { 46 | &self.filename[index] 47 | } 48 | } 49 | 50 | #[derive(Debug, Copy, Clone)] 51 | pub struct FileEntry { 52 | filename: Filename, 53 | address: u32, 54 | len: u16, 55 | } 56 | 57 | impl FileEntry { 58 | fn empty() -> FileEntry { 59 | FileEntry { 60 | filename: Filename::empty(), 61 | address: 0, 62 | len: 0, 63 | } 64 | } 65 | 66 | fn new(filename: Filename, address: u32, len: u16) -> FileEntry { 67 | FileEntry { 68 | filename: filename, 69 | address: address, 70 | len: len, 71 | } 72 | } 73 | 74 | } 75 | 76 | struct NotFound; 77 | 78 | const BUFFER_SIZE: usize = 1024; // not enough RAM to use 4096 bytes of buffer 79 | const SECTOR_SIZE: usize = 4096; 80 | const MAX_FILENAME_LEN: usize = 16; 81 | const MAX_FILES: usize = 46; 82 | 83 | const MAX_FILE_SIZE: usize = 8192; 84 | const DIRECTORY_BYTES: usize = 1024; 85 | 86 | const DIR_ENTRY_BASIC_LINE_SIZE: usize = 4 + 2 + 1 + 16 + 1 + 1; 87 | 88 | pub struct FlashStorage, CS: OutputPin> { 89 | flash: Flash, 90 | 91 | filename: [u8; MAX_FILENAME_LEN], 92 | filename_index: usize, 93 | 94 | buffer: [u8; BUFFER_SIZE], 95 | buffer_valid: bool, 96 | buffer_has_save_data: bool, 97 | 98 | flash_address: u32, 99 | buffer_address: u32, 100 | 101 | pub directory: [FileEntry; MAX_FILES], 102 | directory_loaded: bool, 103 | 104 | current_fileno: usize, 105 | 106 | file_byte_count: usize, 107 | 108 | directory_valid_entries: usize, 109 | 110 | loading_directory: bool, 111 | loading_directory_fileno: usize, 112 | loading_directory_filename_index: usize, 113 | loading_directory_fileindex: usize, 114 | } 115 | 116 | 117 | impl, CS: OutputPin> FlashStorage { 118 | pub fn new(spi_flash: Flash) -> FlashStorage { 119 | FlashStorage { 120 | flash: spi_flash, 121 | 122 | filename: [0u8; MAX_FILENAME_LEN], 123 | filename_index: 0usize, 124 | 125 | buffer: [0u8; BUFFER_SIZE], 126 | buffer_valid: false, 127 | buffer_has_save_data: false, 128 | 129 | flash_address: 0u32, 130 | buffer_address: 0u32, 131 | 132 | directory: [FileEntry::empty(); MAX_FILES], 133 | directory_loaded: false, 134 | 135 | current_fileno: 0usize, 136 | 137 | file_byte_count: 0usize, 138 | 139 | directory_valid_entries: 0, 140 | 141 | loading_directory: false, 142 | loading_directory_fileno: 0, 143 | loading_directory_filename_index: 0, 144 | loading_directory_fileindex: 0, 145 | } 146 | } 147 | 148 | fn flush(&mut self) { 149 | self.flash.erase_sectors(self.buffer_address, 1).unwrap_or_default(); 150 | self.flash.write_bytes(self.buffer_address, &mut self.buffer).unwrap_or_default(); 151 | self.flash.read(self.buffer_address, &mut self.buffer).unwrap_or_default(); 152 | self.buffer_has_save_data = false; 153 | } 154 | 155 | fn put_byte(&mut self, address: u32, value: u8) { 156 | let flash_address = address % BUFFER_SIZE as u32 + (address / BUFFER_SIZE as u32) * SECTOR_SIZE as u32; 157 | if flash_address < self.buffer_address 158 | || flash_address > self.buffer_address + self.buffer.len() as u32 159 | || !self.buffer_valid || !self.buffer_has_save_data 160 | { 161 | if self.buffer_has_save_data { 162 | self.flush(); 163 | } 164 | 165 | for i in 0..self.buffer.len() { 166 | self.buffer[i] = 0u8; 167 | } 168 | 169 | self.buffer_address = (address / BUFFER_SIZE as u32) * SECTOR_SIZE as u32; 170 | self.flash 171 | .read(self.buffer_address, &mut self.buffer) 172 | .unwrap_or_default(); 173 | self.buffer_valid = true; 174 | self.buffer_has_save_data = true; 175 | } 176 | 177 | self.buffer[(flash_address - self.buffer_address) as usize] = value; 178 | } 179 | 180 | fn put_word(&mut self, address: u32, value: u32) { 181 | self.put_byte(address, (value >> 24) as u8); 182 | self.put_byte(address+1, (value >> 16) as u8); 183 | self.put_byte(address+2, (value >> 8) as u8); 184 | self.put_byte(address+3, value as u8); 185 | } 186 | 187 | fn put_hword(&mut self, address: u32, value: u16) { 188 | self.put_byte(address, (value >> 8) as u8); 189 | self.put_byte(address+1, value as u8); 190 | } 191 | 192 | fn get_byte(&mut self, address: u32) -> u8 { 193 | let flash_address = address % BUFFER_SIZE as u32 + (address / BUFFER_SIZE as u32) * SECTOR_SIZE as u32; 194 | if flash_address < self.buffer_address 195 | || flash_address > self.buffer_address + self.buffer.len() as u32 196 | || !self.buffer_valid 197 | { 198 | if self.buffer_has_save_data { 199 | self.flush(); 200 | } 201 | 202 | self.buffer_address = (address / BUFFER_SIZE as u32) * SECTOR_SIZE as u32; 203 | self.flash 204 | .read(self.buffer_address, &mut self.buffer) 205 | .unwrap_or_default(); 206 | self.buffer_valid = true; 207 | self.buffer_has_save_data = false; 208 | } 209 | 210 | self.buffer[(flash_address - self.buffer_address) as usize] 211 | } 212 | 213 | fn get_word(&mut self, address: u32) -> u32 { 214 | ((self.get_byte(address) as u32) << 24) 215 | + ((self.get_byte(address + 1) as u32) << 16) 216 | + ((self.get_byte(address + 2) as u32) << 8) 217 | + (self.get_byte(address + 3) as u32) 218 | } 219 | 220 | fn get_hword(&mut self, address: u32) -> u16 { 221 | ((self.get_byte(address) as u16) << 8) + (self.get_byte(address + 1) as u16) 222 | } 223 | 224 | fn find_file(&mut self, file_name: [u8; 16]) -> Result<(FileEntry, usize), NotFound> { 225 | self.ensure_directory(); 226 | 227 | let file_to_find = Filename::new(file_name); 228 | for i in 0..46 { 229 | if self.directory[i].filename == file_to_find { 230 | return Ok( (self.directory[i],i) ); 231 | } 232 | } 233 | 234 | Err(NotFound {}) 235 | } 236 | 237 | pub fn ensure_directory(&mut self) { 238 | if !self.directory_loaded { 239 | let mut tmp_directory = [FileEntry::empty(); MAX_FILES]; 240 | for i in 0..MAX_FILES { 241 | let mut filename = [0u8; 16]; 242 | for j in 0usize..16usize { 243 | filename[j] = 244 | self.get_byte((0 as u32 + i as u32 * 22 as u32 + j as u32) as u32); 245 | } 246 | let file_address = self.get_word((0 + i * 22 + 16) as u32); 247 | let file_len = self.get_hword((0 + i * 22 + 20) as u32); 248 | tmp_directory[i] = FileEntry::new(Filename::new(filename), file_address, file_len); 249 | } 250 | self.directory = tmp_directory; 251 | self.directory_loaded = true; 252 | } 253 | } 254 | 255 | fn put_directory_entry(&mut self, index: usize, entry: FileEntry) { 256 | self.directory[index] = entry; 257 | 258 | let i = index; 259 | for j in 0..16 { 260 | self.put_byte((0 as u32 + i as u32 * 22 as u32 + j as u32) as u32, entry.filename.byte_at(j)); 261 | } 262 | self.put_word((0 + i * 22 + 16) as u32, entry.address); 263 | self.put_hword((0 + i * 22 + 20) as u32, entry.len); 264 | } 265 | 266 | fn add_file(&mut self, file_name: [u8; 16]) -> usize { 267 | self.ensure_directory(); 268 | let fname = Filename::new(file_name); 269 | 270 | for i in 0..46 { 271 | let entry = self.directory[i]; 272 | 273 | if entry.address == 0 || entry.address == 0xffffffff || entry.filename == fname { 274 | let address = i * MAX_FILE_SIZE + DIRECTORY_BYTES; 275 | self.put_directory_entry(i, FileEntry::new(Filename::new(file_name), address as u32, 0)); 276 | return i; 277 | } 278 | } 279 | 280 | 9999 281 | } 282 | 283 | fn set_filesize(&mut self, fileno: usize, filesize: u16) { 284 | self.put_hword((fileno * 22 + 20) as u32, filesize); 285 | self.flush(); 286 | self.directory_loaded = false; 287 | } 288 | } 289 | 290 | 291 | impl, CS: OutputPin> pet::io::Storage for FlashStorage { 292 | fn start_filename(&mut self) { 293 | self.filename[0] = 0; 294 | self.filename[1] = 0; 295 | self.filename[2] = 0; 296 | self.filename[3] = 0; 297 | self.filename[4] = 0; 298 | self.filename[5] = 0; 299 | self.filename[6] = 0; 300 | self.filename[7] = 0; 301 | self.filename[8] = 0; 302 | self.filename[9] = 0; 303 | self.filename[10] = 0; 304 | self.filename[11] = 0; 305 | self.filename[12] = 0; 306 | self.filename[13] = 0; 307 | self.filename[14] = 0; 308 | self.filename[15] = 0; 309 | self.filename_index = 0; 310 | self.file_byte_count = 0; 311 | } 312 | 313 | fn next_filename_byte(&mut self, value: u8) { 314 | self.filename[self.filename_index] = value; 315 | self.filename_index += 1; 316 | } 317 | 318 | fn fname_done(&mut self) { 319 | if self.filename[0] == '$' as u8 { 320 | self.loading_directory = true; 321 | 322 | for i in 0..MAX_FILES { 323 | if self.directory[i].address != 0xffff && self.directory[i].address!=0x0 { 324 | self.loading_directory_fileno = i; 325 | break; 326 | } 327 | } 328 | 329 | self.loading_directory_filename_index = 0; 330 | 331 | return; 332 | } 333 | 334 | self.loading_directory = false; 335 | match self.find_file(self.filename){ 336 | Ok((_, fileno)) => { self.current_fileno = fileno; } 337 | Err(_) => { self.current_fileno = 9999; } 338 | } 339 | } 340 | 341 | 342 | fn start_save(&mut self) { 343 | let fileno = self.add_file(self.filename); 344 | 345 | if fileno != 9999 { 346 | self.current_fileno = fileno; 347 | } 348 | } 349 | 350 | fn end_save(&mut self) { 351 | self.flush(); 352 | self.set_filesize(self.current_fileno, self.file_byte_count as u16); 353 | } 354 | 355 | fn has_data_to_load(&mut self) -> bool { 356 | if self.loading_directory { 357 | return true; 358 | } 359 | 360 | self.load_data_len() > 0 361 | } 362 | 363 | fn load_data_byte(&mut self, index: usize) -> u8 { 364 | self.file_byte_count = index + 1; 365 | 366 | if self.loading_directory { 367 | let current_entry = if self.loading_directory_fileno != 9999 { 368 | Some(&self.directory[self.loading_directory_fileno]) 369 | } else { 370 | None 371 | }; 372 | 373 | fn calculate_next_basic_line_ptr(loading_directory_fileindex: usize, entry_count: usize) -> usize { 374 | if loading_directory_fileindex >= entry_count { 375 | 0 376 | } else { 377 | 0x401usize + (loading_directory_fileindex + 1) * DIR_ENTRY_BASIC_LINE_SIZE 378 | } 379 | } 380 | 381 | fn filename_byte_in_basic(entry: Option<&FileEntry>, index: usize) -> u8 { 382 | match entry { 383 | Some(entry) => { 384 | let filename = entry.filename; 385 | 386 | if index >= 16 { 387 | if index > 0 && filename[15] != 0 { 388 | return '"' as u8; 389 | } else { 390 | return ' ' as u8; 391 | } 392 | } 393 | 394 | let b = filename[index]; 395 | 396 | if b != 0 { 397 | b 398 | } else { 399 | if index > 0 && filename[index - 1] != 0 { 400 | '"' as u8 401 | } else { 402 | ' ' as u8 403 | } 404 | } 405 | 406 | } 407 | None => { 0 } 408 | } 409 | 410 | } 411 | 412 | fn len_or_0(v: Option<&FileEntry>) -> u16 { 413 | match v { 414 | Some(entry) => entry.len, 415 | _ => 0 416 | } 417 | } 418 | 419 | if index == 0 { 420 | return 0x01; 421 | } 422 | 423 | if index == 1 { 424 | return 0x04; 425 | } 426 | 427 | let res = match self.loading_directory_filename_index { 428 | 0 => (((calculate_next_basic_line_ptr(self.loading_directory_fileindex, self.directory_valid_entries))) & 0xff) as u8, 429 | 1 => ((((calculate_next_basic_line_ptr(self.loading_directory_fileindex, self.directory_valid_entries))) & 0xff00) >> 8) as u8, 430 | 431 | 2 => ((len_or_0(current_entry)) & 0xff) as u8, 432 | 3 => (((len_or_0(current_entry)) & 0xff00) >> 8) as u8, 433 | 434 | 4..=5 => ' ' as u8, 435 | 436 | 6 => '"' as u8, 437 | 438 | 7..=23 => filename_byte_in_basic(current_entry, self.loading_directory_filename_index - 7), 439 | 440 | 24 => 0u8, 441 | 442 | _ => 0u8, 443 | }; 444 | 445 | self.loading_directory_filename_index += 1; 446 | 447 | if self.loading_directory_filename_index > DIR_ENTRY_BASIC_LINE_SIZE - 1 { 448 | self.loading_directory_filename_index = 0; 449 | self.loading_directory_fileindex += 1; 450 | 451 | let start_at = self.loading_directory_fileno + 1; 452 | self.loading_directory_fileno = 9999; 453 | for i in start_at..MAX_FILES { 454 | if self.directory[i].address != 0xffffffff && self.directory[i].address!=0x0 { 455 | self.loading_directory_fileno = i; 456 | } 457 | } 458 | } 459 | 460 | return res; 461 | } 462 | 463 | 464 | 465 | let res = self.get_byte(self.current_fileno as u32 * MAX_FILE_SIZE as u32 + DIRECTORY_BYTES as u32 + index as u32); 466 | res 467 | } 468 | 469 | fn save_data_byte(&mut self, index: usize, value: u8) { 470 | self.file_byte_count = index + 1; 471 | self.put_byte(self.current_fileno as u32 * MAX_FILE_SIZE as u32 + DIRECTORY_BYTES as u32 + index as u32, value); 472 | } 473 | 474 | fn load_data_len(&mut self) -> usize { 475 | if self.loading_directory { 476 | self.ensure_directory(); 477 | let mut entries = 0; 478 | for entry in self.directory.iter() { 479 | if entry.address != 0xffffffff && entry.address != 0x0 { 480 | entries += 1; 481 | } 482 | } 483 | self.directory_valid_entries = entries; 484 | return entries * DIR_ENTRY_BASIC_LINE_SIZE + 2 + 2; 485 | } 486 | 487 | let entry = self.find_file(self.filename); 488 | match entry { 489 | Ok(entry) => entry.0.len as usize, 490 | Err(_) => 0usize, 491 | } 492 | } 493 | } 494 | -------------------------------------------------------------------------------- /pet/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use mos6502::Memory; 4 | 5 | use core::cell::RefCell; 6 | 7 | pub mod io; 8 | use io::Io; 9 | use io::Keyboard; 10 | use io::Storage; 11 | 12 | const ROM_C000: &'static [u8] = include_bytes!("../rom/rom-b-c000.bin"); 13 | const ROM_D000: &'static [u8] = include_bytes!("../rom/rom-b-d000.bin"); 14 | const ROM_E000: &'static [u8] = include_bytes!("../rom/rom-e-e000.bin"); 15 | const ROM_F000: &'static [u8] = include_bytes!("../rom/rom-k-f000.bin"); 16 | 17 | pub struct Ram<'a> { 18 | pub ram: RefCell<&'a mut [u8; 8192]>, 19 | pub vid_ram: RefCell<&'a mut [u8; 2048]>, 20 | pub io: RefCell>, 21 | } 22 | 23 | impl<'a> Ram<'a> { 24 | pub fn new( 25 | ram: &'a mut [u8; 8192], 26 | vid_ram: &'a mut [u8; 2048], 27 | storage: &'a mut dyn Storage, 28 | ) -> Ram<'a> { 29 | let mut io = Io::new(Keyboard::new(), storage); 30 | io.reset(); 31 | 32 | Ram { 33 | ram: RefCell::new(ram), 34 | vid_ram: RefCell::new(vid_ram), 35 | io: RefCell::new(io), 36 | } 37 | } 38 | } 39 | 40 | impl<'a> Memory for Ram<'a> { 41 | fn get(&self, addr: u16) -> u8 { 42 | if addr >= 0xe800 && addr < 0xe850 { 43 | return self.io.borrow_mut().read(addr - 0xe800); 44 | } 45 | 46 | if addr >= 0x8000 && addr < 0x8800 { 47 | return self.vid_ram.borrow()[(addr - 0x8000) as usize]; 48 | } 49 | if addr >= 0xc000 && addr < 0xd000 { 50 | return ROM_C000[(addr - 0xc000) as usize]; 51 | } 52 | if addr >= 0xd000 && addr < 0xe000 { 53 | return ROM_D000[(addr - 0xd000) as usize]; 54 | } 55 | if addr >= 0xe000 && addr < 0xf000 { 56 | return ROM_E000[(addr - 0xe000) as usize]; 57 | } 58 | if addr >= 0xf000 { 59 | return ROM_F000[(addr - 0xf000) as usize]; 60 | } 61 | 62 | if addr >= 0x2000 { 63 | return 0; 64 | } 65 | 66 | self.ram.borrow()[addr as usize] 67 | } 68 | 69 | fn set(&self, addr: u16, v: u8) { 70 | if addr >= 0xe800 && addr < 0xe850 { 71 | self.io.borrow_mut().write(addr - 0xe800, v); 72 | return; 73 | } 74 | 75 | if addr >= 0x8000 && addr < 0x8800 { 76 | self.vid_ram.borrow_mut()[(addr - 0x8000) as usize] = v; 77 | return; 78 | } 79 | 80 | if addr >= 0x2000 { 81 | return; 82 | } 83 | 84 | self.ram.borrow_mut()[addr as usize] = v; 85 | } 86 | } 87 | 88 | #[cfg(test)] 89 | mod tests { 90 | extern crate std; 91 | use std::println; 92 | use std::string::String; 93 | use mos6502::Cpu; 94 | 95 | use super::*; 96 | 97 | const CHARCONV: [u16; 306] = [ 98 | 0x00, 0x0040, 0x01, 0x0041, 0x02, 0x0042, 0x03, 0x0043, 0x04, 0x0044, 0x05, 0x0045, 0x06, 99 | 0x0046, 0x07, 0x0047, 0x08, 0x0048, 0x09, 0x0049, 0x0A, 0x004A, 0x0B, 0x004B, 0x0C, 0x004C, 100 | 0x0D, 0x004D, 0x0E, 0x004E, 0x0F, 0x004F, 0x10, 0x0050, 0x11, 0x0051, 0x12, 0x0052, 0x13, 101 | 0x0053, 0x14, 0x0054, 0x15, 0x0055, 0x16, 0x0056, 0x17, 0x0057, 0x18, 0x0058, 0x19, 0x0059, 102 | 0x1A, 0x005A, 0x1B, 0x005B, 0x1C, 0x005C, 0x1D, 0x005D, 0x1E, 0x2191, 0x1F, 0x2190, 0x20, 103 | 0x0020, 0x21, 0x0021, 0x22, 0x0022, 0x23, 0x0023, 0x24, 0x0024, 0x25, 0x0025, 0x26, 0x0026, 104 | 0x27, 0x0027, 0x28, 0x0028, 0x29, 0x0029, 0x2A, 0x002A, 0x2B, 0x002B, 0x2C, 0x002C, 0x2D, 105 | 0x002D, 0x2E, 0x002E, 0x2F, 0x002F, 0x30, 0x0030, 0x31, 0x0031, 0x32, 0x0032, 0x33, 0x0033, 106 | 0x34, 0x0034, 0x35, 0x0035, 0x36, 0x0036, 0x37, 0x0037, 0x38, 0x0038, 0x39, 0x0039, 0x3A, 107 | 0x003A, 0x3B, 0x003B, 0x3C, 0x003C, 0x3D, 0x003D, 0x3E, 0x003E, 0x3F, 0x003F, 0x40, 0x2500, 108 | 0x41, 0x0061, 0x42, 0x0062, 0x43, 0x0063, 0x44, 0x0064, 0x45, 0x0065, 0x46, 0x0066, 0x47, 109 | 0x0067, 0x48, 0x0068, 0x49, 0x0069, 0x4A, 0x006A, 0x4B, 0x006B, 0x4C, 0x006C, 0x4D, 0x006D, 110 | 0x4E, 0x006E, 0x4F, 0x006F, 0x50, 0x0071, 0x51, 0x0072, 0x52, 0x0072, 0x53, 0x0073, 0x54, 111 | 0x0074, 0x55, 0x0075, 0x56, 0x0076, 0x57, 0x0077, 0x58, 0x0078, 0x59, 0x0079, 0x5A, 0x007A, 112 | 0x5B, 0x253C, 0x5C, 0x258C, 0x5D, 0x2502, 0x5E, 0x2591, 0x5F, 0x25A7, 0x60, 0x0020, 0x61, 113 | 0x258C, 0x62, 0x2584, 0x63, 0x2594, 0x64, 0x2581, 0x65, 0x258F, 0x66, 0x2592, 0x67, 0x2595, 114 | 0x68, 0x2584, 0x69, 0x25A8, 0x6A, 0x2595, 0x6B, 0x251C, 0x6C, 0x2597, 0x6D, 0x2514, 0x6E, 115 | 0x2510, 0x6F, 0x2582, 0x70, 0x250C, 0x71, 0x2534, 0x72, 0x252C, 0x73, 0x2524, 0x74, 0x258E, 116 | 0x75, 0x258D, 0x76, 0x2590, 0x77, 0x2594, 0x78, 0x2580, 0x79, 0x2583, 0x7A, 0x2713, 0x7B, 117 | 0x2596, 0x7C, 0x259D, 0x7D, 0x2518, 0x7E, 0x2598, 0x7F, 0x259A, 0xDE, 0x2591, 0xDF, 0x25A7, 118 | 0xE0, 0x2588, 0xE1, 0x2590, 0xE2, 0x2580, 0xE3, 0x2587, 0xE4, 0x2580, 0xE5, 0x2598, 0xE6, 119 | 0x2592, 0xE7, 0x2589, 0xE9, 0x25A8, 0xEA, 0x258A, 0xEC, 0x259B, 0xEF, 0x2580, 0xF4, 0x2590, 120 | 0xF5, 0x2590, 0xF6, 0x258B, 0xF7, 0x2586, 0xF8, 0x2585, 0xF9, 0x2580, 0xFB, 0x259C, 0xFC, 121 | 0x2599, 0xFE, 0x259F, 0xFF, 0x259E, 0xA0, 0x2588, 122 | ]; 123 | 124 | // see http://www.6502.org/users/andre/petindex/keyboards.html#graph 125 | const ASCII2PET: [u8; 8 * 10 * 2] = [ 126 | '!' as u8, 0x00, '#' as u8, 0x01, '%' as u8, 0x02, '&' as u8, 0x03, '(' as u8, 0x04, 127 | 127 as u8, 0x05, // BACKSPACE? 128 | 0 as u8, 0x06, // HOME 129 | 0 as u8, 0x07, // CRSR RIGHT 130 | '"' as u8, 0x10, '$' as u8, 0x11, '\'' as u8, 0x12, '\\' as u8, 0x13, ')' as u8, 0x14, 131 | '~' as u8, 0x15, // UNASSIGNED 132 | 0 as u8, 0x16, // CRSR DOWN 133 | 127 as u8, 0x17, // DELETE? 134 | 'q' as u8, 0x20, 'e' as u8, 0x21, 't' as u8, 0x22, 'u' as u8, 0x23, 'o' as u8, 0x24, 135 | '^' as u8, 0x25, '7' as u8, 0x26, '9' as u8, 0x27, 'w' as u8, 0x30, 'r' as u8, 0x31, 136 | 'y' as u8, 0x32, 'i' as u8, 0x33, 'p' as u8, 0x34, '~' as u8, 0x35, '8' as u8, 0x36, 137 | '/' as u8, 0x37, 'a' as u8, 0x40, 'd' as u8, 0x41, 'g' as u8, 0x42, 'j' as u8, 0x43, 138 | 'l' as u8, 0x44, '~' as u8, 0x45, '4' as u8, 0x46, '6' as u8, 0x47, 's' as u8, 0x50, 139 | 'f' as u8, 0x51, 'h' as u8, 0x52, 'k' as u8, 0x53, ':' as u8, 0x54, '~' as u8, 0x55, 140 | '5' as u8, 0x56, '*' as u8, 0x57, 'z' as u8, 0x60, 'c' as u8, 0x61, 'b' as u8, 0x62, 141 | 'm' as u8, 0x63, ';' as u8, 0x64, '\r' as u8, 0x65, '1' as u8, 0x66, '3' as u8, 0x67, 142 | 'x' as u8, 0x70, 'v' as u8, 0x71, 'n' as u8, 0x72, ',' as u8, 0x73, '?' as u8, 0x74, 143 | '~' as u8, 0x75, '2' as u8, 0x76, '+' as u8, 0x77, '~' as u8, 0x80, // LEFT SHIFT 144 | '@' as u8, 0x81, ']' as u8, 0x82, '~' as u8, 0x83, '>' as u8, 0x84, '~' as u8, 145 | 0x85, // RIGHT SHIFT 146 | '0' as u8, 0x86, '-' as u8, 0x87, '~' as u8, 0x90, // REVERSE ON? 147 | '[' as u8, 0x91, ' ' as u8, 0x92, '<' as u8, 0x93, '~' as u8, 0x94, // STOP 148 | '~' as u8, 0x95, '.' as u8, 0x96, '=' as u8, 0x97, 149 | ]; 150 | 151 | fn screen_as_string(mem: &Ram) -> String { 152 | let mut res = String::new(); 153 | let mut x = 0; 154 | for addr in 0x8000usize..0x8400usize { 155 | let v = mem.get(addr as u16); 156 | 157 | let mut ascii: char = 32 as char; 158 | for i in (0..306).step_by(2) { 159 | if v == CHARCONV[i] as u8 { 160 | ascii = CHARCONV[i + 1] as u8 as char; 161 | } 162 | } 163 | 164 | res.push(ascii); 165 | 166 | x += 1; 167 | if x == 40 { 168 | x = 0; 169 | res.push('\n'); 170 | } 171 | } 172 | 173 | res 174 | } 175 | 176 | struct TestStorage { 177 | filename: [u8; 16], 178 | load_data_length: usize, 179 | load_data: [u8; 13], 180 | save_data: [u8; 256], 181 | 182 | filename_index: usize, 183 | } 184 | 185 | impl TestStorage { 186 | fn new() -> TestStorage { 187 | TestStorage { 188 | filename: [0u8; 16], 189 | load_data_length: 13usize, 190 | load_data: [ 191 | 0x01, 0x04, 0x0a, 0x04, 0x64, 0x00, 0x8f, 0x20, 0x48, 0x49, 0x00, 0x00, 0x00, 192 | ], 193 | save_data: [0u8; 256], 194 | 195 | filename_index: 0usize, 196 | } 197 | } 198 | } 199 | 200 | impl Storage for TestStorage { 201 | fn start_filename(&mut self) { 202 | println!("start filename"); 203 | self.filename_index = 0; 204 | } 205 | 206 | fn next_filename_byte(&mut self, value: u8) { 207 | println!("next filename byte {} = char {}", value, value as char); 208 | self.filename[self.filename_index] = value; 209 | } 210 | 211 | fn start_save(&mut self) { 212 | println!("save start"); 213 | } 214 | 215 | fn has_data_to_load(&mut self) -> bool { 216 | println!("has_data_to_load"); 217 | self.load_data_length > 0 218 | } 219 | 220 | fn load_data_byte(&mut self, index: usize) -> u8 { 221 | println!("load data {}", index); 222 | self.load_data[index] 223 | } 224 | 225 | fn save_data_byte(&mut self, index: usize, value: u8) { 226 | println!("save byte {} {}", index, value); 227 | self.save_data[index] = value; 228 | } 229 | 230 | fn load_data_len(&mut self) -> usize { 231 | println!("load data len"); 232 | self.load_data.len() 233 | } 234 | 235 | fn end_save(&mut self) { 236 | println!("end save"); 237 | } 238 | 239 | fn fname_done(&mut self) { 240 | println!("filename done"); 241 | } 242 | } 243 | 244 | #[test] 245 | fn it_works() { 246 | let mut test_storage = TestStorage::new(); 247 | let enter_str = "10 for i=0 to 15\r20 print \"hello world\" i\r30 next\rrun\r"; 248 | let mut str_idx = 0; 249 | let mut keyboard_cntr = 50000; 250 | 251 | let mut ram = [0u8; 8192]; 252 | let mut vid_ram = [0u8; 2048]; 253 | let mem = Ram::new(&mut ram, &mut vid_ram, &mut test_storage); 254 | let mut cpu = Cpu::new(&mem); 255 | cpu.reset(); 256 | 257 | let mut cycle_cnt: u64 = 0; 258 | 259 | let mut tick_cntr = 0u32; 260 | let mut cnt = 0; 261 | for _ in 0..6393000 { 262 | if cycle_cnt == 0 { 263 | cnt += 1; 264 | cycle_cnt = cpu.step(); 265 | cycle_cnt -= 1; 266 | } else { 267 | cycle_cnt -= 1; 268 | } 269 | 270 | tick_cntr += 1; 271 | if tick_cntr >= 1000 { 272 | tick_cntr = 0; 273 | let trigger_irq = mem.io.borrow_mut().tick(); 274 | if trigger_irq { 275 | cpu.trigger_irq(); 276 | } 277 | } 278 | 279 | // simulate entering a string 280 | if keyboard_cntr == 30000 { 281 | if str_idx < enter_str.len() { 282 | let mut row_col = 0xff; 283 | let c = *&enter_str.as_bytes()[str_idx]; 284 | for i in (0..ASCII2PET.len()).step_by(2) { 285 | if c == ASCII2PET[i] { 286 | row_col = ASCII2PET[i + 1]; 287 | } 288 | } 289 | if row_col != 0xff { 290 | mem.io.borrow_mut().keyboard.key_down(row_col); 291 | } 292 | } 293 | } 294 | if keyboard_cntr == 0 { 295 | if str_idx < enter_str.len() { 296 | let mut row_col = 0xff; 297 | let c = *&enter_str.as_bytes()[str_idx]; 298 | for i in (0..ASCII2PET.len()).step_by(2) { 299 | if c == ASCII2PET[i] { 300 | row_col = ASCII2PET[i + 1]; 301 | } 302 | } 303 | if row_col != 0xff { 304 | mem.io.borrow_mut().keyboard.key_up(row_col); 305 | } 306 | str_idx += 1; 307 | } 308 | keyboard_cntr = 50000; 309 | } 310 | keyboard_cntr -= 1; 311 | } 312 | 313 | let screen_content = screen_as_string(&mem); 314 | println!("{}\n", screen_content); 315 | 316 | println!("{}", cnt); 317 | 318 | assert!(screen_content.contains("HELLO WORLD 10")); 319 | } 320 | 321 | #[test] 322 | fn load_from_disk_works() { 323 | let mut test_storage = TestStorage::new(); 324 | 325 | let second_str = "list\r"; 326 | 327 | let mut enter_str = "load \"test\",8\r"; 328 | let mut str_idx = 0; 329 | 330 | let mut keyboard_cntr = 50000; 331 | 332 | let mut ram = [0u8; 8192]; 333 | let mut vid_ram = [0u8; 2048]; 334 | let mem = Ram::new(&mut ram, &mut vid_ram, &mut test_storage); 335 | let mut cpu = Cpu::new(&mem); 336 | cpu.reset(); 337 | 338 | println!("{:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x}", 339 | mem.get(0x400), mem.get(0x401), mem.get(0x402), mem.get(0x403), 340 | mem.get(0x404), mem.get(0x405), mem.get(0x406), mem.get(0x407), 341 | mem.get(0x408), mem.get(0x409), mem.get(0x40a), mem.get(0x40b), 342 | 343 | mem.get(0x40c), mem.get(0x40d), mem.get(0x40e), mem.get(0x40f), 344 | mem.get(0x410), mem.get(0x411), mem.get(0x412), mem.get(0x413), 345 | mem.get(0x414), mem.get(0x415), mem.get(0x416), mem.get(0x417), 346 | 347 | ); 348 | 349 | let mut cycle_cnt: u64 = 0; 350 | 351 | let mut tick_cntr = 0u32; 352 | let mut cnt = 0; 353 | for i in 0..9393000 { 354 | if i == 5393000 { 355 | enter_str = second_str; 356 | str_idx = 0; 357 | keyboard_cntr = 50000; 358 | } 359 | 360 | if cycle_cnt == 0 { 361 | cnt += 1; 362 | cycle_cnt = cpu.step(); 363 | cycle_cnt -= 1; 364 | } else { 365 | cycle_cnt -= 1; 366 | } 367 | 368 | tick_cntr += 1; 369 | if tick_cntr >= 1000 { 370 | tick_cntr = 0; 371 | let trigger_irq = mem.io.borrow_mut().tick(); 372 | if trigger_irq { 373 | cpu.trigger_irq(); 374 | } 375 | } 376 | 377 | // simulate entering a string 378 | if keyboard_cntr == 30000 { 379 | if str_idx < enter_str.len() { 380 | let mut row_col = 0xff; 381 | let c = *&enter_str.as_bytes()[str_idx]; 382 | for i in (0..ASCII2PET.len()).step_by(2) { 383 | if c == ASCII2PET[i] { 384 | row_col = ASCII2PET[i + 1]; 385 | } 386 | } 387 | if row_col != 0xff { 388 | mem.io.borrow_mut().keyboard.key_down(row_col); 389 | } 390 | } 391 | } 392 | if keyboard_cntr == 0 { 393 | if str_idx < enter_str.len() { 394 | let mut row_col = 0xff; 395 | let c = *&enter_str.as_bytes()[str_idx]; 396 | for i in (0..ASCII2PET.len()).step_by(2) { 397 | if c == ASCII2PET[i] { 398 | row_col = ASCII2PET[i + 1]; 399 | } 400 | } 401 | if row_col != 0xff { 402 | mem.io.borrow_mut().keyboard.key_up(row_col); 403 | } 404 | str_idx += 1; 405 | } 406 | keyboard_cntr = 50000; 407 | } 408 | keyboard_cntr -= 1; 409 | } 410 | 411 | let screen_content = screen_as_string(&mem); 412 | println!("{}\n", screen_content); 413 | 414 | println!("{}", cnt); 415 | 416 | println!("{:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x} {:x}", 417 | mem.get(0x400), mem.get(0x401), mem.get(0x402), mem.get(0x403), 418 | mem.get(0x404), mem.get(0x405), mem.get(0x406), mem.get(0x407), 419 | mem.get(0x408), mem.get(0x409), mem.get(0x40a), mem.get(0x40b), 420 | 421 | mem.get(0x40c), mem.get(0x40d), mem.get(0x40e), mem.get(0x40f), 422 | mem.get(0x410), mem.get(0x411), mem.get(0x412), mem.get(0x413), 423 | mem.get(0x414), mem.get(0x415), mem.get(0x416), mem.get(0x417), 424 | ); 425 | 426 | assert!(screen_content.contains("100 REM HI")); 427 | } 428 | 429 | #[test] 430 | fn save_works() { 431 | let mut test_storage = TestStorage::new(); 432 | 433 | let enter_str = "10 rem hello world\rsave\"test\",8\r"; 434 | let mut str_idx = 0; 435 | let mut keyboard_cntr = 50000; 436 | 437 | let mut ram = [0u8; 8192]; 438 | let mut vid_ram = [0u8; 2048]; 439 | let mem = Ram::new(&mut ram, &mut vid_ram, &mut test_storage); 440 | let mut cpu = Cpu::new(&mem); 441 | cpu.reset(); 442 | 443 | let mut cycle_cnt: u64 = 0; 444 | 445 | let mut tick_cntr = 0u32; 446 | let mut cnt = 0; 447 | for _ in 0..6393000 { 448 | if cycle_cnt == 0 { 449 | cnt += 1; 450 | cycle_cnt = cpu.step(); 451 | cycle_cnt -= 1; 452 | } else { 453 | cycle_cnt -= 1; 454 | } 455 | 456 | tick_cntr += 1; 457 | if tick_cntr >= 1000 { 458 | tick_cntr = 0; 459 | let trigger_irq = mem.io.borrow_mut().tick(); 460 | if trigger_irq { 461 | cpu.trigger_irq(); 462 | } 463 | } 464 | 465 | // simulate entering a string 466 | if keyboard_cntr == 30000 { 467 | if str_idx < enter_str.len() { 468 | let mut row_col = 0xff; 469 | let c = *&enter_str.as_bytes()[str_idx]; 470 | for i in (0..ASCII2PET.len()).step_by(2) { 471 | if c == ASCII2PET[i] { 472 | row_col = ASCII2PET[i + 1]; 473 | } 474 | } 475 | if row_col != 0xff { 476 | mem.io.borrow_mut().keyboard.key_down(row_col); 477 | } 478 | } 479 | } 480 | if keyboard_cntr == 0 { 481 | if str_idx < enter_str.len() { 482 | let mut row_col = 0xff; 483 | let c = *&enter_str.as_bytes()[str_idx]; 484 | for i in (0..ASCII2PET.len()).step_by(2) { 485 | if c == ASCII2PET[i] { 486 | row_col = ASCII2PET[i + 1]; 487 | } 488 | } 489 | if row_col != 0xff { 490 | mem.io.borrow_mut().keyboard.key_up(row_col); 491 | } 492 | str_idx += 1; 493 | } 494 | keyboard_cntr = 50000; 495 | } 496 | keyboard_cntr -= 1; 497 | } 498 | 499 | let screen_content = screen_as_string(&mem); 500 | println!("{}\n", screen_content); 501 | 502 | println!("{}", cnt); 503 | 504 | assert!(test_storage.save_data[ 0]==0x01); 505 | assert!(test_storage.save_data[ 1]==0x04); 506 | assert!(test_storage.save_data[ 2]==0x13); 507 | assert!(test_storage.save_data[ 3]==0x04); 508 | assert!(test_storage.save_data[ 4]==0x0a); 509 | assert!(test_storage.save_data[ 5]==0x00); 510 | assert!(test_storage.save_data[ 6]==0x8f); 511 | assert!(test_storage.save_data[ 7]==0x20); 512 | assert!(test_storage.save_data[ 8]==0x48); 513 | assert!(test_storage.save_data[ 9]==0x45); 514 | assert!(test_storage.save_data[10]==0x4c); 515 | assert!(test_storage.save_data[11]==0x4c); 516 | assert!(test_storage.save_data[12]==0x4f); 517 | assert!(test_storage.save_data[13]==0x20); 518 | assert!(test_storage.save_data[14]==0x57); 519 | assert!(test_storage.save_data[15]==0x4f); 520 | assert!(test_storage.save_data[16]==0x52); 521 | assert!(test_storage.save_data[17]==0x4c); 522 | assert!(test_storage.save_data[18]==0x44); 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /pet/src/io/mod.rs: -------------------------------------------------------------------------------- 1 | // base is 0xe800 2 | const PIA1: u16 = 0x0010; 3 | const PIA2: u16 = 0x0020; 4 | const PORTA: u16 = 0x00; 5 | const CRA: u16 = 0x01; 6 | const PORTB: u16 = 0x02; 7 | const CRB: u16 = 0x03; 8 | 9 | const VIA: u16 = 0x0040; 10 | const VPORTB: u16 = 0x00; 11 | const VPORTA: u16 = 0x01; 12 | const DDRB: u16 = 0x02; 13 | const DDRA: u16 = 0x03; 14 | const T1LO: u16 = 0x04; 15 | const T1HI: u16 = 0x05; 16 | const T1LLO: u16 = 0x06; 17 | const T1LHI: u16 = 0x07; 18 | const T2LO: u16 = 0x08; 19 | const T2HI: u16 = 0x09; 20 | const SHIFT: u16 = 0x0a; 21 | const ACR: u16 = 0x0b; 22 | const PCR: u16 = 0x0c; 23 | const IFR: u16 = 0x0d; 24 | const IER: u16 = 0x0e; 25 | 26 | const IER_MASTER: u8 = 0x80; 27 | const IER_TIMER1: u8 = 0x40; 28 | const IER_TIMER2: u8 = 0x20; 29 | const IER_CB1_ACTIVE: u8 = 0x10; 30 | const IER_CB2_ACTIVE: u8 = 0x08; 31 | const IER_SHIFT_REG: u8 = 0x04; 32 | const IER_CA1_ACTIVE: u8 = 0x02; 33 | const IER_CA2_ACTIVE: u8 = 0x01; 34 | 35 | const VIA_VIDEO_RETRACE: u8 = 0x20; 36 | const VIA_ACR_SHIFT_MASK: u8 = 0x1c; 37 | const VIA_ACR_T1_CONTINUOUS: u8 = 0x40; 38 | 39 | // 1ms internal clock tick 40 | const TICK_FREQ: u16 = 1000; 41 | 42 | // 50Hz system interrupt frequency 43 | const SYS_TICKS: u16 = 20; 44 | 45 | const PIA1_PORTB: u16 = PIA1 + PORTB; 46 | const PIA1_PORTA: u16 = PIA1 + PORTA; 47 | const PIA1_CRA: u16 = PIA1 + CRA; 48 | const PIA1_CRB: u16 = PIA1 + CRB; 49 | 50 | const PIA2_PORTB: u16 = PIA2 + PORTB; 51 | const PIA2_PORTA: u16 = PIA2 + PORTA; 52 | const PIA2_CRA: u16 = PIA2 + CRA; 53 | const PIA2_CRB: u16 = PIA2 + CRB; 54 | 55 | const VIA_VPORTA: u16 = VIA + VPORTA; 56 | const VIA_VPORTB: u16 = VIA + VPORTB; 57 | const VIA_SHIFT: u16 = VIA + SHIFT; 58 | const VIA_ACR: u16 = VIA + ACR; 59 | const VIA_IER: u16 = VIA + IER; 60 | const VIA_IFR: u16 = VIA + IFR; 61 | const VIA_T1LO: u16 = VIA + T1LO; 62 | const VIA_T1HI: u16 = VIA + T1HI; 63 | const VIA_T1LLO: u16 = VIA + T1LLO; 64 | const VIA_T1LHI: u16 = VIA + T1LHI; 65 | const VIA_T2LO: u16 = VIA + T2LO; 66 | const VIA_T2HI: u16 = VIA + T2HI; 67 | const VIA_PCR: u16 = VIA + PCR; 68 | const VIA_DDRA: u16 = VIA + DDRA; 69 | const VIA_DDRB: u16 = VIA + DDRB; 70 | 71 | const VIA_ANH: u16 = 0x4f; 72 | 73 | #[derive(Debug)] 74 | pub struct Io<'a> { 75 | ticks: u16, 76 | timer1: bool, 77 | timer2: bool, 78 | t1: u16, 79 | t2: u16, 80 | t1_latch: u16, 81 | acr: u8, 82 | ier: u8, 83 | ifr: u8, 84 | ddra: u8, 85 | ddrb: u8, 86 | porta: u8, 87 | portb: u8, 88 | 89 | pia1_pa_in: u8, 90 | pia1_pa_out: u8, 91 | pia1_ddra: u8, 92 | pia1_cra: u8, 93 | pia1_pb_in: u8, 94 | pia1_pb_out: u8, 95 | pia1_ddrb: u8, 96 | pia1_crb: u8, 97 | pia1_ca2: u8, 98 | 99 | pia2_pa_in: u8, 100 | pia2_pa_out: u8, 101 | pia2_ddra: u8, 102 | pia2_cra: u8, 103 | pia2_pb_in: u8, 104 | pia2_pb_out: u8, 105 | pia2_ddrb: u8, 106 | pia2_crb: u8, 107 | 108 | via_drb_in: u8, 109 | via_drb_out: u8, 110 | via_dra_in: u8, 111 | via_dra_out: u8, 112 | 113 | via_t1cl: u8, 114 | via_t1ch: u8, 115 | via_t1_1shot: u8, 116 | via_t1ll: u8, 117 | via_t1lh: u8, 118 | via_t2cl: u8, 119 | via_t2ch: u8, 120 | via_t2_1shot: u8, 121 | via_sr: u8, 122 | via_pcr: u8, 123 | 124 | pub keyboard: Keyboard, 125 | pub ieee: Ieee<'a>, 126 | } 127 | 128 | impl<'a> Io<'a> { 129 | pub fn new(keyboard: Keyboard, storage: &'a mut dyn Storage) -> Io { 130 | Io { 131 | ticks: 0, 132 | timer1: false, 133 | timer2: false, 134 | t1: 0, 135 | t2: 0, 136 | t1_latch: 0, 137 | acr: 0, 138 | ier: 0x80, 139 | ifr: 0, 140 | ddra: 0, 141 | ddrb: 0, 142 | porta: 0, 143 | portb: 0, 144 | 145 | pia1_pa_in: 0xf0, 146 | pia1_pa_out: 0, 147 | pia1_ddra: 0, 148 | pia1_cra: 0, 149 | pia1_pb_in: 0xff, 150 | pia1_pb_out: 0, 151 | pia1_ddrb: 0, 152 | pia1_crb: 0, 153 | pia1_ca2: 0, 154 | 155 | pia2_pa_in: 0, 156 | pia2_pa_out: 0, 157 | pia2_ddra: 0, 158 | pia2_cra: 0, 159 | pia2_pb_in: 0, 160 | pia2_pb_out: 0, 161 | pia2_ddrb: 0, 162 | pia2_crb: 0, 163 | 164 | via_drb_in: 0, 165 | via_drb_out: 0, 166 | via_dra_in: 0, 167 | via_dra_out: 0, 168 | via_t1cl: 0xff, 169 | via_t1ch: 0xff, 170 | via_t1_1shot: 0, 171 | via_t1ll: 0xff, 172 | via_t1lh: 0xff, 173 | via_t2cl: 0xff, 174 | via_t2ch: 0xff, 175 | via_t2_1shot: 0, 176 | via_sr: 0, 177 | via_pcr: 0, 178 | 179 | keyboard: keyboard, 180 | 181 | ieee: Ieee::new(storage), 182 | } 183 | } 184 | 185 | pub fn reset(&mut self) { 186 | self.acr = 0x00; 187 | self.ifr = 0x00; 188 | self.ier = 0x00; 189 | 190 | self.t1 = 0; 191 | self.t2 = 0; 192 | 193 | self.timer1 = false; 194 | self.timer2 = false; 195 | 196 | self.keyboard.reset(); 197 | } 198 | 199 | // true -> trigger IRQ, false otherwise 200 | pub fn tick(&mut self) -> bool { 201 | let mut raise_irq = false; 202 | 203 | if self.ticks == SYS_TICKS { 204 | self.ticks = 0; 205 | self.portb |= VIA_VIDEO_RETRACE; 206 | raise_irq = true; 207 | } else { 208 | self.portb = self.portb & !VIA_VIDEO_RETRACE; 209 | self.ticks += 1; 210 | } 211 | 212 | if self.timer1 { 213 | if self.t1 < TICK_FREQ { 214 | self.t1 = 0; 215 | self.timer1 = false; 216 | self.ifr |= IER_TIMER1; 217 | 218 | if (self.ier & IER_MASTER) != 0 && (self.ier & IER_TIMER1) != 0 { 219 | raise_irq = true; 220 | } 221 | } else { 222 | self.t1 -= TICK_FREQ; 223 | } 224 | } 225 | 226 | if self.timer2 { 227 | if self.t2 < TICK_FREQ { 228 | self.t2 = 0; 229 | self.timer2 = false; 230 | self.ifr |= IER_TIMER2; 231 | 232 | if (self.ier & IER_MASTER) != 0 && (self.ier & IER_TIMER2) != 0 { 233 | raise_irq = true; 234 | } 235 | } else { 236 | self.t2 -= TICK_FREQ; 237 | } 238 | } 239 | 240 | if (self.pia1_cra & 0x81) == 0x81 241 | || (self.pia1_cra & 0x48) == 0x48 242 | || (self.pia1_crb & 0x81) == 0x81 243 | || (self.pia1_crb & 0x48) == 0x48 244 | { 245 | raise_irq = true; 246 | } 247 | 248 | if (self.ifr & self.ier & 0x7f) != 0 { 249 | self.ifr |= 0x80; 250 | raise_irq = true; 251 | } else { 252 | self.ifr &= !0x80; 253 | } 254 | 255 | raise_irq 256 | } 257 | 258 | pub fn read(&mut self, offset: u16) -> u8 { 259 | let mut r = 0x00u8; 260 | match offset { 261 | PIA1_PORTA => { 262 | if (self.pia1_cra & 0x04) != 0 { 263 | /* Clear IRQs in CRA as side-effect of reading PA. */ 264 | if (self.pia1_cra & 0xC0) != 0 { 265 | self.pia1_cra &= 0x3F; 266 | } 267 | if (self.pia1_ddra & 0x40) == 0 { 268 | if self.ieee.eoi_in() { 269 | self.pia1_pa_in |= 0x40; 270 | } else { 271 | self.pia1_pa_in &= 0xbf; 272 | } 273 | } 274 | 275 | r = (self.pia1_pa_in & !self.pia1_ddra) | (self.pia1_pa_out & self.pia1_ddra); 276 | } else { 277 | r = 0x80 + self.keyboard.row(); 278 | } 279 | } 280 | PIA1_CRA => { 281 | r = self.pia1_cra; 282 | } 283 | PIA1_PORTB => { 284 | if (self.pia1_crb & 0x04) != 0 { 285 | /* Clear IRQs in CRB as side-effect of reading PB. */ 286 | if (self.pia1_crb & 0xC0) != 0 { 287 | self.pia1_crb &= 0x3F; 288 | } 289 | r = (self.pia1_pb_in & !self.pia1_ddrb) | (self.pia1_pb_out & self.pia1_ddrb); 290 | } else { 291 | r = self.keyboard.read(); 292 | } 293 | } 294 | PIA1_CRB => { 295 | r = self.pia1_crb; 296 | } 297 | 298 | VIA_T1LO => { 299 | r = (self.t1 & 0xff) as u8; 300 | self.ifr &= !IER_TIMER1; 301 | } 302 | VIA_T1HI => { 303 | r = ((self.t1 >> 8) & 0xff) as u8; 304 | } 305 | VIA_T1LLO => { 306 | r = (self.t1_latch & 0xff) as u8; 307 | } 308 | VIA_T1LHI => { 309 | r = ((self.t1_latch >> 8) & 0xff) as u8; 310 | } 311 | VIA_T2LO => { 312 | r = (self.t2 & 0xff) as u8; 313 | self.ifr &= !IER_TIMER2; 314 | } 315 | VIA_T2HI => { 316 | r = ((self.t2 >> 8) & 0xff) as u8; 317 | } 318 | 319 | PIA2_PORTA => { 320 | if (self.pia2_cra & 0x04) != 0 { 321 | /* Clear IRQs in CRA as side-effect of reading PA. */ 322 | if (self.pia2_cra & 0xC0) != 0 { 323 | self.pia2_cra &= 0x3F; 324 | // this.updateIrq(); 325 | } 326 | if self.pia2_ddra == 0 { 327 | self.pia2_pa_in = self.ieee.dio_in(); 328 | } 329 | r = (self.pia2_pa_in & !self.pia2_ddra) | (self.pia2_pa_out & self.pia2_ddra); 330 | } else { 331 | r = self.pia2_ddra; 332 | } 333 | } 334 | PIA2_CRA => { 335 | r = self.pia2_cra; 336 | } 337 | PIA2_PORTB => { 338 | if (self.pia2_crb & 0x04) != 0 { 339 | /* Clear IRQs in CRB as side-effect of reading PB. */ 340 | if (self.pia2_crb & 0x3F) != 0 { 341 | self.pia2_crb &= 0x3F; 342 | } 343 | r = (self.pia2_pb_in & !self.pia2_ddrb) | (self.pia2_pb_out & self.pia2_ddrb); 344 | } else { 345 | r = self.pia2_ddrb; 346 | } 347 | } 348 | 349 | PIA2_CRB => { 350 | if self.ieee.srq_in() { 351 | self.pia2_crb |= 0x80; 352 | } else { 353 | self.pia2_crb &= 0x7f; 354 | } 355 | r = self.pia2_crb; 356 | } 357 | 358 | VIA_VPORTB => { 359 | /* Clear CB2 interrupt flag IFR3 (if not "independent" 360 | * interrupt) 361 | */ 362 | if (self.via_pcr & 0xa0) != 0x20 { 363 | if (self.ifr & 0x08) != 0 { 364 | self.ifr &= !0x08; 365 | if (self.ier & 0x08) != 0 { 366 | //this.updateIrq(); 367 | } 368 | } 369 | } 370 | /* Clear CB1 interrupt flag IFR4 */ 371 | if (self.ifr & 0x10) != 0 { 372 | self.ifr &= !0x10; 373 | if (self.ier & 0x10) != 0 { 374 | // this.updateIrq(); 375 | } 376 | } 377 | if (self.ddrb & 0x80) == 0 { 378 | if self.ieee.dav_in() { 379 | self.via_drb_in |= 0x80; 380 | } else { 381 | self.via_drb_in &= 0x7f; 382 | } 383 | } 384 | if (self.ddrb & 0x40) == 0 { 385 | if self.ieee.nrfd_in() { 386 | self.via_drb_in |= 0x40; 387 | } else { 388 | self.via_drb_in &= 0xbf; 389 | } 390 | } 391 | if (self.ddrb & 0x01) == 0 { 392 | if self.ieee.ndac_in() { 393 | self.via_drb_in |= 0x01; 394 | } else { 395 | self.via_drb_in &= 0xfe; 396 | } 397 | } 398 | r = (self.via_drb_in & !self.ddrb) | (self.via_drb_out & self.ddrb); 399 | } 400 | 401 | VIA_VPORTA => { 402 | /* Clear CA2 interrupt flag IFR0 (if not "independent" 403 | * interrupt) 404 | */ 405 | if (self.via_pcr & 0x0a) != 0x02 { 406 | if (self.ifr & 0x01) != 0 { 407 | self.ifr &= !0x01; 408 | if (self.ier & 0x01) != 0 { 409 | // this.updateIrq(); 410 | } 411 | } 412 | } 413 | 414 | /* Clear CA1 interrupt flag IFR1 */ 415 | if (self.ifr & 0x02) != 0 { 416 | self.ifr &= !0x02; 417 | if (self.ier & 0x02) != 0 { 418 | // this.updateIrq(); 419 | } 420 | } 421 | r = (self.via_dra_in & !self.ddra) | (self.via_dra_out & self.ddra); 422 | } 423 | 424 | VIA_DDRB => { 425 | r = self.ddrb; 426 | } 427 | 428 | VIA_DDRA => { 429 | r = self.ddra; 430 | } 431 | 432 | VIA_SHIFT => { 433 | /* Clear SR int flag IFR2 */ 434 | if (self.ifr & 0x04) != 0 { 435 | self.ifr &= !0x04; 436 | if (self.ier & 0x04) != 0 { 437 | // nothing 438 | } 439 | } 440 | r = self.via_sr; 441 | } 442 | VIA_ACR => { 443 | r = self.acr; 444 | } 445 | VIA_PCR => { 446 | r = self.via_pcr; 447 | } 448 | VIA_IFR => { 449 | r = self.ifr; 450 | } 451 | VIA_IER => { 452 | r = self.ier; 453 | } 454 | VIA_ANH => { 455 | // VIA_PA with no handshake. 456 | r = (self.via_dra_in & !self.ddra) | (self.via_dra_out & self.ddra); 457 | } 458 | _ => {} 459 | } 460 | 461 | r 462 | } 463 | 464 | pub fn write(&mut self, offset: u16, v: u8) { 465 | match offset { 466 | PIA1_PORTA => { 467 | if (self.pia1_cra & 0x04) != 0 { 468 | self.pia1_pa_out = v; 469 | // Which keyrow are we accessing? 470 | if (self.pia1_pa_out & 15) < 10 { 471 | self.keyboard.write(v & 0x0f); 472 | self.pia1_pb_in = self.keyboard.row(); // ???? keyrow[pia1_pa_out & 15]; 473 | } 474 | } else { 475 | self.pia1_ddra = v; 476 | } 477 | } 478 | PIA1_CRA => { 479 | self.pia1_cra = (self.pia1_cra & 0xc0) | (v & 0x3f); 480 | // Change in CA2? (screen blank) 481 | if (self.pia1_cra & 0x38) == 0x38 && self.pia1_ca2 == 0 { 482 | // CA2 transitioning high. (Screen On) 483 | self.pia1_ca2 = 1; 484 | self.ieee.eoi_out(true); 485 | } else if (self.pia1_cra & 0x38) == 0x30 && self.pia1_ca2 != 0 { 486 | // CA2 transitioning low. (Screen Blank) 487 | self.pia1_ca2 = 0; 488 | self.ieee.eoi_out(false); 489 | } 490 | } 491 | 492 | PIA1_CRB => { 493 | // ??? 494 | } 495 | 496 | VIA_T1LLO => { 497 | self.t1_latch = (self.t1 & 0xff00) | v as u16; 498 | } 499 | VIA_T1LO => { 500 | self.t1_latch = (self.t1 & 0xff00) | v as u16; 501 | } 502 | VIA_T1LHI => { 503 | self.t1_latch = (self.t1_latch & 0xff) | (v as u16) << 8; 504 | self.ifr &= !IER_TIMER1; 505 | } 506 | VIA_T1HI => { 507 | self.t1 = self.t1_latch; 508 | self.ifr &= !IER_TIMER1; 509 | self.timer1 = true; 510 | } 511 | VIA_T2LO => { 512 | self.t2 = v as u16; 513 | self.timer2 = false; 514 | self.ifr &= !IER_TIMER2; 515 | } 516 | VIA_T2HI => { 517 | self.t2 += (v as u16) << 8; 518 | self.ifr &= !IER_TIMER2; 519 | self.timer2 = true; 520 | } 521 | PIA2_PORTA => { 522 | if self.pia2_cra & 0x04 != 0 { 523 | self.pia2_pa_out = v; 524 | } else { 525 | self.pia2_ddra = v; 526 | } 527 | } 528 | PIA2_CRA => { 529 | self.pia2_cra = (self.pia2_cra & 0xc0) | (v & 0x3f); 530 | self.ieee.ndac_out((self.pia2_cra & 0x08) != 0x00); 531 | } 532 | PIA2_PORTB => { 533 | if (self.pia2_crb & 0x04) != 0 { 534 | self.pia2_pb_out = v; 535 | if self.pia2_ddrb == 0xff { 536 | self.ieee.dio_out(self.pia2_pb_out); 537 | } 538 | } else { 539 | self.pia2_ddrb = v; 540 | } 541 | } 542 | PIA2_CRB => { 543 | self.pia2_crb = (self.pia2_crb & 0xc0) | (v & 0x3f); 544 | self.ieee.dav_out((self.pia2_crb & 0x08) != 0x00); 545 | } 546 | 547 | VIA_VPORTB => { 548 | // Clear CB2 interrupt flag IFR3 (if not "independent" interrupt) 549 | if (self.via_pcr & 0xa0) != 0x20 { 550 | if (self.ifr & 0x08) != 0 { 551 | self.ifr &= !0x08; 552 | if (self.ier & 0x08) != 0 { 553 | // XXX this.updateIrq(); 554 | } 555 | } 556 | } 557 | // Clear CB1 interrupt flag IFR4 558 | if (self.ifr & 0x10) != 0 { 559 | self.ifr &= !0x10; 560 | if (self.ier & 0x10) != 0 { 561 | // XXX this.updateIrq(); 562 | } 563 | } 564 | self.via_drb_out = v; 565 | 566 | // IEEE outputs 567 | if (self.ddrb & 0x04) != 0 { 568 | self.ieee.atn_out((self.via_drb_out & 0x04) != 0x00); 569 | } 570 | if (self.ddrb & 0x02) != 0 { 571 | self.ieee.nrfd_out((self.via_drb_out & 0x02) != 0x00); 572 | } 573 | } 574 | VIA_VPORTA => { 575 | // Clear CA2 interrupt flag IFR0 (if not "independent" interrupt) 576 | if (self.via_pcr & 0x0a) != 0x02 { 577 | if (self.ifr & 0x01) != 0 { 578 | self.ifr &= !0x01; 579 | if (self.ier & 0x01) != 0 { 580 | // nothing 581 | } 582 | } 583 | } 584 | 585 | // Clear CA1 interrupt flag IFR1 586 | if (self.ifr & 0x02) != 0 { 587 | self.ifr &= !0x02; 588 | if (self.ier & 0x02) != 0 { 589 | // nothing 590 | } 591 | } 592 | self.via_dra_out = v; 593 | } 594 | VIA_DDRB => { 595 | self.ddrb = v; 596 | } 597 | VIA_DDRA => { 598 | self.ddra = v; 599 | } 600 | VIA_SHIFT => { 601 | /* Clear SR int flag IFR2 */ 602 | if (self.ifr & 0x04) != 0 { 603 | self.ifr &= !0x04; 604 | if (self.ier & 0x04) != 0 { 605 | // nothing 606 | } 607 | } 608 | self.via_sr = v; 609 | } 610 | VIA_ACR => { 611 | self.acr = v; 612 | } 613 | VIA_PCR => { 614 | /* Did we change CA2 output? */ 615 | if (self.via_pcr & 0x0c) == 0x0c 616 | && (v & 0x0c) == 0x0c 617 | && ((self.via_pcr ^ v) & 0x02) != 0 618 | { 619 | // nothing 620 | } 621 | self.via_pcr = v; 622 | } 623 | VIA_IER => { 624 | if (v & 0x80) != 0 { 625 | self.ier |= v; 626 | } else { 627 | self.ier &= !v; 628 | } 629 | } 630 | VIA_ANH => { 631 | /* VIA_PA with no handshake. */ 632 | self.via_dra_out = v; 633 | } 634 | 635 | _ => {} 636 | } 637 | } 638 | } 639 | 640 | #[derive(Debug)] 641 | pub struct Keyboard { 642 | rows: [u8; 10], 643 | row: u8, 644 | } 645 | 646 | impl Keyboard { 647 | pub fn new() -> Keyboard { 648 | Keyboard { 649 | rows: [0u8; 10], 650 | row: 0, 651 | } 652 | } 653 | 654 | pub fn reset(&mut self) { 655 | for i in 0..10 { 656 | self.rows[i] = 0; 657 | } 658 | } 659 | 660 | pub fn read(&mut self) -> u8 { 661 | self.rows[self.row as usize] ^ 0xff 662 | } 663 | 664 | pub fn row(&mut self) -> u8 { 665 | self.row 666 | } 667 | 668 | pub fn write(&mut self, v: u8) { 669 | self.row = v; 670 | } 671 | 672 | /// Sets a key represented by the parameter to down. 673 | /// the high nibble is the row, the low nibble is the column 674 | pub fn key_down(&mut self, k: u8) { 675 | self.rows[((k & 0xf0) >> 4) as usize] |= 1 << (k & 0x0f); 676 | } 677 | 678 | /// Sets a key represented by the parameter to up. 679 | /// the high nibble is the row, the low nibble is the column 680 | pub fn key_up(&mut self, k: u8) { 681 | self.rows[((k & 0xf0) >> 4) as usize] &= !(1 << (k & 0x0f)); 682 | } 683 | } 684 | 685 | const MY_ADDRESS: u8 = 8; 686 | 687 | #[derive(Debug)] 688 | enum IeeeState { 689 | IDLE, 690 | LISTEN, 691 | FNAME, 692 | LOAD, 693 | SAVE, 694 | SAVE1, 695 | } 696 | 697 | pub trait Storage { 698 | fn start_filename(&mut self); 699 | 700 | fn next_filename_byte(&mut self, value: u8); 701 | 702 | fn start_save(&mut self); 703 | 704 | fn end_save(&mut self); 705 | 706 | fn fname_done(&mut self); 707 | 708 | fn has_data_to_load(&mut self) -> bool; 709 | 710 | fn load_data_byte(&mut self, index: usize) -> u8; 711 | 712 | fn save_data_byte(&mut self, index: usize, value: u8); 713 | 714 | fn load_data_len(&mut self) -> usize; 715 | } 716 | 717 | impl core::fmt::Debug for dyn Storage { 718 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 719 | write!(f, "Storage") 720 | } 721 | } 722 | 723 | pub struct Ieee<'a> { 724 | state: IeeeState, 725 | dio: u8, 726 | nrfd_i: bool, 727 | ndac_i: bool, 728 | ndac_o: bool, 729 | nrfd_o: bool, 730 | atn: bool, 731 | dav_i: bool, 732 | dav_o: bool, 733 | srq: bool, 734 | eoi_i: bool, 735 | eoi_o: bool, 736 | 737 | old_rom: bool, 738 | data_index: usize, 739 | 740 | storage: &'a mut dyn Storage, 741 | } 742 | 743 | impl<'a> core::fmt::Debug for Ieee<'a> { 744 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 745 | write!(f, "Ieee") 746 | } 747 | } 748 | 749 | impl<'a> Ieee<'a> { 750 | fn new(storage: &'a mut dyn Storage) -> Ieee<'a> { 751 | Ieee { 752 | state: IeeeState::IDLE, 753 | dio: 0, 754 | nrfd_i: true, 755 | ndac_i: true, 756 | ndac_o: true, 757 | nrfd_o: true, 758 | atn: true, 759 | dav_i: true, 760 | dav_o: true, 761 | srq: true, 762 | eoi_i: true, 763 | eoi_o: true, 764 | old_rom: false, 765 | data_index: 0, 766 | storage: storage, 767 | } 768 | } 769 | 770 | fn data_in(&mut self, d8: u8) { 771 | if !self.atn { 772 | match self.state { 773 | IeeeState::IDLE => { 774 | if d8 == 0x20 + MY_ADDRESS { 775 | self.state = IeeeState::LISTEN; 776 | } else if d8 == 0x40 + MY_ADDRESS { 777 | self.old_rom = false; 778 | self.state = IeeeState::LOAD; 779 | } else if d8 == 0x7f && self.storage.has_data_to_load() { 780 | // Old PET ROMs LOAD. 781 | // Assume program starts at either 0x0400 or 0x0401. 782 | self.data_index = if self.storage.load_data_byte(0) == 0 { 783 | 2 784 | } else { 785 | 1 786 | }; 787 | 788 | // Put first data on bus. 789 | self.dio = self.storage.load_data_byte(self.data_index) ^ 0xff; 790 | self.dav_i = false; 791 | 792 | self.old_rom = true; 793 | self.state = IeeeState::LOAD; 794 | } else if d8 == 0x3f { 795 | // Old PET ROMs save 796 | self.old_rom = true; 797 | self.storage.start_save(); 798 | self.state = IeeeState::SAVE1; 799 | } 800 | } 801 | IeeeState::LISTEN => { 802 | // unlisten 803 | if d8 == 0x3f { 804 | self.state = IeeeState::IDLE; 805 | } else if d8 == 0xf0 || d8 == 0xf1 { 806 | // load or save 807 | self.storage.start_filename(); 808 | self.data_index = 0; 809 | self.state = IeeeState::FNAME; 810 | } else if d8 == 0x61 { 811 | self.storage.start_save(); 812 | self.state = IeeeState::SAVE; 813 | } 814 | } 815 | IeeeState::FNAME => { 816 | // unlisten 817 | if d8 == 0x3f { 818 | self.storage.fname_done(); 819 | self.state = IeeeState::IDLE; 820 | } 821 | } 822 | IeeeState::LOAD => { 823 | // untalk 824 | if d8 == 0x5f { 825 | self.state = IeeeState::IDLE; 826 | } 827 | } 828 | IeeeState::SAVE => { 829 | // unlisten 830 | if d8 == 0x3f { 831 | self.storage.end_save(); 832 | self.state = IeeeState::IDLE; 833 | } 834 | } 835 | IeeeState::SAVE1 => { 836 | if self.eoi_o { 837 | // Data comes with ATN low in old ROMs. 838 | self.storage.save_data_byte(self.data_index, d8); 839 | self.data_index += 1; 840 | } else { 841 | // Ignore last byte. 842 | self.storage.end_save(); 843 | self.old_rom = false; 844 | self.state = IeeeState::IDLE; 845 | } 846 | } 847 | } 848 | } else { 849 | match self.state { 850 | IeeeState::FNAME => { 851 | self.storage.next_filename_byte(d8); 852 | } 853 | IeeeState::SAVE => { 854 | self.storage.save_data_byte(self.data_index, d8); 855 | self.data_index += 1; 856 | } 857 | _ => {} 858 | } 859 | } 860 | } 861 | 862 | fn dio_out(&mut self, d8: u8) { 863 | self.dio = d8; 864 | } 865 | 866 | fn dio_in(&self) -> u8 { 867 | self.dio 868 | } 869 | 870 | fn ndac_in(&self) -> bool { 871 | self.ndac_i && self.ndac_o 872 | } 873 | 874 | fn ndac_out(&mut self, flag: bool) { 875 | if !self.ndac_o && flag { 876 | // Positive transition of NDAC. Data acknowledged. 877 | if let IeeeState::LOAD = self.state { 878 | self.dav_i = true; 879 | self.eoi_i = true; 880 | self.data_index += 1; 881 | if self.old_rom && self.data_index == self.storage.load_data_len() { 882 | self.state = IeeeState::IDLE; 883 | } 884 | } 885 | } 886 | self.ndac_o = flag; 887 | } 888 | 889 | fn nrfd_in(&self) -> bool { 890 | self.nrfd_i && self.nrfd_o 891 | } 892 | 893 | fn nrfd_out(&mut self, flag: bool) { 894 | if !self.nrfd_o && flag { 895 | // Positive transition of NRFD. Put data on bus. 896 | if let IeeeState::LOAD = self.state { 897 | if self.data_index < self.storage.load_data_len() { 898 | self.dio = self.storage.load_data_byte(self.data_index) ^ 0xff; 899 | self.dav_i = false; 900 | if self.data_index == self.storage.load_data_len() - 1 { 901 | self.eoi_i = false; 902 | } 903 | } 904 | } 905 | } 906 | self.nrfd_o = flag; 907 | } 908 | 909 | fn eoi_in(&self) -> bool { 910 | self.eoi_i && self.eoi_o 911 | } 912 | 913 | fn eoi_out(&mut self, flag: bool) { 914 | self.eoi_o = flag; 915 | } 916 | 917 | fn atn_out(&mut self, flag: bool) { 918 | if self.atn && !flag { 919 | self.ndac_i = false; 920 | } else if !self.atn && flag { 921 | if let IeeeState::LOAD = self.state { 922 | if self.nrfd_o { 923 | // put 1st byte on bus 924 | self.dio = self.storage.load_data_byte(0) ^ 0xff; 925 | self.dav_i = false; 926 | } 927 | } 928 | } 929 | self.atn = flag; 930 | } 931 | 932 | fn dav_out(&mut self, flag: bool) { 933 | if self.dav_o && !flag { 934 | // Negative transition of DAV. 935 | self.ndac_i = true; 936 | self.nrfd_i = false; 937 | self.data_in(self.dio ^ 0xff); 938 | } else if !self.dav_o && flag { 939 | // Positive transition of DAV. 940 | self.ndac_i = false; 941 | self.nrfd_i = true; 942 | } 943 | self.dav_o = flag; 944 | } 945 | 946 | fn dav_in(&self) -> bool { 947 | self.dav_i && self.dav_o 948 | } 949 | 950 | fn srq_in(&self) -> bool { 951 | self.srq 952 | } 953 | } 954 | -------------------------------------------------------------------------------- /mos6502/data/6502_functional_test.hex: -------------------------------------------------------------------------------- 1 | :16000A000000000000000000008001C38241007F001F71800FFF3C 2 | :200020007F80FF0F8F8F180219021A021B021C02200103020402050206020B015F026002FF 3 | :1C0040006102620263026402650266025B025C025D025E020302040204010501B3 4 | :20020000000000000000000000290060490060090060690060E90060C38241007F8080002C 5 | :2002200002000286048200010387058301400061412000C080E1C1A080030081018002018E 6 | :2002400000810180000003010001028081818081807F80FF00010080800200001F71800F72 7 | :1B026000FF7F80FF0F8F8F00F11F00F0FFFFFFFFF0F00F00FF7F80028000806E 8 | :20040000D8A2FF9AA9008D0002A2054C3304A005D0084C120488888888888888888888F048 9 | :20042000174C2104CACACACACACACACACACAF0DE4C3004D0F44C3504AD0002C900D0FEA9CA 10 | :20044000018D0002A0FE8898AA1008186902EAEAEAEAEAEAEAEAEAEA497F8DE604A9004CB1 11 | :20046000E504CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACAE7 12 | :20048000CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA1C 13 | :2004A000CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACAFC 14 | :2004C000CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACADC 15 | :2004E000CACACACACAF03ECACACACACACACACACACACACACACACACACACACACACACACACACA22 16 | :20050000CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA9B 17 | :20052000CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA7B 18 | :20054000CACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACACA5B 19 | :20056000CACACACACACAEAEAEAEAEAF0084C6D05EAEAEAEAEAEAEAEAEAEAC000F0034C460E 20 | :2005800004AD0002C901D0FEA9028D0002C001D0034C9105A900C900D0FE90FE30FEC9019A 21 | :2005A000F0FEB0FE10FEAAE000D0FE90FE30FEE001F0FEB0FE10FEA8C000D0FE90FE30FE00 22 | :2005C000C001F0FEB0FE10FEAD0002C902D0FEA9038D0002A2FF9AA95548A9AA48CDFE0145 23 | :2005E000D0FEBA8AC9FDD0FE68C9AAD0FE68C955D0FECDFF01D0FEBAE0FFD0FEAD0002C9DE 24 | :2006000003D0FEA9048D0002A9FF4828101A501B901CD01D30034C160670034C1B06B00359 25 | :200620004C2006F00F4C25064C28064C2B064C2E064C310608BAE0FED0FE68C9FFD0FEBAB2 26 | :20064000E0FFD0FEA9004828301A701BB01CF01D10034C520650034C570690034C5C06D062 27 | :200660000F4C61064C64064C67064C6A064C6D060868C930D0FEA9024828D002F0034C7EED 28 | :2006800006B00290034C8506300210034C8C06700250034C9306A9014828F002D0034C9EA2 29 | :2006A000069002B0034CA506300210034CAC06700250034CB306A9804828F002D0034CBE83 30 | :2006C00006B00290034CC506100230034CCC06700250034CD306A9404828F002D0034CDE23 31 | :2006E00006B00290034CE506300210034CEC06500270034CF306A9FD4828F002D0034CFEC6 32 | :20070000069002B0034C0507100230034C0C07500270034C1307A9FE4828D002F0034C1E21 33 | :2007200007B00290034C2507100230034C2C07500270034C3307A97F4828D002F0034C3EFF 34 | :20074000079002B0034C4507300210034C4C07500270034C5307A9BF4828D002F0034C5E1F 35 | :20076000079002B0034C6507100230034C6C07700250034C7307AD0002C904D0FEA9058D61 36 | :200780000002A255A0AAA9FF48A901284808C901D0FE6848C9FFD0FE28A90048A9002848F4 37 | :2007A00008C900D0FE6848C930D0FE28A9FF48A9FF284808C9FFD0FE6848C9FFD0FE28A93E 38 | :2007C0000048A901284808C901D0FE6848C930D0FE28A9FF48A900284808C900D0FE684820 39 | :2007E000C9FFD0FE28A90048A9FF284808C9FFD0FE6848C930D0FE28A9FF48A900286808C4 40 | :20080000C9FFD0FE6848C9FDD0FE28A90048A9FF286808C900D0FE6848C932D0FE28A9FFC5 41 | :2008200048A9FE286808C901D0FE6848C97DD0FE28A90048A900286808C9FFD0FE6848C96C 42 | :20084000B0D0FE28A9FF48A9FF286808C900D0FE6848C97FD0FE28A90048A9FE286808C944 43 | :2008600001D0FE6848C930D0FE28E055D0FEC0AAD0FEAD0002C905D0FEA9068D0002A9009D 44 | :2008800048A93C2849C308C9FFD0FE6848C9B0D0FE28A90048A9C32849C308C900D0FE68A2 45 | :2008A00048C932D0FE28AD0002C906D0FEA9078D0002A224A042A90048A91828EA08C9181F 46 | :2008C000D0FE6848C930D0FE28E024D0FEC042D0FEA2DBA0BDA9FF48A9E728EA08C9E7D015 47 | :2008E000FE6848C9FFD0FE28E0DBD0FEC0BDD0FEAD0002C907D0FEA9088D0002A900482812 48 | :20090000A946A241A0524CEF36EAEAD0FEE8E8F0FE10FE90FE50FEC9ECD0FEE042D0FEC025 49 | :200920004FD0FECAC8C8C849AA4C3209EAEAD0FEE8E8F0FE30FE90FE50FEC946D0FEE04196 50 | :20094000D0FEC052D0FEAD0002C908D0FEA9098D0002A9004828A949A24EA0446C1E37EAD0 51 | :20096000D0FE88880888888828F0FE10FE90FE50FEC9E3D0FEE04FD0FEC03ED0FEBAE0FF1D 52 | :20098000D0FEAD0002C909D0FEA90A8D0002A9004828A94AA253A052205D37088888882889 53 | :2009A000F0FE10FE90FE50FEC9E0D0FEE054D0FEC04CD0FEBAE0FFD0FEAD0002C90AD0FE55 54 | :2009C000A90B8D0002A90048A942A252A04B28008808888888C9E8D0FEE053D0FEC045D074 55 | :2009E000FE68C930D0FEBAE0FFD0FEA9FF48A9BDA2ADA0B428008808888888C917D0FEE08C 56 | :200A0000AED0FEC0AED0FE68C9FFD0FEBAE0FFD0FEAD0002C90BD0FEA90C8D0002A9FF4834 57 | :200A20002818086848C9FED0FE2838086848C9FFD0FE2858086848C9FBD0FE287808684859 58 | :200A4000C9FFD0FE28D8086848C9F7D0FE28F8086848C9FFD0FE28B8086848C9BFD0FE2836 59 | :200A6000A9004828086848C930D0FE2838086848C931D0FE2818086848C930D0FE28780897 60 | :200A80006848C934D0FE2858086848C930D0FE28F8086848C938D0FE28D8086848C930D0B4 61 | :200AA000FE28A9404828086848C970D0FE28B8086848C930D0FE28AD0002C90CD0FEA90D69 62 | :200AC0008D0002A2FEA9FF4828E808E0FFD0FE6848C9FDD0FE28E808E000D0FE6848C97F30 63 | :200AE000D0FE28E808E001D0FE6848C97DD0FE28CA08E000D0FE6848C97FD0FE28CA08E055 64 | :200B0000FFD0FE6848C9FDD0FE28CAA9004828E808E0FFD0FE6848C9B0D0FE28E808E0002D 65 | :200B2000D0FE6848C932D0FE28E808E001D0FE6848C930D0FE28CA08E000D0FE6848C932DD 66 | :200B4000D0FE28CA08E0FFD0FE6848C9B0D0FE28A0FEA9FF4828C808C0FFD0FE6848C9FD78 67 | :200B6000D0FE28C808C000D0FE6848C97FD0FE28C808C001D0FE6848C97DD0FE288808C098 68 | :200B800000D0FE6848C97FD0FE288808C0FFD0FE6848C9FDD0FE2888A9004828C808C0FFE0 69 | :200BA000D0FE6848C9B0D0FE28C808C000D0FE6848C932D0FE28C808C001D0FE6848C93041 70 | :200BC000D0FE288808C000D0FE6848C932D0FE288808C0FFD0FE6848C9B0D0FE28A2FFA9DA 71 | :200BE000FF48288A08C9FFD0FE6848C9FDD0FE2808E8288A08C900D0FE6848C97FD0FE28C3 72 | :200C000008E8288A08C901D0FE6848C97DD0FE28A90048288A08C901D0FE6848C930D0FEEC 73 | :200C20002808CA288A08C900D0FE6848C932D0FE2808CA288A08C9FFD0FE6848C9B0D0FEAF 74 | :200C400028A0FFA9FF48289808C9FFD0FE6848C9FDD0FE2808C8289808C900D0FE6848C96B 75 | :200C60007FD0FE2808C8289808C901D0FE6848C97DD0FE28A90048289808C901D0FE6848E2 76 | :200C8000C930D0FE280888289808C900D0FE6848C932D0FE280888289808C9FFD0FE684837 77 | :200CA000C9B0D0FE28A9FF48A2FF8A28A808C0FFD0FE6848C9FDD0FE2808E88A28A808C0C7 78 | :200CC00000D0FE6848C97FD0FE2808E88A28A808C001D0FE6848C97DD0FE28A90048A900F1 79 | :200CE0008A28A808C001D0FE6848C930D0FE2808CA8A28A808C000D0FE6848C932D0FE2801 80 | :200D000008CA8A28A808C0FFD0FE6848C9B0D0FE28A9FF48A0FF9828AA08E0FFD0FE684895 81 | :200D2000C9FDD0FE2808C89828AA08E000D0FE6848C97FD0FE2808C89828AA08E001D0FE28 82 | :200D40006848C97DD0FE28A90048A9009828AA08E001D0FE6848C930D0FE2808889828AA53 83 | :200D600008E000D0FE6848C932D0FE2808889828AA08E0FFD0FE6848C9B0D0FE28AD00029F 84 | :200D8000C90DD0FEA90E8D0002A201A9FF48289A08AD0101C9FFD0FEA90048289A08AD015E 85 | :200DA00001C930D0FECAA9FF48289A08AD0001C9FFD0FEA90048289A08AD0001C930D0FE73 86 | :200DC000CAA9FF48289A08ADFF01C9FFD0FEA90048289A08ADFF01C930A2019AA9FF482896 87 | :200DE000BA08E001D0FEAD0101C97DD0FEA9FF4828BA08E000D0FEAD0001C97FD0FEA9FFCB 88 | :200E00004828BA08E0FFD0FEADFF01C9FDD0FEA2019AA9004828BA08E001D0FEAD0101C973 89 | :200E200030D0FEA9004828BA08E000D0FEAD0001C932D0FEA9004828BA08E0FFD0FEADFF80 90 | :200E400001C9B0D0FE68AD0002C90ED0FEA90F8D0002A003A9004828B615088A49C3289961 91 | :200E600003020849C3D91802D0FE684930D91D02D0FE8810DFA003A9FF4828B615088A4919 92 | :200E8000C3289903020849C3D91802D0FE68497DD91D02D0FE8810DFA003A9004828BE18F7 93 | :200EA00002088A49C3AA28960C0849C3D91500D0FE684930D91D02D0FE8810DEA003A9FFE6 94 | :200EC0004828BE1802088A49C3AA28960C0849C3D91500D0FE68497DD91D02D0FE8810DE7E 95 | :200EE000A003A200B90C0049C3D91500D0FE960CB9030249C3D91802D0FE8A990302881033 96 | :200F0000E3AD0002C90FD0FEA9108D0002A0FDB61B8A99090188C0FAB0F5A0FDBE1E0196BF 97 | :200F20001288C0FAB0F6A003A200B90C00D91500D0FE960CB90302D91802D0FE8A990302A8 98 | :200F40008810E7AD0002C910D0FEA9118D0002A203A9004828B415089849C3289D0302086E 99 | :200F600049C3DD1802D0FE684930DD1D02D0FECA10DFA203A9FF4828B415089849C3289D45 100 | :200F800003020849C3DD1802D0FE68497DDD1D02D0FECA10DFA203A9004828BC180208988E 101 | :200FA00049C3A828940C0849C3D515D0FE684930DD1D02D0FECA10DFA203A9FF4828BC18F4 102 | :200FC00002089849C3A828940C0849C3D515D0FE68497DDD1D02D0FECA10DFA203A000B57C 103 | :200FE0000C49C3D515D0FE940CBD030249C3DD1802D0FE8A9D0302CA10E5AD0002C911D0AA 104 | :20100000FEA9128D0002A2FDB41B989D0901CAE0FAB0F5A2FDBC1E019412CAE0FAB0F6A286 105 | :2010200003A000B50CD515D0FE940CBD0302DD1802D0FE8A9D0302CA10E9AD0002C912D024 106 | :20104000FEA9138D0002A9004828A615088A49C3AA288E03020849C3AAE0C3D0FE68493060 107 | :20106000CD1D02D0FEA9004828A616088A49C3AA288E04020849C3AAE082D0FE684930CD41 108 | :201080001E02D0FEA9004828A617088A49C3AA288E05020849C3AAE041D0FE684930CD1F0D 109 | :2010A00002D0FEA9004828A618088A49C3AA288E06020849C3AAE000D0FE684930CD200247 110 | :2010C000D0FEA9FF4828A615088A49C3AA288E03020849C3AAE0C3D0FE68497DCD1D02D053 111 | :2010E000FEA9FF4828A616088A49C3AA288E04020849C3AAE082D0FE68497DCD1E02D0FE43 112 | :20110000A9FF4828A617088A49C3AA288E05020849C3AAE041D0FE68497DCD1F02D0FEA9B5 113 | :20112000FF4828A618088A49C3AA288E06020849C3AAE000D0FE68497DCD2002D0FEA9007C 114 | :201140004828AE1802088A49C3AA28860C0849C3C515D0FE684930CD1D02D0FEA9004828E5 115 | :20116000AE1902088A49C3AA28860D0849C3C516D0FE684930CD1E02D0FEA9004828AE1A69 116 | :2011800002088A49C3AA28860E0849C3C517D0FE684930CD1F02D0FEA9004828AE1B020802 117 | :2011A0008A49C3AA28860F0849C3C518D0FE684930CD2002D0FEA9FF4828AE1802088A491A 118 | :2011C000C3AA28860C0849C3AAE415D0FE68497DCD1D02D0FEA9FF4828AE1902088A49C3FC 119 | :2011E000AA28860D0849C3AAE416D0FE68497DCD1E02D0FEA9FF4828AE1A02088A49C3AAF1 120 | :2012000028860E0849C3AAE417D0FE68497DCD1F02D0FEA9FF4828AE1B02088A49C3AA284E 121 | :20122000860F0849C3AAE418D0FE68497DCD2002D0FEA9004828A2C308EC1802D0FE68499B 122 | :2012400030CD1D02D0FEA9004828A28208EC1902D0FE684930CD1E02D0FEA9004828A241F2 123 | :2012600008EC1A02D0FE684930CD1F02D0FEA9004828A20008EC1B02D0FE684930CD200289 124 | :20128000D0FEA9FF4828A2C308EC1802D0FE68497DCD1D02D0FEA9FF4828A28208EC1902F4 125 | :2012A000D0FE68497DCD1E02D0FEA9FF4828A24108EC1A02D0FE68497DCD1F02D0FEA9FF0C 126 | :2012C0004828A20008EC1B02D0FE68497DCD2002D0FEA200A50C49C3C515D0FE860CAD03E9 127 | :2012E0000249C3CD1802D0FE8E0302A50D49C3C516D0FE860DAD040249C3CD1902D0FE8E9B 128 | :201300000402A50E49C3C517D0FE860EAD050249C3CD1A02D0FE8E0502A50F49C3C518D051 129 | :20132000FE860FAD060249C3CD1B02D0FE8E0602AD0002C913D0FEA9148D0002A90048284D 130 | :20134000A415089849C3A8288C03020849C3A8C0C3D0FE684930CD1D02D0FEA9004828A462 131 | :2013600016089849C3A8288C04020849C3A8C082D0FE684930CD1E02D0FEA9004828A4170D 132 | :20138000089849C3A8288C05020849C3A8C041D0FE684930CD1F02D0FEA9004828A4180839 133 | :2013A0009849C3A8288C06020849C3A8C000D0FE684930CD2002D0FEA9FF4828A4150898CC 134 | :2013C00049C3A8288C03020849C3A8C0C3D0FE68497DCD1D02D0FEA9FF4828A416089849F0 135 | :2013E000C3A8288C04020849C3A8C082D0FE68497DCD1E02D0FEA9FF4828A417089849C394 136 | :20140000A8288C05020849C3A8C041D0FE68497DCD1F02D0FEA9FF4828A418089849C3A8CC 137 | :20142000288C06020849C3A8C000D0FE68497DCD2002D0FEA9004828AC1802089849C3A888 138 | :2014400028840C0849C3A8C415D0FE684930CD1D02D0FEA9004828AC1902089849C3A8287A 139 | :20146000840D0849C3A8C416D0FE684930CD1E02D0FEA9004828AC1A02089849C3A82884FA 140 | :201480000E0849C3A8C417D0FE684930CD1F02D0FEA9004828AC1B02089849C3A828840F4B 141 | :2014A0000849C3A8C418D0FE684930CD2002D0FEA9FF4828AC1802089849C3A828840C0836 142 | :2014C00049C3A8C515D0FE68497DCD1D02D0FEA9FF4828AC1902089849C3A828840D08498B 143 | :2014E000C3A8C516D0FE68497DCD1E02D0FEA9FF4828AC1A02089849C3A828840E0849C3ED 144 | :20150000A8C517D0FE68497DCD1F02D0FEA9FF4828AC1B02089849C3A828840F0849C3A8E3 145 | :20152000C518D0FE68497DCD2002D0FEA9004828A0C308CC1802D0FE684930CD1D02D0FE42 146 | :20154000A9004828A08208CC1902D0FE684930CD1E02D0FEA9004828A04108CC1A02D0FE3F 147 | :20156000684930CD1F02D0FEA9004828A00008CC1B02D0FE684930CD2002D0FEA9FF4828A0 148 | :20158000A0C308CC1802D0FE68497DCD1D02D0FEA9FF4828A08208CC1902D0FE68497DCD52 149 | :2015A0001E02D0FEA9FF4828A04108CC1A02D0FE68497DCD1F02D0FEA9FF4828A00008CC10 150 | :2015C0001B02D0FE68497DCD2002D0FEA000A50C49C3C515D0FE840CAD030249C3CD1802FB 151 | :2015E000D0FE8C0302A50D49C3C516D0FE840DAD040249C3CD1902D0FE8C0402A50E49C3CE 152 | :20160000C517D0FE840EAD050249C3CD1A02D0FE8C0502A50F49C3C518D0FE840FAD0602D1 153 | :2016200049C3CD1B02D0FE8C0602AD0002C914D0FEA9158D0002A203A9004828B5150849D2 154 | :20164000C3289D03020849C3DD1802D0FE684930DD1D02D0FECA10E0A203A9FF4828B51538 155 | :201660000849C3289D03020849C3DD1802D0FE68497DDD1D02D0FECA10E0A203A900482843 156 | :20168000BD18020849C328950C0849C3D515D0FE684930DD1D02D0FECA10E1A203A9FF48CF 157 | :2016A00028BD18020849C328950C0849C3D515D0FE68497DDD1D02D0FECA10E1A203A0008A 158 | :2016C000B50C49C3D515D0FE940CBD030249C3DD1802D0FE8A9D0302CA10E5AD0002C915DA 159 | :2016E000D0FEA9168D0002A003A9004828B1260849C3289903020849C3D91802D0FE6849DB 160 | :2017000030D91D02D0FE8810E0A003A9FF4828B1260849C3289903020849C3D91802D0FE1A 161 | :2017200068497DD91D02D0FE8810E0A003A200B9030249C3D91802D0FE8A9903028810EFBE 162 | :20174000A003A9004828B918020849C32891320849C3D126D0FE684930D91D02D0FE8810E3 163 | :20176000E1A003A9FF4828B918020849C32891320849C3D126D0FE68497DD91D02D0FE88A6 164 | :2017800010E1A003A200B9030249C3D91802D0FE8A9903028810EFA206A003A9004828A1D4 165 | :2017A000260849C32881320849C3D91802D0FE684930D91D02D0FECACA8810DFA206A00342 166 | :2017C000A9FF4828A1260849C32881320849C3D91802D0FE68497DD91D02D0FECACA881046 167 | :2017E000DFA003A200B9030249C3D91802D0FE8A9903028810EFAD0002C916D0FEA9178DE2 168 | :201800000002A2FDB51B9D0901CAE0FAB0F6A2FDBD1E019512CAE0FAB0F6A203A000B50CF4 169 | :20182000D515D0FE940CBD0302DD1802D0FE8A9D0302CA10E9A0FBA2FEA12E990B01CACA97 170 | :2018400088C0F8B0F4A003A200B90302D91802D0FE8A9903028810F1A0FBB92001913A8867 171 | :20186000C0F8B0F6A003A200B90302D91802D0FE8A9903028810F1A0FBA2FEB130813ACAF4 172 | :20188000CA88C0F8B0F5A003A200B90302D91802D0FE8A9903028810F1AD0002C917D0FEC7 173 | :2018A000A9188D0002A9004828A5150849C3288D03020849C3C9C3D0FE684930CD1D02D02C 174 | :2018C000FEA9004828A5160849C3288D04020849C3C982D0FE684930CD1E02D0FEA90048AD 175 | :2018E00028A5170849C3288D05020849C3C941D0FE684930CD1F02D0FEA9004828A51808CD 176 | :2019000049C3288D06020849C3C900D0FE684930CD2002D0FEA9FF4828A5150849C3288D1A 177 | :2019200003020849C3C9C3D0FE68497DCD1D02D0FEA9FF4828A5160849C3288D0402084959 178 | :20194000C3C982D0FE68497DCD1E02D0FEA9FF4828A5170849C3288D05020849C3C941D030 179 | :20196000FE68497DCD1F02D0FEA9FF4828A5180849C3288D06020849C3C900D0FE68497D00 180 | :20198000CD2002D0FEA9004828AD18020849C328850C0849C3C515D0FE684930CD1D02D084 181 | :2019A000FEA9004828AD19020849C328850D0849C3C516D0FE684930CD1E02D0FEA9004830 182 | :2019C00028AD1A020849C328850E0849C3C517D0FE684930CD1F02D0FEA9004828AD1B0209 183 | :2019E0000849C328850F0849C3C518D0FE684930CD2002D0FEA9FF4828AD18020849C3289F 184 | :201A0000850C0849C3C515D0FE68497DCD1D02D0FEA9FF4828AD19020849C328850D084992 185 | :201A2000C3C516D0FE68497DCD1E02D0FEA9FF4828AD1A020849C328850E0849C3C517D0E1 186 | :201A4000FE68497DCD1F02D0FEA9FF4828AD1B020849C328850F0849C3C518D0FE68497DFF 187 | :201A6000CD2002D0FEA9004828A9C308CD1802D0FE684930CD1D02D0FEA9004828A9820880 188 | :201A8000CD1902D0FE684930CD1E02D0FEA9004828A94108CD1A02D0FE684930CD1F02D093 189 | :201AA000FEA9004828A90008CD1B02D0FE684930CD2002D0FEA9FF4828A9C308CD1802D0C5 190 | :201AC000FE68497DCD1D02D0FEA9FF4828A98208CD1902D0FE68497DCD1E02D0FEA9FF4846 191 | :201AE00028A94108CD1A02D0FE68497DCD1F02D0FEA9FF4828A90008CD1B02D0FE68497D7C 192 | :201B0000CD2002D0FEA200A50C49C3C515D0FE860CAD030249C3CD1802D0FE8E0302A50DB7 193 | :201B200049C3C516D0FE860DAD040249C3CD1902D0FE8E0402A50E49C3C517D0FE860EADAA 194 | :201B4000050249C3CD1A02D0FE8E0502A50F49C3C518D0FE860FAD060249C3CD1B02D0FEAD 195 | :201B60008E0602AD0002C918D0FEA9198D0002A90048A9FF28241808C9FFD0FE6848C932DA 196 | :201B8000D0FE28A90048A90128241708C901D0FE6848C970D0FE28A90048A90128241608CD 197 | :201BA000C901D0FE6848C9B2D0FE28A90048A90128241508C901D0FE6848C9F0D0FE28A9C8 198 | :201BC000FF48A9FF28241808C9FFD0FE6848C93FD0FE28A9FF48A90128241708C901D0FEC9 199 | :201BE0006848C97DD0FE28A9FF48A90128241608C901D0FE6848C9BFD0FE28A9FF48A90195 200 | :201C000028241508C901D0FE6848C9FDD0FE28A90048A9FF282C1B0208C9FFD0FE6848C936 201 | :201C200032D0FE28A90048A901282C1A0208C901D0FE6848C970D0FE28A90048A901282C03 202 | :201C4000190208C901D0FE6848C9B2D0FE28A90048A901282C180208C901D0FE6848C9F096 203 | :201C6000D0FE28A9FF48A9FF282C1B0208C9FFD0FE6848C93FD0FE28A9FF48A901282C1A11 204 | :201C80000208C901D0FE6848C97DD0FE28A9FF48A901282C190208C901D0FE6848C9BFD005 205 | :201CA000FE28A9FF48A901282C180208C901D0FE6848C9FDD0FE28AD0002C919D0FEA91AC8 206 | :201CC0008D0002A90048A28028E419086848C931D0FE28CAE419086848C933D0FE28CAE476 207 | :201CE0001908E07ED0FE6848C9B0D0FE28A9FF48A28028E419086848C97DD0FE28CAE41986 208 | :201D0000086848C97FD0FE28CAE41908E07ED0FE6848C9FCD0FE28A90048A28028EC1C0222 209 | :201D2000086848C931D0FE28CAEC1C02086848C933D0FE28CAEC1C0208E07ED0FE6848C9FC 210 | :201D4000B0D0FE28A9FF48A28028EC1C02086848C97DD0FE28CAEC1C02086848C97FD0FE03 211 | :201D600028CAEC1C0208E07ED0FE6848C9FCD0FE28A90048A28028E07F086848C931D0FEAE 212 | :201D800028CAE07F086848C933D0FE28CAE07F08E07ED0FE6848C9B0D0FE28A9FF48A280BA 213 | :201DA00028E07F086848C97DD0FE28CAE07F086848C97FD0FE28CAE07F08E07ED0FE68487C 214 | :201DC000C9FCD0FE28AD0002C91AD0FEA91B8D0002A90048A08028C419086848C931D0FEFF 215 | :201DE0002888C419086848C933D0FE2888C41908C07ED0FE6848C9B0D0FE28A9FF48A08004 216 | :201E000028C419086848C97DD0FE2888C419086848C97FD0FE2888C41908C07ED0FE684845 217 | :201E2000C9FCD0FE28A90048A08028CC1C02086848C931D0FE2888CC1C02086848C933D026 218 | :201E4000FE2888CC1C0208C07ED0FE6848C9B0D0FE28A9FF48A08028CC1C02086848C97D97 219 | :201E6000D0FE2888CC1C02086848C97FD0FE2888CC1C0208C07ED0FE6848C9FCD0FE28A96A 220 | :201E80000048A08028C07F086848C931D0FE2888C07F086848C933D0FE2888C07F08C07E4D 221 | :201EA000D0FE6848C9B0D0FE28A9FF48A08028C07F086848C97DD0FE2888C07F086848C982 222 | :201EC0007FD0FE2888C07F08C07ED0FE6848C9FCD0FE28AD0002C91BD0FEA91C8D0002A9E9 223 | :201EE0000048A98028C51908C980D0FE6848C931D0FE28A90048A97F28C51908C97FD0FE6F 224 | :201F00006848C933D0FE28A90048A97E28C51908C97ED0FE6848C9B0D0FE28A9FF48A980AC 225 | :201F200028C51908C980D0FE6848C97DD0FE28A9FF48A97F28C51908C97FD0FE6848C97F5C 226 | :201F4000D0FE28A9FF48A97E28C51908C97ED0FE6848C9FCD0FE28A90048A98028CD1C02B9 227 | :201F600008C980D0FE6848C931D0FE28A90048A97F28CD1C0208C97FD0FE6848C933D0FEDE 228 | :201F800028A90048A97E28CD1C0208C97ED0FE6848C9B0D0FE28A9FF48A98028CD1C02087E 229 | :201FA000C980D0FE6848C97DD0FE28A9FF48A97F28CD1C0208C97FD0FE6848C97FD0FE28E7 230 | :201FC000A9FF48A97E28CD1C0208C97ED0FE6848C9FCD0FE28A90048A98028C97F08C98074 231 | :201FE000D0FE6848C931D0FE28A90048A97F28C97F08C97FD0FE6848C933D0FE28A9004839 232 | :20200000A97E28C97F08C97ED0FE6848C9B0D0FE28A9FF48A98028C97F08C980D0FE684895 233 | :20202000C97DD0FE28A9FF48A97F28C97F08C97FD0FE6848C97FD0FE28A9FF48A97E28C9C8 234 | :202040007F08C97ED0FE6848C9FCD0FE28A204A90048A98028D51508C980D0FE6848C931DE 235 | :20206000D0FE28A90048A97F28D51508C97FD0FE6848C933D0FE28A90048A97E28D515084D 236 | :20208000C97ED0FE6848C9B0D0FE28A9FF48A98028D51508C980D0FE6848C97DD0FE28A92D 237 | :2020A000FF48A97F28D51508C97FD0FE6848C97FD0FE28A9FF48A97E28D51508C97ED0FE4D 238 | :2020C0006848C9FCD0FE28A90048A98028DD180208C980D0FE6848C931D0FE28A90048A903 239 | :2020E0007F28DD180208C97FD0FE6848C933D0FE28A90048A97E28DD180208C97ED0FE68C6 240 | :2021000048C9B0D0FE28A9FF48A98028DD180208C980D0FE6848C97DD0FE28A9FF48A97FAD 241 | :2021200028DD180208C97FD0FE6848C97FD0FE28A9FF48A97E28DD180208C97ED0FE684871 242 | :20214000C9FCD0FE28A004A208A90048A98028D9180208C980D0FE6848C931D0FE28A900D9 243 | :2021600048A97F28D9180208C97FD0FE6848C933D0FE28A90048A97E28D9180208C97ED0C2 244 | :20218000FE6848C9B0D0FE28A9FF48A98028D9180208C980D0FE6848C97DD0FE28A9FF48F3 245 | :2021A000A97F28D9180208C97FD0FE6848C97FD0FE28A9FF48A97E28D9180208C97ED0FE81 246 | :2021C0006848C9FCD0FE28A90048A98028C12608C980D0FE6848C931D0FE28A90048A97F93 247 | :2021E00028C12608C97FD0FE6848C933D0FE28A90048A97E28C12608C97ED0FE6848C9B0A3 248 | :20220000D0FE28A9FF48A98028C12608C980D0FE6848C97DD0FE28A9FF48A97F28C1260866 249 | :20222000C97FD0FE6848C97FD0FE28A9FF48A97E28C12608C97ED0FE6848C9FCD0FE28A943 250 | :202240000048A98028D12608C980D0FE6848C931D0FE28A90048A97F28D12608C97FD0FED9 251 | :202260006848C933D0FE28A90048A97E28D12608C97ED0FE6848C9B0D0FE28A9FF48A98030 252 | :2022800028D12608C980D0FE6848C97DD0FE28A9FF48A97F28D12608C97FD0FE6848C97FC7 253 | :2022A000D0FE28A9FF48A97E28D12608C97ED0FE6848C9FCD0FE28AD0002C91CD0FEA91D42 254 | :2022C0008D0002A205A90048B513280A08DD2102D0FE684930DD3902D0FECA10E8A205A92E 255 | :2022E000FF48B513280A08DD2102D0FE68497CDD3902D0FECA10E8A205A90048B513284A20 256 | :2023000008DD2D02D0FE684930DD4502D0FECA10E8A205A9FF48B513284A08DD2D02D0FE93 257 | :2023200068497CDD4502D0FECA10E8A205A90048B513282A08DD2102D0FE684930DD39023B 258 | :20234000D0FECA10E8A205A9FE48B513282A08DD2102D0FE68497CDD3902D0FECA10E8A2F0 259 | :2023600005A90148B513282A08DD2702D0FE684930DD3F02D0FECA10E8A205A9FF48B51382 260 | :20238000282A08DD2702D0FE68497CDD3F02D0FECA10E8A205A90048B513286A08DD2D022E 261 | :2023A000D0FE684930DD4502D0FECA10E8A205A9FE48B513286A08DD2D02D0FE68497CDDDE 262 | :2023C0004502D0FECA10E8A205A90148B513286A08DD3302D0FE684930DD4B02D0FECA1098 263 | :2023E000E8A205A9FF48B513286A08DD3302D0FE68497CDD4B02D0FECA10E8AD0002C91DA0 264 | :20240000D0FEA91E8D0002A205A90048B513850C28060C08A50CDD2102D0FE684930DD39EF 265 | :2024200002D0FECA10E3A205A9FF48B513850C28060C08A50CDD2102D0FE68497CDD390219 266 | :20244000D0FECA10E3A205A90048B513850C28460C08A50CDD2D02D0FE684930DD4502D01E 267 | :20246000FECA10E3A205A9FF48B513850C28460C08A50CDD2D02D0FE68497CDD4502D0FE85 268 | :20248000CA10E3A205A90048B513850C28260C08A50CDD2102D0FE684930DD3902D0FECA1C 269 | :2024A00010E3A205A9FE48B513850C28260C08A50CDD2102D0FE68497CDD3902D0FECA106C 270 | :2024C000E3A205A90148B513850C28260C08A50CDD2702D0FE684930DD3F02D0FECA10E3B6 271 | :2024E000A205A9FF48B513850C28260C08A50CDD2702D0FE68497CDD3F02D0FECA10E3A28D 272 | :2025000005A90048B513850C28660C08A50CDD2D02D0FE684930DD4502D0FECA10E3A20508 273 | :20252000A9FE48B513850C28660C08A50CDD2D02D0FE68497CDD4502D0FECA10E3A205A9FA 274 | :202540000148B513850C28660C08A50CDD3302D0FE684930DD4B02D0FECA10E3A205A9FFC1 275 | :2025600048B513850C28660C08A50CDD3302D0FE68497CDD4B02D0FECA10E3AD0002C91E0F 276 | :20258000D0FEA91F8D0002A205A90048B5138D0302280E030208AD0302DD2102D0FE6849B0 277 | :2025A00030DD3902D0FECA10E0A205A9FF48B5138D0302280E030208AD0302DD2102D0FE97 278 | :2025C00068497CDD3902D0FECA10E0A205A90048B5138D0302284E030208AD0302DD2D02FB 279 | :2025E000D0FE684930DD4502D0FECA10E0A205A9FF48B5138D0302284E030208AD0302DD7D 280 | :202600002D02D0FE68497CDD4502D0FECA10E0A205A90048B5138D0302282E030208AD03DF 281 | :2026200002DD2102D0FE684930DD3902D0FECA10E0A205A9FE48B5138D0302282E030208F6 282 | :20264000AD0302DD2102D0FE68497CDD3902D0FECA10E0A205A90148B5138D0302282E03E1 283 | :202660000208AD0302DD2702D0FE684930DD3F02D0FECA10E0A205A9FF48B5138D0302282A 284 | :202680002E030208AD0302DD2702D0FE68497CDD3F02D0FECA10E0A205A90048B5138D03B6 285 | :2026A00002286E030208AD0302DD2D02D0FE684930DD4502D0FECA10E0A205A9FE48B513FE 286 | :2026C0008D0302286E030208AD0302DD2D02D0FE68497CDD4502D0FECA10E0A205A90148C7 287 | :2026E000B5138D0302286E030208AD0302DD3302D0FE684930DD4B02D0FECA10E0A205A968 288 | :20270000FF48B5138D0302286E030208AD0302DD3302D0FE68497CDD4B02D0FECA10E0AD57 289 | :202720000002C91FD0FEA9208D0002A205A90048B513950C28160C08B50CDD2102D0FE683F 290 | :202740004930DD3902D0FECA10E3A205A9FF48B513950C28160C08B50CDD2102D0FE6849CB 291 | :202760007CDD3902D0FECA10E3A205A90048B513950C28560C08B50CDD2D02D0FE6849302B 292 | :20278000DD4502D0FECA10E3A205A9FF48B513950C28560C08B50CDD2D02D0FE68497CDD53 293 | :2027A0004502D0FECA10E3A205A90048B513950C28360C08B50CDD2102D0FE684930DD394E 294 | :2027C00002D0FECA10E3A205A9FE48B513950C28360C08B50CDD2102D0FE68497CDD390227 295 | :2027E000D0FECA10E3A205A90148B513950C28360C08B50CDD2702D0FE684930DD3F02D076 296 | :20280000FECA10E3A205A9FF48B513950C28360C08B50CDD2702D0FE68497CDD3F02D0FEDD 297 | :20282000CA10E3A205A90048B513950C28760C08B50CDD2D02D0FE684930DD4502D0FECAF0 298 | :2028400010E3A205A9FE48B513950C28760C08B50CDD2D02D0FE68497CDD4502D0FECA1040 299 | :20286000E3A205A90148B513950C28760C08B50CDD3302D0FE684930DD4B02D0FECA10E38A 300 | :20288000A205A9FF48B513950C28760C08B50CDD3302D0FE68497CDD4B02D0FECA10E3AD56 301 | :2028A0000002C920D0FEA9218D0002A205A90048B5139D0302281E030208BD0302DD2102EF 302 | :2028C000D0FE684930DD3902D0FECA10E0A205A9FF48B5139D0302281E030208BD0302DDB6 303 | :2028E0002102D0FE68497CDD3902D0FECA10E0A205A90048B5139D0302285E030208BD03C5 304 | :2029000002DD2D02D0FE684930DD4502D0FECA10E0A205A9FF48B5139D0302285E030208BA 305 | :20292000BD0302DD2D02D0FE68497CDD4502D0FECA10E0A205A90048B5139D0302283E03B7 306 | :202940000208BD0302DD2102D0FE684930DD3902D0FECA10E0A205A9FE48B5139D03022834 307 | :202960003E030208BD0302DD2102D0FE68497CDD3902D0FECA10E0A205A90148B5139D03AE 308 | :2029800002283E030208BD0302DD2702D0FE684930DD3F02D0FECA10E0A205A9FF48B51346 309 | :2029A0009D0302283E030208BD0302DD2702D0FE68497CDD3F02D0FECA10E0A205A9004801 310 | :2029C000B5139D0302287E030208BD0302DD2D02D0FE684930DD4502D0FECA10E0A205A961 311 | :2029E000FE48B5139D0302287E030208BD0302DD2D02D0FE68497CDD4502D0FECA10E0A25D 312 | :202A000005A90148B5139D0302287E030208BD0302DD3302D0FE684930DD4B02D0FECA104D 313 | :202A2000E0A205A9FF48B5139D0302287E030208BD0302DD3302D0FE68497CDD4B02D0FE3B 314 | :202A4000CA10E0AD0002C921D0FEA9228D0002A200A97E850CA9004828E60C08A50CDD51AF 315 | :202A600002D0FE684930DD5602D0FEE8E002D004A9FE850CE005D0DDCAE60CA9004828C69F 316 | :202A80000C08A50CDD5102D0FE684930DD5602D0FECA300AE001D0E3A981850CD0DDA200ED 317 | :202AA000A97E850CA9FF4828E60C08A50CDD5102D0FE68497DDD5602D0FEE8E002D004A925 318 | :202AC000FE850CE005D0DDCAE60CA9FF4828C60C08A50CDD5102D0FE68497DDD5602D0FE47 319 | :202AE000CA300AE001D0E3A981850CD0DDAD0002C922D0FEA9238D0002A200A97E8D0302B8 320 | :202B0000A9004828EE030208AD0302DD5102D0FE684930DD5602D0FEE8E002D005A9FE8D3A 321 | :202B20000302E005D0DACAEE0302A9004828CE030208AD0302DD5102D0FE684930DD56028A 322 | :202B4000D0FECA300BE001D0E1A9818D0302D0DAA200A97E8D0302A9FF4828EE030208AD8F 323 | :202B60000302DD5102D0FE68497DDD5602D0FEE8E002D005A9FE8D0302E005D0DACAEE03FF 324 | :202B800002A9FF4828CE030208AD0302DD5102D0FE68497DDD5602D0FECA300BE001D0E1C8 325 | :202BA000A9818D0302D0DAAD0002C923D0FEA9248D0002A200A97E950CA9004828F60C085D 326 | :202BC000B50CDD5102D0FE684930DD5602D0FEB50CE8E002D002A9FEE005D0DBCAA90295B4 327 | :202BE0000CA9004828D60C08B50CDD5102D0FE684930DD5602D0FEB50CCA3008E001D0DFD0 328 | :202C0000A981D0DBA200A97E950CA9FF4828F60C08B50CDD5102D0FE68497DDD5602D0FE63 329 | :202C2000B50CE8E002D002A9FEE005D0DBCAA902950CA9FF4828D60C08B50CDD5102D0FE29 330 | :202C400068497DDD5602D0FEB50CCA3008E001D0DFA981D0DBAD0002C924D0FEA9258D0056 331 | :202C600002A200A97E9D0302A9004828FE030208BD0302DD5102D0FE684930DD5602D0FE1F 332 | :202C8000BD0302E8E002D002A9FEE005D0D7CAA9029D0302A9004828DE030208BD0302DDE9 333 | :202CA0005102D0FE684930DD5602D0FEBD0302CA3008E001D0DBA981D0D7A200A97E9D0385 334 | :202CC00002A9FF4828FE030208BD0302DD5102D0FE68497DDD5602D0FEBD0302E8E002D082 335 | :202CE00002A9FEE005D0D7CAA9029D0302A9FF4828DE030208BD0302DD5102D0FE68497D97 336 | :202D0000DD5602D0FEBD0302CA3008E001D0DBA981D0D7AD0002C925D0FEA9268D0002A224 337 | :202D200003B51E8D0A02A90048BD6B022820090208DD7302D0FE684930DD7702D0FECA10AF 338 | :202D4000E0A203B51E8D0A02A9FF48BD6B022820090208DD7302D0FE68497DDD7702D0FE9B 339 | :202D6000CA10E0A203B51E850CA90048BD6B0228250C08DD7302D0FE684930DD7702D0FEEF 340 | :202D8000CA10E2A203B51E850CA9FF48BD6B0228250C08DD7302D0FE68497DDD7702D0FE81 341 | :202DA000CA10E2A203B51E8D0302A90048BD6B02282D030208DD7302D0FE684930DD770279 342 | :202DC000D0FECA10E0A203B51E8D0302A9FF48BD6B02282D030208DD7302D0FE68497DDDBA 343 | :202DE0007702D0FECA1002A203A90048BD6B0228351E08DD7302D0FE684930DD7702D0FE48 344 | :202E0000CA10E6A203A9FF48BD6B0228351E08DD7302D0FE68497DDD7702D0FECA10E6A2DC 345 | :202E200003A90048BD6B02283D5F0208DD7302D0FE684930DD7702D0FECA10E5A203A9FF75 346 | :202E400048BD6B02283D5F0208DD7302D0FE68497DDD7702D0FECA10E5A003A90048B96B49 347 | :202E60000228395F0208D97302D0FE684930D97702D0FE8810E5A003A9FF48B96B022839D2 348 | :202E80005F0208D97302D0FE68497DD97702D0FE8810E5A206A003A90048B96B0228213CFB 349 | :202EA00008D97302D0FE684930D97702D0FECACA8810E4A206A003A9FF48B96B0228213CF7 350 | :202EC00008D97302D0FE68497DD97702D0FECACA8810E4A003A90048B96B0228313C08D940 351 | :202EE0007302D0FE684930D97702D0FE8810E6A003A9FF48B96B0228313C08D97302D0FE9E 352 | :202F000068497DD97702D0FE8810E6AD0002C926D0FEA9278D0002A203B5228D0D02A90059 353 | :202F200048BD6F0228200C0208DD7302D0FE684930DD7702D0FECA10E0A203B5228D0D02C6 354 | :202F4000A9FF48BD6F0228200C0208DD7302D0FE68497DDD7702D0FECA10E0A203B52285C8 355 | :202F60000CA90048BD6F0228450C08DD7302D0FE684930DD7702D0FECA10E2A203B52285C3 356 | :202F80000CA9FF48BD6F0228450C08DD7302D0FE68497DDD7702D0FECA10E2A203B5228D4F 357 | :202FA0000302A90048BD6F02284D030208DD7302D0FE684930DD7702D0FECA10E0A203B532 358 | :202FC000228D0302A9FF48BD6F02284D030208DD7302D0FE68497DDD7702D0FECA1002A2AD 359 | :202FE00003A90048BD6F0228552208DD7302D0FE684930DD7702D0FECA10E6A203A9FF488E 360 | :20300000BD6F0228552208DD7302D0FE68497DDD7702D0FECA10E6A203A90048BD6F0228BD 361 | :203020005D630208DD7302D0FE684930DD7702D0FECA10E5A203A9FF48BD6F02285D630235 362 | :2030400008DD7302D0FE68497DDD7702D0FECA10E5A003A90048B96F022859630208D9733F 363 | :2030600002D0FE684930D97702D0FE8810E5A003A9FF48B96F022859630208D97302D0FE3B 364 | :2030800068497DD97702D0FE8810E5A206A003A90048B96F0228414408D97302D0FE68497D 365 | :2030A00030D97702D0FECACA8810E4A206A003A9FF48B96F0228414408D97302D0FE6849C9 366 | :2030C0007DD97702D0FECACA8810E4A003A90048B96F0228514408D97302D0FE684930D9EA 367 | :2030E0007702D0FE8810E6A003A9FF48B96F0228514408D97302D0FE68497DD97702D0FE1F 368 | :203100008810E6AD0002C927D0FEA9288D0002A203B51A8D1002A90048BD670228200F02E1 369 | :2031200008DD7302D0FE684930DD7702D0FECA10E0A203B51A8D1002A9FF48BD6702282037 370 | :203140000F0208DD7302D0FE68497DDD7702D0FECA10E0A203B51A850CA90048BD670228E6 371 | :20316000050C08DD7302D0FE684930DD7702D0FECA10E2A203B51A850CA9FF48BD67022812 372 | :20318000050C08DD7302D0FE68497DDD7702D0FECA10E2A203B51A8D0302A90048BD6702CB 373 | :2031A000280D030208DD7302D0FE684930DD7702D0FECA10E0A203B51A8D0302A9FF48BD3B 374 | :2031C0006702280D030208DD7302D0FE68497DDD7702D0FECA1002A203A90048BD67022812 375 | :2031E000151A08DD7302D0FE684930DD7702D0FECA10E6A203A9FF48BD670228151A08DDBC 376 | :203200007302D0FE68497DDD7702D0FECA10E6A203A90048BD6702281D5B0208DD7302D0D1 377 | :20322000FE684930DD7702D0FECA10E5A203A9FF48BD6702281D5B0208DD7302D0FE684996 378 | :203240007DDD7702D0FECA10E5A003A90048B9670228195B0208D97302D0FE684930D97765 379 | :2032600002D0FE8810E5A003A9FF48B9670228195B0208D97302D0FE68497DD97702D0FE3C 380 | :203280008810E5A206A003A90048B9670228014C08D97302D0FE684930D97702D0FECACA25 381 | :2032A0008810E4A206A003A9FF48B9670228014C08D97302D0FE68497DD97702D0FECACABA 382 | :2032C0008810E4A003A90048B9670228114C08D97302D0FE684930D97702D0FE8810E6A0F4 383 | :2032E00003A9FF48B9670228114C08D97302D0FE68497DD97702D0FE8810E658AD0002C974 384 | :2033000028D0FEA9298D0002D8A20EA0FFA900850C850D850E8D0302850F8510A9FF8512D6 385 | :203320008D0402A90285111820A235E60CE60F080868298228D002E610051085113820A20B 386 | :2033400035C60CE60DD0E0A9008510EE0302E60E086829828511C612CE0402A50E850FD02A 387 | :20336000C6AD0002C929D0FEA92A8D0002F8A20EA0FFA999850D850E8D0302850FA90185B3 388 | :203380000C8510A90085128D040238206F34C60CA50FD008C610A999850FD012290FD00CBE 389 | :2033A000C60FC60FC60FC60FC60FC60FC60F18206F34E60CA50DF015290FD00CC60DC60DFC 390 | :2033C000C60DC60DC60DC60DC60D4C8A33A999850DA50EF030290FD018C60EC60EC60EC6BC 391 | :2033E0000EC60EC60EE612E612E612E612E612E612C60EE612A5128D0402A50E8D03028567 392 | :203400000FE610D085AD0002C92AD0FEA92B8D000218D808A9556955C9AAD0FE18F808A9C9 393 | :20342000556955C910D0FED828A9556955C910D0FE28A9556955C9AAD0FE18A93448A9550F 394 | :203440004808F8A93448A94C4808D840A9556955C910D0FE40A9556955C9AAD0FEAD000253 395 | :20346000C92BD0FEA9F08D00024C69344C000408A50D650E08C50FD0FE682901C510D0FE1D 396 | :203480002808A50DE51208C50FD0FE682901C510D0FE2808A50D6D030208C50FD0FE6829E5 397 | :2034A00001C510D0FE2808A50DED040208C50FD0FE682901C510D0FE2808A50E8D1302A58A 398 | :2034C0000D20120208C50FD0FE682901C510D0FE2808A5128D1602A50D20150208C50FD0AB 399 | :2034E000FE682901C510D0FE2808A50D750008C50FD0FE682901C510D0FE2808A50DF50488 400 | :2035000008C50FD0FE682901C510D0FE2808A50D7DF50108C50FD0FE682901C510D0FE2870 401 | :2035200008A50DFDF60108C50FD0FE682901C510D0FE2808A50D79040108C50FD0FE682963 402 | :2035400001C510D0FE2808A50DF9050108C50FD0FE682901C510D0FE2808A50D614608C5B1 403 | :203560000FD0FE682901C510D0FE2808A50DE14808C50FD0FE682901C510D0FE2808A50D6D 404 | :20358000715808C50FD0FE682901C510D0FE2808A50DF15A08C50FD0FE682901C510D0FE77 405 | :2035A0002860A511298348A50D450E300AA50D450F10046809404868851108A50D650E0854 406 | :2035C000C50FD0FE6829C3C511D0FE2808A50DE51208C50FD0FE6829C3C511D0FE2808A501 407 | :2035E0000D6D030208C50FD0FE6829C3C511D0FE2808A50DED040208C50FD0FE6829C3C512 408 | :2036000011D0FE2808A50E8D1302A50D20120208C50FD0FE6829C3C511D0FE2808A5128D4A 409 | :203620001602A50D20150208C50FD0FE6829C3C511D0FE2808A50D750008C50FD0FE682955 410 | :20364000C3C511D0FE2808A50DF50408C50FD0FE6829C3C511D0FE2808A50D7DF50108C564 411 | :203660000FD0FE6829C3C511D0FE2808A50DFDF60108C50FD0FE6829C3C511D0FE2808A528 412 | :203680000D79040108C50FD0FE6829C3C511D0FE2808A50DF9050108C50FD0FE6829C3C559 413 | :2036A00011D0FE2808A50D614608C50FD0FE6829C3C511D0FE2808A50DE14808C50FD0FE4B 414 | :2036C0006829C3C511D0FE2808A50D715808C50FD0FE6829C3C511D0FE2808A50DF15A0870 415 | :2036E000C50FD0FE6829C3C511D0FE286088880888888828B0FE70FE30FEF0FEC946D0FEBB 416 | :20370000E041D0FEC04FD0FE488A48BAE0FDD0FE68AAA9FF482868E849AA4C0F090027372F 417 | :2037200064094C223788880888888828B0FE70FE30FEF0FEC949D0FEE04ED0FEC041D0FEBC 418 | :20374000488A48BAE0FDD0FE68AAA9FF482868E849AA6C20374C55374C000488880888886A 419 | :203760008828B0FE70FE30FEF0FEC94AD0FEE053D0FEC04FD0FE488A48BAE0FBD0FEADFF79 420 | :2037800001C909D0FEADFE01C99AD0FEA9FF482868AA68E849AA604C97374C00044C9D3754 421 | :2037A0004C00044CA3374C0004888808888888C9BDF042C942D0FEE052D0FEC048D0FE85A7 422 | :2037C0000A860BBABD0201C930D0FE68C934D0FEBAE0FCD0FEADFF01C909D0FEADFE01C9B4 423 | :2037E000D1D0FEA9FF48A60BE8A50A49AA28404CEF374C0004E0ADD0FEC0B1D0FE850A8626 424 | :203800000BBABD0201C9FFD0FE680908C9FFD0FEBAE0FCD0FEADFF01C909D0FEADFE01C958 425 | :15382000F7D0FEA90448A60BE8A50A49AA28404C2F384C000433 426 | :06FFFA009D37A337AB3771 427 | :00040001FB 428 | -------------------------------------------------------------------------------- /mos6502/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | // based on https://github.com/fogleman/nes/blob/master/nes/cpu.go 4 | 5 | const INTERRUPT_NONE: u8 = 1; 6 | const INTERRUPT_NMI: u8 = 2; 7 | const INTERRUPT_IRQ: u8 = 3; 8 | 9 | // instructionModes indicates the addressing mode for each instruction 10 | const INSTRUCTION_MODES: [u8; 256] = [ 11 | 6, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 12 | 2, 2, 2, 1, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 13 | 6, 3, 2, 2, 2, 2, 6, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 14 | 12, 6, 3, 6, 3, 2, 2, 2, 2, 6, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 8, 1, 1, 1, 10, 9, 6, 9, 15 | 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2, 5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 5, 1, 1, 1, 1, 10, 16 | 9, 6, 9, 12, 12, 13, 13, 6, 3, 6, 3, 2, 2, 3, 3, 5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 5, 1, 1, 17 | 1, 1, 10, 9, 6, 9, 12, 12, 13, 13, 6, 3, 6, 3, 2, 2, 3, 3, 5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 18 | 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2, 5, 7, 5, 7, 11, 11, 11, 11, 19 | 6, 5, 6, 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2, 20 | ]; 21 | 22 | // instructionSizes indicates the size of each instruction in bytes 23 | const INSTRUCTION_SIZES: [u8; 256] = [ 24 | 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 25 | 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 26 | 1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 27 | 1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 28 | 2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, 29 | 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 30 | 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 31 | 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 32 | ]; 33 | 34 | // instructionCycles indicates the number of cycles used by each instruction, 35 | // not including conditional cycles 36 | const INSTRUCTION_CYCLES: [u8; 256] = [ 37 | 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 38 | 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 39 | 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 40 | 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 41 | 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, 42 | 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, 43 | 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 44 | 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 45 | ]; 46 | 47 | pub struct Cpu<'a> { 48 | memory: &'a dyn Memory, // memory interface 49 | cycles: u64, // number of cycles 50 | pub pc: u16, // program counter 51 | sp: u8, // stack pointer 52 | a: u8, // accumulator 53 | x: u8, // x register 54 | y: u8, // y register 55 | c: bool, // carry flag 56 | z: bool, // zero flag 57 | i: bool, // interrupt disable flag 58 | d: bool, // decimal mode flag 59 | b: bool, // break command flag 60 | u: bool, // unused flag 61 | v: bool, // overflow flag 62 | n: bool, // negative flag 63 | interrupt: u8, // interrupt type to perform 64 | stall: u16, // number of cycles to stall 65 | } 66 | 67 | #[derive(Debug)] 68 | enum Mode { 69 | ModeAbsolute, 70 | ModeAbsoluteX, 71 | ModeAbsoluteY, 72 | ModeAccumulator, 73 | ModeImmediate, 74 | ModeImplied, 75 | ModeIndexedIndirect, 76 | ModeIndirect, 77 | ModeIndirectIndexed, 78 | ModeRelative, 79 | ModeZeroPage, 80 | ModeZeroPageX, 81 | ModeZeroPageY, 82 | } 83 | 84 | fn to_mode(value: u8) -> Mode { 85 | match value - 1 { 86 | 0 => Mode::ModeAbsolute, 87 | 1 => Mode::ModeAbsoluteX, 88 | 2 => Mode::ModeAbsoluteY, 89 | 3 => Mode::ModeAccumulator, 90 | 4 => Mode::ModeImmediate, 91 | 5 => Mode::ModeImplied, 92 | 6 => Mode::ModeIndexedIndirect, 93 | 7 => Mode::ModeIndirect, 94 | 8 => Mode::ModeIndirectIndexed, 95 | 9 => Mode::ModeRelative, 96 | 10 => Mode::ModeZeroPage, 97 | 11 => Mode::ModeZeroPageX, 98 | 12 => Mode::ModeZeroPageY, 99 | _ => panic!("Unable to map mode {}", value), 100 | } 101 | } 102 | 103 | struct StepInfo { 104 | address: u16, 105 | pc: u16, 106 | mode: Mode, 107 | } 108 | 109 | pub trait Memory { 110 | fn get(&self, addr: u16) -> u8; 111 | 112 | fn set(&self, addr: u16, v: u8); 113 | } 114 | 115 | impl<'a> Cpu<'a> { 116 | pub fn new(memory: &'a dyn Memory) -> Self { 117 | Cpu { 118 | memory: memory, 119 | cycles: 0, 120 | pc: 0, 121 | sp: 0, 122 | a: 0, 123 | x: 0, 124 | y: 0, 125 | c: false, 126 | z: false, 127 | i: false, 128 | d: false, 129 | b: false, 130 | u: false, 131 | v: false, 132 | n: false, 133 | interrupt: 0, 134 | stall: 0, 135 | } 136 | } 137 | 138 | pub fn reset(&mut self) { 139 | self.pc = self.read16(0xfffc); 140 | self.sp = 0xfd; 141 | 142 | self.set_flags(0x24); 143 | } 144 | 145 | pub fn start_at(&mut self, addr: u16) { 146 | self.pc = addr; 147 | self.sp = 0xfd; 148 | 149 | self.set_flags(0x24); 150 | } 151 | 152 | fn read16(&self, addr: u16) -> u16 { 153 | let lo = self.read(addr); 154 | let hi = self.read(addr + 1); 155 | (hi as u16) << 8 as u16 | lo as u16 156 | } 157 | 158 | // read16bug emulates a 6502 bug that caused the low byte to wrap without 159 | // incrementing the high byte 160 | fn read16bug(&self, addr: u16) -> u16 { 161 | let a = addr; 162 | let b = (a & 0xff00) | ((((a & 0xff) as u8).overflowing_add(1)).0) as u16; 163 | let lo = self.read(a); 164 | let hi = self.read(b); 165 | (hi as u16) << 8 as u16 | lo as u16 166 | } 167 | 168 | fn read(&self, addr: u16) -> u8 { 169 | self.memory.get(addr) 170 | } 171 | 172 | // SetFlags sets the processor status flags 173 | fn set_flags(&mut self, flags: u8) { 174 | self.c = (flags >> 0) & 1 == 1; 175 | self.z = (flags >> 1) & 1 == 1; 176 | self.i = (flags >> 2) & 1 == 1; 177 | self.d = (flags >> 3) & 1 == 1; 178 | self.b = (flags >> 4) & 1 == 1; 179 | self.u = (flags >> 5) & 1 == 1; 180 | self.v = (flags >> 6) & 1 == 1; 181 | self.n = (flags >> 7) & 1 == 1; 182 | } 183 | 184 | // pagesDiffer returns true if the two addresses reference different pages 185 | fn pages_differ(a: u16, b: u16) -> bool { 186 | a & 0xff00 != b & 0xff00 187 | } 188 | 189 | // addBranchCycles adds a cycle for taking a branch and adds another cycle 190 | // if the branch jumps to a new page 191 | fn add_branch_cycles(&mut self, info: &StepInfo) { 192 | self.cycles += 1; 193 | if Self::pages_differ(info.pc, info.address) { 194 | self.cycles += 1; 195 | } 196 | } 197 | 198 | fn compare(&mut self, a: u8, b: u8) { 199 | let r = a.overflowing_sub(b).0; 200 | self.set_zn(r); 201 | if a >= b { 202 | self.c = true; 203 | } else { 204 | self.c = false; 205 | } 206 | } 207 | 208 | // setZ sets the zero flag if the argument is zero 209 | fn set_z(&mut self, value: u8) { 210 | if value == 0 { 211 | self.z = true; 212 | } else { 213 | self.z = false; 214 | } 215 | } 216 | 217 | // setN sets the negative flag if the argument is negative (high bit is set) 218 | fn set_n(&mut self, value: u8) { 219 | if value & 0x80 != 0 { 220 | self.n = true; 221 | } else { 222 | self.n = false; 223 | } 224 | } 225 | 226 | // setZN sets the zero flag and the negative flag 227 | fn set_zn(&mut self, value: u8) { 228 | self.set_z(value); 229 | self.set_n(value); 230 | } 231 | 232 | // push pushes a byte onto the stack 233 | fn push(&mut self, value: u8) { 234 | self.write(0x100 as u16 | self.sp as u16, value); 235 | let r = self.sp.overflowing_sub(1).0; 236 | self.sp = r; 237 | } 238 | 239 | // pull pops a byte from the stack 240 | fn pull(&mut self) -> u8 { 241 | let r = self.sp.overflowing_add(1).0; 242 | self.sp = r; 243 | self.read(0x100 as u16 | self.sp as u16) 244 | } 245 | 246 | fn write(&mut self, addr: u16, value: u8) { 247 | self.memory.set(addr, value); 248 | } 249 | 250 | // push16 pushes two bytes onto the stack 251 | fn push16(&mut self, value: u16) { 252 | let hi = (value >> 8) as u8; 253 | let lo = (value & 0xff) as u8; 254 | self.push(hi); 255 | self.push(lo); 256 | } 257 | 258 | // pull16 pops two bytes from the stack 259 | fn pull16(&mut self) -> u16 { 260 | let lo = self.pull(); 261 | let hi = self.pull(); 262 | (hi as u16) << 8 as u16 | lo as u16 263 | } 264 | 265 | // Flags returns the processor status flags 266 | fn flags(&self) -> u8 { 267 | let mut flags = 0u8; 268 | flags |= if self.c { 1 } else { 0 } << 0; 269 | flags |= if self.z { 1 } else { 0 } << 1; 270 | flags |= if self.i { 1 } else { 0 } << 2; 271 | flags |= if self.d { 1 } else { 0 } << 3; 272 | flags |= if self.b { 1 } else { 0 } << 4; 273 | flags |= if self.u { 1 } else { 0 } << 5; 274 | flags |= if self.v { 1 } else { 0 } << 6; 275 | flags |= if self.n { 1 } else { 0 } << 7; 276 | flags 277 | } 278 | 279 | // triggerNMI causes a non-maskable interrupt to occur on the next cycle 280 | pub fn trigger_nmi(&mut self) { 281 | self.interrupt = INTERRUPT_NMI; 282 | } 283 | 284 | // triggerIRQ causes an IRQ interrupt to occur on the next cycle 285 | pub fn trigger_irq(&mut self) { 286 | if !self.i { 287 | self.interrupt = INTERRUPT_IRQ; 288 | } 289 | } 290 | 291 | // Step executes a single CPU instruction 292 | pub fn step(&mut self) -> u64 { 293 | if self.stall > 0 { 294 | self.stall -= 1; 295 | return 1; 296 | } 297 | 298 | let cycles = self.cycles; 299 | 300 | match self.interrupt { 301 | INTERRUPT_NMI => self.cpu_nmi(), 302 | INTERRUPT_IRQ => self.cpu_irq(), 303 | _ => {} 304 | } 305 | self.interrupt = INTERRUPT_NONE; 306 | 307 | let opcode = self.read(self.pc); 308 | let mode = to_mode(INSTRUCTION_MODES[opcode as usize]); 309 | 310 | let address: u16; 311 | let mut page_crossed = false; 312 | 313 | match mode { 314 | Mode::ModeAbsolute => address = self.read16(self.pc + 1), 315 | Mode::ModeAbsoluteX => { 316 | address = self.read16(self.pc + 1) + self.x as u16; 317 | page_crossed = Cpu::pages_differ(address - self.x as u16, address) 318 | } 319 | Mode::ModeAbsoluteY => { 320 | address = self.read16(self.pc + 1) + self.y as u16; 321 | page_crossed = Cpu::pages_differ(address - self.y as u16, address) 322 | } 323 | Mode::ModeAccumulator => { 324 | address = 0; 325 | } 326 | Mode::ModeImmediate => { 327 | address = self.pc + 1; 328 | } 329 | Mode::ModeImplied => { 330 | address = 0; 331 | } 332 | Mode::ModeIndexedIndirect => { 333 | address = self.read16bug(self.read(self.pc + 1).overflowing_add(self.x).0 as u16); 334 | } 335 | Mode::ModeIndirect => { 336 | address = self.read16bug(self.read16(self.pc + 1)); 337 | } 338 | Mode::ModeIndirectIndexed => { 339 | address = self.read16bug(self.read(self.pc + 1) as u16) + self.y as u16; 340 | page_crossed = Cpu::pages_differ(address - self.y as u16, address); 341 | } 342 | Mode::ModeRelative => { 343 | let offset = self.read(self.pc + 1) as u16; 344 | if offset < 0x80 { 345 | address = self.pc.overflowing_add(2).0.overflowing_add(offset).0 346 | } else { 347 | address = self 348 | .pc 349 | .overflowing_add(2) 350 | .0 351 | .overflowing_add(offset) 352 | .0 353 | .overflowing_sub(0x100) 354 | .0 355 | } 356 | } 357 | Mode::ModeZeroPage => { 358 | address = self.read(self.pc + 1) as u16; 359 | } 360 | Mode::ModeZeroPageX => { 361 | address = (self.read(self.pc + 1) as u16 + self.x as u16) as u16 & 0xff 362 | } 363 | Mode::ModeZeroPageY => { 364 | address = (self.read(self.pc + 1) as u16 + self.y as u16) as u16 & 0xff 365 | } 366 | } 367 | 368 | self.pc += INSTRUCTION_SIZES[opcode as usize] as u16; 369 | self.cycles += INSTRUCTION_CYCLES[opcode as usize] as u64; 370 | if page_crossed { 371 | self.cycles += 1; 372 | } 373 | 374 | let info = StepInfo { 375 | address: address, 376 | pc: self.pc, 377 | mode: mode, 378 | }; 379 | 380 | self.execute_opcode(opcode, &info); 381 | 382 | self.cycles - cycles 383 | } 384 | 385 | fn execute_opcode(&mut self, opcode: u8, info: &StepInfo) { 386 | match opcode { 387 | 0 => self.brk(info), 388 | 1 => self.ora(info), 389 | 2 => self.kil(info), 390 | 3 => self.slo(info), 391 | 4 => self.nop(info), 392 | 5 => self.ora(info), 393 | 6 => self.asl(info), 394 | 7 => self.slo(info), 395 | 8 => self.php(info), 396 | 9 => self.ora(info), 397 | 10 => self.asl(info), 398 | 11 => self.anc(info), 399 | 12 => self.nop(info), 400 | 13 => self.ora(info), 401 | 14 => self.asl(info), 402 | 15 => self.slo(info), 403 | 16 => self.bpl(info), 404 | 17 => self.ora(info), 405 | 18 => self.kil(info), 406 | 19 => self.slo(info), 407 | 20 => self.nop(info), 408 | 21 => self.ora(info), 409 | 22 => self.asl(info), 410 | 23 => self.slo(info), 411 | 24 => self.clc(info), 412 | 25 => self.ora(info), 413 | 26 => self.nop(info), 414 | 27 => self.slo(info), 415 | 28 => self.nop(info), 416 | 29 => self.ora(info), 417 | 30 => self.asl(info), 418 | 31 => self.slo(info), 419 | 32 => self.jsr(info), 420 | 33 => self.and(info), 421 | 34 => self.kil(info), 422 | 35 => self.rla(info), 423 | 36 => self.bit(info), 424 | 37 => self.and(info), 425 | 38 => self.rol(info), 426 | 39 => self.rla(info), 427 | 40 => self.plp(info), 428 | 41 => self.and(info), 429 | 42 => self.rol(info), 430 | 43 => self.anc(info), 431 | 44 => self.bit(info), 432 | 45 => self.and(info), 433 | 46 => self.rol(info), 434 | 47 => self.rla(info), 435 | 48 => self.bmi(info), 436 | 49 => self.and(info), 437 | 50 => self.kil(info), 438 | 51 => self.rla(info), 439 | 52 => self.nop(info), 440 | 53 => self.and(info), 441 | 54 => self.rol(info), 442 | 55 => self.rla(info), 443 | 56 => self.sec(info), 444 | 57 => self.and(info), 445 | 58 => self.nop(info), 446 | 59 => self.rla(info), 447 | 60 => self.nop(info), 448 | 61 => self.and(info), 449 | 62 => self.rol(info), 450 | 63 => self.rla(info), 451 | 64 => self.rti(info), 452 | 65 => self.eor(info), 453 | 66 => self.kil(info), 454 | 67 => self.sre(info), 455 | 68 => self.nop(info), 456 | 69 => self.eor(info), 457 | 70 => self.lsr(info), 458 | 71 => self.sre(info), 459 | 72 => self.pha(info), 460 | 73 => self.eor(info), 461 | 74 => self.lsr(info), 462 | 75 => self.alr(info), 463 | 76 => self.jmp(info), 464 | 77 => self.eor(info), 465 | 78 => self.lsr(info), 466 | 79 => self.sre(info), 467 | 80 => self.bvc(info), 468 | 81 => self.eor(info), 469 | 82 => self.kil(info), 470 | 83 => self.sre(info), 471 | 84 => self.nop(info), 472 | 85 => self.eor(info), 473 | 86 => self.lsr(info), 474 | 87 => self.sre(info), 475 | 88 => self.cli(info), 476 | 89 => self.eor(info), 477 | 90 => self.nop(info), 478 | 91 => self.sre(info), 479 | 92 => self.nop(info), 480 | 93 => self.eor(info), 481 | 94 => self.lsr(info), 482 | 95 => self.sre(info), 483 | 96 => self.rts(info), 484 | 97 => self.adc(info), 485 | 98 => self.kil(info), 486 | 99 => self.rra(info), 487 | 100 => self.nop(info), 488 | 101 => self.adc(info), 489 | 102 => self.ror(info), 490 | 103 => self.rra(info), 491 | 104 => self.pla(info), 492 | 105 => self.adc(info), 493 | 106 => self.ror(info), 494 | 107 => self.arr(info), 495 | 108 => self.jmp(info), 496 | 109 => self.adc(info), 497 | 110 => self.ror(info), 498 | 111 => self.rra(info), 499 | 112 => self.bvs(info), 500 | 113 => self.adc(info), 501 | 114 => self.kil(info), 502 | 115 => self.rra(info), 503 | 116 => self.nop(info), 504 | 117 => self.adc(info), 505 | 118 => self.ror(info), 506 | 119 => self.rra(info), 507 | 120 => self.sei(info), 508 | 121 => self.adc(info), 509 | 122 => self.nop(info), 510 | 123 => self.rra(info), 511 | 124 => self.nop(info), 512 | 125 => self.adc(info), 513 | 126 => self.ror(info), 514 | 127 => self.rra(info), 515 | 128 => self.nop(info), 516 | 129 => self.sta(info), 517 | 130 => self.nop(info), 518 | 131 => self.sax(info), 519 | 132 => self.sty(info), 520 | 133 => self.sta(info), 521 | 134 => self.stx(info), 522 | 135 => self.sax(info), 523 | 136 => self.dey(info), 524 | 137 => self.nop(info), 525 | 138 => self.txa(info), 526 | 139 => self.xaa(info), 527 | 140 => self.sty(info), 528 | 141 => self.sta(info), 529 | 142 => self.stx(info), 530 | 143 => self.sax(info), 531 | 144 => self.bcc(info), 532 | 145 => self.sta(info), 533 | 146 => self.kil(info), 534 | 147 => self.ahx(info), 535 | 148 => self.sty(info), 536 | 149 => self.sta(info), 537 | 150 => self.stx(info), 538 | 151 => self.sax(info), 539 | 152 => self.tya(info), 540 | 153 => self.sta(info), 541 | 154 => self.txs(info), 542 | 155 => self.tas(info), 543 | 156 => self.shy(info), 544 | 157 => self.sta(info), 545 | 158 => self.shx(info), 546 | 159 => self.ahx(info), 547 | 160 => self.ldy(info), 548 | 161 => self.lda(info), 549 | 162 => self.ldx(info), 550 | 163 => self.lax(info), 551 | 164 => self.ldy(info), 552 | 165 => self.lda(info), 553 | 166 => self.ldx(info), 554 | 167 => self.lax(info), 555 | 168 => self.tay(info), 556 | 169 => self.lda(info), 557 | 170 => self.tax(info), 558 | 171 => self.lax(info), 559 | 172 => self.ldy(info), 560 | 173 => self.lda(info), 561 | 174 => self.ldx(info), 562 | 175 => self.lax(info), 563 | 176 => self.bcs(info), 564 | 177 => self.lda(info), 565 | 178 => self.kil(info), 566 | 179 => self.lax(info), 567 | 180 => self.ldy(info), 568 | 181 => self.lda(info), 569 | 182 => self.ldx(info), 570 | 183 => self.lax(info), 571 | 184 => self.clv(info), 572 | 185 => self.lda(info), 573 | 186 => self.tsx(info), 574 | 187 => self.las(info), 575 | 188 => self.ldy(info), 576 | 189 => self.lda(info), 577 | 190 => self.ldx(info), 578 | 191 => self.lax(info), 579 | 192 => self.cpy(info), 580 | 193 => self.cmp(info), 581 | 194 => self.nop(info), 582 | 195 => self.dcp(info), 583 | 196 => self.cpy(info), 584 | 197 => self.cmp(info), 585 | 198 => self.dec(info), 586 | 199 => self.dcp(info), 587 | 200 => self.iny(info), 588 | 201 => self.cmp(info), 589 | 202 => self.dex(info), 590 | 203 => self.axs(info), 591 | 204 => self.cpy(info), 592 | 205 => self.cmp(info), 593 | 206 => self.dec(info), 594 | 207 => self.dcp(info), 595 | 208 => self.bne(info), 596 | 209 => self.cmp(info), 597 | 210 => self.kil(info), 598 | 211 => self.dcp(info), 599 | 212 => self.nop(info), 600 | 213 => self.cmp(info), 601 | 214 => self.dec(info), 602 | 215 => self.dcp(info), 603 | 216 => self.cld(info), 604 | 217 => self.cmp(info), 605 | 218 => self.nop(info), 606 | 219 => self.dcp(info), 607 | 220 => self.nop(info), 608 | 221 => self.cmp(info), 609 | 222 => self.dec(info), 610 | 223 => self.dcp(info), 611 | 224 => self.cpx(info), 612 | 225 => self.sbc(info), 613 | 226 => self.nop(info), 614 | 227 => self.isc(info), 615 | 228 => self.cpx(info), 616 | 229 => self.sbc(info), 617 | 230 => self.inc(info), 618 | 231 => self.isc(info), 619 | 232 => self.inx(info), 620 | 233 => self.sbc(info), 621 | 234 => self.nop(info), 622 | 235 => self.sbc(info), 623 | 236 => self.cpx(info), 624 | 237 => self.sbc(info), 625 | 238 => self.inc(info), 626 | 239 => self.isc(info), 627 | 240 => self.beq(info), 628 | 241 => self.sbc(info), 629 | 242 => self.kil(info), 630 | 243 => self.isc(info), 631 | 244 => self.nop(info), 632 | 245 => self.sbc(info), 633 | 246 => self.inc(info), 634 | 247 => self.isc(info), 635 | 248 => self.sed(info), 636 | 249 => self.sbc(info), 637 | 250 => self.nop(info), 638 | 251 => self.isc(info), 639 | 252 => self.nop(info), 640 | 253 => self.sbc(info), 641 | 254 => self.inc(info), 642 | 255 => self.isc(info), 643 | } 644 | } 645 | 646 | fn cpu_nmi(&mut self) { 647 | self.push16(self.pc); 648 | self.push(self.flags()); 649 | self.pc = self.read16(0xfffa); 650 | self.i = true; 651 | self.cycles += 7; 652 | } 653 | 654 | fn cpu_irq(&mut self) { 655 | self.push16(self.pc); 656 | self.push(self.flags()); 657 | self.pc = self.read16(0xfffe); 658 | self.i = true; 659 | self.cycles += 7; 660 | } 661 | 662 | // ADC - Add with Carry 663 | fn adc(&mut self, info: &StepInfo) { 664 | let a = self.a; 665 | let b = self.read(info.address); 666 | let c = if self.c { 1 } else { 0 }; 667 | self.a = a.overflowing_add(b).0.overflowing_add(c).0; 668 | self.set_zn(self.a); 669 | if a as u32 + b as u32 + c as u32 > 0xff { 670 | self.c = true; 671 | } else { 672 | self.c = false; 673 | } 674 | if (a ^ b) & 0x80 == 0 && (a ^ self.a) & 0x80 != 0 { 675 | self.v = true; 676 | } else { 677 | self.v = false; 678 | } 679 | } 680 | 681 | // AND - Logical AND 682 | fn and(&mut self, info: &StepInfo) { 683 | self.a = self.a & self.read(info.address); 684 | self.set_zn(self.a); 685 | } 686 | 687 | // ASL - Arithmetic Shift Left 688 | fn asl(&mut self, info: &StepInfo) { 689 | if let Mode::ModeAccumulator = info.mode { 690 | self.c = (self.a >> 7) & 1 == 1; 691 | self.a <<= 1; 692 | self.set_zn(self.a); 693 | } else { 694 | let mut value = self.read(info.address); 695 | self.c = (value >> 7) & 1 == 1; 696 | value <<= 1; 697 | self.write(info.address, value); 698 | self.set_zn(value); 699 | } 700 | } 701 | 702 | // BCC - Branch if Carry Clear 703 | fn bcc(&mut self, info: &StepInfo) { 704 | if !self.c { 705 | self.pc = info.address; 706 | self.add_branch_cycles(info); 707 | } 708 | } 709 | 710 | // BCS - Branch if Carry Set 711 | fn bcs(&mut self, info: &StepInfo) { 712 | if self.c { 713 | self.pc = info.address; 714 | self.add_branch_cycles(info); 715 | } 716 | } 717 | 718 | // BEQ - Branch if Equal 719 | fn beq(&mut self, info: &StepInfo) { 720 | if self.z { 721 | self.pc = info.address; 722 | self.add_branch_cycles(info); 723 | } 724 | } 725 | 726 | // BIT - Bit Test 727 | fn bit(&mut self, info: &StepInfo) { 728 | let value = self.read(info.address); 729 | self.v = (value >> 6) & 1 == 1; 730 | self.set_z(value & self.a); 731 | self.set_n(value); 732 | } 733 | 734 | // BMI - Branch if Minus 735 | fn bmi(&mut self, info: &StepInfo) { 736 | if self.n { 737 | self.pc = info.address; 738 | self.add_branch_cycles(info); 739 | } 740 | } 741 | 742 | // BNE - Branch if Not Equal 743 | fn bne(&mut self, info: &StepInfo) { 744 | if !self.z { 745 | self.pc = info.address; 746 | self.add_branch_cycles(info); 747 | } 748 | } 749 | 750 | // BPL - Branch if Positive 751 | fn bpl(&mut self, info: &StepInfo) { 752 | if !self.n { 753 | self.pc = info.address; 754 | self.add_branch_cycles(info); 755 | } 756 | } 757 | 758 | // BRK - Force Interrupt 759 | fn brk(&mut self, info: &StepInfo) { 760 | self.push16(self.pc); 761 | self.php(info); 762 | self.sei(info); 763 | self.pc = self.read16(0xfffe); 764 | } 765 | 766 | // BVC - Branch if Overflow Clear 767 | fn bvc(&mut self, info: &StepInfo) { 768 | if !self.v { 769 | self.pc = info.address; 770 | self.add_branch_cycles(info); 771 | } 772 | } 773 | 774 | // BVS - Branch if Overflow Set 775 | fn bvs(&mut self, info: &StepInfo) { 776 | if self.v { 777 | self.pc = info.address; 778 | self.add_branch_cycles(info); 779 | } 780 | } 781 | 782 | // CLC - Clear Carry Flag 783 | fn clc(&mut self, _info: &StepInfo) { 784 | self.c = false; 785 | } 786 | 787 | // CLD - Clear Decimal Mode 788 | fn cld(&mut self, _info: &StepInfo) { 789 | self.d = false; 790 | } 791 | 792 | // CLI - Clear Interrupt Disable 793 | fn cli(&mut self, _info: &StepInfo) { 794 | self.i = false; 795 | } 796 | 797 | // CLV - Clear Overflow Flag 798 | fn clv(&mut self, _info: &StepInfo) { 799 | self.v = false; 800 | } 801 | 802 | // CMP - Compare 803 | fn cmp(&mut self, info: &StepInfo) { 804 | let value = self.read(info.address); 805 | self.compare(self.a, value); 806 | } 807 | 808 | // CPX - Compare X Register 809 | fn cpx(&mut self, info: &StepInfo) { 810 | let value = self.read(info.address); 811 | self.compare(self.x, value); 812 | } 813 | 814 | // CPY - Compare Y Register 815 | fn cpy(&mut self, info: &StepInfo) { 816 | let value = self.read(info.address); 817 | self.compare(self.y, value); 818 | } 819 | 820 | // DEC - Decrement Memory 821 | fn dec(&mut self, info: &StepInfo) { 822 | let value = self.read(info.address).overflowing_sub(1).0; 823 | self.write(info.address, value); 824 | self.set_zn(value); 825 | } 826 | 827 | // DEX - Decrement X Register 828 | fn dex(&mut self, _info: &StepInfo) { 829 | self.x = self.x.overflowing_sub(1).0; 830 | self.set_zn(self.x); 831 | } 832 | 833 | // DEY - Decrement Y Register 834 | fn dey(&mut self, _info: &StepInfo) { 835 | let r = self.y.overflowing_sub(1).0; 836 | self.y = r; 837 | self.set_zn(self.y); 838 | } 839 | 840 | // EOR - Exclusive OR 841 | fn eor(&mut self, info: &StepInfo) { 842 | self.a = self.a ^ self.read(info.address); 843 | self.set_zn(self.a); 844 | } 845 | 846 | // INC - Increment Memory 847 | fn inc(&mut self, info: &StepInfo) { 848 | let value = self.read(info.address).overflowing_add(1).0; 849 | self.write(info.address, value); 850 | self.set_zn(value); 851 | } 852 | 853 | // INX - Increment X Register 854 | fn inx(&mut self, _info: &StepInfo) { 855 | self.x = self.x.overflowing_add(1).0; 856 | self.set_zn(self.x); 857 | } 858 | 859 | // INY - Increment Y Register 860 | fn iny(&mut self, _info: &StepInfo) { 861 | self.y = self.y.overflowing_add(1).0; 862 | self.set_zn(self.y); 863 | } 864 | 865 | // JMP - Jump 866 | fn jmp(&mut self, info: &StepInfo) { 867 | self.pc = info.address; 868 | } 869 | 870 | // JSR - Jump to Subroutine 871 | fn jsr(&mut self, info: &StepInfo) { 872 | self.push16(self.pc - 1); 873 | self.pc = info.address; 874 | } 875 | 876 | // LDA - Load Accumulator 877 | fn lda(&mut self, info: &StepInfo) { 878 | self.a = self.read(info.address); 879 | self.set_zn(self.a); 880 | } 881 | 882 | // LDX - Load X Register 883 | fn ldx(&mut self, info: &StepInfo) { 884 | self.x = self.read(info.address); 885 | self.set_zn(self.x); 886 | } 887 | 888 | // LDY - Load Y Register 889 | fn ldy(&mut self, info: &StepInfo) { 890 | self.y = self.read(info.address); 891 | self.set_zn(self.y); 892 | } 893 | 894 | // LSR - Logical Shift Right 895 | fn lsr(&mut self, info: &StepInfo) { 896 | if let Mode::ModeAccumulator = info.mode { 897 | self.c = self.a & 1 == 1; 898 | self.a >>= 1; 899 | self.set_zn(self.a); 900 | } else { 901 | let mut value = self.read(info.address); 902 | self.c = value & 1 == 1; 903 | value >>= 1; 904 | self.write(info.address, value); 905 | self.set_zn(value); 906 | } 907 | } 908 | 909 | // NOP - No Operation 910 | fn nop(&mut self, _info: &StepInfo) {} 911 | 912 | // ORA - Logical Inclusive OR 913 | fn ora(&mut self, info: &StepInfo) { 914 | self.a = self.a | self.read(info.address); 915 | self.set_zn(self.a); 916 | } 917 | 918 | // PHA - Push Accumulator 919 | fn pha(&mut self, _info: &StepInfo) { 920 | self.push(self.a); 921 | } 922 | 923 | // PHP - Push Processor Status 924 | fn php(&mut self, _info: &StepInfo) { 925 | self.push(self.flags() | 0x10); 926 | } 927 | 928 | // PLA - Pull Accumulator 929 | fn pla(&mut self, _info: &StepInfo) { 930 | self.a = self.pull(); 931 | self.set_zn(self.a); 932 | } 933 | 934 | // PLP - Pull Processor Status 935 | fn plp(&mut self, _info: &StepInfo) { 936 | let pulled = self.pull(); 937 | self.set_flags(pulled & 0xEF | 0x20); 938 | } 939 | 940 | // ROL - Rotate Left 941 | fn rol(&mut self, info: &StepInfo) { 942 | if let Mode::ModeAccumulator = info.mode { 943 | let c = self.c; 944 | self.c = (self.a >> 7) & 1 == 1; 945 | self.a = (self.a << 1) | if c { 1 } else { 0 }; 946 | self.set_zn(self.a); 947 | } else { 948 | let c = self.c; 949 | let mut value = self.read(info.address); 950 | self.c = (value >> 7) & 1 == 1; 951 | value = (value << 1) | if c { 1 } else { 0 }; 952 | self.write(info.address, value); 953 | self.set_zn(value); 954 | } 955 | } 956 | 957 | // ROR - Rotate Right 958 | fn ror(&mut self, info: &StepInfo) { 959 | if let Mode::ModeAccumulator = info.mode { 960 | let c = self.c; 961 | self.c = self.a & 1 == 1; 962 | self.a = (self.a >> 1) | (if c { 1 } else { 0 } << 7); 963 | self.set_zn(self.a); 964 | } else { 965 | let c = self.c; 966 | let mut value = self.read(info.address); 967 | self.c = value & 1 == 1; 968 | value = (value >> 1) | (if c { 1 } else { 0 } << 7); 969 | self.write(info.address, value); 970 | self.set_zn(value); 971 | } 972 | } 973 | 974 | // RTI - Return from Interrupt 975 | fn rti(&mut self, _info: &StepInfo) { 976 | let pulled = self.pull(); 977 | self.set_flags(pulled & 0xef | 0x20); 978 | self.pc = self.pull16(); 979 | } 980 | 981 | // RTS - Return from Subroutine 982 | fn rts(&mut self, _info: &StepInfo) { 983 | self.pc = self.pull16() + 1; 984 | } 985 | 986 | // SBC - Subtract with Carry 987 | fn sbc(&mut self, info: &StepInfo) { 988 | let a = self.a; 989 | let b = self.read(info.address); 990 | let c = self.c; 991 | self.a = a 992 | .overflowing_sub(b) 993 | .0 994 | .overflowing_sub(1 - if c { 1 } else { 0 }) 995 | .0; 996 | self.set_zn(self.a); 997 | if a as i32 - b as i32 - (1 - if c { 1 } else { 0 }) as i32 >= 0 { 998 | self.c = true; 999 | } else { 1000 | self.c = false; 1001 | } 1002 | if (a ^ b) & 0x80 != 0 && (a ^ self.a) & 0x80 != 0 { 1003 | self.v = true; 1004 | } else { 1005 | self.v = false; 1006 | } 1007 | } 1008 | 1009 | // SEC - Set Carry Flag 1010 | fn sec(&mut self, _info: &StepInfo) { 1011 | self.c = true; 1012 | } 1013 | 1014 | // SED - Set Decimal Flag 1015 | fn sed(&mut self, _info: &StepInfo) { 1016 | self.d = true; 1017 | } 1018 | 1019 | // SEI - Set Interrupt Disable 1020 | fn sei(&mut self, _info: &StepInfo) { 1021 | self.i = true; 1022 | } 1023 | 1024 | // STA - Store Accumulator 1025 | fn sta(&mut self, info: &StepInfo) { 1026 | self.write(info.address, self.a); 1027 | } 1028 | 1029 | // STX - Store X Register 1030 | fn stx(&mut self, info: &StepInfo) { 1031 | self.write(info.address, self.x); 1032 | } 1033 | 1034 | // STY - Store Y Register 1035 | fn sty(&mut self, info: &StepInfo) { 1036 | self.write(info.address, self.y); 1037 | } 1038 | 1039 | // TAX - Transfer Accumulator to X 1040 | fn tax(&mut self, _info: &StepInfo) { 1041 | self.x = self.a; 1042 | self.set_zn(self.x); 1043 | } 1044 | 1045 | // TAY - Transfer Accumulator to Y 1046 | fn tay(&mut self, _info: &StepInfo) { 1047 | self.y = self.a; 1048 | self.set_zn(self.y); 1049 | } 1050 | 1051 | // TSX - Transfer Stack Pointer to X 1052 | fn tsx(&mut self, _info: &StepInfo) { 1053 | self.x = self.sp; 1054 | self.set_zn(self.x); 1055 | } 1056 | 1057 | // TXA - Transfer X to Accumulator 1058 | fn txa(&mut self, _info: &StepInfo) { 1059 | self.a = self.x; 1060 | self.set_zn(self.a); 1061 | } 1062 | 1063 | // TXS - Transfer X to Stack Pointer 1064 | fn txs(&mut self, _info: &StepInfo) { 1065 | self.sp = self.x; 1066 | } 1067 | 1068 | // TYA - Transfer Y to Accumulator 1069 | fn tya(&mut self, _info: &StepInfo) { 1070 | self.a = self.y; 1071 | self.set_zn(self.a); 1072 | } 1073 | 1074 | // illegal opcodes below 1075 | 1076 | fn ahx(&mut self, _info: &StepInfo) {} 1077 | 1078 | fn alr(&mut self, _info: &StepInfo) {} 1079 | 1080 | fn anc(&mut self, _info: &StepInfo) {} 1081 | 1082 | fn arr(&mut self, _info: &StepInfo) {} 1083 | 1084 | fn axs(&mut self, _info: &StepInfo) {} 1085 | 1086 | fn dcp(&mut self, _info: &StepInfo) {} 1087 | 1088 | fn isc(&mut self, _info: &StepInfo) {} 1089 | 1090 | fn kil(&mut self, _info: &StepInfo) {} 1091 | 1092 | fn las(&mut self, _info: &StepInfo) {} 1093 | 1094 | fn lax(&mut self, _info: &StepInfo) {} 1095 | 1096 | fn rla(&mut self, _info: &StepInfo) {} 1097 | 1098 | fn rra(&mut self, _info: &StepInfo) {} 1099 | 1100 | fn sax(&mut self, _info: &StepInfo) {} 1101 | 1102 | fn shx(&mut self, _info: &StepInfo) {} 1103 | 1104 | fn shy(&mut self, _info: &StepInfo) {} 1105 | 1106 | fn slo(&mut self, _info: &StepInfo) {} 1107 | 1108 | fn sre(&mut self, _info: &StepInfo) {} 1109 | 1110 | fn tas(&mut self, _info: &StepInfo) {} 1111 | 1112 | fn xaa(&mut self, _info: &StepInfo) {} 1113 | } 1114 | 1115 | #[cfg(test)] 1116 | mod tests { 1117 | extern crate std; 1118 | use core::cell::RefCell; 1119 | use std::fs::File; 1120 | use std::io::prelude::*; 1121 | use std::string::String; 1122 | 1123 | use super::*; 1124 | 1125 | #[test] 1126 | fn it_works() { 1127 | struct Mem { 1128 | ram: RefCell<[u8; 65536]>, 1129 | } 1130 | 1131 | impl Memory for Mem { 1132 | fn get(&self, addr: u16) -> u8 { 1133 | self.ram.borrow()[addr as usize] 1134 | } 1135 | 1136 | fn set(&self, addr: u16, v: u8) { 1137 | self.ram.borrow_mut()[addr as usize] = v 1138 | } 1139 | } 1140 | 1141 | // see https://github.com/Klaus2m5/6502_65C02_functional_tests/blob/master/6502_functional_test.a65 1142 | let mut f = File::open("data/6502_functional_test.hex").unwrap(); 1143 | let mut buffer = [0; 65536]; 1144 | f.read(&mut buffer).unwrap(); 1145 | 1146 | let mut ram = [0u8; 65536]; 1147 | let mut index = 0usize; 1148 | 1149 | loop { 1150 | if buffer[index] != ':' as u8 { 1151 | panic!("Expected ':' but was {}", buffer[index] as char); 1152 | } 1153 | index += 1; 1154 | 1155 | let mut len_str = String::new(); 1156 | len_str.push(buffer[index] as char); 1157 | len_str.push(buffer[index + 1] as char); 1158 | index += 2; 1159 | let len = u32::from_str_radix(&len_str, 16).unwrap(); 1160 | if len == 0 { 1161 | break; 1162 | } 1163 | 1164 | let mut addr_str = String::new(); 1165 | addr_str.push(buffer[index] as char); 1166 | addr_str.push(buffer[index + 1] as char); 1167 | addr_str.push(buffer[index + 2] as char); 1168 | addr_str.push(buffer[index + 3] as char); 1169 | index += 4; 1170 | let mut addr = u32::from_str_radix(&addr_str, 16).unwrap() as usize; 1171 | 1172 | index += 2; // skip type 1173 | 1174 | for _ in 0..len { 1175 | let mut byte_str = String::new(); 1176 | byte_str.push(buffer[index] as char); 1177 | byte_str.push(buffer[index + 1] as char); 1178 | index += 2; 1179 | let byte = u8::from_str_radix(&byte_str, 16).unwrap(); 1180 | ram[addr] = byte; 1181 | addr += 1usize; 1182 | } 1183 | 1184 | index += 2; // skip checksum 1185 | 1186 | while buffer[index] == 10 || buffer[index] == 13 { 1187 | index += 1; 1188 | } 1189 | } 1190 | 1191 | let mut mem = Mem { 1192 | ram: RefCell::new(ram), 1193 | }; 1194 | 1195 | let mut cpu = Cpu::new(&mut mem); 1196 | cpu.start_at(0x400); 1197 | 1198 | let mut max = 0u16; 1199 | let mut last_good_pc = 0u16; 1200 | let mut last_pc = 0u16; 1201 | for _ in 0..82500 { 1202 | cpu.step(); 1203 | 1204 | if last_pc == cpu.pc { 1205 | panic!( 1206 | "wrong? pc = {:x} x = {:x}, y = {:x} Z={} C={} probably last good pc = {:x}", 1207 | cpu.pc, cpu.x, cpu.y, cpu.z, cpu.c, last_good_pc 1208 | ); 1209 | } 1210 | 1211 | last_good_pc = last_pc; 1212 | last_pc = cpu.pc; 1213 | 1214 | if cpu.pc > max { 1215 | max = cpu.pc; 1216 | } 1217 | } 1218 | 1219 | assert!(max == 0x382e, "max pc should be reached"); 1220 | } 1221 | } 1222 | --------------------------------------------------------------------------------