1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 16:31:31 +00:00
CLK/OSBindings/Mac/Clock SignalTests/68000ControlFlowTests.mm
2022-05-29 15:26:56 -04:00

553 lines
15 KiB
Plaintext

//
// 68000ControlFlowTests.m
// Clock SignalTests
//
// Created by Thomas Harte on 28/06/2019.
//
// Largely ported from the tests of the Portable 68k Emulator.
//
#import <XCTest/XCTest.h>
#include "TestRunner68000.hpp"
@interface M68000ControlFlowTests : XCTestCase
@end
@implementation M68000ControlFlowTests {
std::unique_ptr<RAM68000> _machine;
}
- (void)setUp {
_machine = std::make_unique<RAM68000>();
}
- (void)tearDown {
_machine.reset();
}
// MARK: Bcc
- (void)performBccb:(uint16_t)opcode {
_machine->set_program({
uint16_t(opcode | 6) // Bcc.b +6
});
_machine->run_for_instructions(1);
}
- (void)performBccw:(uint16_t)opcode {
_machine->set_program({
opcode, 0x0006 // Bcc.w +6
});
_machine->run_for_instructions(1);
}
- (void)testBHIb {
[self performBccb:0x6200];
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1008 + 4);
XCTAssertEqual(_machine->get_cycle_count(), 10);
}
- (void)testBLOb {
[self performBccb:0x6500];
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1002 + 4);
XCTAssertEqual(_machine->get_cycle_count(), 8);
}
- (void)testBHIw {
[self performBccw:0x6200];
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1008 + 4);
XCTAssertEqual(_machine->get_cycle_count(), 10);
}
- (void)testBLOw {
[self performBccw:0x6500];
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1004 + 4);
XCTAssertEqual(_machine->get_cycle_count(), 12);
}
// MARK: BRA
- (void)testBRAb {
_machine->set_program({
0x6004 // BRA.b +4
});
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1006 + 4);
XCTAssertEqual(_machine->get_cycle_count(), 10);
}
- (void)testBRAw {
_machine->set_program({
0x6000, 0x0004 // BRA.b +4
});
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1006 + 4);
XCTAssertEqual(_machine->get_cycle_count(), 10);
}
// MARK: BSR
- (void)testBSRw {
_machine->set_program({
0x6100, 0x0006 // BSR.w $1008
}, 0x3000);
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1008 + 4);
XCTAssertEqual(state.registers.stack_pointer(), 0x2ffc);
XCTAssertEqual(state.registers.status & ConditionCode::AllConditions, 0);
XCTAssertEqual(*_machine->ram_at(0x2ffc), 0);
XCTAssertEqual(*_machine->ram_at(0x2ffe), 0x1004);
XCTAssertEqual(_machine->get_cycle_count(), 18);
}
- (void)testBSRb {
_machine->set_program({
0x6106 // BSR.b $1008
}, 0x3000);
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1008 + 4);
XCTAssertEqual(state.registers.stack_pointer(), 0x2ffc);
XCTAssertEqual(state.registers.status & ConditionCode::AllConditions, 0);
XCTAssertEqual(*_machine->ram_at(0x2ffc), 0);
XCTAssertEqual(*_machine->ram_at(0x2ffe), 0x1002);
XCTAssertEqual(_machine->get_cycle_count(), 18);
}
// MARK: CHK
- (void)performCHKd1:(uint32_t)d1 d2:(uint32_t)d2 {
_machine->set_program({
0x4581 // CHK D1, D2
}, 0);
_machine->set_registers([=](auto &registers) {
registers.data[1] = d1;
registers.data[2] = d2;
registers.status |= ConditionCode::AllConditions;
});
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.data[1], d1);
XCTAssertEqual(state.registers.data[2], d2);
}
// Re: CHK, below; the final state of N is undocumented if Dn >= 0 and Dn < <ea>.
// Z, V and C are also undocumented by Motorola, but are documneted by 68knotes.txt.
- (void)testCHK_1111v1111 {
[self performCHKd1:0x1111 d2:0x1111]; // Neither exception-generating state applies.
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1002 + 4);
XCTAssertEqual(
state.registers.status & (ConditionCode::Extend | ConditionCode::Zero | ConditionCode::Overflow | ConditionCode::Carry),
ConditionCode::Extend);
XCTAssertEqual(10, _machine->get_cycle_count());
}
- (void)testCHK_1111v0000 {
[self performCHKd1:0x1111 d2:0x0000]; // Neither exception-generating state applies.
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1002 + 4);
XCTAssertEqual(
state.registers.status & (ConditionCode::Extend | ConditionCode::Zero | ConditionCode::Overflow | ConditionCode::Carry),
ConditionCode::Extend | ConditionCode::Zero);
XCTAssertEqual(10, _machine->get_cycle_count());
}
- (void)testCHK_8000v8001 {
[self performCHKd1:0x8000 d2:0x8001]; // Both less than 0 and D2 greater than D1.
const auto state = _machine->get_processor_state();
XCTAssertNotEqual(state.registers.program_counter, 0x1002 + 4);
XCTAssertEqual(state.registers.stack_pointer(), 0xfffffffa);
XCTAssertEqual(state.registers.status & ConditionCode::AllConditions, ConditionCode::Extend | ConditionCode::Negative);
XCTAssertEqual(38, _machine->get_cycle_count());
}
- (void)testCHK_8000v8000 {
[self performCHKd1:0x8000 d2:0x8000]; // Less than 0.
const auto state = _machine->get_processor_state();
XCTAssertNotEqual(state.registers.program_counter, 0x1002 + 4);
XCTAssertEqual(state.registers.status & ConditionCode::AllConditions, ConditionCode::Extend | ConditionCode::Negative);
XCTAssertEqual(40, _machine->get_cycle_count());
}
// MARK: DBcc
- (void)performDBccTestOpcode:(uint16_t)opcode status:(uint16_t)status d2Outcome:(uint32_t)d2Output {
_machine->set_program({
opcode, 0x0008 // DBcc D2, +8
});
_machine->set_registers([=](auto &registers) {
registers.status = status;
registers.data[2] = 1;
});
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.data[2], d2Output);
XCTAssertEqual(state.registers.status, status);
}
- (void)testDBT {
[self performDBccTestOpcode:0x50ca status:0 d2Outcome:1];
}
- (void)testDBF {
[self performDBccTestOpcode:0x51ca status:0 d2Outcome:0];
}
- (void)testDBHI {
[self performDBccTestOpcode:0x52ca status:0 d2Outcome:1];
}
- (void)testDBHI_Carry {
[self performDBccTestOpcode:0x52ca status:ConditionCode::Carry d2Outcome:0];
}
- (void)testDBHI_Zero {
[self performDBccTestOpcode:0x52ca status:ConditionCode::Zero d2Outcome:0];
}
- (void)testDBLS_CarryOverflow {
[self performDBccTestOpcode:0x53ca status:ConditionCode::Carry | ConditionCode::Overflow d2Outcome:1];
}
- (void)testDBLS_Carry {
[self performDBccTestOpcode:0x53ca status:ConditionCode::Carry d2Outcome:1];
}
- (void)testDBLS_Overflow {
[self performDBccTestOpcode:0x53ca status:ConditionCode::Overflow d2Outcome:0];
}
- (void)testDBCC_Carry {
[self performDBccTestOpcode:0x54ca status:ConditionCode::Carry d2Outcome:0];
}
- (void)testDBCC {
[self performDBccTestOpcode:0x54ca status:0 d2Outcome:1];
}
- (void)testDBCS {
[self performDBccTestOpcode:0x55ca status:0 d2Outcome:0];
}
- (void)testDBCS_Carry {
[self performDBccTestOpcode:0x55ca status:ConditionCode::Carry d2Outcome:1];
}
- (void)testDBNE {
[self performDBccTestOpcode:0x56ca status:0 d2Outcome:1];
}
- (void)testDBNE_Zero {
[self performDBccTestOpcode:0x56ca status:ConditionCode::Zero d2Outcome:0];
}
- (void)testDBEQ {
[self performDBccTestOpcode:0x57ca status:0 d2Outcome:0];
}
- (void)testDBEQ_Zero {
[self performDBccTestOpcode:0x57ca status:ConditionCode::Zero d2Outcome:1];
}
- (void)testDBVC {
[self performDBccTestOpcode:0x58ca status:0 d2Outcome:1];
}
- (void)testDBVC_Overflow {
[self performDBccTestOpcode:0x58ca status:ConditionCode::Overflow d2Outcome:0];
}
- (void)testDBVS {
[self performDBccTestOpcode:0x59ca status:0 d2Outcome:0];
}
- (void)testDBVS_Overflow {
[self performDBccTestOpcode:0x59ca status:ConditionCode::Overflow d2Outcome:1];
}
- (void)testDBPL {
[self performDBccTestOpcode:0x5aca status:0 d2Outcome:1];
}
- (void)testDBPL_Negative {
[self performDBccTestOpcode:0x5aca status:ConditionCode::Negative d2Outcome:0];
}
- (void)testDBMI {
[self performDBccTestOpcode:0x5bca status:0 d2Outcome:0];
}
- (void)testDBMI_Negative {
[self performDBccTestOpcode:0x5bca status:ConditionCode::Negative d2Outcome:1];
}
- (void)testDBGE_NegativeOverflow {
[self performDBccTestOpcode:0x5cca status:ConditionCode::Negative | ConditionCode::Overflow d2Outcome:1];
}
- (void)testDBGE {
[self performDBccTestOpcode:0x5cca status:0 d2Outcome:1];
}
- (void)testDBGE_Negative {
[self performDBccTestOpcode:0x5cca status:ConditionCode::Negative d2Outcome:0];
}
- (void)testDBGE_Overflow {
[self performDBccTestOpcode:0x5cca status:ConditionCode::Overflow d2Outcome:0];
}
- (void)testDBLT_NegativeOverflow {
[self performDBccTestOpcode:0x5dca status:ConditionCode::Negative | ConditionCode::Overflow d2Outcome:0];
}
- (void)testDBLT {
[self performDBccTestOpcode:0x5dca status:0 d2Outcome:0];
}
- (void)testDBLT_Negative {
[self performDBccTestOpcode:0x5dca status:ConditionCode::Negative d2Outcome:1];
}
- (void)testDBLT_Overflow {
[self performDBccTestOpcode:0x5dca status:ConditionCode::Overflow d2Outcome:1];
}
- (void)testDBGT {
[self performDBccTestOpcode:0x5eca status:0 d2Outcome:1];
}
- (void)testDBGT_ZeroNegativeOverflow {
[self performDBccTestOpcode:0x5eca status:ConditionCode::Zero | ConditionCode::Negative | ConditionCode::Overflow d2Outcome:0];
}
- (void)testDBGT_NegativeOverflow {
[self performDBccTestOpcode:0x5eca status:ConditionCode::Negative | ConditionCode::Overflow d2Outcome:1];
}
- (void)testDBGT_Zero {
[self performDBccTestOpcode:0x5eca status:ConditionCode::Zero d2Outcome:0];
}
- (void)testDBLE {
[self performDBccTestOpcode:0x5fca status:0 d2Outcome:0];
}
- (void)testDBLE_Zero {
[self performDBccTestOpcode:0x5fca status:ConditionCode::Zero d2Outcome:1];
}
- (void)testDBLE_Negative {
[self performDBccTestOpcode:0x5fca status:ConditionCode::Negative d2Outcome:1];
}
- (void)testDBLE_NegativeOverflow {
[self performDBccTestOpcode:0x5fca status:ConditionCode::Negative | ConditionCode::Overflow d2Outcome:0];
}
/* Further DBF tests omitted; they seemed to be duplicative, assuming I'm not suffering a failure of comprehension. */
// MARK: JMP
- (void)testJMP_A1 {
_machine->set_program({
0x4ed1 // JMP (A1)
});
_machine->set_registers([=](auto &registers) {
registers.address[1] = 0x3000;
});
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.address[1], 0x3000);
XCTAssertEqual(state.registers.program_counter, 0x3000 + 4);
XCTAssertEqual(8, _machine->get_cycle_count());
}
- (void)testJMP_PC {
_machine->set_program({
0x4efa, 0x000a // JMP PC+a (i.e. to 0x100c)
});
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x100c + 4);
XCTAssertEqual(10, _machine->get_cycle_count());
}
// MARK: JSR
- (void)testJSR_PC {
_machine->set_program({
0x4eba, 0x000a // JSR (+a)PC ; JSR to $100c
}, 0x2000);
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.stack_pointer(), 0x1ffc);
XCTAssertEqual(state.registers.program_counter, 0x100c + 4);
XCTAssertEqual(*_machine->ram_at(0x1ffc), 0x0000);
XCTAssertEqual(*_machine->ram_at(0x1ffe), 0x1004);
XCTAssertEqual(18, _machine->get_cycle_count());
}
- (void)testJSR_XXXl {
_machine->set_program({
0x4eb9, 0x0000, 0x1008 // JSR ($1008).l
}, 0x2000);
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.stack_pointer(), 0x1ffc);
XCTAssertEqual(state.registers.program_counter, 0x1008 + 4);
XCTAssertEqual(*_machine->ram_at(0x1ffc), 0x0000);
XCTAssertEqual(*_machine->ram_at(0x1ffe), 0x1006);
XCTAssertEqual(20, _machine->get_cycle_count());
}
// MARK: NOP
- (void)testNOP {
_machine->set_program({
0x4e71 // NOP
});
_machine->run_for_instructions(1);
XCTAssertEqual(4, _machine->get_cycle_count());
}
// MARK: RTR
- (void)testRTR {
_machine->set_program({
0x4e77 // RTR
}, 0x2000);
*_machine->ram_at(0x2000) = 0x7fff;
*_machine->ram_at(0x2002) = 0;
*_machine->ram_at(0x2004) = 0xc;
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.stack_pointer(), 0x2006);
XCTAssertEqual(state.registers.program_counter, 0x10);
XCTAssertEqual(state.registers.status & ConditionCode::AllConditions, ConditionCode::AllConditions);
XCTAssertEqual(20, _machine->get_cycle_count());
}
// MARK: RTS
- (void)testRTS {
_machine->set_program({
0x4e75 // RTS
}, 0x2000);
*_machine->ram_at(0x2000) = 0x0000;
*_machine->ram_at(0x2002) = 0x000c;
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.stack_pointer(), 0x2004);
XCTAssertEqual(state.registers.program_counter, 0x000c + 4);
XCTAssertEqual(16, _machine->get_cycle_count());
}
// MARK: TRAP
- (void)testTRAP {
_machine->set_program({
0x4e41 // TRAP #1
});
_machine->set_registers([=](auto &registers) {
registers.status = 0x700;
registers.user_stack_pointer = 0x200;
registers.supervisor_stack_pointer = 0x206;
});
*_machine->ram_at(0x84) = 0xfffe;
*_machine->ram_at(0xfffe) = 0x4e71;
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.status, 0x2700);
XCTAssertEqual(*_machine->ram_at(0x200), 0x700);
XCTAssertEqual(*_machine->ram_at(0x202), 0x0000);
XCTAssertEqual(*_machine->ram_at(0x204), 0x1002);
XCTAssertEqual(state.registers.supervisor_stack_pointer, 0x200);
XCTAssertEqual(34, _machine->get_cycle_count());
}
// MARK: TRAPV
- (void)testTRAPV_taken {
_machine->set_program({
0x4e76 // TRAPV
}, 0x206);
_machine->set_registers([=](auto &registers) {
registers.status = 0x702;
registers.supervisor_stack_pointer = 0x206;
});
*_machine->ram_at(0x1e) = 0xfffe;
*_machine->ram_at(0xfffe) = 0x4e71;
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.status, 0x2702);
XCTAssertEqual(state.registers.stack_pointer(), 0x200);
XCTAssertEqual(*_machine->ram_at(0x202), 0x0000);
XCTAssertEqual(*_machine->ram_at(0x204), 0x1002);
XCTAssertEqual(*_machine->ram_at(0x200), 0x702);
XCTAssertEqual(34, _machine->get_cycle_count());
}
- (void)testTRAPV_untaken {
_machine->set_program({
0x4e76 // TRAPV
});
_machine->run_for_instructions(1);
const auto state = _machine->get_processor_state();
XCTAssertEqual(state.registers.program_counter, 0x1002 + 4);
XCTAssertEqual(4, _machine->get_cycle_count());
}
@end