diff --git a/Components/68901/MFP68901.cpp b/Components/68901/MFP68901.cpp index be1b48a86..5466ad959 100644 --- a/Components/68901/MFP68901.cpp +++ b/Components/68901/MFP68901.cpp @@ -8,21 +8,154 @@ #include "MFP68901.hpp" +#include "../../Outputs/Log.hpp" + using namespace Motorola::MFP68901; uint8_t MFP68901::read(int address) { - /* TODO */ + address &= 0x1f; + switch(address) { + case 0x00: LOG("Read: general purpose IO"); break; + case 0x01: LOG("Read: active edge"); break; + case 0x02: LOG("Read: data direction"); break; + case 0x03: LOG("Read: interrupt enable A"); break; + case 0x04: LOG("Read: interrupt enable B"); break; + case 0x05: LOG("Read: interrupt pending A"); break; + case 0x06: LOG("Read: interrupt pending B"); break; + case 0x07: LOG("Read: interrupt in-service A"); break; + case 0x08: LOG("Read: interrupt in-service B"); break; + case 0x09: LOG("Read: interrupt mask A"); break; + case 0x0a: LOG("Read: interrupt mask B"); break; + case 0x0b: LOG("Read: vector"); break; + case 0x0c: LOG("Read: timer A control"); break; + case 0x0d: LOG("Read: timer B control"); break; + case 0x0e: LOG("Read: timers C/D control"); break; + case 0x0f: case 0x10: case 0x11: case 0x12: + return get_timer_data(address - 0xf); + case 0x13: LOG("Read: sync character generator"); break; + case 0x14: LOG("Read: USART control"); break; + case 0x15: LOG("Read: receiver status"); break; + case 0x16: LOG("Read: transmitter status"); break; + case 0x17: LOG("Read: USART data"); break; + } return 0xff; } void MFP68901::write(int address, uint8_t value) { - /* TODO */ + address &= 0x1f; + switch(address) { + case 0x00: LOG("Write: general purpose IO"); break; + case 0x01: LOG("Write: active edge"); break; + case 0x02: LOG("Write: data direction"); break; + case 0x03: LOG("Write: interrupt enable A"); break; + case 0x04: LOG("Write: interrupt enable B"); break; + case 0x05: LOG("Write: interrupt pending A"); break; + case 0x06: LOG("Write: interrupt pending B"); break; + case 0x07: LOG("Write: interrupt in-service A"); break; + case 0x08: LOG("Write: interrupt in-service B"); break; + case 0x09: LOG("Write: interrupt mask A"); break; + case 0x0a: LOG("Write: interrupt mask B"); break; + case 0x0b: LOG("Write: vector"); break; + case 0x0c: + case 0x0d: { + const auto timer = address - 0xc; + const bool reset = value & 0x10; + switch(value & 0xf) { + case 0x0: set_timer_mode(timer, TimerMode::Stopped, 0, reset); break; + case 0x1: set_timer_mode(timer, TimerMode::Delay, 4, reset); break; + case 0x2: set_timer_mode(timer, TimerMode::Delay, 10, reset); break; + case 0x3: set_timer_mode(timer, TimerMode::Delay, 16, reset); break; + case 0x4: set_timer_mode(timer, TimerMode::Delay, 50, reset); break; + case 0x5: set_timer_mode(timer, TimerMode::Delay, 64, reset); break; + case 0x6: set_timer_mode(timer, TimerMode::Delay, 100, reset); break; + case 0x7: set_timer_mode(timer, TimerMode::Delay, 200, reset); break; + case 0x8: set_timer_mode(timer, TimerMode::EventCount, 0, reset); break; + case 0x9: set_timer_mode(timer, TimerMode::PulseWidth, 4, reset); break; + case 0xa: set_timer_mode(timer, TimerMode::PulseWidth, 10, reset); break; + case 0xb: set_timer_mode(timer, TimerMode::PulseWidth, 16, reset); break; + case 0xc: set_timer_mode(timer, TimerMode::PulseWidth, 50, reset); break; + case 0xd: set_timer_mode(timer, TimerMode::PulseWidth, 64, reset); break; + case 0xe: set_timer_mode(timer, TimerMode::PulseWidth, 100, reset); break; + case 0xf: set_timer_mode(timer, TimerMode::PulseWidth, 200, reset); break; + } + } break; + case 0x0e: + switch(value & 7) { + case 0: set_timer_mode(3, TimerMode::Stopped, 0, false); break; + case 1: set_timer_mode(3, TimerMode::Delay, 4, false); break; + case 2: set_timer_mode(3, TimerMode::Delay, 10, false); break; + case 3: set_timer_mode(3, TimerMode::Delay, 16, false); break; + case 4: set_timer_mode(3, TimerMode::Delay, 50, false); break; + case 5: set_timer_mode(3, TimerMode::Delay, 64, false); break; + case 6: set_timer_mode(3, TimerMode::Delay, 100, false); break; + case 7: set_timer_mode(3, TimerMode::Delay, 200, false); break; + } + switch((value >> 4) & 7) { + case 0: set_timer_mode(2, TimerMode::Stopped, 0, false); break; + case 1: set_timer_mode(2, TimerMode::Delay, 4, false); break; + case 2: set_timer_mode(2, TimerMode::Delay, 10, false); break; + case 3: set_timer_mode(2, TimerMode::Delay, 16, false); break; + case 4: set_timer_mode(2, TimerMode::Delay, 50, false); break; + case 5: set_timer_mode(2, TimerMode::Delay, 64, false); break; + case 6: set_timer_mode(2, TimerMode::Delay, 100, false); break; + case 7: set_timer_mode(2, TimerMode::Delay, 200, false); break; + } + break; + case 0x0f: case 0x10: case 0x11: case 0x12: + set_timer_data(address - 0xf, value); + break; + case 0x13: LOG("Write: sync character generator"); break; + case 0x14: LOG("Write: USART control"); break; + case 0x15: LOG("Write: receiver status"); break; + case 0x16: LOG("Write: transmitter status"); break; + case 0x17: LOG("Write: USART data"); break; + } } -void MFP68901::run_for(HalfCycles) { - /* TODO */ +void MFP68901::run_for(HalfCycles time) { + cycles_left_ += time; + + // TODO: this is the stupidest possible implementation. Improve. + int cycles = cycles_left_.flush().as_int(); + while(cycles--) { + for(int c = 0; c < 4; ++c) { + if(timers_[c].mode >= TimerMode::Delay) { + --timers_[c].divisor; + if(!timers_[c].divisor) { + timers_[c].divisor = timers_[c].prescale; + + --timers_[c].value; + if(!timers_[c].value) { + // TODO: interrupt. + } + } + } + } + } } HalfCycles MFP68901::get_next_sequence_point() { return HalfCycles(-1); } + +// MARK: - Timers + +void MFP68901::set_timer_mode(int timer, TimerMode mode, int prescale, bool reset_timer) { + timers_[timer].mode = mode; + timers_[timer].prescale = prescale; + if(reset_timer) { + timers_[timer].divisor = prescale; + timers_[timer].value = timers_[timer].reload_value; + } +} + +void MFP68901::set_timer_data(int timer, uint8_t value) { + if(timers_[timer].mode == TimerMode::Stopped) { + timers_[timer].value = value; + } + timers_[timer].reload_value = value; +} + +uint8_t MFP68901::get_timer_data(int timer) { + return timers_[timer].value; +} diff --git a/Components/68901/MFP68901.hpp b/Components/68901/MFP68901.hpp index fbecb5c0b..d94cc04c5 100644 --- a/Components/68901/MFP68901.hpp +++ b/Components/68901/MFP68901.hpp @@ -22,6 +22,25 @@ class MFP68901 { void run_for(HalfCycles); HalfCycles get_next_sequence_point(); + + private: + // MARK: - Timers + enum class TimerMode { + Stopped, EventCount, Delay, PulseWidth + }; + void set_timer_mode(int timer, TimerMode, int prescale, bool reset_timer); + void set_timer_data(int timer, uint8_t); + uint8_t get_timer_data(int timer); + + struct Timer { + TimerMode mode = TimerMode::Stopped; + uint8_t value = 0; + uint8_t reload_value = 0; + int prescale = 1; + int divisor = 0; + } timers_[4]; + + HalfCycles cycles_left_; }; } diff --git a/Machines/AtariST/AtariST.cpp b/Machines/AtariST/AtariST.cpp index b7bef6755..042261312 100644 --- a/Machines/AtariST/AtariST.cpp +++ b/Machines/AtariST/AtariST.cpp @@ -96,7 +96,7 @@ class ConcreteMachine: /* TODO: DTack, bus error, VPA. */ auto address = cycle.word_address(); - if(cycle.data_select_active()) printf("%c %06x\n", (cycle.operation & Microcycle::Read) ? 'r' : 'w', *cycle.address & 0xffffff); +// if(cycle.data_select_active()) printf("%c %06x\n", (cycle.operation & Microcycle::Read) ? 'r' : 'w', *cycle.address & 0xffffff); uint16_t *memory; switch(memory_map_[address >> 15]) { case BusDevice::MostlyRAM: @@ -222,6 +222,19 @@ class ConcreteMachine: mfp_->write(int(address), cycle.value->halves.high); } } + + /* + Atari ST GPIP bits: + + GPIP 7: monochrome monitor detect + GPIP 6: RS-232 ring indicator + GPIP 5: FD/HD interrupt + GPIP 4: keyboard/MIDI interrupt + GPIP 3: unused + GPIP 2: RS-232 clear to send + GPIP 1: RS-232 carrier detect + GPIP 0: centronics busy + */ break; } return HalfCycles(0);