| 1 | /* pdp10_tim.c: PDP-10 tim subsystem simulator\r |
| 2 | \r |
| 3 | Copyright (c) 1993-2007, Robert M Supnik\r |
| 4 | \r |
| 5 | Permission is hereby granted, free of charge, to any person obtaining a\r |
| 6 | copy of this software and associated documentation files (the "Software"),\r |
| 7 | to deal in the Software without restriction, including without limitation\r |
| 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r |
| 9 | and/or sell copies of the Software, and to permit persons to whom the\r |
| 10 | Software is furnished to do so, subject to the following conditions:\r |
| 11 | \r |
| 12 | The above copyright notice and this permission notice shall be included in\r |
| 13 | all copies or substantial portions of the Software.\r |
| 14 | \r |
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r |
| 18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r |
| 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r |
| 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r |
| 21 | \r |
| 22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r |
| 23 | used in advertising or otherwise to promote the sale, use or other dealings\r |
| 24 | in this Software without prior written authorization from Robert M Supnik.\r |
| 25 | \r |
| 26 | tim timer subsystem\r |
| 27 | \r |
| 28 | 18-Jun-07 RMS Added UNIT_IDLE flag\r |
| 29 | 03-Nov-06 RMS Rewritten to support idling\r |
| 30 | 29-Oct-06 RMS Added clock coscheduling function\r |
| 31 | 02-Feb-04 RMS Exported variables needed by Ethernet simulator\r |
| 32 | 29-Jan-02 RMS New data structures\r |
| 33 | 06-Jan-02 RMS Added enable/disable support\r |
| 34 | 02-Dec-01 RMS Fixed bug in ITS PC sampling (found by Dave Conroy)\r |
| 35 | 31-Aug-01 RMS Changed int64 to t_int64 for Windoze\r |
| 36 | 17-Jul-01 RMS Moved function prototype\r |
| 37 | 04-Jul-01 RMS Added DZ11 support\r |
| 38 | */\r |
| 39 | \r |
| 40 | #include "pdp10_defs.h"\r |
| 41 | #include <time.h>\r |
| 42 | \r |
| 43 | /* Invariants */\r |
| 44 | \r |
| 45 | #define TIM_HW_FREQ 4100000 /* 4.1Mhz */\r |
| 46 | #define TIM_HWRE_MASK 07777\r |
| 47 | #define UNIT_V_Y2K (UNIT_V_UF + 0) /* Y2K compliant OS */\r |
| 48 | #define UNIT_Y2K (1u << UNIT_V_Y2K)\r |
| 49 | \r |
| 50 | /* Clock mode TOPS-10/ITS */\r |
| 51 | \r |
| 52 | #define TIM_TPS_T10 60\r |
| 53 | #define TIM_WAIT_T10 8000\r |
| 54 | #define TIM_MULT_T10 1\r |
| 55 | #define TIM_ITS_QUANT (TIM_HW_FREQ / TIM_TPS_T10)\r |
| 56 | \r |
| 57 | /* Clock mode TOPS-20/KLAD */\r |
| 58 | \r |
| 59 | #define TIM_TPS_T20 1001\r |
| 60 | #define TIM_WAIT_T20 500\r |
| 61 | #define TIM_MULT_T20 16\r |
| 62 | \r |
| 63 | /* Probability function for TOPS-20 idlelock */\r |
| 64 | \r |
| 65 | #define PROB(x) (((rand() * 100) / RAND_MAX) >= (x))\r |
| 66 | \r |
| 67 | d10 tim_base[2] = { 0, 0 }; /* 71b timebase */\r |
| 68 | d10 tim_ttg = 0; /* time to go */\r |
| 69 | d10 tim_period = 0; /* period */\r |
| 70 | d10 quant = 0; /* ITS quantum */\r |
| 71 | int32 tim_mult = TIM_MULT_T10; /* tmxr poll mult */\r |
| 72 | int32 tim_t20_prob = 33; /* TOPS-20 prob */\r |
| 73 | \r |
| 74 | /* Exported variables */\r |
| 75 | \r |
| 76 | int32 clk_tps = TIM_TPS_T10; /* clock ticks/sec */\r |
| 77 | int32 tmr_poll = TIM_WAIT_T10; /* clock poll */\r |
| 78 | int32 tmxr_poll = TIM_WAIT_T10 * TIM_MULT_T10; /* term mux poll */\r |
| 79 | \r |
| 80 | extern int32 apr_flg, pi_act;\r |
| 81 | extern UNIT cpu_unit;\r |
| 82 | extern d10 pcst;\r |
| 83 | extern a10 pager_PC;\r |
| 84 | extern int32 t20_idlelock;\r |
| 85 | \r |
| 86 | DEVICE tim_dev;\r |
| 87 | t_stat tcu_rd (int32 *data, int32 PA, int32 access);\r |
| 88 | t_stat tim_svc (UNIT *uptr);\r |
| 89 | t_stat tim_reset (DEVICE *dptr);\r |
| 90 | void tim_incr_base (d10 *base, d10 incr);\r |
| 91 | \r |
| 92 | extern d10 Read (a10 ea, int32 prv);\r |
| 93 | extern d10 ReadM (a10 ea, int32 prv);\r |
| 94 | extern void Write (a10 ea, d10 val, int32 prv);\r |
| 95 | extern void WriteP (a10 ea, d10 val);\r |
| 96 | extern int32 pi_eval (void);\r |
| 97 | extern t_stat wr_nop (int32 data, int32 PA, int32 access);\r |
| 98 | \r |
| 99 | /* TIM data structures\r |
| 100 | \r |
| 101 | tim_dev TIM device descriptor\r |
| 102 | tim_unit TIM unit descriptor\r |
| 103 | tim_reg TIM register list\r |
| 104 | */\r |
| 105 | \r |
| 106 | DIB tcu_dib = { IOBA_TCU, IOLN_TCU, &tcu_rd, &wr_nop, 0 };\r |
| 107 | \r |
| 108 | UNIT tim_unit = { UDATA (&tim_svc, UNIT_IDLE, 0), TIM_WAIT_T10 };\r |
| 109 | \r |
| 110 | REG tim_reg[] = {\r |
| 111 | { BRDATA (TIMEBASE, tim_base, 8, 36, 2) },\r |
| 112 | { ORDATA (TTG, tim_ttg, 36) },\r |
| 113 | { ORDATA (PERIOD, tim_period, 36) },\r |
| 114 | { ORDATA (QUANT, quant, 36) },\r |
| 115 | { DRDATA (TIME, tim_unit.wait, 24), REG_NZ + PV_LEFT },\r |
| 116 | { DRDATA (PROB, tim_t20_prob, 6), REG_NZ + PV_LEFT + REG_HIDDEN },\r |
| 117 | { DRDATA (POLL, tmr_poll, 32), REG_HRO + PV_LEFT },\r |
| 118 | { DRDATA (MUXPOLL, tmxr_poll, 32), REG_HRO + PV_LEFT },\r |
| 119 | { DRDATA (MULT, tim_mult, 6), REG_HRO + PV_LEFT },\r |
| 120 | { DRDATA (TPS, clk_tps, 12), REG_HRO + PV_LEFT },\r |
| 121 | { NULL }\r |
| 122 | };\r |
| 123 | \r |
| 124 | MTAB tim_mod[] = {\r |
| 125 | { UNIT_Y2K, 0, "non Y2K OS", "NOY2K", NULL },\r |
| 126 | { UNIT_Y2K, UNIT_Y2K, "Y2K OS", "Y2K", NULL },\r |
| 127 | { MTAB_XTD|MTAB_VDV, 000, "ADDRESS", NULL,\r |
| 128 | NULL, &show_addr, NULL },\r |
| 129 | { 0 }\r |
| 130 | };\r |
| 131 | \r |
| 132 | DEVICE tim_dev = {\r |
| 133 | "TIM", &tim_unit, tim_reg, tim_mod,\r |
| 134 | 1, 0, 0, 0, 0, 0,\r |
| 135 | NULL, NULL, &tim_reset,\r |
| 136 | NULL, NULL, NULL,\r |
| 137 | &tcu_dib, DEV_UBUS\r |
| 138 | };\r |
| 139 | \r |
| 140 | /* Timer instructions */\r |
| 141 | \r |
| 142 | /* Timer - if the timer is running at less than hardware frequency,\r |
| 143 | need to interpolate the value by calculating how much of the current\r |
| 144 | clock tick has elapsed, and what that equates to in msec. */\r |
| 145 | \r |
| 146 | t_bool rdtim (a10 ea, int32 prv)\r |
| 147 | {\r |
| 148 | d10 tempbase[2];\r |
| 149 | \r |
| 150 | ReadM (INCA (ea), prv); /* check 2nd word */\r |
| 151 | tempbase[0] = tim_base[0]; /* copy time base */\r |
| 152 | tempbase[1] = tim_base[1];\r |
| 153 | if (tim_mult != TIM_MULT_T20) { /* interpolate? */\r |
| 154 | int32 used;\r |
| 155 | d10 incr;\r |
| 156 | used = tmr_poll - (sim_is_active (&tim_unit) - 1);\r |
| 157 | incr = (d10) (((double) used * TIM_HW_FREQ) /\r |
| 158 | ((double) tmr_poll * (double) clk_tps));\r |
| 159 | tim_incr_base (tempbase, incr);\r |
| 160 | }\r |
| 161 | tempbase[0] = tempbase[0] & ~((d10) TIM_HWRE_MASK); /* clear low 12b */\r |
| 162 | Write (ea, tempbase[0], prv);\r |
| 163 | Write (INCA(ea), tempbase[1], prv);\r |
| 164 | return FALSE;\r |
| 165 | }\r |
| 166 | \r |
| 167 | t_bool wrtim (a10 ea, int32 prv)\r |
| 168 | {\r |
| 169 | tim_base[0] = Read (ea, prv);\r |
| 170 | tim_base[1] = CLRS (Read (INCA (ea), prv));\r |
| 171 | return FALSE;\r |
| 172 | }\r |
| 173 | \r |
| 174 | t_bool rdint (a10 ea, int32 prv)\r |
| 175 | {\r |
| 176 | Write (ea, tim_period, prv);\r |
| 177 | return FALSE;\r |
| 178 | }\r |
| 179 | \r |
| 180 | t_bool wrint (a10 ea, int32 prv)\r |
| 181 | {\r |
| 182 | tim_period = Read (ea, prv);\r |
| 183 | tim_ttg = tim_period;\r |
| 184 | return FALSE;\r |
| 185 | }\r |
| 186 | \r |
| 187 | /* Timer service - the timer is only serviced when the 'ttg' register\r |
| 188 | has reached 0 based on the expected frequency of clock interrupts. */\r |
| 189 | \r |
| 190 | t_stat tim_svc (UNIT *uptr)\r |
| 191 | {\r |
| 192 | if (cpu_unit.flags & UNIT_KLAD) /* diags? */\r |
| 193 | tmr_poll = uptr->wait; /* fixed clock */\r |
| 194 | else tmr_poll = sim_rtc_calb (clk_tps); /* else calibrate */\r |
| 195 | sim_activate (uptr, tmr_poll); /* reactivate unit */\r |
| 196 | tmxr_poll = tmr_poll * tim_mult; /* set mux poll */\r |
| 197 | tim_incr_base (tim_base, tim_period); /* incr time base */\r |
| 198 | tim_ttg = tim_period; /* reload */\r |
| 199 | apr_flg = apr_flg | APRF_TIM; /* request interrupt */\r |
| 200 | if (Q_ITS) { /* ITS? */\r |
| 201 | if (pi_act == 0)\r |
| 202 | quant = (quant + TIM_ITS_QUANT) & DMASK;\r |
| 203 | if (TSTS (pcst)) { /* PC sampling? */\r |
| 204 | WriteP ((a10) pcst & AMASK, pager_PC); /* store sample */\r |
| 205 | pcst = AOB (pcst); /* add 1,,1 */\r |
| 206 | }\r |
| 207 | } /* end ITS */\r |
| 208 | else if (t20_idlelock && PROB (100 - tim_t20_prob))\r |
| 209 | t20_idlelock = 0;\r |
| 210 | return SCPE_OK;\r |
| 211 | }\r |
| 212 | \r |
| 213 | /* Clock coscheduling routine */\r |
| 214 | \r |
| 215 | int32 clk_cosched (int32 wait)\r |
| 216 | {\r |
| 217 | int32 t;\r |
| 218 | \r |
| 219 | if (tim_mult == TIM_MULT_T20) return wait;\r |
| 220 | t = sim_is_active (&tim_unit);\r |
| 221 | return (t? t - 1: wait);\r |
| 222 | }\r |
| 223 | \r |
| 224 | void tim_incr_base (d10 *base, d10 incr)\r |
| 225 | {\r |
| 226 | base[1] = base[1] + incr; /* add on incr */\r |
| 227 | base[0] = base[0] + (base[1] >> 35); /* carry to high */\r |
| 228 | base[0] = base[0] & DMASK; /* mask high */\r |
| 229 | base[1] = base[1] & MMASK; /* mask low */\r |
| 230 | return;\r |
| 231 | }\r |
| 232 | \r |
| 233 | /* Timer reset */\r |
| 234 | \r |
| 235 | t_stat tim_reset (DEVICE *dptr)\r |
| 236 | {\r |
| 237 | tim_period = 0; /* clear timer */\r |
| 238 | tim_ttg = 0;\r |
| 239 | apr_flg = apr_flg & ~APRF_TIM; /* clear interrupt */\r |
| 240 | tmr_poll = sim_rtc_init (tim_unit.wait); /* init timer */\r |
| 241 | sim_activate_abs (&tim_unit, tmr_poll); /* activate unit */\r |
| 242 | tmxr_poll = tmr_poll * tim_mult; /* set mux poll */\r |
| 243 | return SCPE_OK;\r |
| 244 | }\r |
| 245 | \r |
| 246 | /* Set timer parameters from CPU model */\r |
| 247 | \r |
| 248 | t_stat tim_set_mod (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 249 | {\r |
| 250 | if (val & (UNIT_T20|UNIT_KLAD)) {\r |
| 251 | clk_tps = TIM_TPS_T20;\r |
| 252 | uptr->wait = TIM_WAIT_T20;\r |
| 253 | tmr_poll = TIM_WAIT_T20;\r |
| 254 | tim_mult = TIM_MULT_T20;\r |
| 255 | uptr->flags = uptr->flags | UNIT_Y2K;\r |
| 256 | }\r |
| 257 | else {\r |
| 258 | clk_tps = TIM_TPS_T10;\r |
| 259 | uptr->wait = TIM_WAIT_T10;\r |
| 260 | tmr_poll = TIM_WAIT_T10;\r |
| 261 | tim_mult = TIM_MULT_T10;\r |
| 262 | if (Q_ITS) uptr->flags = uptr->flags | UNIT_Y2K;\r |
| 263 | else uptr->flags = uptr->flags & ~UNIT_Y2K;\r |
| 264 | }\r |
| 265 | tmxr_poll = tmr_poll * tim_mult;\r |
| 266 | return SCPE_OK;\r |
| 267 | }\r |
| 268 | \r |
| 269 | /* Time of year clock */\r |
| 270 | \r |
| 271 | t_stat tcu_rd (int32 *data, int32 PA, int32 access)\r |
| 272 | {\r |
| 273 | time_t curtim;\r |
| 274 | struct tm *tptr;\r |
| 275 | \r |
| 276 | curtim = time (NULL); /* get time */\r |
| 277 | tptr = localtime (&curtim); /* decompose */\r |
| 278 | if (tptr == NULL) return SCPE_NXM; /* Y2K prob? */\r |
| 279 | if ((tptr->tm_year > 99) && !(tim_unit.flags & UNIT_Y2K))\r |
| 280 | tptr->tm_year = 99; \r |
| 281 | \r |
| 282 | switch ((PA >> 1) & 03) { /* decode PA<3:1> */\r |
| 283 | \r |
| 284 | case 0: /* year/month/day */\r |
| 285 | *data = (((tptr->tm_year) & 0177) << 9) |\r |
| 286 | (((tptr->tm_mon + 1) & 017) << 5) |\r |
| 287 | ((tptr->tm_mday) & 037);\r |
| 288 | return SCPE_OK;\r |
| 289 | \r |
| 290 | case 1: /* hour/minute */\r |
| 291 | *data = (((tptr->tm_hour) & 037) << 8) |\r |
| 292 | ((tptr->tm_min) & 077);\r |
| 293 | return SCPE_OK;\r |
| 294 | \r |
| 295 | case 2: /* second */\r |
| 296 | *data = (tptr->tm_sec) & 077;\r |
| 297 | return SCPE_OK;\r |
| 298 | \r |
| 299 | case 3: /* status */\r |
| 300 | *data = CSR_DONE;\r |
| 301 | return SCPE_OK;\r |
| 302 | }\r |
| 303 | \r |
| 304 | return SCPE_NXM; /* can't get here */\r |
| 305 | }\r |