1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +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:
Thomas Harte 2019-03-16 17:54:58 -04:00
parent 720aba3f2d
commit 388d808536
6 changed files with 140 additions and 54 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
};
/*!

View File

@ -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;
}

View File

@ -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 });
}

View File

@ -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;