1
0
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:
Thomas Harte 2023-12-07 10:52:45 -05:00 committed by GitHub
commit eb4a8bfef9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 153 additions and 10 deletions

View File

@ -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) {

View File

@ -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();
}

View 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 */

View File

@ -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;

View File

@ -134,6 +134,7 @@ enum Name {
// PCCompatible.
PCCompatibleGLaBIOS,
PCCompatibleGLaTICK,
PCCompatiblePhoenix80286BIOS,
PCCompatibleMDAFont,
PCCompatibleCGAFont,

View File

@ -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;

Binary file not shown.

View File

@ -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.