├── rustfmt.toml ├── .gitignore ├── res └── space-invaders.gif ├── Cargo.toml ├── src ├── lib.rs ├── bit.rs ├── memory.rs ├── register.rs ├── asm.rs └── cpu.rs ├── scripts └── get_cpu_tests.py ├── examples └── test_roms.rs ├── README.md └── tests └── test_instr.rs /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 120 2 | use_small_heuristics = "Max" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | 5 | /res/cpu_tests 6 | -------------------------------------------------------------------------------- /res/space-invaders.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohanson/i8080/HEAD/res/space-invaders.gif -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "i8080" 3 | version = "1.0.0" 4 | authors = ["mohanson "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | rog = "0.1" 9 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod asm; 2 | pub mod bit; 3 | mod cpu; 4 | mod memory; 5 | mod register; 6 | 7 | pub use cpu::Cpu; 8 | pub use memory::{Linear, Memory}; 9 | pub use register::Flag; 10 | -------------------------------------------------------------------------------- /src/bit.rs: -------------------------------------------------------------------------------- 1 | pub fn get(n: u8, b: usize) -> bool { 2 | (n & (1 << b)) != 0 3 | } 4 | 5 | pub fn set(n: u8, b: usize) -> u8 { 6 | n | (1 << b) 7 | } 8 | 9 | pub fn clr(n: u8, b: usize) -> u8 { 10 | n & !(1 << b) 11 | } 12 | -------------------------------------------------------------------------------- /src/memory.rs: -------------------------------------------------------------------------------- 1 | pub trait Memory { 2 | fn get(&self, a: u16) -> u8; 3 | 4 | fn set(&mut self, a: u16, v: u8); 5 | 6 | fn get_word(&self, a: u16) -> u16 { 7 | u16::from(self.get(a)) | (u16::from(self.get(a + 1)) << 8) 8 | } 9 | 10 | fn set_word(&mut self, a: u16, v: u16) { 11 | self.set(a, (v & 0xFF) as u8); 12 | self.set(a + 1, (v >> 8) as u8) 13 | } 14 | } 15 | 16 | #[derive(Default)] 17 | pub struct Linear { 18 | pub data: Vec, 19 | } 20 | 21 | impl Memory for Linear { 22 | fn get(&self, a: u16) -> u8 { 23 | self.data[usize::from(a)] 24 | } 25 | 26 | fn set(&mut self, a: u16, v: u8) { 27 | self.data[usize::from(a)] = v 28 | } 29 | } 30 | 31 | impl Linear { 32 | pub fn new() -> Self { 33 | Self { data: vec![0; 65536] } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /scripts/get_cpu_tests.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import os.path 3 | 4 | import requests 5 | 6 | prefix = 'http://altairclone.com/downloads/cpu_tests/' 7 | suffix = [ 8 | '+README.TXT', 9 | '8080EXER.COM', 10 | '8080EXER.MAC', 11 | '8080EXER.PNG', 12 | '8080EXER.PRN', 13 | '8080EXM.COM', 14 | '8080EXM.MAC', 15 | '8080EXM.PRN', 16 | '8080PRE.COM', 17 | '8080PRE.MAC', 18 | '8080PRE.PRN', 19 | '8080_8085 CPU Exerciser.pdf', 20 | 'CPUTEST.COM', 21 | 'TST8080.ASM', 22 | 'TST8080.COM', 23 | 'TST8080.PRN', 24 | ] 25 | saveto = './res/cpu_tests' 26 | 27 | 28 | def wget(url: str, dst: str): 29 | resp = requests.get(url, stream=True) 30 | with open(dst, 'wb') as f: 31 | with contextlib.closing(resp) as r: 32 | for data in r.iter_content(chunk_size=8 * 1024): 33 | f.write(data) 34 | 35 | 36 | def main(): 37 | if not os.path.exists(saveto): 38 | print(f'Make dir {saveto}') 39 | os.makedirs(saveto, 0o755) 40 | for e in suffix: 41 | src = prefix + e 42 | dst = os.path.join(saveto, e) 43 | print(f'Get {src}') 44 | wget(src, dst) 45 | 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /examples/test_roms.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::fs::File; 3 | use std::io::Read; 4 | use std::path::Path; 5 | use std::rc::Rc; 6 | 7 | use i8080::{Cpu, Linear, Memory}; 8 | 9 | fn load_test(mem: Rc>, path: impl AsRef) { 10 | let mut file = File::open(path.as_ref()).unwrap(); 11 | let mut buf = Vec::new(); 12 | file.read_to_end(&mut buf).unwrap(); 13 | mem.borrow_mut().data[0x0100..(buf.len() + 0x0100)].clone_from_slice(&buf[..]); 14 | println!("Test loaded: {:?}", path.as_ref()); 15 | } 16 | 17 | fn exec_test(path: impl AsRef) { 18 | println!("*******************"); 19 | let mem = Rc::new(RefCell::new(Linear::new())); 20 | load_test(mem.clone(), path); 21 | let mut cpu = Cpu::power_up(mem.clone()); 22 | mem.borrow_mut().set(0x0005, 0xc9); 23 | // Because tests used the pseudo instruction ORG 0x0100 24 | cpu.reg.pc = 0x0100; 25 | loop { 26 | if cpu.halted { 27 | break; 28 | } 29 | cpu.next(); 30 | if cpu.reg.pc == 0x05 { 31 | if cpu.reg.c == 0x09 { 32 | let mut a = cpu.reg.get_de(); 33 | loop { 34 | let c = mem.borrow().get(a); 35 | if c as char == '$' { 36 | break; 37 | } else { 38 | a += 1; 39 | } 40 | print!("{}", c as char); 41 | } 42 | } 43 | if cpu.reg.c == 0x02 { 44 | print!("{}", cpu.reg.e as char); 45 | } 46 | } 47 | if cpu.reg.pc == 0x00 { 48 | println!(""); 49 | println!(""); 50 | break; 51 | } 52 | } 53 | } 54 | 55 | fn main() { 56 | exec_test("./res/cpu_tests/8080PRE.COM"); 57 | exec_test("./res/cpu_tests/TST8080.COM"); 58 | exec_test("./res/cpu_tests/CPUTEST.COM"); 59 | exec_test("./res/cpu_tests/8080EXM.COM"); 60 | } 61 | -------------------------------------------------------------------------------- /src/register.rs: -------------------------------------------------------------------------------- 1 | use super::bit; 2 | 3 | // ------------- 4 | // | A Flags | ---> Program Status Word 5 | // | B C | ---> B 6 | // | D E | ---> D 7 | // | H L | ---> H 8 | // | SP | ---> Stack Pointer 9 | // | PC | ---> Program Counter 10 | // ------------- 11 | #[derive(Default)] 12 | pub struct Register { 13 | pub a: u8, 14 | pub f: u8, // The F register is indirectly accessible by the programer. 15 | pub b: u8, 16 | pub c: u8, 17 | pub d: u8, 18 | pub e: u8, 19 | pub h: u8, 20 | pub l: u8, 21 | pub sp: u16, 22 | pub pc: u16, 23 | } 24 | 25 | // Some instructions, however, allow you to use the registers A,B,C,D,E,H,L as 16-bit registers by pairing them up 26 | // in the following manner: AF,BC,DE,HL. 27 | impl Register { 28 | pub fn get_af(&self) -> u16 { 29 | (u16::from(self.a) << 8) | u16::from(self.f) 30 | } 31 | 32 | pub fn get_bc(&self) -> u16 { 33 | (u16::from(self.b) << 8) | u16::from(self.c) 34 | } 35 | 36 | pub fn get_de(&self) -> u16 { 37 | (u16::from(self.d) << 8) | u16::from(self.e) 38 | } 39 | 40 | pub fn get_hl(&self) -> u16 { 41 | (u16::from(self.h) << 8) | u16::from(self.l) 42 | } 43 | 44 | pub fn set_af(&mut self, v: u16) { 45 | self.a = (v >> 8) as u8; 46 | self.f = (v & 0x00d5 | 0x0002) as u8; 47 | } 48 | 49 | pub fn set_bc(&mut self, v: u16) { 50 | self.b = (v >> 8) as u8; 51 | self.c = (v & 0x00ff) as u8; 52 | } 53 | 54 | pub fn set_de(&mut self, v: u16) { 55 | self.d = (v >> 8) as u8; 56 | self.e = (v & 0x00ff) as u8; 57 | } 58 | 59 | pub fn set_hl(&mut self, v: u16) { 60 | self.h = (v >> 8) as u8; 61 | self.l = (v & 0x00ff) as u8; 62 | } 63 | } 64 | 65 | pub enum Flag { 66 | S = 7, // Sign Flag 67 | Z = 6, // Zero Flag 68 | A = 4, // Also called AC, Auxiliary Carry Flag 69 | P = 2, // Parity Flag 70 | C = 0, // Carry Flag 71 | } 72 | 73 | impl Register { 74 | pub fn get_flag(&self, f: Flag) -> bool { 75 | bit::get(self.f, f as usize) 76 | } 77 | 78 | pub fn set_flag(&mut self, f: Flag, v: bool) { 79 | if v { 80 | self.f = bit::set(self.f, f as usize) 81 | } else { 82 | self.f = bit::clr(self.f, f as usize) 83 | } 84 | } 85 | } 86 | 87 | impl Register { 88 | pub fn power_up() -> Self { 89 | Self { f: 0b0000_0010, ..Default::default() } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # i8080 2 | 3 | i8080 is a emulator for Intel 8080 cpu. 4 | 5 | - [8080 Programmers Manual](http://altairclone.com/downloads/manuals/8080%20Programmers%20Manual.pdf) 6 | - [8080 opcodes](http://pastraiser.com/cpu/i8080/i8080_opcodes.html) 7 | 8 | ```toml 9 | [dependencies] 10 | i8080 = { git = "https://github.com/mohanson/i8080" } 11 | ``` 12 | 13 | # Tests 14 | 15 | The test roms (cpu_tests folder) are taken from [http://altairclone.com/downloads/cpu_tests/](http://altairclone.com/downloads/cpu_tests/). 16 | 17 | ```sh 18 | $ python ./scripts/get_cpu_tests.py 19 | $ cargo run --release --example test_roms 20 | ``` 21 | 22 | ```text 23 | ******************* 24 | Test loaded: "./res/cpu_tests/8080PRE.COM" 25 | 8080 Preliminary tests complete 26 | 27 | ******************* 28 | Test loaded: "./res/cpu_tests/TST8080.COM" 29 | MICROCOSM ASSOCIATES 8080/8085 CPU DIAGNOSTIC 30 | VERSION 1.0 (C) 1980 31 | 32 | CPU IS OPERATIONAL 33 | 34 | ******************* 35 | Test loaded: "./res/cpu_tests/CPUTEST.COM" 36 | 37 | DIAGNOSTICS II V1.2 - CPU TEST 38 | COPYRIGHT (C) 1981 - SUPERSOFT ASSOCIATES 39 | 40 | ABCDEFGHIJKLMNOPQRSTUVWXYZ 41 | CPU IS 8080/8085 42 | BEGIN TIMING TEST 43 | END TIMING TEST 44 | CPU TESTS OK 45 | 46 | 47 | ******************* 48 | Test loaded: "./res/cpu_tests/8080EXM.COM" 49 | 8080 instruction exerciser 50 | dad ................ PASS! crc is:14474ba6 51 | aluop nn...................... PASS! crc is:9e922f9e 52 | aluop ....... PASS! crc is:cf762c86 53 | ............. PASS! crc is:bb3f030c 54 | a................... PASS! crc is:adb6460e 55 | b................... PASS! crc is:83ed1345 56 | b................... PASS! crc is:f79287cd 57 | c................... PASS! crc is:e5f6721b 58 | d................... PASS! crc is:15b5579a 59 | d................... PASS! crc is:7f4e2501 60 | e................... PASS! crc is:cf2ab396 61 | h................... PASS! crc is:12b2952c 62 | h................... PASS! crc is:9f2b23c0 63 | l................... PASS! crc is:ff57d356 64 | m................... PASS! crc is:92e963bd 65 | sp.................. PASS! crc is:d5702fab 66 | lhld nnnn..................... PASS! crc is:a9c3d5cb 67 | shld nnnn..................... PASS! crc is:e8864f26 68 | lxi ,nnnn........... PASS! crc is:fcf46e12 69 | ldax .................... PASS! crc is:2b821d5f 70 | mvi ,nn...... PASS! crc is:eaa72044 71 | mov ,....... PASS! crc is:10b58cee 72 | sta nnnn / lda nnnn........... PASS! crc is:ed57af72 73 | ............. PASS! crc is:e0d89235 74 | stax .................... PASS! crc is:2b0471e9 75 | Tests complete 76 | ``` 77 | 78 | # Space-Invaders 79 | 80 | Space Invaders (Japanese: スペースインベーダー Hepburn: Supēsu Inbēdā) is a 1978 arcade game created by Tomohiro Nishikado. It was manufactured and sold by Taito in Japan, and licensed in the United States by the Midway division of Bally. Within the shooter genre, Space Invaders was the first fixed shooter and set the template for the shoot 'em up genre. The goal is to defeat wave after wave of descending aliens with a horizontally moving laser to earn as many points as possible. 81 | 82 | The game uses an **Intel 8080 central processing unit (CPU)**, displays raster graphics on a CRT monitor, and uses monaural sound hosted by a combination of analog circuitry and a Texas Instruments SN76477 sound chip. 83 | 84 | ![img](./res/space-invaders.gif) 85 | 86 | I use a separate repo to implement this game, please goto [https://github.com/mohanson/space-invaders](https://github.com/mohanson/space-invaders) 87 | 88 | # Licences 89 | 90 | MIT 91 | -------------------------------------------------------------------------------- /src/asm.rs: -------------------------------------------------------------------------------- 1 | pub fn asm(opcode: u8) -> &'static str { 2 | match opcode { 3 | 0x00 => "NOP ", 4 | 0x01 => "LXI BC ", 5 | 0x02 => "STAX BC ", 6 | 0x03 => "INX BC ", 7 | 0x04 => "INR B ", 8 | 0x05 => "DCR B ", 9 | 0x06 => "MVI B ", 10 | 0x07 => "RLC ", 11 | 0x08 => "NOP ", 12 | 0x09 => "DAD BC ", 13 | 0x0A => "LDAX BC ", 14 | 0x0B => "DCX BC ", 15 | 0x0C => "INR C ", 16 | 0x0D => "DCR C ", 17 | 0x0E => "MVI C ", 18 | 0x0F => "RRC ", 19 | 20 | 0x10 => "NOP ", 21 | 0x11 => "LXI DE ", 22 | 0x12 => "STAX DE ", 23 | 0x13 => "INX DE ", 24 | 0x14 => "INR D ", 25 | 0x15 => "DCR D ", 26 | 0x16 => "MVI D ", 27 | 0x17 => "RAL ", 28 | 0x18 => "NOP ", 29 | 0x19 => "DAD DE ", 30 | 0x1A => "LDAX DE ", 31 | 0x1B => "DCX DE ", 32 | 0x1C => "INR E ", 33 | 0x1D => "DCR E ", 34 | 0x1E => "MVI E ", 35 | 0x1F => "RAR ", 36 | 37 | 0x20 => "NOP ", 38 | 0x21 => "LXI HL ", 39 | 0x22 => "SHLD ", 40 | 0x23 => "INX HL ", 41 | 0x24 => "INR H ", 42 | 0x25 => "DCR H ", 43 | 0x26 => "MVI H ", 44 | 0x27 => "DAA ", 45 | 0x28 => "NOP ", 46 | 0x29 => "DAD HL ", 47 | 0x2A => "LHLD ", 48 | 0x2B => "DCX HL ", 49 | 0x2C => "INR L ", 50 | 0x2D => "DCR L ", 51 | 0x2E => "MVI L ", 52 | 0x2F => "CMA ", 53 | 54 | 0x30 => "NOP ", 55 | 0x31 => "LXI SP ", 56 | 0x32 => "STA ", 57 | 0x33 => "INX SP ", 58 | 0x34 => "INR M ", 59 | 0x35 => "DCR M ", 60 | 0x36 => "MVI M ", 61 | 0x37 => "STC ", 62 | 0x38 => "NOP ", 63 | 0x39 => "DAD SP ", 64 | 0x3A => "LDA ", 65 | 0x3B => "DCX SP ", 66 | 0x3C => "INR A ", 67 | 0x3D => "DCR A ", 68 | 0x3E => "MVI A ", 69 | 0x3F => "CMC ", 70 | 71 | 0x40 => "MOV (B, B)", 72 | 0x41 => "MOV (B, C)", 73 | 0x42 => "MOV (B, D)", 74 | 0x43 => "MOV (B, E)", 75 | 0x44 => "MOV (B, H)", 76 | 0x45 => "MOV (B, L)", 77 | 0x46 => "MOV (B, M)", 78 | 0x47 => "MOV (B, A)", 79 | 0x48 => "MOV (C, B)", 80 | 0x49 => "MOV (C, C)", 81 | 0x4A => "MOV (C, D)", 82 | 0x4B => "MOV (C, E)", 83 | 0x4C => "MOV (C, H)", 84 | 0x4D => "MOV (C, L)", 85 | 0x4E => "MOV (C, M)", 86 | 0x4F => "MOV (C, A)", 87 | 88 | 0x50 => "MOV (D, B)", 89 | 0x51 => "MOV (D, C)", 90 | 0x52 => "MOV (D, D)", 91 | 0x53 => "MOV (D, E)", 92 | 0x54 => "MOV (D, H)", 93 | 0x55 => "MOV (D, L)", 94 | 0x56 => "MOV (D, M)", 95 | 0x57 => "MOV (D, A)", 96 | 0x58 => "MOV (E, B)", 97 | 0x59 => "MOV (E, C)", 98 | 0x5A => "MOV (E, D)", 99 | 0x5B => "MOV (E, E)", 100 | 0x5C => "MOV (E, H)", 101 | 0x5D => "MOV (E, L)", 102 | 0x5E => "MOV (E, M)", 103 | 0x5F => "MOV (E, A)", 104 | 105 | 0x60 => "MOV (H, B)", 106 | 0x61 => "MOV (H, C)", 107 | 0x62 => "MOV (H, D)", 108 | 0x63 => "MOV (H, E)", 109 | 0x64 => "MOV (H, H)", 110 | 0x65 => "MOV (H, L)", 111 | 0x66 => "MOV (H, M)", 112 | 0x67 => "MOV (H, A)", 113 | 0x68 => "MOV (L, B)", 114 | 0x69 => "MOV (L, C)", 115 | 0x6A => "MOV (L, D)", 116 | 0x6B => "MOV (L, E)", 117 | 0x6C => "MOV (L, H)", 118 | 0x6D => "MOV (L, L)", 119 | 0x6E => "MOV (L, M)", 120 | 0x6F => "MOV (L, A)", 121 | 122 | 0x70 => "MOV (M, B)", 123 | 0x71 => "MOV (M, C)", 124 | 0x72 => "MOV (M, D)", 125 | 0x73 => "MOV (M, E)", 126 | 0x74 => "MOV (M, H)", 127 | 0x75 => "MOV (M, L)", 128 | 0x76 => "HLT ", 129 | 0x77 => "MOV (M, A)", 130 | 0x78 => "MOV (A, B)", 131 | 0x79 => "MOV (A, C)", 132 | 0x7A => "MOV (A, D)", 133 | 0x7B => "MOV (A, E)", 134 | 0x7C => "MOV (A, H)", 135 | 0x7D => "MOV (A, L)", 136 | 0x7E => "MOV (A, M)", 137 | 0x7F => "MOV (A, A)", 138 | 139 | 0x80 => "ADD B ", 140 | 0x81 => "ADD C ", 141 | 0x82 => "ADD D ", 142 | 0x83 => "ADD E ", 143 | 0x84 => "ADD H ", 144 | 0x85 => "ADD L ", 145 | 0x86 => "ADD M ", 146 | 0x87 => "ADD A ", 147 | 0x88 => "ADC B ", 148 | 0x89 => "ADC C ", 149 | 0x8A => "ADC D ", 150 | 0x8B => "ADC E ", 151 | 0x8C => "ADC H ", 152 | 0x8D => "ADC L ", 153 | 0x8E => "ADC M ", 154 | 0x8F => "ADC A ", 155 | 156 | 0x90 => "SUB B ", 157 | 0x91 => "SUB C ", 158 | 0x92 => "SUB D ", 159 | 0x93 => "SUB E ", 160 | 0x94 => "SUB H ", 161 | 0x95 => "SUB L ", 162 | 0x96 => "SUB M ", 163 | 0x97 => "SUB A ", 164 | 0x98 => "SBB B ", 165 | 0x99 => "SBB C ", 166 | 0x9A => "SBB D ", 167 | 0x9B => "SBB E ", 168 | 0x9C => "SBB H ", 169 | 0x9D => "SBB L ", 170 | 0x9E => "SBB M ", 171 | 0x9F => "SBB A ", 172 | 173 | 0xA0 => "ANA B ", 174 | 0xA1 => "ANA C ", 175 | 0xA2 => "ANA D ", 176 | 0xA3 => "ANA E ", 177 | 0xA4 => "ANA H ", 178 | 0xA5 => "ANA L ", 179 | 0xA6 => "ANA M ", 180 | 0xA7 => "ANA A ", 181 | 0xA8 => "XRA B ", 182 | 0xA9 => "XRA C ", 183 | 0xAA => "XRA D ", 184 | 0xAB => "XRA E ", 185 | 0xAC => "XRA H ", 186 | 0xAD => "XRA L ", 187 | 0xAE => "XRA M ", 188 | 0xAF => "XRA A ", 189 | 190 | 0xB0 => "ORA B ", 191 | 0xB1 => "ORA C ", 192 | 0xB2 => "ORA D ", 193 | 0xB3 => "ORA E ", 194 | 0xB4 => "ORA H ", 195 | 0xB5 => "ORA L ", 196 | 0xB6 => "ORA M ", 197 | 0xB7 => "ORA A ", 198 | 0xB8 => "CMP B ", 199 | 0xB9 => "CMP C ", 200 | 0xBA => "CMP D ", 201 | 0xBB => "CMP E ", 202 | 0xBC => "CMP H ", 203 | 0xBD => "CMP L ", 204 | 0xBE => "CMP M ", 205 | 0xBF => "CMP A ", 206 | 207 | 0xC0 => "RNZ ", 208 | 0xC1 => "POP BC ", 209 | 0xC2 => "JNZ ", 210 | 0xC3 => "JMP ", 211 | 0xC4 => "CNZ ", 212 | 0xC5 => "PUSH B ", 213 | 0xC6 => "ADI ", 214 | 0xC7 => "RST 0 ", 215 | 0xC8 => "RZ ", 216 | 0xC9 => "RET ", 217 | 0xCA => "JZ ", 218 | 0xCB => "JMP ", 219 | 0xCC => "CZ ", 220 | 0xCD => "CALL ", 221 | 0xCE => "ACI ", 222 | 0xCF => "RST 1 ", 223 | 224 | 0xD0 => "RNC ", 225 | 0xD1 => "POP DE ", 226 | 0xD2 => "JNC ", 227 | 0xD3 => "OUT ", 228 | 0xD4 => "CNC ", 229 | 0xD5 => "PUSH D ", 230 | 0xD6 => "SUI ", 231 | 0xD7 => "RST 2 ", 232 | 0xD8 => "RC ", 233 | 0xD9 => "RET ", 234 | 0xDA => "JC ", 235 | 0xDB => "IN ", 236 | 0xDC => "CC ", 237 | 0xDD => "CALL ", 238 | 0xDE => "SBI ", 239 | 0xDF => "RST 3 ", 240 | 241 | 0xE0 => "RPO ", 242 | 0xE1 => "POP HL ", 243 | 0xE2 => "JPO ", 244 | 0xE3 => "XTHL ", 245 | 0xE4 => "CPO ", 246 | 0xE5 => "PUSH H ", 247 | 0xE6 => "ANI ", 248 | 0xE7 => "RST 4 ", 249 | 0xE8 => "RPE ", 250 | 0xE9 => "PCHL ", 251 | 0xEA => "JPE ", 252 | 0xEB => "XCHG ", 253 | 0xEC => "CPE ", 254 | 0xED => "CALL ", 255 | 0xEE => "XRI ", 256 | 0xEF => "RST 5 ", 257 | 258 | 0xF0 => "RP ", 259 | 0xF1 => "POP PSW ", 260 | 0xF2 => "JP ", 261 | 0xF3 => "DI ", 262 | 0xF4 => "CP ", 263 | 0xF5 => "PUSH PSW ", 264 | 0xF6 => "ORI ", 265 | 0xF7 => "RST 6 ", 266 | 0xF8 => "RM ", 267 | 0xF9 => "SPHL ", 268 | 0xFA => "JM ", 269 | 0xFB => "EI ", 270 | 0xFC => "CM ", 271 | 0xFD => "CALL ", 272 | 0xFE => "CPI ", 273 | 0xFF => "RST 7 ", 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /tests/test_instr.rs: -------------------------------------------------------------------------------- 1 | use i8080::{Flag, Linear, Memory}; 2 | use std::cell::RefCell; 3 | use std::rc::Rc; 4 | 5 | #[test] 6 | fn test_inr() { 7 | let mem = Rc::new(RefCell::new(Linear::new())); 8 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 9 | cpu.reg.c = 0x99; 10 | mem.borrow_mut().set(0x0000, 0x0c); 11 | cpu.next(); 12 | assert_eq!(cpu.reg.c, 0x9a); 13 | } 14 | 15 | #[test] 16 | fn test_dcr() { 17 | let mem = Rc::new(RefCell::new(Linear::new())); 18 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 19 | cpu.reg.h = 0x3a; 20 | cpu.reg.l = 0x7c; 21 | mem.borrow_mut().set(0x3a7c, 0x40); 22 | mem.borrow_mut().set(0x0000, 0x35); 23 | cpu.next(); 24 | assert_eq!(mem.borrow().get(0x3a7c), 0x3f); 25 | } 26 | 27 | #[test] 28 | fn test_cma() { 29 | let mem = Rc::new(RefCell::new(Linear::new())); 30 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 31 | cpu.reg.a = 0x51; 32 | mem.borrow_mut().set(0x0000, 0x2f); 33 | cpu.next(); 34 | assert_eq!(cpu.reg.a, 0xae); 35 | } 36 | 37 | #[test] 38 | fn test_daa() { 39 | let mem = Rc::new(RefCell::new(Linear::new())); 40 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 41 | cpu.reg.a = 0x9b; 42 | mem.borrow_mut().set(0x0000, 0x27); 43 | cpu.next(); 44 | assert_eq!(cpu.reg.a, 1); 45 | assert!(cpu.reg.get_flag(Flag::A)); 46 | assert!(cpu.reg.get_flag(Flag::C)); 47 | } 48 | 49 | #[test] 50 | fn test_mov() { 51 | let mem = Rc::new(RefCell::new(Linear::new())); 52 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 53 | cpu.reg.a = 0xff; 54 | cpu.reg.h = 0x2b; 55 | cpu.reg.l = 0xe9; 56 | mem.borrow_mut().set(0x0000, 0x77); 57 | cpu.next(); 58 | assert_eq!(mem.borrow().get(0x2be9), 0xff); 59 | } 60 | 61 | #[test] 62 | fn test_stax() { 63 | let mem = Rc::new(RefCell::new(Linear::new())); 64 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 65 | cpu.reg.a = 0xff; 66 | cpu.reg.b = 0x3f; 67 | cpu.reg.c = 0x16; 68 | mem.borrow_mut().set(0x0000, 0x02); 69 | cpu.next(); 70 | assert_eq!(mem.borrow().get(0x3f16), 0xff); 71 | } 72 | 73 | #[test] 74 | fn test_ldax() { 75 | let mem = Rc::new(RefCell::new(Linear::new())); 76 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 77 | cpu.reg.d = 0x93; 78 | cpu.reg.e = 0x8b; 79 | mem.borrow_mut().set(0x938b, 0xff); 80 | mem.borrow_mut().set(0x0000, 0x1a); 81 | cpu.next(); 82 | assert_eq!(cpu.reg.a, 0xff); 83 | } 84 | 85 | #[test] 86 | fn test_add_1() { 87 | let mem = Rc::new(RefCell::new(Linear::new())); 88 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 89 | cpu.reg.d = 0x2e; 90 | cpu.reg.a = 0x6c; 91 | mem.borrow_mut().set(0x0000, 0x82); 92 | cpu.next(); 93 | assert_eq!(cpu.reg.a, 0x9a); 94 | assert_eq!(cpu.reg.get_flag(Flag::S), true); 95 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 96 | assert_eq!(cpu.reg.get_flag(Flag::A), true); 97 | assert_eq!(cpu.reg.get_flag(Flag::P), true); 98 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 99 | } 100 | 101 | #[test] 102 | fn test_add_2() { 103 | let mem = Rc::new(RefCell::new(Linear::new())); 104 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 105 | cpu.reg.a = 0x01; 106 | mem.borrow_mut().set(0x0000, 0x87); 107 | cpu.next(); 108 | assert_eq!(cpu.reg.a, 0x02); 109 | } 110 | 111 | #[test] 112 | fn test_adc_1() { 113 | let mem = Rc::new(RefCell::new(Linear::new())); 114 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 115 | cpu.reg.a = 0x42; 116 | cpu.reg.c = 0x3d; 117 | mem.borrow_mut().set(0x0000, 0x89); 118 | cpu.next(); 119 | assert_eq!(cpu.reg.a, 0x7f); 120 | assert_eq!(cpu.reg.get_flag(Flag::S), false); 121 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 122 | assert_eq!(cpu.reg.get_flag(Flag::A), false); 123 | assert_eq!(cpu.reg.get_flag(Flag::P), false); 124 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 125 | } 126 | 127 | #[test] 128 | fn test_adc_2() { 129 | let mem = Rc::new(RefCell::new(Linear::new())); 130 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 131 | cpu.reg.a = 0x42; 132 | cpu.reg.c = 0x3d; 133 | cpu.reg.set_flag(Flag::C, true); 134 | mem.borrow_mut().set(0x0000, 0x89); 135 | cpu.next(); 136 | assert_eq!(cpu.reg.a, 0x80); 137 | assert_eq!(cpu.reg.get_flag(Flag::S), true); 138 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 139 | assert_eq!(cpu.reg.get_flag(Flag::A), true); 140 | assert_eq!(cpu.reg.get_flag(Flag::P), false); 141 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 142 | } 143 | 144 | #[test] 145 | fn test_adc_3() { 146 | let mem = Rc::new(RefCell::new(Linear::new())); 147 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 148 | cpu.reg.a = 0x3f; 149 | cpu.reg.f = 0xd3; 150 | mem.borrow_mut().set(0x0000, 0x8f); 151 | cpu.next(); 152 | assert_eq!(cpu.reg.a, 0x7f); 153 | assert_eq!(cpu.reg.f, 0x12); 154 | } 155 | 156 | #[test] 157 | fn test_sub() { 158 | let mem = Rc::new(RefCell::new(Linear::new())); 159 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 160 | cpu.reg.a = 0x3e; 161 | mem.borrow_mut().set(0x0000, 0x97); 162 | cpu.next(); 163 | assert_eq!(cpu.reg.a, 0x00); 164 | assert_eq!(cpu.reg.get_flag(Flag::S), false); 165 | assert_eq!(cpu.reg.get_flag(Flag::Z), true); 166 | assert_eq!(cpu.reg.get_flag(Flag::A), true); 167 | assert_eq!(cpu.reg.get_flag(Flag::P), true); 168 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 169 | } 170 | 171 | #[test] 172 | fn test_sbb() { 173 | let mem = Rc::new(RefCell::new(Linear::new())); 174 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 175 | cpu.reg.l = 0x02; 176 | cpu.reg.a = 0x04; 177 | cpu.reg.set_flag(Flag::C, true); 178 | mem.borrow_mut().set(0x0000, 0x9d); 179 | cpu.next(); 180 | assert_eq!(cpu.reg.a, 0x01); 181 | assert_eq!(cpu.reg.get_flag(Flag::S), false); 182 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 183 | assert_eq!(cpu.reg.get_flag(Flag::A), true); 184 | assert_eq!(cpu.reg.get_flag(Flag::P), false); 185 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 186 | } 187 | 188 | #[test] 189 | fn test_ana() { 190 | let mem = Rc::new(RefCell::new(Linear::new())); 191 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 192 | cpu.reg.a = 0xfc; 193 | cpu.reg.c = 0x0f; 194 | mem.borrow_mut().set(0x0000, 0xa1); 195 | cpu.next(); 196 | assert_eq!(cpu.reg.a, 0x0c); 197 | } 198 | 199 | #[test] 200 | fn test_xra_1() { 201 | let mem = Rc::new(RefCell::new(Linear::new())); 202 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 203 | cpu.reg.a = 0x0a; 204 | cpu.reg.b = 0x0b; 205 | cpu.reg.c = 0x0c; 206 | mem.borrow_mut().set(0x0000, 0xaf); 207 | mem.borrow_mut().set(0x0001, 0x47); 208 | mem.borrow_mut().set(0x0002, 0x4f); 209 | cpu.next(); 210 | cpu.next(); 211 | cpu.next(); 212 | assert_eq!(cpu.reg.a, 0x00); 213 | assert_eq!(cpu.reg.b, 0x00); 214 | assert_eq!(cpu.reg.c, 0x00); 215 | } 216 | 217 | #[test] 218 | fn test_xra_2() { 219 | let mem = Rc::new(RefCell::new(Linear::new())); 220 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 221 | cpu.reg.a = 0xff; 222 | cpu.reg.b = 0b1010_1010; 223 | mem.borrow_mut().set(0x0000, 0xa8); 224 | cpu.next(); 225 | assert_eq!(cpu.reg.a, 0b0101_0101); 226 | } 227 | 228 | #[test] 229 | fn test_xra_3() {} 230 | 231 | #[test] 232 | fn test_ora() { 233 | let mem = Rc::new(RefCell::new(Linear::new())); 234 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 235 | cpu.reg.a = 0x33; 236 | cpu.reg.c = 0x0f; 237 | mem.borrow_mut().set(0x0000, 0xb1); 238 | cpu.next(); 239 | assert_eq!(cpu.reg.a, 0x3f); 240 | } 241 | 242 | #[test] 243 | fn test_cmp_1() { 244 | let mem = Rc::new(RefCell::new(Linear::new())); 245 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 246 | cpu.reg.a = 0x0a; 247 | cpu.reg.e = 0x05; 248 | mem.borrow_mut().set(0x0000, 0xbb); 249 | cpu.next(); 250 | assert_eq!(cpu.reg.a, 0x0a); 251 | assert_eq!(cpu.reg.e, 0x05); 252 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 253 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 254 | } 255 | 256 | #[test] 257 | fn test_cmp_2() { 258 | let mem = Rc::new(RefCell::new(Linear::new())); 259 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 260 | cpu.reg.a = 0x02; 261 | cpu.reg.e = 0x05; 262 | mem.borrow_mut().set(0x0000, 0xbb); 263 | cpu.next(); 264 | assert_eq!(cpu.reg.a, 0x02); 265 | assert_eq!(cpu.reg.e, 0x05); 266 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 267 | assert_eq!(cpu.reg.get_flag(Flag::C), true); 268 | } 269 | 270 | #[test] 271 | fn test_cmp_3() { 272 | let mem = Rc::new(RefCell::new(Linear::new())); 273 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 274 | cpu.reg.a = 0xe5; 275 | cpu.reg.e = 0x05; 276 | mem.borrow_mut().set(0x0000, 0xbb); 277 | cpu.next(); 278 | assert_eq!(cpu.reg.a, 0xe5); 279 | assert_eq!(cpu.reg.e, 0x05); 280 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 281 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 282 | } 283 | 284 | #[test] 285 | fn test_rlc() { 286 | let mem = Rc::new(RefCell::new(Linear::new())); 287 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 288 | cpu.reg.a = 0xf2; 289 | mem.borrow_mut().set(0x0000, 0x07); 290 | cpu.next(); 291 | assert_eq!(cpu.reg.a, 0xe5); 292 | assert_eq!(cpu.reg.get_flag(Flag::C), true); 293 | } 294 | 295 | #[test] 296 | fn test_rrc() { 297 | let mem = Rc::new(RefCell::new(Linear::new())); 298 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 299 | cpu.reg.a = 0xf2; 300 | mem.borrow_mut().set(0x0000, 0x0f); 301 | cpu.next(); 302 | assert_eq!(cpu.reg.a, 0x79); 303 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 304 | } 305 | 306 | #[test] 307 | fn test_ral() { 308 | let mem = Rc::new(RefCell::new(Linear::new())); 309 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 310 | cpu.reg.a = 0xb5; 311 | mem.borrow_mut().set(0x0000, 0x17); 312 | cpu.next(); 313 | assert_eq!(cpu.reg.a, 0x6a); 314 | assert_eq!(cpu.reg.get_flag(Flag::C), true); 315 | } 316 | 317 | #[test] 318 | fn test_rar() { 319 | let mem = Rc::new(RefCell::new(Linear::new())); 320 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 321 | cpu.reg.a = 0x6a; 322 | cpu.reg.set_flag(Flag::C, true); 323 | mem.borrow_mut().set(0x0000, 0x1f); 324 | cpu.next(); 325 | assert_eq!(cpu.reg.a, 0xb5); 326 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 327 | } 328 | 329 | #[test] 330 | fn test_stack_push_1() { 331 | let mem = Rc::new(RefCell::new(Linear::new())); 332 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 333 | cpu.reg.d = 0x8f; 334 | cpu.reg.e = 0x9d; 335 | cpu.reg.sp = 0x3a2c; 336 | mem.borrow_mut().set(0x0000, 0xd5); 337 | cpu.next(); 338 | assert_eq!(mem.borrow().get(0x3a2b), 0x8f); 339 | assert_eq!(mem.borrow().get(0x3a2a), 0x9d); 340 | assert_eq!(cpu.reg.sp, 0x3a2a); 341 | } 342 | 343 | #[test] 344 | fn test_stack_push_2() { 345 | let mem = Rc::new(RefCell::new(Linear::new())); 346 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 347 | cpu.reg.a = 0x1f; 348 | cpu.reg.sp = 0x502a; 349 | cpu.reg.set_flag(Flag::C, true); 350 | cpu.reg.set_flag(Flag::Z, true); 351 | cpu.reg.set_flag(Flag::P, true); 352 | mem.borrow_mut().set(0x0000, 0xf5); 353 | cpu.next(); 354 | assert_eq!(mem.borrow().get(0x5029), 0x1f); 355 | assert_eq!(mem.borrow().get(0x5028), 0x47); 356 | assert_eq!(cpu.reg.sp, 0x5028); 357 | } 358 | 359 | #[test] 360 | fn test_stack_pop_1() { 361 | let mem = Rc::new(RefCell::new(Linear::new())); 362 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 363 | mem.borrow_mut().set(0x1239, 0x3d); 364 | mem.borrow_mut().set(0x123a, 0x93); 365 | cpu.reg.sp = 0x1239; 366 | mem.borrow_mut().set(0x0000, 0xe1); 367 | cpu.next(); 368 | assert_eq!(cpu.reg.l, 0x3d); 369 | assert_eq!(cpu.reg.h, 0x93); 370 | assert_eq!(cpu.reg.sp, 0x123b); 371 | } 372 | 373 | #[test] 374 | fn test_stack_pop_2() { 375 | let mem = Rc::new(RefCell::new(Linear::new())); 376 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 377 | mem.borrow_mut().set(0x2c00, 0xc3); 378 | mem.borrow_mut().set(0x2c01, 0xff); 379 | cpu.reg.sp = 0x2c00; 380 | mem.borrow_mut().set(0x0000, 0xf1); 381 | cpu.next(); 382 | assert_eq!(cpu.reg.a, 0xff); 383 | assert_eq!(cpu.reg.f, 0xc3); 384 | assert_eq!(cpu.reg.get_flag(Flag::S), true); 385 | assert_eq!(cpu.reg.get_flag(Flag::Z), true); 386 | assert_eq!(cpu.reg.get_flag(Flag::A), false); 387 | assert_eq!(cpu.reg.get_flag(Flag::P), false); 388 | assert_eq!(cpu.reg.get_flag(Flag::C), true); 389 | } 390 | 391 | #[test] 392 | fn test_dad_1() { 393 | let mem = Rc::new(RefCell::new(Linear::new())); 394 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 395 | cpu.reg.b = 0x33; 396 | cpu.reg.c = 0x9f; 397 | cpu.reg.h = 0xa1; 398 | cpu.reg.l = 0x7b; 399 | mem.borrow_mut().set(0x0000, 0x09); 400 | cpu.next(); 401 | assert_eq!(cpu.reg.h, 0xd5); 402 | assert_eq!(cpu.reg.l, 0x1a); 403 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 404 | } 405 | 406 | #[test] 407 | fn test_dad_2() { 408 | let mem = Rc::new(RefCell::new(Linear::new())); 409 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 410 | cpu.reg.h = 0xa1; 411 | cpu.reg.l = 0x7b; 412 | mem.borrow_mut().set(0x0000, 0x29); 413 | cpu.next(); 414 | assert_eq!(cpu.reg.get_hl(), 0xa17b << 1); 415 | } 416 | 417 | #[test] 418 | fn test_inx_1() { 419 | let mem = Rc::new(RefCell::new(Linear::new())); 420 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 421 | cpu.reg.d = 0x38; 422 | cpu.reg.e = 0xff; 423 | mem.borrow_mut().set(0x0000, 0x13); 424 | cpu.next(); 425 | assert_eq!(cpu.reg.d, 0x39); 426 | assert_eq!(cpu.reg.e, 0x00); 427 | } 428 | 429 | #[test] 430 | fn test_inx_2() { 431 | let mem = Rc::new(RefCell::new(Linear::new())); 432 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 433 | cpu.reg.sp = 0xffff; 434 | mem.borrow_mut().set(0x0000, 0x33); 435 | cpu.next(); 436 | assert_eq!(cpu.reg.sp, 0x0000); 437 | } 438 | 439 | #[test] 440 | fn test_dcx() { 441 | let mem = Rc::new(RefCell::new(Linear::new())); 442 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 443 | cpu.reg.h = 0x98; 444 | cpu.reg.l = 0x00; 445 | mem.borrow_mut().set(0x0000, 0x2b); 446 | cpu.next(); 447 | assert_eq!(cpu.reg.h, 0x97); 448 | assert_eq!(cpu.reg.l, 0xff); 449 | } 450 | 451 | #[test] 452 | fn test_xchg() { 453 | let mem = Rc::new(RefCell::new(Linear::new())); 454 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 455 | cpu.reg.h = 0x00; 456 | cpu.reg.l = 0xff; 457 | cpu.reg.d = 0x33; 458 | cpu.reg.e = 0x55; 459 | mem.borrow_mut().set(0x0000, 0xeb); 460 | cpu.next(); 461 | assert_eq!(cpu.reg.h, 0x33); 462 | assert_eq!(cpu.reg.l, 0x55); 463 | assert_eq!(cpu.reg.d, 0x00); 464 | assert_eq!(cpu.reg.e, 0xff); 465 | } 466 | 467 | #[test] 468 | fn test_xthl() { 469 | let mem = Rc::new(RefCell::new(Linear::new())); 470 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 471 | cpu.reg.sp = 0x10ad; 472 | cpu.reg.h = 0x0b; 473 | cpu.reg.l = 0x3c; 474 | mem.borrow_mut().set(0x10ad, 0xf0); 475 | mem.borrow_mut().set(0x10ae, 0x0d); 476 | mem.borrow_mut().set(0x0000, 0xe3); 477 | cpu.next(); 478 | assert_eq!(cpu.reg.h, 0x0d); 479 | assert_eq!(cpu.reg.l, 0xf0); 480 | assert_eq!(mem.borrow().get(0x10ad), 0x3c); 481 | assert_eq!(mem.borrow().get(0x10ae), 0x0b); 482 | } 483 | 484 | #[test] 485 | fn test_sphl() { 486 | let mem = Rc::new(RefCell::new(Linear::new())); 487 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 488 | cpu.reg.h = 0x50; 489 | cpu.reg.l = 0x6c; 490 | mem.borrow_mut().set(0x0000, 0xf9); 491 | cpu.next(); 492 | assert_eq!(cpu.reg.sp, 0x506c); 493 | } 494 | 495 | #[test] 496 | fn test_mvi() { 497 | let mem = Rc::new(RefCell::new(Linear::new())); 498 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 499 | mem.borrow_mut().set(0x0000, 0x26); 500 | mem.borrow_mut().set(0x0001, 0x3c); 501 | mem.borrow_mut().set(0x0002, 0x2e); 502 | mem.borrow_mut().set(0x0003, 0xf4); 503 | mem.borrow_mut().set(0x0004, 0x36); 504 | mem.borrow_mut().set(0x0005, 0xff); 505 | cpu.next(); 506 | cpu.next(); 507 | cpu.next(); 508 | assert_eq!(cpu.reg.h, 0x3c); 509 | assert_eq!(cpu.reg.l, 0xf4); 510 | assert_eq!(mem.borrow().get(0x3cf4), 0xff); 511 | } 512 | 513 | #[test] 514 | fn test_adi() { 515 | let mem = Rc::new(RefCell::new(Linear::new())); 516 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 517 | mem.borrow_mut().set(0x0000, 0x3e); 518 | mem.borrow_mut().set(0x0001, 0x14); 519 | mem.borrow_mut().set(0x0002, 0xc6); 520 | mem.borrow_mut().set(0x0003, 0x42); 521 | mem.borrow_mut().set(0x0004, 0xc6); 522 | mem.borrow_mut().set(0x0005, 0xbe); 523 | cpu.next(); 524 | cpu.next(); 525 | cpu.next(); 526 | assert_eq!(cpu.reg.a, 0x14); 527 | assert_eq!(cpu.reg.get_flag(Flag::S), false); 528 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 529 | assert_eq!(cpu.reg.get_flag(Flag::A), true); 530 | assert_eq!(cpu.reg.get_flag(Flag::P), true); 531 | assert_eq!(cpu.reg.get_flag(Flag::C), true); 532 | } 533 | 534 | #[test] 535 | fn test_aci() { 536 | let mem = Rc::new(RefCell::new(Linear::new())); 537 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 538 | mem.borrow_mut().set(0x0000, 0x3e); 539 | mem.borrow_mut().set(0x0001, 0x56); 540 | mem.borrow_mut().set(0x0002, 0xce); 541 | mem.borrow_mut().set(0x0003, 0xbe); 542 | mem.borrow_mut().set(0x0004, 0xce); 543 | mem.borrow_mut().set(0x0005, 0x42); 544 | cpu.next(); 545 | cpu.next(); 546 | cpu.next(); 547 | assert_eq!(cpu.reg.a, 0x57); 548 | } 549 | 550 | #[test] 551 | fn test_sui() { 552 | let mem = Rc::new(RefCell::new(Linear::new())); 553 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 554 | mem.borrow_mut().set(0x0000, 0x3e); 555 | mem.borrow_mut().set(0x0001, 0x00); 556 | mem.borrow_mut().set(0x0002, 0xd6); 557 | mem.borrow_mut().set(0x0003, 0x01); 558 | cpu.next(); 559 | cpu.next(); 560 | assert_eq!(cpu.reg.a, 0xff); 561 | assert_eq!(cpu.reg.get_flag(Flag::S), true); 562 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 563 | assert_eq!(cpu.reg.get_flag(Flag::A), false); 564 | assert_eq!(cpu.reg.get_flag(Flag::P), true); 565 | assert_eq!(cpu.reg.get_flag(Flag::C), true); 566 | } 567 | 568 | #[test] 569 | fn test_sbi_1() { 570 | let mem = Rc::new(RefCell::new(Linear::new())); 571 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 572 | mem.borrow_mut().set(0x0000, 0xde); 573 | mem.borrow_mut().set(0x0001, 0x01); 574 | cpu.next(); 575 | assert_eq!(cpu.reg.a, 0xff); 576 | assert_eq!(cpu.reg.get_flag(Flag::S), true); 577 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 578 | assert_eq!(cpu.reg.get_flag(Flag::A), false); 579 | assert_eq!(cpu.reg.get_flag(Flag::P), true); 580 | assert_eq!(cpu.reg.get_flag(Flag::C), true); 581 | } 582 | 583 | #[test] 584 | fn test_sbi_2() { 585 | let mem = Rc::new(RefCell::new(Linear::new())); 586 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 587 | cpu.reg.set_flag(Flag::C, true); 588 | mem.borrow_mut().set(0x0000, 0xde); 589 | mem.borrow_mut().set(0x0001, 0x01); 590 | cpu.next(); 591 | assert_eq!(cpu.reg.a, 0xfe); 592 | assert_eq!(cpu.reg.get_flag(Flag::S), true); 593 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 594 | assert_eq!(cpu.reg.get_flag(Flag::A), false); 595 | assert_eq!(cpu.reg.get_flag(Flag::P), false); 596 | assert_eq!(cpu.reg.get_flag(Flag::C), true); 597 | } 598 | 599 | #[test] 600 | fn test_ani() { 601 | let mem = Rc::new(RefCell::new(Linear::new())); 602 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 603 | cpu.reg.c = 0x3a; 604 | mem.borrow_mut().set(0x0000, 0x79); 605 | mem.borrow_mut().set(0x0001, 0xe6); 606 | mem.borrow_mut().set(0x0002, 0x0f); 607 | cpu.next(); 608 | cpu.next(); 609 | assert_eq!(cpu.reg.a, 0x0a); 610 | } 611 | 612 | #[test] 613 | fn test_xri() { 614 | let mem = Rc::new(RefCell::new(Linear::new())); 615 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 616 | cpu.reg.a = 0x3b; 617 | mem.borrow_mut().set(0x0000, 0xee); 618 | mem.borrow_mut().set(0x0001, 0x81); 619 | cpu.next(); 620 | assert_eq!(cpu.reg.a, 0xba); 621 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 622 | } 623 | 624 | #[test] 625 | fn test_ori() { 626 | let mem = Rc::new(RefCell::new(Linear::new())); 627 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 628 | cpu.reg.c = 0xb5; 629 | mem.borrow_mut().set(0x0000, 0x79); 630 | mem.borrow_mut().set(0x0001, 0xf6); 631 | mem.borrow_mut().set(0x0002, 0x0f); 632 | cpu.next(); 633 | cpu.next(); 634 | assert_eq!(cpu.reg.a, 0xbf); 635 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 636 | } 637 | 638 | #[test] 639 | fn test_cpi() { 640 | let mem = Rc::new(RefCell::new(Linear::new())); 641 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 642 | mem.borrow_mut().set(0x0000, 0x3e); 643 | mem.borrow_mut().set(0x0001, 0x4a); 644 | mem.borrow_mut().set(0x0002, 0xfe); 645 | mem.borrow_mut().set(0x0003, 0x40); 646 | cpu.next(); 647 | cpu.next(); 648 | assert_eq!(cpu.reg.a, 0x4a); 649 | assert_eq!(cpu.reg.get_flag(Flag::Z), false); 650 | assert_eq!(cpu.reg.get_flag(Flag::C), false); 651 | } 652 | 653 | #[test] 654 | fn test_sta() { 655 | let mem = Rc::new(RefCell::new(Linear::new())); 656 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 657 | cpu.reg.a = 0xff; 658 | mem.borrow_mut().set(0x0000, 0x32); 659 | mem.borrow_mut().set(0x0001, 0xb3); 660 | mem.borrow_mut().set(0x0002, 0x05); 661 | cpu.next(); 662 | assert_eq!(mem.borrow().get(0x05b3), 0xff); 663 | } 664 | 665 | #[test] 666 | fn test_lda() { 667 | let mem = Rc::new(RefCell::new(Linear::new())); 668 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 669 | mem.borrow_mut().set(0x0300, 0xff); 670 | mem.borrow_mut().set(0x0000, 0x3a); 671 | mem.borrow_mut().set(0x0001, 0x00); 672 | mem.borrow_mut().set(0x0002, 0x03); 673 | cpu.next(); 674 | assert_eq!(cpu.reg.a, 0xff); 675 | } 676 | 677 | #[test] 678 | fn test_shld() { 679 | let mem = Rc::new(RefCell::new(Linear::new())); 680 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 681 | cpu.reg.h = 0xae; 682 | cpu.reg.l = 0x29; 683 | mem.borrow_mut().set(0x0000, 0x22); 684 | mem.borrow_mut().set(0x0001, 0x0a); 685 | mem.borrow_mut().set(0x0002, 0x01); 686 | cpu.next(); 687 | assert_eq!(mem.borrow().get(0x010a), 0x29); 688 | assert_eq!(mem.borrow().get(0x010b), 0xae); 689 | } 690 | 691 | #[test] 692 | fn test_lhld() { 693 | let mem = Rc::new(RefCell::new(Linear::new())); 694 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 695 | mem.borrow_mut().set(0x025b, 0xff); 696 | mem.borrow_mut().set(0x025c, 0x03); 697 | mem.borrow_mut().set(0x0000, 0x2a); 698 | mem.borrow_mut().set(0x0001, 0x5b); 699 | mem.borrow_mut().set(0x0002, 0x02); 700 | cpu.next(); 701 | assert_eq!(cpu.reg.l, 0xff); 702 | assert_eq!(cpu.reg.h, 0x03); 703 | } 704 | 705 | #[test] 706 | fn test_pchl() { 707 | let mem = Rc::new(RefCell::new(Linear::new())); 708 | let mut cpu = i8080::Cpu::power_up(mem.clone()); 709 | cpu.reg.h = 0x41; 710 | cpu.reg.l = 0x3e; 711 | mem.borrow_mut().set(0x0000, 0xe9); 712 | cpu.next(); 713 | assert_eq!(cpu.reg.pc, 0x413e); 714 | } 715 | -------------------------------------------------------------------------------- /src/cpu.rs: -------------------------------------------------------------------------------- 1 | use super::asm; 2 | use super::bit; 3 | use super::memory::Memory; 4 | use super::register::{Flag, Register}; 5 | use rog::debugln; 6 | use std::cell::RefCell; 7 | use std::mem; 8 | use std::rc::Rc; 9 | use std::thread; 10 | use std::time; 11 | 12 | pub const CLOCK_FREQUENCY: u32 = 2_000_000; 13 | pub const STEP_TIME: u32 = 16; 14 | pub const STEP_CYCLES: u32 = (STEP_TIME as f64 / (1000_f64 / CLOCK_FREQUENCY as f64)) as u32; 15 | 16 | // 0 1 2 3 4 5 6 7 8 9 a b c d e f 17 | const OP_CYCLES: [u32; 256] = [ 18 | 04, 10, 07, 05, 05, 05, 07, 04, 04, 10, 07, 05, 05, 05, 07, 04, // 0 19 | 04, 10, 07, 05, 05, 05, 07, 04, 04, 10, 07, 05, 05, 05, 07, 04, // 1 20 | 04, 10, 16, 05, 05, 05, 07, 04, 04, 10, 16, 05, 05, 05, 07, 04, // 2 21 | 04, 10, 13, 05, 10, 10, 10, 04, 04, 10, 13, 05, 05, 05, 07, 04, // 3 22 | 05, 05, 05, 05, 05, 05, 07, 05, 05, 05, 05, 05, 05, 05, 07, 05, // 4 23 | 05, 05, 05, 05, 05, 05, 07, 05, 05, 05, 05, 05, 05, 05, 07, 05, // 5 24 | 05, 05, 05, 05, 05, 05, 07, 05, 05, 05, 05, 05, 05, 05, 07, 05, // 6 25 | 07, 07, 07, 07, 07, 07, 07, 07, 05, 05, 05, 05, 05, 05, 07, 05, // 7 26 | 04, 04, 04, 04, 04, 04, 07, 04, 04, 04, 04, 04, 04, 04, 07, 04, // 8 27 | 04, 04, 04, 04, 04, 04, 07, 04, 04, 04, 04, 04, 04, 04, 07, 04, // 9 28 | 04, 04, 04, 04, 04, 04, 07, 04, 04, 04, 04, 04, 04, 04, 07, 04, // a 29 | 04, 04, 04, 04, 04, 04, 07, 04, 04, 04, 04, 04, 04, 04, 07, 04, // b 30 | 05, 10, 10, 10, 11, 11, 07, 11, 05, 10, 10, 10, 11, 17, 07, 11, // c 31 | 05, 10, 10, 10, 11, 11, 07, 11, 05, 10, 10, 10, 11, 17, 07, 11, // d 32 | 05, 10, 10, 18, 11, 11, 07, 11, 05, 05, 10, 05, 11, 17, 07, 11, // e 33 | 05, 10, 10, 04, 11, 11, 07, 11, 05, 05, 10, 04, 11, 17, 07, 11, // f 34 | ]; 35 | 36 | pub struct Cpu { 37 | pub reg: Register, 38 | pub mem: Rc>, 39 | pub halted: bool, 40 | pub inte: bool, 41 | 42 | step_cycles: u32, 43 | step_zero: time::SystemTime, 44 | } 45 | 46 | impl Cpu { 47 | pub fn power_up(mem: Rc>) -> Self { 48 | Self { 49 | reg: Register::power_up(), 50 | mem, 51 | halted: false, 52 | inte: false, 53 | step_cycles: 0, 54 | step_zero: time::SystemTime::now(), 55 | } 56 | } 57 | 58 | fn imm_ds(&mut self) -> u8 { 59 | let v = self.mem.borrow().get(self.reg.pc); 60 | self.reg.pc += 1; 61 | v 62 | } 63 | 64 | fn imm_dw(&mut self) -> u16 { 65 | let v = self.mem.borrow().get_word(self.reg.pc); 66 | self.reg.pc += 2; 67 | v 68 | } 69 | 70 | fn get_m(&self) -> u8 { 71 | let a = self.reg.get_hl(); 72 | self.mem.borrow().get(a) 73 | } 74 | 75 | fn set_m(&mut self, v: u8) { 76 | let a = self.reg.get_hl(); 77 | self.mem.borrow_mut().set(a, v) 78 | } 79 | 80 | fn stack_add(&mut self, v: u16) { 81 | self.reg.sp = self.reg.sp.wrapping_sub(2); 82 | self.mem.borrow_mut().set_word(self.reg.sp, v); 83 | } 84 | 85 | fn stack_pop(&mut self) -> u16 { 86 | let r = self.mem.borrow().get_word(self.reg.sp); 87 | self.reg.sp = self.reg.sp.wrapping_add(2); 88 | r 89 | } 90 | 91 | fn alu_inr(&mut self, n: u8) -> u8 { 92 | let r = n.wrapping_add(1); 93 | self.reg.set_flag(Flag::S, bit::get(r, 7)); 94 | self.reg.set_flag(Flag::Z, r == 0x00); 95 | self.reg.set_flag(Flag::A, (n & 0x0f) + 0x01 > 0x0f); 96 | self.reg.set_flag(Flag::P, r.count_ones() & 0x01 == 0x00); 97 | r 98 | } 99 | 100 | fn alu_dcr(&mut self, n: u8) -> u8 { 101 | let r = n.wrapping_sub(1); 102 | self.reg.set_flag(Flag::S, bit::get(r, 7)); 103 | self.reg.set_flag(Flag::Z, r == 0x00); 104 | self.reg.set_flag(Flag::A, (r & 0x0f) != 0x0f); 105 | self.reg.set_flag(Flag::P, r.count_ones() & 0x01 == 0x00); 106 | r 107 | } 108 | 109 | // The integht-bit hexadecimal number in the accumulator is.adjusted to form tow four bit binary codecd decimal 110 | // digits by the following two process 111 | fn alu_daa(&mut self) { 112 | let mut a: u8 = 0; 113 | let mut c = self.reg.get_flag(Flag::C); 114 | let lsb = self.reg.a & 0x0f; 115 | let msb = self.reg.a >> 4; 116 | // If the least significant four bits of the accumulator represents a number greater than 9, or if the Auxiliary 117 | // Carry bit is equal to one, the accumulator is incremented by six. Otherwise, no incrementing occurs. 118 | if (lsb > 9) || self.reg.get_flag(Flag::A) { 119 | a += 0x06; 120 | } 121 | // If the most significant four bits of the accumulator now represent a number greater than 9, or if the normal 122 | // carry bit is equal to one, the most sign ificant four bits of the accumulator are incremented by six. 123 | if (msb > 9) || self.reg.get_flag(Flag::C) || (msb >= 9 && lsb > 9) { 124 | a += 0x60; 125 | c = true; 126 | } 127 | self.alu_add(a); 128 | self.reg.set_flag(Flag::C, c); 129 | } 130 | 131 | fn alu_add(&mut self, n: u8) { 132 | let a = self.reg.a; 133 | let r = a.wrapping_add(n); 134 | self.reg.set_flag(Flag::S, bit::get(r, 7)); 135 | self.reg.set_flag(Flag::Z, r == 0x00); 136 | self.reg.set_flag(Flag::A, (a & 0x0f) + (n & 0x0f) > 0x0f); 137 | self.reg.set_flag(Flag::P, r.count_ones() & 0x01 == 0x00); 138 | self.reg.set_flag(Flag::C, u16::from(a) + u16::from(n) > 0xff); 139 | self.reg.a = r; 140 | } 141 | 142 | fn alu_adc(&mut self, n: u8) { 143 | let c = u8::from(self.reg.get_flag(Flag::C)); 144 | let a = self.reg.a; 145 | let r = a.wrapping_add(n).wrapping_add(c); 146 | self.reg.set_flag(Flag::S, bit::get(r, 7)); 147 | self.reg.set_flag(Flag::Z, r == 0x00); 148 | self.reg.set_flag(Flag::A, (a & 0x0f) + (n & 0x0f) + c > 0x0f); 149 | self.reg.set_flag(Flag::P, r.count_ones() & 0x01 == 0x00); 150 | self.reg.set_flag(Flag::C, u16::from(a) + u16::from(n) + u16::from(c) > 0xff); 151 | self.reg.a = r; 152 | } 153 | 154 | fn alu_sub(&mut self, n: u8) { 155 | let a = self.reg.a; 156 | let r = a.wrapping_sub(n); 157 | self.reg.set_flag(Flag::S, bit::get(r, 7)); 158 | self.reg.set_flag(Flag::Z, r == 0x00); 159 | self.reg.set_flag(Flag::A, (a as i8 & 0x0f) - (n as i8 & 0x0f) >= 0x00); 160 | self.reg.set_flag(Flag::P, r.count_ones() & 0x01 == 0x00); 161 | self.reg.set_flag(Flag::C, u16::from(a) < u16::from(n)); 162 | self.reg.a = r; 163 | } 164 | 165 | fn alu_sbb(&mut self, n: u8) { 166 | let c = u8::from(self.reg.get_flag(Flag::C)); 167 | let a = self.reg.a; 168 | let r = a.wrapping_sub(n).wrapping_sub(c); 169 | self.reg.set_flag(Flag::S, bit::get(r, 7)); 170 | self.reg.set_flag(Flag::Z, r == 0x00); 171 | self.reg.set_flag(Flag::A, (a as i8 & 0x0f) - (n as i8 & 0x0f) - (c as i8) >= 0x00); 172 | self.reg.set_flag(Flag::P, r.count_ones() & 0x01 == 0x00); 173 | self.reg.set_flag(Flag::C, u16::from(a) < u16::from(n) + u16::from(c)); 174 | self.reg.a = r; 175 | } 176 | 177 | fn alu_ana(&mut self, n: u8) { 178 | let r = self.reg.a & n; 179 | self.reg.set_flag(Flag::S, bit::get(r, 7)); 180 | self.reg.set_flag(Flag::Z, r == 0x00); 181 | self.reg.set_flag(Flag::A, ((self.reg.a | n) & 0x08) != 0); 182 | self.reg.set_flag(Flag::P, r.count_ones() & 0x01 == 0x00); 183 | self.reg.set_flag(Flag::C, false); 184 | self.reg.a = r; 185 | } 186 | 187 | fn alu_xra(&mut self, n: u8) { 188 | let r = self.reg.a ^ n; 189 | self.reg.set_flag(Flag::S, bit::get(r, 7)); 190 | self.reg.set_flag(Flag::Z, r == 0x00); 191 | self.reg.set_flag(Flag::A, false); 192 | self.reg.set_flag(Flag::P, r.count_ones() & 0x01 == 0x00); 193 | self.reg.set_flag(Flag::C, false); 194 | self.reg.a = r; 195 | } 196 | 197 | fn alu_ora(&mut self, n: u8) { 198 | let r = self.reg.a | n; 199 | self.reg.set_flag(Flag::S, bit::get(r, 7)); 200 | self.reg.set_flag(Flag::Z, r == 0x00); 201 | self.reg.set_flag(Flag::A, false); 202 | self.reg.set_flag(Flag::P, r.count_ones() & 0x01 == 0x00); 203 | self.reg.set_flag(Flag::C, false); 204 | self.reg.a = r; 205 | } 206 | 207 | fn alu_cmp(&mut self, n: u8) { 208 | let r = self.reg.a; 209 | self.alu_sub(n); 210 | self.reg.a = r; 211 | } 212 | 213 | fn alu_rlc(&mut self) { 214 | let c = bit::get(self.reg.a, 7); 215 | let r = (self.reg.a << 1) | u8::from(c); 216 | self.reg.set_flag(Flag::C, c); 217 | self.reg.a = r; 218 | } 219 | 220 | fn alu_rrc(&mut self) { 221 | let c = bit::get(self.reg.a, 0); 222 | let r = if c { 0x80 | (self.reg.a >> 1) } else { self.reg.a >> 1 }; 223 | self.reg.set_flag(Flag::C, c); 224 | self.reg.a = r; 225 | } 226 | 227 | fn alu_ral(&mut self) { 228 | let c = bit::get(self.reg.a, 7); 229 | let r = (self.reg.a << 1) | u8::from(self.reg.get_flag(Flag::C)); 230 | self.reg.set_flag(Flag::C, c); 231 | self.reg.a = r; 232 | } 233 | 234 | fn alu_rar(&mut self) { 235 | let c = bit::get(self.reg.a, 0); 236 | let r = if self.reg.get_flag(Flag::C) { 0x80 | (self.reg.a >> 1) } else { self.reg.a >> 1 }; 237 | self.reg.set_flag(Flag::C, c); 238 | self.reg.a = r; 239 | } 240 | 241 | fn alu_dad(&mut self, n: u16) { 242 | let a = self.reg.get_hl(); 243 | let r = a.wrapping_add(n); 244 | self.reg.set_flag(Flag::C, a > 0xffff - n); 245 | self.reg.set_hl(r); 246 | } 247 | 248 | pub fn next(&mut self) -> u32 { 249 | if self.halted { 250 | return 0; 251 | } 252 | let opcode = self.imm_ds(); 253 | let opcode = match opcode { 254 | 0x08 | 0x10 | 0x18 | 0x20 | 0x28 | 0x30 | 0x38 => 0x00, 255 | 0xcb => 0xc3, 256 | 0xd9 => 0xc9, 257 | 0xdd | 0xed | 0xfd => 0xcd, 258 | _ => opcode, 259 | }; 260 | 261 | debugln!( 262 | "{} PC={:04x} SP={:04x} A={:02x} F={:02x} B={:02x} C={:02x} D={:02x} E={:02x} H={:02x} L={:02x}", 263 | asm::asm(opcode), 264 | self.reg.pc.wrapping_sub(1), 265 | self.reg.sp, 266 | self.reg.a, 267 | self.reg.f, 268 | self.reg.b, 269 | self.reg.c, 270 | self.reg.d, 271 | self.reg.e, 272 | self.reg.h, 273 | self.reg.l 274 | ); 275 | 276 | let mut ecycle = 0; 277 | match opcode { 278 | // CARRY BIT INSTRUCTIONS 279 | 0x3f => self.reg.set_flag(Flag::C, !self.reg.get_flag(Flag::C)), 280 | 0x37 => self.reg.set_flag(Flag::C, true), 281 | 282 | // INR Increment Register or Memory 283 | 0x04 => self.reg.b = self.alu_inr(self.reg.b), 284 | 0x0c => self.reg.c = self.alu_inr(self.reg.c), 285 | 0x14 => self.reg.d = self.alu_inr(self.reg.d), 286 | 0x1c => self.reg.e = self.alu_inr(self.reg.e), 287 | 0x24 => self.reg.h = self.alu_inr(self.reg.h), 288 | 0x2c => self.reg.l = self.alu_inr(self.reg.l), 289 | 0x34 => { 290 | let a = self.get_m(); 291 | let b = self.alu_inr(a); 292 | self.set_m(b); 293 | } 294 | 0x3c => self.reg.a = self.alu_inr(self.reg.a), 295 | 296 | // DCR Decrement Register or Memory 297 | 0x05 => self.reg.b = self.alu_dcr(self.reg.b), 298 | 0x0d => self.reg.c = self.alu_dcr(self.reg.c), 299 | 0x15 => self.reg.d = self.alu_dcr(self.reg.d), 300 | 0x1d => self.reg.e = self.alu_dcr(self.reg.e), 301 | 0x25 => self.reg.h = self.alu_dcr(self.reg.h), 302 | 0x2d => self.reg.l = self.alu_dcr(self.reg.l), 303 | 0x35 => { 304 | let a = self.get_m(); 305 | let b = self.alu_dcr(a); 306 | self.set_m(b); 307 | } 308 | 0x3d => self.reg.a = self.alu_dcr(self.reg.a), 309 | 310 | // CMA Complement Accumulator 311 | 0x2f => self.reg.a = !self.reg.a, 312 | 313 | // DAA Decimal Adjust Accumulator 314 | 0x27 => self.alu_daa(), 315 | 316 | // NOP INSTRUCTIONS 317 | 0x00 => {} 318 | 319 | // MOV Instruction 320 | 0x40 => {} 321 | 0x41 => self.reg.b = self.reg.c, 322 | 0x42 => self.reg.b = self.reg.d, 323 | 0x43 => self.reg.b = self.reg.e, 324 | 0x44 => self.reg.b = self.reg.h, 325 | 0x45 => self.reg.b = self.reg.l, 326 | 0x46 => self.reg.b = self.get_m(), 327 | 0x47 => self.reg.b = self.reg.a, 328 | 0x48 => self.reg.c = self.reg.b, 329 | 0x49 => {} 330 | 0x4a => self.reg.c = self.reg.d, 331 | 0x4b => self.reg.c = self.reg.e, 332 | 0x4c => self.reg.c = self.reg.h, 333 | 0x4d => self.reg.c = self.reg.l, 334 | 0x4e => self.reg.c = self.get_m(), 335 | 0x4f => self.reg.c = self.reg.a, 336 | 0x50 => self.reg.d = self.reg.b, 337 | 0x51 => self.reg.d = self.reg.c, 338 | 0x52 => {} 339 | 0x53 => self.reg.d = self.reg.e, 340 | 0x54 => self.reg.d = self.reg.h, 341 | 0x55 => self.reg.d = self.reg.l, 342 | 0x56 => self.reg.d = self.get_m(), 343 | 0x57 => self.reg.d = self.reg.a, 344 | 0x58 => self.reg.e = self.reg.b, 345 | 0x59 => self.reg.e = self.reg.c, 346 | 0x5a => self.reg.e = self.reg.d, 347 | 0x5b => {} 348 | 0x5c => self.reg.e = self.reg.h, 349 | 0x5d => self.reg.e = self.reg.l, 350 | 0x5e => self.reg.e = self.get_m(), 351 | 0x5f => self.reg.e = self.reg.a, 352 | 0x60 => self.reg.h = self.reg.b, 353 | 0x61 => self.reg.h = self.reg.c, 354 | 0x62 => self.reg.h = self.reg.d, 355 | 0x63 => self.reg.h = self.reg.e, 356 | 0x64 => {} 357 | 0x65 => self.reg.h = self.reg.l, 358 | 0x66 => self.reg.h = self.get_m(), 359 | 0x67 => self.reg.h = self.reg.a, 360 | 0x68 => self.reg.l = self.reg.b, 361 | 0x69 => self.reg.l = self.reg.c, 362 | 0x6a => self.reg.l = self.reg.d, 363 | 0x6b => self.reg.l = self.reg.e, 364 | 0x6c => self.reg.l = self.reg.h, 365 | 0x6d => {} 366 | 0x6e => self.reg.l = self.get_m(), 367 | 0x6f => self.reg.l = self.reg.a, 368 | 0x70 => self.set_m(self.reg.b), 369 | 0x71 => self.set_m(self.reg.c), 370 | 0x72 => self.set_m(self.reg.d), 371 | 0x73 => self.set_m(self.reg.e), 372 | 0x74 => self.set_m(self.reg.h), 373 | 0x75 => self.set_m(self.reg.l), 374 | 0x77 => self.set_m(self.reg.a), 375 | 0x78 => self.reg.a = self.reg.b, 376 | 0x79 => self.reg.a = self.reg.c, 377 | 0x7a => self.reg.a = self.reg.d, 378 | 0x7b => self.reg.a = self.reg.e, 379 | 0x7c => self.reg.a = self.reg.h, 380 | 0x7d => self.reg.a = self.reg.l, 381 | 0x7e => self.reg.a = self.get_m(), 382 | 0x7f => {} 383 | 384 | // STAX Store Accumulator 385 | 0x02 => self.mem.borrow_mut().set(self.reg.get_bc(), self.reg.a), 386 | 0x12 => self.mem.borrow_mut().set(self.reg.get_de(), self.reg.a), 387 | 388 | // LDAX Load Accumulator 389 | 0x0a => self.reg.a = self.mem.borrow().get(self.reg.get_bc()), 390 | 0x1a => self.reg.a = self.mem.borrow().get(self.reg.get_de()), 391 | 392 | // ADD ADD Register or Memory To Accumulator 393 | 0x80 => self.alu_add(self.reg.b), 394 | 0x81 => self.alu_add(self.reg.c), 395 | 0x82 => self.alu_add(self.reg.d), 396 | 0x83 => self.alu_add(self.reg.e), 397 | 0x84 => self.alu_add(self.reg.h), 398 | 0x85 => self.alu_add(self.reg.l), 399 | 0x86 => self.alu_add(self.get_m()), 400 | 0x87 => self.alu_add(self.reg.a), 401 | 402 | // ADC ADD Register or Memory To Accumulator With Carry 403 | 0x88 => self.alu_adc(self.reg.b), 404 | 0x89 => self.alu_adc(self.reg.c), 405 | 0x8a => self.alu_adc(self.reg.d), 406 | 0x8b => self.alu_adc(self.reg.e), 407 | 0x8c => self.alu_adc(self.reg.h), 408 | 0x8d => self.alu_adc(self.reg.l), 409 | 0x8e => self.alu_adc(self.get_m()), 410 | 0x8f => self.alu_adc(self.reg.a), 411 | 412 | // SUB Subtract Register or Memory From Accumulator 413 | 0x90 => self.alu_sub(self.reg.b), 414 | 0x91 => self.alu_sub(self.reg.c), 415 | 0x92 => self.alu_sub(self.reg.d), 416 | 0x93 => self.alu_sub(self.reg.e), 417 | 0x94 => self.alu_sub(self.reg.h), 418 | 0x95 => self.alu_sub(self.reg.l), 419 | 0x96 => self.alu_sub(self.get_m()), 420 | 0x97 => self.alu_sub(self.reg.a), 421 | 422 | // SBB Subtract Register or Memory From Accumulator With Borrow 423 | 0x98 => self.alu_sbb(self.reg.b), 424 | 0x99 => self.alu_sbb(self.reg.c), 425 | 0x9a => self.alu_sbb(self.reg.d), 426 | 0x9b => self.alu_sbb(self.reg.e), 427 | 0x9c => self.alu_sbb(self.reg.h), 428 | 0x9d => self.alu_sbb(self.reg.l), 429 | 0x9e => self.alu_sbb(self.get_m()), 430 | 0x9f => self.alu_sbb(self.reg.a), 431 | 432 | // ANA Logical and Register or Memory With Accumulator 433 | 0xa0 => self.alu_ana(self.reg.b), 434 | 0xa1 => self.alu_ana(self.reg.c), 435 | 0xa2 => self.alu_ana(self.reg.d), 436 | 0xa3 => self.alu_ana(self.reg.e), 437 | 0xa4 => self.alu_ana(self.reg.h), 438 | 0xa5 => self.alu_ana(self.reg.l), 439 | 0xa6 => self.alu_ana(self.get_m()), 440 | 0xa7 => self.alu_ana(self.reg.a), 441 | 442 | // XRA Logical Exclusive-Or Register or Memory With Accumulator (Zero Accumulator) 443 | 0xa8 => self.alu_xra(self.reg.b), 444 | 0xa9 => self.alu_xra(self.reg.c), 445 | 0xaa => self.alu_xra(self.reg.d), 446 | 0xab => self.alu_xra(self.reg.e), 447 | 0xac => self.alu_xra(self.reg.h), 448 | 0xad => self.alu_xra(self.reg.l), 449 | 0xae => self.alu_xra(self.get_m()), 450 | 0xaf => self.alu_xra(self.reg.a), 451 | 452 | // ORA Logical or Register or Memory With Accumulator 453 | 0xb0 => self.alu_ora(self.reg.b), 454 | 0xb1 => self.alu_ora(self.reg.c), 455 | 0xb2 => self.alu_ora(self.reg.d), 456 | 0xb3 => self.alu_ora(self.reg.e), 457 | 0xb4 => self.alu_ora(self.reg.h), 458 | 0xb5 => self.alu_ora(self.reg.l), 459 | 0xb6 => self.alu_ora(self.get_m()), 460 | 0xb7 => self.alu_ora(self.reg.a), 461 | 462 | // CMP Compare Register or Memory With Accumulator 463 | 0xb8 => self.alu_cmp(self.reg.b), 464 | 0xb9 => self.alu_cmp(self.reg.c), 465 | 0xba => self.alu_cmp(self.reg.d), 466 | 0xbb => self.alu_cmp(self.reg.e), 467 | 0xbc => self.alu_cmp(self.reg.h), 468 | 0xbd => self.alu_cmp(self.reg.l), 469 | 0xbe => self.alu_cmp(self.get_m()), 470 | 0xbf => self.alu_cmp(self.reg.a), 471 | 472 | // RLC Rotate Accumulator Left 473 | 0x07 => self.alu_rlc(), 474 | 475 | // RRC Rotate Accumulator Right 476 | 0x0f => self.alu_rrc(), 477 | 478 | // RAL Rotate Accumulator Left Through Carry 479 | 0x17 => self.alu_ral(), 480 | 481 | // RAR Rotate Accumulator Right Through Carry 482 | 0x1f => self.alu_rar(), 483 | 484 | // PUSH Push Data Onto Stack 485 | 0xc5 => self.stack_add(self.reg.get_bc()), 486 | 0xd5 => self.stack_add(self.reg.get_de()), 487 | 0xe5 => self.stack_add(self.reg.get_hl()), 488 | 0xf5 => self.stack_add(self.reg.get_af()), 489 | 490 | // POP Pop Data Off Stack 491 | 0xc1 => { 492 | let a = self.stack_pop(); 493 | self.reg.set_bc(a); 494 | } 495 | 0xd1 => { 496 | let a = self.stack_pop(); 497 | self.reg.set_de(a); 498 | } 499 | 0xe1 => { 500 | let a = self.stack_pop(); 501 | self.reg.set_hl(a); 502 | } 503 | 0xf1 => { 504 | let a = self.stack_pop(); 505 | self.reg.set_af(a); 506 | } 507 | 508 | // DAD Double Add 509 | 0x09 => self.alu_dad(self.reg.get_bc()), 510 | 0x19 => self.alu_dad(self.reg.get_de()), 511 | 0x29 => self.alu_dad(self.reg.get_hl()), 512 | 0x39 => self.alu_dad(self.reg.sp), 513 | 514 | // INX Increment Register Pair 515 | 0x03 => self.reg.set_bc(self.reg.get_bc().wrapping_add(1)), 516 | 0x13 => self.reg.set_de(self.reg.get_de().wrapping_add(1)), 517 | 0x23 => self.reg.set_hl(self.reg.get_hl().wrapping_add(1)), 518 | 0x33 => self.reg.sp = self.reg.sp.wrapping_add(1), 519 | 520 | // DCX Decrement Register Pair 521 | 0x0b => self.reg.set_bc(self.reg.get_bc().wrapping_sub(1)), 522 | 0x1b => self.reg.set_de(self.reg.get_de().wrapping_sub(1)), 523 | 0x2b => self.reg.set_hl(self.reg.get_hl().wrapping_sub(1)), 524 | 0x3b => self.reg.sp = self.reg.sp.wrapping_sub(1), 525 | 526 | // XCHG Exchange Registers 527 | 0xeb => { 528 | mem::swap(&mut self.reg.h, &mut self.reg.d); 529 | mem::swap(&mut self.reg.l, &mut self.reg.e); 530 | } 531 | 532 | // XTHL Exchange Stack 533 | 0xe3 => { 534 | let a = self.mem.borrow().get_word(self.reg.sp); 535 | let b = self.reg.get_hl(); 536 | self.reg.set_hl(a); 537 | self.mem.borrow_mut().set_word(self.reg.sp, b) 538 | } 539 | 540 | // SPHL Load SP From H And L 541 | 0xf9 => self.reg.sp = self.reg.get_hl(), 542 | 543 | // LXI Load Immediate Data 544 | 0x01 => { 545 | let a = self.imm_dw(); 546 | self.reg.set_bc(a); 547 | } 548 | 0x11 => { 549 | let a = self.imm_dw(); 550 | self.reg.set_de(a); 551 | } 552 | 0x21 => { 553 | let a = self.imm_dw(); 554 | self.reg.set_hl(a); 555 | } 556 | 0x31 => { 557 | let a = self.imm_dw(); 558 | self.reg.sp = a; 559 | } 560 | 561 | // MVI Move Immediate Data 562 | 0x06 => self.reg.b = self.imm_ds(), 563 | 0x0e => self.reg.c = self.imm_ds(), 564 | 0x16 => self.reg.d = self.imm_ds(), 565 | 0x1e => self.reg.e = self.imm_ds(), 566 | 0x26 => self.reg.h = self.imm_ds(), 567 | 0x2e => self.reg.l = self.imm_ds(), 568 | 0x36 => { 569 | let a = self.imm_ds(); 570 | self.set_m(a); 571 | } 572 | 0x3e => self.reg.a = self.imm_ds(), 573 | 574 | // ADI Add Immediate To Accumulator 575 | 0xc6 => { 576 | let a = self.imm_ds(); 577 | self.alu_add(a); 578 | } 579 | 580 | // ACI Add Immediate To Accumulator With Carry 581 | 0xce => { 582 | let a = self.imm_ds(); 583 | self.alu_adc(a); 584 | } 585 | 586 | // SUI Subtract Immediate From Accumulator 587 | 0xd6 => { 588 | let a = self.imm_ds(); 589 | self.alu_sub(a); 590 | } 591 | 592 | // SBI Subtract Immediate from Accumulator With Borrow 593 | 0xde => { 594 | let v = self.imm_ds(); 595 | self.alu_sbb(v); 596 | } 597 | 598 | // ANI And Immediate With AccumulatorLabel 599 | 0xe6 => { 600 | let a = self.imm_ds(); 601 | self.alu_ana(a); 602 | } 603 | 604 | // XRI Exclusive-Or Immediate With Accumulator 605 | 0xee => { 606 | let a = self.imm_ds(); 607 | self.alu_xra(a); 608 | } 609 | 610 | // ORI Or Immediate With Accumulator 611 | 0xf6 => { 612 | let a = self.imm_ds(); 613 | self.alu_ora(a); 614 | } 615 | 616 | // CPI Compare Immediate With Accumulator 617 | 0xfe => { 618 | let a = self.imm_ds(); 619 | self.alu_cmp(a); 620 | } 621 | 622 | // STA Store Accumulator Direct 623 | 0x32 => { 624 | let a = self.imm_dw(); 625 | self.mem.borrow_mut().set(a, self.reg.a); 626 | } 627 | 628 | // LDA Load Accumulator Direct 629 | 0x3a => { 630 | let a = self.imm_dw(); 631 | let b = self.mem.borrow().get(a); 632 | self.reg.a = b; 633 | } 634 | 635 | // SHLD Store Hand L Direct 636 | 0x22 => { 637 | let a = self.imm_dw(); 638 | self.mem.borrow_mut().set_word(a, self.reg.get_hl()); 639 | } 640 | 641 | // LHLD Load HAnd L Direct 642 | 0x2a => { 643 | let a = self.imm_dw(); 644 | let b = self.mem.borrow().get_word(a); 645 | self.reg.set_hl(b); 646 | } 647 | 648 | // PCHL Load Program Counter 649 | 0xe9 => self.reg.pc = self.reg.get_hl(), 650 | 651 | // JUMP INSTRUCTIONS 652 | 0xc3 | 0xda | 0xd2 | 0xca | 0xc2 | 0xfa | 0xf2 | 0xea | 0xe2 => { 653 | let a = self.imm_dw(); 654 | let cond = match opcode { 655 | // JMP JUMP 656 | 0xc3 => true, 657 | // JC Jump If Carry 658 | 0xda => self.reg.get_flag(Flag::C), 659 | // JNC Jump If No Carry 660 | 0xd2 => !self.reg.get_flag(Flag::C), 661 | // JZ Jump If Zero 662 | 0xca => self.reg.get_flag(Flag::Z), 663 | // JNZ Jump If Not Zero 664 | 0xc2 => !self.reg.get_flag(Flag::Z), 665 | // JM Jump If Minus 666 | 0xfa => self.reg.get_flag(Flag::S), 667 | // JP Jump If Positive 668 | 0xf2 => !self.reg.get_flag(Flag::S), 669 | // JPE Jump If Parity Even 670 | 0xea => self.reg.get_flag(Flag::P), 671 | // JPO Jump If Parity Odd 672 | 0xe2 => !self.reg.get_flag(Flag::P), 673 | _ => unimplemented!(), 674 | }; 675 | if cond { 676 | self.reg.pc = a; 677 | } 678 | } 679 | 680 | // CALL SUBROUTINE INSTRUCTIONS 681 | 0xcd | 0xdc | 0xd4 | 0xcc | 0xc4 | 0xfc | 0xf4 | 0xec | 0xe4 => { 682 | let a = self.imm_dw(); 683 | let cond = match opcode { 684 | // CALL Call 685 | 0xcd => true, 686 | // CC Call If Carry 687 | 0xdc => self.reg.get_flag(Flag::C), 688 | // CNC Call If No Carry 689 | 0xd4 => !self.reg.get_flag(Flag::C), 690 | // CZ Call If Zero 691 | 0xcc => self.reg.get_flag(Flag::Z), 692 | // CNZ Call If Not Zero 693 | 0xc4 => !self.reg.get_flag(Flag::Z), 694 | // CM Call If Minus 695 | 0xfc => self.reg.get_flag(Flag::S), 696 | // CP Call If Plus 697 | 0xf4 => !self.reg.get_flag(Flag::S), 698 | // CPE Call If Parity Even 699 | 0xec => self.reg.get_flag(Flag::P), 700 | // CPO Call If Parity Odd 701 | 0xe4 => !self.reg.get_flag(Flag::P), 702 | _ => unimplemented!(), 703 | }; 704 | if cond { 705 | ecycle = 6; 706 | self.stack_add(self.reg.pc); 707 | self.reg.pc = a; 708 | } 709 | } 710 | 711 | // RETURN FROM SUBROUTINE INSTRUCTIONS 712 | 0xc9 | 0xd8 | 0xd0 | 0xc8 | 0xc0 | 0xf8 | 0xf0 | 0xe8 | 0xe0 => { 713 | let cond = match opcode { 714 | // RET Return 715 | 0xc9 => true, 716 | // RC Return If Carry 717 | 0xd8 => self.reg.get_flag(Flag::C), 718 | // RNC Return If No Carry 719 | 0xd0 => !self.reg.get_flag(Flag::C), 720 | // RZ Return If Zero 721 | 0xc8 => self.reg.get_flag(Flag::Z), 722 | // RNZ Return If Not Zero 723 | 0xc0 => !self.reg.get_flag(Flag::Z), 724 | // RM Return If Minus 725 | 0xf8 => self.reg.get_flag(Flag::S), 726 | // RP Return If Plus 727 | 0xf0 => !self.reg.get_flag(Flag::S), 728 | // RPE Return If Parity Even 729 | 0xe8 => self.reg.get_flag(Flag::P), 730 | // RPO Return If Parity Odd 731 | 0xe0 => !self.reg.get_flag(Flag::P), 732 | _ => unimplemented!(), 733 | }; 734 | if cond { 735 | ecycle = 6; 736 | self.reg.pc = self.stack_pop() 737 | } 738 | } 739 | 740 | // RST INSTRUCTION 741 | 0xc7 | 0xcf | 0xd7 | 0xdf | 0xe7 | 0xef | 0xf7 | 0xff => { 742 | self.stack_add(self.reg.pc); 743 | self.reg.pc = u16::from(opcode & 0x38); 744 | } 745 | 746 | // INTERRUPT FLIP-FLOP INSTRUCTIONS 747 | 0xfb => self.inte = true, 748 | 0xf3 => self.inte = false, 749 | 750 | // INPUT/OUTPUT INSTRUCTIONS 751 | // Note: You need to implement this instr yourself 752 | 0xdb => { 753 | let _ = self.imm_ds(); 754 | } 755 | 0xd3 => { 756 | let _ = self.imm_ds(); 757 | } 758 | 759 | // HLT HALT INSTRUCTION 760 | 0x76 => self.halted = true, 761 | _ => {} 762 | }; 763 | 764 | OP_CYCLES[opcode as usize] + ecycle 765 | } 766 | 767 | pub fn step(&mut self) -> u32 { 768 | if self.step_cycles > STEP_CYCLES { 769 | self.step_cycles -= STEP_CYCLES; 770 | let d = time::SystemTime::now().duration_since(self.step_zero).unwrap(); 771 | let s = u64::from(STEP_TIME.saturating_sub(d.as_millis() as u32)); 772 | debugln!("CPU: sleep {} millis", s); 773 | thread::sleep(time::Duration::from_millis(s)); 774 | self.step_zero = self.step_zero.checked_add(time::Duration::from_millis(u64::from(STEP_TIME))).unwrap(); 775 | } 776 | let cycles = self.next(); 777 | self.step_cycles += cycles; 778 | cycles 779 | } 780 | 781 | pub fn inte_handle(&mut self, addr: u16) { 782 | if self.inte { 783 | self.inte = false; 784 | self.stack_add(self.reg.pc); 785 | self.reg.pc = addr; 786 | self.step_cycles += OP_CYCLES[0xcd]; 787 | } 788 | } 789 | } 790 | --------------------------------------------------------------------------------