3b2: Improve TOD calculation
This commit is contained in:
parent
7df0468b75
commit
22b8e211c6
4 changed files with 44 additions and 198 deletions
|
@ -293,13 +293,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 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 */
|
||||
|
||||
|
||||
/* TIMING SECTION */
|
||||
/* ----------------------------------------------- */
|
||||
|
|
195
3B2/3b2_sysdev.c
195
3B2/3b2_sysdev.c
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue