1use crate::Z80; 2use std::borrow::Cow; 3use std::cmp::Ordering; 4use std::mem; 5 6#[derive(Debug, Clone, Copy, PartialEq, Eq)] 7enum IndexRegister { 8 Ix, 9 Iy, 10} 11 12impl IndexRegister { 13 fn label(self) -> &'static str { 14 match self { 15 Self::Ix => "ix", 16 Self::Iy => "iy", 17 } 18 } 19 20 fn indirect_with_offset(self, offset: i8) -> String { 21 match offset.cmp(&0) { 22 Ordering::Equal => match self { 23 Self::Ix => "(ix)".into(), 24 Self::Iy => "(iy)".into(), 25 }, 26 Ordering::Greater => match self { 27 Self::Ix => format!("(ix+{offset})"), 28 Self::Iy => format!("(iy+{offset})"), 29 }, 30 Ordering::Less => match self { 31 Self::Ix => format!("(ix{offset})"), 32 Self::Iy => format!("(iy{offset})"), 33 }, 34 } 35 } 36 37 fn high_label(self) -> &'static str { 38 match self { 39 Self::Ix => "ixh", 40 Self::Iy => "iyh", 41 } 42 } 43 44 fn low_label(self) -> &'static str { 45 match self { 46 Self::Ix => "ixl", 47 Self::Iy => "iyl", 48 } 49 } 50} 51 52#[derive(Debug, Clone, Copy, PartialEq, Eq)] 53struct IndexWithOffset(IndexRegister, i8); 54 55impl IndexWithOffset { 56 fn indirect_with_offset(self) -> String { 57 self.0.indirect_with_offset(self.1) 58 } 59} 60 61struct ByteReader<F> { 62 pc: u16, 63 reader: F, 64 opcodes: Vec<u8>, 65} 66 67impl<F: FnMut() -> u8> ByteReader<F> { 68 fn read_byte(&mut self) -> u8 { 69 let opcode = (self.reader)(); 70 71 self.pc = self.pc.wrapping_add(1); 72 self.opcodes.push(opcode); 73 74 opcode 75 } 76 77 fn read_word(&mut self) -> u16 { 78 let lsb = self.read_byte(); 79 let msb = self.read_byte(); 80 u16::from_le_bytes([lsb, msb]) 81 } 82} 83 84#[derive(Debug, Clone, Copy, PartialEq, Eq)] 85pub enum IndirectRegister { 86 Bc, 87 De, 88 Hl, 89 Sp, 90 Ix { displacement: i8 }, 91 Iy { displacement: i8 }, 92} 93 94impl IndirectRegister { 95 fn from_index(index: IndexRegister, displacement: i8) -> Self { 96 match index { 97 IndexRegister::Ix => Self::Ix { displacement }, 98 IndexRegister::Iy => Self::Iy { displacement }, 99 } 100 } 101} 102 103#[derive(Debug, Clone, Copy, PartialEq, Eq)] 104pub enum MemoryAccess { 105 Absolute(u16), 106 Indirect(IndirectRegister), 107} 108 109impl MemoryAccess { 110 #[must_use] 111 pub fn resolve_address(self, cpu: &Z80) -> u16 { 112 match self { 113 Self::Absolute(address) => address, 114 Self::Indirect(register) => { 115 let registers = cpu.registers(); 116 match register { 117 IndirectRegister::Bc => u16::from_be_bytes([registers.b, registers.c]), 118 IndirectRegister::De => u16::from_be_bytes([registers.d, registers.e]), 119 IndirectRegister::Hl => u16::from_be_bytes([registers.h, registers.l]), 120 IndirectRegister::Sp => registers.sp, 121 IndirectRegister::Ix { displacement } => { 122 registers.ix.wrapping_add_signed(displacement.into()) 123 } 124 IndirectRegister::Iy { displacement } => { 125 registers.iy.wrapping_add_signed(displacement.into()) 126 } 127 } 128 } 129 } 130 } 131} 132 133#[derive(Debug, Clone)] 134pub struct DisassembledInstruction { 135 pub opcodes: Vec<u8>, 136 pub text: String, 137 pub memory_access: Option<MemoryAccess>, 138} 139 140impl DisassembledInstruction { 141 #[must_use] 142 pub fn new() -> Self { 143 Self { opcodes: Vec::new(), text: String::new(), memory_access: None } 144 } 145 146 fn clear(&mut self) { 147 self.opcodes.clear(); 148 self.text.clear(); 149 self.memory_access = None; 150 } 151 152 #[must_use] 153 fn parse_memory_access(&self, index: Option<IndexRegister>) -> Option<MemoryAccess> { 154 let i = (0..self.opcodes.len()) 155 .find(|&i| self.opcodes[i] != 0xDD && self.opcodes[i] != 0xFD)?; 156 157 let opcode = self.opcodes[i]; 158 match opcode { 159 0x02 | 0x0A => { 160 // ld (bc), a 161 // ld a, (bc) 162 Some(MemoryAccess::Indirect(IndirectRegister::Bc)) 163 } 164 0x12 | 0x1A => { 165 // ld (de), a 166 // ld a, (de) 167 Some(MemoryAccess::Indirect(IndirectRegister::De)) 168 } 169 0x22 | 0x2A | 0x32 | 0x3A => { 170 // ld (nn), hl 171 // ld hl, (nn) 172 // ld (nn), a 173 // ld a, (nn) 174 let lsb = self.opcodes.get(i + 1).copied()?; 175 let msb = self.opcodes.get(i + 2).copied()?; 176 Some(MemoryAccess::Absolute(u16::from_le_bytes([lsb, msb]))) 177 } 178 0x34..=0x36 | 0x86 | 0x8E | 0x96 | 0x9E | 0xA6 | 0xAE | 0xB6 | 0xBE => { 179 // inc (hl) 180 // dec (hl) 181 // ld (hl), nn 182 // add a, (hl) 183 // adc a, (hl) 184 // sub (hl) 185 // sbc a, (hl) 186 // and (hl) 187 // xor (hl) 188 // or (hl) 189 // cp (hl) 190 let register = index.map_or(IndirectRegister::Hl, |index| { 191 let displacement = self.opcodes.get(i + 1).copied().unwrap_or(0) as i8; 192 IndirectRegister::from_index(index, displacement) 193 }); 194 Some(MemoryAccess::Indirect(register)) 195 } 196 0x40..=0x75 | 0x77..=0x7F => { 197 // ld r, (hl) 198 // ld (hl), r 199 if opcode & 7 != 6 && (opcode >> 3) & 7 != 6 { 200 return None; 201 } 202 let register = index.map_or(IndirectRegister::Hl, |index| { 203 let displacement = self.opcodes.get(i + 1).copied().unwrap_or(0) as i8; 204 IndirectRegister::from_index(index, displacement) 205 }); 206 Some(MemoryAccess::Indirect(register)) 207 } 208 0xE3 => { 209 // ex (sp), hl 210 Some(MemoryAccess::Indirect(IndirectRegister::Sp)) 211 } 212 0xCB => self.parse_memory_access_cb(i + 1, index), 213 0xED => self.parse_memory_access_ed(i + 1), 214 _ => None, 215 } 216 } 217 218 fn parse_memory_access_cb( 219 &self, 220 i: usize, 221 index: Option<IndexRegister>, 222 ) -> Option<MemoryAccess> { 223 if let Some(index) = index { 224 // All index-prefixed CB-prefixed instructions perform the same memory access 225 let displacement = self.opcodes.get(i).copied()? as i8; 226 return Some(MemoryAccess::Indirect(IndirectRegister::from_index(index, displacement))); 227 } 228 229 let opcode = self.opcodes.get(i).copied()?; 230 if opcode & 7 == 6 { 231 return Some(MemoryAccess::Indirect(IndirectRegister::Hl)); 232 } 233 234 None 235 } 236 237 fn parse_memory_access_ed(&self, i: usize) -> Option<MemoryAccess> { 238 let opcode = self.opcodes.get(i).copied()?; 239 240 match opcode { 241 0x43 | 0x4B | 0x53 | 0x5B | 0x63 | 0x6B | 0x73 | 0x7B => { 242 // ld (nn), rr 243 // ld rr, (nn) 244 let lsb = self.opcodes.get(i + 1).copied()?; 245 let msb = self.opcodes.get(i + 2).copied()?; 246 Some(MemoryAccess::Absolute(u16::from_le_bytes([lsb, msb]))) 247 } 248 0xA0 | 0xA1 | 0xA8 | 0xA9 | 0xB0 | 0xB1 | 0xB8 | 0xB9 => { 249 // ldi[r], cpi[r], ldd[r], cpd[r] 250 Some(MemoryAccess::Indirect(IndirectRegister::Hl)) 251 } 252 _ => None, 253 } 254 } 255} 256 257impl Default for DisassembledInstruction { 258 fn default() -> Self { 259 Self::new() 260 } 261} 262 263pub fn disassemble_into(out: &mut DisassembledInstruction, pc: u16, reader: impl FnMut() -> u8) { 264 out.clear(); 265 let mut reader = ByteReader { pc, reader, opcodes: mem::take(&mut out.opcodes) }; 266 267 let mut index: Option<IndexRegister> = None; 268 269 let opcode = loop { 270 match reader.read_byte() { 271 0xDD => index = Some(IndexRegister::Ix), 272 0xFD => index = Some(IndexRegister::Iy), 273 opcode => break opcode, 274 } 275 }; 276 277 out.text = match opcode { 278 0x00 => "nop".into(), 279 0x01 | 0x11 | 0x21 | 0x31 => ld_dd_immediate(opcode, index, &mut reader), 280 0x02 => "ld (bc), a".into(), 281 0x03 | 0x13 | 0x23 | 0x33 => inc_ss(opcode, index), 282 0x04 | 0x0C | 0x14 | 0x1C | 0x24 | 0x2C | 0x34 | 0x3C => inc_r(opcode, index, &mut reader), 283 0x05 | 0x0D | 0x15 | 0x1D | 0x25 | 0x2D | 0x35 | 0x3D => dec_r(opcode, index, &mut reader), 284 0x06 | 0x0E | 0x16 | 0x1E | 0x26 | 0x2E | 0x36 | 0x3E => { 285 ld_r_immediate(opcode, index, &mut reader) 286 } 287 0x07 => "rlca".into(), 288 0x08 => "ex af, af'".into(), 289 0x09 | 0x19 | 0x29 | 0x39 => add_hl_ss(opcode, index), 290 0x0A => "ld a, (bc)".into(), 291 0x0B | 0x1B | 0x2B | 0x3B => dec_ss(opcode, index), 292 0x0F => "rrca".into(), 293 0x10 => djnz(&mut reader), 294 0x12 => "ld (de), a".into(), 295 0x17 => "rla".into(), 296 0x18 => jr(&mut reader), 297 0x1A => "ld a, (de)".into(), 298 0x1F => "rra".into(), 299 0x20 | 0x28 | 0x30 | 0x38 => jr_cc(opcode, &mut reader), 300 0x22 => ld_direct_hl(index, &mut reader), 301 0x27 => "daa".into(), 302 0x2A => ld_hl_direct(index, &mut reader), 303 0x2F => "cpl".into(), 304 0x32 => ld_direct_a(&mut reader), 305 0x37 => "scf".into(), 306 0x3A => ld_a_direct(&mut reader), 307 0x3F => "ccf".into(), 308 0x40..=0x75 | 0x77..=0x7F => ld_r_r(opcode, index, &mut reader), 309 0x76 => "halt".into(), 310 0x80..=0x87 => add_a_r(opcode, index, &mut reader), 311 0x88..=0x8F => adc_a_r(opcode, index, &mut reader), 312 0x90..=0x97 => sub_a_r(opcode, index, &mut reader), 313 0x98..=0x9F => sbc_a_r(opcode, index, &mut reader), 314 0xA0..=0xA7 => and_a_r(opcode, index, &mut reader), 315 0xA8..=0xAF => xor_a_r(opcode, index, &mut reader), 316 0xB0..=0xB7 => or_a_r(opcode, index, &mut reader), 317 0xB8..=0xBF => cp_a_r(opcode, index, &mut reader), 318 0xC0 | 0xC8 | 0xD0 | 0xD8 | 0xE0 | 0xE8 | 0xF0 | 0xF8 => ret_cc(opcode), 319 0xC1 | 0xD1 | 0xE1 | 0xF1 => pop_qq(opcode, index), 320 0xC2 | 0xCA | 0xD2 | 0xDA | 0xE2 | 0xEA | 0xF2 | 0xFA => jp_cc_nn(opcode, &mut reader), 321 0xC3 => jp_nn(&mut reader), 322 0xC4 | 0xCC | 0xD4 | 0xDC | 0xE4 | 0xEC | 0xF4 | 0xFC => call_cc_nn(opcode, &mut reader), 323 0xC5 | 0xD5 | 0xE5 | 0xF5 => push_qq(opcode, index), 324 0xC6 => add_a_immediate(&mut reader), 325 0xC7 | 0xCF | 0xD7 | 0xDF | 0xE7 | 0xEF | 0xF7 | 0xFF => rst(opcode), 326 0xC9 => "ret".into(), 327 0xCD => call_nn(&mut reader), 328 0xCE => adc_a_immediate(&mut reader), 329 0xD3 => out_n_a(&mut reader), 330 0xD6 => sub_a_immediate(&mut reader), 331 0xD9 => "exx".into(), 332 0xDB => in_a_n(&mut reader), 333 0xDE => sbc_a_immediate(&mut reader), 334 0xE3 => ex_stack_hl(index), 335 0xE6 => and_a_immediate(&mut reader), 336 0xE9 => jp_hl(index), 337 0xEB => "ex de, hl".into(), 338 0xEE => xor_a_immediate(&mut reader), 339 0xF3 => "di".into(), 340 0xF6 => or_a_immediate(&mut reader), 341 0xF9 => ld_sp_hl(index), 342 0xFB => "ei".into(), 343 0xFE => cp_a_immediate(&mut reader), 344 0xCB => cb_prefix(index, &mut reader), 345 0xED => ed_prefix(&mut reader), 346 0xDD | 0xFD => unreachable!("would still be in above index register loop"), 347 }; 348 349 out.opcodes = mem::take(&mut reader.opcodes); 350 out.memory_access = out.parse_memory_access(index); 351} 352 353fn cb_prefix(index: Option<IndexRegister>, reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 354 // For DD+CB and FD+CB instructions, the index offset comes before the last opcode byte 355 let index = index.map(|index| { 356 let offset = reader.read_byte() as i8; 357 IndexWithOffset(index, offset) 358 }); 359 360 let opcode = reader.read_byte(); 361 362 match opcode { 363 0x00..=0x07 => shift_rotate_op("rlc", opcode, index), 364 0x08..=0x0F => shift_rotate_op("rrc", opcode, index), 365 0x10..=0x17 => shift_rotate_op("rl", opcode, index), 366 0x18..=0x1F => shift_rotate_op("rr", opcode, index), 367 0x20..=0x27 => shift_rotate_op("sla", opcode, index), 368 0x28..=0x2F => shift_rotate_op("sra", opcode, index), 369 0x30..=0x37 => shift_rotate_op("sll", opcode, index), 370 0x38..=0x3F => shift_rotate_op("srl", opcode, index), 371 0x40..=0x7F => bit_op("bit", opcode, index), 372 0x80..=0xBF => bit_op("res", opcode, index), 373 0xC0..=0xFF => bit_op("set", opcode, index), 374 } 375} 376 377fn ed_prefix(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 378 let opcode = reader.read_byte(); 379 380 match opcode { 381 0x40 | 0x48 | 0x50 | 0x58 | 0x60 | 0x68 | 0x70 | 0x78 => in_r_c(opcode), 382 0x41 | 0x49 | 0x51 | 0x59 | 0x61 | 0x69 | 0x71 | 0x79 => out_c_r(opcode), 383 0x42 | 0x52 | 0x62 | 0x72 => sbc_hl_ss(opcode), 384 0x43 | 0x53 | 0x63 | 0x73 => ld_direct_dd(opcode, reader), 385 0x44 => "neg".into(), 386 0x45 => "retn".into(), 387 0x46 => "im 0".into(), 388 0x47 => "ld i, a".into(), 389 0x4A | 0x5A | 0x6A | 0x7A => adc_hl_ss(opcode), 390 0x4B | 0x5B | 0x6B | 0x7B => ld_dd_direct(opcode, reader), 391 0x4F => "ld r, a".into(), 392 0x56 => "im 1".into(), 393 0x57 => "ld a, i".into(), 394 0x5E => "im 2".into(), 395 0x5F => "ld a, r".into(), 396 0x67 => "rrd".into(), 397 0x6F => "rld".into(), 398 0xA0 => "ldi".into(), 399 0xA1 => "cpi".into(), 400 0xA2 => "ini".into(), 401 0xA3 => "outi".into(), 402 0xA8 => "ldd".into(), 403 0xA9 => "cpd".into(), 404 0xAA => "ind".into(), 405 0xAB => "outd".into(), 406 0xB0 => "ldir".into(), 407 0xB1 => "cpir".into(), 408 0xB2 => "inir".into(), 409 0xB3 => "otir".into(), 410 0xB8 => "lddr".into(), 411 0xB9 => "cpdr".into(), 412 0xBA => "indr".into(), 413 0xBB => "otdr".into(), 414 _ => "illegal".into(), 415 } 416} 417 418fn r_register( 419 opcode: u8, 420 index: Option<IndexRegister>, 421 reader: &mut ByteReader<impl FnMut() -> u8>, 422) -> Cow<'static, str> { 423 match opcode & 7 { 424 0 => "b".into(), 425 1 => "c".into(), 426 2 => "d".into(), 427 3 => "e".into(), 428 4 => index.map_or("h", IndexRegister::high_label).into(), 429 5 => index.map_or("l", IndexRegister::low_label).into(), 430 6 => index 431 .map_or("(hl)".into(), |index| { 432 let offset = reader.read_byte() as i8; 433 index.indirect_with_offset(offset) 434 }) 435 .into(), 436 7 => "a".into(), 437 _ => unreachable!("value & 7 is always <= 7"), 438 } 439} 440 441fn dd_register(opcode: u8, index: Option<IndexRegister>) -> &'static str { 442 match (opcode >> 4) & 3 { 443 0 => "bc", 444 1 => "de", 445 2 => index.map_or("hl", IndexRegister::label), 446 3 => "sp", 447 _ => unreachable!("value & 3 is always <= 3"), 448 } 449} 450 451fn qq_register(opcode: u8, index: Option<IndexRegister>) -> &'static str { 452 match (opcode >> 4) & 3 { 453 0 => "bc", 454 1 => "de", 455 2 => index.map_or("hl", IndexRegister::label), 456 3 => "af", 457 _ => unreachable!("value & 3 is always <= 3"), 458 } 459} 460 461fn ld_dd_immediate( 462 opcode: u8, 463 index: Option<IndexRegister>, 464 reader: &mut ByteReader<impl FnMut() -> u8>, 465) -> String { 466 let register = dd_register(opcode, index); 467 let immediate = reader.read_word(); 468 format!("ld {register}, #0x{immediate:04X}") 469} 470 471fn inc_ss(opcode: u8, index: Option<IndexRegister>) -> String { 472 let register = dd_register(opcode, index); 473 format!("inc {register}") 474} 475 476fn dec_ss(opcode: u8, index: Option<IndexRegister>) -> String { 477 let register = dd_register(opcode, index); 478 format!("dec {register}") 479} 480 481fn inc_r( 482 opcode: u8, 483 index: Option<IndexRegister>, 484 reader: &mut ByteReader<impl FnMut() -> u8>, 485) -> String { 486 let register = r_register(opcode >> 3, index, reader); 487 format!("inc {register}") 488} 489 490fn dec_r( 491 opcode: u8, 492 index: Option<IndexRegister>, 493 reader: &mut ByteReader<impl FnMut() -> u8>, 494) -> String { 495 let register = r_register(opcode >> 3, index, reader); 496 format!("dec {register}") 497} 498 499fn ld_r_immediate( 500 opcode: u8, 501 index: Option<IndexRegister>, 502 reader: &mut ByteReader<impl FnMut() -> u8>, 503) -> String { 504 let register = r_register(opcode >> 3, index, reader); 505 let immediate = reader.read_byte(); 506 format!("ld {register}, #0x{immediate:02X}") 507} 508 509fn add_hl_ss(opcode: u8, index: Option<IndexRegister>) -> String { 510 let dest = index.map_or("hl", IndexRegister::label); 511 let source = dd_register(opcode, index); 512 format!("add {dest}, {source}") 513} 514 515fn djnz(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 516 let offset = reader.read_byte() as i8; 517 let address = reader.pc.wrapping_add_signed(offset.into()); 518 format!("djnz ${address:04X}") 519} 520 521fn jr(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 522 let offset = reader.read_byte() as i8; 523 let address = reader.pc.wrapping_add_signed(offset.into()); 524 format!("jr ${address:04X}") 525} 526 527fn jr_cc(opcode: u8, reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 528 let offset = reader.read_byte() as i8; 529 let address = reader.pc.wrapping_add_signed(offset.into()); 530 let condition = match (opcode >> 3) & 3 { 531 0 => "nz", 532 1 => "z", 533 2 => "nc", 534 3 => "c", 535 _ => unreachable!("value & 3 is always <= 3"), 536 }; 537 format!("jr {condition}, ${address:04X}") 538} 539 540fn ld_direct_hl( 541 index: Option<IndexRegister>, 542 reader: &mut ByteReader<impl FnMut() -> u8>, 543) -> String { 544 let address = reader.read_word(); 545 let register = index.map_or("hl", IndexRegister::label); 546 format!("ld (${address:04X}), {register}") 547} 548 549fn ld_hl_direct( 550 index: Option<IndexRegister>, 551 reader: &mut ByteReader<impl FnMut() -> u8>, 552) -> String { 553 let address = reader.read_word(); 554 let register = index.map_or("hl", IndexRegister::label); 555 format!("ld {register}, (${address:04X})") 556} 557 558fn ld_direct_a(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 559 let address = reader.read_word(); 560 format!("ld (${address:04X}), a") 561} 562 563fn ld_a_direct(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 564 let address = reader.read_word(); 565 format!("ld a, (${address:04X})") 566} 567 568fn ld_r_r( 569 opcode: u8, 570 index: Option<IndexRegister>, 571 reader: &mut ByteReader<impl FnMut() -> u8>, 572) -> String { 573 let dest_index = if (opcode & 7) != 6 { index } else { None }; 574 let dest = r_register(opcode >> 3, dest_index, reader); 575 576 let source_index = if ((opcode >> 3) & 7) != 6 { index } else { None }; 577 let source = r_register(opcode, source_index, reader); 578 579 format!("ld {dest}, {source}") 580} 581 582fn add_a_r( 583 opcode: u8, 584 index: Option<IndexRegister>, 585 reader: &mut ByteReader<impl FnMut() -> u8>, 586) -> String { 587 let source = r_register(opcode, index, reader); 588 format!("add a, {source}") 589} 590 591fn adc_a_r( 592 opcode: u8, 593 index: Option<IndexRegister>, 594 reader: &mut ByteReader<impl FnMut() -> u8>, 595) -> String { 596 let source = r_register(opcode, index, reader); 597 format!("adc a, {source}") 598} 599 600fn sub_a_r( 601 opcode: u8, 602 index: Option<IndexRegister>, 603 reader: &mut ByteReader<impl FnMut() -> u8>, 604) -> String { 605 let source = r_register(opcode, index, reader); 606 format!("sub {source}") 607} 608 609fn sbc_a_r( 610 opcode: u8, 611 index: Option<IndexRegister>, 612 reader: &mut ByteReader<impl FnMut() -> u8>, 613) -> String { 614 let source = r_register(opcode, index, reader); 615 format!("sbc a, {source}") 616} 617 618fn and_a_r( 619 opcode: u8, 620 index: Option<IndexRegister>, 621 reader: &mut ByteReader<impl FnMut() -> u8>, 622) -> String { 623 let source = r_register(opcode, index, reader); 624 format!("and {source}") 625} 626 627fn xor_a_r( 628 opcode: u8, 629 index: Option<IndexRegister>, 630 reader: &mut ByteReader<impl FnMut() -> u8>, 631) -> String { 632 let source = r_register(opcode, index, reader); 633 format!("xor {source}") 634} 635 636fn or_a_r( 637 opcode: u8, 638 index: Option<IndexRegister>, 639 reader: &mut ByteReader<impl FnMut() -> u8>, 640) -> String { 641 let source = r_register(opcode, index, reader); 642 format!("or {source}") 643} 644 645fn cp_a_r( 646 opcode: u8, 647 index: Option<IndexRegister>, 648 reader: &mut ByteReader<impl FnMut() -> u8>, 649) -> String { 650 let source = r_register(opcode, index, reader); 651 format!("cp {source}") 652} 653 654fn jump_condition(opcode: u8) -> &'static str { 655 match (opcode >> 3) & 7 { 656 0 => "nz", 657 1 => "z", 658 2 => "nc", 659 3 => "c", 660 4 => "po", 661 5 => "pe", 662 6 => "p", 663 7 => "m", 664 _ => unreachable!("value & 7 is <= 7"), 665 } 666} 667 668fn ret_cc(opcode: u8) -> String { 669 let condition = jump_condition(opcode); 670 format!("ret {condition}") 671} 672 673fn pop_qq(opcode: u8, index: Option<IndexRegister>) -> String { 674 let register = qq_register(opcode, index); 675 format!("pop {register}") 676} 677 678fn push_qq(opcode: u8, index: Option<IndexRegister>) -> String { 679 let register = qq_register(opcode, index); 680 format!("push {register}") 681} 682 683fn jp_nn(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 684 let address = reader.read_word(); 685 format!("jp ${address:04X}") 686} 687 688fn call_nn(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 689 let address = reader.read_word(); 690 format!("call ${address:04X}") 691} 692 693fn jp_cc_nn(opcode: u8, reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 694 let condition = jump_condition(opcode); 695 let address = reader.read_word(); 696 format!("jp {condition}, ${address:04X}") 697} 698 699fn call_cc_nn(opcode: u8, reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 700 let condition = jump_condition(opcode); 701 let address = reader.read_word(); 702 format!("call {condition}, ${address:04X}") 703} 704 705fn add_a_immediate(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 706 let operand = reader.read_byte(); 707 format!("add a, #0x{operand:02X}") 708} 709 710fn adc_a_immediate(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 711 let operand = reader.read_byte(); 712 format!("adc a, #0x{operand:02X}") 713} 714 715fn sub_a_immediate(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 716 let operand = reader.read_byte(); 717 format!("sub #0x{operand:02X}") 718} 719 720fn sbc_a_immediate(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 721 let operand = reader.read_byte(); 722 format!("sbc a, #0x{operand:02X}") 723} 724 725fn and_a_immediate(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 726 let operand = reader.read_byte(); 727 format!("and #0x{operand:02X}") 728} 729 730fn xor_a_immediate(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 731 let operand = reader.read_byte(); 732 format!("xor #0x{operand:02X}") 733} 734 735fn or_a_immediate(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 736 let operand = reader.read_byte(); 737 format!("or #0x{operand:02X}") 738} 739 740fn cp_a_immediate(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 741 let operand = reader.read_byte(); 742 format!("cp #0x{operand:02X}") 743} 744 745fn rst(opcode: u8) -> String { 746 let address = opcode & 0x38; 747 format!("rst ${address:02X}") 748} 749 750fn out_n_a(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 751 let address = reader.read_byte(); 752 format!("out (${address:02X}), a") 753} 754 755fn in_a_n(reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 756 let address = reader.read_byte(); 757 format!("in a, (${address:02X})") 758} 759 760fn ex_stack_hl(index: Option<IndexRegister>) -> String { 761 let source = index.map_or("hl", IndexRegister::label); 762 format!("ex (sp), {source}") 763} 764 765fn jp_hl(index: Option<IndexRegister>) -> String { 766 let register = index.map_or("hl", IndexRegister::label); 767 format!("jp ({register})") 768} 769 770fn ld_sp_hl(index: Option<IndexRegister>) -> String { 771 let register = index.map_or("hl", IndexRegister::label); 772 format!("ld sp, {register}") 773} 774 775fn bit_register(opcode: u8) -> &'static str { 776 match opcode & 7 { 777 0 => "b", 778 1 => "c", 779 2 => "d", 780 3 => "e", 781 4 => "h", 782 5 => "l", 783 6 => "(hl)", 784 7 => "a", 785 _ => unreachable!("value & 7 is always <= 7"), 786 } 787} 788 789fn shift_rotate_op(name: &str, opcode: u8, index: Option<IndexWithOffset>) -> String { 790 let register = 791 index.map_or_else(|| bit_register(opcode).into(), IndexWithOffset::indirect_with_offset); 792 format!("{name} {register}") 793} 794 795fn bit_op(name: &str, opcode: u8, index: Option<IndexWithOffset>) -> String { 796 let bit = (opcode >> 3) & 7; 797 let register = 798 index.map_or_else(|| bit_register(opcode).into(), IndexWithOffset::indirect_with_offset); 799 format!("{name} {bit}, {register}") 800} 801 802fn r_register_no_hl(opcode: u8) -> Option<&'static str> { 803 match (opcode >> 3) & 7 { 804 0 => Some("b"), 805 1 => Some("c"), 806 2 => Some("d"), 807 3 => Some("e"), 808 4 => Some("h"), 809 5 => Some("l"), 810 6 => None, 811 7 => Some("a"), 812 _ => unreachable!("value & 7 is always <= 7"), 813 } 814} 815 816fn in_r_c(opcode: u8) -> String { 817 let Some(register) = r_register_no_hl(opcode) else { return "illegal".into() }; 818 format!("in {register}, (c)") 819} 820 821fn out_c_r(opcode: u8) -> String { 822 let Some(register) = r_register_no_hl(opcode) else { return "illegal".into() }; 823 format!("out (c), {register}") 824} 825 826fn sbc_hl_ss(opcode: u8) -> String { 827 let register = dd_register(opcode, None); 828 format!("sbc hl, {register}") 829} 830 831fn adc_hl_ss(opcode: u8) -> String { 832 let register = dd_register(opcode, None); 833 format!("adc hl, {register}") 834} 835 836fn ld_direct_dd(opcode: u8, reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 837 let address = reader.read_word(); 838 let register = dd_register(opcode, None); 839 format!("ld (${address:04X}), {register}") 840} 841 842fn ld_dd_direct(opcode: u8, reader: &mut ByteReader<impl FnMut() -> u8>) -> String { 843 let address = reader.read_word(); 844 let register = dd_register(opcode, None); 845 format!("ld {register}, (${address:04X})") 846} 847 848#[cfg(test)] 849mod tests { 850 use super::*; 851 852 fn iter_reader(iterable: impl IntoIterator<Item = u8>) -> impl FnMut() -> u8 { 853 let mut iter = iterable.into_iter(); 854 move || iter.next().expect("ran out of opcodes") 855 } 856 857 #[test] 858 fn ld_r_r_index() { 859 let mut out = DisassembledInstruction::new(); 860 861 disassemble_into(&mut out, 0, iter_reader([0x66])); 862 assert_eq!(out.text.as_str(), "ld h, (hl)"); 863 864 disassemble_into(&mut out, 0, iter_reader([0xDD, 0x66, 0x05])); 865 assert_eq!(out.text.as_str(), "ld h, (ix+5)"); 866 867 disassemble_into(&mut out, 0, iter_reader([0x75])); 868 assert_eq!(out.text.as_str(), "ld (hl), l"); 869 870 disassemble_into(&mut out, 0, iter_reader([0xFD, 0x75, 0xFD])); 871 assert_eq!(out.text.as_str(), "ld (iy-3), l"); 872 873 disassemble_into(&mut out, 0, iter_reader([0x76])); 874 assert_eq!(out.text.as_str(), "halt"); 875 876 disassemble_into(&mut out, 0, iter_reader([0xDD, 0x76])); 877 assert_eq!(out.text.as_str(), "halt"); 878 } 879 880 #[test] 881 fn other_register_operands() { 882 let mut out = DisassembledInstruction::new(); 883 884 disassemble_into(&mut out, 0, iter_reader([0x0C])); 885 assert_eq!(out.text.as_str(), "inc c"); 886 887 disassemble_into(&mut out, 0, iter_reader([0xA2])); 888 assert_eq!(out.text.as_str(), "and d"); 889 890 disassemble_into(&mut out, 0, iter_reader([0x2D])); 891 assert_eq!(out.text.as_str(), "dec l"); 892 893 disassemble_into(&mut out, 0, iter_reader([0xFD, 0x36, 0x00, 0x34])); 894 assert_eq!(out.text.as_str(), "ld (iy), #0x34"); 895 } 896}