2019-03-09 05:00:23 +00:00
|
|
|
|
//
|
|
|
|
|
// 68000.hpp
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 08/03/2019.
|
|
|
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#ifndef MC68000_h
|
|
|
|
|
#define MC68000_h
|
|
|
|
|
|
|
|
|
|
#include <cstdint>
|
2019-03-12 02:47:58 +00:00
|
|
|
|
#include <iomanip>
|
2019-03-10 21:27:34 +00:00
|
|
|
|
#include <iostream>
|
2019-03-17 01:47:46 +00:00
|
|
|
|
#include <tuple>
|
2019-03-09 05:00:23 +00:00
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
2019-03-10 21:27:34 +00:00
|
|
|
|
#include "../RegisterSizes.hpp"
|
2019-03-09 05:00:23 +00:00
|
|
|
|
|
|
|
|
|
namespace CPU {
|
|
|
|
|
namespace MC68000 {
|
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
|
/*!
|
|
|
|
|
A microcycle is an atomic unit of 68000 bus activity — it is a single item large enough
|
|
|
|
|
fully to specify a sequence of bus events that occur without any possible interruption.
|
|
|
|
|
|
|
|
|
|
Concretely, a standard read cycle breaks down into at least two microcycles:
|
|
|
|
|
|
|
|
|
|
1) a 5 half-cycle length microcycle in which the address strobe is signalled; and
|
|
|
|
|
2) a 3 half-cycle length microcycle in which at least one of the data strobes is
|
|
|
|
|
signalled, and the data bus is sampled.
|
|
|
|
|
|
|
|
|
|
That is, assuming DTack were signalled when microcycle (1) ended. If not then additional
|
|
|
|
|
wait state microcycles would fall between those two parts.
|
|
|
|
|
|
|
|
|
|
The 68000 data sheet defines when the address becomes valid during microcycle (1), and
|
|
|
|
|
when the address strobe is actually asserted. But those timings are fixed. So simply
|
|
|
|
|
telling you that this was a microcycle during which the address trobe was signalled is
|
|
|
|
|
sufficient fully to describe the bus activity.
|
|
|
|
|
|
|
|
|
|
(Aside: see the 68000 template's definition for options re: implicit DTack; if your
|
|
|
|
|
68000 owner can always predict exactly how long it will hold DTack following observation
|
|
|
|
|
of an address-strobing microcycle, it can just supply those periods for accounting and
|
|
|
|
|
avoid the runtime cost of actual DTack emulation. But such as the bus allows.)
|
|
|
|
|
*/
|
2019-03-09 05:00:23 +00:00
|
|
|
|
struct Microcycle {
|
2019-03-10 21:27:34 +00:00
|
|
|
|
/*
|
2019-03-16 21:54:58 +00:00
|
|
|
|
The operation code is composed of several parts; a compound low part
|
|
|
|
|
that can be masked off with TypeMask identifies the type of the cycle;
|
|
|
|
|
some of the other status lines are also present in the top parts of the int.
|
2019-03-10 21:27:34 +00:00
|
|
|
|
*/
|
2019-03-16 21:54:58 +00:00
|
|
|
|
static const int TypeMask = 3;
|
2019-03-10 21:27:34 +00:00
|
|
|
|
|
2019-03-16 21:54:58 +00:00
|
|
|
|
/// A NewAddress cycle is one in which the address strobe is initially low but becomes high;
|
|
|
|
|
/// this correlates to states 0 to 5 of a standard read/write cycle.
|
|
|
|
|
static const int Idle = 0;
|
|
|
|
|
|
|
|
|
|
/// A NewAddress cycle is one in which the address strobe is initially low but becomes high;
|
|
|
|
|
/// this correlates to states 0 to 5 of a standard read/write cycle.
|
|
|
|
|
static const int NewAddress = 1;
|
|
|
|
|
|
|
|
|
|
/// A SameAddress cycle is one in which the address strobe is continuously asserted, but neither
|
|
|
|
|
/// of the data strobes are.
|
|
|
|
|
static const int SameAddress = 2;
|
|
|
|
|
|
2019-03-30 03:40:54 +00:00
|
|
|
|
/// A Reset cycle is one in which the RESET output is asserted.
|
|
|
|
|
static const int Reset = 3;
|
|
|
|
|
|
2019-03-16 21:54:58 +00:00
|
|
|
|
/// Indicates that the address and both data select strobes are active.
|
|
|
|
|
static const int SelectWord = 1 << 2;
|
|
|
|
|
|
|
|
|
|
/// Indicates that the address strobe and exactly one of the data strobes are active; you can determine
|
|
|
|
|
/// which by inspecting the low bit of the provided address. The RW line indicates a read.
|
|
|
|
|
static const int SelectByte = 1 << 3;
|
|
|
|
|
|
|
|
|
|
/// If set, indicates a read. Otherwise, a write.
|
|
|
|
|
static const int Read = 1 << 4;
|
|
|
|
|
|
|
|
|
|
/// Contains the value of line FC0.
|
|
|
|
|
static const int IsData = 1 << 5;
|
|
|
|
|
|
|
|
|
|
/// Contains the value of line FC1.
|
|
|
|
|
static const int IsProgram = 1 << 6;
|
2019-03-10 21:42:13 +00:00
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
|
int operation = 0;
|
2019-03-17 01:47:46 +00:00
|
|
|
|
HalfCycles length = HalfCycles(4);
|
2019-03-10 21:27:34 +00:00
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
For expediency, this provides a full 32-bit byte-resolution address — e.g.
|
|
|
|
|
if reading indirectly via an address register, this will indicate the full
|
|
|
|
|
value of the address register.
|
|
|
|
|
|
|
|
|
|
The receiver should ignore bits 0 and 24+.
|
|
|
|
|
*/
|
|
|
|
|
const uint32_t *address = nullptr;
|
|
|
|
|
RegisterPair16 *value = nullptr;
|
2019-03-16 21:54:58 +00:00
|
|
|
|
|
2019-03-17 01:47:46 +00:00
|
|
|
|
bool operator ==(const Microcycle &rhs) const {
|
|
|
|
|
return
|
|
|
|
|
std::make_tuple(value, address, length, operation, value) ==
|
|
|
|
|
std::make_tuple(rhs.value, rhs.address, rhs.length, rhs.operation, rhs.value);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-16 21:54:58 +00:00
|
|
|
|
// Various inspectors.
|
|
|
|
|
|
|
|
|
|
/*! @returns true if any data select line is active; @c false otherwise. */
|
|
|
|
|
inline bool data_select_active() const {
|
|
|
|
|
return bool(operation & (SelectWord | SelectByte));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
@returns 0 if this byte access wants the low part of a 16-bit word; 8 if it wants the high part.
|
|
|
|
|
*/
|
|
|
|
|
inline unsigned int byte_shift() const {
|
|
|
|
|
return (((*address) & 1) << 3) ^ 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
2019-04-19 17:29:35 +00:00
|
|
|
|
Obtains the mask to apply to a word that will leave only the byte this microcycle is selecting.
|
|
|
|
|
|
2019-03-16 21:54:58 +00:00
|
|
|
|
@returns 0x00ff if this byte access wants the low part of a 16-bit word; 0xff00 if it wants the high part.
|
|
|
|
|
*/
|
2019-04-19 17:29:35 +00:00
|
|
|
|
inline uint16_t byte_mask() const {
|
|
|
|
|
return uint16_t(0xff00) >> (((*address) & 1) << 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
Obtains the mask to apply to a word that will leave only the byte this microcycle **isn't** selecting.
|
|
|
|
|
i.e. this is the part of a word that should be untouched by this microcycle.
|
|
|
|
|
|
|
|
|
|
@returns 0xff00 if this byte access wants the low part of a 16-bit word; 0x00ff if it wants the high part.
|
|
|
|
|
*/
|
|
|
|
|
inline uint16_t untouched_byte_mask() const {
|
|
|
|
|
return uint16_t(uint16_t(0xff) << (((*address) & 1) << 3));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
Assuming this cycle is a byte write, mutates @c destination by writing the byte to the proper upper or
|
|
|
|
|
lower part, retaining the other half.
|
|
|
|
|
*/
|
|
|
|
|
uint16_t write_byte(uint16_t destination) const {
|
|
|
|
|
return uint16_t((destination & untouched_byte_mask()) | (value->halves.low << byte_shift()));
|
2019-03-16 21:54:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-19 17:29:35 +00:00
|
|
|
|
/*!
|
|
|
|
|
@returns non-zero if this is a byte read and 68000 LDS is asserted.
|
|
|
|
|
*/
|
2019-03-16 21:54:58 +00:00
|
|
|
|
inline int lower_data_select() const {
|
|
|
|
|
return (operation & SelectByte) & ((*address & 1) << 3);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-19 17:29:35 +00:00
|
|
|
|
/*!
|
|
|
|
|
@returns non-zero if this is a byte read and 68000 UDS is asserted.
|
|
|
|
|
*/
|
2019-03-16 21:54:58 +00:00
|
|
|
|
inline int upper_data_select() const {
|
|
|
|
|
return (operation & SelectByte) & ~((*address & 1) << 3);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-19 17:29:35 +00:00
|
|
|
|
/*!
|
|
|
|
|
@returns the address being accessed at the precision a 68000 supplies it —
|
|
|
|
|
only 24 address bit precision, with the low bit shifted out. So it's the
|
|
|
|
|
68000 address at word precision: address 0 is the first word in the address
|
|
|
|
|
space, address 1 is the second word (i.e. the third and fourth bytes) in
|
|
|
|
|
the address space, etc.
|
|
|
|
|
*/
|
2019-03-16 21:54:58 +00:00
|
|
|
|
uint32_t word_address() const {
|
|
|
|
|
return (address ? (*address) & 0x00fffffe : 0) >> 1;
|
|
|
|
|
}
|
2019-03-09 05:00:23 +00:00
|
|
|
|
};
|
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
|
/*!
|
|
|
|
|
This is the prototype for a 68000 bus handler; real bus handlers can descend from this
|
|
|
|
|
in order to get default implementations of any changes that may occur in the expected interface.
|
|
|
|
|
*/
|
2019-03-09 05:00:23 +00:00
|
|
|
|
class BusHandler {
|
|
|
|
|
public:
|
2019-03-10 21:27:34 +00:00
|
|
|
|
/*!
|
2019-03-10 21:42:13 +00:00
|
|
|
|
Provides the bus handler with a single Microcycle to 'perform'.
|
2019-03-10 21:27:34 +00:00
|
|
|
|
|
2019-03-10 21:42:13 +00:00
|
|
|
|
FC0 and FC1 are provided inside the microcycle as the IsData and IsProgram
|
|
|
|
|
flags; FC2 is provided here as is_supervisor — it'll be either 0 or 1.
|
2019-03-10 21:27:34 +00:00
|
|
|
|
*/
|
2019-03-10 21:42:13 +00:00
|
|
|
|
HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
|
2019-03-10 21:27:34 +00:00
|
|
|
|
return HalfCycles(0);
|
2019-03-09 05:00:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
|
void flush() {}
|
2019-04-24 13:59:54 +00:00
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
Provides information about the path of execution if enabled via the template.
|
|
|
|
|
*/
|
|
|
|
|
void will_perform(uint32_t address, uint16_t opcode) {}
|
2019-03-09 05:00:23 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#include "Implementation/68000Storage.hpp"
|
|
|
|
|
|
|
|
|
|
class ProcessorBase: public ProcessorStorage {
|
|
|
|
|
};
|
|
|
|
|
|
2019-03-18 01:57:00 +00:00
|
|
|
|
struct ProcessorState {
|
|
|
|
|
uint32_t data[8];
|
|
|
|
|
uint32_t address[7];
|
|
|
|
|
uint32_t user_stack_pointer, supervisor_stack_pointer;
|
|
|
|
|
uint32_t program_counter;
|
|
|
|
|
uint16_t status;
|
|
|
|
|
|
|
|
|
|
// TODO: More state needed to indicate current instruction, the processor's
|
|
|
|
|
// progress through it, and anything it has fetched so far.
|
|
|
|
|
// uint16_t current_instruction;
|
|
|
|
|
};
|
|
|
|
|
|
2019-04-24 13:59:54 +00:00
|
|
|
|
template <class T, bool dtack_is_implicit, bool signal_will_perform = false> class Processor: public ProcessorBase {
|
2019-03-09 05:00:23 +00:00
|
|
|
|
public:
|
2019-03-10 22:40:12 +00:00
|
|
|
|
Processor(T &bus_handler) : ProcessorBase(), bus_handler_(bus_handler) {}
|
|
|
|
|
|
|
|
|
|
void run_for(HalfCycles duration);
|
2019-03-09 05:00:23 +00:00
|
|
|
|
|
2019-03-18 01:57:00 +00:00
|
|
|
|
using State = ProcessorState;
|
|
|
|
|
State get_state();
|
|
|
|
|
void set_state(const State &);
|
|
|
|
|
|
2019-04-29 17:45:53 +00:00
|
|
|
|
/// Sets the DTack line — @c true for active, @c false for inactive.
|
|
|
|
|
void set_dtack(bool);
|
|
|
|
|
|
|
|
|
|
/// Sets the VPA (valid peripheral address) line — @c true for active, @c false for inactive.
|
|
|
|
|
void set_is_peripheral_address(bool);
|
|
|
|
|
|
|
|
|
|
/// Sets the bus error line — @c true for active, @c false for inactive.
|
|
|
|
|
void set_bus_error(bool);
|
|
|
|
|
|
|
|
|
|
/// Sets the interrupt lines, IPL0, IPL1 and IPL2.
|
|
|
|
|
void set_interrupt_level(int);
|
|
|
|
|
|
|
|
|
|
/// Sets the bus request line.
|
|
|
|
|
void set_bus_request(bool);
|
|
|
|
|
|
|
|
|
|
/// Sets the bus acknowledge line.
|
|
|
|
|
void set_bus_acknowledge(bool);
|
|
|
|
|
|
2019-03-09 05:00:23 +00:00
|
|
|
|
private:
|
|
|
|
|
T &bus_handler_;
|
|
|
|
|
};
|
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
|
#include "Implementation/68000Implementation.hpp"
|
|
|
|
|
|
2019-03-09 05:00:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* MC68000_h */
|