├── .circleci └── config.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── images └── mario.png └── src ├── catridge.rs ├── cpu.rs ├── io_device.rs ├── joypad.rs ├── main.rs ├── mmu.rs ├── ppu.rs └── timer.rs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | style: 4 | docker: 5 | - image: circleci/rust:1.49.0-buster 6 | steps: 7 | - checkout 8 | - run: rustup component add rustfmt 9 | - run: rustfmt --check src/main.rs 10 | build: 11 | docker: 12 | - image: circleci/rust:1.49.0-buster 13 | steps: 14 | - checkout 15 | - run: sudo apt update && sudo apt install libsdl2-dev 16 | - run: cargo check 17 | - run: cargo build 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/e313e296c1e284d908adcb95bf4f2812617d4de1/Rust.gitignore 2 | 3 | # Generated by Cargo 4 | # will have compiled files and executables 5 | /target/ 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | 14 | 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gbr" 3 | version = "0.1.1" 4 | authors = ["Keichi Takahashi "] 5 | description = "Yet another Game Boy emulator in Rust." 6 | repository = "https://github.com/keichi/gbr" 7 | readme = "README.md" 8 | keywords = ["gameboy", "dmg", "emulator"] 9 | categories = ["emulators"] 10 | license = "MIT" 11 | include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] 12 | 13 | [dependencies] 14 | log = "0.4" 15 | env_logger = "0.6" 16 | sdl2 = "0.32.1" 17 | 18 | [badges] 19 | circle-ci = { repository = "keichi/gbr", branch = "master" } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Keichi Takahashi 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gbr [![CircleCI](https://circleci.com/gh/keichi/gbr.svg?style=svg)](https://circleci.com/gh/keichi/gbr) 2 | 3 | Yet another Game Boy emulator in Rust. 4 | 5 | ![Mario](https://raw.githubusercontent.com/keichi/gbr/master/images/mario.png) 6 | 7 | ## Prerequisites 8 | 9 | - Rust 1.31.1 10 | - SDL2 11 | 12 | ## Status 13 | 14 | - [x] CPU 15 | - [x] Instructions 16 | - [x] Instruction timing 17 | - [x] Interrupt handling 18 | - [ ] PPU 19 | - [x] Background 20 | - [x] Window 21 | - [x] Sprite 22 | - [x] V-blank interrupt 23 | - [x] LCDC STAT interrupt 24 | - [x] Sprite and background priority 25 | - [ ] OAM bug 26 | - [x] Joypad 27 | - [x] Joypad input 28 | - [x] Joypad interrupt 29 | - [ ] Catridge 30 | - [x] Catridge loading 31 | - [x] Data 32 | - [x] MBC1 33 | - [ ] MBC3 34 | - [ ] MBC5 35 | - [ ] External RAM persistence 36 | - [x] Timer 37 | - [x] Timer registers 38 | - [x] Timer overflow interrupt 39 | - [ ] APU 40 | -------------------------------------------------------------------------------- /images/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keichi/gbr/b74813d7619d2cce716e22dfdee4990053c59068/images/mario.png -------------------------------------------------------------------------------- /src/catridge.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{Read, Write}; 3 | 4 | use io_device::IODevice; 5 | 6 | pub struct Catridge { 7 | rom: Vec, 8 | ram: Vec, 9 | #[allow(dead_code)] 10 | mbc_type: u8, 11 | ram_enable: bool, 12 | bank_no_upper: u8, 13 | bank_no_lower: u8, 14 | num_rom_banks: u8, 15 | mode: bool, 16 | } 17 | 18 | impl Catridge { 19 | pub fn new(fname: &str) -> Self { 20 | let mut rom = Vec::new(); 21 | let mut file = File::open(fname).unwrap(); 22 | file.read_to_end(&mut rom).unwrap(); 23 | 24 | let rom_size: usize = match rom[0x0148] { 25 | 0 => 32 * 1024, 26 | n => 32 * 1024 << (n as usize), 27 | }; 28 | 29 | let num_rom_banks = 2 << rom[0x0148]; 30 | 31 | let ram_size: usize = match rom[0x0149] { 32 | 0 => 0, 33 | 1 => 2 * 1024, 34 | 2 => 8 * 1024, 35 | 3 => 32 * 1024, 36 | 4 => 128 * 1024, 37 | 5 => 64 * 1024, 38 | _ => panic!("RAM size invalid"), 39 | }; 40 | 41 | let mbc_type = rom[0x0147]; 42 | 43 | let mbc_name = match mbc_type { 44 | 0x00 => "ROM ONLY", 45 | 0x01 => "MBC1", 46 | 0x02 => "MBC1+RAM", 47 | 0x03 => "MBC1+RAM+BATTERY", 48 | 0x05 => "MBC2", 49 | 0x06 => "MBC2+BATTERY", 50 | 0x08 => "ROM+RAM", 51 | 0x09 => "ROM+RAM+BATTERY", 52 | 0x0b => "MMM01", 53 | 0x0c => "MMM01+RAM", 54 | 0x0d => "MMM01+RAM+BATTERY", 55 | 0x0f => "MBC3+TIMER+BATTERY", 56 | 0x10 => "MBC3+TIMER+RAM+BATTERY", 57 | 0x11 => "MBC3", 58 | 0x12 => "MBC3+RAM", 59 | 0x13 => "MBC3+RAM+BATTERY", 60 | 0x19 => "MBC5", 61 | 0x1a => "MBC5+RAM", 62 | 0x1b => "MBC5+RAM+BATTERY", 63 | 0x1c => "MBC5+RUMBLE", 64 | 0x1d => "MBC5+RUMBLE+RAM", 65 | 0x1e => "MBC5+RUMBLE+RAM+BATTERY", 66 | 0x20 => "MBC6", 67 | 0x22 => "MBC7+SENSOR+RUMBLE+RAM+BATTERY", 68 | 0xfc => "POCKET CAMERA", 69 | 0xfd => "BANDAI TAMA5", 70 | 0xfe => "HuC3", 71 | 0xff => "HuC1+RAM+BATTERY", 72 | _ => "Unknown", 73 | }; 74 | 75 | let mut chksum: u8 = 0; 76 | for i in 0x0134..0x014d { 77 | chksum = chksum.wrapping_sub(rom[i]).wrapping_sub(1); 78 | } 79 | 80 | if rom_size != rom.len() { 81 | panic!("ROM file invalid"); 82 | } 83 | 84 | if chksum != rom[0x014d] { 85 | panic!("ROM header checksum is incorrect"); 86 | } 87 | 88 | info!("ROM size {}KB", rom_size / 1024); 89 | info!("RAM size {}KB", ram_size / 1024); 90 | info!("MBC type {}", mbc_name); 91 | 92 | Catridge { 93 | rom: rom, 94 | ram: vec![0; ram_size], 95 | mbc_type: mbc_type, 96 | ram_enable: false, 97 | bank_no_upper: 0, 98 | bank_no_lower: 0, 99 | num_rom_banks: num_rom_banks, 100 | mode: false, 101 | } 102 | } 103 | 104 | fn rom_bank_no(&self) -> u8 { 105 | let bank_no = if self.mode { 106 | self.bank_no_lower 107 | } else { 108 | self.bank_no_upper << 5 | self.bank_no_lower 109 | }; 110 | 111 | let bank_no = match bank_no { 112 | 0 | 0x20 | 0x40 | 0x60 => bank_no + 1, 113 | _ => bank_no, 114 | }; 115 | 116 | bank_no & (self.num_rom_banks - 1) 117 | } 118 | 119 | fn ram_bank_no(&self) -> u8 { 120 | if self.mode { 121 | self.bank_no_upper 122 | } else { 123 | 0 124 | } 125 | } 126 | 127 | pub fn read_save_file(&mut self, fname: &str) { 128 | info!("Reading save file from: {}", fname); 129 | 130 | if let Ok(mut file) = File::open(fname) { 131 | self.ram = Vec::new(); 132 | file.read_to_end(&mut self.ram).unwrap(); 133 | } 134 | } 135 | 136 | pub fn write_save_file(&mut self, fname: &str) { 137 | info!("Writing save file to: {}", fname); 138 | 139 | if let Ok(mut file) = File::create(fname) { 140 | file.write_all(&mut self.ram).unwrap(); 141 | } 142 | } 143 | } 144 | 145 | impl IODevice for Catridge { 146 | fn write(&mut self, addr: u16, val: u8) { 147 | match addr { 148 | // RAM enable 149 | 0x0000..=0x1fff => self.ram_enable = val & 0x0f == 0x0a, 150 | // ROM bank number (lower 5 bits) 151 | 0x2000..=0x3fff => self.bank_no_lower = val & 0x1f, 152 | // RAM bank number or ROM bank number (upper 2 bits) 153 | 0x4000..=0x5fff => self.bank_no_upper = val & 0x03, 154 | // ROM/RAM mode select 155 | 0x6000..=0x7fff => self.mode = val & 0x01 > 0, 156 | // RAM bank 00-03 157 | 0xa000..=0xbfff => { 158 | if !self.ram_enable { 159 | return; 160 | } 161 | let offset = (8 * 1024) * self.ram_bank_no() as usize; 162 | self.ram[(addr & 0x1fff) as usize + offset] = val 163 | } 164 | _ => unreachable!("Unexpected address: 0x{:04x}", addr), 165 | } 166 | } 167 | 168 | fn read(&self, addr: u16) -> u8 { 169 | match addr { 170 | // ROM bank 00 171 | 0x0000..=0x3fff => self.rom[addr as usize], 172 | // ROM bank 01-7f 173 | 0x4000..=0x7fff => { 174 | let offset = (16 * 1024) * self.rom_bank_no() as usize; 175 | self.rom[(addr & 0x3fff) as usize + offset] 176 | } 177 | // RAM bank 00-03 178 | 0xa000..=0xbfff => { 179 | if !self.ram_enable { 180 | return 0xff; 181 | } 182 | let offset = (8 * 1024) * self.ram_bank_no() as usize; 183 | self.ram[(addr & 0x1fff) as usize + offset] 184 | } 185 | _ => unreachable!("Unexpected address: 0x{:04x}", addr), 186 | } 187 | } 188 | 189 | fn update(&mut self, _tick: u8) {} 190 | } 191 | -------------------------------------------------------------------------------- /src/cpu.rs: -------------------------------------------------------------------------------- 1 | use mmu::MMU; 2 | 3 | pub struct CPU { 4 | pub mmu: MMU, 5 | pc: u16, 6 | sp: u16, 7 | a: u8, 8 | f: u8, 9 | b: u8, 10 | c: u8, 11 | d: u8, 12 | e: u8, 13 | h: u8, 14 | l: u8, 15 | ime: bool, 16 | tick: u8, // This is T-cycle (4.194304 MHz), not M-cycle 17 | halted: bool, 18 | } 19 | 20 | impl CPU { 21 | /// Creates a new `CPU` 22 | pub fn new(rom_name: &str) -> Self { 23 | CPU { 24 | mmu: MMU::new(rom_name), 25 | pc: 0x100, 26 | sp: 0, 27 | a: 0, 28 | f: 0, 29 | b: 0, 30 | c: 0, 31 | d: 0, 32 | e: 0, 33 | h: 0, 34 | l: 0, 35 | ime: false, 36 | tick: 0, 37 | halted: false, 38 | } 39 | } 40 | 41 | /// Reads AF register 42 | fn af(&self) -> u16 { 43 | (self.a as u16) << 8 | self.f as u16 44 | } 45 | 46 | /// Writes AF register 47 | fn set_af(&mut self, val: u16) { 48 | self.a = (val >> 8 & 0xff) as u8; 49 | self.f = (val & 0xff) as u8; 50 | } 51 | 52 | /// Reads BC register 53 | fn bc(&self) -> u16 { 54 | (self.b as u16) << 8 | self.c as u16 55 | } 56 | 57 | /// Writes BC register 58 | fn set_bc(&mut self, val: u16) { 59 | self.b = (val >> 8 & 0xff) as u8; 60 | self.c = (val & 0xff) as u8; 61 | } 62 | 63 | /// Reads DE register 64 | fn de(&self) -> u16 { 65 | (self.d as u16) << 8 | self.e as u16 66 | } 67 | 68 | /// Writes DE register 69 | fn set_de(&mut self, val: u16) { 70 | self.d = (val >> 8 & 0xff) as u8; 71 | self.e = (val & 0xff) as u8; 72 | } 73 | 74 | /// Reads HL register 75 | fn hl(&self) -> u16 { 76 | (self.h as u16) << 8 | self.l as u16 77 | } 78 | 79 | /// Writes HL register 80 | fn set_hl(&mut self, val: u16) { 81 | self.h = (val >> 8 & 0xff) as u8; 82 | self.l = (val & 0xff) as u8; 83 | } 84 | 85 | /// Sets Z flag 86 | fn set_f_z(&mut self, z: bool) { 87 | self.f = (self.f & !(1 << 7)) | (u8::from(z) << 7); 88 | } 89 | 90 | /// Returns Z flag 91 | fn f_z(&self) -> bool { 92 | (self.f >> 7) & 1 == 1 93 | } 94 | 95 | /// Sets N flag 96 | fn set_f_n(&mut self, n: bool) { 97 | self.f = (self.f & !(1 << 6)) | (u8::from(n) << 6); 98 | } 99 | 100 | /// Returns N flag 101 | fn f_n(&self) -> bool { 102 | (self.f >> 6) & 1 == 1 103 | } 104 | 105 | /// Sets H flag 106 | fn set_f_h(&mut self, h: bool) { 107 | self.f = (self.f & !(1 << 5)) | (u8::from(h) << 5); 108 | } 109 | 110 | /// Returns H flag 111 | fn f_h(&self) -> bool { 112 | (self.f >> 5) & 1 == 1 113 | } 114 | 115 | /// Sets C flag 116 | fn set_f_c(&mut self, c: bool) { 117 | self.f = (self.f & !(1 << 4)) | (u8::from(c) << 4); 118 | } 119 | 120 | /// Returns C flag 121 | fn f_c(&self) -> bool { 122 | (self.f >> 4) & 1 == 1 123 | } 124 | 125 | /// Converst 8-bit register index to name 126 | fn reg_to_string(idx: u8) -> String { 127 | match idx { 128 | 0 => String::from("B"), 129 | 1 => String::from("C"), 130 | 2 => String::from("D"), 131 | 3 => String::from("E"), 132 | 4 => String::from("H"), 133 | 5 => String::from("L"), 134 | 6 => String::from("(HL)"), 135 | 7 => String::from("A"), 136 | _ => panic!("Invalid operand index: {}", idx), 137 | } 138 | } 139 | 140 | /// Converst 16-bit register index to name 141 | fn reg16_to_string(idx: u8) -> String { 142 | match idx { 143 | 0 => String::from("BC"), 144 | 1 => String::from("DE"), 145 | 2 => String::from("HL"), 146 | 3 => String::from("SP"), 147 | _ => panic!("Invalid operand index: {}", idx), 148 | } 149 | } 150 | 151 | /// Writes 8-bit operand 152 | fn write_r8(&mut self, idx: u8, val: u8) { 153 | match idx { 154 | 0 => self.b = val, 155 | 1 => self.c = val, 156 | 2 => self.d = val, 157 | 3 => self.e = val, 158 | 4 => self.h = val, 159 | 5 => self.l = val, 160 | 6 => { 161 | let hl = self.hl(); 162 | self.write_mem8(hl, val); 163 | } 164 | 7 => self.a = val, 165 | _ => panic!("Invalid operand index: {}", idx), 166 | } 167 | } 168 | 169 | /// Reads 8-bit operand 170 | fn read_r8(&mut self, idx: u8) -> u8 { 171 | match idx { 172 | 0 => self.b, 173 | 1 => self.c, 174 | 2 => self.d, 175 | 3 => self.e, 176 | 4 => self.h, 177 | 5 => self.l, 178 | 6 => { 179 | let hl = self.hl(); 180 | self.read_mem8(hl) 181 | } 182 | 7 => self.a, 183 | _ => panic!("Invalid operand index: {}", idx), 184 | } 185 | } 186 | 187 | /// Writes 16-bit operand 188 | fn write_r16(&mut self, idx: u8, val: u16) { 189 | match idx { 190 | 0 => self.set_bc(val), 191 | 1 => self.set_de(val), 192 | 2 => self.set_hl(val), 193 | 3 => self.sp = val, 194 | _ => panic!("Invalid operand index: {}", idx), 195 | } 196 | } 197 | 198 | /// Reads 16-bit operand 199 | fn read_r16(&mut self, idx: u8) -> u16 { 200 | match idx { 201 | 0 => self.bc(), 202 | 1 => self.de(), 203 | 2 => self.hl(), 204 | 3 => self.sp, 205 | _ => panic!("Invalid operand index: {}", idx), 206 | } 207 | } 208 | 209 | /// Reads 8-bit immediate from memory 210 | fn read_d8(&mut self) -> u8 { 211 | let pc = self.pc; 212 | let imm = self.read_mem8(pc); 213 | self.pc = self.pc.wrapping_add(1); 214 | 215 | imm 216 | } 217 | 218 | /// Reads 16-bit immediate from memory 219 | fn read_d16(&mut self) -> u16 { 220 | let pc = self.pc; 221 | let imm = self.read_mem16(pc); 222 | self.pc = self.pc.wrapping_add(2); 223 | 224 | imm 225 | } 226 | 227 | /// Checks branch condition 228 | fn cc(&self, idx: u8) -> bool { 229 | match idx { 230 | 0 => !self.f_z(), 231 | 1 => self.f_z(), 232 | 2 => !self.f_c(), 233 | 3 => self.f_c(), 234 | _ => panic!("Invalid branch condition index: {}", idx), 235 | } 236 | } 237 | 238 | /// Converts branch condition to name 239 | fn cc_to_string(idx: u8) -> String { 240 | match idx { 241 | 0 => String::from("NZ"), 242 | 1 => String::from("Z"), 243 | 2 => String::from("NC"), 244 | 3 => String::from("C"), 245 | _ => panic!("Invalid branch condition index: {}", idx), 246 | } 247 | } 248 | 249 | /// Writes 8-bit value to memory 250 | fn write_mem8(&mut self, addr: u16, val: u8) { 251 | self.mmu.write(addr, val); 252 | 253 | self.tick += 4; 254 | } 255 | 256 | /// Reads 8-bit value from memory 257 | fn read_mem8(&mut self, addr: u16) -> u8 { 258 | let ret = self.mmu.read(addr); 259 | 260 | self.tick += 4; 261 | 262 | ret 263 | } 264 | 265 | /// Writes 16-bit value to memory 266 | fn write_mem16(&mut self, addr: u16, val: u16) { 267 | self.write_mem8(addr, (val & 0xff) as u8); 268 | self.write_mem8(addr.wrapping_add(1), (val >> 8) as u8); 269 | } 270 | 271 | /// Reads 16-bit value from memory 272 | fn read_mem16(&mut self, addr: u16) -> u16 { 273 | let lo = self.read_mem8(addr); 274 | let hi = self.read_mem8(addr.wrapping_add(1)); 275 | 276 | (hi as u16) << 8 | lo as u16 277 | } 278 | 279 | /// NOP 280 | fn nop(&mut self) { 281 | trace!("NOP"); 282 | } 283 | 284 | /// LD r16, d16 285 | fn ld_r16_d16(&mut self, reg: u8) { 286 | let val = self.read_d16(); 287 | 288 | trace!("LD {}, 0x{:04x}", Self::reg16_to_string(reg), val); 289 | 290 | self.write_r16(reg, val); 291 | } 292 | 293 | /// LD (d16), SP 294 | fn ld_ind_d16_sp(&mut self) { 295 | let addr = self.read_d16(); 296 | let sp = self.sp; 297 | 298 | trace!("LD (0x{:04x}), SP", addr); 299 | 300 | self.write_mem16(addr, sp); 301 | } 302 | 303 | /// LD SP, HL 304 | fn ld_sp_hl(&mut self) { 305 | trace!("LD SP, HL"); 306 | 307 | self.tick += 4; 308 | 309 | self.sp = self.hl(); 310 | } 311 | 312 | /// ADD HL, r16 313 | fn add_hl_r16(&mut self, reg: u8) { 314 | trace!("ADD HL, {}", Self::reg16_to_string(reg)); 315 | 316 | let hl = self.hl(); 317 | let val = self.read_r16(reg); 318 | 319 | let half_carry = (hl & 0xfff) + (val & 0xfff) > 0xfff; 320 | let (res, carry) = hl.overflowing_add(val); 321 | self.set_hl(res); 322 | 323 | self.tick += 4; 324 | 325 | self.set_f_n(false); 326 | self.set_f_h(half_carry); 327 | self.set_f_c(carry); 328 | } 329 | 330 | fn _add_sp(&mut self, offset: i8) -> u16 { 331 | let val = offset as u16; 332 | 333 | let half_carry = (self.sp & 0x0f) + (val & 0x0f) > 0x0f; 334 | let carry = (self.sp & 0xff) + (val & 0xff) > 0xff; 335 | 336 | self.set_f_z(false); 337 | self.set_f_n(false); 338 | self.set_f_h(half_carry); 339 | self.set_f_c(carry); 340 | 341 | self.sp.wrapping_add(val) 342 | } 343 | 344 | /// ADD SP, d8 345 | fn add_sp_d8(&mut self) { 346 | let val = self.read_d8() as i8; 347 | 348 | trace!("ADD SP, {}", val); 349 | 350 | self.sp = self._add_sp(val); 351 | 352 | self.tick += 8; 353 | } 354 | 355 | /// LD HL, SP+d8 356 | fn ld_hl_sp_d8(&mut self) { 357 | let offset = self.read_d8() as i8; 358 | 359 | trace!("LD HL, SP{:+}", offset); 360 | 361 | self.tick += 4; 362 | 363 | let res = self._add_sp(offset); 364 | self.set_hl(res); 365 | } 366 | 367 | /// AND r8 368 | fn and_r8(&mut self, reg: u8) { 369 | trace!("AND {}", Self::reg_to_string(reg)); 370 | 371 | let res = self.a & self.read_r8(reg); 372 | 373 | self.a = res; 374 | 375 | self.set_f_z(res == 0); 376 | self.set_f_n(false); 377 | self.set_f_h(true); 378 | self.set_f_c(false); 379 | } 380 | 381 | /// OR r8 382 | fn or_r8(&mut self, reg: u8) { 383 | trace!("OR {}", Self::reg_to_string(reg)); 384 | 385 | let res = self.a | self.read_r8(reg); 386 | 387 | self.a = res; 388 | 389 | self.set_f_z(res == 0); 390 | self.set_f_n(false); 391 | self.set_f_h(false); 392 | self.set_f_c(false); 393 | } 394 | 395 | /// XOR r8 396 | fn xor_r8(&mut self, reg: u8) { 397 | trace!("XOR {}", Self::reg_to_string(reg)); 398 | 399 | let res = self.a ^ self.read_r8(reg); 400 | 401 | self.a = res; 402 | 403 | self.set_f_z(res == 0); 404 | self.set_f_n(false); 405 | self.set_f_h(false); 406 | self.set_f_c(false); 407 | } 408 | 409 | /// CP r8 410 | fn cp_r8(&mut self, reg: u8) { 411 | trace!("CP {}", Self::reg_to_string(reg)); 412 | 413 | let a = self.a; 414 | let val = self.read_r8(reg); 415 | 416 | self.set_f_z(a == val); 417 | self.set_f_n(true); 418 | self.set_f_h(a & 0x0f < val & 0x0f); 419 | self.set_f_c(a < val); 420 | } 421 | 422 | /// Decimal adjust register A 423 | fn daa(&mut self) { 424 | trace!("DAA"); 425 | 426 | let mut a = self.a; 427 | 428 | if !self.f_n() { 429 | if self.f_c() || a > 0x99 { 430 | a = a.wrapping_add(0x60); 431 | self.set_f_c(true); 432 | } 433 | if self.f_h() || a & 0x0f > 0x09 { 434 | a = a.wrapping_add(0x06); 435 | } 436 | } else { 437 | if self.f_c() { 438 | a = a.wrapping_sub(0x60); 439 | } 440 | if self.f_h() { 441 | a = a.wrapping_sub(0x06); 442 | } 443 | } 444 | 445 | self.a = a; 446 | 447 | self.set_f_z(a == 0); 448 | self.set_f_h(false); 449 | } 450 | 451 | /// Complement A 452 | fn cpl(&mut self) { 453 | trace!("CPL"); 454 | 455 | self.a = !self.a; 456 | self.set_f_n(true); 457 | self.set_f_h(true); 458 | } 459 | 460 | /// Complement carry flag 461 | fn ccf(&mut self) { 462 | trace!("CCF"); 463 | 464 | self.set_f_n(false); 465 | self.set_f_h(false); 466 | 467 | let c = self.f_c(); 468 | self.set_f_c(!c); 469 | } 470 | 471 | /// Set carry flag 472 | fn scf(&mut self) { 473 | trace!("SCF"); 474 | 475 | self.set_f_n(false); 476 | self.set_f_h(false); 477 | self.set_f_c(true); 478 | } 479 | 480 | fn _add(&mut self, val: u8) { 481 | let half_carry = (self.a & 0xf) + (val & 0xf) > 0xf; 482 | let (res, carry) = self.a.overflowing_add(val); 483 | 484 | self.a = res; 485 | 486 | self.set_f_z(res == 0); 487 | self.set_f_n(false); 488 | self.set_f_h(half_carry); 489 | self.set_f_c(carry); 490 | } 491 | 492 | /// ADD r8 493 | fn add_r8(&mut self, reg: u8) { 494 | let val = self.read_r8(reg); 495 | 496 | trace!("ADD {}", Self::reg_to_string(reg)); 497 | 498 | self._add(val); 499 | } 500 | 501 | /// ADC r8 502 | fn adc_r8(&mut self, reg: u8) { 503 | let val = self.read_r8(reg); 504 | 505 | trace!("ADC {}", Self::reg_to_string(reg)); 506 | 507 | self._adc(val); 508 | } 509 | 510 | /// SUB r8 511 | fn sub_r8(&mut self, reg: u8) { 512 | let val = self.read_r8(reg); 513 | 514 | trace!("SUB {}", Self::reg_to_string(reg)); 515 | 516 | self._sub(val); 517 | } 518 | 519 | /// SBC r8 520 | fn sbc_r8(&mut self, reg: u8) { 521 | let val = self.read_r8(reg); 522 | 523 | trace!("SBC {}", Self::reg_to_string(reg)); 524 | 525 | self._sbc(val); 526 | } 527 | 528 | /// ADD d8 529 | fn add_d8(&mut self) { 530 | let val = self.read_d8(); 531 | 532 | trace!("ADD 0x{:02x}", val); 533 | 534 | self._add(val); 535 | } 536 | 537 | fn _sub(&mut self, val: u8) { 538 | let half_carry = (self.a & 0xf) < (val & 0xf); 539 | let (res, carry) = self.a.overflowing_sub(val); 540 | 541 | self.a = res; 542 | 543 | self.set_f_z(res == 0); 544 | self.set_f_n(true); 545 | self.set_f_h(half_carry); 546 | self.set_f_c(carry); 547 | } 548 | 549 | /// SUB d8 550 | fn sub_d8(&mut self) { 551 | let val = self.read_d8(); 552 | 553 | trace!("SUB 0x{:02x}", val); 554 | 555 | self._sub(val); 556 | } 557 | 558 | fn _adc(&mut self, val: u8) { 559 | let c = if self.f_c() { 1 } else { 0 }; 560 | 561 | let res = self.a.wrapping_add(val).wrapping_add(c); 562 | let half_carry = (self.a & 0xf) + (val & 0xf) + c > 0xf; 563 | let carry = (self.a as u16) + (val as u16) + (c as u16) > 0xff; 564 | 565 | self.a = res; 566 | 567 | self.set_f_z(res == 0); 568 | self.set_f_n(false); 569 | self.set_f_h(half_carry); 570 | self.set_f_c(carry); 571 | } 572 | 573 | /// ADC d8 574 | fn adc_d8(&mut self) { 575 | let val = self.read_d8(); 576 | 577 | trace!("ADC 0x{:02x}", val); 578 | 579 | self._adc(val); 580 | } 581 | 582 | fn _sbc(&mut self, val: u8) { 583 | let c = if self.f_c() { 1 } else { 0 }; 584 | 585 | let res = self.a.wrapping_sub(val).wrapping_sub(c); 586 | let half_carry = (self.a & 0xf) < (val & 0xf) + c; 587 | let carry = (self.a as u16) < (val as u16) + (c as u16); 588 | 589 | self.a = res; 590 | 591 | self.set_f_z(res == 0); 592 | self.set_f_n(true); 593 | self.set_f_h(half_carry); 594 | self.set_f_c(carry); 595 | } 596 | 597 | /// SBC d8 598 | fn sbc_d8(&mut self) { 599 | let val = self.read_d8(); 600 | 601 | trace!("SBC 0x{:02x}", val); 602 | 603 | self._sbc(val); 604 | } 605 | 606 | /// AND d8 607 | fn and_d8(&mut self) { 608 | let val = self.read_d8(); 609 | 610 | trace!("AND 0x{:02x}", val); 611 | 612 | let res = self.a & val; 613 | 614 | self.a = res; 615 | 616 | self.set_f_z(res == 0); 617 | self.set_f_n(false); 618 | self.set_f_h(true); 619 | self.set_f_c(false); 620 | } 621 | 622 | /// OR d8 623 | fn or_d8(&mut self) { 624 | let val = self.read_d8(); 625 | 626 | trace!("OR 0x{:02x}", val); 627 | 628 | let res = self.a | val; 629 | 630 | self.a = res; 631 | 632 | self.set_f_z(res == 0); 633 | self.set_f_n(false); 634 | self.set_f_h(false); 635 | self.set_f_c(false); 636 | } 637 | 638 | /// XOR d8 639 | fn xor_d8(&mut self) { 640 | let val = self.read_d8(); 641 | 642 | trace!("XOR 0x{:02x}", val); 643 | 644 | let res = self.a ^ val; 645 | 646 | self.a = res; 647 | 648 | self.set_f_z(res == 0); 649 | self.set_f_n(false); 650 | self.set_f_h(false); 651 | self.set_f_c(false); 652 | } 653 | 654 | /// CP d8 655 | fn cp_d8(&mut self) { 656 | let imm = self.read_d8(); 657 | 658 | trace!("CP 0x{:02x}", imm); 659 | 660 | let a = self.a; 661 | 662 | self.set_f_z(a == imm); 663 | self.set_f_n(true); 664 | self.set_f_h(a & 0x0f < imm & 0x0f); 665 | self.set_f_c(a < imm); 666 | } 667 | 668 | fn ldi_hl_a(&mut self) { 669 | trace!("LD (HL+), A"); 670 | 671 | let addr = self.hl(); 672 | let a = self.a; 673 | self.write_mem8(addr, a); 674 | let hl = self.hl(); 675 | self.set_hl(hl.wrapping_add(1)); 676 | } 677 | 678 | fn ldd_hl_a(&mut self) { 679 | trace!("LD (HL-), A"); 680 | 681 | let addr = self.hl(); 682 | let a = self.a; 683 | self.write_mem8(addr, a); 684 | let hl = self.hl(); 685 | self.set_hl(hl.wrapping_sub(1)); 686 | } 687 | 688 | fn ldi_a_hl(&mut self) { 689 | trace!("LD A, (HL+)"); 690 | 691 | let addr = self.hl(); 692 | self.a = self.read_mem8(addr); 693 | let hl = self.hl(); 694 | self.set_hl(hl.wrapping_add(1)); 695 | } 696 | 697 | fn ldd_a_hl(&mut self) { 698 | trace!("LD A, (HL-)"); 699 | 700 | let addr = self.hl(); 701 | self.a = self.read_mem8(addr); 702 | let hl = self.hl(); 703 | self.set_hl(hl.wrapping_sub(1)); 704 | } 705 | 706 | fn ld_ind_bc_a(&mut self) { 707 | trace!("LD (BC), A"); 708 | 709 | let addr = self.bc(); 710 | let a = self.a; 711 | self.write_mem8(addr, a); 712 | } 713 | 714 | fn ld_ind_de_a(&mut self) { 715 | trace!("LD (DE), A"); 716 | 717 | let addr = self.de(); 718 | let a = self.a; 719 | self.write_mem8(addr, a); 720 | } 721 | 722 | fn ld_a_ind_bc(&mut self) { 723 | trace!("LD A, (BC)"); 724 | 725 | let bc = self.bc(); 726 | 727 | self.a = self.read_mem8(bc); 728 | } 729 | 730 | fn ld_a_ind_de(&mut self) { 731 | trace!("LD A, (DE)"); 732 | 733 | let de = self.de(); 734 | 735 | self.a = self.read_mem8(de); 736 | } 737 | 738 | /// Test bit 739 | fn bit(&mut self, pos: u8, reg: u8) { 740 | trace!("BIT {}, {}", pos, Self::reg_to_string(reg)); 741 | 742 | let z = (self.read_r8(reg) >> pos & 1) == 0; 743 | self.set_f_z(z); 744 | self.set_f_n(false); 745 | self.set_f_h(true); 746 | } 747 | 748 | /// Set bit 749 | fn set(&mut self, pos: u8, reg: u8) { 750 | trace!("SET {}, {}", pos, Self::reg_to_string(reg)); 751 | 752 | let val = self.read_r8(reg); 753 | self.write_r8(reg, val | (1 << pos)); 754 | } 755 | 756 | /// Reset bit 757 | fn res(&mut self, pos: u8, reg: u8) { 758 | trace!("RES {}, {}", pos, Self::reg_to_string(reg)); 759 | 760 | let val = self.read_r8(reg); 761 | self.write_r8(reg, val & !(1 << pos)); 762 | } 763 | 764 | fn _rl(&mut self, reg: u8) { 765 | let orig = self.read_r8(reg); 766 | let res = (orig << 1) | (if self.f_c() { 1 } else { 0 }); 767 | self.write_r8(reg, res); 768 | 769 | self.set_f_z(res == 0); 770 | self.set_f_n(false); 771 | self.set_f_h(false); 772 | self.set_f_c(orig >> 7 & 1 == 1); 773 | } 774 | 775 | /// Rotate left through carry 776 | fn rl(&mut self, reg: u8) { 777 | trace!("RL {}", Self::reg_to_string(reg)); 778 | 779 | self._rl(reg); 780 | } 781 | 782 | fn _rlc(&mut self, reg: u8) { 783 | let orig = self.read_r8(reg); 784 | let res = orig.rotate_left(1); 785 | self.write_r8(reg, res); 786 | 787 | self.set_f_z(res == 0); 788 | self.set_f_n(false); 789 | self.set_f_h(false); 790 | self.set_f_c(orig >> 7 & 1 == 1); 791 | } 792 | 793 | /// Rotate left 794 | fn rlc(&mut self, reg: u8) { 795 | trace!("RLC {}", Self::reg_to_string(reg)); 796 | 797 | self._rlc(reg); 798 | } 799 | 800 | fn _rr(&mut self, reg: u8) { 801 | let orig = self.read_r8(reg); 802 | let res = (orig >> 1) | (if self.f_c() { 1 } else { 0 } << 7); 803 | self.write_r8(reg, res); 804 | 805 | self.set_f_z(res == 0); 806 | self.set_f_n(false); 807 | self.set_f_h(false); 808 | self.set_f_c(orig & 1 == 1); 809 | } 810 | 811 | /// Rotate right through carry 812 | fn rr(&mut self, reg: u8) { 813 | trace!("RR {}", Self::reg_to_string(reg)); 814 | 815 | self._rr(reg); 816 | } 817 | 818 | fn _rrc(&mut self, reg: u8) { 819 | let orig = self.read_r8(reg); 820 | let res = orig.rotate_right(1); 821 | self.write_r8(reg, res); 822 | 823 | self.set_f_z(res == 0); 824 | self.set_f_n(false); 825 | self.set_f_h(false); 826 | self.set_f_c(orig & 1 == 1); 827 | } 828 | 829 | /// Rotate right 830 | fn rrc(&mut self, reg: u8) { 831 | trace!("RRC {}", Self::reg_to_string(reg)); 832 | 833 | self._rrc(reg); 834 | } 835 | 836 | /// Shift left into carry 837 | fn sla(&mut self, reg: u8) { 838 | trace!("SLA {}", Self::reg_to_string(reg)); 839 | 840 | let orig = self.read_r8(reg); 841 | let res = orig << 1; 842 | self.write_r8(reg, res); 843 | 844 | self.set_f_z(res == 0); 845 | self.set_f_n(false); 846 | self.set_f_h(false); 847 | self.set_f_c(orig & 0x80 > 0); 848 | } 849 | 850 | /// Shift right into carry 851 | fn sra(&mut self, reg: u8) { 852 | trace!("SRA {}", Self::reg_to_string(reg)); 853 | 854 | let orig = self.read_r8(reg); 855 | let res = (orig >> 1) | (orig & 0x80); 856 | self.write_r8(reg, res); 857 | 858 | self.set_f_z(res == 0); 859 | self.set_f_n(false); 860 | self.set_f_h(false); 861 | self.set_f_c(orig & 1 > 0); 862 | } 863 | 864 | /// Swap low/hi-nibble 865 | fn swap(&mut self, reg: u8) { 866 | trace!("SWAP {}", Self::reg_to_string(reg)); 867 | 868 | let orig = self.read_r8(reg); 869 | let res = ((orig & 0x0f) << 4) | ((orig & 0xf0) >> 4); 870 | self.write_r8(reg, res); 871 | 872 | self.set_f_z(res == 0); 873 | self.set_f_n(false); 874 | self.set_f_h(false); 875 | self.set_f_c(false); 876 | } 877 | 878 | /// Shift right through carry 879 | fn srl(&mut self, reg: u8) { 880 | trace!("SRL {}", Self::reg_to_string(reg)); 881 | 882 | let orig = self.read_r8(reg); 883 | let res = orig >> 1; 884 | self.write_r8(reg, res); 885 | 886 | self.set_f_z(res == 0); 887 | self.set_f_n(false); 888 | self.set_f_h(false); 889 | self.set_f_c(orig & 1 == 1); 890 | } 891 | 892 | fn _jp(&mut self, addr: u16) { 893 | self.pc = addr; 894 | 895 | self.tick += 4; 896 | } 897 | 898 | fn jp_cc_d8(&mut self, cci: u8) { 899 | let addr = self.read_d16(); 900 | 901 | trace!("JP {}, 0x{:04x}", Self::cc_to_string(cci), addr); 902 | 903 | if self.cc(cci) { 904 | self._jp(addr); 905 | } 906 | } 907 | 908 | /// Unconditional jump to d16 909 | fn jp_d16(&mut self) { 910 | let address = self.read_d16(); 911 | 912 | trace!("JP 0x{:04x}", address); 913 | 914 | self._jp(address); 915 | } 916 | 917 | /// Unconditional jump to HL 918 | fn jp_hl(&mut self) { 919 | trace!("JP (HL)"); 920 | 921 | self.pc = self.hl(); 922 | } 923 | 924 | /// Jump to pc+d8 if CC 925 | fn jr_cc_d8(&mut self, cci: u8) { 926 | let offset = self.read_d8() as i8; 927 | 928 | trace!("JR {}, {}", Self::cc_to_string(cci), offset); 929 | 930 | if self.cc(cci) { 931 | self._jr(offset); 932 | } 933 | } 934 | 935 | fn _jr(&mut self, offset: i8) { 936 | self.pc = self.pc.wrapping_add(offset as u16); 937 | 938 | self.tick += 4; 939 | } 940 | 941 | /// Jump to pc+d8 942 | fn jr_d8(&mut self) { 943 | let offset = self.read_d8() as i8; 944 | 945 | trace!("JR {}", offset); 946 | 947 | self._jr(offset); 948 | } 949 | 950 | fn ld_io_d8_a(&mut self) { 951 | let offset = self.read_d8() as u16; 952 | let addr = 0xff00 | offset; 953 | let a = self.a; 954 | 955 | trace!("LD (0xff00+0x{:02x}), A", offset); 956 | 957 | self.write_mem8(addr, a); 958 | } 959 | 960 | fn ld_a_io_d8(&mut self) { 961 | let offset = self.read_d8() as u16; 962 | let addr = 0xff00 | offset; 963 | 964 | trace!("LD A, (0xff00+0x{:02x})", offset); 965 | 966 | self.a = self.read_mem8(addr); 967 | } 968 | 969 | fn ld_io_c_a(&mut self) { 970 | let addr = 0xff00 | self.c as u16; 971 | let a = self.a; 972 | 973 | trace!("LD (0xff00+C), A"); 974 | 975 | self.write_mem8(addr, a); 976 | } 977 | 978 | fn ld_a_io_c(&mut self) { 979 | let addr = 0xff00 | self.c as u16; 980 | 981 | trace!("LD A, (0xff00+C)"); 982 | 983 | self.a = self.read_mem8(addr); 984 | } 985 | 986 | /// LD r8, d8 987 | fn ld_r8_d8(&mut self, reg: u8) { 988 | let imm = self.read_d8(); 989 | 990 | trace!("LD {}, 0x{:02x}", Self::reg_to_string(reg), imm); 991 | 992 | self.write_r8(reg, imm); 993 | } 994 | 995 | /// INC r8 996 | fn inc_r8(&mut self, reg: u8) { 997 | trace!("INC {}", Self::reg_to_string(reg)); 998 | 999 | let orig = self.read_r8(reg); 1000 | let res = orig.wrapping_add(1); 1001 | self.write_r8(reg, res); 1002 | 1003 | self.set_f_z(res == 0); 1004 | self.set_f_h(orig & 0x0f == 0x0f); 1005 | self.set_f_n(false); 1006 | } 1007 | 1008 | /// DEC r8 1009 | fn dec_r8(&mut self, reg: u8) { 1010 | trace!("DEC {}", Self::reg_to_string(reg)); 1011 | 1012 | let orig = self.read_r8(reg); 1013 | let res = orig.wrapping_sub(1); 1014 | self.write_r8(reg, res); 1015 | 1016 | self.set_f_z(res == 0); 1017 | self.set_f_h(orig & 0x0f == 0x00); 1018 | self.set_f_n(true); 1019 | } 1020 | 1021 | /// LD r8, r8 1022 | fn ld_r8_r8(&mut self, reg1: u8, reg2: u8) { 1023 | trace!( 1024 | "LD {}, {}", 1025 | Self::reg_to_string(reg1), 1026 | Self::reg_to_string(reg2) 1027 | ); 1028 | 1029 | let val = self.read_r8(reg2); 1030 | self.write_r8(reg1, val); 1031 | } 1032 | 1033 | fn _call(&mut self, addr: u16) { 1034 | self.sp = self.sp.wrapping_sub(2); 1035 | let sp = self.sp; 1036 | let pc = self.pc; 1037 | 1038 | self.tick += 4; 1039 | 1040 | self.write_mem16(sp, pc); 1041 | self.pc = addr; 1042 | } 1043 | 1044 | /// CALL d16 1045 | fn call_d16(&mut self) { 1046 | let addr = self.read_d16(); 1047 | 1048 | trace!("CALL 0x{:04x}", addr); 1049 | 1050 | self._call(addr); 1051 | } 1052 | 1053 | /// CALL CC, d16 1054 | fn call_cc_d16(&mut self, cci: u8) { 1055 | let addr = self.read_d16(); 1056 | 1057 | trace!("CALL {}, 0x{:04x}", Self::cc_to_string(cci), addr); 1058 | 1059 | if self.cc(cci) { 1060 | self._call(addr); 1061 | } 1062 | } 1063 | 1064 | fn rst(&mut self, addr: u8) { 1065 | trace!("RST 0x{:02x}", addr); 1066 | 1067 | self._call(addr as u16); 1068 | } 1069 | 1070 | fn _ret(&mut self) { 1071 | let sp = self.sp; 1072 | self.pc = self.read_mem16(sp); 1073 | self.sp = self.sp.wrapping_add(2); 1074 | 1075 | self.tick += 4; 1076 | } 1077 | 1078 | /// RET 1079 | fn ret(&mut self) { 1080 | trace!("RET"); 1081 | 1082 | self._ret(); 1083 | } 1084 | 1085 | /// RET CC 1086 | fn ret_cc(&mut self, cci: u8) { 1087 | trace!("RET {}", Self::cc_to_string(cci)); 1088 | 1089 | self.tick += 4; 1090 | 1091 | if self.cc(cci) { 1092 | self._ret(); 1093 | } 1094 | } 1095 | 1096 | /// PUSH BC 1097 | fn push_bc(&mut self) { 1098 | trace!("PUSH BC"); 1099 | 1100 | self.sp = self.sp.wrapping_sub(2); 1101 | let val = self.bc(); 1102 | let sp = self.sp; 1103 | 1104 | self.tick += 4; 1105 | 1106 | self.write_mem16(sp, val); 1107 | } 1108 | 1109 | /// PUSH DE 1110 | fn push_de(&mut self) { 1111 | trace!("PUSH DE"); 1112 | 1113 | self.sp = self.sp.wrapping_sub(2); 1114 | let val = self.de(); 1115 | let sp = self.sp; 1116 | 1117 | self.tick += 4; 1118 | 1119 | self.write_mem16(sp, val); 1120 | } 1121 | 1122 | /// PUSH HL 1123 | fn push_hl(&mut self) { 1124 | trace!("PUSH HL"); 1125 | 1126 | self.sp = self.sp.wrapping_sub(2); 1127 | let val = self.hl(); 1128 | let sp = self.sp; 1129 | 1130 | self.tick += 4; 1131 | 1132 | self.write_mem16(sp, val); 1133 | } 1134 | 1135 | /// PUSH AF 1136 | fn push_af(&mut self) { 1137 | trace!("PUSH AF"); 1138 | 1139 | self.sp = self.sp.wrapping_sub(2); 1140 | let val = self.af(); 1141 | let sp = self.sp; 1142 | 1143 | self.tick += 4; 1144 | 1145 | self.write_mem16(sp, val); 1146 | } 1147 | 1148 | /// POP BC 1149 | fn pop_bc(&mut self) { 1150 | trace!("POP BC"); 1151 | 1152 | let sp = self.sp; 1153 | let val = self.read_mem16(sp); 1154 | self.set_bc(val); 1155 | self.sp = self.sp.wrapping_add(2); 1156 | } 1157 | 1158 | /// POP DE 1159 | fn pop_de(&mut self) { 1160 | trace!("POP DE"); 1161 | 1162 | let sp = self.sp; 1163 | let val = self.read_mem16(sp); 1164 | self.set_de(val); 1165 | self.sp = self.sp.wrapping_add(2); 1166 | } 1167 | 1168 | /// POP HL 1169 | fn pop_hl(&mut self) { 1170 | trace!("POP HL"); 1171 | 1172 | let sp = self.sp; 1173 | let val = self.read_mem16(sp); 1174 | self.set_hl(val); 1175 | self.sp = self.sp.wrapping_add(2); 1176 | } 1177 | 1178 | /// POP AF 1179 | fn pop_af(&mut self) { 1180 | trace!("POP AF"); 1181 | 1182 | let sp = self.sp; 1183 | // lower nibble of F is always zero 1184 | let val = self.read_mem16(sp) & 0xfff0; 1185 | self.set_af(val); 1186 | self.sp = self.sp.wrapping_add(2); 1187 | } 1188 | 1189 | fn rlca(&mut self) { 1190 | trace!("RLCA"); 1191 | 1192 | self._rlc(7); 1193 | self.set_f_z(false); 1194 | } 1195 | 1196 | fn rla(&mut self) { 1197 | trace!("RLA"); 1198 | 1199 | self._rl(7); 1200 | self.set_f_z(false); 1201 | } 1202 | 1203 | fn rrca(&mut self) { 1204 | trace!("RLRA"); 1205 | 1206 | self._rrc(7); 1207 | self.set_f_z(false); 1208 | } 1209 | 1210 | fn rra(&mut self) { 1211 | trace!("RRA"); 1212 | 1213 | self._rr(7); 1214 | self.set_f_z(false); 1215 | } 1216 | 1217 | fn inc_r16(&mut self, reg: u8) { 1218 | trace!("INC {}", Self::reg16_to_string(reg)); 1219 | 1220 | let val = self.read_r16(reg); 1221 | self.write_r16(reg, val.wrapping_add(1)); 1222 | 1223 | self.tick += 4; 1224 | } 1225 | 1226 | fn dec_r16(&mut self, reg: u8) { 1227 | trace!("DEC {}", Self::reg16_to_string(reg)); 1228 | 1229 | let val = self.read_r16(reg); 1230 | self.write_r16(reg, val.wrapping_sub(1)); 1231 | 1232 | self.tick += 4; 1233 | } 1234 | 1235 | fn ld_ind_d16_a(&mut self) { 1236 | let addr = self.read_d16(); 1237 | let a = self.a; 1238 | 1239 | trace!("LD (0x{:04x}), A", addr); 1240 | 1241 | self.write_mem8(addr, a); 1242 | } 1243 | 1244 | fn ld_a_ind_d16(&mut self) { 1245 | let addr = self.read_d16(); 1246 | 1247 | trace!("LD A, (0x{:04x})", addr); 1248 | 1249 | self.a = self.read_mem8(addr); 1250 | } 1251 | 1252 | /// Disable interrupt 1253 | fn di(&mut self) { 1254 | trace!("DI"); 1255 | 1256 | self.ime = false; 1257 | } 1258 | 1259 | /// Enable interrupt 1260 | fn ei(&mut self) { 1261 | trace!("EI"); 1262 | 1263 | self.ime = true; 1264 | } 1265 | 1266 | /// Enable interrupt and return 1267 | fn reti(&mut self) { 1268 | trace!("RETI"); 1269 | 1270 | self.ime = true; 1271 | 1272 | self._ret(); 1273 | } 1274 | 1275 | /// Prefixed instructions 1276 | fn prefix(&mut self) { 1277 | let opcode = self.read_d8(); 1278 | let pos = opcode >> 3 & 0x7; 1279 | let reg = opcode & 0x7; 1280 | 1281 | match opcode { 1282 | 0x00..=0x07 => self.rlc(reg), 1283 | 0x08..=0x0f => self.rrc(reg), 1284 | 0x10..=0x17 => self.rl(reg), 1285 | 0x18..=0x1f => self.rr(reg), 1286 | 0x20..=0x27 => self.sla(reg), 1287 | 0x28..=0x2f => self.sra(reg), 1288 | 0x30..=0x37 => self.swap(reg), 1289 | 0x38..=0x3f => self.srl(reg), 1290 | 0x40..=0x7f => self.bit(pos, reg), 1291 | 0x80..=0xbf => self.res(pos, reg), 1292 | 0xc0..=0xff => self.set(pos, reg), 1293 | } 1294 | } 1295 | 1296 | /// HALT 1297 | fn halt(&mut self) { 1298 | trace!("HALT"); 1299 | 1300 | if self.ime { 1301 | self.halted = true; 1302 | } 1303 | } 1304 | 1305 | /// Execute a single instruction and handle IRQs. 1306 | pub fn step(&mut self) -> u8 { 1307 | let mut total_tick = 0; 1308 | 1309 | self.tick = 0; 1310 | 1311 | if self.halted { 1312 | self.tick += 4; 1313 | } else { 1314 | self.fetch_and_exec(); 1315 | } 1316 | 1317 | total_tick += self.tick; 1318 | 1319 | self.mmu.update(self.tick); 1320 | 1321 | if self.ime { 1322 | self.tick = 0; 1323 | self.check_irqs(); 1324 | self.mmu.update(self.tick); 1325 | 1326 | total_tick += self.tick; 1327 | } 1328 | 1329 | total_tick 1330 | } 1331 | 1332 | /// Checks IRQs and execute ISRs if requested. 1333 | fn check_irqs(&mut self) { 1334 | // Bit 0 has the highest priority 1335 | for i in 0..5 { 1336 | let irq = self.mmu.int_flag & (1 << i) > 0; 1337 | let ie = self.mmu.int_enable & (1 << i) > 0; 1338 | 1339 | // If interrupt is requested and enabled 1340 | if irq && ie { 1341 | self.call_isr(i); 1342 | break; 1343 | } 1344 | } 1345 | } 1346 | 1347 | /// Calls requested interrupt service routine. 1348 | fn call_isr(&mut self, id: u8) { 1349 | // Reset corresponding bit in IF 1350 | self.mmu.int_flag &= !(1 << id); 1351 | // Clear IME (disable any further interrupts) 1352 | self.ime = false; 1353 | self.halted = false; 1354 | 1355 | let isr: u16 = match id { 1356 | 0 => 0x40, 1357 | 1 => 0x48, 1358 | 2 => 0x50, 1359 | 3 => 0x80, 1360 | 4 => 0x70, 1361 | _ => panic!("Invalid IRQ id {}", id), 1362 | }; 1363 | 1364 | self.tick += 8; 1365 | 1366 | debug!("Calling ISR 0x{:02x}", isr); 1367 | 1368 | self._call(isr); 1369 | } 1370 | 1371 | /// Fetches and executes a single instructions. 1372 | fn fetch_and_exec(&mut self) { 1373 | let opcode = self.read_d8(); 1374 | let reg = opcode & 7; 1375 | let reg2 = opcode >> 3 & 7; 1376 | 1377 | match opcode { 1378 | // NOP 1379 | 0x00 => self.nop(), 1380 | 1381 | // LD r16, d16 1382 | 0x01 | 0x11 | 0x21 | 0x31 => self.ld_r16_d16(opcode >> 4), 1383 | 1384 | // LD (d16), SP 1385 | 0x08 => self.ld_ind_d16_sp(), 1386 | 1387 | // LD SP, HL 1388 | 0xf9 => self.ld_sp_hl(), 1389 | 1390 | // LD A, (r16) 1391 | 0x02 => self.ld_ind_bc_a(), 1392 | 0x12 => self.ld_ind_de_a(), 1393 | 0x0a => self.ld_a_ind_bc(), 1394 | 0x1a => self.ld_a_ind_de(), 1395 | 1396 | // PUSH r16 1397 | 0xc5 => self.push_bc(), 1398 | 0xd5 => self.push_de(), 1399 | 0xe5 => self.push_hl(), 1400 | 0xf5 => self.push_af(), 1401 | 1402 | // POP r16 1403 | 0xc1 => self.pop_bc(), 1404 | 0xd1 => self.pop_de(), 1405 | 0xe1 => self.pop_hl(), 1406 | 0xf1 => self.pop_af(), 1407 | 1408 | // Conditional absolute jump 1409 | 0xc2 | 0xd2 | 0xca | 0xda => self.jp_cc_d8(reg2), 1410 | 1411 | // Unconditional absolute jump 1412 | 0xc3 => self.jp_d16(), 1413 | 0xe9 => self.jp_hl(), 1414 | 1415 | // Conditional relative jump 1416 | 0x20 | 0x30 | 0x28 | 0x38 => self.jr_cc_d8(reg2 - 4), 1417 | 1418 | // Unconditional relative jump 1419 | 0x18 => self.jr_d8(), 1420 | 1421 | // Bit rotate on A 1422 | 0x07 => self.rlca(), 1423 | 0x17 => self.rla(), 1424 | 0x0f => self.rrca(), 1425 | 0x1f => self.rra(), 1426 | 1427 | // Arithmethic/logical operation on 16-bit register 1428 | 0x09 | 0x19 | 0x29 | 0x39 => self.add_hl_r16(opcode >> 4), 1429 | 0xe8 => self.add_sp_d8(), 1430 | 0xf8 => self.ld_hl_sp_d8(), 1431 | 1432 | // Arithmethic/logical operation on 8-bit register 1433 | 0x80..=0x87 => self.add_r8(reg), 1434 | 0x88..=0x8f => self.adc_r8(reg), 1435 | 0x90..=0x97 => self.sub_r8(reg), 1436 | 0x98..=0x9f => self.sbc_r8(reg), 1437 | 0xa0..=0xa7 => self.and_r8(reg), 1438 | 0xb0..=0xb7 => self.or_r8(reg), 1439 | 0xa8..=0xaf => self.xor_r8(reg), 1440 | 0xb8..=0xbf => self.cp_r8(reg), 1441 | 1442 | // DAA 1443 | 0x27 => self.daa(), 1444 | 1445 | // CPL 1446 | 0x2f => self.cpl(), 1447 | 1448 | // SCF, CCF 1449 | 0x37 => self.scf(), 1450 | 0x3f => self.ccf(), 1451 | 1452 | // Arithmethic/logical operation on A 1453 | 0xc6 => self.add_d8(), 1454 | 0xd6 => self.sub_d8(), 1455 | 0xe6 => self.and_d8(), 1456 | 0xf6 => self.or_d8(), 1457 | 0xce => self.adc_d8(), 1458 | 0xde => self.sbc_d8(), 1459 | 0xee => self.xor_d8(), 1460 | 0xfe => self.cp_d8(), 1461 | 1462 | // LDI, LDD 1463 | 0x22 => self.ldi_hl_a(), 1464 | 0x32 => self.ldd_hl_a(), 1465 | 0x2a => self.ldi_a_hl(), 1466 | 0x3a => self.ldd_a_hl(), 1467 | 1468 | // LD IO port 1469 | 0xe0 => self.ld_io_d8_a(), 1470 | 0xf0 => self.ld_a_io_d8(), 1471 | 0xe2 => self.ld_io_c_a(), 1472 | 0xf2 => self.ld_a_io_c(), 1473 | 1474 | // LD r8, d8 1475 | 0x06 | 0x0e | 0x16 | 0x1e | 0x26 | 0x2e | 0x36 | 0x3e => self.ld_r8_d8(reg2), 1476 | 1477 | // INC r8 1478 | 0x04 | 0x0c | 0x14 | 0x1c | 0x24 | 0x2c | 0x34 | 0x3c => self.inc_r8(reg2), 1479 | 1480 | // DEC r8 1481 | 0x05 | 0x0d | 0x15 | 0x1d | 0x25 | 0x2d | 0x35 | 0x3d => self.dec_r8(reg2), 1482 | 1483 | // LD r8, r8 1484 | 0x40..=0x75 | 0x77..=0x7f => self.ld_r8_r8(reg2, reg), 1485 | 1486 | // LD (d16), A 1487 | 0xea => self.ld_ind_d16_a(), 1488 | 1489 | // LD A, (d16) 1490 | 0xfa => self.ld_a_ind_d16(), 1491 | 1492 | // INC, DEC r16 1493 | 0x03 | 0x13 | 0x23 | 0x33 => self.inc_r16(opcode >> 4), 1494 | 0x0b | 0x1b | 0x2b | 0x3b => self.dec_r16(opcode >> 4), 1495 | 1496 | // Unconditional call 1497 | 0xcd => self.call_d16(), 1498 | 1499 | // Conditional call 1500 | 0xc4 | 0xd4 | 0xcc | 0xdc => self.call_cc_d16(reg2), 1501 | 1502 | // Unconditional ret 1503 | 0xc9 => self.ret(), 1504 | 1505 | // Conditional ret 1506 | 0xc0 | 0xd0 | 0xc8 | 0xd8 => self.ret_cc(reg2), 1507 | 1508 | // RETI 1509 | 0xd9 => self.reti(), 1510 | 1511 | // RST 1512 | 0xc7 | 0xcf | 0xd7 | 0xdf | 0xe7 | 0xef | 0xf7 | 0xff => self.rst(opcode - 0xc7), 1513 | 1514 | // DI, EI 1515 | 0xf3 => self.di(), 1516 | 0xfb => self.ei(), 1517 | 1518 | // CB prefixed 1519 | 0xcb => self.prefix(), 1520 | 1521 | // HALT 1522 | 0x76 => self.halt(), 1523 | 1524 | _ => panic!("Unimplemented opcode 0x{:x}", opcode), 1525 | } 1526 | } 1527 | 1528 | /// Dumps current CPU state. 1529 | #[allow(dead_code)] 1530 | pub fn dump(&self) { 1531 | println!("CPU State:"); 1532 | println!("PC: 0x{:04x} SP: 0x{:04x}", self.pc, self.sp); 1533 | println!("AF: 0x{:04x} BC: 0x{:04x}", self.af(), self.bc()); 1534 | println!("DE: 0x{:04x} HL: 0x{:04x}", self.de(), self.hl()); 1535 | println!("T: {}", self.tick); 1536 | } 1537 | } 1538 | -------------------------------------------------------------------------------- /src/io_device.rs: -------------------------------------------------------------------------------- 1 | /// An IO device connected to the bus. 2 | pub trait IODevice { 3 | /// Writes a byte to an address. 4 | fn write(&mut self, addr: u16, val: u8); 5 | 6 | /// Reads a byte from an address. 7 | fn read(&self, addr: u16) -> u8; 8 | 9 | /// Progresses the clock for a given number of ticks. 10 | fn update(&mut self, tick: u8); 11 | } 12 | -------------------------------------------------------------------------------- /src/joypad.rs: -------------------------------------------------------------------------------- 1 | use io_device::IODevice; 2 | 3 | /// Joypad 4 | pub struct Joypad { 5 | /// Joypad 6 | joyp: u8, 7 | /// Keypress state 8 | key_state: u8, 9 | /// Interrupt request 10 | pub irq: bool, 11 | } 12 | 13 | #[derive(Hash, Eq, PartialEq)] 14 | pub enum Key { 15 | Down, 16 | Up, 17 | Left, 18 | Right, 19 | Start, 20 | Select, 21 | B, 22 | A, 23 | } 24 | 25 | impl Joypad { 26 | /// Creates a new `Joypad`. 27 | pub fn new() -> Self { 28 | Joypad { 29 | joyp: 0xff, 30 | key_state: 0xff, 31 | irq: false, 32 | } 33 | } 34 | 35 | pub fn keydown(&mut self, key: Key) { 36 | match key { 37 | Key::Down => self.key_state &= !0x80, 38 | Key::Up => self.key_state &= !0x40, 39 | Key::Left => self.key_state &= !0x20, 40 | Key::Right => self.key_state &= !0x10, 41 | Key::Start => self.key_state &= !0x08, 42 | Key::Select => self.key_state &= !0x04, 43 | Key::B => self.key_state &= !0x02, 44 | Key::A => self.key_state &= !0x01, 45 | } 46 | 47 | self.irq = true; 48 | } 49 | 50 | pub fn keyup(&mut self, key: Key) { 51 | match key { 52 | Key::Down => self.key_state |= 0x80, 53 | Key::Up => self.key_state |= 0x40, 54 | Key::Left => self.key_state |= 0x20, 55 | Key::Right => self.key_state |= 0x10, 56 | Key::Start => self.key_state |= 0x08, 57 | Key::Select => self.key_state |= 0x04, 58 | Key::B => self.key_state |= 0x02, 59 | Key::A => self.key_state |= 0x01, 60 | } 61 | } 62 | } 63 | 64 | impl IODevice for Joypad { 65 | fn write(&mut self, addr: u16, val: u8) { 66 | match addr { 67 | 0xff00 => self.joyp = (self.joyp & 0xcf) | (val & 0x30), 68 | _ => unreachable!("Unexpected address: 0x{:04x}", addr), 69 | } 70 | } 71 | 72 | fn read(&self, addr: u16) -> u8 { 73 | match addr { 74 | 0xff00 => { 75 | // Direction keys selected 76 | if self.joyp & 0x10 == 0 { 77 | (self.joyp & 0xf0) | (self.key_state >> 4) & 0x0f 78 | // Button keys selected 79 | } else if self.joyp & 0x20 == 0 { 80 | (self.joyp & 0xf0) | self.key_state & 0x0f 81 | } else { 82 | self.joyp 83 | } 84 | } 85 | _ => unreachable!("Unexpected address: 0x{:04x}", addr), 86 | } 87 | } 88 | 89 | fn update(&mut self, _tick: u8) {} 90 | } 91 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | extern crate sdl2; 8 | 9 | use std::thread; 10 | use std::time; 11 | 12 | use sdl2::event::Event; 13 | use sdl2::keyboard::Keycode; 14 | use sdl2::pixels::PixelFormatEnum; 15 | 16 | mod catridge; 17 | mod cpu; 18 | mod io_device; 19 | mod joypad; 20 | mod mmu; 21 | mod ppu; 22 | mod timer; 23 | 24 | /// Translates keycode to `joypad::Key` enum. 25 | fn translate_keycode(key: Keycode) -> Option { 26 | match key { 27 | Keycode::Down => Some(joypad::Key::Down), 28 | Keycode::Up => Some(joypad::Key::Up), 29 | Keycode::Left => Some(joypad::Key::Left), 30 | Keycode::Right => Some(joypad::Key::Right), 31 | Keycode::Return => Some(joypad::Key::Start), 32 | Keycode::RShift => Some(joypad::Key::Select), 33 | Keycode::X => Some(joypad::Key::A), 34 | Keycode::Z => Some(joypad::Key::B), 35 | _ => None, 36 | } 37 | } 38 | 39 | /// Handles key down event. 40 | fn handle_keydown(cpu: &mut cpu::CPU, key: Keycode) { 41 | translate_keycode(key).map(|k| cpu.mmu.joypad.keydown(k)); 42 | } 43 | 44 | /// Handles key up event. 45 | fn handle_keyup(cpu: &mut cpu::CPU, key: Keycode) { 46 | translate_keycode(key).map(|k| cpu.mmu.joypad.keyup(k)); 47 | } 48 | 49 | /// Returns ROM filename. 50 | fn rom_fname() -> String { 51 | env::args().nth(1).unwrap() 52 | } 53 | 54 | /// Returns save filename for current ROM. 55 | fn save_fname() -> String { 56 | let mut path_buf = PathBuf::from(rom_fname()); 57 | path_buf.set_extension("sav"); 58 | path_buf.to_str().unwrap().to_string() 59 | } 60 | 61 | fn main() { 62 | env_logger::init(); 63 | 64 | let sdl_context = sdl2::init().unwrap(); 65 | let video_subsystem = sdl_context.video().unwrap(); 66 | 67 | let window = video_subsystem 68 | .window("gbr", 320, 288) 69 | .position_centered() 70 | .build() 71 | .unwrap(); 72 | 73 | let mut canvas = window.into_canvas().build().unwrap(); 74 | 75 | let texture_creator = canvas.texture_creator(); 76 | 77 | let mut texture = texture_creator 78 | .create_texture_streaming(PixelFormatEnum::RGB24, 160, 144) 79 | .unwrap(); 80 | let mut event_pump = sdl_context.event_pump().unwrap(); 81 | 82 | let mut cpu = cpu::CPU::new(&rom_fname()); 83 | 84 | cpu.mmu.catridge.read_save_file(&save_fname()); 85 | 86 | 'running: loop { 87 | let now = time::Instant::now(); 88 | let mut elapsed_tick: u32 = 0; 89 | 90 | // Emulate one frame 91 | while elapsed_tick < 456 * (144 + 10) { 92 | elapsed_tick += cpu.step() as u32; 93 | } 94 | 95 | texture 96 | .with_lock(None, |buf: &mut [u8], pitch: usize| { 97 | let fb = cpu.mmu.ppu.frame_buffer(); 98 | 99 | for y in 0..144 { 100 | for x in 0..160 { 101 | let offset = y * pitch + x * 3; 102 | let color = fb[y * 160 + x]; 103 | 104 | buf[offset] = color; 105 | buf[offset + 1] = color; 106 | buf[offset + 2] = color; 107 | } 108 | } 109 | }) 110 | .unwrap(); 111 | 112 | canvas.clear(); 113 | canvas.copy(&texture, None, None).unwrap(); 114 | canvas.present(); 115 | 116 | for event in event_pump.poll_iter() { 117 | match event { 118 | Event::Quit { .. } 119 | | Event::KeyDown { 120 | keycode: Some(Keycode::Escape), 121 | .. 122 | } => break 'running, 123 | Event::KeyDown { 124 | keycode: Some(keycode), 125 | .. 126 | } => handle_keydown(&mut cpu, keycode), 127 | Event::KeyUp { 128 | keycode: Some(keycode), 129 | .. 130 | } => handle_keyup(&mut cpu, keycode), 131 | _ => (), 132 | } 133 | } 134 | 135 | let wait = time::Duration::from_micros(1000000 / 60); 136 | let elapsed = now.elapsed(); 137 | 138 | if wait > elapsed { 139 | thread::sleep(wait - elapsed); 140 | } 141 | } 142 | 143 | cpu.mmu.catridge.write_save_file(&save_fname()); 144 | } 145 | -------------------------------------------------------------------------------- /src/mmu.rs: -------------------------------------------------------------------------------- 1 | use catridge::Catridge; 2 | use io_device::IODevice; 3 | use joypad::Joypad; 4 | use ppu::PPU; 5 | use timer::Timer; 6 | 7 | /// Memory space. 8 | pub struct MMU { 9 | /// Catridge 10 | pub catridge: Catridge, 11 | /// RAM 12 | ram: [u8; 0x2000], 13 | /// High RAM 14 | hram: [u8; 0x7f], 15 | /// Joypad 16 | pub joypad: Joypad, 17 | /// Timer 18 | timer: Timer, 19 | // TODO should this be public? 20 | /// Pixel Processing Unit 21 | pub ppu: PPU, 22 | /// Interrupt flag 23 | pub int_flag: u8, 24 | /// Interrupt enable 25 | pub int_enable: u8, 26 | } 27 | 28 | impl MMU { 29 | /// Creates a new `MMU`. 30 | pub fn new(rom_name: &str) -> Self { 31 | MMU { 32 | catridge: Catridge::new(rom_name), 33 | ram: [0; 0x2000], 34 | hram: [0; 0x7f], 35 | joypad: Joypad::new(), 36 | ppu: PPU::new(), 37 | timer: Timer::new(), 38 | int_flag: 0, 39 | int_enable: 0, 40 | } 41 | } 42 | 43 | /// Starts a DMA transfer. 44 | // TODO OAM DMA Timing 45 | fn do_dma(&mut self, val: u8) { 46 | if val < 0x80 || 0xdf < val { 47 | panic!("Invalid DMA source address") 48 | } 49 | 50 | let src_base = (val as u16) << 8; 51 | let dst_base = 0xfe00; 52 | 53 | for i in 0..0xa0 { 54 | let tmp = self.read(src_base | i); 55 | self.write(dst_base | i, tmp); 56 | } 57 | } 58 | 59 | /// Writes a byte to an address. 60 | pub fn write(&mut self, addr: u16, val: u8) { 61 | match addr { 62 | // ROM 63 | 0x0000..=0x7fff => self.catridge.write(addr, val), 64 | // VRAM 65 | 0x8000..=0x9fff => self.ppu.write(addr, val), 66 | // External RAM 67 | 0xa000..=0xbfff => self.catridge.write(addr, val), 68 | // RAM 69 | 0xc000..=0xdfff => self.ram[(addr & 0x1fff) as usize] = val, 70 | // Echo RAM 71 | 0xe000..=0xfdff => self.ram[((addr - 0x2000) & 0x1fff) as usize] = val, 72 | // OAM 73 | 0xfe00..=0xfe9f => self.ppu.write(addr, val), 74 | // Joypad 75 | 0xff00 => self.joypad.write(addr, val), 76 | // Timer 77 | 0xff04..=0xff07 => self.timer.write(addr, val), 78 | // Interrupt flag 79 | 0xff0f => self.int_flag = val, 80 | // PPU 81 | 0xff40..=0xff45 | 0xff47..=0xff4b => self.ppu.write(addr, val), 82 | // OAM DMA 83 | 0xff46 => self.do_dma(val), 84 | // HRAM 85 | 0xff80..=0xfffe => self.hram[(addr & 0x7f) as usize] = val, 86 | // Interrupt enable 87 | 0xffff => self.int_enable = val, 88 | _ => (), 89 | } 90 | } 91 | 92 | /// Reads a byte from an address. 93 | pub fn read(&self, addr: u16) -> u8 { 94 | match addr { 95 | // ROM 96 | 0x0000..=0x7fff => self.catridge.read(addr), 97 | // VRAM 98 | 0x8000..=0x9fff => self.ppu.read(addr), 99 | // External RAM 100 | 0xa000..=0xbfff => self.catridge.read(addr), 101 | // RAM 102 | 0xc000..=0xdfff => self.ram[(addr & 0x1fff) as usize], 103 | // Echo RAM 104 | 0xe000..=0xfdff => self.ram[((addr - 0x2000) & 0x1fff) as usize], 105 | // OAM 106 | 0xfe00..=0xfe9f => self.ppu.read(addr), 107 | // Joypad 108 | 0xff00 => self.joypad.read(addr), 109 | // Timer 110 | 0xff04..=0xff07 => self.timer.read(addr), 111 | // Interrupt flag 112 | 0xff0f => self.int_flag, 113 | // PPU 114 | 0xff40..=0xff45 | 0xff47..=0xff4b => self.ppu.read(addr), 115 | // HRAM 116 | 0xff80..=0xfffe => self.hram[(addr & 0x7f) as usize], 117 | // Interrupt enable 118 | 0xffff => self.int_enable, 119 | _ => 0xff, 120 | } 121 | } 122 | 123 | /// Progresses the clock for a given number of ticks. 124 | pub fn update(&mut self, tick: u8) { 125 | self.catridge.update(tick); 126 | self.ppu.update(tick); 127 | self.timer.update(tick); 128 | self.joypad.update(tick); 129 | 130 | if self.ppu.irq_vblank { 131 | self.int_flag |= 0x1; 132 | self.ppu.irq_vblank = false; 133 | } 134 | 135 | if self.ppu.irq_lcdc { 136 | self.int_flag |= 0x2; 137 | self.ppu.irq_lcdc = false; 138 | } 139 | 140 | if self.timer.irq { 141 | self.int_flag |= 0x4; 142 | self.timer.irq = false; 143 | } 144 | 145 | if self.joypad.irq { 146 | self.int_flag |= 0x10; 147 | self.joypad.irq = false; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/ppu.rs: -------------------------------------------------------------------------------- 1 | use io_device::IODevice; 2 | 3 | /// Width of screen in pixels. 4 | const SCREEN_W: u8 = 160; 5 | /// Height of screen in pixels. 6 | const SCREEN_H: u8 = 144; 7 | 8 | #[derive(Copy, Clone, PartialEq)] 9 | enum BGPriority { 10 | Color0, 11 | Color123, 12 | } 13 | 14 | /// Pixel Processing Unit. 15 | pub struct PPU { 16 | /// VRAM 17 | vram: [u8; 0x2000], 18 | /// OAM 19 | oam: [u8; 0xa0], 20 | /// LCD Control 21 | lcdc: u8, 22 | /// Status 23 | stat: u8, 24 | /// Scroll Y 25 | scy: u8, 26 | /// Scroll X 27 | scx: u8, 28 | /// Y-Coordinate 29 | ly: u8, 30 | /// LY Compare 31 | lyc: u8, 32 | /// DMA Transfer and Start Address 33 | dma: u8, 34 | /// Background Palette Data 35 | bgp: u8, 36 | /// Object Palette 0 Data 37 | obp0: u8, 38 | /// Object Palette 1 Data 39 | obp1: u8, 40 | /// Window Y Position 41 | wy: u8, 42 | /// Window X Position minus 7 43 | wx: u8, 44 | /// V-Blank interrupt request 45 | pub irq_vblank: bool, 46 | /// LCDC interrupt request 47 | pub irq_lcdc: bool, 48 | /// Elapsed clocks in current mode 49 | counter: u16, 50 | /// Frame buffer 51 | frame_buffer: [u8; (SCREEN_W as usize) * (SCREEN_H as usize)], 52 | /// Current scanline 53 | scanline: [u8; SCREEN_W as usize], 54 | /// Background priority 55 | bg_prio: [BGPriority; SCREEN_W as usize], 56 | } 57 | 58 | impl PPU { 59 | // VRAM map 60 | // 0x0000-0x07ff: Tile set #1 61 | // 0x0800-0x0fff: Tile set #2 62 | // 0x1000-0x17ff: Tile set #3 63 | // 0x1800-0x1bff: Tile map #1 64 | // 0x1c00-0x1fff: Tile map #2 65 | 66 | /// Creates a new `PPU` 67 | pub fn new() -> Self { 68 | PPU { 69 | vram: [0; 0x2000], 70 | oam: [0; 0xa0], 71 | lcdc: 0x80, 72 | stat: 0x02, 73 | scy: 0, 74 | scx: 0, 75 | ly: 0, 76 | lyc: 0, 77 | dma: 0, 78 | bgp: 0, 79 | obp0: 0, 80 | obp1: 0, 81 | wy: 0, 82 | wx: 0, 83 | irq_vblank: false, 84 | irq_lcdc: false, 85 | counter: 0, 86 | scanline: [0; SCREEN_W as usize], 87 | frame_buffer: [0; (SCREEN_W as usize) * (SCREEN_H as usize)], 88 | bg_prio: [BGPriority::Color0; SCREEN_W as usize], 89 | } 90 | } 91 | 92 | /// Fetches tile data from VRAM. 93 | fn fetch_tile(&self, tile_no: u8, offset_y: u8, tile_data_sel: bool) -> (u8, u8) { 94 | // Fetch tile data from tile set 95 | let tile_data_addr = if tile_data_sel { 96 | // Use tile set #1 (0x0000-0x07ff) and #2 (0x0800-0x0fff) 97 | (tile_no as u16) << 4 98 | } else { 99 | // Use tile set #2 (0x0800-0x0fff) and #3 (0x1000-0x17ff) 100 | (0x1000 as u16).wrapping_add(((tile_no as i8 as i16) << 4) as u16) 101 | }; 102 | let row_addr = tile_data_addr + (offset_y << 1) as u16; 103 | 104 | let tile0 = self.vram[row_addr as usize]; 105 | let tile1 = self.vram[(row_addr + 1) as usize]; 106 | 107 | (tile0, tile1) 108 | } 109 | 110 | /// Fetches BG or Window tile data from VRAM. 111 | fn fetch_bg_window_tile( 112 | &self, 113 | tile_x: u8, 114 | tile_y: u8, 115 | offset_y: u8, 116 | tile_map_base: u16, 117 | ) -> (u8, u8) { 118 | // Fetch tile index from tile map 119 | let tile_map_addr = tile_map_base | ((tile_x & 0x1f) as u16 + ((tile_y as u16) << 5)); 120 | let tile_no = self.vram[tile_map_addr as usize]; 121 | 122 | self.fetch_tile(tile_no, offset_y, self.lcdc & 0x10 > 0) 123 | } 124 | 125 | /// Fetches BG tile data from VRAM. 126 | fn fetch_bg_tile(&self, tile_x: u8, tile_y: u8, offset_y: u8) -> (u8, u8) { 127 | // Fetch tile index from tile map 128 | let tile_map_base = if self.lcdc & 0x8 > 0 { 0x1c00 } else { 0x1800 }; 129 | 130 | self.fetch_bg_window_tile(tile_x, tile_y, offset_y, tile_map_base) 131 | } 132 | 133 | /// Fetches Window tile data from VRAM. 134 | fn fetch_window_tile(&self, tile_x: u8, tile_y: u8, offset_y: u8) -> (u8, u8) { 135 | // Fetch tile index from tile map 136 | let tile_map_base = if self.lcdc & 0x40 > 0 { 0x1c00 } else { 0x1800 }; 137 | 138 | self.fetch_bg_window_tile(tile_x, tile_y, offset_y, tile_map_base) 139 | } 140 | 141 | /// Converts color number to brightness using palette. 142 | fn map_color(&self, color_no: u8, palette: u8) -> u8 { 143 | match (palette >> (color_no << 1)) & 0x3 { 144 | 0 => 0xff, 145 | 1 => 0xaa, 146 | 2 => 0x55, 147 | 3 | _ => 0x00, 148 | } 149 | } 150 | 151 | /// Returns the color number at a given position from tile data. 152 | fn get_color_no(&self, tile: (u8, u8), bitpos: u8) -> u8 { 153 | let lo_bit = tile.0 >> bitpos & 1; 154 | let hi_bit = tile.1 >> bitpos & 1; 155 | 156 | hi_bit << 1 | lo_bit 157 | } 158 | 159 | /// Renders BG. 160 | fn render_bg(&mut self) { 161 | // Tile coordinate 162 | let mut tile_x = self.scx >> 3; 163 | let mut tile_y = self.scy.wrapping_add(self.ly) >> 3; 164 | 165 | // Offset of current pixel within tile 166 | let mut offset_x = self.scx & 0x7; 167 | let mut offset_y = self.scy.wrapping_add(self.ly) & 0x7; 168 | 169 | let mut tile = self.fetch_bg_tile(tile_x, tile_y, offset_y); 170 | 171 | let mut window = false; 172 | 173 | for x in 0..SCREEN_W { 174 | // Check if window is enabled 175 | if self.lcdc & 0x20 > 0 { 176 | if self.wy <= self.ly && self.wx == x + 7 { 177 | tile_x = 0; 178 | tile_y = (self.ly - self.wy) >> 3; 179 | offset_x = 0; 180 | offset_y = (self.ly - self.wy) & 0x7; 181 | tile = self.fetch_window_tile(tile_x, tile_y, offset_y); 182 | window = true; 183 | } 184 | } 185 | 186 | let color_no = self.get_color_no(tile, 7 - offset_x); 187 | let color = self.map_color(color_no, self.bgp); 188 | 189 | self.bg_prio[x as usize] = if color_no == 0 { 190 | BGPriority::Color0 191 | } else { 192 | BGPriority::Color123 193 | }; 194 | 195 | self.scanline[x as usize] = color; 196 | 197 | offset_x += 1; 198 | 199 | // Move on to next tile 200 | if offset_x >= 8 { 201 | offset_x = 0; 202 | tile_x += 1; 203 | 204 | if window { 205 | tile = self.fetch_window_tile(tile_x, tile_y, offset_y); 206 | } else { 207 | tile = self.fetch_bg_tile(tile_x, tile_y, offset_y); 208 | } 209 | } 210 | } 211 | } 212 | 213 | /// Renders sprites. 214 | fn render_sprites(&mut self) { 215 | let mut n_sprites = 0; 216 | let height = if self.lcdc & 0x4 > 0 { 16 } else { 8 }; 217 | 218 | for i in 0..40 { 219 | // Parse OAM entry 220 | let entry_addr = i << 2; 221 | let sprite_y = self.oam[entry_addr]; 222 | let sprite_x = self.oam[entry_addr + 1]; 223 | let flags = self.oam[entry_addr + 3]; 224 | 225 | let obj_prio = flags & 0x80 > 0; 226 | let flip_y = flags & 0x40 > 0; 227 | let flip_x = flags & 0x20 > 0; 228 | let palette = if flags & 0x10 > 0 { 229 | self.obp1 230 | } else { 231 | self.obp0 232 | }; 233 | 234 | // Check if sprite is visible on this scanline 235 | if sprite_y <= self.ly + 16 - height || sprite_y > self.ly + 16 { 236 | continue; 237 | } 238 | 239 | // Up to 10 sprites can be rendered on one scanline 240 | n_sprites += 1; 241 | if n_sprites > 10 { 242 | break; 243 | } 244 | 245 | // Check if sprite is within the screen 246 | if sprite_x == 0 || sprite_x > SCREEN_W + 8 - 1 { 247 | continue; 248 | } 249 | 250 | // Tile number 251 | let tile_no = if self.lcdc & 0x4 > 0 { 252 | // 8x16 sprite 253 | if (self.ly + 8 < sprite_y) ^ flip_y { 254 | self.oam[entry_addr + 2] & 0xfe 255 | } else { 256 | self.oam[entry_addr + 2] | 0x01 257 | } 258 | } else { 259 | // 8x8 sprite 260 | self.oam[entry_addr + 2] 261 | }; 262 | 263 | // Y-offset within the tile 264 | let offset_y = if flip_y { 265 | 7 - ((self.ly + 16 - sprite_y) & 0x7) 266 | } else { 267 | (self.ly + 16 - sprite_y) & 0x7 268 | }; 269 | 270 | // Fetch tile data 271 | let tile = self.fetch_tile(tile_no, offset_y, true); 272 | 273 | for offset_x in 0..8 { 274 | if offset_x + sprite_x < 8 { 275 | continue; 276 | } 277 | 278 | let x = offset_x + sprite_x - 8; 279 | 280 | if x >= SCREEN_W { 281 | break; 282 | } 283 | 284 | let bitpos = if flip_x { offset_x } else { 7 - offset_x }; 285 | let color_no = self.get_color_no(tile, bitpos); 286 | if color_no == 0 { 287 | continue; 288 | } 289 | if self.bg_prio[x as usize] == BGPriority::Color123 && obj_prio { 290 | continue; 291 | } 292 | let color = self.map_color(color_no, palette); 293 | 294 | self.scanline[x as usize] = color; 295 | } 296 | } 297 | } 298 | 299 | /// Renders a scanline. 300 | fn render_scanline(&mut self) { 301 | if self.lcdc & 0x1 > 0 { 302 | self.render_bg(); 303 | } 304 | if self.lcdc & 0x2 > 0 { 305 | self.render_sprites(); 306 | } 307 | 308 | for x in 0..SCREEN_W { 309 | let ix = (x as usize) + (self.ly as usize) * (SCREEN_W as usize); 310 | self.frame_buffer[ix] = self.scanline[x as usize]; 311 | } 312 | } 313 | 314 | /// Returns the current contents of the frame buffer. 315 | pub fn frame_buffer(&self) -> &[u8] { 316 | &self.frame_buffer 317 | } 318 | 319 | /// Checks LYC interrupt. 320 | fn update_lyc_interrupt(&mut self) { 321 | // LYC=LY coincidence interrupt 322 | if self.ly == self.lyc { 323 | self.stat |= 0x4; 324 | 325 | if self.stat & 0x40 > 0 { 326 | self.irq_lcdc = true; 327 | } 328 | } else { 329 | self.stat &= !0x4; 330 | } 331 | } 332 | 333 | /// Checks LCD mode interrupt. 334 | fn update_mode_interrupt(&mut self) { 335 | // Mode interrupts 336 | match self.stat & 0x3 { 337 | // H-Blank interrupt 338 | 0 if self.stat & 0x8 > 0 => self.irq_lcdc = true, 339 | // V-Blank interrupt 340 | 1 if self.stat & 0x10 > 0 => self.irq_lcdc = true, 341 | // OAM Search interrupt 342 | 2 if self.stat & 0x20 > 0 => self.irq_lcdc = true, 343 | _ => (), 344 | } 345 | } 346 | } 347 | 348 | impl IODevice for PPU { 349 | fn write(&mut self, addr: u16, val: u8) { 350 | match addr { 351 | // VRAM 352 | 0x8000..=0x9fff => { 353 | // VRAM is inaccessible during pixel transfer 354 | if self.stat & 0x3 != 3 { 355 | self.vram[(addr & 0x1fff) as usize] = val 356 | } 357 | } 358 | 359 | // OAM 360 | 0xfe00..=0xfe9f => { 361 | // OAM is only accessible during H-Blank and V-Blank 362 | if self.stat & 0x3 == 0 || self.stat & 0x3 == 1 { 363 | self.oam[(addr & 0x00ff) as usize] = val; 364 | } 365 | } 366 | 367 | // IO registers 368 | 0xff40 => { 369 | if self.lcdc & 0x80 != val & 0x80 { 370 | self.ly = 0; 371 | self.counter = 0; 372 | 373 | let mode = if val & 0x80 > 0 { 2 } else { 0 }; 374 | self.stat = (self.stat & 0xf8) | mode; 375 | self.update_mode_interrupt(); 376 | } 377 | 378 | self.lcdc = val; 379 | } 380 | 0xff41 => self.stat = (val & 0xf8) | (self.stat & 0x3), 381 | 0xff42 => self.scy = val, 382 | 0xff43 => self.scx = val, 383 | 0xff44 => (), 384 | 0xff45 => { 385 | if self.lyc != val { 386 | self.lyc = val; 387 | self.update_lyc_interrupt(); 388 | } 389 | } 390 | 0xff47 => self.bgp = val, 391 | 0xff48 => self.obp0 = val, 392 | 0xff49 => self.obp1 = val, 393 | 0xff4a => self.wy = val, 394 | 0xff4b => self.wx = val, 395 | 396 | _ => unreachable!("Unexpected address: 0x{:04x}", addr), 397 | } 398 | } 399 | 400 | fn read(&self, addr: u16) -> u8 { 401 | match addr { 402 | // VRAM 403 | 0x8000..=0x9fff => { 404 | // VRAM is inaccessible during pixel transfer 405 | if self.stat & 0x3 != 3 { 406 | self.vram[(addr & 0x1fff) as usize] 407 | } else { 408 | 0xff 409 | } 410 | } 411 | 412 | // OAM 413 | 0xfe00..=0xfe9f => { 414 | // OAM is only accessible during H-Blank and V-Blank 415 | if self.stat & 0x3 == 0 || self.stat & 0x3 == 1 { 416 | self.oam[(addr & 0x00ff) as usize] 417 | } else { 418 | 0xff 419 | } 420 | } 421 | 422 | // IO registers 423 | 0xff40 => self.lcdc, 424 | 0xff41 => self.stat, 425 | 0xff42 => self.scy, 426 | 0xff43 => self.scx, 427 | 0xff44 => self.ly, 428 | 0xff45 => self.lyc, 429 | 0xff46 => self.dma, 430 | 0xff47 => self.bgp, 431 | 0xff48 => self.obp0, 432 | 0xff49 => self.obp1, 433 | 0xff4a => self.wy, 434 | 0xff4b => self.wx, 435 | 436 | _ => unreachable!("Unexpected address: 0x{:04x}", addr), 437 | } 438 | } 439 | 440 | fn update(&mut self, tick: u8) { 441 | if self.lcdc & 0x80 == 0 { 442 | return; 443 | } 444 | 445 | self.counter += tick as u16; 446 | 447 | match self.stat & 0x3 { 448 | // OAM Search (80 clocks) 449 | 2 => { 450 | if self.counter >= 80 { 451 | self.counter -= 80; 452 | // Transition to Pixel Transfer mode 453 | self.stat = (self.stat & 0xf8) | 3; 454 | self.render_scanline(); 455 | } 456 | } 457 | // Pixel Transfer (172 clocks) 458 | 3 => { 459 | if self.counter >= 172 { 460 | self.counter -= 172; 461 | // Transition to H-Blank mode 462 | self.stat = self.stat & 0xf8; 463 | self.update_mode_interrupt(); 464 | } 465 | } 466 | // H-Blank (204 clocks) 467 | 0 => { 468 | if self.counter >= 204 { 469 | self.counter -= 204; 470 | self.ly += 1; 471 | 472 | if self.ly >= SCREEN_H { 473 | // Transition to V-Blank mode 474 | self.stat = (self.stat & 0xf8) | 1; 475 | self.irq_vblank = true; 476 | } else { 477 | // Transition to OAM Search mode 478 | self.stat = (self.stat & 0xf8) | 2; 479 | } 480 | 481 | self.update_lyc_interrupt(); 482 | self.update_mode_interrupt(); 483 | } 484 | } 485 | // V-Blank (4560 clocks or 10 lines) 486 | 1 | _ => { 487 | if self.counter >= 456 { 488 | self.counter -= 456; 489 | self.ly += 1; 490 | 491 | if self.ly >= 154 { 492 | // Transition to OAM Search mode 493 | self.stat = (self.stat & 0xf8) | 2; 494 | self.ly = 0; 495 | 496 | self.update_mode_interrupt(); 497 | } 498 | 499 | self.update_lyc_interrupt(); 500 | } 501 | } 502 | } 503 | } 504 | } 505 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | use io_device::IODevice; 2 | 3 | pub struct Timer { 4 | /// Timer counter 5 | tima: u8, 6 | /// Timer modulo 7 | tma: u8, 8 | /// Timer control 9 | tac: u8, 10 | /// Internal 16-bit counter 11 | counter: u16, 12 | /// Interrupt request 13 | pub irq: bool, 14 | } 15 | 16 | impl Timer { 17 | /// Creates a new `Timer`. 18 | pub fn new() -> Self { 19 | Timer { 20 | tima: 0, 21 | tma: 0, 22 | tac: 0, 23 | counter: 0, 24 | irq: false, 25 | } 26 | } 27 | } 28 | 29 | impl IODevice for Timer { 30 | fn write(&mut self, addr: u16, val: u8) { 31 | match addr { 32 | // DIV 33 | 0xff04 => self.counter = 0, 34 | // TIMA 35 | 0xff05 => self.tima = val, 36 | // TMA 37 | 0xff06 => self.tma = val, 38 | // TAC 39 | 0xff07 => self.tac = val & 0x7, 40 | _ => unreachable!("Unexpected address: 0x{:04x}", addr), 41 | } 42 | } 43 | 44 | fn read(&self, addr: u16) -> u8 { 45 | match addr { 46 | // DIV 47 | 0xff04 => (self.counter >> 8) as u8, 48 | // TIMA 49 | 0xff05 => self.tima, 50 | // TMA 51 | 0xff06 => self.tma, 52 | // TAC 53 | 0xff07 => self.tac, 54 | _ => unreachable!("Unexpected address: 0x{:04x}", addr), 55 | } 56 | } 57 | 58 | fn update(&mut self, tick: u8) { 59 | let counter_prev = self.counter; 60 | 61 | self.counter = self.counter.wrapping_add(tick as u16); 62 | 63 | if self.tac & 4 > 0 { 64 | let divider = match self.tac & 3 { 65 | 0 => 10, 66 | 1 => 4, 67 | 2 => 6, 68 | 3 | _ => 8, 69 | }; 70 | 71 | let x = self.counter >> divider; 72 | let y = counter_prev >> divider; 73 | let mask = (1 << (16 - divider)) - 1; 74 | let diff = x.wrapping_sub(y) & mask; 75 | 76 | if diff > 0 { 77 | let (res, overflow) = self.tima.overflowing_add(diff as u8); 78 | 79 | if overflow { 80 | self.tima = self.tma + (diff as u8 - 1); 81 | self.irq = true; 82 | } else { 83 | self.tima = res; 84 | } 85 | } 86 | } 87 | } 88 | } 89 | --------------------------------------------------------------------------------