3b2: Improve TOD calculation

This commit is contained in:
Seth Morabito 2017-11-29 21:44:40 +00:00
parent 7df0468b75
commit 22b8e211c6
4 changed files with 44 additions and 198 deletions

View file

@ -292,13 +292,10 @@ noret __libc_longjmp (jmp_buf buf, int val);
/* Timer definitions */
#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */
#define TMR_TOD 1 /* The Time-of-Day clock */
#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */
#define TPS_CLK 100 /* 100 ticks per second */
#define CLK_MIN_TICKS 500 /* No fewer than 500 sim steps between ticks */
#define TPS_CLK 100 /* 100 ticks per second */
#define TPS_TOD 10 /* 10 ticks per second */
#define CLK_MIN_TICKS 500 /* No fewer than 500 sim steps between ticks */
/* TIMING SECTION */

View file

@ -671,20 +671,17 @@ void timer_write(uint32 pa, uint32 val, size_t size)
}
/*
* MM58174A Real-Time-Clock
* MM58174A Time Of Day Clock
*
* Despite its name, this device is not used by the 3B2 as a clock. It
* is only used to store the current date and time between boots. It
* is set when an operator changes the date and time. Is is read at
* boot time. Therefore, we do not need to treat it as a clock or
* timer device here.
*/
/* The year is NOT stored in the TOD clock. However, it does store an
* integer indicating which year in a 4-year Leap-Year cycle the
* current year is. To simplify converting from seconds into a TOD
* structure, we will map this into the range 1985...1988, for
* internal purposes only.
*/
int tod_years[4] = {85, 86, 87, 88};
UNIT tod_unit = {
UDATA(&tod_svc, UNIT_IDLE+UNIT_FIX+UNIT_BINK, sizeof(TOD_DATA))
UDATA(NULL, UNIT_FIX+UNIT_BINK, sizeof(TOD_DATA))
};
DEVICE tod_dev = {
@ -692,18 +689,13 @@ DEVICE tod_dev = {
1, 16, 8, 4, 16, 32,
NULL, NULL, &tod_reset,
NULL, &tod_attach, &tod_detach,
NULL, DEV_DEBUG, 0, sys_deb_tab, NULL, NULL,
NULL, 0, 0, sys_deb_tab, NULL, NULL,
&tod_help, NULL, NULL,
&tod_description
};
t_stat tod_reset(DEVICE *dptr)
{
int32 t;
t = sim_rtcn_init_unit(&tod_unit, TPS_TOD, TMR_TOD);
sim_activate_after(&tod_unit, 1000000 / TPS_TOD);
if (tod_unit.filebuf == NULL) {
tod_unit.filebuf = calloc(sizeof(TOD_DATA), 1);
if (tod_unit.filebuf == NULL) {
@ -717,7 +709,6 @@ t_stat tod_reset(DEVICE *dptr)
t_stat tod_attach(UNIT *uptr, CONST char *cptr)
{
t_stat r;
TOD_DATA *td = (TOD_DATA *)uptr->filebuf;
uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE);
@ -727,12 +718,6 @@ t_stat tod_attach(UNIT *uptr, CONST char *cptr)
uptr->flags = uptr->flags & (uint32) ~(UNIT_ATTABLE | UNIT_BUFABLE);
} else {
uptr->hwmark = (uint32) uptr->capac;
if (!td->ssec) {
tod_init_time();
} else {
tod_sync();
}
tod_update_tdata();
}
return r;
@ -741,10 +726,6 @@ t_stat tod_attach(UNIT *uptr, CONST char *cptr)
t_stat tod_detach(UNIT *uptr)
{
t_stat r;
TOD_DATA *td = (TOD_DATA *)uptr->filebuf;
/* We're about to detach, so update the current tod clock */
tod_update_tdata();
r = detach_unit(uptr);
@ -756,33 +737,17 @@ t_stat tod_detach(UNIT *uptr)
}
/*
* If no time is set in the system clock, grab it from the
* real wall clock.
* Re-set the tod_data registers based on the current simulated time.
*/
void tod_init_time()
void tod_resync()
{
TOD_DATA *td = (TOD_DATA *)tod_unit.filebuf;
int result;
struct timespec now;
sim_rtcn_get_time(&now, TMR_TOD);
td->rsec = (time_t)now.tv_sec;
td->ssec = (t_uint64)now.tv_sec * 10;
}
/*
* Re-set the tod_data registers based on the current value of ssec
*/
void tod_update_tdata()
{
int result;
struct timespec now;
struct tm tm;
time_t sec;
TOD_DATA *td = (TOD_DATA *)tod_unit.filebuf;
time_t sec = td->ssec / 10;
sim_debug(EXECUTE_MSG, &tod_dev,
">>> Epoch time from tod_update_tdata() = %lu\n",
sec);
sim_rtcn_get_time(&now, TMR_CLK);
sec = now.tv_sec - td->delta;
/* Populate the tm struct based on current sim_time */
localtime_r(&sec, &tm);
@ -800,38 +765,20 @@ void tod_update_tdata()
td->unit_day = tm.tm_mday % 10;
td->ten_day = tm.tm_mday / 10;
td->year = 1 << ((tm.tm_year - 1) % 4);
sim_rtcn_get_time(&now, TMR_TOD);
sim_debug(EXECUTE_MSG, &tod_dev,
">>> Updated rsec to %lu\n",
now.tv_sec);
td->rsec = now.tv_sec;
}
void tod_sync()
{
int result;
struct timespec now;
TOD_DATA *td = (TOD_DATA *)tod_unit.filebuf;
sim_rtcn_get_time(&now, TMR_TOD);
sim_debug(EXECUTE_MSG, &tod_dev,
">>> Syncing clock. Clock gained %lu seconds\n",
now.tv_sec - td->rsec);
td->ssec += ((time_t)now.tv_sec - td->rsec) * (t_uint64)10;
}
/*
* Re-set the value of ssec based on the current values of the
* tod_data registers.
* Re-calculate the delta between real time and simulated time
*/
void tod_update_ssec()
void tod_update_delta()
{
struct timespec now;
struct tm tm = {0};
time_t simtime = 0;
time_t ssec;
TOD_DATA *td = (TOD_DATA *)tod_unit.filebuf;
sim_rtcn_get_time(&now, TMR_CLK);
/* We've been asked to re-set the TOD register values based on the
* current 1/10th seconds since the epoch (ssec) */
/* Compute the simulated seconds value */
tm.tm_sec = (td->ten_sec * 10) + td->unit_sec;
tm.tm_min = (td->ten_min * 10) + td->unit_min;
tm.tm_hour = (td->ten_hour * 10) + td->unit_hour;
@ -848,26 +795,9 @@ void tod_update_ssec()
case 8: /* Leap Year */
tm.tm_year = 88;
}
tm.tm_isdst = 0;
simtime = mktime(&tm);
sim_debug(EXECUTE_MSG, &tod_dev,
">>> Epoch time from tod_update_ssec() = %lu\n",
simtime);
td->ssec = (t_uint64)simtime * 10;
}
t_stat tod_svc(UNIT *uptr)
{
int32 t;
TOD_DATA *td = (TOD_DATA *)(uptr->filebuf);
td->ssec++;
t = sim_rtcn_calb(TPS_TOD, TMR_TOD);
sim_activate_after(&tod_unit, (uint32) (1000000 / TPS_TOD));
return SCPE_OK;
ssec = mktime(&tm);
td->delta = now.tv_sec - ssec;
}
uint32 tod_read(uint32 pa, size_t size)
@ -875,70 +805,36 @@ uint32 tod_read(uint32 pa, size_t size)
uint8 reg;
TOD_DATA *td = (TOD_DATA *)(tod_unit.filebuf);
tod_resync();
reg = pa - TODBASE;
switch(reg) {
case 0x04: /* 1/10 Sec */
return td->tsec;
case 0x08: /* 1 Sec */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->unit_sec=%d\n",
td->unit_sec);
return td->unit_sec;
case 0x0c: /* 10 Sec */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->ten_sec=%d\n",
td->ten_sec);
return td->ten_sec;
case 0x10: /* 1 Min */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->unit_min=%d\n",
td->unit_min);
return td->unit_min;
case 0x14: /* 10 Min */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->ten_min=%d\n",
td->ten_min);
return td->ten_min;
case 0x18: /* 1 Hour */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->unit_hour=%d\n",
td->unit_hour);
return td->unit_hour;
case 0x1c: /* 10 Hour */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->ten_hour=%d\n",
td->ten_hour);
return td->ten_hour;
case 0x20: /* 1 Day */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->unit_day=%d\n",
td->unit_day);
return td->unit_day;
case 0x24: /* 10 Day */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->ten_day=%d\n",
td->ten_day);
return td->ten_day;
case 0x28: /* Day of Week */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->wday=%d\n",
td->wday);
return td->wday;
case 0x2c: /* 1 Month */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->unit_mon=%d\n",
td->unit_mon);
return td->unit_mon;
case 0x30: /* 10 Month */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->ten_mon=%d\n",
td->ten_mon);
return td->ten_mon;
case 0x34: /* Year */
sim_debug(READ_MSG, &tod_dev,
"READ TOD: td->year=%d\n",
td->year);
return td->year;
default:
break;
@ -960,83 +856,42 @@ void tod_write(uint32 pa, uint32 val, size_t size)
break;
case 0x08: /* 1 Sec */
td->unit_sec = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->unit_sec=%d\n",
td->unit_sec);
break;
case 0x0c: /* 10 Sec */
td->ten_sec = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->ten_sec=%d\n",
td->ten_sec);
break;
case 0x10: /* 1 Min */
td->unit_min = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->unit_min=%d\n",
td->unit_min);
break;
case 0x14: /* 10 Min */
td->ten_min = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->ten_min=%d\n",
td->ten_min);
break;
case 0x18: /* 1 Hour */
td->unit_hour = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->unit_hour=%d\n",
td->unit_hour);
break;
case 0x1c: /* 10 Hour */
td->ten_hour = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->ten_hour=%d\n",
td->ten_hour);
break;
case 0x20: /* 1 Day */
td->unit_day = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->unit_day=%d\n",
td->unit_day);
break;
case 0x24: /* 10 Day */
td->ten_day = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->ten_day=%d\n",
td->ten_day);
break;
case 0x28: /* Day of Week */
td->wday = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->wday=%d\n",
td->wday);
break;
case 0x2c: /* 1 Month */
td->unit_mon = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->unit_mon=%d\n",
td->unit_mon);
break;
case 0x30: /* 10 Month */
td->ten_mon = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->ten_mon=%d\n",
td->ten_mon);
break;
case 0x34: /* Year */
td->year = (uint8) val;
sim_debug(WRITE_MSG, &tod_dev,
"WRITE TOD: td->year=%d\n",
td->year);
case 0x38:
if (val & 1) {
/* Start the clock */
sim_debug(WRITE_MSG, &tod_dev, "Start the clock and resync TOD registers.\n");
tod_update_ssec();
} else {
/* Stop the clock */
sim_debug(WRITE_MSG, &tod_dev, "Stop the clock\n");
tod_update_delta();
}
break;
default:

