mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +00:00
Switches to providing UDS and LDS implicitly via address.
Also makes sure that the difference between a non-data cycle that starts without the address strobe active and one that starts with it active can be discerned.
This commit is contained in:
parent
720aba3f2d
commit
388d808536
@ -39,19 +39,26 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
uint32_t address = cycle.address ? (*cycle.address) & 0x00fffffe : 0;
|
||||
const uint32_t word_address = cycle.word_address();
|
||||
|
||||
switch(cycle.operation & (CPU::MC68000::Microcycle::LowerData | CPU::MC68000::Microcycle::UpperData)) {
|
||||
case 0: break;
|
||||
case CPU::MC68000::Microcycle::LowerData:
|
||||
cycle.value->halves.low = ram_[address >> 1] >> 8;
|
||||
break;
|
||||
case CPU::MC68000::Microcycle::UpperData:
|
||||
cycle.value->halves.high = ram_[address >> 1] & 0xff;
|
||||
break;
|
||||
case CPU::MC68000::Microcycle::UpperData | CPU::MC68000::Microcycle::LowerData:
|
||||
cycle.value->full = ram_[address >> 1];
|
||||
break;
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
if(cycle.data_select_active()) {
|
||||
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||
default: break;
|
||||
|
||||
case Microcycle::SelectWord | Microcycle::Read:
|
||||
cycle.value->full = ram_[word_address];
|
||||
break;
|
||||
case Microcycle::SelectByte | Microcycle::Read:
|
||||
cycle.value->halves.low = ram_[word_address] >> cycle.byte_shift();
|
||||
break;
|
||||
case Microcycle::SelectWord:
|
||||
ram_[word_address] = cycle.value->full;
|
||||
break;
|
||||
case Microcycle::SelectByte:
|
||||
ram_[word_address] = (cycle.value->full & cycle.byte_mask()) | (ram_[word_address] & (0xffff ^ cycle.byte_mask()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return HalfCycles(0);
|
||||
|
@ -30,33 +30,39 @@ class EmuTOS: public CPU::MC68000::BusHandler {
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
uint32_t address = cycle.address ? (*cycle.address) & 0x00fffffe : 0;
|
||||
uint32_t word_address = cycle.word_address();
|
||||
|
||||
// As much about the Atari ST's memory map as is relevant here: the ROM begins
|
||||
// at 0xfc0000, and the first eight bytes are mirrored to the first four memory
|
||||
// addresses in order for /RESET to work properly. RAM otherwise fills the first
|
||||
// 512kb of the address space. Trying to write to ROM raises a bus error.
|
||||
|
||||
const bool is_rom = address > 0xfc0000 || address < 8;
|
||||
const bool is_rom = word_address > (0xfc0000 >> 1) || word_address < 4;
|
||||
uint16_t *const base = is_rom ? emuTOS_.data() : ram_.data();
|
||||
if(is_rom) {
|
||||
address &= 0x1ffff;
|
||||
word_address &= 0xffff;
|
||||
} else {
|
||||
address &= 0x7ffff;
|
||||
word_address &= 0x3ffff;
|
||||
}
|
||||
|
||||
// TODO: discern reads and writes (!)
|
||||
switch(cycle.operation & (CPU::MC68000::Microcycle::LowerData | CPU::MC68000::Microcycle::UpperData)) {
|
||||
case 0: break;
|
||||
case CPU::MC68000::Microcycle::LowerData:
|
||||
cycle.value->halves.low = base[address >> 1] >> 8;
|
||||
break;
|
||||
case CPU::MC68000::Microcycle::UpperData:
|
||||
cycle.value->halves.high = base[address >> 1] & 0xff;
|
||||
break;
|
||||
case CPU::MC68000::Microcycle::UpperData | CPU::MC68000::Microcycle::LowerData:
|
||||
cycle.value->full = base[address >> 1];
|
||||
break;
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
if(cycle.data_select_active()) {
|
||||
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||
default: break;
|
||||
|
||||
case Microcycle::SelectWord | Microcycle::Read:
|
||||
cycle.value->full = base[word_address];
|
||||
break;
|
||||
case Microcycle::SelectByte | Microcycle::Read:
|
||||
cycle.value->halves.low = base[word_address] >> cycle.byte_shift();
|
||||
break;
|
||||
case Microcycle::SelectWord:
|
||||
base[word_address] = cycle.value->full;
|
||||
break;
|
||||
case Microcycle::SelectByte:
|
||||
base[word_address] = (cycle.value->full & cycle.byte_mask()) | (base[word_address] & (0xffff ^ cycle.byte_mask()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return HalfCycles(0);
|
||||
|
@ -45,16 +45,39 @@ namespace MC68000 {
|
||||
*/
|
||||
struct Microcycle {
|
||||
/*
|
||||
The operation code is a mask of all the signals that relevantly became active during
|
||||
this microcycle.
|
||||
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.
|
||||
*/
|
||||
static const int Address = 1 << 0;
|
||||
static const int UpperData = 1 << 1;
|
||||
static const int LowerData = 1 << 2;
|
||||
static const int ReadWrite = 1 << 3; // Set = read; unset = write.
|
||||
static const int TypeMask = 3;
|
||||
|
||||
static const int IsData = 1 << 4; // i.e. this is FC0.
|
||||
static const int IsProgram = 1 << 5; // i.e. this is FC1.
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
int operation = 0;
|
||||
HalfCycles length = HalfCycles(2);
|
||||
@ -68,6 +91,39 @@ struct Microcycle {
|
||||
*/
|
||||
const uint32_t *address = nullptr;
|
||||
RegisterPair16 *value = nullptr;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns 0x00ff if this byte access wants the low part of a 16-bit word; 0xff00 if it wants the high part.
|
||||
*/
|
||||
inline unsigned int byte_mask() const {
|
||||
return 0xff00 >> (((*address) & 1) << 3);
|
||||
}
|
||||
|
||||
inline int lower_data_select() const {
|
||||
return (operation & SelectByte) & ((*address & 1) << 3);
|
||||
}
|
||||
|
||||
inline int upper_data_select() const {
|
||||
return (operation & SelectByte) & ~((*address & 1) << 3);
|
||||
}
|
||||
|
||||
uint32_t word_address() const {
|
||||
return (address ? (*address) & 0x00fffffe : 0) >> 1;
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -52,7 +52,7 @@ template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
|
||||
result += (destination & 0xf0) - (source & 0xf0);
|
||||
if(result > 0x90) result -= 0x60;
|
||||
|
||||
// Set all flags essentially as if this were normal addition.
|
||||
// Set all flags essentially as if this were normal subtraction.
|
||||
zero_flag_ |= result & 0xff;
|
||||
extend_flag_ = carry_flag_ = result & ~0xff;
|
||||
negative_flag_ = result & 0x80;
|
||||
@ -62,6 +62,21 @@ template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
|
||||
active_program_->destination->halves.low.halves.low = uint8_t(result);
|
||||
} break;
|
||||
|
||||
case Operation::MOVEb:
|
||||
zero_flag_ = active_program_->destination->halves.low.halves.low = active_program_->source->halves.low.halves.low;
|
||||
negative_flag_ = zero_flag_ & 0x80;
|
||||
break;
|
||||
|
||||
case Operation::MOVEw:
|
||||
zero_flag_ = active_program_->destination->halves.low.full = active_program_->source->halves.low.full;
|
||||
negative_flag_ = zero_flag_ & 0x8000;
|
||||
break;
|
||||
|
||||
case Operation::MOVEl:
|
||||
zero_flag_ = active_program_->destination->full = active_program_->source->full;
|
||||
negative_flag_ = zero_flag_ & 0x80000000;
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cerr << "Should do something with program operation " << int(active_program_->operation) << std::endl;
|
||||
break;
|
||||
@ -107,7 +122,7 @@ template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
|
||||
|
||||
// Check for DTack if this isn't being treated implicitly.
|
||||
if(!dtack_is_implicit) {
|
||||
if(active_step_->microcycle.operation & (Microcycle::UpperData | Microcycle::LowerData) && !dtack_) {
|
||||
if(active_step_->microcycle.data_select_active() && !dtack_) {
|
||||
// TODO: perform wait state.
|
||||
continue;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ ProcessorStorage::ProcessorStorage() {
|
||||
is_supervisor_ = 1;
|
||||
}
|
||||
|
||||
size_t ProcessorStorage::assemble_program(const char *access_pattern, const std::vector<uint32_t *> &addresses, int data_mask) {
|
||||
size_t ProcessorStorage::assemble_program(const char *access_pattern, const std::vector<uint32_t *> &addresses, bool read_full_words) {
|
||||
const size_t start = all_bus_steps_.size();
|
||||
auto address_iterator = addresses.begin();
|
||||
RegisterPair32 *scratch_data_read = bus_data_;
|
||||
@ -64,13 +64,13 @@ size_t ProcessorStorage::assemble_program(const char *access_pattern, const std:
|
||||
case 'F': // Fetch SSP MSW.
|
||||
case 'f': // Fetch SSP LSW.
|
||||
step.microcycle.length = HalfCycles(5);
|
||||
step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess.
|
||||
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram; // IsProgram is a guess.
|
||||
step.microcycle.address = &effective_address_;
|
||||
step.microcycle.value = isupper(access_pattern[1]) ? &stack_pointers_[1].halves.high : &stack_pointers_[1].halves.low;
|
||||
all_bus_steps_.push_back(step);
|
||||
|
||||
step.microcycle.length = HalfCycles(3);
|
||||
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
|
||||
step.microcycle.operation = Microcycle::SelectWord | Microcycle::Read | Microcycle::IsProgram;
|
||||
step.action = BusStep::Action::IncrementEffectiveAddress;
|
||||
all_bus_steps_.push_back(step);
|
||||
|
||||
@ -80,13 +80,13 @@ size_t ProcessorStorage::assemble_program(const char *access_pattern, const std:
|
||||
case 'V': // Fetch exception vector low.
|
||||
case 'v': // Fetch exception vector high.
|
||||
step.microcycle.length = HalfCycles(5);
|
||||
step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess.
|
||||
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram; // IsProgram is a guess.
|
||||
step.microcycle.address = &effective_address_;
|
||||
step.microcycle.value = isupper(access_pattern[1]) ? &program_counter_.halves.high : &program_counter_.halves.low;
|
||||
all_bus_steps_.push_back(step);
|
||||
|
||||
step.microcycle.length = HalfCycles(3);
|
||||
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
|
||||
step.microcycle.operation |= Microcycle::SelectWord | Microcycle::Read | Microcycle::IsProgram;
|
||||
step.action = BusStep::Action::IncrementEffectiveAddress;
|
||||
all_bus_steps_.push_back(step);
|
||||
|
||||
@ -95,14 +95,14 @@ size_t ProcessorStorage::assemble_program(const char *access_pattern, const std:
|
||||
|
||||
case 'p': // Fetch from the program counter into the prefetch queue.
|
||||
step.microcycle.length = HalfCycles(5);
|
||||
step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram;
|
||||
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram;
|
||||
step.microcycle.address = &program_counter_.full;
|
||||
step.microcycle.value = &prefetch_queue_[1];
|
||||
step.action = BusStep::Action::AdvancePrefetch;
|
||||
all_bus_steps_.push_back(step);
|
||||
|
||||
step.microcycle.length = HalfCycles(3);
|
||||
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
|
||||
step.microcycle.operation |= Microcycle::SelectWord | Microcycle::Read | Microcycle::IsProgram;
|
||||
step.action = BusStep::Action::IncrementProgramCounter;
|
||||
all_bus_steps_.push_back(step);
|
||||
|
||||
@ -117,13 +117,13 @@ size_t ProcessorStorage::assemble_program(const char *access_pattern, const std:
|
||||
RegisterPair32 **scratch_data = is_read ? &scratch_data_read : &scratch_data_write;
|
||||
|
||||
step.microcycle.length = HalfCycles(5);
|
||||
step.microcycle.operation = Microcycle::Address | (is_read ? Microcycle::ReadWrite : 0);
|
||||
step.microcycle.operation = Microcycle::NewAddress | (is_read ? Microcycle::Read : 0);
|
||||
step.microcycle.address = *address_iterator;
|
||||
step.microcycle.value = isupper(access_pattern[1]) ? &(*scratch_data)->halves.high : &(*scratch_data)->halves.low;
|
||||
all_bus_steps_.push_back(step);
|
||||
|
||||
step.microcycle.length = HalfCycles(3);
|
||||
step.microcycle.operation |= data_mask;
|
||||
step.microcycle.operation |= (read_full_words ? Microcycle::SelectWord : Microcycle::SelectByte) | (is_read ? Microcycle::Read : 0);
|
||||
all_bus_steps_.push_back(step);
|
||||
|
||||
++address_iterator;
|
||||
@ -155,7 +155,7 @@ ProcessorStorage::BusStepCollection ProcessorStorage::assemble_standard_bus_step
|
||||
|
||||
for(int s = 0; s < 8; ++s) {
|
||||
for(int d = 0; d < 8; ++d) {
|
||||
collection.double_predec_byte[s][d] = assemble_program("n nr nr np nw", { &address_[s].full, &address_[d].full, &address_[d].full }, Microcycle::LowerData);
|
||||
collection.double_predec_byte[s][d] = assemble_program("n nr nr np nw", { &address_[s].full, &address_[d].full, &address_[d].full }, false);
|
||||
collection.double_predec_word[s][d] = assemble_program("n nr nr np nw", { &address_[s].full, &address_[d].full, &address_[d].full });
|
||||
// collection.double_predec_long[s][d] = assemble_program("n nr nR nr nR nw np nW", { &address_[s].full, &address_[d].full, &address_[d].full });
|
||||
}
|
||||
|
@ -26,11 +26,11 @@ class ProcessorStorage {
|
||||
|
||||
// Various status bits.
|
||||
int is_supervisor_;
|
||||
int zero_flag_; // The zero flag is set if this value is zero.
|
||||
int carry_flag_; // The carry flag is set if this value is non-zero.
|
||||
int extend_flag_; // The extend flag is set if this value is non-zero.
|
||||
int overflow_flag_; // The overflow flag is set if this value is non-zero.
|
||||
int negative_flag_; // The negative flag is set if this value is non-zero.
|
||||
uint_fast32_t zero_flag_; // The zero flag is set if this value is zero.
|
||||
uint_fast32_t carry_flag_; // The carry flag is set if this value is non-zero.
|
||||
uint_fast32_t extend_flag_; // The extend flag is set if this value is non-zero.
|
||||
uint_fast32_t overflow_flag_; // The overflow flag is set if this value is non-zero.
|
||||
uint_fast32_t negative_flag_; // The negative flag is set if this value is non-zero.
|
||||
|
||||
// Generic sources and targets for memory operations.
|
||||
uint32_t effective_address_;
|
||||
@ -39,6 +39,8 @@ class ProcessorStorage {
|
||||
enum class Operation {
|
||||
ABCD, SBCD,
|
||||
ADD, AND, EOR, OR, SUB,
|
||||
|
||||
MOVEb, MOVEw, MOVEl
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -161,7 +163,7 @@ class ProcessorStorage {
|
||||
The user should fill in the steps necessary to get data into or extract
|
||||
data from those.
|
||||
*/
|
||||
size_t assemble_program(const char *access_pattern, const std::vector<uint32_t *> &addresses = {}, int data_mask = Microcycle::UpperData | Microcycle::LowerData);
|
||||
size_t assemble_program(const char *access_pattern, const std::vector<uint32_t *> &addresses = {}, bool read_full_words = true);
|
||||
|
||||
struct BusStepCollection {
|
||||
size_t six_step_Dn;
|
||||
|
Loading…
Reference in New Issue
Block a user