Skip to content

Commit c1db0aa

Browse files
Improve TOY clock
- Macros were added for the various bits in registers. - IRQF and UI (interrupt request flag and update-ended interrupt) were implemented. - Setting the access port with the read value was moved from toy_handle_periodic_interrupt to toy_write.
1 parent 3fddaf8 commit c1db0aa

File tree

2 files changed

+72
-44
lines changed

2 files changed

+72
-44
lines changed

src/AliM1543C.cpp

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -670,15 +670,14 @@ void CAliM1543C::reg_61_write(u8 data) {
670670
* Read time-of-year clock ports (70h-73h).
671671
**/
672672
u8 CAliM1543C::toy_read(u32 address) {
673-
674673
// printf("%%ALI-I-READTOY: read port %02x: 0x%02x\n", (u32)(0x70 + address),
675674
// state.toy_access_ports[address]);
676675
return (u8)state.toy_access_ports[address];
677676
}
678677

679678
/**
680679
* Write time-of-year clock ports (70h-73h). On a write to port 0, recalculate
681-
*clock values.
680+
* clock values.
682681
**/
683682
void CAliM1543C::toy_write(u32 address, u8 data) {
684683
time_t ltime;
@@ -693,17 +692,17 @@ void CAliM1543C::toy_write(u32 address, u8 data) {
693692
switch (address) {
694693
case 0:
695694
if ((data & 0x7f) < 14) {
696-
state.toy_stored_data[0x0d] = 0x80; // data is geldig!
695+
// Assign VRT (valid RAM and time) bit
696+
state.toy_stored_data[RTC_REG_D] = RTC_VRT;
697697

698-
// update clock.......
698+
// Update time
699699
time(&ltime);
700700
gmtime_s(&stime, &ltime);
701-
if (state.toy_stored_data[0x0b] & 4) {
702-
701+
if (state.toy_stored_data[RTC_REG_B] & RTC_DM) {
703702
// binary
704703
state.toy_stored_data[0] = (u8)(stime.tm_sec);
705704
state.toy_stored_data[2] = (u8)(stime.tm_min);
706-
if (state.toy_stored_data[0x0b] & 2) // 24-hour
705+
if (state.toy_stored_data[RTC_REG_B] & RTC_2412) // 24-hour
707706
state.toy_stored_data[4] = (u8)(stime.tm_hour);
708707
else
709708
// 12-hour
@@ -714,7 +713,6 @@ void CAliM1543C::toy_write(u32 address, u8 data) {
714713
state.toy_stored_data[8] = (u8)(stime.tm_mon + 1);
715714
state.toy_stored_data[9] = (u8)(stime.tm_year % 100);
716715
} else {
717-
718716
// BCD
719717
state.toy_stored_data[0] =
720718
(u8)(((stime.tm_sec / 10) << 4) | (stime.tm_sec % 10));
@@ -738,38 +736,21 @@ void CAliM1543C::toy_write(u32 address, u8 data) {
738736
((stime.tm_year % 100) % 10));
739737
}
740738

741-
// Debian Linux wants something out of 0x0a. It gets initialized
742-
// with 0x26, by the SRM
743-
// Ah, here's something from the linux kernel:
744-
//# /********************************************************
745-
//# * register details
746-
//# ********************************************************/
747-
//# #define RTC_FREQ_SELECT RTC_REG_A
748-
//#
749-
//# /* update-in-progress - set to "1" 244 microsecs before RTC goes off
750-
// the bus, # * reset after update (may take 1.984ms @ 32768Hz RefClock)
751-
// is complete, # * totalling to a max high interval of 2.228 ms. # */ # #
752-
// define RTC_UIP 0x80 # # define RTC_DIV_CTL 0x70 # /* divider control:
753-
// refclock values 4.194 / 1.049 MHz / 32.768 kHz */ # # define
754-
// RTC_REF_CLCK_4MHZ 0x00 # # define RTC_REF_CLCK_1MHZ 0x10 # # define
755-
// RTC_REF_CLCK_32KHZ 0x20 # /* 2 values for divider stage reset, others
756-
// for "testing purposes only" */ # # define RTC_DIV_RESET1 0x60 # #
757-
// define RTC_DIV_RESET2 0x70 # /* Periodic intr. / Square wave rate
758-
// select. 0=none, 1=32.8kHz,... 15=2Hz */ # # define RTC_RATE_SELECT 0x0F
759-
//#
760-
// The SRM-init value of 0x26 means:
761-
// xtal speed 32.768KHz (standard)
739+
// SRM initializes the value of A register to 0x26. This means:
740+
// xtal speed is set to MC_BASE_32_KHz 32.768KHz (standard)
762741
// periodic interrupt rate divisor of 32 = interrupt every 976.562 ms
763742
// (1024Hz clock)
764-
if (state.toy_stored_data[0x0a] & 0x80) {
743+
if (state.toy_stored_data[RTC_REG_A] & RTC_UIP) {
765744
// Once the UIP line goes high, we have to stay high for 2228us.
766745
hold_count--;
767-
if (hold_count == 0) {
768-
state.toy_stored_data[0x0a] &= ~0x80;
746+
if (hold_count == 0 || (state.toy_stored_data[RTC_REG_B] & RTC_SET)) {
747+
// Set UIP low and trigger the related interrupt.
748+
state.toy_stored_data[RTC_REG_A] &= ~RTC_UIP;
749+
state.toy_stored_data[RTC_REG_C] |= RTC_UF;
750+
toy_update_irqf();
769751
read_count = 0;
770752
}
771753
} else {
772-
773754
// UIP isn't high, so if we're looping and waiting for it to go, it
774755
// will take 1,000,000/(IPus*3) reads for a 3 instruction loop.
775756
// If it happens to be a one time read, it'll only throw our
@@ -778,20 +759,31 @@ void CAliM1543C::toy_write(u32 address, u8 data) {
778759
read_count++;
779760
if (read_count > 1000000 / (IPus * 3)) // 3541 @ 847IPus
780761
{
781-
state.toy_stored_data[0x0a] |= 0x80;
762+
state.toy_stored_data[RTC_REG_A] |= RTC_UIP;
782763
hold_count =
783764
(2228 / (IPus * 3)) + 1; // .876 @ 847IPus, so we add one.
784765
}
785766
}
786767
}
787768

788769
toy_handle_periodic_interrupt(data);
770+
toy_update_irqf();
771+
772+
// Assign specified data to port so it can be read by the program
773+
state.toy_access_ports[1] = state.toy_stored_data[data & 0x7f];
774+
775+
// Register C is cleared after a read, and we don't care if it's a write
776+
if (data == RTC_REG_C)
777+
state.toy_stored_data[data & 0x7f] = 0;
778+
789779
break;
790780
case 1:
791-
if (state.toy_access_ports[0] == 0x0b &&
781+
if (state.toy_access_ports[0] == RTC_REG_B &&
792782
data & 0x040) // If we're writing to register B, we make register C look
793783
// like it fired.
794-
state.toy_stored_data[0x0c] = 0xf0;
784+
// TODO: Do actual interrupt implementation instead of
785+
// a workaround.
786+
state.toy_stored_data[RTC_REG_C] = 0xf0;
795787
state.toy_stored_data[state.toy_access_ports[0] & 0x7f] = (u8)data;
796788
break;
797789

@@ -818,10 +810,10 @@ void CAliM1543C::toy_handle_periodic_interrupt(u8 data) {
818810

819811
// For the meaning of the period calculation see the table on page 14 of the
820812
// aforementioned datasheet
821-
int rate_pow = state.toy_stored_data[0x0a] & 0x0f;
813+
int rate_pow = state.toy_stored_data[RTC_REG_A] & 0x0f;
822814
double period = (1 << rate_pow) / 65536.0;
823815

824-
if (state.toy_stored_data[0x0a] & MC_BASE_32_KHz) {
816+
if (state.toy_stored_data[RTC_REG_A] & MC_BASE_32_KHz) {
825817
if (rate_pow == 0x1) {
826818
period = 1 / 256.0;
827819
} else if (rate_pow == 0x2) {
@@ -833,15 +825,24 @@ void CAliM1543C::toy_handle_periodic_interrupt(u8 data) {
833825
// Elapsed time since last check is equal or greater than the specified
834826
// period - fire the interrupt by setting the PF flag in register C
835827
// (see page 16 in the datasheet).
836-
state.toy_stored_data[0x0c] |= RTC_PF;
828+
state.toy_stored_data[RTC_REG_C] |= RTC_PF;
837829
state.toy_pi_last_fire = now;
838830
}
831+
}
839832

840-
state.toy_access_ports[1] = state.toy_stored_data[data & 0x7f];
841-
842-
// register C is cleared after a read, and we don't care if it's a write
843-
if (data == 0x0c)
844-
state.toy_stored_data[data & 0x7f] = 0;
833+
/**
834+
* Update RTC interrupt request flag
835+
**/
836+
void CAliM1543C::toy_update_irqf() {
837+
if ((state.toy_stored_data[RTC_REG_B] & RTC_PIE &&
838+
state.toy_stored_data[RTC_REG_C] & RTC_PF) ||
839+
(state.toy_stored_data[RTC_REG_B] & RTC_UIE &&
840+
state.toy_stored_data[RTC_REG_C] & RTC_UF) ||
841+
(state.toy_stored_data[RTC_REG_B] & RTC_AIE &&
842+
state.toy_stored_data[RTC_REG_C] & RTC_AF))
843+
state.toy_stored_data[RTC_REG_C] |= RTC_IRQF;
844+
else
845+
state.toy_stored_data[RTC_REG_C] &= ~RTC_IRQF;
845846
}
846847

847848
/**

src/AliM1543C.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,32 @@
143143

144144
#define PIT_OFFSET_MAX 6
145145

146+
// RTC register A (MC_BASE_32_KHz is a divider bits configuration)
147+
#define RTC_REG_A 0x0a
148+
#define RTC_UIP 0x80
146149
#define MC_BASE_32_KHz 0x20
150+
151+
// RTC register B (here most options reside; 0x08 is the unused square wave
152+
// enable pin)
153+
#define RTC_REG_B 0x0b
154+
#define RTC_SET 0x80
155+
#define RTC_PIE 0x40
156+
#define RTC_AIE 0x20
157+
#define RTC_UIE 0x10
158+
#define RTC_DM 0x04
159+
#define RTC_2412 0x02
160+
#define RTC_DSE 0x01
161+
162+
// RTC register C (rest of register is always zero)
163+
#define RTC_REG_C 0x0c
164+
#define RTC_IRQF 0x80
147165
#define RTC_PF 0x40
166+
#define RTC_AF 0x20
167+
#define RTC_UF 0x10
168+
169+
// RTC register D (rest of register is always zero)
170+
#define RTC_REG_D 0x0d
171+
#define RTC_VRT 0x80
148172

149173
/**
150174
* \brief Emulated ISA part of the ALi M1543C chipset.
@@ -155,6 +179,8 @@
155179
* Documentation consulted:
156180
* - Ali M1543C B1 South Bridge Version 1.20
157181
*(http://mds.gotdns.com/sensors/docs/ali/1543dScb1-120.pdf)
182+
* - MC146818 RTC
183+
*(https://www.nxp.com/docs/en/data-sheet/MC146818.pdf)
158184
* - Keyboard Scancodes, by Andries Brouwer
159185
*(http://www.win.tue.nl/~aeb/linux/kbd/scancodes.html)
160186
* .
@@ -193,6 +219,7 @@ class CAliM1543C : public CPCIDevice, public CRunnable {
193219
u8 toy_read(u32 address);
194220
void toy_write(u32 address, u8 data);
195221
void toy_handle_periodic_interrupt(u8 data);
222+
void toy_update_irqf();
196223

197224
// Timer/Counter
198225
u8 pit_read(u32 address);

0 commit comments

Comments
 (0)