mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-08 03:54:27 +00:00
Flattens the Macintosh's perform_bus_operation, for legibility.
This commit is contained in:
parent
69b94719a1
commit
7886cd63bd
@ -137,6 +137,9 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
using Microcycle = CPU::MC68000::Microcycle;
|
using Microcycle = CPU::MC68000::Microcycle;
|
||||||
|
|
||||||
HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
|
HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
|
||||||
|
// TODO: pick a delay if this is a video-clashing memory fetch.
|
||||||
|
HalfCycles delay(0);
|
||||||
|
|
||||||
time_since_video_update_ += cycle.length;
|
time_since_video_update_ += cycle.length;
|
||||||
iwm_.time_since_update += cycle.length;
|
iwm_.time_since_update += cycle.length;
|
||||||
|
|
||||||
@ -209,167 +212,152 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
} else {
|
} else {
|
||||||
mc68000_.set_interrupt_level(0);
|
mc68000_.set_interrupt_level(0);
|
||||||
}
|
}
|
||||||
// mc68000_.set_interrupt_level(
|
|
||||||
// (via_.get_interrupt_line() ? 1 : 0) |
|
|
||||||
// (scc_.get_interrupt_line() ? 2 : 0)
|
|
||||||
// /* TODO: to emulate a programmer's switch: have it set bit 2 when pressed. */
|
|
||||||
// );
|
|
||||||
|
|
||||||
// A null cycle leaves nothing else to do.
|
// A null cycle leaves nothing else to do.
|
||||||
if(cycle.operation) {
|
if(!cycle.operation) return delay;
|
||||||
auto word_address = cycle.word_address();
|
|
||||||
|
|
||||||
// Everything above E0 0000 is signalled as being on the peripheral bus.
|
auto word_address = cycle.word_address();
|
||||||
mc68000_.set_is_peripheral_address(word_address >= 0x700000);
|
|
||||||
|
|
||||||
if(word_address >= 0x400000) {
|
// Everything above E0 0000 is signalled as being on the peripheral bus.
|
||||||
if(cycle.data_select_active()) {
|
mc68000_.set_is_peripheral_address(word_address >= 0x700000);
|
||||||
const int register_address = word_address >> 8;
|
|
||||||
|
|
||||||
switch(word_address & 0x78f000) {
|
// All code below deals only with reads and writes — cycles in which a
|
||||||
case 0x70f000:
|
// data select is active. So quit now if this is not the active part of
|
||||||
// VIA accesses are via address 0xefe1fe + register*512,
|
// a read or write.
|
||||||
// which at word precision is 0x77f0ff + register*256.
|
if(!cycle.data_select_active()) return delay;
|
||||||
if(cycle.operation & Microcycle::Read) {
|
|
||||||
cycle.value->halves.low = via_.get_register(register_address);
|
// Check whether this access maps into the IO area; if so then
|
||||||
|
// apply more complicated decoding logic.
|
||||||
|
if(word_address >= 0x400000) {
|
||||||
|
const int register_address = word_address >> 8;
|
||||||
|
|
||||||
|
switch(word_address & 0x78f000) {
|
||||||
|
case 0x70f000:
|
||||||
|
// VIA accesses are via address 0xefe1fe + register*512,
|
||||||
|
// which at word precision is 0x77f0ff + register*256.
|
||||||
|
if(cycle.operation & Microcycle::Read) {
|
||||||
|
cycle.value->halves.low = via_.get_register(register_address);
|
||||||
|
} else {
|
||||||
|
via_.set_register(register_address, cycle.value->halves.low);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x68f000:
|
||||||
|
// The IWM; this is a purely polled device, so can be run on demand.
|
||||||
|
iwm_.flush();
|
||||||
|
if(cycle.operation & Microcycle::Read) {
|
||||||
|
cycle.value->halves.low = iwm_.iwm.read(register_address);
|
||||||
|
} else {
|
||||||
|
iwm_.iwm.write(register_address, cycle.value->halves.low);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x780000:
|
||||||
|
// Phase read.
|
||||||
|
if(cycle.operation & Microcycle::Read) {
|
||||||
|
cycle.value->halves.low = phase_ & 7;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x480000: case 0x48f000:
|
||||||
|
case 0x580000: case 0x58f000:
|
||||||
|
// Any word access here adjusts phase.
|
||||||
|
if(cycle.operation & Microcycle::SelectWord) {
|
||||||
|
++phase_;
|
||||||
|
} else {
|
||||||
|
if(word_address < 0x500000) {
|
||||||
|
// A0 = 1 => reset; A0 = 0 => read.
|
||||||
|
if(*cycle.address & 1) {
|
||||||
|
scc_.reset();
|
||||||
} else {
|
} else {
|
||||||
via_.set_register(register_address, cycle.value->halves.low);
|
const auto read = scc_.read(int(word_address));
|
||||||
}
|
if(cycle.operation & Microcycle::Read) {
|
||||||
break;
|
cycle.value->halves.low = read;
|
||||||
|
|
||||||
case 0x68f000:
|
|
||||||
// The IWM; this is a purely polled device, so can be run on demand.
|
|
||||||
#ifndef NDEBUG
|
|
||||||
// printf("[%06x]: ", mc68000_.get_state().program_counter);
|
|
||||||
#endif
|
|
||||||
iwm_.flush();
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
|
||||||
cycle.value->halves.low = iwm_.iwm.read(register_address);
|
|
||||||
} else {
|
|
||||||
iwm_.iwm.write(register_address, cycle.value->halves.low);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x780000:
|
|
||||||
// Phase read.
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
|
||||||
cycle.value->halves.low = phase_ & 7;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x480000: case 0x48f000:
|
|
||||||
case 0x580000: case 0x58f000:
|
|
||||||
// Any word access here adjusts phase.
|
|
||||||
if(cycle.operation & Microcycle::SelectWord) {
|
|
||||||
++phase_;
|
|
||||||
} else {
|
|
||||||
if(word_address < 0x500000) {
|
|
||||||
// A0 = 1 => reset; A0 = 0 => read.
|
|
||||||
if(*cycle.address & 1) {
|
|
||||||
scc_.reset();
|
|
||||||
} else {
|
|
||||||
const auto read = scc_.read(int(word_address));
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
|
||||||
cycle.value->halves.low = read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(*cycle.address & 1) {
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
|
||||||
scc_.write(int(word_address), 0xff);
|
|
||||||
} else {
|
|
||||||
scc_.write(int(word_address), cycle.value->halves.low);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
|
if(*cycle.address & 1) {
|
||||||
default:
|
if(cycle.operation & Microcycle::Read) {
|
||||||
if(cycle.operation & Microcycle::Read) {
|
scc_.write(int(word_address), 0xff);
|
||||||
printf("Unrecognised read %06x\n", *cycle.address & 0xffffff);
|
} else {
|
||||||
cycle.value->halves.low = 0x00;
|
scc_.write(int(word_address), cycle.value->halves.low);
|
||||||
} else {
|
}
|
||||||
printf("Unrecognised write %06x\n", *cycle.address & 0xffffff);
|
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
if(cycle.operation & Microcycle::SelectWord) cycle.value->halves.high = 0xff;
|
break;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(cycle.data_select_active()) {
|
|
||||||
uint16_t *memory_base = nullptr;
|
|
||||||
auto operation = cycle.operation;
|
|
||||||
|
|
||||||
// When ROM overlay is enabled, the ROM begins at both $000000 and $400000,
|
default:
|
||||||
// and RAM is available at $600000.
|
if(cycle.operation & Microcycle::Read) {
|
||||||
//
|
printf("Unrecognised read %06x\n", *cycle.address & 0xffffff);
|
||||||
// Otherwise RAM is mapped at $000000 and ROM from $400000.
|
cycle.value->halves.low = 0x00;
|
||||||
if(
|
|
||||||
(ROM_is_overlay_ && word_address >= 0x300000) ||
|
|
||||||
(!ROM_is_overlay_ && word_address < 0x200000)
|
|
||||||
) {
|
|
||||||
memory_base = ram_;
|
|
||||||
word_address &= ram_mask_;
|
|
||||||
update_video();
|
|
||||||
} else {
|
} else {
|
||||||
memory_base = rom_;
|
printf("Unrecognised write %06x\n", *cycle.address & 0xffffff);
|
||||||
word_address &= rom_mask_;
|
|
||||||
|
|
||||||
// Disallow writes to ROM; also it doesn't mirror above 0x60000, ever.
|
|
||||||
if(!(operation & Microcycle::Read) || word_address >= 0x300000) operation = 0;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
const auto masked_operation = operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read | Microcycle::InterruptAcknowledge);
|
|
||||||
switch(masked_operation) {
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Catches the deliberation set of operation to 0 above.
|
|
||||||
case 0: break;
|
|
||||||
|
|
||||||
case Microcycle::InterruptAcknowledge | Microcycle::SelectByte:
|
|
||||||
// The Macintosh uses autovectored interrupts.
|
|
||||||
mc68000_.set_is_peripheral_address(true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Microcycle::SelectWord | Microcycle::Read:
|
|
||||||
cycle.value->full = memory_base[word_address];
|
|
||||||
break;
|
|
||||||
case Microcycle::SelectByte | Microcycle::Read:
|
|
||||||
cycle.value->halves.low = uint8_t(memory_base[word_address] >> cycle.byte_shift());
|
|
||||||
break;
|
|
||||||
case Microcycle::SelectWord:
|
|
||||||
memory_base[word_address] = cycle.value->full;
|
|
||||||
break;
|
|
||||||
case Microcycle::SelectByte:
|
|
||||||
memory_base[word_address] = uint16_t(
|
|
||||||
(cycle.value->halves.low << cycle.byte_shift()) |
|
|
||||||
(memory_base[word_address] & cycle.untouched_byte_mask())
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if(!(operation & Microcycle::Read) && (word_address == (0x00000172 >> 1))) {
|
|
||||||
// if(operation & Microcycle::SelectByte)
|
|
||||||
// printf("MBState: %02x\n", cycle.value->halves.low);
|
|
||||||
// else
|
|
||||||
// printf("MBState: %04x\n", cycle.value->full);
|
|
||||||
// }
|
|
||||||
// if(
|
|
||||||
// (
|
|
||||||
// (word_address == (0x00000352 >> 1))
|
|
||||||
// || (word_address == (0x00000354 >> 1))
|
|
||||||
// || (word_address == (0x00005d16 >> 1))
|
|
||||||
// )
|
|
||||||
// ) {
|
|
||||||
// printf("%s %08x: %04x from around %08x\n", (operation & Microcycle::Read) ? "Read" : "Write", word_address << 1, memory_base[word_address], mc68000_.get_state().program_counter);
|
|
||||||
// }
|
|
||||||
} else {
|
|
||||||
// TODO: add delay if this is a RAM access and video blocks it momentarily.
|
|
||||||
// "Each [video] fetch took two cycles out of eight"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if(cycle.operation & Microcycle::SelectWord) cycle.value->halves.high = 0xff;
|
||||||
|
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Having reached here, this is a RAM or ROM access.
|
||||||
|
|
||||||
|
// When ROM overlay is enabled, the ROM begins at both $000000 and $400000,
|
||||||
|
// and RAM is available at $600000.
|
||||||
|
//
|
||||||
|
// Otherwise RAM is mapped at $000000 and ROM from $400000.
|
||||||
|
uint16_t *memory_base;
|
||||||
|
if(
|
||||||
|
(!ROM_is_overlay_ && word_address < 0x200000) ||
|
||||||
|
(ROM_is_overlay_ && word_address >= 0x300000)
|
||||||
|
) {
|
||||||
|
memory_base = ram_;
|
||||||
|
word_address &= ram_mask_;
|
||||||
|
update_video();
|
||||||
|
} else {
|
||||||
|
memory_base = rom_;
|
||||||
|
word_address &= rom_mask_;
|
||||||
|
|
||||||
|
// Writes to ROM have no effect, and it doesn't mirror above 0x60000.
|
||||||
|
if(!(cycle.operation & Microcycle::Read)) return delay;
|
||||||
|
if(word_address >= 0x300000) {
|
||||||
|
if(cycle.operation & Microcycle::SelectWord) {
|
||||||
|
cycle.value->full = 0xffff;
|
||||||
|
} else {
|
||||||
|
cycle.value->halves.low = 0xff;
|
||||||
|
}
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read | Microcycle::InterruptAcknowledge)) {
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Catches the deliberation set of operation to 0 above.
|
||||||
|
case 0: break;
|
||||||
|
|
||||||
|
case Microcycle::InterruptAcknowledge | Microcycle::SelectByte:
|
||||||
|
// The Macintosh uses autovectored interrupts.
|
||||||
|
mc68000_.set_is_peripheral_address(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Microcycle::SelectWord | Microcycle::Read:
|
||||||
|
cycle.value->full = memory_base[word_address];
|
||||||
|
break;
|
||||||
|
case Microcycle::SelectByte | Microcycle::Read:
|
||||||
|
cycle.value->halves.low = uint8_t(memory_base[word_address] >> cycle.byte_shift());
|
||||||
|
break;
|
||||||
|
case Microcycle::SelectWord:
|
||||||
|
memory_base[word_address] = cycle.value->full;
|
||||||
|
break;
|
||||||
|
case Microcycle::SelectByte:
|
||||||
|
memory_base[word_address] = uint16_t(
|
||||||
|
(cycle.value->halves.low << cycle.byte_shift()) |
|
||||||
|
(memory_base[word_address] & cycle.untouched_byte_mask())
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -383,7 +371,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
EFE1FE+: VIA
|
EFE1FE+: VIA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return HalfCycles(0);
|
return delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
|
Loading…
Reference in New Issue
Block a user