hg80 1.0.0

Z80 and Z80N CPU core, stepped one clock edge at a time
Documentation
use super::*;

#[test]
fn the_pair_field_names_the_three_register_pairs_an_opcode_can_reach() {
    let cpu = loaded();
    assert_eq!(cpu.addressed_pair(0b00, true), Wide::Bc);
    assert_eq!(cpu.addressed_pair(0b01, true), Wide::De);
    assert_eq!(cpu.addressed_pair(0b10, true), Wide::Hl);
}

#[test]
fn an_index_prefix_redirects_a_reference_to_the_pair_it_replaces() {
    let mut cpu = loaded();

    cpu.index_state = IndexState::Ix;
    assert_eq!(cpu.addressed_pair(0b10, true), Wide::Ix);
    assert_eq!(cpu.addressed_pair(0b00, true), Wide::Bc);
    assert_eq!(cpu.addressed_pair(0b01, true), Wide::De);

    cpu.index_state = IndexState::Iy;
    assert_eq!(cpu.addressed_pair(0b10, true), Wide::Iy);
}

#[test]
fn a_displaced_reference_means_the_pair_it_names_rather_than_the_index_register() {
    let mut cpu = loaded();
    cpu.index_state = IndexState::Ix;
    cpu.set_index_displaced_to(true);
    assert_eq!(cpu.addressed_pair(0b10, true), Wide::Hl);
}

#[test]
fn a_selector_that_does_not_substitute_keeps_the_pair_it_names() {
    let mut cpu = loaded();
    cpu.index_state = IndexState::Ix;
    assert_eq!(cpu.addressed_pair(0b10, false), Wide::Hl);
}

#[test]
fn the_first_operand_bus_reaches_every_source_its_selector_names() {
    let cpu = loaded();
    assert_eq!(cpu.first_operand(0b0000), 0xB0);
    assert_eq!(cpu.first_operand(0b0001), 0xC0);
    assert_eq!(cpu.first_operand(0b0010), 0xD0);
    assert_eq!(cpu.first_operand(0b0011), 0xE0);
    assert_eq!(cpu.first_operand(0b0100), 0x40);
    assert_eq!(cpu.first_operand(0b0101), 0x50);
    assert_eq!(cpu.first_operand(0b0110), 0x5A);
    assert_eq!(cpu.first_operand(0b0111), 0xA1);
    assert_eq!(cpu.first_operand(0b1000), 0x90);
    assert_eq!(cpu.first_operand(0b1001), 0x80);
    assert_eq!(cpu.first_operand(0b1010), 0x00);
}

#[test]
fn the_second_operand_bus_reaches_the_flags_and_the_program_counter_as_well() {
    let cpu = loaded();
    assert_eq!(cpu.second_operand(0b0000), 0xB0);
    assert_eq!(cpu.second_operand(0b0101), 0x50);
    assert_eq!(cpu.second_operand(0b0110), 0x5A);
    assert_eq!(cpu.second_operand(0b0111), 0xA1);
    assert_eq!(cpu.second_operand(0b1000), 0x90);
    assert_eq!(cpu.second_operand(0b1001), 0x80);
    assert_eq!(cpu.second_operand(0b1010), 0x01);
    assert_eq!(cpu.second_operand(0b1011), 0xF2);
    assert_eq!(cpu.second_operand(0b1100), 0x34);
    assert_eq!(cpu.second_operand(0b1101), 0x12);
    assert_eq!(cpu.second_operand(0b1110), 0x00);
}

#[test]
fn the_two_buses_carry_a_different_constant_so_that_one_counts_up_and_the_other_down() {
    let cpu = loaded();
    assert_eq!(cpu.first_operand(0b1010), 0x00);
    assert_eq!(cpu.second_operand(0b1010), 0x01);
}

#[test]
fn an_index_prefix_reaches_the_halves_of_the_index_register_through_the_operand_buses() {
    let mut cpu = loaded();
    cpu.index_state = IndexState::Ix;
    assert_eq!(cpu.first_operand(0b0100), 0x11);
    assert_eq!(cpu.first_operand(0b0101), 0x22);

    cpu.index_state = IndexState::Iy;
    assert_eq!(cpu.second_operand(0b0100), 0x33);
    assert_eq!(cpu.second_operand(0b0101), 0x44);
}

#[test]
fn a_displaced_bit_instruction_puts_the_byte_it_read_on_both_buses() {
    let cpu = loaded();
    assert_eq!(cpu.operands(0b0000, 0b0111, true), (0x5A, 0x5A));
    assert_eq!(cpu.operands(0b0000, 0b0111, false), (0xB0, 0xA1));
}