View file

@ -80,14 +80,8 @@ void csr_write(uint32 pa, uint32 val, size_t size);
/* TOD */
typedef enum tod_action {
TOD_SAVE,
TOD_RESTORE
} TOD_ACTION;
typedef struct tod_data {
time_t rsec; /* Real clock time from the host */
t_uint64 ssec; /* Simulated time in 1/10th second since the epoch */
int32 delta; /* Delta between simulated time and real time (sec.) */
uint8 tsec; /* 1/10 seconds */
uint8 unit_sec; /* 1's column seconds */
uint8 ten_sec; /* 10's column seconds */
@ -104,11 +98,8 @@ typedef struct tod_data {
uint8 pad[3]; /* Padding to 32 bytes */
} TOD_DATA;
void tod_init_time();
void tod_update_tdata();
void tod_update_ssec();
void tod_sync();
t_stat tod_svc(UNIT *uptr);
void tod_resync();
void tod_update_delta();
t_stat tod_reset(DEVICE *dptr);
t_stat tod_attach(UNIT *uptr, CONST char *cptr);
t_stat tod_detach(UNIT *uptr);

View file

@ -3,9 +3,6 @@ AT&T 3B2 Simulator
This module contains a simulator for the AT&T 3B2 Model 400 microcomputer.
*CAUTION*: The simulator is under active and heavy development. It is
usable today, but please consider this emulator to be a beta.
Devices
-------
@ -20,6 +17,7 @@ devices are given in parentheses:
- SCN2681A Integrated DUART (IU)
- TMS2793 Integrated Floppy Controller (IF)
- uPD7261A Integrated MFM Fixed Disk Controller (ID)
- MM58174A Time Of Day Clock (TOD)
Usage
-----
@ -39,11 +37,14 @@ You will be greeted with the message:
SYSTEM FAILURE: CONSULT YOUR SYSTEM ADMINISTRATION UTILITIES GUIDE
NVRAM can be saved between boots by attaching it to a file.
NVRAM and Time of Day can be saved between boots by attaching both
devices to files.
sim> ATTACH NVRAM <file>
sim> ATTACH NVRAM <nvram-file>
sim> ATTACH TOD <tod-file>
On subsequent boots, you will instead see the message
If you have no operating system installed on the hard drive, on
subsequent boots you will instead see the message
SELF-CHECK
@ -68,10 +69,11 @@ of available firmware programs.
Booting UNIX SVR3
-----------------
UNIX SVR3 for the 3B2 partially boots. To boot UNIX, attach the first
disk image from the 3B2 "Essential Utilities" distribution.
UNIX SVR3 is the only operating system available for the 3B2. To boot
UNIX, attach the first disk image from the 3B2 "Essential Utilities"
distribution.
sim> ATTACH IF <disk-image>
sim> ATTACH IF <floppy-image>
sim> BOOT CPU
Once you reach the `SYSTEM FAILURE` message, type `mcp` to enter
@ -92,8 +94,9 @@ Installing SVR3
---------------
To install SVR3 to the first hard disk, first, attach a new image
to the ID0 device:
sim> ATTACH ID0 <disk-image>
sim> ATTACH ID0 <hd-image>
Then, boot the file `idtools` from the "3B2 Maintenance Utilities -
Issue 4.0" floppy diskette.