From 7c7df669adb98f0b503a75a6f1a3ad2553336da0 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 12 Nov 2012 15:33:35 -0800 Subject: [PATCH] Asynchronous Support scp.c, scp.h - added sim_uname (Unit Name) API to simplify places which might want to display it (mostly debug messages). - added support for clock co-scheduling - added debugging to trace event queue activities sim_defs.h - added support for clock co-scheduling - added support for sim_uname (Thread local storage macro) - added support for debugging to trace event queue activities - simplified debug code by using sim_uname - fixed support macro for sim_is_active when asynch timers are in use sim_rev.h - fixed nested comments sim_tmxr.c, sim_tmxr.h - added support for clock co-scheduling - simplified debug code by using sim_uname - added support for devices which poll for output on different units sim_timer.c, sim_timer.h - added support for clock co-scheduling - fixed asynchronous clock calibration to smooth out calibration adjustments - simplified debug code by using sim_uname - added ability (when running with asynchronous support) to explicitly disable or enable asynchronous timer support. - changed sim_timer_inst_per_sec to return a double value since the result is always used in a double expression and integer overflow could occur under strange timing conditions vax/vax_stddev.c - converted from simulator specific clock co-scheduling to generic clock co-scheduling. vax/vax_cpu.c - added EVENT and ACTIVATE debug flag (SET CPU DEBUG=EVENT;ACTIVATE) support pdp11/pdp11_dz.c - converted from simulator specific clock co-scheduling to generic clock co-scheduling. pdp11/pdp11_vh.c - converted from simulator specific clock co-scheduling to generic clock co-scheduling. pdp11/pdp11_xq.c - converted from simulator specific clock co-scheduling to generic clock co-scheduling. --- 0readmeAsynchIO.txt | 28 +++++++ PDP11/pdp11_dz.c | 4 +- PDP11/pdp11_vh.c | 22 +++--- PDP11/pdp11_xq.c | 26 +++++-- VAX/vax_cpu.c | 2 + VAX/vax_stddev.c | 13 +--- scp.c | 50 +++++++++++-- scp.h | 1 + sim_defs.h | 48 ++++++++++-- sim_rev.h | 22 +++--- sim_timer.c | 177 +++++++++++++++++++++++++++++++++----------- sim_timer.h | 4 +- sim_tmxr.c | 77 ++++++++++++++++--- sim_tmxr.h | 8 +- 14 files changed, 367 insertions(+), 115 deletions(-) diff --git a/0readmeAsynchIO.txt b/0readmeAsynchIO.txt index 95d2a82f..b8fa50a6 100644 --- a/0readmeAsynchIO.txt +++ b/0readmeAsynchIO.txt @@ -272,6 +272,34 @@ drifted some 4 minutes in 35 minutes time (approximately 10%). The same OS disk also running with idling enabled booted for 4 hours had less that 5 seconds of clock drift (approximately 0.03%). +Co-Scheduling Clock and Multiplexer (or other devices) + +Many simulator devices have needs to periodically executed with timing on the +order of the simulated system's clock ticks. There are numerous reasons for +this type of execution. Meanwhile, many of these events aren't particular +about exactly when they execute as long as they execute frequently enough. +Frequently executing events has the potential to interfere with a simulator's +attempts to idle when the simulated system isn't actually doing useful work. + +Interactions with attempts to 'co-schedule' multiplexer polling with clock +ticks can cause strange simulator behaviors. These strange behaviors only +happen under a combination of conditions: + 1) a multiplexer device is defined in the simulator configuration, + 2) the multiplexor device is NOT attached, and thus is not being managed by + the asynchronous multiplexer support + 3) the multiplexer device schedules polling (co-scheduled) when not + attached (such polling will never produce anything input). +In prior simh versions support for clock co-scheduling was implmented +separately by each simulator, and usually was expressed by code of the form: + sim_activate (uptr, clk_cosched (tmxr_poll)); +As a part of asynchronous timer support, the simulator framework has been +extended to generically provide clock co-scheduling support. The use of this +new capability requires an initial call (usually in the clock device reset +routing) of the form: + sim_register_clock_unit (&clk_unit); +Once the clock unit has been registered, co-scheduling is achieved by replacing +the earlier sim_activate with the following: + sim_clock_coschedule (&dz_unit, tmxr_poll); Run time requirements to use SIM_ASYNCH_IO. The Posix threads API (pthreads) is required for asynchronous execution. diff --git a/PDP11/pdp11_dz.c b/PDP11/pdp11_dz.c index 67740107..cf002910 100644 --- a/PDP11/pdp11_dz.c +++ b/PDP11/pdp11_dz.c @@ -334,7 +334,7 @@ switch ((PA >> 1) & 03) { /* case on PA<2:1> */ if (data & CSR_CLR) /* clr? reset */ dz_clear (dz, FALSE); if (data & CSR_MSE) /* MSE? start poll */ - sim_activate (&dz_unit, clk_cosched (tmxr_poll)); + sim_clock_coschedule (&dz_unit, tmxr_poll); else dz_csr[dz] &= ~(CSR_SA | CSR_RDONE | CSR_TRDY); if ((data & CSR_RIE) == 0) /* RIE = 0? */ dz_clr_rxint (dz); @@ -439,7 +439,7 @@ if (t) { /* any enabled? */ dz_update_rcvi (); /* upd rcv intr */ tmxr_poll_tx (&dz_desc); /* poll output */ dz_update_xmti (); /* upd xmt intr */ - sim_activate (uptr, clk_cosched (tmxr_poll)); /* reactivate */ + sim_clock_coschedule (uptr, tmxr_poll); /* reactivate */ } return SCPE_OK; } diff --git a/PDP11/pdp11_vh.c b/PDP11/pdp11_vh.c index 5b1bb0d9..dd8ef599 100644 --- a/PDP11/pdp11_vh.c +++ b/PDP11/pdp11_vh.c @@ -343,7 +343,7 @@ static DIB vh_dib = { static UNIT vh_unit[VH_MUXES] = { { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, - { UDATA (&vh_timersvc, 0, 0) }, + { UDATA (&vh_timersvc, UNIT_IDLE, 0) }, }; static const REG vh_reg[] = { @@ -841,9 +841,9 @@ static t_stat vh_wr ( int32 data, if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP)) data &= ~CSR_MASTER_RESET; if (vh == 0) /* Only start unit service on the first unit. Units are polled there */ - sim_activate (&vh_unit[0], clk_cosched (tmxr_poll)); + sim_clock_coschedule (&vh_unit[0], tmxr_poll); vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */ - sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); + sim_clock_coschedule (&vh_unit[1], tmxr_poll); } if ((data & CSR_RXIE) == 0) vh_clr_rxint (vh); @@ -875,7 +875,7 @@ static t_stat vh_wr ( int32 data, break; if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; - sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); + sim_clock_coschedule (&vh_unit[1], tmxr_poll); break; } if (vh_unit[vh].flags & UNIT_MODEDHU) { @@ -920,7 +920,7 @@ static t_stat vh_wr ( int32 data, case 2: /* LPR */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; - sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); + sim_clock_coschedule (&vh_unit[1], tmxr_poll); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -947,7 +947,7 @@ static t_stat vh_wr ( int32 data, case 3: /* STAT/FIFODATA */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; - sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); + sim_clock_coschedule (&vh_unit[1], tmxr_poll); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -971,7 +971,7 @@ static t_stat vh_wr ( int32 data, case 4: /* LNCTRL */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; - sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); + sim_clock_coschedule (&vh_unit[1], tmxr_poll); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -1039,7 +1039,7 @@ static t_stat vh_wr ( int32 data, case 5: /* TBUFFAD1 */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; - sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); + sim_clock_coschedule (&vh_unit[1], tmxr_poll); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -1055,7 +1055,7 @@ static t_stat vh_wr ( int32 data, case 6: /* TBUFFAD2 */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; - sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); + sim_clock_coschedule (&vh_unit[1], tmxr_poll); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -1076,7 +1076,7 @@ static t_stat vh_wr ( int32 data, case 7: /* TBUFFCT */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; - sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); + sim_clock_coschedule (&vh_unit[1], tmxr_poll); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -1154,7 +1154,7 @@ static t_stat vh_timersvc ( UNIT *uptr ) } } if (again) - sim_activate (uptr, clk_cosched (tmxr_poll)); /* requeue ourselves */ + sim_clock_coschedule (uptr, tmxr_poll); /* requeue ourselves */ return (SCPE_OK); } diff --git a/PDP11/pdp11_xq.c b/PDP11/pdp11_xq.c index 4937fe4d..86c1ae28 100644 --- a/PDP11/pdp11_xq.c +++ b/PDP11/pdp11_xq.c @@ -435,10 +435,10 @@ REG xqb_reg[] = { }; MTAB xq_mod[] = { - { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, - NULL, &show_addr, NULL }, - { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, - NULL, &show_vec, NULL }, + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, { MTAB_XTD | MTAB_VDV, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", &xq_setmac, &xq_showmac, NULL }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "ETH", "ETH", @@ -2139,13 +2139,20 @@ void xq_start_receiver(CTLR* xq) return; /* start the read service timer or enable asynch reading as appropriate */ - if (xq->var->must_poll) - sim_activate(xq->unit, (sim_idle_enab ? clk_cosched(tmxr_poll) : (tmr_poll*clk_tps)/xq->var->poll)); + if (xq->var->must_poll) { + if (sim_idle_enab) + sim_clock_coschedule(xq->unit, tmxr_poll); + else + sim_activate(xq->unit, (tmr_poll*clk_tps)/xq->var->poll); + } else if ((xq->var->poll == 0) || (xq->var->mode == XQ_T_DELQA_PLUS)) eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks); else - sim_activate(xq->unit, (sim_idle_enab ? clk_cosched(tmxr_poll) : (tmr_poll*clk_tps)/xq->var->poll)); + if (sim_idle_enab) + sim_clock_coschedule(xq->unit, tmxr_poll); + else + sim_activate(xq->unit, (tmr_poll*clk_tps)/xq->var->poll); } void xq_stop_receiver(CTLR* xq) @@ -2520,7 +2527,10 @@ t_stat xq_svc(UNIT* uptr) /* resubmit service timer */ if ((xq->var->must_poll) || (xq->var->poll && (xq->var->mode != XQ_T_DELQA_PLUS))) - sim_activate(uptr, (sim_idle_enab ? clk_cosched(tmxr_poll) : (tmr_poll*clk_tps)/xq->var->poll)); + if (sim_idle_enab) + sim_clock_coschedule(uptr, tmxr_poll); + else + sim_activate(uptr, (tmr_poll*clk_tps)/xq->var->poll); return SCPE_OK; } diff --git a/VAX/vax_cpu.c b/VAX/vax_cpu.c index 613d32bc..a5d476cc 100644 --- a/VAX/vax_cpu.c +++ b/VAX/vax_cpu.c @@ -491,6 +491,8 @@ DEBTAB cpu_deb[] = { { "INTEXC", LOG_CPU_I }, { "REI", LOG_CPU_R }, { "CONTEXT", LOG_CPU_P }, + { "EVENT", SIM_DBG_EVENT }, + { "ACTIVATE", SIM_DBG_ACTIVATE }, { NULL, 0 } }; diff --git a/VAX/vax_stddev.c b/VAX/vax_stddev.c index a7c118fd..0ad9a32c 100644 --- a/VAX/vax_stddev.c +++ b/VAX/vax_stddev.c @@ -321,7 +321,7 @@ t_stat tti_svc (UNIT *uptr) { int32 c; -sim_activate (uptr, KBD_WAIT (uptr->wait, clk_cosched (tmr_poll))); +sim_clock_coschedule (uptr, KBD_WAIT (uptr->wait, tmr_poll)); /* continue poll */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; @@ -405,16 +405,6 @@ if (!todr_blow && todr_reg) /* if running? */ return SCPE_OK; } -/* Clock coscheduling routine */ - -int32 clk_cosched (int32 wait) -{ -int32 t; - -t = sim_is_active (&clk_unit); -return (t? t - 1: wait); -} - int32 todr_rd (void) { TOY *toy = (TOY *)clk_unit.filebuf; @@ -498,6 +488,7 @@ t_stat clk_reset (DEVICE *dptr) { int32 t; +sim_register_clock_unit (&clk_unit); clk_csr = 0; CLR_INT (CLK); if (!sim_is_running) { /* RESET (not IORESET)? */ diff --git a/scp.c b/scp.c index c41afe43..f3db52ea 100644 --- a/scp.c +++ b/scp.c @@ -313,6 +313,7 @@ pthread_t sim_asynch_main_threadid; UNIT * volatile sim_asynch_queue = QUEUE_LIST_END; UNIT * volatile sim_wallclock_queue = QUEUE_LIST_END; UNIT * volatile sim_wallclock_entry = NULL; +UNIT * volatile sim_clock_cosched_queue = QUEUE_LIST_END; int32 sim_asynch_check; int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */ int32 sim_asynch_inst_latency = 20; /* assume 5 mip simulator */ @@ -2311,11 +2312,11 @@ int32 accum; if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_clock_queue == QUEUE_LIST_END) - fprintf (st, "%s event queue empty, time = %.0f\n", - sim_name, sim_time); + fprintf (st, "%s event queue empty, time = %.0f, executing %.0f instructios/sec\n", + sim_name, sim_time, sim_timer_inst_per_sec ()); else { - fprintf (st, "%s event queue status, time = %.0f\n", - sim_name, sim_time); + fprintf (st, "%s event queue status, time = %.0f, executing %.0f instructions/sec\n", + sim_name, sim_time, sim_timer_inst_per_sec ()); accum = 0; for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) { if (uptr == &sim_step_unit) @@ -2334,10 +2335,10 @@ else { pthread_mutex_lock (&sim_timer_lock); if (sim_wallclock_queue == QUEUE_LIST_END) fprintf (st, "%s wall clock event queue empty, time = %.0f\n", - sim_name, sim_time); + sim_name, sim_time); else { fprintf (st, "%s wall clock event queue status, time = %.0f\n", - sim_name, sim_time); + sim_name, sim_time); for (uptr = sim_wallclock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) { if ((dptr = find_dev_from_unit (uptr)) != NULL) { fprintf (st, " %s", sim_dname (dptr)); @@ -2348,6 +2349,19 @@ else { fprintf (st, " after %d usec\n", uptr->a_usec_delay); } } +if (sim_clock_cosched_queue != QUEUE_LIST_END) { + fprintf (st, "%s clock co-schedule event queue status, time = %.0f\n", + sim_name, sim_time); + for (uptr = sim_clock_cosched_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) { + if ((dptr = find_dev_from_unit (uptr)) != NULL) { + fprintf (st, " %s", sim_dname (dptr)); + if (dptr->numunits > 1) + fprintf (st, " unit %d", (int32) (uptr - dptr->units)); + } + else fprintf (st, " Unknown"); + fprintf (st, "\n"); + } + } pthread_mutex_unlock (&sim_timer_lock); pthread_mutex_lock (&sim_asynch_lock); fprintf (st, "asynchronous pending event queue\n"); @@ -3090,6 +3104,19 @@ char *sim_dname (DEVICE *dptr) return (dptr->lname? dptr->lname: dptr->name); } +/* Get unit display name */ + +char *sim_uname (UNIT *uptr) +{ +DEVICE *d = find_dev_from_unit(uptr); +static AIO_TLS char uname[CBUFSIZE]; + +if (d->numunits == 1) + return sim_dname (d); +sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units)); +return uname; +} + /* Save command sa[ve] filename save state to specified file @@ -5303,6 +5330,7 @@ if (stop_cpu) /* stop CPU? */ UPDATE_SIM_TIME; /* update sim time */ if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */ sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Emptry New Interval = %d\n", sim_interval); return SCPE_OK; } do { @@ -5314,6 +5342,7 @@ do { sim_interval = sim_clock_queue->time; else sim_interval = noqueue_time = NOQUEUE_WAIT; + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Event for %s\n", sim_uname (uptr)); AIO_EVENT_BEGIN(uptr); if (uptr->action != NULL) reason = uptr->action (uptr); @@ -5324,9 +5353,12 @@ do { (sim_interval <= 0) && (sim_clock_queue != QUEUE_LIST_END)); -if (sim_clock_queue == QUEUE_LIST_END) /* queue empty? */ +if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */ sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ - + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d\n", sim_interval); + } +else + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d(%s)\n", sim_interval, sim_uname(sim_clock_queue)); return reason; } @@ -5354,6 +5386,8 @@ if (sim_is_active_bool (uptr)) /* already active? */ return SCPE_OK; UPDATE_SIM_TIME; /* update sim time */ +sim_debug (SIM_DBG_ACTIVATE, sim_dflt_dev, "Activating %s delay=%d\n", sim_uname (uptr), event_time); + prvptr = NULL; accum = 0; for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) { diff --git a/scp.h b/scp.h index 6810c6a0..7a690bec 100644 --- a/scp.h +++ b/scp.h @@ -103,6 +103,7 @@ t_stat deassign_device (DEVICE *dptr); t_stat reset_all (uint32 start_device); t_stat reset_all_p (uint32 start_device); char *sim_dname (DEVICE *dptr); +char *sim_uname (UNIT *dptr); t_stat get_yn (char *ques, t_stat deflt); char *get_sim_opt (int32 opt, char *cptr, t_stat *st); char *get_glyph (char *iptr, char *optr, char mchar); diff --git a/sim_defs.h b/sim_defs.h index 1d9f028b..f018a618 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -527,6 +527,9 @@ struct sim_debtab { #define DEBUG_PRI(d,m) (sim_deb && (d.dctrl & (m))) #define DEBUG_PRJ(d,m) (sim_deb && (d->dctrl & (m))) +#define SIM_DBG_EVENT 0x10000 +#define SIM_DBG_ACTIVATE 0x20000 + /* File Reference */ struct sim_fileref { char name[CBUFSIZE]; /* file name */ @@ -579,6 +582,7 @@ typedef struct sim_fileref FILEREF; #include "sim_timer.h" #include "sim_ether.h" #include "sim_fio.h" +#include "sim_tmxr.h" /* Asynch/Threaded I/O support */ @@ -598,11 +602,25 @@ extern pthread_cond_t sim_tmxr_poll_cond; extern pthread_mutex_t sim_tmxr_poll_lock; extern pthread_t sim_asynch_main_threadid; extern UNIT * volatile sim_asynch_queue; +extern UNIT * volatile sim_clock_cosched_queue; extern volatile t_bool sim_idle_wait; extern int32 sim_asynch_check; extern int32 sim_asynch_latency; extern int32 sim_asynch_inst_latency; +/* Thread local storage */ +#if defined(__GNUC__) && !defined(__APPLE__) +#define AIO_TLS __thread +#elif defined(__DECC_VER) || defined(_MSC_VER) +#define AIO_TLS __declspec(thread) +#else +/* Other compiler environment, then don't worry about thread local storage. */ +/* It is primarily used only used in debugging messages */ +#define AIO_TLS +#endif + + + #define AIO_INIT \ if (1) { \ sim_asynch_main_threadid = pthread_self(); \ @@ -662,6 +680,23 @@ extern int32 sim_asynch_inst_latency; } \ if ((uptr)->next == NULL) \ (uptr)->a_due_time = (uptr)->a_usec_delay = 0; \ + else { \ + nptr = QUEUE_LIST_END; \ + if ((uptr) == sim_clock_cosched_queue) { \ + sim_clock_cosched_queue = (uptr)->next; \ + (uptr)->next = NULL; \ + } \ + else \ + for (cptr = sim_clock_cosched_queue; \ + (cptr != QUEUE_LIST_END); \ + cptr = cptr->next) \ + if (cptr->next == (uptr)) { \ + cptr->next = (uptr)->next; \ + nptr = cptr; \ + (uptr)->next = NULL; \ + break; \ + } \ + } \ pthread_mutex_unlock (&sim_timer_lock); \ } \ } \ @@ -674,10 +709,12 @@ extern int32 sim_asynch_inst_latency; cptr != QUEUE_LIST_END; \ cptr = cptr->next) \ if ((uptr) == cptr) { \ - int32 inst_per_sec = sim_timer_inst_per_sec (); \ + double inst_per_sec = sim_timer_inst_per_sec (); \ int32 result; \ \ result = (int32)(((uptr)->a_due_time - sim_timenow_double())*inst_per_sec);\ + if (result < 0) \ + result = 0; \ pthread_mutex_unlock (&sim_timer_lock); \ return result + 1; \ } \ @@ -756,10 +793,8 @@ extern int32 sim_asynch_inst_latency; } else (void)0 #define AIO_ACTIVATE(caller, uptr, event_time) \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ - DEVICE *d; \ - if (sim_deb) \ - d = find_dev_from_unit(uptr); \ - sim_debug (TIMER_DBG_QUEUE, &sim_timer_dev, "asynch event on %s after %d instructions\n", d->name, event_time);\ + UNIT *ouptr = (uptr); \ + sim_debug (TIMER_DBG_QUEUE, &sim_timer_dev, "asynch event on %s after %d instructions\n", sim_uname(uptr), event_time);\ if (uptr->a_next) { /* already queued? */ \ uptr->a_activate_call = sim_activate_abs; \ } else { \ @@ -781,7 +816,7 @@ extern int32 sim_asynch_inst_latency; } \ sim_asynch_check = 0; /* try to force check */ \ if (sim_idle_wait) { \ - sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", d->name, event_time);\ + sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(ouptr), event_time);\ pthread_cond_signal (&sim_idle_wake); \ } \ return SCPE_OK; \ @@ -858,6 +893,7 @@ extern int32 sim_asynch_inst_latency; #define AIO_EVENT_BEGIN(uptr) #define AIO_EVENT_COMPLETE(uptr, reason) #define AIO_SET_INTERRUPT_LATENCY(instpersec) +#define AIO_TLS #endif /* SIM_ASYNCH_IO */ #endif diff --git a/sim_rev.h b/sim_rev.h index 93b173ca..38c1e47c 100644 --- a/sim_rev.h +++ b/sim_rev.h @@ -356,7 +356,7 @@ patch date module(s) and fix(es) - moved all Qbus devices to BR4; deleted RP definitions -/* V3.8 revision history + V3.8 revision history 1 08-Feb-09 scp.c: - revised RESTORE unit logic for consistency @@ -709,7 +709,7 @@ patch date module(s) and fix(es) - fixed declarations (Mark Pizzolato) -/* V3.7 revision history + V3.7 revision history 3 02-Sep-07 scp.c: - fixed bug in SET THROTTLE command @@ -981,7 +981,7 @@ patch date module(s) and fix(es) - fixed operand order in EIS instructions (W.F.J. Mueller) -/* V3.6 revision history + V3.6 revision history 1 25-Jul-06 sim_console.c: - implemented SET/SHOW PCHAR @@ -1155,7 +1155,7 @@ patch date module(s) and fix(es) - fixed bug in reported VA on faulting cross-page write -/* V3.5 revision history + V3.5 revision history patch date module(s) and fix(es) @@ -1291,7 +1291,7 @@ patch date module(s) and fix(es) vax_io.c: revised autoconfiguration algorithm and interface -/* V3.4 revision history + V3.4 revision history 0 01-May-04 scp.c: - fixed ASSERT code @@ -1329,7 +1329,7 @@ patch date module(s) and fix(es) pdp11_tu.c: fixed error reporting -/* V3.3 revision history + V3.3 revision history 2 08-Mar-05 scp.c: added ASSERT command (Dave Bryan) @@ -1581,7 +1581,7 @@ patch date module(s) and fix(es) - split from vax_sys.c - removed PTR, PTP -/* V3.2 revision history + V3.2 revision history 3 03-Sep-04 scp.c: - added ECHO command (Dave Bryan) @@ -1780,7 +1780,7 @@ patch date module(s) and fix(es) - added PC read fault in EXTxV - fixed PC write fault in INSV -/* V3.1 revision history + V3.1 revision history 0 29-Dec-03 sim_defs.h, scp.c: added output stall status @@ -1893,7 +1893,7 @@ patch date module(s) and fix(es) - revised instruction decoding - added instruction history -/* V3.0 revision history + V3.0 revision history 2 15-Sep-03 scp.c: - fixed end-of-file problem in dep, idep @@ -2059,7 +2059,7 @@ patch date module(s) and fix(es) gri_cpu.c: fixed bug in SC queue pointer management -/* V2.10 revision history + V2.10 revision history 4 03-Mar-03 scp.c - added .ini startup file capability @@ -2357,7 +2357,7 @@ patch date module(s) and fix(es) IBM 1620: first release -/* V2.9 revision history + V2.9 revision history 11 20-Jul-02 i1401_mt.c: on read, end of record stores group mark without word mark (Van Snyder) diff --git a/sim_timer.c b/sim_timer.c index 1ac7a602..5f3ca2b4 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -79,6 +79,8 @@ routines are not. */ +#define NOT_MUX_USING_CODE /* sim_tmxr library provider or agnostic */ + #include "sim_defs.h" #include #include @@ -99,7 +101,13 @@ static uint32 sim_throt_val = 0; static uint32 sim_throt_state = 0; static uint32 sim_throt_sleep_time = 0; static int32 sim_throt_wait = 0; - +static UNIT *sim_clock_unit = NULL; +static t_bool sim_asynch_timer = +#if defined (SIM_ASYNCH_IO) + TRUE; +#else + FALSE; +#endif extern int32 sim_interval, sim_switches; extern FILE *sim_log; extern UNIT *sim_clock_queue; @@ -577,17 +585,20 @@ int32 sim_rtcn_calb (int32 ticksper, int32 tmr) uint32 new_rtime, delta_rtime; int32 delta_vtime; double new_gtime; +int32 new_currd; if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return 10000; rtc_hz[tmr] = ticksper; rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */ -if (rtc_ticks[tmr] < ticksper) /* 1 sec yet? */ +if (rtc_ticks[tmr] < ticksper) { /* 1 sec yet? */ return rtc_currd[tmr]; + } rtc_ticks[tmr] = 0; /* reset ticks */ rtc_elapsed[tmr] = rtc_elapsed[tmr] + 1; /* count sec */ -if (!rtc_avail) /* no timer? */ +if (!rtc_avail) { /* no timer? */ return rtc_currd[tmr]; + } new_rtime = sim_os_msec (); /* wall time */ sim_debug (DBG_TRC, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d, tmr=%d) rtime=%d\n", ticksper, tmr, new_rtime); if (sim_idle_idled) { @@ -609,21 +620,32 @@ rtc_rtime[tmr] = new_rtime; /* adv wall time */ rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */ if (delta_rtime > 30000) { /* gap too big? */ rtc_currd[tmr] = rtc_initd[tmr]; + rtc_gtime[tmr] = sim_gtime(); /* save instruction time */ sim_debug (DBG_CAL, &sim_timer_dev, "gap too big: delta = %d - result: %d\n", delta_rtime, rtc_initd[tmr]); return rtc_initd[tmr]; /* can't calibr */ } new_gtime = sim_gtime(); -if (sim_asynch_enabled) +if (sim_asynch_enabled && sim_asynch_timer) if (rtc_elapsed[tmr] > sim_idle_stable) { /* An asynchronous clock, merely needs to divide the number of */ /* instructions actually executed by the clock rate. */ - rtc_currd[tmr] = (int32)((new_gtime - rtc_gtime[tmr])/ticksper); + new_currd = (int32)((new_gtime - rtc_gtime[tmr])/ticksper); + /* avoid excessive swings in the calibrated result */ + if (new_currd > 10*rtc_currd[tmr]) /* don't swing big too fast */ + new_currd = 10*rtc_currd[tmr]; + else + if (new_currd < rtc_currd[tmr]/10) /* don't swing small too fast */ + new_currd = rtc_currd[tmr]/10; + rtc_currd[tmr] = new_currd; rtc_gtime[tmr] = new_gtime; /* save instruction time */ + if (rtc_currd[tmr] == 127) + sim_debug (DBG_CAL, &sim_timer_dev, "asynch calibration small: %d\n", rtc_currd[tmr]); sim_debug (DBG_CAL, &sim_timer_dev, "asynch calibration result: %d\n", rtc_currd[tmr]); return rtc_currd[tmr]; /* calibrated result */ } else { rtc_currd[tmr] = rtc_initd[tmr]; + rtc_gtime[tmr] = new_gtime; /* save instruction time */ sim_debug (DBG_CAL, &sim_timer_dev, "asynch not stable calibration result: %d\n", rtc_initd[tmr]); return rtc_initd[tmr]; /* initial result until stable */ } @@ -685,7 +707,7 @@ for (tmr=0; tmrname, sim_interval); - } +else + sim_debug (DBG_IDL, &sim_timer_dev, "sleeping for %d ms - pending event on %s in %d instructions\n", w_ms, sim_uname(sim_clock_queue), sim_interval); act_ms = SIM_IDLE_MS_SLEEP (w_ms); /* wait */ act_cyc = act_ms * cyc_ms; if (act_ms < w_ms) /* awakened early? */ @@ -802,10 +855,8 @@ if (sim_interval > act_cyc) else sim_interval = 0; /* or fire immediately */ if (sim_clock_queue == QUEUE_LIST_END) sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event in %d instructions\n", act_ms, sim_interval); -else { - DEVICE *d = find_dev_from_unit(sim_clock_queue); - sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event on %s in %d instructions\n", act_ms, d->name, sim_interval); - } +else + sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event on %s in %d instructions\n", act_ms, sim_uname(sim_clock_queue), sim_interval); return TRUE; } @@ -1099,21 +1150,19 @@ sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - starting\n"); pthread_mutex_lock (&sim_timer_lock); pthread_cond_signal (&sim_timer_startup_cond); /* Signal we're ready to go */ -while (sim_asynch_enabled && sim_is_running) { +while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) { struct timespec start_time, stop_time; struct timespec due_time; double wait_usec; int32 inst_delay; - int32 inst_per_sec; + double inst_per_sec; UNIT *uptr; - DEVICE *d; if (sim_wallclock_entry) { /* something to insert in queue? */ UNIT *cptr, *prvptr; - d = find_dev_from_unit(sim_wallclock_entry); - sim_debug (DBG_TIM, &sim_timer_dev, (d->numunits > 1) ? "_timer_thread() - timing %s%d for %d usec\n" : "_timer_thread() - timing %s%.0d for %d usec\n", - d->name, (int)(sim_wallclock_entry-d->units), sim_wallclock_entry->time); + sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - timing %s for %d usec\n", + sim_uname(sim_wallclock_entry), sim_wallclock_entry->time); uptr = sim_wallclock_entry; sim_wallclock_entry = NULL; @@ -1140,7 +1189,6 @@ while (sim_asynch_enabled && sim_is_running) { /* the goal being to let the last fractional part of the due time */ /* be done by counting instructions */ _double_to_timespec (&due_time, sim_wallclock_queue->a_due_time-(((double)sim_idle_rate_ms)*0.0005)); - d = find_dev_from_unit(sim_wallclock_queue); /* find this before waiting */ } else { due_time.tv_sec = 0x7FFFFFFF; /* Sometime when 32 bit time_t wraps */ @@ -1171,9 +1219,16 @@ while (sim_asynch_enabled && sim_is_running) { inst_delay = (int32)(inst_per_sec*(_timespec_to_double(&due_time)-_timespec_to_double(&stop_time))); uptr->a_last_fired_time = uptr->a_due_time; } - sim_debug (DBG_TIM, &sim_timer_dev, (d->numunits > 1) ? "_timer_thread() - slept %.0fms - activating(%s%d,%d)\n" : "_timer_thread() - slept %.0fms - activating(%s%.0d,%d)\n", - 1000.0*(_timespec_to_double (&stop_time)-_timespec_to_double (&start_time)), d->name, (int)(uptr-d->units), inst_delay); + sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - slept %.0fms - activating(%s,%d)\n", + 1000.0*(_timespec_to_double (&stop_time)-_timespec_to_double (&start_time)), sim_uname(uptr), inst_delay); sim_activate (uptr, inst_delay); + if (sim_clock_unit == uptr) + while (sim_clock_cosched_queue != QUEUE_LIST_END) { + uptr = sim_clock_cosched_queue; + sim_clock_cosched_queue = uptr->next; + uptr->next = NULL; + sim_activate (uptr, inst_delay); + } } else /* Something wants to adjust the queue */ if (sim_timer_event_canceled) @@ -1195,7 +1250,7 @@ void sim_start_timer_services (void) { #if defined(SIM_ASYNCH_IO) pthread_mutex_lock (&sim_timer_lock); -if (sim_asynch_enabled) { +if (sim_asynch_enabled && sim_asynch_timer) { pthread_attr_t attr; UNIT *cptr; double delta_due_time; @@ -1247,7 +1302,7 @@ else t_stat sim_timer_change_asynch (void) { #if defined(SIM_ASYNCH_IO) -if (sim_asynch_enabled) +if (sim_asynch_enabled && sim_asynch_timer) sim_start_timer_services (); else { UNIT *uptr; @@ -1270,13 +1325,17 @@ else { return SCPE_OK; } -int32 sim_timer_inst_per_sec (void) +/* Instruction Execution rate. */ +/* returns a double since it is mostly used in double expressions and + to avoid overflow if/when strange timing delays might produce unexpected results */ + +double sim_timer_inst_per_sec (void) { -int32 inst_per_sec; +double inst_per_sec = SIM_INITIAL_IPS; if (sim_calb_tmr == -1) - return SIM_INITIAL_IPS; -inst_per_sec = rtc_currd[sim_calb_tmr]*rtc_hz[sim_calb_tmr]; + return inst_per_sec; +inst_per_sec = ((double)rtc_currd[sim_calb_tmr])*rtc_hz[sim_calb_tmr]; if (0 == inst_per_sec) inst_per_sec = SIM_INITIAL_IPS; return inst_per_sec; @@ -1285,23 +1344,20 @@ return inst_per_sec; t_stat sim_timer_activate_after (UNIT *uptr, int32 usec_delay) { int32 inst_delay; -int32 inst_per_sec; -DEVICE *d; +double inst_per_sec; AIO_VALIDATE; if (sim_is_active_bool (uptr)) /* already active? */ return SCPE_OK; inst_per_sec = sim_timer_inst_per_sec (); -inst_delay = (int32)((((double)inst_per_sec)*usec_delay)/1000000.0); +inst_delay = (int32)((inst_per_sec*usec_delay)/1000000.0); #if defined(SIM_ASYNCH_IO) -if ((sim_calb_tmr == -1) || /* if No timer initialized +if ((sim_calb_tmr == -1) || /* if No timer initialized */ (inst_delay < rtc_currd[sim_calb_tmr]) || /* or sooner than next clock tick? */ (rtc_elapsed[sim_calb_tmr] < sim_idle_stable) || /* or not idle stable yet */ - (!sim_asynch_enabled)) { /* or asynch disabled */ - if (sim_deb) - d = find_dev_from_unit(uptr); - sim_debug (DBG_TIM, &sim_timer_dev, (d->numunits > 1) ? "sim_timer_activate_after() - activating %s%d after %d instructions\n" : "sim_timer_activate_after() - activating %s%.0d after %d instructions\n" , - d->name, (int)(uptr-d->units), inst_delay); + (!(sim_asynch_enabled && sim_asynch_timer))) { /* or asynch disabled */ + sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after() - activating %s after %d instructions\n", + sim_uname(uptr), inst_delay); return _sim_activate (uptr, inst_delay); /* queue it now */ } if (1) { @@ -1349,10 +1405,8 @@ if (1) { } uptr->time = usec_delay; - if (sim_deb) - d = find_dev_from_unit(uptr); - sim_debug (DBG_TIM, &sim_timer_dev, (d->numunits > 1) ? "sim_timer_activate_after() - queue addition %s%d at %.6f\n" : "sim_timer_activate_after() - queue addition %s%.0d at %.6f\n" , - d->name, (int)(uptr-d->units), uptr->a_due_time); + sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after() - queue addition %s at %.6f\n", + sim_uname(uptr), uptr->a_due_time); } pthread_mutex_lock (&sim_timer_lock); sim_wallclock_entry = uptr; @@ -1364,3 +1418,36 @@ return _sim_activate (uptr, inst_delay); /* queue it now */ #endif } +/* Clock coscheduling routines */ + +t_stat sim_register_clock_unit (UNIT *uptr) +{ +sim_clock_unit = uptr; +return SCPE_OK; +} + +t_stat sim_clock_coschedule (UNIT *uptr, int32 interval) +{ +if (NULL == sim_clock_unit) + return sim_activate (uptr, interval); +else + if (sim_asynch_enabled && sim_asynch_timer) { + if (!sim_is_active_bool (uptr)) { /* already active? */ +#if defined(SIM_ASYNCH_IO) + sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule() - queueing %s for clock co-schedule\n", sim_uname (uptr)); + pthread_mutex_lock (&sim_timer_lock); + uptr->next = sim_clock_cosched_queue; + sim_clock_cosched_queue = uptr; + pthread_mutex_unlock (&sim_timer_lock); +#endif + } + return SCPE_OK; + } + else { + int32 t; + + t = sim_is_active (sim_clock_unit); + return sim_activate (uptr, t? t - 1: interval); + } +} + diff --git a/sim_timer.h b/sim_timer.h index 46f9014e..5b9f999a 100644 --- a/sim_timer.h +++ b/sim_timer.h @@ -111,7 +111,9 @@ void sim_start_timer_services (void); void sim_stop_timer_services (void); t_stat sim_timer_change_asynch (void); t_stat sim_timer_activate_after (UNIT *uptr, int32 usec_delay); -int32 sim_timer_inst_per_sec (void); +t_stat sim_register_clock_unit (UNIT *uptr); +t_stat sim_clock_coschedule (UNIT *uptr, int32 interval); +double sim_timer_inst_per_sec (void); extern t_bool sim_idle_enab; /* idle enabled flag */ extern volatile t_bool sim_idle_wait; /* idle waiting flag */ diff --git a/sim_tmxr.c b/sim_tmxr.c index ed53c06e..3f326cf1 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -699,6 +699,25 @@ for (i = 0; i < mp->lines; i++) { /* initialize lines */ return SCPE_OK; } +/* Declare which unit polls for input + + Inputs: + *mp = the mux + line = the line number + *uptr_poll = the unit which polls + + Outputs: + none + + Implementation note: + + Only devices which poll on a unit different from the unit provided + at MUX attach time need call this function. Calling this API is + necessary for asynchronous multiplexer support and unnecessary + otherwise. + +*/ + t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll) { if ((line < 0) || (line >= mp->lines)) @@ -720,6 +739,33 @@ else return SCPE_OK; } +/* Declare which unit polls for output + + Inputs: + *mp = the mux + line = the line number + *uptr_poll = the unit which polls for output + + Outputs: + none + + Implementation note: + + Only devices which poll on a unit different from the unit provided + at MUX attach time need call this function AND different from the + unit which polls for input. Calling this API is necessary for + asynchronous multiplexer support and unnecessary otherwise. + +*/ + +t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll) +{ +if ((line < 0) || (line >= mp->lines)) + return SCPE_ARG; +mp->ldsc[line].o_uptr = uptr_poll; +return SCPE_OK; +} + static TMXR **tmxr_open_devices = NULL; static int tmxr_open_device_count = 0; @@ -770,7 +816,7 @@ while (sim_asynch_enabled) { if ((tmxr_open_device_count == 0) || (!sim_is_running)) { for (j=0; jname, (int)(activated[j]-d->units), activated[j]->a_poll_waiter_count); + sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Removing interest in %s. Other interest: %d\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count); --activated[j]->a_poll_waiter_count; --sim_tmxr_poll_count; } @@ -827,7 +873,7 @@ while (sim_asynch_enabled) { mp->uptr->a_polling_now = TRUE; mp->uptr->a_poll_waiter_count = 0; d = find_dev_from_unit(mp->uptr); - sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating %s%d to poll connect\n", d->name, (int)(mp->uptr-d->units)); + sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating %s to poll connect\n", sim_uname(mp->uptr)); pthread_mutex_unlock (&sim_tmxr_poll_lock); _sim_activate (mp->uptr, 0); pthread_mutex_lock (&sim_tmxr_poll_lock); @@ -847,8 +893,8 @@ while (sim_asynch_enabled) { mp->ldsc[j].uptr->a_polling_now = TRUE; mp->ldsc[j].uptr->a_poll_waiter_count = 0; d = find_dev_from_unit(mp->ldsc[j].uptr); - sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Line %d Activating %s%d to poll data: %d/%d\n", - j, d->name, (int)(mp->ldsc[j].uptr-d->units), tmxr_tqln(&mp->ldsc[j]), tmxr_rqln (&mp->ldsc[j])); + sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Line %d Activating %s to poll data: %d/%d\n", + j, sim_uname(mp->ldsc[j].uptr), tmxr_tqln(&mp->ldsc[j]), tmxr_rqln (&mp->ldsc[j])); pthread_mutex_unlock (&sim_tmxr_poll_lock); _sim_activate (mp->ldsc[j].uptr, 0); pthread_mutex_lock (&sim_tmxr_poll_lock); @@ -884,14 +930,14 @@ while (sim_asynch_enabled) { activated[j]->a_polling_now = TRUE; activated[j]->a_poll_waiter_count = 1; d = find_dev_from_unit(activated[j]); - sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating for data %s%d\n", d->name, (int)(activated[j]-d->units)); + sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating for data %s\n", sim_uname(activated[j])); pthread_mutex_unlock (&sim_tmxr_poll_lock); _sim_activate (activated[j], 0); pthread_mutex_lock (&sim_tmxr_poll_lock); } else { d = find_dev_from_unit(activated[j]); - sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Already Activated %s%d %d times\n", d->name, (int)(activated[j]-d->units), activated[j]->a_poll_waiter_count); + sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Already Activated %s%d %d times\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count); ++activated[j]->a_poll_waiter_count; } } @@ -1040,10 +1086,8 @@ else { for (j = 0; j < mp->lines; j++) { lp = mp->ldsc + j; fprintf (st, "Line: %d", j); - if (lp->uptr && (lp->uptr != lp->mp->uptr)) { - DEVICE *dptr = find_dev_from_unit (lp->uptr); - fprintf (st, " - Unit: %s%d\n", dptr->name, (int)(lp->uptr-dptr->units)); - } + if (lp->uptr && (lp->uptr != lp->mp->uptr)) + fprintf (st, " - Unit: %s\n", sim_uname (lp->uptr)); else fprintf (st, "\n"); if (!lp->conn) @@ -1157,6 +1201,19 @@ return _sim_activate_after (uptr, usecs_walltime); #endif } +t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval) +{ +#if defined(SIM_ASYNCH_IO) +if ((!(uptr->flags & UNIT_TM_POLL)) || + (!sim_asynch_enabled)) { + return sim_clock_coschedule (uptr, interval); + } +return SCPE_OK; +#else +return sim_clock_coschedule (uptr, interval); +#endif +} + /* Stub examine and deposit */ t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) diff --git a/sim_tmxr.h b/sim_tmxr.h index 7a793be2..b2996569 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -91,7 +91,8 @@ struct tmln { char rbr[TMXR_MAXBUF]; /* rcv break */ char *txb; /* xmt buffer */ TMXR *mp; /* back pointer to mux */ - UNIT *uptr; /* pointer to receive poll unit */ + UNIT *uptr; /* input polling unit (default to mp->uptr) */ + UNIT *o_uptr; /* output polling unit (default to lp->uptr)*/ }; struct tmxr { @@ -101,7 +102,7 @@ struct tmxr { TMLN *ldsc; /* line descriptors */ int32 *lnorder; /* line connection order */ DEVICE *dptr; /* multiplexer device */ - UNIT *uptr; /* polling unit */ + UNIT *uptr; /* polling unit (connection) */ char logfiletmpl[FILENAME_MAX]; /* template logfile name */ int32 txcount; /* count of transmit bytes */ int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */ @@ -119,6 +120,7 @@ t_stat tmxr_close_master (TMXR *mp); t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, char *cptr, t_bool async); t_stat tmxr_detach (TMXR *mp, UNIT *uptr); t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll); +t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll); t_stat tmxr_set_console_input_unit (UNIT *uptr); t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); @@ -140,6 +142,7 @@ t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc); t_stat tmxr_activate (UNIT *uptr, int32 interval); t_stat tmxr_activate_after (UNIT *uptr, int32 usecs_walltime); +t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval); t_stat tmxr_change_async (void); t_stat tmxr_startup (void); t_stat tmxr_shutdown (void); @@ -156,6 +159,7 @@ extern FILE *sim_deb; /* debug file */ #if (!defined(NOT_MUX_USING_CODE)) #define sim_activate tmxr_activate #define sim_activate_after tmxr_activate_after +#define sim_clock_coschedule tmxr_clock_coschedule #endif #else #define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, FALSE)