2019-06-29 01:58:38 +00:00
|
|
|
//
|
|
|
|
// TestRunner68000.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 28/06/2019.
|
|
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef TestRunner68000_h
|
|
|
|
#define TestRunner68000_h
|
|
|
|
|
|
|
|
#include <array>
|
2022-05-25 14:55:03 +00:00
|
|
|
#include <vector>
|
2019-06-29 01:58:38 +00:00
|
|
|
|
2022-05-25 14:55:03 +00:00
|
|
|
#include "../../../Processors/68000Mk2/68000Mk2.hpp"
|
2019-06-29 01:58:38 +00:00
|
|
|
|
2022-05-25 14:55:03 +00:00
|
|
|
using namespace InstructionSet::M68k;
|
2019-06-29 01:58:38 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
Provides a 68000 with 64kb of RAM in its low address space;
|
|
|
|
/RESET will put the supervisor stack pointer at 0xFFFF and
|
|
|
|
begin execution at 0x0400.
|
|
|
|
*/
|
2022-05-25 14:55:03 +00:00
|
|
|
class RAM68000: public CPU::MC68000Mk2::BusHandler {
|
2019-06-29 01:58:38 +00:00
|
|
|
public:
|
2022-05-26 00:22:38 +00:00
|
|
|
RAM68000() : m68000_(*this) {}
|
2019-06-29 01:58:38 +00:00
|
|
|
|
2021-08-07 21:51:00 +00:00
|
|
|
uint32_t initial_pc() const {
|
|
|
|
return 0x1000;
|
|
|
|
}
|
|
|
|
|
2022-05-26 00:22:38 +00:00
|
|
|
void set_program(
|
|
|
|
const std::vector<uint16_t> &program,
|
|
|
|
uint32_t stack_pointer = 0x206) {
|
2019-06-29 01:58:38 +00:00
|
|
|
memcpy(&ram_[0x1000 >> 1], program.data(), program.size() * sizeof(uint16_t));
|
|
|
|
|
2022-05-26 00:22:38 +00:00
|
|
|
// Ensure the condition codes start unset and set the initial program counter
|
|
|
|
// and supervisor stack pointer.
|
|
|
|
auto state = get_processor_state();
|
|
|
|
state.registers.status &= ~ConditionCode::AllConditions;
|
|
|
|
state.registers.program_counter = initial_pc();
|
|
|
|
state.registers.supervisor_stack_pointer = stack_pointer;
|
|
|
|
set_processor_state(state);
|
2019-06-29 01:58:38 +00:00
|
|
|
}
|
|
|
|
|
2020-09-27 19:10:29 +00:00
|
|
|
void will_perform(uint32_t, uint16_t) {
|
2019-06-29 01:58:38 +00:00
|
|
|
--instructions_remaining_;
|
2022-05-25 20:32:26 +00:00
|
|
|
if(!instructions_remaining_) {
|
2022-05-26 00:22:38 +00:00
|
|
|
throw StopException();
|
2022-05-25 20:32:26 +00:00
|
|
|
}
|
2019-06-29 01:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void run_for_instructions(int count) {
|
2022-05-26 00:22:38 +00:00
|
|
|
instructions_remaining_ = count;
|
|
|
|
if(!instructions_remaining_) return;
|
2019-06-29 01:58:38 +00:00
|
|
|
|
2022-05-26 00:22:38 +00:00
|
|
|
try {
|
|
|
|
while(true) {
|
|
|
|
run_for(HalfCycles(2000));
|
|
|
|
}
|
|
|
|
} catch (const StopException &) {}
|
2019-06-29 01:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void run_for(HalfCycles cycles) {
|
|
|
|
m68000_.run_for(cycles);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t *ram_at(uint32_t address) {
|
|
|
|
return &ram_[(address >> 1) % ram_.size()];
|
|
|
|
}
|
|
|
|
|
2022-05-25 14:55:03 +00:00
|
|
|
HalfCycles perform_bus_operation(const CPU::MC68000Mk2::Microcycle &cycle, int) {
|
2019-06-29 01:58:38 +00:00
|
|
|
const uint32_t word_address = cycle.word_address();
|
|
|
|
if(instructions_remaining_) duration_ += cycle.length;
|
|
|
|
|
2022-05-25 14:55:03 +00:00
|
|
|
using Microcycle = CPU::MC68000Mk2::Microcycle;
|
2019-06-29 01:58:38 +00:00
|
|
|
if(cycle.data_select_active()) {
|
|
|
|
if(cycle.operation & Microcycle::InterruptAcknowledge) {
|
2022-05-25 14:55:03 +00:00
|
|
|
cycle.value->b = 10;
|
2019-06-29 01:58:38 +00:00
|
|
|
} else {
|
|
|
|
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
|
|
|
default: break;
|
|
|
|
|
|
|
|
case Microcycle::SelectWord | Microcycle::Read:
|
2022-05-25 14:55:03 +00:00
|
|
|
cycle.value->w = ram_[word_address % ram_.size()];
|
2019-06-29 01:58:38 +00:00
|
|
|
break;
|
|
|
|
case Microcycle::SelectByte | Microcycle::Read:
|
2022-05-25 14:55:03 +00:00
|
|
|
cycle.value->b = ram_[word_address % ram_.size()] >> cycle.byte_shift();
|
2019-06-29 01:58:38 +00:00
|
|
|
break;
|
|
|
|
case Microcycle::SelectWord:
|
2022-05-25 14:55:03 +00:00
|
|
|
ram_[word_address % ram_.size()] = cycle.value->w;
|
2019-06-29 01:58:38 +00:00
|
|
|
break;
|
|
|
|
case Microcycle::SelectByte:
|
|
|
|
ram_[word_address % ram_.size()] = uint16_t(
|
2022-05-25 14:55:03 +00:00
|
|
|
(cycle.value->b << cycle.byte_shift()) |
|
2019-06-29 01:58:38 +00:00
|
|
|
(ram_[word_address % ram_.size()] & cycle.untouched_byte_mask())
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return HalfCycles(0);
|
|
|
|
}
|
|
|
|
|
2022-05-25 14:55:03 +00:00
|
|
|
CPU::MC68000Mk2::State get_processor_state() {
|
2022-05-26 00:22:38 +00:00
|
|
|
return m68000_.get_state();
|
2019-06-29 01:58:38 +00:00
|
|
|
}
|
|
|
|
|
2022-05-25 14:55:03 +00:00
|
|
|
void set_processor_state(const CPU::MC68000Mk2::State &state) {
|
2022-05-26 00:22:38 +00:00
|
|
|
m68000_.decode_from_state(state.registers);
|
2019-06-29 01:58:38 +00:00
|
|
|
}
|
|
|
|
|
2022-05-25 14:55:03 +00:00
|
|
|
auto &processor() {
|
2019-06-29 01:58:38 +00:00
|
|
|
return m68000_;
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_cycle_count() {
|
2019-10-30 02:36:29 +00:00
|
|
|
return int(duration_.as_integral()) >> 1;
|
2019-06-29 01:58:38 +00:00
|
|
|
}
|
|
|
|
|
2020-01-09 03:35:28 +00:00
|
|
|
void reset_cycle_count() {
|
|
|
|
duration_ = HalfCycles(0);
|
|
|
|
}
|
|
|
|
|
2019-06-29 01:58:38 +00:00
|
|
|
private:
|
2022-05-26 00:22:38 +00:00
|
|
|
struct StopException {};
|
|
|
|
|
2022-05-25 20:05:28 +00:00
|
|
|
CPU::MC68000Mk2::Processor<RAM68000, true, true, true> m68000_;
|
2019-07-01 01:43:30 +00:00
|
|
|
std::array<uint16_t, 256*1024> ram_{};
|
2019-06-29 01:58:38 +00:00
|
|
|
int instructions_remaining_;
|
|
|
|
HalfCycles duration_;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* TestRunner68000_h */
|