1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-11 04:28:58 +00:00

Merge pull request #1028 from TomHarte/68000Perform

Add free function implementation of 68000 operations, and an instruction-set interpreter.
This commit is contained in:
Thomas Harte 2022-05-15 07:21:03 -04:00 committed by GitHub
commit dfaf8ce64e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 3628 additions and 477 deletions

View File

@ -76,8 +76,6 @@ constexpr Operation Predecoder<model>::operation(OpT op) {
}
switch(op) {
case MOVEMtoRl: case MOVEMtoMl: return Operation::MOVEMl;
case MOVEMtoRw: case MOVEMtoMw: return Operation::MOVEMw;
case MOVEPtoRl: case MOVEPtoMl: return Operation::MOVEPl;
case MOVEPtoRw: case MOVEPtoMw: return Operation::MOVEPw;
@ -258,7 +256,14 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
case SUBQw: case SUBQl:
return ~TwoOperandMask<
Quick,
AlterableAddressingModes
AlterableAddressingModesNoAn
>::value;
case ADDQAw: case ADDQAl:
case SUBQAw: case SUBQAl:
return ~TwoOperandMask<
Quick,
An
>::value;
case OpT(Operation::MOVEb):
@ -449,16 +454,16 @@ template <uint8_t op> uint32_t Predecoder<model>::invalid_operands() {
An
>::value;
case MOVEMtoMw: case MOVEMtoMl:
case OpT(Operation::MOVEMtoMw): case OpT(Operation::MOVEMtoMl):
return ~TwoOperandMask<
Imm,
Ind | PreDec | d16An | d8AnXn | XXXw | XXXl
>::value;
case MOVEMtoRw: case MOVEMtoRl:
case OpT(Operation::MOVEMtoRw): case OpT(Operation::MOVEMtoRl):
return ~TwoOperandMask<
Ind | PostInc | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn,
Imm
Imm,
Ind | PostInc | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn
>::value;
case MOVEPtoRl: case MOVEPtoRw:
@ -490,7 +495,7 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::validated
op1_mode, op1_reg,
op2_mode, op2_reg,
requires_supervisor<model>(operation),
size(operation),
operand_size(operation),
condition);
}
@ -503,7 +508,7 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::validated
op1_mode, op1_reg,
op2_mode, op2_reg,
requires_supervisor<model>(operation),
size(operation),
operand_size(operation),
condition);
}
@ -789,26 +794,27 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
// b0b2 and b3b5: effective address.
// [already decoded: b10: direction]
//
case MOVEMtoMl: case MOVEMtoMw:
case OpT(Operation::MOVEMtoMl): case OpT(Operation::MOVEMtoMw):
case OpT(Operation::MOVEMtoRl): case OpT(Operation::MOVEMtoRw):
return validated<op, validate>(
AddressingMode::ImmediateData, 0,
combined_mode(ea_mode, ea_register), ea_register);
case MOVEMtoRl: case MOVEMtoRw:
return validated<op, validate>(
combined_mode(ea_mode, ea_register), ea_register,
AddressingMode::ImmediateData, 0);
//
// MARK: TRAP, BCCb, BSRb
//
// No further operands decoded, but note that one is somewhere in the opcode.
//
case OpT(Operation::TRAP):
case OpT(Operation::Bccb):
case OpT(Operation::BSRb):
return validated<op, validate>(AddressingMode::Quick);
case OpT(Operation::Bccb):
return validated<op, validate>(
AddressingMode::Quick, 0,
AddressingMode::None, 0,
Condition((instruction >> 8) & 0xf));
//
// MARK: LINKw
//
@ -827,7 +833,9 @@ template <uint8_t op, bool validate> Preinstruction Predecoder<model>::decode(ui
// b9b11: an immediate value, embedded in the opcode.
//
case ADDQb: case ADDQw: case ADDQl:
case ADDQAw: case ADDQAl:
case SUBQb: case SUBQw: case SUBQl:
case SUBQAw: case SUBQAl:
return validated<op, validate>(
AddressingMode::Quick, 0,
combined_mode(ea_mode, ea_register), ea_register);
@ -1073,10 +1081,10 @@ Preinstruction Predecoder<model>::decode4(uint16_t instruction) {
case 0x840: Decode(Op::PEA);
// 4-128 (p232)
case 0x880: Decode(MOVEMtoMw);
case 0x8c0: Decode(MOVEMtoMl);
case 0xc80: Decode(MOVEMtoRw);
case 0xcc0: Decode(MOVEMtoRl);
case 0x880: Decode(Op::MOVEMtoMw);
case 0x8c0: Decode(Op::MOVEMtoMl);
case 0xc80: Decode(Op::MOVEMtoRw);
case 0xcc0: Decode(Op::MOVEMtoRl);
// 4-192 (p296)
case 0xa00: Decode(Op::TSTb);
@ -1108,16 +1116,50 @@ template <Model model>
Preinstruction Predecoder<model>::decode5(uint16_t instruction) {
using Op = Operation;
switch(instruction & 0x1c0) {
switch(instruction & 0x1f8) {
// 4-11 (p115)
case 0x000: Decode(ADDQb);
case 0x040: Decode(ADDQw);
case 0x080: Decode(ADDQl);
case 0x000:
case 0x010: case 0x018:
case 0x020: case 0x028:
case 0x030: case 0x038:
Decode(ADDQb);
case 0x040:
case 0x050: case 0x058:
case 0x060: case 0x068:
case 0x070: case 0x078:
Decode(ADDQw);
case 0x080:
case 0x090: case 0x098:
case 0x0a0: case 0x0a8:
case 0x0b0: case 0x0b8:
Decode(ADDQl);
case 0x048: Decode(ADDQAw);
case 0x088: Decode(ADDQAl);
// 4-181 (p285)
case 0x100: Decode(SUBQb);
case 0x140: Decode(SUBQw);
case 0x180: Decode(SUBQl);
case 0x100:
case 0x110: case 0x118:
case 0x120: case 0x128:
case 0x130: case 0x138:
Decode(SUBQb);
case 0x140:
case 0x150: case 0x158:
case 0x160: case 0x168:
case 0x170: case 0x178:
Decode(SUBQw);
case 0x180:
case 0x190: case 0x198:
case 0x1a0: case 0x1a8:
case 0x1b0: case 0x1b8:
Decode(SUBQl);
case 0x148: Decode(SUBQAw);
case 0x188: Decode(SUBQAl);
default: break;
}

View File

@ -66,10 +66,7 @@ template <Model model> class Predecoder {
// time that's knowable from the Operation alone, hence the rather awkward
// extension of @c Operation.
enum ExtendedOperation: OpT {
MOVEMtoRl = uint8_t(Operation::Max) + 1, MOVEMtoRw,
MOVEMtoMl, MOVEMtoMw,
MOVEPtoRl, MOVEPtoRw,
MOVEPtoRl = uint8_t(Operation::Max) + 1, MOVEPtoRw,
MOVEPtoMl, MOVEPtoMw,
MOVEQ,

View File

@ -0,0 +1,50 @@
//
// ExceptionVectors.hpp
// Clock Signal
//
// Created by Thomas Harte on 10/05/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_M68k_ExceptionVectors_hpp
#define InstructionSets_M68k_ExceptionVectors_hpp
namespace InstructionSet {
namespace M68k {
enum Exception {
InitialStackPointer = 0,
InitialProgramCounter = 1,
AccessFault = 2,
AddressError = 3,
IllegalInstruction = 4,
IntegerDivideByZero = 5,
CHK = 6,
TRAPV = 7,
PrivilegeViolation = 8,
Trace = 9,
Line1010 = 10,
Line1111 = 11,
CoprocessorProtocolViolation = 13,
FormatError = 14,
UninitialisedInterrupt = 15,
SpuriousInterrupt = 24,
InterruptAutovectorBase = 25,
TrapBase = 32,
FPBranchOrSetOnUnorderedCondition = 48,
FPInexactResult = 49,
FPDivideByZero = 50,
FPUnderflow = 51,
FPOperandError = 52,
FPOverflow = 53,
FPSignallingNAN = 54,
FPUnimplementedDataType = 55,
MMUConfigurationError = 56,
MMUIllegalOperationError = 57,
MMUAccessLevelViolationError = 58,
};
}
}
#endif /* InstructionSets_M68k_ExceptionVectors_hpp */

View File

@ -0,0 +1,178 @@
//
// Executor.hpp
// Clock Signal
//
// Created by Thomas Harte on 29/04/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_M68k_Executor_hpp
#define InstructionSets_M68k_Executor_hpp
#include "Decoder.hpp"
#include "Instruction.hpp"
#include "Model.hpp"
#include "Perform.hpp"
#include "Status.hpp"
namespace InstructionSet {
namespace M68k {
/// Maps the 68k function codes such that bits 0, 1 and 2 represent
/// FC0, FC1 and FC2 respectively.
enum class FunctionCode {
UserData = 0b001,
UserProgram = 0b010,
SupervisorData = 0b101,
SupervisorProgram = 0b110,
InterruptAcknowledge = 0b111,
};
/// The Executor is templated on a class that implements bus handling as defined below;
/// the bus handler is responsible for all reads and writes, and will also receive resets and
/// interrupt acknowledgements.
///
/// The executor will provide 32-bit addresses and act as if it had a 32-bit data bus, even
/// if interpretting the original 68000 instruction set.
struct BusHandler {
/// Write @c value of type/size @c IntT to @c address with the processor signalling
/// a FunctionCode of @c function. @c IntT will be one of @c uint8_t, @c uint16_t
/// or @c uint32_t.
template <typename IntT> void write(uint32_t address, IntT value, FunctionCode function);
/// Read and return a value of type/size @c IntT from @c address with the processor signalling
/// a FunctionCode of @c function. @c IntT will be one of @c uint8_t, @c uint16_t
/// or @c uint32_t.
template <typename IntT> IntT read(uint32_t address, FunctionCode function);
/// React to the processor programmatically strobing its RESET output.
void reset();
/// Respond to an interrupt acknowledgement at @c interrupt_level from the processor.
/// Should return @c -1 in order to trigger autovectoring, or the appropriate exception vector
/// number otherwise.
///
/// It is undefined behaviour to return a number greater than 255.
int acknowlege_interrupt(int interrupt_level);
};
/// Ties together the decoder, sequencer and performer to provide an executor for 680x0 instruction streams.
/// As is standard for these executors, no bus- or cache-level fidelity to any real 680x0 is attempted. This is
/// simply an executor of 680x0 code.
template <Model model, typename BusHandler> class Executor {
public:
Executor(BusHandler &);
/// Reset the processor, back to a state as if just externally reset.
void reset();
/// Executes the number of instructions specified;
/// other events — such as initial reset or branching
/// to exceptions — may be zero costed, and interrupts
/// will not necessarily take effect immediately when signalled.
void run_for_instructions(int);
/// Call this at any time to interrupt processing with a bus error;
/// the function code and address must be provided. Internally
/// this will raise a C++ exception, and therefore doesn't return.
[[noreturn]] void signal_bus_error(FunctionCode, uint32_t address);
/// Sets the current input interrupt level.
void set_interrupt_level(int);
// TODO: this will likely be shared in some capacity with the bus-accurate versions of the 680x0;
// therefore it will almost certainly be factored out in future.
struct Registers {
uint32_t data[8], address[7];
uint32_t user_stack_pointer;
uint32_t supervisor_stack_pointer;
uint16_t status;
uint32_t program_counter;
};
Registers get_state();
void set_state(const Registers &);
private:
class State: public NullFlowController {
public:
State(BusHandler &handler) : bus_handler_(handler) {}
void run(int &);
bool stopped = false;
void read(DataSize size, uint32_t address, CPU::SlicedInt32 &value);
void write(DataSize size, uint32_t address, CPU::SlicedInt32 value);
template <typename IntT> IntT read(uint32_t address, bool is_from_pc = false);
template <typename IntT> void write(uint32_t address, IntT value);
template <typename IntT> IntT read_pc();
// Processor state.
Status status;
CPU::SlicedInt32 program_counter;
CPU::SlicedInt32 registers[16]; // D0D7 followed by A0A7.
CPU::SlicedInt32 stack_pointers[2];
uint32_t instruction_address;
uint16_t instruction_opcode;
// Things that are ephemerally duplicative of Status.
int active_stack_pointer = 0;
Status::FlagT should_trace = 0;
// Bus state.
int interrupt_input = 0;
// A lookup table to ensure that A7 is adjusted by 2 rather than 1 in
// postincrement and predecrement mode.
static constexpr uint32_t byte_increments[] = {
1, 1, 1, 1, 1, 1, 1, 2
};
// Flow control; Cf. Perform.hpp.
template <bool use_current_instruction_pc = true> void raise_exception(int);
void did_update_status();
template <typename IntT> void complete_bcc(bool matched_condition, IntT offset);
void complete_dbcc(bool matched_condition, bool overflowed, int16_t offset);
void bsr(uint32_t offset);
void jmp(uint32_t);
void jsr(uint32_t offset);
void rtr();
void rts();
void rte();
void stop();
void reset();
void link(Preinstruction instruction, uint32_t offset);
void unlink(uint32_t &address);
void pea(uint32_t address);
void move_to_usp(uint32_t address);
void move_from_usp(uint32_t &address);
template <typename IntT> void movep(Preinstruction instruction, uint32_t source, uint32_t dest);
template <typename IntT> void movem_toM(Preinstruction instruction, uint32_t source, uint32_t dest);
template <typename IntT> void movem_toR(Preinstruction instruction, uint32_t source, uint32_t dest);
void tas(Preinstruction instruction, uint32_t address);
private:
BusHandler &bus_handler_;
Predecoder<model> decoder_;
struct EffectiveAddress {
CPU::SlicedInt32 value;
bool requires_fetch;
};
EffectiveAddress calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index);
uint32_t index_8bitdisplacement();
} state_;
};
}
}
#include "Implementation/ExecutorImplementation.hpp"
#endif /* InstructionSets_M68k_Executor_hpp */

View File

@ -0,0 +1,695 @@
//
// ExecutorImplementation.hpp
// Clock Signal
//
// Created by Thomas Harte on 01/05/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_M68k_ExecutorImplementation_hpp
#define InstructionSets_M68k_ExecutorImplementation_hpp
#include "../Perform.hpp"
#include "../ExceptionVectors.hpp"
#include <cassert>
namespace InstructionSet {
namespace M68k {
#define An(x) state_.registers[8 + x]
#define Dn(x) state_.registers[x]
#define sp An(7)
#define AccessException(code, address, vector) \
uint64_t(((vector) << 8) | uint64_t(code) | ((address) << 16))
// MARK: - Executor itself.
template <Model model, typename BusHandler>
Executor<model, BusHandler>::Executor(BusHandler &handler) : state_(handler) {
reset();
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::reset() {
// Establish: supervisor state, all interrupts blocked.
state_.status.set_status(0b0010'0011'1000'0000);
state_.did_update_status();
// Clear the STOPped state, if currently active.
state_.stopped = false;
// Seed stack pointer and program counter.
sp.l = state_.template read<uint32_t>(0) & 0xffff'fffe;
state_.program_counter.l = state_.template read<uint32_t>(4);
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::signal_bus_error(FunctionCode code, uint32_t address) {
throw AccessException(code, address, Exception::AccessFault);
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::set_interrupt_level(int level) {
state_.interrupt_input_ = level;
state_.stopped &= state_.interrupt_input_ <= state_.status.interrupt_level;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::run_for_instructions(int count) {
if(state_.stopped) return;
while(count > 0) {
try {
state_.run(count);
} catch (uint64_t exception) {
// Potiental source of an exception #1: STOP. Check for that first.
if(state_.stopped) return;
// Unpack the exception; this is the converse of the AccessException macro.
const int vector_address = (exception >> 6) & 0xfc;
const uint16_t code = uint16_t(exception & 0xff);
const uint32_t faulting_address = uint32_t(exception >> 16);
// Grab the status to store, then switch into supervisor mode.
const uint16_t status = state_.status.status();
state_.status.is_supervisor = true;
state_.status.trace_flag = 0;
state_.did_update_status();
// Ensure no tracing occurs into the exception.
state_.should_trace = 0;
// Push status and the program counter at instruction start.
state_.template write<uint16_t>(sp.l - 14, code);
state_.template write<uint32_t>(sp.l - 12, faulting_address);
state_.template write<uint16_t>(sp.l - 8, state_.instruction_opcode);
state_.template write<uint16_t>(sp.l - 6, status);
state_.template write<uint16_t>(sp.l - 4, state_.instruction_address);
sp.l -= 14;
// Fetch the new program counter; reset on a double fault.
try {
state_.program_counter.l = state_.template read<uint32_t>(vector_address);
} catch (uint64_t) {
// TODO: I think this is incorrect, but need to verify consistency
// across different 680x0s.
reset();
}
}
}
}
template <Model model, typename BusHandler>
typename Executor<model, BusHandler>::Registers Executor<model, BusHandler>::get_state() {
Registers result;
for(int c = 0; c < 8; c++) {
result.data[c] = Dn(c).l;
}
for(int c = 0; c < 7; c++) {
result.address[c] = An(c).l;
}
result.status = state_.status.status();
result.program_counter = state_.program_counter.l;
state_.stack_pointers[state_.active_stack_pointer] = sp;
result.user_stack_pointer = state_.stack_pointers[0].l;
result.supervisor_stack_pointer = state_.stack_pointers[1].l;
return result;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::set_state(const Registers &state) {
for(int c = 0; c < 8; c++) {
Dn(c).l = state.data[c];
}
for(int c = 0; c < 7; c++) {
An(c).l = state.address[c];
}
state_.status.set_status(state.status);
state_.did_update_status();
state_.program_counter.l = state.program_counter;
state_.stack_pointers[0].l = state.user_stack_pointer;
state_.stack_pointers[1].l = state.supervisor_stack_pointer;
sp = state_.stack_pointers[state_.active_stack_pointer];
}
#undef Dn
#undef An
// MARK: - State.
#define An(x) registers[8 + x]
#define Dn(x) registers[x]
template <Model model, typename BusHandler>
template <typename IntT>
IntT Executor<model, BusHandler>::State::read(uint32_t address, bool is_from_pc) {
const auto code = FunctionCode((active_stack_pointer << 2) | 1 << int(is_from_pc));
if(model == Model::M68000 && sizeof(IntT) > 1 && address & 1) {
throw AccessException(code, address, Exception::AddressError | (int(is_from_pc) << 3) | (1 << 4));
}
return bus_handler_.template read<IntT>(address, code);
}
template <Model model, typename BusHandler>
template <typename IntT>
void Executor<model, BusHandler>::State::write(uint32_t address, IntT value) {
const auto code = FunctionCode((active_stack_pointer << 2) | 1);
if(model == Model::M68000 && sizeof(IntT) > 1 && address & 1) {
throw AccessException(code, address, Exception::AddressError);
}
bus_handler_.template write<IntT>(address, value, code);
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::read(DataSize size, uint32_t address, CPU::SlicedInt32 &value) {
switch(size) {
case DataSize::Byte: value.b = read<uint8_t>(address); break;
case DataSize::Word: value.w = read<uint16_t>(address); break;
case DataSize::LongWord: value.l = read<uint32_t>(address); break;
}
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::write(DataSize size, uint32_t address, CPU::SlicedInt32 value) {
switch(size) {
case DataSize::Byte: write<uint8_t>(address, value.b); break;
case DataSize::Word: write<uint16_t>(address, value.w); break;
case DataSize::LongWord: write<uint32_t>(address, value.l); break;
}
}
template <Model model, typename BusHandler>
template <typename IntT> IntT Executor<model, BusHandler>::State::read_pc() {
const IntT result = read<IntT>(program_counter.l, true);
if constexpr (sizeof(IntT) == 4) {
program_counter.l += 4;
} else {
program_counter.l += 2;
}
return result;
}
template <Model model, typename BusHandler>
uint32_t Executor<model, BusHandler>::State::index_8bitdisplacement() {
// TODO: if not a 68000, check bit 8 for whether this should be a full extension word;
// also include the scale field even if not.
const auto extension = read_pc<uint16_t>();
const auto offset = int8_t(extension);
const int register_index = (extension >> 12) & 7;
const uint32_t displacement = registers[register_index + ((extension >> 12) & 0x08)].l;
const uint32_t sized_displacement = (extension & 0x800) ? displacement : int16_t(displacement);
return offset + sized_displacement;
}
template <Model model, typename BusHandler>
typename Executor<model, BusHandler>::State::EffectiveAddress
Executor<model, BusHandler>::State::calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index) {
EffectiveAddress ea;
switch(instruction.mode(index)) {
case AddressingMode::None:
// Permit an uninitialised effective address to be returned;
// this value shouldn't be used.
break;
//
// Operands that don't have effective addresses, which are returned as values.
//
case AddressingMode::DataRegisterDirect:
case AddressingMode::AddressRegisterDirect:
ea.value = registers[instruction.lreg(index)];
ea.requires_fetch = false;
break;
case AddressingMode::Quick:
ea.value.l = quick(opcode, instruction.operation);
ea.requires_fetch = false;
break;
case AddressingMode::ImmediateData:
switch(instruction.operand_size()) {
case DataSize::Byte:
ea.value.l = read_pc<uint16_t>() & 0xff;
break;
case DataSize::Word:
ea.value.l = read_pc<uint16_t>();
break;
case DataSize::LongWord:
ea.value.l = read_pc<uint32_t>();
break;
}
ea.requires_fetch = false;
break;
//
// Absolute addresses.
//
case AddressingMode::AbsoluteShort:
ea.value.l = int16_t(read_pc<uint16_t>());
ea.requires_fetch = true;
break;
case AddressingMode::AbsoluteLong:
ea.value.l = read_pc<uint32_t>();
ea.requires_fetch = true;
break;
//
// Address register indirects.
//
case AddressingMode::AddressRegisterIndirect:
ea.value = An(instruction.reg(index));
ea.requires_fetch = true;
break;
case AddressingMode::AddressRegisterIndirectWithPostincrement: {
const auto reg = instruction.reg(index);
ea.value = An(reg);
ea.requires_fetch = true;
switch(instruction.operand_size()) {
case DataSize::Byte: An(reg).l += byte_increments[reg]; break;
case DataSize::Word: An(reg).l += 2; break;
case DataSize::LongWord: An(reg).l += 4; break;
}
} break;
case AddressingMode::AddressRegisterIndirectWithPredecrement: {
const auto reg = instruction.reg(index);
switch(instruction.operand_size()) {
case DataSize::Byte: An(reg).l -= byte_increments[reg]; break;
case DataSize::Word: An(reg).l -= 2; break;
case DataSize::LongWord: An(reg).l -= 4; break;
}
ea.value = An(reg);
ea.requires_fetch = true;
} break;
case AddressingMode::AddressRegisterIndirectWithDisplacement:
ea.value.l = An(instruction.reg(index)).l + int16_t(read_pc<uint16_t>());
ea.requires_fetch = true;
break;
case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
ea.value.l = An(instruction.reg(index)).l + index_8bitdisplacement();
ea.requires_fetch = true;
break;
//
// PC-relative addresses.
//
case AddressingMode::ProgramCounterIndirectWithDisplacement:
ea.value.l = program_counter.l + int16_t(read_pc<uint16_t>());
ea.requires_fetch = true;
break;
case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
ea.value.l = program_counter.l + index_8bitdisplacement();
ea.requires_fetch = true;
break;
default:
assert(false);
}
return ea;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::run(int &count) {
while(count--) {
// Check for a new interrupt.
if(interrupt_input > status.interrupt_level) {
const int vector = bus_handler_.acknowlege_interrupt(interrupt_input);
if(vector >= 0) {
raise_exception<false>(vector);
} else {
raise_exception<false>(Exception::InterruptAutovectorBase - 1 + interrupt_input);
}
status.interrupt_level = interrupt_input;
}
// Capture the trace bit, indicating whether to trace
// after this instruction.
//
// If an exception occurs, this value will be cleared, but
// it'll persist across mere status register changes for
// one instruction's duration.
should_trace = status.trace_flag;
// Read the next instruction.
instruction_address = program_counter.l;
instruction_opcode = read_pc<uint16_t>();
const Preinstruction instruction = decoder_.decode(instruction_opcode);
if(instruction.requires_supervisor() && !status.is_supervisor) {
raise_exception(Exception::PrivilegeViolation);
continue;
}
if(instruction.operation == Operation::Undefined) {
switch(instruction_opcode & 0xf000) {
default:
raise_exception(Exception::IllegalInstruction);
continue;
case 0xa000:
raise_exception(Exception::Line1010);
continue;
case 0xf000:
raise_exception(Exception::Line1111);
continue;
}
}
// Temporary storage.
CPU::SlicedInt32 operand_[2];
EffectiveAddress effective_address_[2];
// Calculate effective addresses; copy 'addresses' into the
// operands by default both: (i) because they might be values,
// rather than addresses; and (ii) then they'll be there for use
// by LEA and PEA.
effective_address_[0] = calculate_effective_address(instruction, instruction_opcode, 0);
effective_address_[1] = calculate_effective_address(instruction, instruction_opcode, 1);
operand_[0] = effective_address_[0].value;
operand_[1] = effective_address_[1].value;
// Obtain the appropriate sequence.
const auto flags = operand_flags<model>(instruction.operation);
#define fetch_operand(n) \
if(effective_address_[n].requires_fetch) { \
read(instruction.operand_size(), effective_address_[n].value.l, operand_[n]); \
}
if(flags & FetchOp1) { fetch_operand(0); }
if(flags & FetchOp2) { fetch_operand(1); }
#undef fetch_operand
perform<model>(instruction, operand_[0], operand_[1], status, *this);
#define store_operand(n) \
if(!effective_address_[n].requires_fetch) { \
registers[instruction.lreg(n)] = operand_[n]; \
} else { \
write(instruction.operand_size(), effective_address_[n].value.l, operand_[n]); \
}
if(flags & StoreOp1) { store_operand(0); }
if(flags & StoreOp2) { store_operand(1); }
#undef store_operand
// If the trace bit was set, trigger the trace exception.
if(should_trace) {
raise_exception<false>(Exception::Trace);
}
}
}
// MARK: - Flow Control.
template <Model model, typename BusHandler>
template <bool use_current_instruction_pc>
void Executor<model, BusHandler>::State::raise_exception(int index) {
const uint32_t address = index << 2;
// Grab the status to store, then switch into supervisor mode
// and disable tracing.
const uint16_t previous_status = status.status();
status.is_supervisor = true;
status.trace_flag = 0;
did_update_status();
// Push status and the program counter at instruction start.
write<uint32_t>(sp.l - 4, use_current_instruction_pc ? instruction_address : program_counter.l);
write<uint16_t>(sp.l - 6, previous_status);
sp.l -= 6;
// Ensure no tracing occurs into the exception.
should_trace = 0;
// Fetch the new program counter.
program_counter.l = read<uint32_t>(address);
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::did_update_status() {
// Shuffle the stack pointers.
stack_pointers[active_stack_pointer] = sp;
sp = stack_pointers[int(status.is_supervisor)];
active_stack_pointer = int(status.is_supervisor);
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::stop() {
stopped = true;
// Raise an exception to exit the run loop; it doesn't matter
// what value is used as long as it is a uint64_t, so 0 will do.
throw uint64_t();
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::reset() {
bus_handler_.reset();
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::jmp(uint32_t address) {
program_counter.l = address;
}
template <Model model, typename BusHandler>
template <typename IntT> void Executor<model, BusHandler>::State::complete_bcc(bool branch, IntT offset) {
if(branch) {
program_counter.l = instruction_address + offset + 2;
}
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::complete_dbcc(bool matched_condition, bool overflowed, int16_t offset) {
if(!matched_condition && !overflowed) {
program_counter.l = instruction_address + offset + 2;
}
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::bsr(uint32_t offset) {
sp.l -= 4;
write<uint32_t>(sp.l, program_counter.l);
program_counter.l = instruction_address + offset;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::jsr(uint32_t address) {
sp.l -= 4;
write<uint32_t>(sp.l, program_counter.l);
program_counter.l = address;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::link(Preinstruction instruction, uint32_t offset) {
const auto reg = 8 + instruction.reg<0>();
sp.l -= 4;
write<uint32_t>(sp.l, Dn(reg).l);
Dn(reg) = sp;
sp.l += offset;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::unlink(uint32_t &address) {
sp.l = address;
address = read<uint32_t>(sp.l);
sp.l += 4;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::pea(uint32_t address) {
sp.l -= 4;
write<uint32_t>(sp.l, address);
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::rtr() {
status.set_ccr(read<uint16_t>(sp.l));
sp.l += 2;
rts();
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::rte() {
status.set_status(read<uint16_t>(sp.l));
sp.l += 2;
rts();
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::rts() {
program_counter.l = read<uint32_t>(sp.l);
sp.l += 4;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::tas(Preinstruction instruction, uint32_t address) {
uint8_t value;
if(instruction.mode<0>() != AddressingMode::DataRegisterDirect) {
value = read<uint8_t>(address);
write<uint8_t>(address, value | 0x80);
} else {
value = uint8_t(address);
Dn(instruction.reg<0>()).b = uint8_t(address | 0x80);
}
status.overflow_flag = status.carry_flag = 0;
status.zero_result = value;
status.negative_flag = value & 0x80;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::move_to_usp(uint32_t address) {
stack_pointers[0].l = address;
}
template <Model model, typename BusHandler>
void Executor<model, BusHandler>::State::move_from_usp(uint32_t &address) {
address = stack_pointers[0].l;
}
template <Model model, typename BusHandler>
template <typename IntT>
void Executor<model, BusHandler>::State::movep(Preinstruction instruction, uint32_t source, uint32_t dest) {
if(instruction.mode<0>() == AddressingMode::DataRegisterDirect) {
// Move register to memory.
const uint32_t reg = source;
uint32_t address = dest;
if constexpr (sizeof(IntT) == 4) {
write<uint8_t>(address, uint8_t(reg >> 24));
address += 2;
write<uint8_t>(address, uint8_t(reg >> 16));
address += 2;
}
write<uint8_t>(address, uint8_t(reg >> 8));
address += 2;
write<uint8_t>(address, uint8_t(reg));
} else {
// Move memory to register.
uint32_t &reg = Dn(instruction.reg<1>()).l;
uint32_t address = source;
if constexpr (sizeof(IntT) == 4) {
reg = read<uint8_t>(address) << 24;
address += 2;
reg |= read<uint8_t>(address) << 16;
address += 2;
} else {
reg &= 0xffff0000;
}
reg |= read<uint8_t>(address) << 8;
address += 2;
reg |= read<uint8_t>(address);
}
}
template <Model model, typename BusHandler>
template <typename IntT>
void Executor<model, BusHandler>::State::movem_toM(Preinstruction instruction, uint32_t source, uint32_t dest) {
// Move registers to memory. This is the only permitted use of the predecrement mode,
// which reverses output order.
if(instruction.mode<1>() == AddressingMode::AddressRegisterIndirectWithPredecrement) {
// The structure of the code in the mainline part of the executor is such
// that the address register will already have been predecremented before
// reaching here, and it'll have been by two bytes per the operand size
// rather than according to the instruction size. That's not wanted, so undo it.
//
// TODO: with the caveat that the 68020+ have different behaviour:
//
// "For the MC68020, MC68030, MC68040, and CPU32, if the addressing register is also
// moved to memory, the value written is the initial register value decremented by the
// size of the operation. The MC68000 and MC68010 write the initial register value
// (not decremented)."
An(instruction.reg<1>()).l += 2;
uint32_t address = An(instruction.reg<1>()).l;
int index = 15;
while(source) {
if(source & 1) {
address -= sizeof(IntT);
write<IntT>(address, IntT(registers[index].l));
}
--index;
source >>= 1;
}
An(instruction.reg<1>()).l = address;
return;
}
int index = 0;
while(source) {
if(source & 1) {
write<IntT>(dest, IntT(registers[index].l));
dest += sizeof(IntT);
}
++index;
source >>= 1;
}
}
template <Model model, typename BusHandler>
template <typename IntT>
void Executor<model, BusHandler>::State::movem_toR(Preinstruction instruction, uint32_t source, uint32_t dest) {
// Move memory to registers.
//
// A 68000 convention has been broken here; the instruction form is:
// MOVEM <ea>, #
// ... but the instruction is encoded as [MOVEM] [#] [ea].
//
// This project's decoder decodes as #, <ea>.
int index = 0;
while(source) {
if(source & 1) {
if constexpr (sizeof(IntT) == 2) {
registers[index].l = int16_t(read<uint16_t>(dest));
} else {
registers[index].l = read<uint32_t>(dest);
}
dest += sizeof(IntT);
}
++index;
source >>= 1;
}
if(instruction.mode<1>() == AddressingMode::AddressRegisterIndirectWithPostincrement) {
// "If the effective address is specified by the postincrement mode ...
// [i]f the addressing register is also loaded from memory, the memory value is
// ignored and the register is written with the postincremented effective address."
An(instruction.reg<1>()).l = dest;
}
}
#undef sp
#undef Dn
#undef An
#undef AccessException
}
}
#endif /* InstructionSets_M68k_ExecutorImplementation_hpp */

View File

@ -0,0 +1,145 @@
//
// InstructionOperandFlags.hpp
// Clock Signal
//
// Created by Thomas Harte on 09/05/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_68k_InstructionOperandFlags_hpp
#define InstructionSets_68k_InstructionOperandFlags_hpp
namespace InstructionSet {
namespace M68k {
template <Model model, Operation t_operation> uint8_t operand_flags(Operation r_operation) {
switch((t_operation != Operation::Undefined) ? t_operation : r_operation) {
default:
assert(false);
//
// No operands are fetched or stored.
// (which means that source and destination will appear as their effective addresses)
//
case Operation::PEA:
case Operation::JMP: case Operation::JSR:
case Operation::MOVEPw: case Operation::MOVEPl:
case Operation::MOVEMtoMw: case Operation::MOVEMtoMl:
case Operation::MOVEMtoRw: case Operation::MOVEMtoRl:
case Operation::TAS:
case Operation::RTR: case Operation::RTS: case Operation::RTE:
return 0;
//
// Single-operand read.
//
case Operation::MOVEtoSR: case Operation::MOVEtoCCR: case Operation::MOVEtoUSP:
case Operation::ORItoSR: case Operation::ORItoCCR:
case Operation::ANDItoSR: case Operation::ANDItoCCR:
case Operation::EORItoSR: case Operation::EORItoCCR:
case Operation::Bccb: case Operation::Bccw: case Operation::Bccl:
case Operation::BSRb: case Operation::BSRw: case Operation::BSRl:
case Operation::TSTb: case Operation::TSTw: case Operation::TSTl:
return FetchOp1;
//
// Single-operand write.
//
case Operation::MOVEfromSR: case Operation::MOVEfromUSP:
case Operation::Scc:
return StoreOp1;
//
// Single-operand read-modify-write.
//
case Operation::NBCD:
case Operation::NOTb: case Operation::NOTw: case Operation::NOTl:
case Operation::NEGb: case Operation::NEGw: case Operation::NEGl:
case Operation::NEGXb: case Operation::NEGXw: case Operation::NEGXl:
case Operation::EXTbtow: case Operation::EXTwtol:
case Operation::SWAP:
case Operation::UNLINK:
case Operation::ASLm: case Operation::ASRm:
case Operation::LSLm: case Operation::LSRm:
case Operation::ROLm: case Operation::RORm:
case Operation::ROXLm: case Operation::ROXRm:
return FetchOp1 | StoreOp1;
//
// CLR, which is model-dependent.
//
case Operation::CLRb: case Operation::CLRw: case Operation::CLRl:
if constexpr (model == Model::M68000) {
return FetchOp1 | StoreOp1;
} else {
return StoreOp1;
}
//
// Two-operand; read both.
//
case Operation::CMPb: case Operation::CMPw: case Operation::CMPl:
case Operation::CMPAw: case Operation::CMPAl:
case Operation::CHK:
case Operation::BTST:
case Operation::LINKw:
return FetchOp1 | FetchOp2;
//
// Two-operand; read source, write dest.
//
case Operation::MOVEb: case Operation::MOVEw: case Operation::MOVEl:
case Operation::MOVEAw: case Operation::MOVEAl:
return FetchOp1 | StoreOp2;
//
// Two-operand; read both, write dest.
//
case Operation::ABCD: case Operation::SBCD:
case Operation::ADDb: case Operation::ADDw: case Operation::ADDl:
case Operation::ADDAw: case Operation::ADDAl:
case Operation::ADDXb: case Operation::ADDXw: case Operation::ADDXl:
case Operation::SUBb: case Operation::SUBw: case Operation::SUBl:
case Operation::SUBAw: case Operation::SUBAl:
case Operation::SUBXb: case Operation::SUBXw: case Operation::SUBXl:
case Operation::ORb: case Operation::ORw: case Operation::ORl:
case Operation::ANDb: case Operation::ANDw: case Operation::ANDl:
case Operation::EORb: case Operation::EORw: case Operation::EORl:
case Operation::DIVU: case Operation::DIVS:
case Operation::MULU: case Operation::MULS:
case Operation::ASLb: case Operation::ASLw: case Operation::ASLl:
case Operation::ASRb: case Operation::ASRw: case Operation::ASRl:
case Operation::LSLb: case Operation::LSLw: case Operation::LSLl:
case Operation::LSRb: case Operation::LSRw: case Operation::LSRl:
case Operation::ROLb: case Operation::ROLw: case Operation::ROLl:
case Operation::RORb: case Operation::RORw: case Operation::RORl:
case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl:
case Operation::ROXRb: case Operation::ROXRw: case Operation::ROXRl:
case Operation::BCHG:
case Operation::BCLR: case Operation::BSET:
return FetchOp1 | FetchOp2 | StoreOp2;
//
// Two-operand; read both, write source.
//
case Operation::DBcc:
return FetchOp1 | FetchOp2 | StoreOp1;
//
// Two-operand; read both, write both.
//
case Operation::EXG:
return FetchOp1 | FetchOp2 | StoreOp1 | StoreOp2;
//
// Two-operand; just write destination.
//
case Operation::LEA:
return StoreOp2;
}
}
}
}
#endif /* InstructionSets_68k_InstructionOperandFlags_hpp */

View File

@ -0,0 +1,120 @@
//
// InstructionOperandSize.hpp
// Clock Signal
//
// Created by Thomas Harte on 09/05/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_68k_InstructionOperandSize_hpp
#define InstructionSets_68k_InstructionOperandSize_hpp
namespace InstructionSet {
namespace M68k {
constexpr DataSize operand_size(Operation operation) {
switch(operation) {
// These are given a value arbitrarily, to
// complete the switch statement.
case Operation::Undefined:
case Operation::NOP:
case Operation::STOP:
case Operation::RESET:
case Operation::RTE: case Operation::RTR:
case Operation::TRAP:
case Operation::TRAPV:
case Operation::ABCD: case Operation::SBCD:
case Operation::NBCD:
case Operation::ADDb: case Operation::ADDXb:
case Operation::SUBb: case Operation::SUBXb:
case Operation::MOVEb:
case Operation::ORItoCCR:
case Operation::ANDItoCCR:
case Operation::EORItoCCR:
case Operation::BTST: case Operation::BCLR:
case Operation::BCHG: case Operation::BSET:
case Operation::CMPb: case Operation::TSTb:
case Operation::Bccb: case Operation::BSRb:
case Operation::CLRb:
case Operation::Scc:
case Operation::NEGXb: case Operation::NEGb:
case Operation::ASLb: case Operation::ASRb:
case Operation::LSLb: case Operation::LSRb:
case Operation::ROLb: case Operation::RORb:
case Operation::ROXLb: case Operation::ROXRb:
case Operation::ANDb: case Operation::EORb:
case Operation::NOTb: case Operation::ORb:
case Operation::TAS:
return DataSize::Byte;
case Operation::ADDw: case Operation::ADDAw:
case Operation::ADDXw: case Operation::SUBw:
case Operation::SUBAw: case Operation::SUBXw:
case Operation::MOVEw: case Operation::MOVEAw:
case Operation::ORItoSR:
case Operation::ANDItoSR:
case Operation::EORItoSR:
case Operation::MOVEtoSR:
case Operation::MOVEfromSR:
case Operation::MOVEtoCCR:
case Operation::CMPw: case Operation::CMPAw:
case Operation::TSTw:
case Operation::DBcc:
case Operation::Bccw: case Operation::BSRw:
case Operation::CLRw:
case Operation::NEGXw: case Operation::NEGw:
case Operation::ASLw: case Operation::ASLm:
case Operation::ASRw: case Operation::ASRm:
case Operation::LSLw: case Operation::LSLm:
case Operation::LSRw: case Operation::LSRm:
case Operation::ROLw: case Operation::ROLm:
case Operation::RORw: case Operation::RORm:
case Operation::ROXLw: case Operation::ROXLm:
case Operation::ROXRw: case Operation::ROXRm:
case Operation::MOVEMtoRw:
case Operation::MOVEMtoRl:
case Operation::MOVEMtoMw:
case Operation::MOVEMtoMl:
case Operation::MOVEPw:
case Operation::ANDw: case Operation::EORw:
case Operation::NOTw: case Operation::ORw:
case Operation::DIVU: case Operation::DIVS:
case Operation::MULU: case Operation::MULS:
case Operation::EXTbtow:
case Operation::LINKw:
case Operation::CHK:
return DataSize::Word;
case Operation::ADDl: case Operation::ADDAl:
case Operation::ADDXl: case Operation::SUBl:
case Operation::SUBAl: case Operation::SUBXl:
case Operation::MOVEl: case Operation::MOVEAl:
case Operation::LEA: case Operation::PEA:
case Operation::EXG: case Operation::SWAP:
case Operation::MOVEtoUSP:
case Operation::MOVEfromUSP:
case Operation::CMPl: case Operation::CMPAl:
case Operation::TSTl:
case Operation::JMP: case Operation::JSR:
case Operation::RTS:
case Operation::Bccl: case Operation::BSRl:
case Operation::CLRl:
case Operation::NEGXl: case Operation::NEGl:
case Operation::ASLl: case Operation::ASRl:
case Operation::LSLl: case Operation::LSRl:
case Operation::ROLl: case Operation::RORl:
case Operation::ROXLl: case Operation::ROXRl:
case Operation::MOVEPl:
case Operation::ANDl: case Operation::EORl:
case Operation::NOTl: case Operation::ORl:
case Operation::EXTwtol:
case Operation::UNLINK:
return DataSize::LongWord;
}
}
}
}
#endif /* InstructionSets_68k_InstructionOperandSize_hpp */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,295 @@
//
// Instruction.cpp
// Clock Signal
//
// Created by Thomas Harte on 12/05/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#include "Instruction.hpp"
#include <cassert>
using namespace InstructionSet::M68k;
std::string Preinstruction::operand_description(int index, int opcode) const {
switch(mode(index)) {
default: assert(false);
case AddressingMode::None:
return "";
case AddressingMode::DataRegisterDirect:
return std::string("D") + std::to_string(reg(index));
case AddressingMode::AddressRegisterDirect:
return std::string("A") + std::to_string(reg(index));
case AddressingMode::AddressRegisterIndirect:
return std::string("(A") + std::to_string(reg(index)) + ")";
case AddressingMode::AddressRegisterIndirectWithPostincrement:
return std::string("(A") + std::to_string(reg(index)) + ")+";
case AddressingMode::AddressRegisterIndirectWithPredecrement:
return std::string("-(A") + std::to_string(reg(index)) + ")";
case AddressingMode::AddressRegisterIndirectWithDisplacement:
return std::string("(d16, A") + std::to_string(reg(index)) + ")";
case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
return std::string("(d8, A") + std::to_string(reg(index)) + ", Xn)";
case AddressingMode::ProgramCounterIndirectWithDisplacement:
return "(d16, PC)";
case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
return "(d8, PC, Xn)";
case AddressingMode::AbsoluteShort:
return "(xxx).w";
case AddressingMode::AbsoluteLong:
return "(xxx).l";
case AddressingMode::ImmediateData:
return "#";
case AddressingMode::Quick:
if(opcode == -1) {
return "Q";
}
return std::to_string(int(quick(uint16_t(opcode), operation)));
}
}
std::string Preinstruction::to_string(int opcode) const {
bool flip_operands = false;
const char *instruction;
switch(operation) {
case Operation::Undefined: instruction = "None"; break;
case Operation::NOP: instruction = "NOP"; break;
case Operation::ABCD: instruction = "ABCD"; break;
case Operation::SBCD: instruction = "SBCD"; break;
case Operation::NBCD: instruction = "NBCD"; break;
case Operation::ADDb: instruction = "ADD.b"; break;
case Operation::ADDw: instruction = "ADD.w"; break;
case Operation::ADDl: instruction = "ADD.l"; break;
case Operation::ADDAw:
if(mode<0>() == AddressingMode::Quick) {
instruction = "ADD.w";
} else {
instruction = "ADDA.w";
}
break;
case Operation::ADDAl:
if(mode<0>() == AddressingMode::Quick) {
instruction = "ADD.l";
} else {
instruction = "ADDA.l";
}
break;
case Operation::ADDXb: instruction = "ADDX.b"; break;
case Operation::ADDXw: instruction = "ADDX.w"; break;
case Operation::ADDXl: instruction = "ADDX.l"; break;
case Operation::SUBb: instruction = "SUB.b"; break;
case Operation::SUBw: instruction = "SUB.w"; break;
case Operation::SUBl: instruction = "SUB.l"; break;
case Operation::SUBAw:
if(mode<0>() == AddressingMode::Quick) {
instruction = "SUB.w";
} else {
instruction = "SUBA.w";
}
break;
case Operation::SUBAl:
if(mode<0>() == AddressingMode::Quick) {
instruction = "SUB.l";
} else {
instruction = "SUBA.l";
}
break;
case Operation::SUBXb: instruction = "SUBX.b"; break;
case Operation::SUBXw: instruction = "SUBX.w"; break;
case Operation::SUBXl: instruction = "SUBX.l"; break;
case Operation::MOVEb: instruction = "MOVE.b"; break;
case Operation::MOVEw: instruction = "MOVE.w"; break;
case Operation::MOVEl:
if(mode<0>() == AddressingMode::Quick) {
instruction = "MOVE.q";
} else {
instruction = "MOVE.l";
}
break;
case Operation::MOVEAw: instruction = "MOVEA.w"; break;
case Operation::MOVEAl: instruction = "MOVEA.l"; break;
case Operation::LEA: instruction = "LEA"; break;
case Operation::PEA: instruction = "PEA"; break;
case Operation::MOVEtoSR: instruction = "MOVEtoSR"; break;
case Operation::MOVEfromSR: instruction = "MOVEfromSR"; break;
case Operation::MOVEtoCCR: instruction = "MOVEtoCCR"; break;
case Operation::MOVEtoUSP: instruction = "MOVEtoUSP"; break;
case Operation::MOVEfromUSP: instruction = "MOVEfromUSP"; break;
case Operation::ORItoSR: instruction = "ORItoSR"; break;
case Operation::ORItoCCR: instruction = "ORItoCCR"; break;
case Operation::ANDItoSR: instruction = "ANDItoSR"; break;
case Operation::ANDItoCCR: instruction = "ANDItoCCR"; break;
case Operation::EORItoSR: instruction = "EORItoSR"; break;
case Operation::EORItoCCR: instruction = "EORItoCCR"; break;
case Operation::BTST: instruction = "BTST"; break;
case Operation::BCLR: instruction = "BCLR"; break;
case Operation::BCHG: instruction = "BCHG"; break;
case Operation::BSET: instruction = "BSET"; break;
case Operation::CMPb: instruction = "CMP.b"; break;
case Operation::CMPw: instruction = "CMP.w"; break;
case Operation::CMPl: instruction = "CMP.l"; break;
case Operation::CMPAw: instruction = "CMPA.w"; break;
case Operation::CMPAl: instruction = "CMPA.l"; break;
case Operation::TSTb: instruction = "TST.b"; break;
case Operation::TSTw: instruction = "TST.w"; break;
case Operation::TSTl: instruction = "TST.l"; break;
case Operation::JMP: instruction = "JMP"; break;
case Operation::JSR: instruction = "JSR"; break;
case Operation::RTS: instruction = "RTS"; break;
case Operation::DBcc: instruction = "DBcc"; break;
case Operation::Scc: instruction = "Scc"; break;
case Operation::Bccb:
case Operation::Bccl:
case Operation::Bccw: instruction = "Bcc"; break;
case Operation::BSRb:
case Operation::BSRl:
case Operation::BSRw: instruction = "BSR"; break;
case Operation::CLRb: instruction = "CLR.b"; break;
case Operation::CLRw: instruction = "CLR.w"; break;
case Operation::CLRl: instruction = "CLR.l"; break;
case Operation::NEGXb: instruction = "NEGX.b"; break;
case Operation::NEGXw: instruction = "NEGX.w"; break;
case Operation::NEGXl: instruction = "NEGX.l"; break;
case Operation::NEGb: instruction = "NEG.b"; break;
case Operation::NEGw: instruction = "NEG.w"; break;
case Operation::NEGl: instruction = "NEG.l"; break;
case Operation::ASLb: instruction = "ASL.b"; break;
case Operation::ASLw: instruction = "ASL.w"; break;
case Operation::ASLl: instruction = "ASL.l"; break;
case Operation::ASLm: instruction = "ASL.w"; break;
case Operation::ASRb: instruction = "ASR.b"; break;
case Operation::ASRw: instruction = "ASR.w"; break;
case Operation::ASRl: instruction = "ASR.l"; break;
case Operation::ASRm: instruction = "ASR.w"; break;
case Operation::LSLb: instruction = "LSL.b"; break;
case Operation::LSLw: instruction = "LSL.w"; break;
case Operation::LSLl: instruction = "LSL.l"; break;
case Operation::LSLm: instruction = "LSL.w"; break;
case Operation::LSRb: instruction = "LSR.b"; break;
case Operation::LSRw: instruction = "LSR.w"; break;
case Operation::LSRl: instruction = "LSR.l"; break;
case Operation::LSRm: instruction = "LSR.w"; break;
case Operation::ROLb: instruction = "ROL.b"; break;
case Operation::ROLw: instruction = "ROL.w"; break;
case Operation::ROLl: instruction = "ROL.l"; break;
case Operation::ROLm: instruction = "ROL.w"; break;
case Operation::RORb: instruction = "ROR.b"; break;
case Operation::RORw: instruction = "ROR.w"; break;
case Operation::RORl: instruction = "ROR.l"; break;
case Operation::RORm: instruction = "ROR.w"; break;
case Operation::ROXLb: instruction = "ROXL.b"; break;
case Operation::ROXLw: instruction = "ROXL.w"; break;
case Operation::ROXLl: instruction = "ROXL.l"; break;
case Operation::ROXLm: instruction = "ROXL.w"; break;
case Operation::ROXRb: instruction = "ROXR.b"; break;
case Operation::ROXRw: instruction = "ROXR.w"; break;
case Operation::ROXRl: instruction = "ROXR.l"; break;
case Operation::ROXRm: instruction = "ROXR.w"; break;
case Operation::MOVEMtoMl: instruction = "MOVEM.l"; break;
case Operation::MOVEMtoMw: instruction = "MOVEM.w"; break;
case Operation::MOVEMtoRl:
instruction = "MOVEM.l";
flip_operands = true;
break;
case Operation::MOVEMtoRw:
instruction = "MOVEM.w";
flip_operands = true;
break;
case Operation::MOVEPl: instruction = "MOVEP.l"; break;
case Operation::MOVEPw: instruction = "MOVEP.w"; break;
case Operation::ANDb: instruction = "AND.b"; break;
case Operation::ANDw: instruction = "AND.w"; break;
case Operation::ANDl: instruction = "AND.l"; break;
case Operation::EORb: instruction = "EOR.b"; break;
case Operation::EORw: instruction = "EOR.w"; break;
case Operation::EORl: instruction = "EOR.l"; break;
case Operation::NOTb: instruction = "NOT.b"; break;
case Operation::NOTw: instruction = "NOT.w"; break;
case Operation::NOTl: instruction = "NOT.l"; break;
case Operation::ORb: instruction = "OR.b"; break;
case Operation::ORw: instruction = "OR.w"; break;
case Operation::ORl: instruction = "OR.l"; break;
case Operation::MULU: instruction = "MULU"; break;
case Operation::MULS: instruction = "MULS"; break;
case Operation::DIVU: instruction = "DIVU"; break;
case Operation::DIVS: instruction = "DIVS"; break;
case Operation::RTE: instruction = "RTE"; break;
case Operation::RTR: instruction = "RTR"; break;
case Operation::TRAP: instruction = "TRAP"; break;
case Operation::TRAPV: instruction = "TRAPV"; break;
case Operation::CHK: instruction = "CHK"; break;
case Operation::EXG: instruction = "EXG"; break;
case Operation::SWAP: instruction = "SWAP"; break;
case Operation::TAS: instruction = "TAS"; break;
case Operation::EXTbtow: instruction = "EXT.w"; break;
case Operation::EXTwtol: instruction = "EXT.l"; break;
case Operation::LINKw: instruction = "LINK"; break;
case Operation::UNLINK: instruction = "UNLINK"; break;
case Operation::STOP: instruction = "STOP"; break;
case Operation::RESET: instruction = "RESET"; break;
default:
assert(false);
}
const std::string operand1 = operand_description(0 ^ int(flip_operands), opcode);
const std::string operand2 = operand_description(1 ^ int(flip_operands), opcode);
std::string result = instruction;
if(!operand1.empty()) result += std::string(" ") + operand1;
if(!operand2.empty()) result += std::string(", ") + operand2;
return result;
}

View File

@ -9,9 +9,12 @@
#ifndef InstructionSets_68k_Instruction_hpp
#define InstructionSets_68k_Instruction_hpp
#include <cstdint>
#include "Model.hpp"
#include <cassert>
#include <cstdint>
#include <string>
namespace InstructionSet {
namespace M68k {
@ -70,7 +73,9 @@ enum class Operation: uint8_t {
ROXLb, ROXLw, ROXLl, ROXLm,
ROXRb, ROXRw, ROXRl, ROXRm,
MOVEMl, MOVEMw,
MOVEMtoRl, MOVEMtoRw,
MOVEMtoMl, MOVEMtoMw,
MOVEPl, MOVEPw,
ANDb, ANDw, ANDl,
@ -111,6 +116,7 @@ constexpr bool requires_supervisor(Operation op) {
case Operation::EORItoSR: case Operation::RTE:
case Operation::RESET: case Operation::STOP:
case Operation::MOVEtoUSP: case Operation::MOVEfromUSP:
case Operation::MOVEtoSR:
return true;
default:
@ -125,108 +131,15 @@ enum class DataSize {
};
/// Classifies operations by the size of their memory accesses, if any.
constexpr DataSize size(Operation operation) {
switch(operation) {
// These are given a value arbitrarily, to
// complete the switch statement.
case Operation::Undefined:
case Operation::NOP:
case Operation::STOP:
case Operation::RESET:
case Operation::RTE: case Operation::RTR:
case Operation::TRAP:
case Operation::TRAPV:
///
/// For any operations that don't fit the neat model of reading one or two operands,
/// then writing zero or one, the size determines the data size of the operands only,
/// not any other accesses.
constexpr DataSize operand_size(Operation operation);
case Operation::ABCD: case Operation::SBCD:
case Operation::NBCD:
case Operation::ADDb: case Operation::ADDXb:
case Operation::SUBb: case Operation::SUBXb:
case Operation::MOVEb:
case Operation::ORItoCCR:
case Operation::ANDItoCCR:
case Operation::EORItoCCR:
case Operation::BTST: case Operation::BCLR:
case Operation::BCHG: case Operation::BSET:
case Operation::CMPb: case Operation::TSTb:
case Operation::Bccb: case Operation::BSRb:
case Operation::CLRb:
case Operation::NEGXb: case Operation::NEGb:
case Operation::ASLb: case Operation::ASRb:
case Operation::LSLb: case Operation::LSRb:
case Operation::ROLb: case Operation::RORb:
case Operation::ROXLb: case Operation::ROXRb:
case Operation::ANDb: case Operation::EORb:
case Operation::NOTb: case Operation::ORb:
case Operation::CHK:
case Operation::TAS:
return DataSize::Byte;
case Operation::ADDw: case Operation::ADDAw:
case Operation::ADDXw: case Operation::SUBw:
case Operation::SUBAw: case Operation::SUBXw:
case Operation::MOVEw: case Operation::MOVEAw:
case Operation::ORItoSR:
case Operation::ANDItoSR:
case Operation::EORItoSR:
case Operation::MOVEtoSR:
case Operation::MOVEfromSR:
case Operation::MOVEtoCCR:
case Operation::CMPw: case Operation::CMPAw:
case Operation::TSTw:
case Operation::DBcc: case Operation::Scc:
case Operation::Bccw: case Operation::BSRw:
case Operation::CLRw:
case Operation::NEGXw: case Operation::NEGw:
case Operation::ASLw: case Operation::ASLm:
case Operation::ASRw: case Operation::ASRm:
case Operation::LSLw: case Operation::LSLm:
case Operation::LSRw: case Operation::LSRm:
case Operation::ROLw: case Operation::ROLm:
case Operation::RORw: case Operation::RORm:
case Operation::ROXLw: case Operation::ROXLm:
case Operation::ROXRw: case Operation::ROXRm:
case Operation::MOVEMw:
case Operation::MOVEPw:
case Operation::ANDw: case Operation::EORw:
case Operation::NOTw: case Operation::ORw:
case Operation::DIVU: case Operation::DIVS:
case Operation::MULU: case Operation::MULS:
case Operation::EXTbtow:
case Operation::LINKw:
return DataSize::Word;
case Operation::ADDl: case Operation::ADDAl:
case Operation::ADDXl: case Operation::SUBl:
case Operation::SUBAl: case Operation::SUBXl:
case Operation::MOVEl: case Operation::MOVEAl:
case Operation::LEA: case Operation::PEA:
case Operation::EXG: case Operation::SWAP:
case Operation::MOVEtoUSP:
case Operation::MOVEfromUSP:
case Operation::CMPl: case Operation::CMPAl:
case Operation::TSTl:
case Operation::JMP: case Operation::JSR:
case Operation::RTS:
case Operation::Bccl: case Operation::BSRl:
case Operation::CLRl:
case Operation::NEGXl: case Operation::NEGl:
case Operation::ASLl: case Operation::ASRl:
case Operation::LSLl: case Operation::LSRl:
case Operation::ROLl: case Operation::RORl:
case Operation::ROXLl: case Operation::ROXRl:
case Operation::MOVEMl:
case Operation::MOVEPl:
case Operation::ANDl: case Operation::EORl:
case Operation::NOTl: case Operation::ORl:
case Operation::EXTwtol:
case Operation::UNLINK:
return DataSize::LongWord;
}
}
template <Operation op>
constexpr uint32_t quick(uint16_t instruction) {
switch(op) {
template <Operation t_op = Operation::Undefined>
constexpr uint32_t quick(uint16_t instruction, Operation r_op = Operation::Undefined) {
switch((t_op != Operation::Undefined) ? t_op : r_op) {
case Operation::Bccb:
case Operation::BSRb:
case Operation::MOVEl: return uint32_t(int8_t(instruction));
@ -239,19 +152,22 @@ constexpr uint32_t quick(uint16_t instruction) {
}
}
constexpr uint32_t quick(Operation op, uint16_t instruction) {
switch(op) {
case Operation::MOVEl: return quick<Operation::MOVEl>(instruction);
case Operation::Bccb: return quick<Operation::Bccb>(instruction);
case Operation::BSRb: return quick<Operation::BSRb>(instruction);
case Operation::TRAP: return quick<Operation::TRAP>(instruction);
static constexpr uint8_t FetchOp1 = (1 << 0);
static constexpr uint8_t FetchOp2 = (1 << 1);
static constexpr uint8_t StoreOp1 = (1 << 2);
static constexpr uint8_t StoreOp2 = (1 << 3);
default:
// ADDw is arbitrary; anything other than those listed above will do.
return quick<Operation::ADDw>(instruction);
}
}
/*!
Provides a bitfield with a value in the range 015 indicating which of the provided operation's
operands are accessed via standard fetch and store cycles; the bitfield is composted of
[Fetch/Store]Op[1/2] as defined above.
Unusual bus sequences, such as TAS or MOVEM, are not described here.
*/
template <Model model, Operation t_operation = Operation::Undefined>
uint8_t operand_flags(Operation r_operation = Operation::Undefined);
/// Lists the various condition codes used by the 680x0.
enum class Condition {
True = 0x00, False = 0x01,
High = 0x02, LowOrSame = 0x03,
@ -303,23 +219,23 @@ enum class AddressingMode: uint8_t {
AddressRegisterIndirectWithDisplacement = 0b00'101,
/// (d8, An, Xn)
AddressRegisterIndirectWithIndex8bitDisplacement = 0b00'110,
/// (bd, An, Xn)
/// (bd, An, Xn) [68020+]
AddressRegisterIndirectWithIndexBaseDisplacement = 0b10'000,
/// ([bd, An, Xn], od)
/// ([bd, An, Xn], od) [68020+]
MemoryIndirectPostindexed = 0b10'001,
/// ([bd, An], Xn, od)
/// ([bd, An], Xn, od) [68020+]
MemoryIndirectPreindexed = 0b10'010,
/// (d16, PC)
ProgramCounterIndirectWithDisplacement = 0b01'010,
/// (d8, PC, Xn)
ProgramCounterIndirectWithIndex8bitDisplacement = 0b01'011,
/// (bd, PC, Xn)
/// (bd, PC, Xn) [68020+]
ProgramCounterIndirectWithIndexBaseDisplacement = 0b10'011,
/// ([bd, PC, Xn], od)
/// ([bd, PC, Xn], od) [68020+]
ProgramCounterMemoryIndirectPostindexed = 0b10'100,
/// ([bc, PC], Xn, od)
/// ([bc, PC], Xn, od) [68020+]
ProgramCounterMemoryIndirectPreindexed = 0b10'101,
/// (xxx).W
@ -356,27 +272,44 @@ class Preinstruction {
// For one-operand instructions, only argument 0 will
// be provided, and will be a source and/or destination as
// per the semantics of the operation.
//
// The versions templated on index do a range check;
// if using the runtime versions then results for indices
// other than 0 and 1 are undefined.
AddressingMode mode(int index) const {
return AddressingMode(operands_[index] >> 3);
}
template <int index> AddressingMode mode() const {
if constexpr (index > 1) {
return AddressingMode::None;
}
return AddressingMode(operands_[index] & 0x1f);
return mode(index);
}
int reg(int index) const {
return operands_[index] & 7;
}
template <int index> int reg() const {
if constexpr (index > 1) {
return 0;
}
return operands_[index] >> 5;
return reg(index);
}
bool requires_supervisor() {
/// @returns 07 to indicate data registers 0 to 7, or 815 to indicate address registers 0 to 7 respectively.
/// Provides undefined results if the addressing mode is not either @c DataRegisterDirect or
/// @c AddressRegisterDirect.
int lreg(int index) const {
return operands_[index] & 0xf;
}
bool requires_supervisor() const {
return flags_ & 0x80;
}
DataSize size() {
DataSize operand_size() const {
return DataSize(flags_ & 0x03);
}
Condition condition() {
Condition condition() const {
return Condition((flags_ >> 2) & 0x0f);
}
@ -384,6 +317,8 @@ class Preinstruction {
uint8_t operands_[2] = { uint8_t(AddressingMode::None), uint8_t(AddressingMode::None)};
uint8_t flags_ = 0;
std::string operand_description(int index, int opcode) const;
public:
Preinstruction(
Operation operation,
@ -393,8 +328,8 @@ class Preinstruction {
DataSize size,
Condition condition) : operation(operation)
{
operands_[0] = uint8_t(op1_mode) | uint8_t(op1_reg << 5);
operands_[1] = uint8_t(op2_mode) | uint8_t(op2_reg << 5);
operands_[0] = uint8_t((uint8_t(op1_mode) << 3) | op1_reg);
operands_[1] = uint8_t((uint8_t(op2_mode) << 3) | op2_reg);
flags_ = uint8_t(
(is_supervisor ? 0x80 : 0x00) |
(int(condition) << 2) |
@ -403,9 +338,17 @@ class Preinstruction {
}
Preinstruction() {}
/// Produces a string description of this instruction; if @c opcode
/// is supplied then any quick fields in this instruction will be decoded;
/// otherwise they'll be printed as just 'Q'.
std::string to_string(int opcode = -1) const;
};
}
}
#include "Implementation/InstructionOperandSize.hpp"
#include "Implementation/InstructionOperandFlags.hpp"
#endif /* InstructionSets_68k_Instruction_hpp */

View File

@ -0,0 +1,168 @@
//
// Perform.hpp
// Clock Signal
//
// Created by Thomas Harte on 28/04/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_M68k_Perform_h
#define InstructionSets_M68k_Perform_h
#include "Model.hpp"
#include "Instruction.hpp"
#include "Status.hpp"
#include "../../Numeric/RegisterSizes.hpp"
namespace InstructionSet {
namespace M68k {
struct NullFlowController {
//
// Various operation-specific did-perform notfications; these all relate to operations
// with variable timing on a 68000, providing the fields that contribute to that timing.
//
/// Indicates that a @c MULU was performed, providing the @c source operand.
template <typename IntT> void did_mulu(IntT) {}
/// Indicates that a @c MULS was performed, providing the @c source operand.
template <typename IntT> void did_muls(IntT) {}
/// Indicates that a @c CHK was performed, along with whether the result @c was_under zero or @c was_over the source operand.
void did_chk([[maybe_unused]] bool was_under, [[maybe_unused]] bool was_over) {}
/// Indicates an in-register shift or roll occurred, providing the number of bits shifted by.
void did_shift([[maybe_unused]] int bit_count) {}
/// Indicates that a @c DIVU was performed, providing the @c dividend and @c divisor.
/// If @c did_overflow is @c true then the divide ended in overflow.
template <bool did_overflow> void did_divu([[maybe_unused]] uint32_t dividend, [[maybe_unused]] uint32_t divisor) {}
/// Indicates that a @c DIVS was performed, providing the @c dividend and @c divisor.
/// If @c did_overflow is @c true then the divide ended in overflow.
template <bool did_overflow> void did_divs([[maybe_unused]] int32_t dividend, [[maybe_unused]] int32_t divisor) {}
/// Indicates that a bit-manipulation operation (i.e. BTST, BCHG or BSET) was performed, affecting the bit at posiition @c bit_position.
void did_bit_op([[maybe_unused]] int bit_position) {}
/// Provides a notification that the upper byte of the status register has been affected by the current instruction;
/// this gives an opportunity to track the supervisor flag.
void did_update_status() {}
//
// Operations that don't fit the reductive load-modify-store pattern; these are requests from perform
// that the flow controller do something (and, correspondingly, do not have empty implementations).
//
// All offsets are the native values as encoded in the corresponding operations.
//
/// If @c matched_condition is @c true, apply the @c offset to the PC.
template <typename IntT> void complete_bcc(bool matched_condition, IntT offset);
/// If both @c matched_condition and @c overflowed are @c false, apply @c offset to the PC.
void complete_dbcc(bool matched_condition, bool overflowed, int16_t offset);
/// Push the program counter of the next instruction to the stack, and add @c offset to the PC.
void bsr(uint32_t offset);
/// Push the program counter of the next instruction to the stack, and load @c offset to the PC.
void jsr(uint32_t address);
/// Set the program counter to @c address.
void jmp(uint32_t address);
/// Pop a word from the stack and use that to set the status condition codes. Then pop a new value for the PC.
void rtr();
/// Pop a word from the stack and use that to set the entire status register. Then pop a new value for the PC.
void rte();
/// Pop a new value for the PC from the stack.
void rts();
/// Put the processor into the stopped state, waiting for interrupts.
void stop();
/// Assert the reset output.
void reset();
/// Perform LINK using the address register identified by @c instruction and the specified @c offset.
void link(Preinstruction instruction, uint32_t offset);
/// Perform unlink, with @c address being the target address register.
void unlink(uint32_t &address);
/// Push @c address to the stack.
void pea(uint32_t address);
/// Replace the current user stack pointer with @c address.
/// The processor is guranteed to be in supervisor mode.
void move_to_usp(uint32_t address);
/// Put the value of the user stack pointer into @c address.
/// The processor is guranteed to be in supervisor mode.
void move_from_usp(uint32_t &address);
/// Perform an atomic TAS cycle; if @c instruction indicates that this is a TAS Dn then
/// perform the TAS directly upon that register; otherwise perform it on the memory at
/// @c address. If this is a TAS Dn then @c address will contain the initial value of
/// the register.
void tas(Preinstruction instruction, uint32_t address);
/// Use @c instruction to determine the direction of this MOVEP and perform it;
/// @c source is the first operand provided to the MOVEP — either an address or register
/// contents — and @c dest is the second.
///
/// @c IntT may be either uint16_t or uint32_t.
template <typename IntT> void movep(Preinstruction instruction, uint32_t source, uint32_t dest);
/// Perform a MOVEM to memory, from registers. @c instruction will indicate the mask as the first operand,
/// and the target address and addressing mode as the second; the mask and address are also supplied
/// as @c mask and @c address. If the addressing mode is -(An) then the address register will have
/// been decremented already.
///
/// The receiver is responsible for updating the address register if applicable.
///
/// @c IntT may be either uint16_t or uint32_t.
template <typename IntT> void movem_toM(Preinstruction instruction, uint32_t mask, uint32_t address);
/// Perform a MOVEM to registers, from memory. @c instruction will indicate the mask as the first operand,
/// and the target address and addressing mode as the second; the mask and address are also supplied
/// as @c mask and @c address. If the addressing mode is (An)+ then the address register will have been
/// incremented, but @c address will be its value before that occurred.
///
/// The receiver is responsible for updating the address register if applicable.
///
/// @c IntT may be either uint16_t or uint32_t.
template <typename IntT> void movem_toR(Preinstruction instruction, uint32_t mask, uint32_t address);
/// Raises a short-form exception using @c vector. If @c use_current_instruction_pc is @c true,
/// the program counter for the current instruction is included in the resulting stack frame. Otherwise the program
/// counter for the next instruction is used.
template <bool use_current_instruction_pc = true>
void raise_exception([[maybe_unused]] int vector);
};
/// Performs @c instruction using @c source and @c dest (one or both of which may be ignored as per
/// the semantics of the operation).
///
/// Any change in processor status will be applied to @c status. If this operation does not fit the reductive model
/// of being a read and possibly a modify and possibly a write of up to two operands then the @c flow_controller
/// will be asked to fill in the gaps.
///
/// If the template parameter @c operation is not @c Operation::Undefined then that operation will be performed, ignoring
/// whatever is specifed in @c instruction. This allows selection either at compile time or at run time; per Godbolt all modern
/// compilers seem to be smart enough fully to optimise the compile-time case.
template <
Model model,
typename FlowController,
Operation operation = Operation::Undefined
> void perform(Preinstruction instruction, CPU::RegisterPair32 &source, CPU::RegisterPair32 &dest, Status &status, FlowController &flow_controller);
}
}
#include "Implementation/PerformImplementation.hpp"
#endif /* InstructionSets_M68k_Perform_h */

View File

@ -0,0 +1,127 @@
//
// Status.hpp
// Clock Signal
//
// Created by Thomas Harte on 28/04/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef InstructionSets_M68k_Status_h
#define InstructionSets_M68k_Status_h
#include "Instruction.hpp"
namespace InstructionSet {
namespace M68k {
namespace ConditionCode {
static constexpr uint16_t Carry = 1 << 0;
static constexpr uint16_t Overflow = 1 << 1;
static constexpr uint16_t Zero = 1 << 2;
static constexpr uint16_t Negative = 1 << 3;
static constexpr uint16_t Extend = 1 << 4;
static constexpr uint16_t Supervisor = 1 << 13;
static constexpr uint16_t Trace = 1 << 15;
static constexpr uint16_t InterruptPriorityMask = 0b111 << 8;
}
struct Status {
/// Generally holds an unevaluated flag for potential later lazy evaluation; it'll be zero for one outcome,
/// non-zero for the other, but no guarantees are made about the potential range of non-zero values.
using FlagT = uint_fast32_t;
/* b15 */
FlagT trace_flag = 0; // The trace flag is set if and only if this value is non-zero.
/* b13 */
bool is_supervisor = false; // true => processor is in supervisor mode; false => it isn't.
/* b7b9 */
int interrupt_level = 0; // The direct integer value of the current interrupt level.
// Values of 8 or greater have undefined meaning.
/* b0b4 */
FlagT zero_result = 0; // The zero flag is set if and only if this value is zero.
FlagT carry_flag = 0; // The carry flag is set if and only if this value is non-zero.
FlagT extend_flag = 0; // The extend flag is set if and only if this value is non-zero.
FlagT overflow_flag = 0; // The overflow flag is set if and only if this value is non-zero.
FlagT negative_flag = 0; // The negative flag is set if and only this value is non-zero.
/// Gets the current condition codes.
constexpr uint16_t ccr() const {
return
(carry_flag ? ConditionCode::Carry : 0) |
(overflow_flag ? ConditionCode::Overflow : 0) |
(!zero_result ? ConditionCode::Zero : 0) |
(negative_flag ? ConditionCode::Negative : 0) |
(extend_flag ? ConditionCode::Extend : 0);
}
/// Sets the current condition codes.
constexpr void set_ccr(uint16_t ccr) {
carry_flag = ccr & ConditionCode::Carry;
overflow_flag = ccr & ConditionCode::Overflow;
zero_result = ~ccr & ConditionCode::Zero;
negative_flag = ccr & ConditionCode::Negative;
extend_flag = ccr & ConditionCode::Extend;
}
/// Gets the current value of the status register.
constexpr uint16_t status() const {
return uint16_t(
ccr() |
(interrupt_level << 8) |
(trace_flag ? ConditionCode::Trace : 0) |
(is_supervisor ? ConditionCode::Supervisor : 0)
);
}
/// Sets the current value of the status register;
/// @returns @c true if the processor finishes in supervisor mode; @c false otherwise.
constexpr bool set_status(uint16_t status) {
set_ccr(status);
interrupt_level = (status >> 8) & 7;
trace_flag = status & ConditionCode::Trace;
is_supervisor = status & ConditionCode::Supervisor;
return is_supervisor;
}
/// Evaluates @c condition.
bool evaluate_condition(Condition condition) {
switch(condition) {
default:
case Condition::True: return true;
case Condition::False: return false;
case Condition::High: return zero_result && !carry_flag;
case Condition::LowOrSame: return !zero_result || carry_flag;
case Condition::CarryClear: return !carry_flag;
case Condition::CarrySet: return carry_flag;
case Condition::NotEqual: return zero_result;
case Condition::Equal: return !zero_result;
case Condition::OverflowClear: return !overflow_flag;
case Condition::OverflowSet: return overflow_flag;
case Condition::Positive: return !negative_flag;
case Condition::Negative: return negative_flag;
case Condition::GreaterThanOrEqual:
return (negative_flag && overflow_flag) || (!negative_flag && !overflow_flag);
case Condition::LessThan:
return (negative_flag && !overflow_flag) || (!negative_flag && overflow_flag);
case Condition::GreaterThan:
return zero_result && ((negative_flag && overflow_flag) || (!negative_flag && !overflow_flag));
case Condition::LessThanOrEqual:
return !zero_result || (negative_flag && !overflow_flag) || (!negative_flag && overflow_flag);
}
}
};
}
}
#endif /* InstructionSets_M68k_Status_h */

84
Numeric/RegisterSizes.hpp Normal file
View File

@ -0,0 +1,84 @@
//
// RegisterSizes.hpp
// Clock Signal
//
// Created by Thomas Harte on 14/05/2017.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef RegisterSizes_hpp
#define RegisterSizes_hpp
#include <cstdint>
#pragma pack(push, 1)
// Unions below assume a modern consumer architecture,
// and that this compiler offers C-style anonymous structs.
namespace CPU {
/// Provides access to all intermediate parts of a larger int.
template <typename Full, typename Half> union RegisterPair {
RegisterPair(Full v) : full(v) {}
RegisterPair() {}
Full full;
#if TARGET_RT_BIG_ENDIAN
struct {
Half high, low;
} halves;
#else
struct {
Half low, high;
} halves;
#endif
};
using RegisterPair16 = RegisterPair<uint16_t, uint8_t>;
using RegisterPair32 = RegisterPair<uint32_t, RegisterPair16>;
/// Provides access to lower portions of a larger int.
template <typename IntT> union SlicedInt {};
template <> union SlicedInt<uint16_t> {
struct {
uint16_t w;
};
struct {
#if TARGET_RT_BIG_ENDIAN
uint8_t __padding[1];
#endif
uint8_t b;
};
};
template <> union SlicedInt<uint32_t> {
struct {
uint32_t l;
};
struct {
#if TARGET_RT_BIG_ENDIAN
uint16_t __padding[1];
#endif
uint16_t w;
};
struct {
#if TARGET_RT_BIG_ENDIAN
uint8_t __padding[3];
#endif
uint8_t b;
};
};
using SlicedInt16 = SlicedInt<uint16_t>;
using SlicedInt32 = SlicedInt<uint32_t>;
}
#pragma pack(pop)
#endif /* RegisterSizes_hpp */

View File

@ -131,6 +131,9 @@
4B0ACC3223775819008902D0 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0ACC2223775819008902D0 /* Atari2600.cpp */; };
4B0ACC3323775819008902D0 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0ACC2223775819008902D0 /* Atari2600.cpp */; };
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; };
4B0DA67B282DCDF100C12F17 /* Instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0DA67A282DCC4200C12F17 /* Instruction.cpp */; };
4B0DA67C282DCDF300C12F17 /* Instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0DA67A282DCC4200C12F17 /* Instruction.cpp */; };
4B0DA67D282DCDF300C12F17 /* Instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0DA67A282DCC4200C12F17 /* Instruction.cpp */; };
4B0E04EA1FC9E5DA00F43484 /* CAS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04E81FC9E5DA00F43484 /* CAS.cpp */; };
4B0E04EB1FC9E78800F43484 /* CAS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04E81FC9E5DA00F43484 /* CAS.cpp */; };
4B0E04F11FC9EA9500F43484 /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */; };
@ -311,6 +314,36 @@
4B74CF86231370BC00500CE8 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF83231370BC00500CE8 /* MacintoshVolume.cpp */; };
4B75F979280D7C5100121055 /* 68000DecoderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B75F978280D7C5100121055 /* 68000DecoderTests.mm */; };
4B75F97B280D7C7700121055 /* 68000 Decoding in Resources */ = {isa = PBXBuildFile; fileRef = 4B75F97A280D7C7700121055 /* 68000 Decoding */; };
4B7752A628217DF80073E2C5 /* Dave.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFEA2ED2682A7B900EBF94C /* Dave.cpp */; };
4B7752A728217E060073E2C5 /* Blitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9EC0E126AA27BA0060A31F /* Blitter.cpp */; };
4B7752A828217E110073E2C5 /* Nick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CAA26783E2000CA44E8 /* Nick.cpp */; };
4B7752A928217E200073E2C5 /* 65816Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */; };
4B7752AA28217E370073E2C5 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; };
4B7752AB28217E560073E2C5 /* SZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B946326377C0200E7097C /* SZX.cpp */; };
4B7752AC28217E6E0073E2C5 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F1BB02602645900B85C66 /* StaticAnalyser.cpp */; };
4B7752AD28217E770073E2C5 /* AmigaADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC080C826A238CC00D03FD8 /* AmigaADF.cpp */; };
4B7752AE28217E830073E2C5 /* 2MG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B80CD74256CA15E00176FCC /* 2MG.cpp */; };
4B7752AF28217E890073E2C5 /* DAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE8EB6425C750B50040BC40 /* DAT.cpp */; };
4B7752B028217E9A0073E2C5 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE211FE253FC80900435408 /* StaticAnalyser.cpp */; };
4B7752B128217EA30073E2C5 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC080CF26A257A200D03FD8 /* StaticAnalyser.cpp */; };
4B7752B228217EAE0073E2C5 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CA726781D6500CA44E8 /* StaticAnalyser.cpp */; };
4B7752B328217EB90073E2C5 /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B58F4246CC4E8009C171E /* State.cpp */; };
4B7752B428217ECB0073E2C5 /* ZXSpectrumTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F1C212605996900B85C66 /* ZXSpectrumTAP.cpp */; };
4B7752B528217ED30073E2C5 /* SNA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DD3842634D37E00B3C866 /* SNA.cpp */; };
4B7752B628217EE70073E2C5 /* DSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7CC263E33B10092AEE1 /* DSK.cpp */; };
4B7752B728217EF40073E2C5 /* Chipset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9EC0E426AA4A660060A31F /* Chipset.cpp */; };
4B7752B828217F110073E2C5 /* Amiga.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC080D826A25ADA00D03FD8 /* Amiga.cpp */; };
4B7752B928217F140073E2C5 /* Audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2130E0273A7A0A008A77B4 /* Audio.cpp */; };
4B7752BA28217F160073E2C5 /* Bitplanes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C681C2751A104001671EC /* Bitplanes.cpp */; };
4B7752BB28217F1A0073E2C5 /* Copper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC6236C26F4235400F83DFE /* Copper.cpp */; };
4B7752BC28217F1D0073E2C5 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1A1B1C27320FBB00119335 /* Disk.cpp */; };
4B7752BD28217F200073E2C5 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9EC0E826B384080060A31F /* Keyboard.cpp */; };
4B7752BE28217F220073E2C5 /* MouseJoystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C6818275196E8001671EC /* MouseJoystick.cpp */; };
4B7752BF28217F250073E2C5 /* Sprites.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C681427517A59001671EC /* Sprites.cpp */; };
4B7752C028217F3D0073E2C5 /* Line.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0ACC00237756EC008902D0 /* Line.cpp */; };
4B7752C128217F490073E2C5 /* FAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B477709268FBE4D005C2340 /* FAT.cpp */; };
4B7752C228217F5C0073E2C5 /* Spectrum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5D5C9525F56FC7001B4623 /* Spectrum.cpp */; };
4B7752C328217F720073E2C5 /* Z80.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DD39526360DDF00B3C866 /* Z80.cpp */; };
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */; };
4B778EF023A5D68C0000D260 /* 68000Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */; };
4B778EF123A5D6B50000D260 /* 9918.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04F91FC9FA3100F43484 /* 9918.cpp */; };
@ -444,6 +477,8 @@
4B7C681B275196E8001671EC /* MouseJoystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C6818275196E8001671EC /* MouseJoystick.cpp */; };
4B7C681E2751A104001671EC /* Bitplanes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C681C2751A104001671EC /* Bitplanes.cpp */; };
4B7C681F2751A104001671EC /* Bitplanes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C681C2751A104001671EC /* Bitplanes.cpp */; };
4B7C7A00282C3BCA002D6C0B /* 68000flamewingTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B7C79FF282C3BCA002D6C0B /* 68000flamewingTests.mm */; };
4B7C7A07282C3DED002D6C0B /* flamewing 68000 BCD tests in Resources */ = {isa = PBXBuildFile; fileRef = 4B7C7A06282C3DED002D6C0B /* flamewing 68000 BCD tests */; };
4B7F188E2154825E00388727 /* MasterSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7F188C2154825D00388727 /* MasterSystem.cpp */; };
4B7F188F2154825E00388727 /* MasterSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7F188C2154825D00388727 /* MasterSystem.cpp */; };
4B7F1897215486A200388727 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7F1896215486A100388727 /* StaticAnalyser.cpp */; };
@ -1134,6 +1169,7 @@
4B0ACC2523775819008902D0 /* TIA.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TIA.hpp; sourceTree = "<group>"; };
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRT.cpp; sourceTree = "<group>"; };
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = "<group>"; };
4B0DA67A282DCC4200C12F17 /* Instruction.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Instruction.cpp; sourceTree = "<group>"; };
4B0E04E81FC9E5DA00F43484 /* CAS.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAS.cpp; sourceTree = "<group>"; };
4B0E04E91FC9E5DA00F43484 /* CAS.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CAS.hpp; sourceTree = "<group>"; };
4B0E04F81FC9FA3000F43484 /* 9918.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 9918.hpp; sourceTree = "<group>"; };
@ -1227,7 +1263,6 @@
4B2BFDB01DAEF5FF001A68B8 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Video.cpp; sourceTree = "<group>"; };
4B2BFDB11DAEF5FF001A68B8 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Video.hpp; sourceTree = "<group>"; };
4B2C45411E3C3896002A2389 /* cartridge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cartridge.png; sourceTree = "<group>"; };
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RegisterSizes.hpp; sourceTree = "<group>"; };
4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Electron.cpp; sourceTree = "<group>"; };
4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Electron.hpp; sourceTree = "<group>"; };
4B2E86B525D7490E0024F1E9 /* ReactiveDevice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ReactiveDevice.cpp; sourceTree = "<group>"; };
@ -1451,6 +1486,9 @@
4B7C6819275196E8001671EC /* MouseJoystick.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MouseJoystick.hpp; sourceTree = "<group>"; };
4B7C681C2751A104001671EC /* Bitplanes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Bitplanes.cpp; sourceTree = "<group>"; };
4B7C681D2751A104001671EC /* Bitplanes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Bitplanes.hpp; sourceTree = "<group>"; };
4B7C79FE282AFA9B002D6C0B /* ExceptionVectors.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ExceptionVectors.hpp; sourceTree = "<group>"; };
4B7C79FF282C3BCA002D6C0B /* 68000flamewingTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000flamewingTests.mm; sourceTree = "<group>"; };
4B7C7A06282C3DED002D6C0B /* flamewing 68000 BCD tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "flamewing 68000 BCD tests"; sourceTree = "<group>"; };
4B7F188C2154825D00388727 /* MasterSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MasterSystem.cpp; sourceTree = "<group>"; };
4B7F188D2154825D00388727 /* MasterSystem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MasterSystem.hpp; sourceTree = "<group>"; };
4B7F1895215486A100388727 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
@ -1932,6 +1970,12 @@
4BB4BFAF22A42F290069048D /* MacintoshIMG.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MacintoshIMG.hpp; sourceTree = "<group>"; };
4BB4BFB722A4372E0069048D /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
4BB4BFB822A4372E0069048D /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
4BB5B995281B1D3E00522DA9 /* RegisterSizes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RegisterSizes.hpp; sourceTree = "<group>"; };
4BB5B996281B1E3F00522DA9 /* Perform.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Perform.hpp; sourceTree = "<group>"; };
4BB5B997281B1F7B00522DA9 /* Status.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Status.hpp; sourceTree = "<group>"; };
4BB5B99A281B244400522DA9 /* PerformImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PerformImplementation.hpp; sourceTree = "<group>"; };
4BB5B99C281C805300522DA9 /* Executor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Executor.hpp; sourceTree = "<group>"; };
4BB5B99F281F121200522DA9 /* ExecutorImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ExecutorImplementation.hpp; sourceTree = "<group>"; };
4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimedEventLoop.cpp; sourceTree = "<group>"; };
4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimedEventLoop.hpp; sourceTree = "<group>"; };
4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommodoreGCR.cpp; path = Encodings/CommodoreGCR.cpp; sourceTree = "<group>"; };
@ -2009,6 +2053,8 @@
4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = "<group>"; };
4BC890D1230F86020025A55A /* DirectAccessDevice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DirectAccessDevice.cpp; sourceTree = "<group>"; };
4BC890D2230F86020025A55A /* DirectAccessDevice.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DirectAccessDevice.hpp; sourceTree = "<group>"; };
4BC8C01028294C3A0018A501 /* InstructionOperandSize.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = InstructionOperandSize.hpp; sourceTree = "<group>"; };
4BC8C01228294DEB0018A501 /* InstructionOperandFlags.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = InstructionOperandFlags.hpp; sourceTree = "<group>"; };
4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommodoreTAP.cpp; sourceTree = "<group>"; };
4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CommodoreTAP.hpp; sourceTree = "<group>"; };
4BC9DF441D044FCA00F44158 /* ROMImages */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ROMImages; path = ../../../../ROMImages; sourceTree = "<group>"; };
@ -2403,6 +2449,7 @@
4B1414631B588A1100E04248 /* Test Binaries */ = {
isa = PBXGroup;
children = (
4B7C7A06282C3DED002D6C0B /* flamewing 68000 BCD tests */,
4B680CE323A555CA00451D43 /* 68000 Comparative Tests */,
4B75F97A280D7C7700121055 /* 68000 Decoding */,
4B683B002727BE6F0043E541 /* Amiga Blitter Tests */,
@ -3157,10 +3204,16 @@
4B79629B2819681F008130F9 /* M68k */ = {
isa = PBXGroup;
children = (
4B79629F2819681F008130F9 /* Decoder.cpp */,
4B0DA67A282DCC4200C12F17 /* Instruction.cpp */,
4B79629E2819681F008130F9 /* Decoder.hpp */,
4B7C79FE282AFA9B002D6C0B /* ExceptionVectors.hpp */,
4BB5B99C281C805300522DA9 /* Executor.hpp */,
4B79629C2819681F008130F9 /* Instruction.hpp */,
4B79629D2819681F008130F9 /* Model.hpp */,
4B79629E2819681F008130F9 /* Decoder.hpp */,
4B79629F2819681F008130F9 /* Decoder.cpp */,
4BB5B996281B1E3F00522DA9 /* Perform.hpp */,
4BB5B997281B1F7B00522DA9 /* Status.hpp */,
4BB5B999281B244400522DA9 /* Implementation */,
);
path = M68k;
sourceTree = "<group>";
@ -3204,6 +3257,7 @@
4BD155312716362A00410C6E /* BitSpread.hpp */,
4B7BA03E23D55E7900B98D9E /* CRC.hpp */,
4B7BA03F23D55E7900B98D9E /* LFSR.hpp */,
4BB5B995281B1D3E00522DA9 /* RegisterSizes.hpp */,
4BFEA2F12682A90200EBF94C /* Sizes.hpp */,
);
name = Numeric;
@ -4069,6 +4123,17 @@
path = Macintosh;
sourceTree = "<group>";
};
4BB5B999281B244400522DA9 /* Implementation */ = {
isa = PBXGroup;
children = (
4BB5B99F281F121200522DA9 /* ExecutorImplementation.hpp */,
4BC8C01228294DEB0018A501 /* InstructionOperandFlags.hpp */,
4BC8C01028294C3A0018A501 /* InstructionOperandSize.hpp */,
4BB5B99A281B244400522DA9 /* PerformImplementation.hpp */,
);
path = Implementation;
sourceTree = "<group>";
};
4BB697CF1D4BA44900248BDF /* Encodings */ = {
isa = PBXGroup;
children = (
@ -4157,6 +4222,7 @@
4B680CE123A5553100451D43 /* 68000ComparativeTests.mm */,
4B9D0C4C22C7DA1A00DE1AD3 /* 68000ControlFlowTests.mm */,
4B75F978280D7C5100121055 /* 68000DecoderTests.mm */,
4B7C79FF282C3BCA002D6C0B /* 68000flamewingTests.mm */,
4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */,
4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */,
4BD388872239E198002D14B5 /* 68000Tests.mm */,
@ -4258,7 +4324,6 @@
children = (
4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */,
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
4B1414561B58879D00E04248 /* 6502 */,
4B4DEC15252BFA9C004583AC /* 6502Esque */,
4BF8D4CC251C0C9C00BBE21B /* 65816 */,
@ -5212,6 +5277,7 @@
4BB2997E1B587D8400A49093 /* ldxb in Resources */,
4BB298F51B587D8400A49093 /* adcb in Resources */,
4BB299981B587D8400A49093 /* nopax in Resources */,
4B7C7A07282C3DED002D6C0B /* flamewing 68000 BCD tests in Resources */,
4BB299931B587D8400A49093 /* lxab in Resources */,
4BB299F01B587D8400A49093 /* trap4 in Resources */,
4B8DF6722550D91600F3433C /* CPUBIT.sfc in Resources */,
@ -5562,6 +5628,7 @@
4B47F6C6241C87A100ED06F7 /* Struct.cpp in Sources */,
4B055AE01FAE9B660060FFFF /* CRT.cpp in Sources */,
4B894527201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
4B0DA67C282DCDF300C12F17 /* Instruction.cpp in Sources */,
4BB244D622AABAF600BE20E5 /* z8530.cpp in Sources */,
4BAF2B4F2004580C00480230 /* DMK.cpp in Sources */,
4B055AD01FAE9B030060FFFF /* Tape.cpp in Sources */,
@ -5768,6 +5835,7 @@
4BC080D926A25ADA00D03FD8 /* Amiga.cpp in Sources */,
4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */,
4B4518821F75E91A00926311 /* PCMSegment.cpp in Sources */,
4B0DA67B282DCDF100C12F17 /* Instruction.cpp in Sources */,
4B74CF812312FA9C00500CE8 /* HFV.cpp in Sources */,
4B17B58B20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */,
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
@ -5866,15 +5934,18 @@
4B778F1523A5EC980000D260 /* PartialMachineCycle.cpp in Sources */,
4BEDA43125B3C700000C2DBD /* Executor.cpp in Sources */,
4B778F6123A5F3560000D260 /* Disk.cpp in Sources */,
4B7752B628217EE70073E2C5 /* DSK.cpp in Sources */,
4B778F2523A5EDF40000D260 /* Encoder.cpp in Sources */,
4B778F4223A5F1A70000D260 /* MemoryFuzzer.cpp in Sources */,
4B778F0123A5EBA00000D260 /* MacintoshIMG.cpp in Sources */,
4B7752AD28217E770073E2C5 /* AmigaADF.cpp in Sources */,
4BFF1D3D2235C3C100838EA1 /* EmuTOSTests.mm in Sources */,
4B3F76B925A1635300178AEC /* PowerPCDecoderTests.mm in Sources */,
4B778F0A23A5EC150000D260 /* TapePRG.cpp in Sources */,
4B778F0823A5EC150000D260 /* CSW.cpp in Sources */,
4B778F5323A5F23F0000D260 /* SerialBus.cpp in Sources */,
4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */,
4B7752BA28217F160073E2C5 /* Bitplanes.cpp in Sources */,
4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */,
4B778F4E23A5F2160000D260 /* StaticAnalyser.cpp in Sources */,
4B778F5523A5F2A70000D260 /* Keyboard.cpp in Sources */,
@ -5885,6 +5956,7 @@
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */,
4B778F3123A5F0CB0000D260 /* Keyboard.cpp in Sources */,
4B778F4A23A5F1FB0000D260 /* StaticAnalyser.cpp in Sources */,
4B7752AB28217E560073E2C5 /* SZX.cpp in Sources */,
4BD91D772401C2B8007BDC91 /* PatrikRakTests.swift in Sources */,
4B680CE223A5553100451D43 /* 68000ComparativeTests.mm in Sources */,
4B778F3723A5F11C0000D260 /* Parser.cpp in Sources */,
@ -5892,6 +5964,7 @@
4B90467422C6FADD000E2074 /* 68000BitwiseTests.mm in Sources */,
4B7962A12819681F008130F9 /* Decoder.cpp in Sources */,
4B778F2A23A5EF0F0000D260 /* BitReverse.cpp in Sources */,
4B7752BB28217F1A0073E2C5 /* Copper.cpp in Sources */,
4B778F1D23A5ED470000D260 /* DiskController.cpp in Sources */,
4B778F0023A5EB990000D260 /* G64.cpp in Sources */,
4B778F4B23A5F2030000D260 /* StaticAnalyser.cpp in Sources */,
@ -5905,6 +5978,7 @@
4B08A2751EE35D56008B7065 /* Z80InterruptTests.swift in Sources */,
4B778F0E23A5EC4F0000D260 /* Tape.cpp in Sources */,
4B778F2D23A5EF190000D260 /* MFMDiskController.cpp in Sources */,
4B7752C228217F5C0073E2C5 /* Spectrum.cpp in Sources */,
4B778F2723A5EEF60000D260 /* BinaryDump.cpp in Sources */,
4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */,
4B778F5223A5F22F0000D260 /* StaticAnalyser.cpp in Sources */,
@ -5912,9 +5986,11 @@
4BBF49AF1ED2880200AB3669 /* FUSETests.swift in Sources */,
4B778F4D23A5F20F0000D260 /* StaticAnalyser.cpp in Sources */,
4B778F0423A5EBB00000D260 /* OricMFMDSK.cpp in Sources */,
4B7752BE28217F220073E2C5 /* MouseJoystick.cpp in Sources */,
4B8DF4D825465B7500F3433C /* IIgsMemoryMapTests.mm in Sources */,
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */,
4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */,
4B7752B928217F140073E2C5 /* Audio.cpp in Sources */,
4B778F0F23A5EC560000D260 /* PCMTrack.cpp in Sources */,
4B778F1123A5EC650000D260 /* FileHolder.cpp in Sources */,
4B778EFC23A5EB8B0000D260 /* AcornADF.cpp in Sources */,
@ -5930,15 +6006,20 @@
4B778F4823A5F1E70000D260 /* StaticAnalyser.cpp in Sources */,
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */,
4B778F2823A5EEF80000D260 /* Cartridge.cpp in Sources */,
4B7752B528217ED30073E2C5 /* SNA.cpp in Sources */,
4BEDA3C025B25563000C2DBD /* Decoder.cpp in Sources */,
4B778F4C23A5F2090000D260 /* StaticAnalyser.cpp in Sources */,
4B778F2623A5EE350000D260 /* Acorn.cpp in Sources */,
4B7752C128217F490073E2C5 /* FAT.cpp in Sources */,
4B778F5B23A5F2DE0000D260 /* Tape.cpp in Sources */,
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */,
4B778EF023A5D68C0000D260 /* 68000Storage.cpp in Sources */,
4B7752B228217EAE0073E2C5 /* StaticAnalyser.cpp in Sources */,
4B7752BC28217F1D0073E2C5 /* Disk.cpp in Sources */,
4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */,
4B778EFA23A5EB790000D260 /* DMK.cpp in Sources */,
4BEE1EC122B5E2FD000A26A6 /* Encoder.cpp in Sources */,
4B7752AF28217E890073E2C5 /* DAT.cpp in Sources */,
4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */,
4B778EFF23A5EB940000D260 /* D64.cpp in Sources */,
4BEDA3BB25B25563000C2DBD /* Decoder.cpp in Sources */,
@ -5957,12 +6038,15 @@
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */,
4B778F6023A5F3460000D260 /* Disk.cpp in Sources */,
4B778F5C23A5F3070000D260 /* MSX.cpp in Sources */,
4B7752AA28217E370073E2C5 /* ROMCatalogue.cpp in Sources */,
4B778F0323A5EBB00000D260 /* FAT12.cpp in Sources */,
4B778F4023A5F1910000D260 /* z8530.cpp in Sources */,
4BE3C69727CC32DC000EAD28 /* x86DataPointerTests.mm in Sources */,
4B778EFD23A5EB8E0000D260 /* AppleDSK.cpp in Sources */,
4B7752B728217EF40073E2C5 /* Chipset.cpp in Sources */,
4B778EFB23A5EB7E0000D260 /* HFE.cpp in Sources */,
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */,
4B0DA67D282DCDF300C12F17 /* Instruction.cpp in Sources */,
4BFCA12B1ECBE7C400AC40C1 /* ZexallTests.swift in Sources */,
4B778F2223A5EDDD0000D260 /* PulseQueuedTape.cpp in Sources */,
4B778EF123A5D6B50000D260 /* 9918.cpp in Sources */,
@ -5980,19 +6064,26 @@
4B778F4423A5F1BE0000D260 /* CommodoreGCR.cpp in Sources */,
4B778EF923A5EB740000D260 /* MSA.cpp in Sources */,
4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */,
4B7752A628217DF80073E2C5 /* Dave.cpp in Sources */,
4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */,
4B7752A728217E060073E2C5 /* Blitter.cpp in Sources */,
4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */,
4B8DD3682633B2D400B3C866 /* SpectrumVideoContentionTests.mm in Sources */,
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */,
4B7752A928217E200073E2C5 /* 65816Storage.cpp in Sources */,
4B7752AC28217E6E0073E2C5 /* StaticAnalyser.cpp in Sources */,
4B778F1223A5EC720000D260 /* CRT.cpp in Sources */,
4B778EF423A5DB3A0000D260 /* C1540.cpp in Sources */,
4B778F3C23A5F16F0000D260 /* FIRFilter.cpp in Sources */,
4B778F5423A5F2600000D260 /* UnformattedTrack.cpp in Sources */,
4B7752B028217E9A0073E2C5 /* StaticAnalyser.cpp in Sources */,
4B778EF823A5EB6E0000D260 /* NIB.cpp in Sources */,
4B9D0C4B22C7D70A00DE1AD3 /* 68000BCDTests.mm in Sources */,
4B778F5E23A5F3230000D260 /* Oric.cpp in Sources */,
4B7752B828217F110073E2C5 /* Amiga.cpp in Sources */,
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */,
4BDA8235261E8E000021AA19 /* Z80ContentionTests.mm in Sources */,
4B7752C328217F720073E2C5 /* Z80.cpp in Sources */,
4B778F1A23A5ED320000D260 /* Video.cpp in Sources */,
4B778F3B23A5F1650000D260 /* KeyboardMachine.cpp in Sources */,
4B778F2E23A5F09E0000D260 /* IRQDelegatePortHandler.cpp in Sources */,
@ -6001,17 +6092,20 @@
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */,
4B778EFE23A5EB910000D260 /* CPCDSK.cpp in Sources */,
4B778F5823A5F2C60000D260 /* Tape.cpp in Sources */,
4B7752B328217EB90073E2C5 /* State.cpp in Sources */,
4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */,
4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */,
4B778F2123A5EDD50000D260 /* TrackSerialiser.cpp in Sources */,
4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */,
4BC6237226F94BCB00F83DFE /* MintermTests.mm in Sources */,
4B7752BF28217F250073E2C5 /* Sprites.cpp in Sources */,
4B778F3923A5F11C0000D260 /* Shifter.cpp in Sources */,
4BEE4BD425A26E2B00011BD2 /* x86DecoderTests.mm in Sources */,
4B778F3623A5F1040000D260 /* Target.cpp in Sources */,
4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */,
4B778F3D23A5F1750000D260 /* ncr5380.cpp in Sources */,
4BF701A026FFD32300996424 /* AmigaBlitterTests.mm in Sources */,
4B7752B428217ECB0073E2C5 /* ZXSpectrumTAP.cpp in Sources */,
4B778F6323A5F3630000D260 /* Tape.cpp in Sources */,
4B778EF523A5DB440000D260 /* StaticAnalyser.cpp in Sources */,
4BEE1EC022B5E236000A26A6 /* MacGCRTests.mm in Sources */,
@ -6032,11 +6126,17 @@
4B778F1B23A5ED380000D260 /* Video.cpp in Sources */,
4B778F4723A5F1DD0000D260 /* StaticAnalyser.cpp in Sources */,
4B778F1923A5ED1B0000D260 /* 6502Storage.cpp in Sources */,
4B7752A828217E110073E2C5 /* Nick.cpp in Sources */,
4B7752AE28217E830073E2C5 /* 2MG.cpp in Sources */,
4B08A2781EE39306008B7065 /* TestMachine.mm in Sources */,
4B778F1E23A5EDC00000D260 /* DriveSpeedAccumulator.cpp in Sources */,
4B778F4323A5F1B00000D260 /* ImplicitSectors.cpp in Sources */,
4B7752B128217EA30073E2C5 /* StaticAnalyser.cpp in Sources */,
4B778F5123A5F2290000D260 /* StaticAnalyser.cpp in Sources */,
4B7752C028217F3D0073E2C5 /* Line.cpp in Sources */,
4B7C7A00282C3BCA002D6C0B /* 68000flamewingTests.mm in Sources */,
4B778F0223A5EBA40000D260 /* MFMSectorDump.cpp in Sources */,
4B7752BD28217F200073E2C5 /* Keyboard.cpp in Sources */,
4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */,
4B778F3E23A5F17C0000D260 /* IWM.cpp in Sources */,
4BD91D732401960C007BDC91 /* STX.cpp in Sources */,

View File

@ -9,11 +9,81 @@
#import <XCTest/XCTest.h>
#include "../../../Processors/68000/68000.hpp"
#include "../../../InstructionSets/M68k/Executor.hpp"
#include "../../../InstructionSets/M68k/Decoder.hpp"
#include <array>
#include <memory>
#include <functional>
//#define USE_EXISTING_IMPLEMENTATION
namespace {
/// Binds a 68000 executor to 16mb of RAM.
struct Test68000 {
std::array<uint8_t, 16*1024*1024> ram;
InstructionSet::M68k::Executor<InstructionSet::M68k::Model::M68000, Test68000> processor;
Test68000() : processor(*this) {
}
void run_for_instructions(int instructions) {
processor.run_for_instructions(instructions);
}
// Initial test-case implementation:
// do a very sedate read and write.
template <typename IntT> IntT read(uint32_t address, InstructionSet::M68k::FunctionCode) {
if constexpr (sizeof(IntT) == 1) {
return IntT(ram[address & 0xffffff]);
}
if constexpr (sizeof(IntT) == 2) {
return IntT(
(ram[address & 0xffffff] << 8) |
ram[(address+1) & 0xffffff]
);
}
if constexpr (sizeof(IntT) == 4) {
return IntT(
(ram[address & 0xffffff] << 24) |
(ram[(address+1) & 0xffffff] << 16) |
(ram[(address+2) & 0xffffff] << 8) |
ram[(address+3) & 0xffffff]
);
}
return 0;
}
template <typename IntT> void write(uint32_t address, IntT value, InstructionSet::M68k::FunctionCode) {
if constexpr (sizeof(IntT) == 1) {
ram[address & 0xffffff] = uint8_t(value);
}
if constexpr (sizeof(IntT) == 2) {
ram[address & 0xffffff] = uint8_t(value >> 8);
ram[(address+1) & 0xffffff] = uint8_t(value);
}
if constexpr (sizeof(IntT) == 4) {
ram[address & 0xffffff] = uint8_t(value >> 24);
ram[(address+1) & 0xffffff] = uint8_t(value >> 16);
ram[(address+2) & 0xffffff] = uint8_t(value >> 8);
ram[(address+3) & 0xffffff] = uint8_t(value);
}
}
void reset() {}
int acknowlege_interrupt(int) {
return -1;
}
};
}
@interface M68000ComparativeTests : XCTestCase
@end
@ -23,19 +93,21 @@
NSMutableSet<NSString *> *_failures;
NSMutableArray<NSNumber *> *_failingOpcodes;
Test68000 _test68000;
}
- (void)setUp {
// These will accumulate a list of failing tests and associated opcodes.
_failures = [[NSMutableSet alloc] init];
_failingOpcodes = [[NSMutableArray alloc] init];
// To limit tests run to a subset of files and/or of tests, uncomment and fill in below.
// _fileSet = [NSSet setWithArray:@[@"jmp_jsr.json"]];
// _testSet = [NSSet setWithArray:@[@"CHK 41a8"]];
}
- (void)testAll {
// These will accumulate a list of failing tests and associated opcodes.
_failures = [[NSMutableSet alloc] init];
_failingOpcodes = [[NSMutableArray alloc] init];
// Get the full list of available test files.
NSBundle *const bundle = [NSBundle bundleForClass:[self class]];
NSArray<NSURL *> *const tests = [bundle URLsForResourcesWithExtension:@"json" subdirectory:@"68000 Comparative Tests"];
@ -44,16 +116,24 @@
for(NSURL *url in tests) {
// Compare against a file set if one has been supplied.
if(_fileSet && ![_fileSet containsObject:[url lastPathComponent]]) continue;
NSLog(@"Testing %@", url);
// NSLog(@"Testing %@", url);
[self testJSONAtURL:url];
}
// Output a summary of failures.
NSLog(@"Total: %@", @(_failures.count));
NSLog(@"Failures: %@", _failures);
NSLog(@"Failing opcodes:");
for(NSNumber *number in _failingOpcodes) {
NSLog(@"%04x", number.intValue);
XCTAssert(_failures.count == 0);
// Output a summary of failures, if any.
if(_failures.count) {
NSLog(@"Total failures: %@", @(_failures.count));
NSLog(@"Failures: %@", _failures);
NSLog(@"Failing opcodes:");
InstructionSet::M68k::Predecoder<InstructionSet::M68k::Model::M68000> decoder;
for(NSNumber *number in _failingOpcodes) {
const auto decoded = decoder.decode(number.intValue);
const std::string description = decoded.to_string(number.intValue);
NSLog(@"%04x %s", number.intValue, description.c_str());
}
}
}
@ -70,17 +150,23 @@
// Perform each dictionary in the array as a test.
for(NSDictionary *test in jsonContents) {
if(![test isKindOfClass:[NSDictionary class]]) continue;
[self testOperation:test];
// Only entries with a name are valid.
NSString *const name = test[@"name"];
if(!name) continue;
// Compare against a test set if one has been supplied.
if(_testSet && ![_testSet containsObject:name]) continue;
#ifdef USE_EXISTING_IMPLEMENTATION
[self testOperationClassic:test name:name];
#else
[self testOperationExecutor:test name:name];
#endif
}
}
- (void)testOperation:(NSDictionary *)test {
// Only entries with a name are valid.
NSString *const name = test[@"name"];
if(!name) return;
// Compare against a test set if one has been supplied.
if(_testSet && ![_testSet containsObject:name]) return;
- (void)testOperationClassic:(NSDictionary *)test name:(NSString *)name {
// This is the test class for 68000 execution.
struct Test68000: public CPU::MC68000::BusHandler {
@ -195,4 +281,88 @@
test68000->run_for_instructions(1, comparitor);
}
- (void)setInitialState:(NSDictionary *)test {
// Definitively erase any prior memory contents;
// 0xce is arbitrary but hopefully easier to spot
// in potential errors than e.g. 0x00 or 0xff.
memset(_test68000.ram.data(), 0xce, _test68000.ram.size());
// Apply initial memory state.
NSArray<NSNumber *> *const initialMemory = test[@"initial memory"];
NSEnumerator<NSNumber *> *enumerator = [initialMemory objectEnumerator];
while(true) {
NSNumber *const address = [enumerator nextObject];
NSNumber *const value = [enumerator nextObject];
if(!address || !value) break;
_test68000.ram[address.integerValue] = value.integerValue;
}
// Apply initial processor state.
NSDictionary *const initialState = test[@"initial state"];
auto state = _test68000.processor.get_state();
for(int c = 0; c < 8; ++c) {
const NSString *dX = [@"d" stringByAppendingFormat:@"%d", c];
const NSString *aX = [@"a" stringByAppendingFormat:@"%d", c];
state.data[c] = uint32_t([initialState[dX] integerValue]);
if(c < 7)
state.address[c] = uint32_t([initialState[aX] integerValue]);
}
state.supervisor_stack_pointer = uint32_t([initialState[@"a7"] integerValue]);
state.user_stack_pointer = uint32_t([initialState[@"usp"] integerValue]);
state.status = [initialState[@"sr"] integerValue];
state.program_counter = uint32_t([initialState[@"pc"] integerValue]);
_test68000.processor.set_state(state);
}
- (void)testOperationExecutor:(NSDictionary *)test name:(NSString *)name {
[self setInitialState:test];
// Run the thing.
_test68000.run_for_instructions(1);
// Test the end state.
NSDictionary *const finalState = test[@"final state"];
const auto state = _test68000.processor.get_state();
for(int c = 0; c < 8; ++c) {
const NSString *dX = [@"d" stringByAppendingFormat:@"%d", c];
const NSString *aX = [@"a" stringByAppendingFormat:@"%d", c];
if(state.data[c] != [finalState[dX] integerValue]) [_failures addObject:name];
if(c < 7 && state.address[c] != [finalState[aX] integerValue]) [_failures addObject:name];
// XCTAssertEqual(state.data[c], [finalState[dX] integerValue], @"%@: D%d inconsistent", name, c);
// if(c < 7) {
// XCTAssertEqual(state.address[c], [finalState[aX] integerValue], @"%@: A%d inconsistent", name, c);
// }
}
if(state.supervisor_stack_pointer != [finalState[@"a7"] integerValue]) [_failures addObject:name];
if(state.user_stack_pointer != [finalState[@"usp"] integerValue]) [_failures addObject:name];
if(state.status != [finalState[@"sr"] integerValue]) [_failures addObject:name];
// XCTAssertEqual(state.supervisor_stack_pointer, [finalState[@"a7"] integerValue], @"%@: A7 inconsistent", name);
// XCTAssertEqual(state.user_stack_pointer, [finalState[@"usp"] integerValue], @"%@: USP inconsistent", name);
// XCTAssertEqual(state.status, [finalState[@"sr"] integerValue], @"%@: Status inconsistent", name);
// XCTAssertEqual(state.program_counter, [finalState[@"pc"] integerValue], @"%@: Program counter inconsistent", name);
// Test final memory state.
NSArray<NSNumber *> *const finalMemory = test[@"final memory"];
NSEnumerator *enumerator = [finalMemory objectEnumerator];
while(true) {
NSNumber *const address = [enumerator nextObject];
NSNumber *const value = [enumerator nextObject];
if(!address || !value) break;
// XCTAssertEqual(_test68000.ram[address.integerValue], value.integerValue, @"%@: Memory at location %@ inconsistent", name, address);
if(_test68000.ram[address.integerValue] != value.integerValue) [_failures addObject:name];
}
// If this test is now in the failures set, add the corresponding opcode for
// later logging.
if([_failures containsObject:name]) {
[_failingOpcodes addObject:@(_test68000.read<uint16_t>(0x100, InstructionSet::M68k::FunctionCode()))];
}
}
@end

View File

@ -15,51 +15,6 @@ using namespace InstructionSet::M68k;
@interface M68000DecoderTests : XCTestCase
@end
namespace {
template <int index> NSString *operand(Preinstruction instruction, uint16_t opcode) {
switch(instruction.mode<index>()) {
default: return [NSString stringWithFormat:@"[Mode %d?]", int(instruction.mode<index>())];
case AddressingMode::None:
return @"";
case AddressingMode::DataRegisterDirect:
return [NSString stringWithFormat:@"D%d", instruction.reg<index>()];
case AddressingMode::AddressRegisterDirect:
return [NSString stringWithFormat:@"A%d", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirect:
return [NSString stringWithFormat:@"(A%d)", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirectWithPostincrement:
return [NSString stringWithFormat:@"(A%d)+", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirectWithPredecrement:
return [NSString stringWithFormat:@"-(A%d)", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirectWithDisplacement:
return [NSString stringWithFormat:@"(d16, A%d)", instruction.reg<index>()];
case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
return [NSString stringWithFormat:@"(d8, A%d, Xn)", instruction.reg<index>()];
case AddressingMode::ProgramCounterIndirectWithDisplacement:
return @"(d16, PC)";
case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
return @"(d8, PC, Xn)";
case AddressingMode::AbsoluteShort:
return @"(xxx).w";
case AddressingMode::AbsoluteLong:
return @"(xxx).l";
case AddressingMode::ImmediateData:
return @"#";
case AddressingMode::Quick:
return [NSString stringWithFormat:@"%d", quick(instruction.operation, opcode)];
}
}
}
@implementation M68000DecoderTests
- (void)testInstructionSpecs {
@ -81,208 +36,7 @@ template <int index> NSString *operand(Preinstruction instruction, uint16_t opco
const auto found = decoder.decode(uint16_t(instr));
NSString *instruction;
switch(found.operation) {
case Operation::Undefined: instruction = @"None"; break;
case Operation::NOP: instruction = @"NOP"; break;
case Operation::ABCD: instruction = @"ABCD"; break;
case Operation::SBCD: instruction = @"SBCD"; break;
case Operation::NBCD: instruction = @"NBCD"; break;
case Operation::ADDb: instruction = @"ADD.b"; break;
case Operation::ADDw: instruction = @"ADD.w"; break;
case Operation::ADDl: instruction = @"ADD.l"; break;
case Operation::ADDAw: instruction = @"ADDA.w"; break;
case Operation::ADDAl: instruction = @"ADDA.l"; break;
case Operation::ADDXb: instruction = @"ADDX.b"; break;
case Operation::ADDXw: instruction = @"ADDX.w"; break;
case Operation::ADDXl: instruction = @"ADDX.l"; break;
case Operation::SUBb: instruction = @"SUB.b"; break;
case Operation::SUBw: instruction = @"SUB.w"; break;
case Operation::SUBl: instruction = @"SUB.l"; break;
case Operation::SUBAw: instruction = @"SUBA.w"; break;
case Operation::SUBAl: instruction = @"SUBA.l"; break;
case Operation::SUBXb: instruction = @"SUBX.b"; break;
case Operation::SUBXw: instruction = @"SUBX.w"; break;
case Operation::SUBXl: instruction = @"SUBX.l"; break;
case Operation::MOVEb: instruction = @"MOVE.b"; break;
case Operation::MOVEw: instruction = @"MOVE.w"; break;
case Operation::MOVEl:
if(found.mode<0>() == AddressingMode::Quick) {
instruction = @"MOVE.q";
} else {
instruction = @"MOVE.l";
}
break;
case Operation::MOVEAw: instruction = @"MOVEA.w"; break;
case Operation::MOVEAl: instruction = @"MOVEA.l"; break;
case Operation::LEA: instruction = @"LEA"; break;
case Operation::PEA: instruction = @"PEA"; break;
case Operation::MOVEtoSR: instruction = @"MOVEtoSR"; break;
case Operation::MOVEfromSR: instruction = @"MOVEfromSR"; break;
case Operation::MOVEtoCCR: instruction = @"MOVEtoCCR"; break;
case Operation::MOVEtoUSP: instruction = @"MOVEtoUSP"; break;
case Operation::MOVEfromUSP: instruction = @"MOVEfromUSP"; break;
case Operation::ORItoSR: instruction = @"ORItoSR"; break;
case Operation::ORItoCCR: instruction = @"ORItoCCR"; break;
case Operation::ANDItoSR: instruction = @"ANDItoSR"; break;
case Operation::ANDItoCCR: instruction = @"ANDItoCCR"; break;
case Operation::EORItoSR: instruction = @"EORItoSR"; break;
case Operation::EORItoCCR: instruction = @"EORItoCCR"; break;
case Operation::BTST: instruction = @"BTST"; break;
case Operation::BCLR: instruction = @"BCLR"; break;
case Operation::BCHG: instruction = @"BCHG"; break;
case Operation::BSET: instruction = @"BSET"; break;
case Operation::CMPb: instruction = @"CMP.b"; break;
case Operation::CMPw: instruction = @"CMP.w"; break;
case Operation::CMPl: instruction = @"CMP.l"; break;
case Operation::CMPAw: instruction = @"CMPA.w"; break;
case Operation::CMPAl: instruction = @"CMPA.l"; break;
case Operation::TSTb: instruction = @"TST.b"; break;
case Operation::TSTw: instruction = @"TST.w"; break;
case Operation::TSTl: instruction = @"TST.l"; break;
case Operation::JMP: instruction = @"JMP"; break;
case Operation::JSR: instruction = @"JSR"; break;
case Operation::RTS: instruction = @"RTS"; break;
case Operation::DBcc: instruction = @"DBcc"; break;
case Operation::Scc: instruction = @"Scc"; break;
case Operation::Bccb:
case Operation::Bccl:
case Operation::Bccw: instruction = @"Bcc"; break;
case Operation::BSRb:
case Operation::BSRl:
case Operation::BSRw: instruction = @"BSR"; break;
case Operation::CLRb: instruction = @"CLR.b"; break;
case Operation::CLRw: instruction = @"CLR.w"; break;
case Operation::CLRl: instruction = @"CLR.l"; break;
case Operation::NEGXb: instruction = @"NEGX.b"; break;
case Operation::NEGXw: instruction = @"NEGX.w"; break;
case Operation::NEGXl: instruction = @"NEGX.l"; break;
case Operation::NEGb: instruction = @"NEG.b"; break;
case Operation::NEGw: instruction = @"NEG.w"; break;
case Operation::NEGl: instruction = @"NEG.l"; break;
case Operation::ASLb: instruction = @"ASL.b"; break;
case Operation::ASLw: instruction = @"ASL.w"; break;
case Operation::ASLl: instruction = @"ASL.l"; break;
case Operation::ASLm: instruction = @"ASL.w"; break;
case Operation::ASRb: instruction = @"ASR.b"; break;
case Operation::ASRw: instruction = @"ASR.w"; break;
case Operation::ASRl: instruction = @"ASR.l"; break;
case Operation::ASRm: instruction = @"ASR.w"; break;
case Operation::LSLb: instruction = @"LSL.b"; break;
case Operation::LSLw: instruction = @"LSL.w"; break;
case Operation::LSLl: instruction = @"LSL.l"; break;
case Operation::LSLm: instruction = @"LSL.w"; break;
case Operation::LSRb: instruction = @"LSR.b"; break;
case Operation::LSRw: instruction = @"LSR.w"; break;
case Operation::LSRl: instruction = @"LSR.l"; break;
case Operation::LSRm: instruction = @"LSR.w"; break;
case Operation::ROLb: instruction = @"ROL.b"; break;
case Operation::ROLw: instruction = @"ROL.w"; break;
case Operation::ROLl: instruction = @"ROL.l"; break;
case Operation::ROLm: instruction = @"ROL.w"; break;
case Operation::RORb: instruction = @"ROR.b"; break;
case Operation::RORw: instruction = @"ROR.w"; break;
case Operation::RORl: instruction = @"ROR.l"; break;
case Operation::RORm: instruction = @"ROR.w"; break;
case Operation::ROXLb: instruction = @"ROXL.b"; break;
case Operation::ROXLw: instruction = @"ROXL.w"; break;
case Operation::ROXLl: instruction = @"ROXL.l"; break;
case Operation::ROXLm: instruction = @"ROXL.w"; break;
case Operation::ROXRb: instruction = @"ROXR.b"; break;
case Operation::ROXRw: instruction = @"ROXR.w"; break;
case Operation::ROXRl: instruction = @"ROXR.l"; break;
case Operation::ROXRm: instruction = @"ROXR.w"; break;
case Operation::MOVEMl: instruction = @"MOVEM.l"; break;
case Operation::MOVEMw: instruction = @"MOVEM.w"; break;
case Operation::MOVEPl: instruction = @"MOVEP.l"; break;
case Operation::MOVEPw: instruction = @"MOVEP.w"; break;
case Operation::ANDb: instruction = @"AND.b"; break;
case Operation::ANDw: instruction = @"AND.w"; break;
case Operation::ANDl: instruction = @"AND.l"; break;
case Operation::EORb: instruction = @"EOR.b"; break;
case Operation::EORw: instruction = @"EOR.w"; break;
case Operation::EORl: instruction = @"EOR.l"; break;
case Operation::NOTb: instruction = @"NOT.b"; break;
case Operation::NOTw: instruction = @"NOT.w"; break;
case Operation::NOTl: instruction = @"NOT.l"; break;
case Operation::ORb: instruction = @"OR.b"; break;
case Operation::ORw: instruction = @"OR.w"; break;
case Operation::ORl: instruction = @"OR.l"; break;
case Operation::MULU: instruction = @"MULU"; break;
case Operation::MULS: instruction = @"MULS"; break;
case Operation::DIVU: instruction = @"DIVU"; break;
case Operation::DIVS: instruction = @"DIVS"; break;
case Operation::RTE: instruction = @"RTE"; break;
case Operation::RTR: instruction = @"RTR"; break;
case Operation::TRAP: instruction = @"TRAP"; break;
case Operation::TRAPV: instruction = @"TRAPV"; break;
case Operation::CHK: instruction = @"CHK"; break;
case Operation::EXG: instruction = @"EXG"; break;
case Operation::SWAP: instruction = @"SWAP"; break;
case Operation::TAS: instruction = @"TAS"; break;
case Operation::EXTbtow: instruction = @"EXT.w"; break;
case Operation::EXTwtol: instruction = @"EXT.l"; break;
case Operation::LINKw: instruction = @"LINK"; break;
case Operation::UNLINK: instruction = @"UNLINK"; break;
case Operation::STOP: instruction = @"STOP"; break;
case Operation::RESET: instruction = @"RESET"; break;
// For now, skip any unmapped operations.
default:
XCTAssert(false, @"Operation %d unhandled by test case", int(found.operation));
continue;
}
NSString *const operand1 = operand<0>(found, uint16_t(instr));
NSString *const operand2 = operand<1>(found, uint16_t(instr));
if(operand1.length) instruction = [instruction stringByAppendingFormat:@" %@", operand1];
if(operand2.length) instruction = [instruction stringByAppendingFormat:@", %@", operand2];
XCTAssertFalse(found.mode<0>() == AddressingMode::None && found.mode<1>() != AddressingMode::None, @"Decoding of %@ provided a second operand but not a first", instrName);
NSString *const instruction = [NSString stringWithUTF8String:found.to_string(instr).c_str()];
XCTAssertEqualObjects(instruction, expected, "%@ should decode as %@; got %@", instrName, expected, instruction);
}
}

View File

@ -0,0 +1,103 @@
//
// 68000ComparativeTests.cpp
// Clock SignalTests
//
// Created by Thomas Harte on 14/12/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include "../../../InstructionSets/M68k/Perform.hpp"
using namespace InstructionSet::M68k;
@interface M68000flamewingTests : XCTestCase
@end
@implementation M68000flamewingTests
- (Status)statusWithflamewingFlags:(int)flags {
Status status;
status.carry_flag = status.extend_flag = flags & 2;
status.zero_result = ~flags & 1;
status.negative_flag = 0;
status.overflow_flag = 0;
return status;
}
- (void)validate:(const uint8_t *)test source:(int)source dest:(int)dest flags:(int)flags result:(uint32_t)result status:(Status)status operation:(NSString *)operation {
const uint8_t result_flags = test[0];
const uint8_t result_value = test[1];
NSString *const testName =
[NSString stringWithFormat:@"%@ %02x, %02x [%c%c]", operation, source, dest, (flags & 2) ? 'X' : '-', (flags & 1) ? 'Z' : '-'];
XCTAssertEqual(result, uint32_t(result_value), @"Wrong value received for %@", testName);
XCTAssertEqual(status.ccr(), uint16_t(result_flags), @"Wrong status received for %@", testName);
}
- (void)testAll {
// Get the full list of available test files.
NSBundle *const bundle = [NSBundle bundleForClass:[self class]];
NSURL *const testURL = [bundle URLForResource:@"bcd-table" withExtension:@"bin" subdirectory:@"flamewing 68000 BCD tests"];
NSData *const testData = [NSData dataWithContentsOfURL:testURL];
const uint8_t *bytes = reinterpret_cast<const uint8_t *>(testData.bytes);
NullFlowController flow_controller;
// Test ABCD.
for(int source = 0; source < 256; source++) {
for(int dest = 0; dest < 256; dest++) {
for(int flags = 0; flags < 4; flags++) {
Status status = [self statusWithflamewingFlags:flags];
CPU::SlicedInt32 s, d;
s.l = source;
d.l = dest;
perform<Model::M68000, NullFlowController, Operation::ABCD>(
Preinstruction(), s, d, status, flow_controller);
[self validate:bytes source:source dest:dest flags:flags result:d.l status:status operation:@"ABCD"];
bytes += 2;
}
}
}
// Test SBCD.
for(int source = 0; source < 256; source++) {
for(int dest = 0; dest < 256; dest++) {
for(int flags = 0; flags < 4; flags++) {
Status status = [self statusWithflamewingFlags:flags];
CPU::SlicedInt32 s, d;
s.l = source;
d.l = dest;
perform<Model::M68000, NullFlowController, Operation::SBCD>(
Preinstruction(), s, d, status, flow_controller);
[self validate:bytes source:source dest:dest flags:flags result:d.l status:status operation:@"SBCD"];
bytes += 2;
}
}
}
// Test NBCD.
for(int source = 0; source < 256; source++) {
for(int flags = 0; flags < 4; flags++) {
Status status = [self statusWithflamewingFlags:flags];
CPU::SlicedInt32 s, d;
s.l = source;
perform<Model::M68000, NullFlowController, Operation::NBCD>(
Preinstruction(), s, d, status, flow_controller);
[self validate:bytes source:source dest:0 flags:flags result:s.l status:status operation:@"NBCD"];
bytes += 2;
}
}
}
@end

View File

@ -0,0 +1,5 @@
# flamewing's 68000 BCD tests
These test results were generated by code obtained from https://github.com/flamewing/68k-bcd-verifier
The code itself is licensed under the GPL 3.0; the file included here is not part of the original repository but is generated as part of the build process so it is probably safest to assume it is covered by the GPL despite the rule that program output isn't covered — it is arguably part of the program, not program output.

View File

@ -15,7 +15,7 @@
#include "../6502Esque/6502Esque.hpp"
#include "../6502Esque/Implementation/LazyFlags.hpp"
#include "../RegisterSizes.hpp"
#include "../../Numeric/RegisterSizes.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
namespace CPU {

View File

@ -14,7 +14,7 @@
#include <cstdint>
#include <vector>
#include "../RegisterSizes.hpp"
#include "../../Numeric/RegisterSizes.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../6502Esque/6502Esque.hpp"
#include "../6502Esque/Implementation/LazyFlags.hpp"

View File

@ -20,7 +20,7 @@
#include "../../ClockReceiver/ForceInline.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../RegisterSizes.hpp"
#include "../../Numeric/RegisterSizes.hpp"
namespace CPU {
namespace MC68000 {

View File

@ -1595,6 +1595,8 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
overflow_flag_ = carry_flag_ = 0;
} break;
#undef sbcd
/*
Shifts and rotates.
*/
@ -1764,7 +1766,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
} break;
case Operation::ROLb: rol(destination()->halves.low.halves.low, 8); break;
case Operation::ROLw: rol(destination()->halves.low.full, 16); break;
case Operation::ROLl: rol(destination()->full, 32); break;
case Operation::ROLl: rol(destination()->full, 32); break;
#define ror(destination, size) { \
@ -1793,7 +1795,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
} break;
case Operation::RORb: ror(destination()->halves.low.halves.low, 8); break;
case Operation::RORw: ror(destination()->halves.low.full, 16); break;
case Operation::RORl: ror(destination()->full, 32); break;
case Operation::RORl: ror(destination()->full, 32); break;
#define roxl(destination, size) { \
decode_shift_count(); \
@ -1853,13 +1855,13 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
#undef lsl
#undef asl
#undef set_flags
#undef set_flagsx
#undef decode_shift_count
#undef set_flags_b
#undef set_flags_w
#undef set_flags_l
#undef set_neg_zero_overflow
#undef set_net_zero
#undef set_neg_zero
/*
RTE and RTR share an implementation.

View File

@ -28,11 +28,14 @@ struct State: public Reflection::StructImpl<State> {
Provides the current state of the well-known, published internal registers.
*/
struct Registers: public Reflection::StructImpl<Registers> {
// Official registers.
uint32_t data[8], address[7];
uint32_t user_stack_pointer;
uint32_t supervisor_stack_pointer;
uint16_t status;
uint32_t program_counter;
// The 68000's prefetch queue.
uint32_t prefetch;
uint16_t instruction;
@ -56,8 +59,8 @@ struct State: public Reflection::StructImpl<State> {
} inputs;
/*!
Contains internal state used by this particular implementation of a 6502. Most of it
does not necessarily correlate with anything in a real 6502, and some of it very
Contains internal state used by this particular implementation of a 68000. Most of it
does not necessarily correlate with anything in a real 68000, and some of it very
obviously doesn't.
*/
struct ExecutionState: public Reflection::StructImpl<ExecutionState> {

View File

@ -1,39 +0,0 @@
//
// RegisterSizes.hpp
// Clock Signal
//
// Created by Thomas Harte on 14/05/2017.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef RegisterSizes_hpp
#define RegisterSizes_hpp
#include <cstdint>
namespace CPU {
template <typename Full, typename Half> union RegisterPair {
RegisterPair(Full v) : full(v) {}
RegisterPair() {}
Full full;
#pragma pack(push, 1)
#if TARGET_RT_BIG_ENDIAN
struct {
Half high, low;
} halves;
#else
struct {
Half low, high;
} halves;
#endif
#pragma pack(pop)
};
typedef RegisterPair<uint16_t, uint8_t> RegisterPair16;
typedef RegisterPair<uint32_t, RegisterPair16> RegisterPair32;
}
#endif /* RegisterSizes_hpp */

View File

@ -13,7 +13,7 @@
#include <vector>
#include <cstdint>
#include "../RegisterSizes.hpp"
#include "../../Numeric/RegisterSizes.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../ClockReceiver/ForceInline.hpp"