#[test]
fn a_sixteen_bit_step_does_nothing_unless_it_is_enabled() {
    let mut cpu = loaded();
    cpu.step_pair(0b0000);
    assert_eq!(cpu.registers().bc, 0xB0C0);
    cpu.step_pair(0b1000);
    assert_eq!(cpu.registers().bc, 0xB0C0);
}

#[test]
fn a_sixteen_bit_step_counts_the_pair_its_field_names() {
    let mut cpu = loaded();

    cpu.step_pair(0b0100);
    assert_eq!(cpu.registers().bc, 0xB0C1);
    cpu.step_pair(0b1100);
    assert_eq!(cpu.registers().bc, 0xB0C0);

    cpu.step_pair(0b0101);
    assert_eq!(cpu.registers().de, 0xD0E1);
    cpu.step_pair(0b1101);
    assert_eq!(cpu.registers().de, 0xD0E0);

    cpu.step_pair(0b0110);
    assert_eq!(cpu.registers().hl, 0x4051);
    cpu.step_pair(0b1110);
    assert_eq!(cpu.registers().hl, 0x4050);

    cpu.step_pair(0b0111);
    assert_eq!(cpu.registers().sp, 0x8091);
    cpu.step_pair(0b1111);
    assert_eq!(cpu.registers().sp, 0x8090);
}

#[test]
fn a_sixteen_bit_step_wraps_rather_than_overflowing() {
    let mut cpu = Cpu::new();
    cpu.registers_mut().bc = 0xFFFF;
    cpu.step_pair(0b0100);
    assert_eq!(cpu.registers().bc, 0x0000);
    cpu.step_pair(0b1100);
    assert_eq!(cpu.registers().bc, 0xFFFF);
}

#[test]
fn a_sixteen_bit_step_follows_the_prefix_to_the_index_register() {
    let mut cpu = loaded();
    cpu.index_state = IndexState::Ix;
    cpu.step_pair(0b0110);
    assert_eq!(cpu.registers().ix, 0x1123);
    assert_eq!(cpu.registers().hl, 0x4050);

    cpu.index_state = IndexState::Iy;
    cpu.step_pair(0b1110);
    assert_eq!(cpu.registers().iy, 0x3343);
}

#[test]
fn the_halves_of_a_register_are_written_without_disturbing_each_other() {
    let mut cpu = loaded();
    cpu.set_wide_high(Wide::Hl, 0xAA);
    assert_eq!(cpu.registers().hl, 0xAA50);
    cpu.set_wide_low(Wide::Hl, 0xBB);
    assert_eq!(cpu.registers().hl, 0xAABB);
}

#[test]
fn the_accumulator_and_flags_are_the_two_halves_of_one_register() {
    let cpu = loaded();
    assert_eq!(cpu.accumulator(), 0xA1);
    assert_eq!(cpu.flags(), 0xF2);
}

#[test]
fn exchanging_the_register_set_swaps_the_three_pairs_and_leaves_the_index_registers_alone() {
    let mut cpu = loaded();
    let registers = cpu.registers_mut();
    registers.bc_alt = 0x0102;
    registers.de_alt = 0x0304;
    registers.hl_alt = 0x0506;

    cpu.exchange_register_set();
    assert_eq!(cpu.registers().bc, 0x0102);
    assert_eq!(cpu.registers().de, 0x0304);
    assert_eq!(cpu.registers().hl, 0x0506);
    assert_eq!(cpu.registers().bc_alt, 0xB0C0);
    assert_eq!(cpu.registers().ix, 0x1122);
    assert_eq!(cpu.registers().iy, 0x3344);

    cpu.exchange_register_set();
    assert_eq!(cpu.registers().bc, 0xB0C0);
}

#[test]
fn a_reset_leaves_the_register_pairs_as_they_stand() {
    let mut cpu = loaded();
    cpu.reset();
    assert_eq!(cpu.registers().bc, 0xB0C0);
    assert_eq!(cpu.registers().hl, 0x4050);
    assert_eq!(cpu.registers().de, 0xD0E0);
    assert_eq!(cpu.registers().pc, 0);
    assert_eq!(cpu.registers().i, 0);
    assert_eq!(cpu.registers().r, 0);
    assert!(!cpu.registers().iff1);
    assert!(!cpu.registers().iff2);
}

#[test]
fn a_reset_fills_the_stack_pointer_and_both_accumulators() {
    let mut cpu = loaded();
    cpu.reset();
    assert_eq!(cpu.registers().sp, 0xFFFF);
    assert_eq!(cpu.registers().af, 0xFFFF);
    assert_eq!(cpu.registers().af_alt, 0xFFFF);
}