mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-11 08:23:43 +00:00
Attempts to do just enough with video to get a functioning vertical blank query.
This commit is contained in:
parent
5126163c5d
commit
f466cbadec
@ -26,6 +26,12 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int CLOCK_RATE = 14318180;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
namespace Apple {
|
namespace Apple {
|
||||||
namespace IIgs {
|
namespace IIgs {
|
||||||
|
|
||||||
@ -39,7 +45,7 @@ class ConcreteMachine:
|
|||||||
ConcreteMachine(const Analyser::Static::AppleIIgs::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
ConcreteMachine(const Analyser::Static::AppleIIgs::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||||
m65816_(*this) {
|
m65816_(*this) {
|
||||||
|
|
||||||
set_clock_rate(14318180.0);
|
set_clock_rate(double(CLOCK_RATE));
|
||||||
|
|
||||||
using Target = Analyser::Static::AppleIIgs::Target;
|
using Target = Analyser::Static::AppleIIgs::Target;
|
||||||
std::vector<ROMMachine::ROM> rom_descriptions;
|
std::vector<ROMMachine::ROM> rom_descriptions;
|
||||||
@ -101,8 +107,6 @@ class ConcreteMachine:
|
|||||||
const auto ®ion = MemoryMapRegion(memory_, address);
|
const auto ®ion = MemoryMapRegion(memory_, address);
|
||||||
static bool log = false;
|
static bool log = false;
|
||||||
|
|
||||||
// TODO: potentially push time to clock_.
|
|
||||||
|
|
||||||
if(region.flags & MemoryMap::Region::IsIO) {
|
if(region.flags & MemoryMap::Region::IsIO) {
|
||||||
// Ensure classic auxiliary and language card accesses have effect.
|
// Ensure classic auxiliary and language card accesses have effect.
|
||||||
const bool is_read = isReadOperation(operation);
|
const bool is_read = isReadOperation(operation);
|
||||||
@ -114,11 +118,29 @@ class ConcreteMachine:
|
|||||||
// New video register.
|
// New video register.
|
||||||
case 0xc029:
|
case 0xc029:
|
||||||
if(is_read) {
|
if(is_read) {
|
||||||
*value = 0x01;
|
*value = video_.get_new_video();;
|
||||||
} else {
|
} else {
|
||||||
printf("New video: %02x\n", *value);
|
video_.set_new_video(*value);
|
||||||
// TODO: this bit should affect memory bank selection, somehow?
|
|
||||||
// Cf. Page 90.
|
// 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;
|
break;
|
||||||
|
|
||||||
@ -184,13 +206,7 @@ class ConcreteMachine:
|
|||||||
case 0xc016: AuxiliaryRead(alternative_zero_page); break;
|
case 0xc016: AuxiliaryRead(alternative_zero_page); break;
|
||||||
case 0xc017: AuxiliaryRead(slot_C3_rom); break;
|
case 0xc017: AuxiliaryRead(slot_C3_rom); break;
|
||||||
case 0xc018: VideoRead(get_80_store()); break;
|
case 0xc018: VideoRead(get_80_store()); break;
|
||||||
// case 0xc019: VideoRead(get_is_vertical_blank(cycles_since_video_update_)); break;
|
case 0xc019: VideoRead(get_is_vertical_blank()); 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 0xc01a: VideoRead(get_text()); break;
|
||||||
case 0xc01b: VideoRead(get_mixed()); break;
|
case 0xc01b: VideoRead(get_mixed()); break;
|
||||||
case 0xc01c: VideoRead(get_page2()); break;
|
case 0xc01c: VideoRead(get_page2()); break;
|
||||||
@ -472,6 +488,22 @@ class ConcreteMachine:
|
|||||||
// }
|
// }
|
||||||
fast_access_phase_ = (fast_access_phase_ + duration.as<int>()) % 5; // TODO: modulo something else, to allow for refresh.
|
fast_access_phase_ = (fast_access_phase_ + duration.as<int>()) % 5; // TODO: modulo something else, to allow for refresh.
|
||||||
slow_access_phase_ = (slow_access_phase_ + duration.as<int>()) % 14; // TODO: modulo something else, to allow for stretched cycles.
|
slow_access_phase_ = (slow_access_phase_ + duration.as<int>()) % 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;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,6 +530,7 @@ class ConcreteMachine:
|
|||||||
Apple::IIgs::ADB::GLU adb_glu_;
|
Apple::IIgs::ADB::GLU adb_glu_;
|
||||||
Apple::IIgs::Sound::GLU sound_glu_;
|
Apple::IIgs::Sound::GLU sound_glu_;
|
||||||
Zilog::SCC::z8530 scc_;
|
Zilog::SCC::z8530 scc_;
|
||||||
|
Cycles cycles_since_clock_tick_;
|
||||||
|
|
||||||
// MARK: - Cards.
|
// MARK: - Cards.
|
||||||
|
|
||||||
|
@ -10,9 +10,56 @@
|
|||||||
|
|
||||||
using namespace Apple::IIgs::Video;
|
using namespace Apple::IIgs::Video;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int CyclesPerLine = 910;
|
||||||
|
constexpr int Lines = 263;
|
||||||
|
constexpr int FinalPixelLine = 192;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
VideoBase::VideoBase() :
|
VideoBase::VideoBase() :
|
||||||
VideoSwitches<Cycles>(Cycles(2), [] (Cycles) {}) {
|
VideoSwitches<Cycles>(Cycles(2), [] (Cycles) {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBase::did_set_annunciator_3(bool) {}
|
void VideoBase::did_set_annunciator_3(bool) {}
|
||||||
void VideoBase::did_set_alternative_character_set(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<int>()) % (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;
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#define Apple_IIgs_Video_hpp
|
#define Apple_IIgs_Video_hpp
|
||||||
|
|
||||||
#include "../AppleII/VideoSwitches.hpp"
|
#include "../AppleII/VideoSwitches.hpp"
|
||||||
|
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
|
||||||
namespace Apple {
|
namespace Apple {
|
||||||
namespace IIgs {
|
namespace IIgs {
|
||||||
@ -19,9 +20,27 @@ class VideoBase: public Apple::II::VideoSwitches<Cycles> {
|
|||||||
public:
|
public:
|
||||||
VideoBase();
|
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:
|
private:
|
||||||
void did_set_annunciator_3(bool) override;
|
void did_set_annunciator_3(bool) override;
|
||||||
void did_set_alternative_character_set(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 {
|
class Video: public VideoBase {
|
||||||
|
Loading…
Reference in New Issue
Block a user