From f466cbadec41013335e417424c7730cfa03f4d80 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 5 Nov 2020 17:56:20 -0500 Subject: [PATCH] Attempts to do just enough with video to get a functioning vertical blank query. --- Machines/Apple/AppleIIgs/AppleIIgs.cpp | 77 ++++++++++++++++++-------- Machines/Apple/AppleIIgs/Video.cpp | 47 ++++++++++++++++ Machines/Apple/AppleIIgs/Video.hpp | 19 +++++++ 3 files changed, 121 insertions(+), 22 deletions(-) diff --git a/Machines/Apple/AppleIIgs/AppleIIgs.cpp b/Machines/Apple/AppleIIgs/AppleIIgs.cpp index bac0b2ced..1f84209d3 100644 --- a/Machines/Apple/AppleIIgs/AppleIIgs.cpp +++ b/Machines/Apple/AppleIIgs/AppleIIgs.cpp @@ -26,6 +26,12 @@ #include #include +namespace { + +constexpr int CLOCK_RATE = 14318180; + +} + namespace Apple { namespace IIgs { @@ -39,7 +45,7 @@ class ConcreteMachine: ConcreteMachine(const Analyser::Static::AppleIIgs::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : m65816_(*this) { - set_clock_rate(14318180.0); + set_clock_rate(double(CLOCK_RATE)); using Target = Analyser::Static::AppleIIgs::Target; std::vector rom_descriptions; @@ -101,8 +107,6 @@ class ConcreteMachine: const auto ®ion = MemoryMapRegion(memory_, address); static bool log = false; - // TODO: potentially push time to clock_. - if(region.flags & MemoryMap::Region::IsIO) { // Ensure classic auxiliary and language card accesses have effect. const bool is_read = isReadOperation(operation); @@ -114,11 +118,29 @@ class ConcreteMachine: // New video register. case 0xc029: if(is_read) { - *value = 0x01; + *value = video_.get_new_video();; } else { - printf("New video: %02x\n", *value); - // TODO: this bit should affect memory bank selection, somehow? - // Cf. Page 90. + video_.set_new_video(*value); + + // TODO: I think bits 7 and 0 might also affect the memory map. + // The descripton isn't especially clear — P.90 of the Hardware Reference. + // Revisit if necessary. + } + break; + + // Video [and clock] interrupt register. + case 0xc023: + if(is_read) { + *value = video_.get_interrupt_register(); + } else { + video_.set_interrupt_register(*value); + } + break; + + // Video onterrupt-clear register. + case 0xc032: + if(!is_read) { + video_.clear_interrupts(*value); } break; @@ -183,21 +205,15 @@ class ConcreteMachine: case 0xc015: AuxiliaryRead(internal_CX_rom); break; case 0xc016: AuxiliaryRead(alternative_zero_page); break; case 0xc017: AuxiliaryRead(slot_C3_rom); break; - case 0xc018: VideoRead(get_80_store()); break; -// case 0xc019: VideoRead(get_is_vertical_blank(cycles_since_video_update_)); break; - case 0xc019: { - printf("TODO: vertical blank check\n"); - static uint8_t vblank = 0x80; - *value = vblank; - vblank ^= 0x80; - } break; - case 0xc01a: VideoRead(get_text()); break; - case 0xc01b: VideoRead(get_mixed()); break; - case 0xc01c: VideoRead(get_page2()); break; - case 0xc01d: VideoRead(get_high_resolution()); break; - case 0xc01e: VideoRead(get_alternative_character_set()); break; - case 0xc01f: VideoRead(get_80_columns()); break; - case 0xc046: VideoRead(get_annunciator_3()); break; + case 0xc018: VideoRead(get_80_store()); break; + case 0xc019: VideoRead(get_is_vertical_blank()); break; + case 0xc01a: VideoRead(get_text()); break; + case 0xc01b: VideoRead(get_mixed()); break; + case 0xc01c: VideoRead(get_page2()); break; + case 0xc01d: VideoRead(get_high_resolution()); break; + case 0xc01e: VideoRead(get_alternative_character_set()); break; + case 0xc01f: VideoRead(get_80_columns()); break; + case 0xc046: VideoRead(get_annunciator_3()); break; #undef VideoRead #undef AuxiliaryRead #undef LanguageRead @@ -472,6 +488,22 @@ class ConcreteMachine: // } fast_access_phase_ = (fast_access_phase_ + duration.as()) % 5; // TODO: modulo something else, to allow for refresh. slow_access_phase_ = (slow_access_phase_ + duration.as()) % 14; // TODO: modulo something else, to allow for stretched cycles. + + + // Propagate time far and wide. + cycles_since_clock_tick_ += duration; + auto ticks = cycles_since_clock_tick_.divide(Cycles(CLOCK_RATE)).as_integral(); + while(ticks--) { + clock_.update(); + video_.notify_clock_tick(); // The video controller marshalls the one-second interrupt. + } + + video_.run_for(duration); + + // Update the interrupt line. TODO: should include the sound GLU too. + m65816_.set_irq_line(video_.get_interrupt_register() & 0x80); + + return duration; } @@ -498,6 +530,7 @@ class ConcreteMachine: Apple::IIgs::ADB::GLU adb_glu_; Apple::IIgs::Sound::GLU sound_glu_; Zilog::SCC::z8530 scc_; + Cycles cycles_since_clock_tick_; // MARK: - Cards. diff --git a/Machines/Apple/AppleIIgs/Video.cpp b/Machines/Apple/AppleIIgs/Video.cpp index fc489f2e8..bd47bae9f 100644 --- a/Machines/Apple/AppleIIgs/Video.cpp +++ b/Machines/Apple/AppleIIgs/Video.cpp @@ -10,9 +10,56 @@ using namespace Apple::IIgs::Video; +namespace { + +constexpr int CyclesPerLine = 910; +constexpr int Lines = 263; +constexpr int FinalPixelLine = 192; + +} + VideoBase::VideoBase() : VideoSwitches(Cycles(2), [] (Cycles) {}) { } void VideoBase::did_set_annunciator_3(bool) {} void VideoBase::did_set_alternative_character_set(bool) {} + +void VideoBase::run_for(Cycles cycles) { + // TODO: everything else! + cycles_into_frame_ = (cycles_into_frame_ + cycles.as()) % (CyclesPerLine * Lines); +} + +bool VideoBase::get_is_vertical_blank() { + return cycles_into_frame_ >= FinalPixelLine * CyclesPerLine; +} + +void VideoBase::set_new_video(uint8_t new_video) { + new_video_ = new_video; +} + +uint8_t VideoBase::get_new_video() { + return new_video_; +} + +void VideoBase::clear_interrupts(uint8_t mask) { + set_interrupts(interrupts_ & ~(mask & 0x60)); +} + +void VideoBase::set_interrupt_register(uint8_t mask) { + set_interrupts(interrupts_ | (mask & 0x6)); +} + +uint8_t VideoBase::get_interrupt_register() { + return interrupts_; +} + +void VideoBase::notify_clock_tick() { + set_interrupts(interrupts_ | 0x40); +} + +void VideoBase::set_interrupts(uint8_t new_value) { + interrupts_ = new_value & 0x7f; + if((interrupts_ >> 4) & 0x6) + interrupts_ |= 0x80; +} diff --git a/Machines/Apple/AppleIIgs/Video.hpp b/Machines/Apple/AppleIIgs/Video.hpp index 204cc2cf7..a85f75085 100644 --- a/Machines/Apple/AppleIIgs/Video.hpp +++ b/Machines/Apple/AppleIIgs/Video.hpp @@ -10,6 +10,7 @@ #define Apple_IIgs_Video_hpp #include "../AppleII/VideoSwitches.hpp" +#include "../../../ClockReceiver/ClockReceiver.hpp" namespace Apple { namespace IIgs { @@ -19,9 +20,27 @@ class VideoBase: public Apple::II::VideoSwitches { public: VideoBase(); + void run_for(Cycles); + bool get_is_vertical_blank(); + + void set_new_video(uint8_t); + uint8_t get_new_video(); + + void clear_interrupts(uint8_t); + uint8_t get_interrupt_register(); + void set_interrupt_register(uint8_t); + + void notify_clock_tick(); + private: void did_set_annunciator_3(bool) override; void did_set_alternative_character_set(bool) override; + + uint8_t new_video_ = 0x01; + uint8_t interrupts_ = 0x00; + void set_interrupts(uint8_t); + + int cycles_into_frame_ = 0; }; class Video: public VideoBase {