mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-26 15:32:04 +00:00
Merge pull request #1253 from TomHarte/RTC
Add some small portion of the AT real-time clock.
This commit is contained in:
commit
eb4a8bfef9
@ -15,6 +15,7 @@
|
||||
|
||||
namespace PCCompatible {
|
||||
|
||||
// TODO: border colour isn't currently honoured.
|
||||
class CGA {
|
||||
public:
|
||||
CGA() : crtc_(Motorola::CRTC::Personality::HD6845S, outputter_) {}
|
||||
@ -102,8 +103,7 @@ class CGA {
|
||||
crt(910, 8, Outputs::Display::Type::NTSC60, Outputs::Display::InputDataType::Red2Green2Blue2)
|
||||
{
|
||||
crt.set_visible_area(Outputs::Display::Rect(0.097f, 0.095f, 0.82f, 0.82f));
|
||||
// crt.set_display_type(Outputs::Display::DisplayType::CompositeColour); // TODO: needs to be a user option.
|
||||
crt.set_display_type(Outputs::Display::DisplayType::RGB); // TODO: needs to be a user option.
|
||||
crt.set_display_type(Outputs::Display::DisplayType::RGB);
|
||||
}
|
||||
|
||||
void set_mode(uint8_t control) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "Memory.hpp"
|
||||
#include "PIC.hpp"
|
||||
#include "PIT.hpp"
|
||||
#include "RTC.hpp"
|
||||
|
||||
#include "../../InstructionSets/x86/Decoder.hpp"
|
||||
#include "../../InstructionSets/x86/Flags.hpp"
|
||||
@ -549,6 +550,25 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
if(drive_count) low_switches_ |= 0xb0001;
|
||||
}
|
||||
|
||||
/// Supplies a hint about the user's display choice. If the high switches haven't been read yet and this is a CGA device,
|
||||
/// this hint will be used to select between 40- and 80-column default display.
|
||||
void hint_is_composite(bool composite) {
|
||||
if(high_switches_observed_) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(high_switches_ & 3) {
|
||||
// Do nothing if a non-CGA card is in use.
|
||||
case 0b00: case 0b11:
|
||||
break;
|
||||
|
||||
default:
|
||||
high_switches_ &= ~0b11;
|
||||
high_switches_ |= composite ? 0b01 : 0b10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void set_value(int port, uint8_t value) {
|
||||
switch(port) {
|
||||
case 1:
|
||||
@ -571,6 +591,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
uint8_t get_value(int port) {
|
||||
switch(port) {
|
||||
case 0:
|
||||
high_switches_observed_ = true;
|
||||
return enable_keyboard_ ? keyboard_.read() : uint8_t((high_switches_ << 4) | low_switches_);
|
||||
// Guesses that switches is high and low combined as below.
|
||||
|
||||
@ -580,6 +601,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
// b5: timer 2 output; [TODO]
|
||||
// b4: cassette data input; [TODO]
|
||||
// b3...b0: whichever of the high and low switches is selected.
|
||||
high_switches_observed_ |= use_high_switches_;
|
||||
return
|
||||
use_high_switches_ ? high_switches_ : low_switches_;
|
||||
}
|
||||
@ -587,6 +609,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
};
|
||||
|
||||
private:
|
||||
bool high_switches_observed_ = false;
|
||||
uint8_t high_switches_ = 0;
|
||||
uint8_t low_switches_ = 0;
|
||||
|
||||
@ -601,8 +624,8 @@ using PPI = Intel::i8255::i8255<i8255PortHandler>;
|
||||
template <VideoAdaptor video>
|
||||
class IO {
|
||||
public:
|
||||
IO(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, typename Adaptor<video>::type &card, FloppyController &fdc) :
|
||||
pit_(pit), dma_(dma), ppi_(ppi), pic_(pic), video_(card), fdc_(fdc) {}
|
||||
IO(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, typename Adaptor<video>::type &card, FloppyController &fdc, RTC &rtc) :
|
||||
pit_(pit), dma_(dma), ppi_(ppi), pic_(pic), video_(card), fdc_(fdc), rtc_(rtc) {}
|
||||
|
||||
template <typename IntT> void out(uint16_t port, IntT value) {
|
||||
static constexpr uint16_t crtc_base =
|
||||
@ -617,6 +640,9 @@ class IO {
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0070: rtc_.write<0>(uint8_t(value)); break;
|
||||
case 0x0071: rtc_.write<1>(uint8_t(value)); break;
|
||||
|
||||
// On the XT the NMI can be masked by setting bit 7 on I/O port 0xA0.
|
||||
case 0x00a0:
|
||||
printf("TODO: NMIs %s\n", (value & 0x80) ? "masked" : "unmasked");
|
||||
@ -751,6 +777,8 @@ class IO {
|
||||
case 0x006c: case 0x006d: case 0x006e: case 0x006f:
|
||||
return ppi_.read(port);
|
||||
|
||||
case 0x0071: return rtc_.read();
|
||||
|
||||
case 0x0080: return dma_.pages.page<0>();
|
||||
case 0x0081: return dma_.pages.page<1>();
|
||||
case 0x0082: return dma_.pages.page<2>();
|
||||
@ -804,6 +832,7 @@ class IO {
|
||||
PIC &pic_;
|
||||
typename Adaptor<video>::type &video_;
|
||||
FloppyController &fdc_;
|
||||
RTC &rtc_;
|
||||
};
|
||||
|
||||
class FlowController {
|
||||
@ -880,7 +909,7 @@ class ConcreteMachine:
|
||||
ppi_handler_(speaker_, keyboard_, video, DriveCount),
|
||||
pit_(pit_observer_),
|
||||
ppi_(ppi_handler_),
|
||||
context(pit_, dma_, ppi_, pic_, video_, fdc_)
|
||||
context(pit_, dma_, ppi_, pic_, video_, fdc_, rtc_)
|
||||
{
|
||||
// Set up DMA source/target.
|
||||
dma_.set_memory(&context.memory);
|
||||
@ -892,9 +921,10 @@ class ConcreteMachine:
|
||||
|
||||
// Fetch the BIOS. [8088 only, for now]
|
||||
const auto bios = ROM::Name::PCCompatibleGLaBIOS;
|
||||
const auto tick = ROM::Name::PCCompatibleGLaTICK;
|
||||
const auto font = Video::FontROM;
|
||||
|
||||
ROM::Request request = ROM::Request(bios) && ROM::Request(font);
|
||||
ROM::Request request = ROM::Request(bios) && ROM::Request(tick) && ROM::Request(font);
|
||||
auto roms = rom_fetcher(request);
|
||||
if(!request.validate(roms)) {
|
||||
throw ROMMachine::Error::MissingROMs;
|
||||
@ -903,6 +933,9 @@ class ConcreteMachine:
|
||||
const auto &bios_contents = roms.find(bios)->second;
|
||||
context.memory.install(0x10'0000 - bios_contents.size(), bios_contents.data(), bios_contents.size());
|
||||
|
||||
const auto &tick_contents = roms.find(tick)->second;
|
||||
context.memory.install(0xd'0000, tick_contents.data(), tick_contents.size());
|
||||
|
||||
// Give the video card something to read from.
|
||||
const auto &font_contents = roms.find(font)->second;
|
||||
video_.set_source(context.memory.at(Video::BaseAddress), font_contents);
|
||||
@ -1076,6 +1109,10 @@ class ConcreteMachine:
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
||||
video_.set_display_type(display_type);
|
||||
ppi_handler_.hint_is_composite(
|
||||
(display_type == Outputs::Display::DisplayType::CompositeColour) ||
|
||||
(display_type == Outputs::Display::DisplayType::CompositeMonochrome)
|
||||
);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() const override {
|
||||
@ -1095,15 +1132,16 @@ class ConcreteMachine:
|
||||
|
||||
PIT pit_;
|
||||
PPI ppi_;
|
||||
RTC rtc_;
|
||||
|
||||
PCCompatible::KeyboardMapper keyboard_mapper_;
|
||||
|
||||
struct Context {
|
||||
Context(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, typename Adaptor<video>::type &card, FloppyController &fdc) :
|
||||
Context(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, typename Adaptor<video>::type &card, FloppyController &fdc, RTC &rtc) :
|
||||
segments(registers),
|
||||
memory(registers, segments),
|
||||
flow_controller(registers, segments),
|
||||
io(pit, dma, ppi, pic, card, fdc)
|
||||
io(pit, dma, ppi, pic, card, fdc, rtc)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
96
Machines/PCCompatible/RTC.hpp
Normal file
96
Machines/PCCompatible/RTC.hpp
Normal file
@ -0,0 +1,96 @@
|
||||
//
|
||||
// RTC.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 06/12/2023.
|
||||
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef RTC_h
|
||||
#define RTC_h
|
||||
|
||||
#include <ctime>
|
||||
|
||||
namespace PCCompatible {
|
||||
|
||||
class RTC {
|
||||
public:
|
||||
template <int address>
|
||||
void write(uint8_t value) {
|
||||
switch(address) {
|
||||
default: break;
|
||||
case 0:
|
||||
selected_ = value & 0x7f;
|
||||
// NMI not yet supported.
|
||||
break;
|
||||
case 1:
|
||||
write_register(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t read() {
|
||||
std::time_t now = std::time(NULL);
|
||||
std::tm *time_date = std::localtime(&now);
|
||||
|
||||
switch(selected_) {
|
||||
default:
|
||||
return 0xff;
|
||||
|
||||
case 0x00: return bcd(time_date->tm_sec); // Seconds [0-59]
|
||||
case 0x01: return 0; // Seconds alarm
|
||||
case 0x02: return bcd(time_date->tm_min); // Minutes [0-59]
|
||||
case 0x03: return 0; // Minutes alarm
|
||||
case 0x04:
|
||||
// Hours [1-12 or 0-23]
|
||||
if(statusB_ & 2) {
|
||||
return bcd(time_date->tm_hour);
|
||||
}
|
||||
return
|
||||
// TODO: determine why GLaTICK signals an RTC fault if I enable communication of PM as below.
|
||||
// ((time_date->tm_hour >= 12) ? 0x80 : 0x00) |
|
||||
bcd(1 + (time_date->tm_hour + 11)%12);
|
||||
break;
|
||||
case 0x05: return 0; // Hours alarm
|
||||
case 0x06: return bcd(time_date->tm_wday + 1); // Day of the week [Sunday = 1]
|
||||
case 0x07: return bcd(time_date->tm_mon); // Date of the Month [1-31]
|
||||
case 0x08: return bcd(time_date->tm_mon + 1); // Month [1-12]
|
||||
case 0x09: return bcd(time_date->tm_year % 100); // Year [0-99]
|
||||
case 0x32: return bcd(19 + time_date->tm_year / 100); // Century
|
||||
|
||||
case 0x0a: return statusA_;
|
||||
case 0x0b: return statusB_;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int selected_;
|
||||
|
||||
uint8_t statusA_ = 0x00;
|
||||
uint8_t statusB_ = 0x00;
|
||||
|
||||
template <typename IntT>
|
||||
uint8_t bcd(IntT input) {
|
||||
// If calendar is in binary format, don't convert.
|
||||
if(statusB_ & 4) {
|
||||
return uint8_t(input);
|
||||
}
|
||||
|
||||
// Convert a one or two digit number to BCD.
|
||||
return uint8_t(
|
||||
(input % 10) +
|
||||
((input / 10) * 16)
|
||||
);
|
||||
}
|
||||
|
||||
void write_register(uint8_t value) {
|
||||
switch(selected_) {
|
||||
default: break;
|
||||
case 0x0a: statusA_ = value; break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* RTC_h */
|
@ -570,6 +570,9 @@ Description::Description(Name name) {
|
||||
case Name::PCCompatibleGLaBIOS:
|
||||
*this = Description(name, "PCCompatible", "8088 GLaBIOS 0.2.5", "GLABIOS_0.2.5_8T.ROM", 8 * 1024, 0x9576944cu);
|
||||
break;
|
||||
case Name::PCCompatibleGLaTICK:
|
||||
*this = Description(name, "PCCompatible", "AT GLaTICK 0.8.5", "GLaTICK_0.8.5_AT.ROM", 2 * 1024, 0x371ea3f1u);
|
||||
break;
|
||||
case Name::PCCompatiblePhoenix80286BIOS:
|
||||
*this = Description(name, "PCCompatible", "Phoenix 80286 BIOS 3.05", "Phoenix 80286 ROM BIOS Version 3.05.bin", 32 * 1024, 0x8d0d318au);
|
||||
break;
|
||||
|
@ -134,6 +134,7 @@ enum Name {
|
||||
|
||||
// PCCompatible.
|
||||
PCCompatibleGLaBIOS,
|
||||
PCCompatibleGLaTICK,
|
||||
PCCompatiblePhoenix80286BIOS,
|
||||
PCCompatibleMDAFont,
|
||||
PCCompatibleCGAFont,
|
||||
|
@ -1194,6 +1194,7 @@
|
||||
42AD55302A0C4D5000ACE410 /* 68000Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000Storage.hpp; sourceTree = "<group>"; };
|
||||
42AD55312A0C4D5000ACE410 /* 68000Implementation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000Implementation.hpp; sourceTree = "<group>"; };
|
||||
42E5C3922AC46A7700DA093D /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
|
||||
42EB81252B21788200429AF4 /* RTC.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RTC.hpp; sourceTree = "<group>"; };
|
||||
4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 65C02_extended_opcodes_test.bin; path = "Klaus Dormann/65C02_extended_opcodes_test.bin"; sourceTree = "<group>"; };
|
||||
4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MemptrTests.swift; sourceTree = "<group>"; };
|
||||
4B0333AD2094081A0050B93D /* AppleDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AppleDSK.cpp; sourceTree = "<group>"; };
|
||||
@ -2405,6 +2406,7 @@
|
||||
4267A9C82B0D4EC2008A59BB /* PIC.hpp */,
|
||||
4267A9C72B0C26FA008A59BB /* PIT.hpp */,
|
||||
423820142B1A23C200964EFE /* Registers.hpp */,
|
||||
42EB81252B21788200429AF4 /* RTC.hpp */,
|
||||
423820152B1A23E100964EFE /* Segments.hpp */,
|
||||
);
|
||||
path = PCCompatible;
|
||||
|
BIN
ROMImages/PCCompatible/GLaTICK_0.8.5_AT.ROM
Normal file
BIN
ROMImages/PCCompatible/GLaTICK_0.8.5_AT.ROM
Normal file
Binary file not shown.
@ -1,11 +1,14 @@
|
||||
Expected files:
|
||||
|
||||
GLABIOS_0.2.5_8T.ROM — the 8088 GlaBIOS ROM.
|
||||
GLABIOS_0.2.5_8T.ROM — the 8088 GLaBIOS ROM.
|
||||
GLaTICK_0.8.5_AT.ROM — the GLaBIOS AT RTC option ROM.
|
||||
Phoenix 80286 ROM BIOS Version 3.05.bin — Phoenix's 80286 AT-clone BIOS.
|
||||
EUMDA9.F14 — a dump of the MDA font.
|
||||
CGA.F08 — a dump of the CGA font.
|
||||
|
||||
|
||||
GlaBIOS is an open-source GPLv3 alternative BIOS for XT clones, available from https://glabios.org/
|
||||
GLaBIOS is an open-source GPLv3 alternative BIOS for XT clones, available from https://glabios.org/
|
||||
|
||||
GLaTICK is a real-time clock option ROM, also available from available from https://glabios.org/
|
||||
|
||||
The MDA and CGA fonts are in the form offered at https://github.com/viler-int10h/vga-text-mode-fonts i.e. it's 256 lots of 14 bytes, the first 14 being the content of character 0, the next 14 being the content of character 1, etc.
|
Loading…
x
Reference in New Issue
Block a user