From b4d185f3afc5ab76106983c88799d643d1aed7d1 Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Sun, 5 Aug 2018 22:43:59 -0400 Subject: [PATCH] B5500: Second release of Burroughs B5500 simulator. 1) Minor cleanup of some instruction execution to better match flowcharts. 2) Changes to allow proper reboot if OS crashed. 3) Changes to terminal muliplexer to allow for simH expect/send to work. 4) Some corrections to translation table. 5) Support for new sim_card interface. This includes support for stacking of multiple decks on the input stream. --- B5500/b5500_cpu.c | 114 ++++++++++++++++++--------------------- B5500/b5500_defs.h | 3 +- B5500/b5500_dk.c | 12 +++-- B5500/b5500_dtc.c | 62 +++++++++++++-------- B5500/b5500_mt.c | 2 +- B5500/b5500_sys.c | 13 ++--- B5500/b5500_urec.c | 132 +++++++++++++++++++++++++++------------------ doc/b5500_doc.doc | Bin 143360 -> 143872 bytes 8 files changed, 188 insertions(+), 150 deletions(-) diff --git a/B5500/b5500_cpu.c b/B5500/b5500_cpu.c index 6dcb50d4..e0e7b8f3 100644 --- a/B5500/b5500_cpu.c +++ b/B5500/b5500_cpu.c @@ -213,7 +213,7 @@ uint16 l_reg[2]; /* L current syllable pointer */ uint8 ncsf_reg[2]; /* True if normal state */ uint8 salf_reg[2]; /* True if subrogram mode */ uint8 cwmf_reg[2]; /* True if character mode */ -uint8 hltf[2]; /* True if processor halted */ +uint16 hltf[2]; /* True if processor halted */ uint8 msff_reg[2]; /* Mark stack flag Word mode */ #define TFFF MSFF /* True state in Char mode */ uint8 varf_reg[2]; /* Variant Flag */ @@ -926,7 +926,7 @@ void initiate() { /* Save processor state in case of error or halt */ void storeInterrupt(int forced, int test) { int f; - t_uint64 temp; + uint16 temp; if (forced || test) NCSF = 0; @@ -946,7 +946,7 @@ void storeInterrupt(int forced, int test) { } /* Make ILCW */ B = X | ((i)? PRESENT : 0) | FLAG | DFLAG; - next_addr(S); /* Save B */ + next_addr(S); /* Save B */ memory_cycle(11); } else { if (BROF || test) { /* Push B First */ @@ -959,11 +959,11 @@ void storeInterrupt(int forced, int test) { } } AROF = 0; - B = ICW; /* Set ICW into B */ - next_addr(S); /* Save B */ + B = ICW; /* Set ICW into B */ + next_addr(S); /* Save B */ memory_cycle(11); - B = RCW(f); /* Save IRCW */ - next_addr(S); /* Save B */ + B = RCW(f); /* Save IRCW */ + next_addr(S); /* Save B */ memory_cycle(11); if (CWMF) { /* Get the correct value of R */ @@ -1985,7 +1985,7 @@ sim_instr(void) int i; int j; - reason = 0; + reason = SCPE_OK; hltf[0] = 0; hltf[1] = 0; P1_run = 1; @@ -1993,18 +1993,17 @@ sim_instr(void) while (reason == 0) { /* loop until halted */ if (P1_run == 0) return SCPE_STOP; + /* System is booting, wait until finished loading */ while (loading) { - sim_interval = -1; reason = sim_process_event(); - if (reason != SCPE_OK) { + if (reason != SCPE_OK) break; /* process */ - } } + /* Passed time quantum */ if (sim_interval <= 0) { /* event queue? */ reason = sim_process_event(); - if (reason != SCPE_OK) { + if (reason != SCPE_OK) break; /* process */ - } } if (sim_brk_summ) { @@ -2027,20 +2026,15 @@ sim_instr(void) } /* Toggle between two CPU's. */ + if (TROF == 0 && NCSF) { + if (Q != 0 || ((cpu_index)? HLTF : IAR) != 0) + storeInterrupt(1,0); + } + if (cpu_index == 0 && P2_run == 1) { cpu_index = 1; - /* Check if interrupt pending. */ - if (TROF == 0 && NCSF && ((Q != 0) || HLTF)) - /* Force a SFI */ - storeInterrupt(1,0); } else { cpu_index = 0; - - /* Check if interrupt pending. */ - if (TROF == 0 && NCSF && ((Q != 0) || - (IAR != 0))) - /* Force a SFI */ - storeInterrupt(1,0); } if (TROF == 0) next_prog(); @@ -2184,17 +2178,12 @@ crf_loop: Ma = (F - field) & CORE; memory_cycle(4); AROF = 0; - if (A & FLAG) { - if ((A & PRESENT) == 0) { - if (NCSF) - Q |= PRES_BIT; - break; - } - GH = 0; - } else { - GH = (A >> 12) & 070; - } Ma = CF(A); + GH = 0; + if ((A & FLAG) == 0) + GH = (A >> 12) & 070; + else if ((A & PRESENT) == 0 && NCSF) + Q |= PRES_BIT; break; case CMOP_RDA: /* Recall Destination Address */ @@ -2203,34 +2192,29 @@ crf_loop: S = (F - field) & CORE; memory_cycle(3); BROF = 0; - if (B & FLAG) { - if ((B & PRESENT) == 0) { - if (NCSF) - Q |= PRES_BIT; - break; - } - KV = 0; - } else { - KV = (B >> 12) & 070; - } S = CF(B); + KV = 0; + if ((B & FLAG) == 0) + KV = (B >> 12) & 070; + else if ((B & PRESENT) == 0 && NCSF) + Q |= PRES_BIT; break; case CMOP_RCA: /* Recall Control Address */ AROF = BROF; - A = B; /* Save B temporarly */ + A = B; /* Save B temporarly */ atemp = S; /* Save S */ S = (F - field) & CORE; - memory_cycle(3); /* Load word in B */ + memory_cycle(3);/* Load word in B */ S = atemp; /* Restore S */ if (B & FLAG) { if ((B & PRESENT) == 0) { if (NCSF) Q |= PRES_BIT; - break; + } else { + C = CF(B); + L = 0; } - C = CF(B); - L = 0; } else { C = CF(B); L = LF(B) + 1; @@ -2318,9 +2302,10 @@ crf_loop: A = B; AROF = BROF; B = ((t_uint64)(KV & 070) << (FFIELD_V - 3)) | toC(S); + atemp = S; S = (F - field) & CORE; memory_cycle(013); /* Store B in S */ - S = CF(B); + S = atemp; B = A; BROF = AROF; AROF = 0; @@ -2331,9 +2316,10 @@ crf_loop: A = B; AROF = BROF; B = ((t_uint64)(GH & 070) << (FFIELD_V - 3)) | toC(Ma); + atemp = Ma; Ma = (F - field) & CORE; memory_cycle(015); /* Store B in Ma */ - Ma = CF(B); + Ma = atemp; B = A; BROF = AROF; AROF = 0; @@ -3652,6 +3638,12 @@ control: do { Ma = CF(B); memory_cycle(5); + if (sim_interval <= 0) { /* event queue? */ + reason = sim_process_event(); + if (reason != SCPE_OK) { + break; /* process */ + } + } temp = (B & MANT) + (A & MANT); } while ((temp & EXPO) == 0); A = FLAG | PRESENT | toC(Ma); @@ -3672,17 +3664,12 @@ control: R = 0; F = S; /* Set F and X */ X = toF(S); - if (B & FLAG) { - if ((B & PRESENT) == 0) { - if (NCSF) - Q |= PRES_BIT; - break; - } - KV = 0; - } else { - KV = (uint8)((B >> (FFIELD_V - 3)) & 070); - } S = CF(B); + KV = 0; + if ((B & FLAG) == 0) + KV = (uint8)((B >> (FFIELD_V - 3)) & 070); + else if ((B & PRESENT) == 0 && NCSF) + Q |= PRES_BIT; break; case VARIANT(WMOP_MKS): /* Mark Stack */ @@ -3846,6 +3833,8 @@ cpu_reset(DEVICE * dptr) GH = KV = Q = 0; hltf[0] = 0; P1_run = 0; + IAR = 0; + HALT = 0; idle_addr = 0; sim_brk_types = sim_brk_dflt = SWMASK('E') | SWMASK('A') | SWMASK('B'); @@ -3958,7 +3947,7 @@ cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc) t_stat r; t_value sim_eval; struct InstHistory *h; - static const char flags[] = "ABCNSMV"; + CONST static char flags[] = "ABCNSMV"; if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ @@ -4008,8 +3997,7 @@ cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc) fputc(' ', st); fprint_val(st, h->op, 8, 12, PV_RZRO); fputc(' ', st); - print_opcode(st, h->op, - ((h->flags & F_CWMF) ? char_ops : word_ops)); + print_opcode(st, h->op, ((h->flags & F_CWMF) != 0)); fputc(' ', st); fprint_val(st, h->iar, 8, 16, PV_RZRO); fputc('\n', st); /* end line */ diff --git a/B5500/b5500_defs.h b/B5500/b5500_defs.h index 7877be01..707b62af 100644 --- a/B5500/b5500_defs.h +++ b/B5500/b5500_defs.h @@ -72,8 +72,7 @@ typedef struct _opcode } t_opcode; -extern void print_opcode(FILE * ofile, t_value val, t_opcode *); -extern t_opcode word_ops[], char_ops[]; +void print_opcode(FILE * ofile, t_value val, int chr_mode); t_stat chan_reset(DEVICE *); t_stat chan_boot(t_uint64); diff --git a/B5500/b5500_dk.c b/B5500/b5500_dk.c index 1e0f67bf..ec9c48bf 100644 --- a/B5500/b5500_dk.c +++ b/B5500/b5500_dk.c @@ -280,7 +280,7 @@ t_stat dsk_srv(UNIT * uptr) chan_set_end(chan); } else uptr->u5 &= ~(DK_RDCK|DK_RD|DK_WR); - sim_activate(eptr, 500); + sim_activate(eptr, 8000); return SCPE_OK; } sim_activate(uptr, 100); @@ -432,7 +432,7 @@ t_stat esu_srv(UNIT * uptr) uptr->u5 -= DK_SECT; } } - sim_activate(uptr, (uptr->flags & MODIB) ? 200 :100); + sim_activate(uptr, (uptr->flags & MODIB) ? 500 :300); return SCPE_OK; } @@ -456,8 +456,14 @@ dsk_boot(int32 unit_num, DEVICE * dptr) t_uint64 desc; int i; - for(i = 0; i < 20; i++) + for(i = 0; i < 20; i++) { esu_unit[i].u5 = 0; + sim_cancel(&esu_unit[i]); + } + dsk_unit[0].u5 = 0; + dsk_unit[1].u5 = 0; + sim_cancel(&dsk_unit[0]); + sim_cancel(&dsk_unit[1]); desc = (((t_uint64)dev)<u5 & DTC_WR) { - if (line > dtc_desc.lines || line == -1) { + if (line > dtc_desc.lines || line == -1 || dtc_lstatus[line] & BufDisco) { sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm write invalid %d\n", line); chan_set_notrdy(chan); @@ -434,7 +435,7 @@ t_stat dtc_srv(UNIT * uptr) } if (uptr->u5 & DTC_RD) { - if (line > dtc_desc.lines || line == -1) { + if (line > dtc_desc.lines || line == -1 || dtc_lstatus[line] & BufDisco) { sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm read nothing %d\n", line); chan_set_notrdy(chan); @@ -489,7 +490,7 @@ t_stat dtc_srv(UNIT * uptr) if (dtc_lstatus[line] & BufAbnormal) chan_set_wcflg(chan); if (dtc_ldsc[line].conn == 0) /* connected? */ - dtc_lstatus[line] = BufIRQ|BufNotReady; + dtc_lstatus[line] = BufIRQ|BufAbnormal|BufIRQ|BufIdle; else dtc_lstatus[line] = BufIRQ|BufIdle; dtc_bsize[line] = 0; @@ -524,6 +525,7 @@ dtco_srv(UNIT * uptr) sim_clock_coschedule(uptr, tmxr_poll); ln = tmxr_poll_conn(&dtc_desc); /* look for connect */ if (ln >= 0) { /* got one? */ + dtc_ldsc[ln].rcve = 1; dtc_blimit[ln] = dtc_bufsize-1; dtc_lstatus[ln] = BufIRQ|BufAbnormal|BufWriteRdy; IAR |= IRQ_12; @@ -532,32 +534,43 @@ dtco_srv(UNIT * uptr) /* For each line that is in idle state enable recieve */ for (ln = 0; ln < dtc_desc.lines; ln++) { - if (dtc_ldsc[ln].conn && - (dtc_lstatus[ln] & BufSMASK) == BufIdle) { + dtc_ldsc[ln].rcve = 0; + if (dtc_ldsc[ln].conn && + (dtc_lstatus[ln] & BufSMASK) < BufWrite) { dtc_ldsc[ln].rcve = 1; } } tmxr_poll_rx(&dtc_desc); /* poll for input */ for (ln = 0; ln < DTC_MLINES; ln++) { /* loop thru mux */ /* Check for disconnect */ - if (dtc_ldsc[ln].conn == 0) { /* connected? */ + if (dtc_ldsc[ln].conn == 0 && (dtc_lstatus[ln] & BufDisco) == 0) { /* connected? */ + dtc_ldsc[ln].rcve = 0; switch(dtc_lstatus[ln] & BufSMASK) { - case BufIdle: /* Idle, throw in EOT */ - /* Fall through */ + case BufIdle: /* Idle Flag as disconnected */ + dtc_lstatus[ln] = BufIRQ|BufAbnormal|BufIdle|BufDisco; + dtc_bsize[ln] = 0; + IAR |= IRQ_12; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm disconnect %d idle\n", ln); + break; + case BufWriteRdy: /* Awaiting output, terminate */ dtc_bufptr[ln] = 0; /* Fall through */ case BufInputBusy: /* reading, terminate with EOT */ dtc_buf[ln][dtc_bufptr[ln]++] = 017; - dtc_bsize[ln] = dtc_bufptr[ln]; + dtc_bsize[ln] = dtc_bufptr[ln]+1; dtc_lstatus[ln] = BufIRQ|BufAbnormal|BufReadRdy; IAR |= IRQ_12; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm disconnect %d write\n", ln); break; + case BufOutBusy: /* Terminate Output */ - dtc_lstatus[ln] = BufIRQ|BufIdle; + dtc_lstatus[ln] = BufIRQ|BufIdle|BufAbnormal; dtc_bsize[ln] = 0; IAR |= IRQ_12; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm disconnect %d out\n", ln); break; + default: /* Other cases, ignore until in better state */ break; @@ -568,23 +581,29 @@ dtco_srv(UNIT * uptr) switch(dtc_lstatus[ln] & BufSMASK) { case BufIdle: /* If we have any data to receive */ - if (tmxr_rqln(&dtc_ldsc[ln]) > 0) - dtc_lstatus[ln] = BufInputBusy; - else + if (tmxr_rqln(&dtc_ldsc[ln]) > 0) { + dtc_lstatus[ln] &= ~(BufSMASK); + dtc_lstatus[ln] |= BufInputBusy; + } else break; /* Nothing to do */ sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm recieve %d idle\n", ln); dtc_bufptr[ln] = 0; dtc_bsize[ln] = 0; - case BufInputBusy: - t = 1; - while (t && tmxr_rqln(&dtc_ldsc[ln]) != 0) { - c = tmxr_getc_ln(&dtc_ldsc[ln]) & 0x7f; /* get char */ + /* Fall through */ + + case BufInputBusy: + t = 1; + while (t) { + c = tmxr_getc_ln(&dtc_ldsc[ln]); /* get char */ + if (c == 0) + break; + c &= 0x7f; c1 = ascii_to_con[c]; switch(c) { case '\005': /* ^E ENQ who-are-you */ dtc_lstatus[ln] &= ~(BufSMASK); - dtc_lstatus[ln] |= BufIRQ|BufAbnormal|BufWriteRdy; + dtc_lstatus[ln] |= BufIRQ|BufWriteRdy|BufAbnormal; IAR |= IRQ_12; sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm recieve ENQ %d\n", ln); @@ -786,15 +805,14 @@ t_stat dtc_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc) newln = (int32) get_uint (cptr, 10, DTC_MLINES, &r); if ((r != SCPE_OK) || (newln == dtc_desc.lines)) return r; - if ((newln == 0) || (newln >= DTC_MLINES)) + if ((newln == 0) || (newln > DTC_MLINES)) return SCPE_ARG; - newln--; if (newln < dtc_desc.lines) { - for (i = newln, t = 0; i < dtc_desc.lines; i++) + for (i = newln-1, t = 0; i < dtc_desc.lines; i++) t = t | dtc_ldsc[i].conn; if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) return SCPE_OK; - for (i = newln; i < dtc_desc.lines; i++) { + for (i = newln-1; i < dtc_desc.lines; i++) { if (dtc_ldsc[i].conn) { tmxr_linemsg (&dtc_ldsc[i], "\r\nOperator disconnected line\r\n"); tmxr_send_buffered_data (&dtc_ldsc[i]); diff --git a/B5500/b5500_mt.c b/B5500/b5500_mt.c index e6e98865..55471c77 100644 --- a/B5500/b5500_mt.c +++ b/B5500/b5500_mt.c @@ -570,7 +570,7 @@ mt_attach(UNIT * uptr, CONST char *file) { t_stat r; - if ((r = sim_tape_attach(uptr, file)) != SCPE_OK) + if ((r = sim_tape_attach_ex(uptr, file, 0, 0)) != SCPE_OK) return r; uptr->u5 |= MT_LOADED|MT_BOT; sim_activate(uptr, 50000); diff --git a/B5500/b5500_sys.c b/B5500/b5500_sys.c index 28dfc65e..3eda2654 100644 --- a/B5500/b5500_sys.c +++ b/B5500/b5500_sys.c @@ -136,7 +136,7 @@ const char ascii_to_con[128] = { /* Control */ -1, -1, -1, -1, -1, -1, -1, -1, /*sp ! " # $ % & ' */ - 020, 032, 037, 013, 053, 017, 060, 014, /* 40 - 77 */ + 020, 032, 037, 013, 053, 034, 060, 014, /* 40 - 77 */ /* ( ) * + , - . / */ 075, 055, 054, 072, 033, 040, 073, 021, /* 0 1 2 3 4 5 6 7 */ @@ -150,7 +150,7 @@ const char ascii_to_con[128] = { /* P Q R S T U V W */ 047, 050, 051, 022, 023, 024, 025, 026, /* X Y Z [ \ ] ^ _ */ - 027, 030, 031, 075, 036, 055, 057, 000, + 027, 030, 031, 074, 036, 036, 057, 000, /* ` a b c d e f g */ 035, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ /* h i j k l m n o */ @@ -158,7 +158,7 @@ const char ascii_to_con[128] = { /* p q r s t u v w */ 047, 050, 051, 022, 023, 024, 025, 026, /* x y z { | } ~ del*/ - 027, 030, 031, 057, 077, 017, -1, -1 + 027, 030, 031, 057, 052, 017, -1, -1 }; @@ -341,9 +341,10 @@ t_opcode char_ops[] = { /* Print out an instruction */ void -print_opcode(FILE * of, t_value val, t_opcode * tab) +print_opcode(FILE * of, t_value val, int chr_mode) { uint16 op; + t_opcode *tab = (chr_mode) ? char_ops: word_ops; op = val; while (tab->name != NULL) { @@ -410,14 +411,14 @@ fprint_sym(FILE * of, t_addr addr, t_value * val, UNIT * uptr, int32 sw) fputs(" ", of); for (i = 36; i >= 0; i-=12) { int op = (int)(inst >> i) & 07777; - print_opcode(of, op, word_ops); + print_opcode(of, op, 0); } } if (sw & SWMASK('C')) { /* Char mode opcodes */ fputs(" ", of); for (i = 36; i >= 0; i-=12) { int op = (int)(inst >> i) & 07777; - print_opcode(of, op, char_ops); + print_opcode(of, op, 1); } } if (sw & SWMASK('B')) { /* BCD mode */ diff --git a/B5500/b5500_urec.c b/B5500/b5500_urec.c index 89da43fa..22bda957 100644 --- a/B5500/b5500_urec.c +++ b/B5500/b5500_urec.c @@ -35,10 +35,6 @@ #define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE | MODE_029 #define UNIT_LPR UNIT_ATTABLE | UNIT_DISABLE -/* For Card reader, when set returns end of file at end of deck. */ -/* Reset after sent to system */ -#define MODE_EOF (0x40 << UNIT_V_CARD_MODE) - #define TMR_RTC 0 @@ -84,6 +80,7 @@ DEBTAB cdr_debug[] = { #if NUM_DEVS_CDR > 0 t_stat cdr_boot(int32, DEVICE *); +t_stat cdr_ini(DEVICE *); t_stat cdr_srv(UNIT *); t_stat cdr_attach(UNIT *, CONST char *); t_stat cdr_detach(UNIT *); @@ -92,6 +89,7 @@ const char *cdr_description(DEVICE *dptr); #endif #if NUM_DEVS_CDP > 0 +t_stat cdp_ini(DEVICE *); t_stat cdp_srv(UNIT *); t_stat cdp_attach(UNIT *, CONST char *); t_stat cdp_detach(UNIT *); @@ -106,6 +104,7 @@ struct _lpr_data } lpr_data[NUM_DEVS_LPR]; +t_stat lpr_ini(DEVICE *); t_stat lpr_srv(UNIT *); t_stat lpr_attach(UNIT *, CONST char *); t_stat lpr_detach(UNIT *); @@ -146,16 +145,14 @@ MTAB cdr_mod[] = { {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_card_set_fmt, &sim_card_show_fmt, NULL, "Sets card format"}, - {MODE_EOF, MODE_EOF, "EOF", "EOF", NULL, NULL, NULL, - "Causes EOF to be set when reader empty"}, {0} }; DEVICE cdr_dev = { "CR", cdr_unit, NULL, cdr_mod, NUM_DEVS_CDR, 8, 15, 1, 8, 8, - NULL, NULL, NULL, &cdr_boot, &cdr_attach, &cdr_detach, - NULL, DEV_DISABLE | DEV_DEBUG, 0, cdr_debug, + NULL, NULL, &cdr_ini, &cdr_boot, &cdr_attach, &cdr_detach, + NULL, DEV_DISABLE | DEV_DEBUG | DEV_CARD, 0, cdr_debug, NULL, NULL, &cdr_help, NULL, NULL, &cdr_description }; @@ -176,8 +173,8 @@ MTAB cdp_mod[] = { DEVICE cdp_dev = { "CP", cdp_unit, NULL, cdp_mod, NUM_DEVS_CDP, 8, 15, 1, 8, 8, - NULL, NULL, NULL, NULL, &cdp_attach, &cdp_detach, - NULL, DEV_DISABLE | DEV_DEBUG, 0, cdr_debug, + NULL, NULL, &cdp_ini, NULL, &cdp_attach, &cdp_detach, + NULL, DEV_DISABLE | DEV_DEBUG | DEV_CARD, 0, cdr_debug, NULL, NULL, &cdp_help, NULL, NULL, &cdp_description }; @@ -202,7 +199,7 @@ MTAB lpr_mod[] = { DEVICE lpr_dev = { "LP", lpr_unit, NULL, lpr_mod, NUM_DEVS_LPR, 8, 15, 1, 8, 8, - NULL, NULL, NULL, NULL, &lpr_attach, &lpr_detach, + NULL, NULL, &lpr_ini, NULL, &lpr_attach, &lpr_detach, NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, NULL, NULL, &lpr_help, NULL, NULL, &lpr_description @@ -217,7 +214,7 @@ UNIT con_unit[] = { DEVICE con_dev = { "CON", con_unit, NULL, NULL, NUM_DEVS_CON, 8, 15, 1, 8, 8, - NULL, NULL, con_ini, NULL, NULL, NULL, + NULL, NULL, &con_ini, NULL, NULL, NULL, NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, NULL, NULL, &con_help, NULL, NULL, &con_description @@ -227,6 +224,17 @@ DEVICE con_dev = { #if ((NUM_DEVS_CDR > 0) | (NUM_DEVS_CDP > 0)) +t_stat +cdr_ini(DEVICE *dptr) { + int i; + + for(i = 0; i < NUM_DEVS_CDR; i++) { + cdr_unit[i].u5 = 0; + sim_cancel(&cdr_unit[i]); + } + return SCPE_OK; +} + /* * Device entry points for card reader. * And Card punch. @@ -255,15 +263,13 @@ t_stat card_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) /* Check if we ran out of cards */ if (uptr->u5 & URCSTA_EOF) { /* If end of file, return to system */ - if (uptr->flags & MODE_EOF) { - sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d report eof\n", u, - chan); - chan_set_eof(chan); - uptr->flags &= ~MODE_EOF; + if (sim_card_input_hopper_count(uptr) != 0) + uptr->u5 &= ~URCSTA_EOF; + else { + /* Clear unit ready */ + iostatus &= ~(CARD1_FLAG << u); + return SCPE_UNATT; } - /* Clear unit ready */ - iostatus &= ~(CARD1_FLAG << u); - return SCPE_UNATT; } if (cmd & URCSTA_BINARY) { @@ -307,6 +313,7 @@ t_stat cdr_srv(UNIT *uptr) { int chan = URCSTA_CHMASK & uptr->u5; int u = (uptr - cdr_unit); + uint16 *image = (uint16 *)(uptr->up7); if (uptr->u5 & URCSTA_EOF) { sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d unready\n", u, chan); @@ -319,32 +326,27 @@ cdr_srv(UNIT *uptr) { /* Check if new card requested. */ if (uptr->u4 == 0 && uptr->u5 & URCSTA_ACTIVE && (uptr->u5 & URCSTA_CARD) == 0) { - switch(sim_read_card(uptr)) { - case SCPE_UNATT: + switch(sim_read_card(uptr, image)) { + case CDSE_EMPTY: iostatus &= ~(CARD1_FLAG << u); uptr->u5 &= ~(URCSTA_ACTIVE); iostatus &= ~(CARD1_FLAG << u); chan_set_notrdy(chan); break; - case SCPE_EOF: + case CDSE_EOF: /* If end of file, return to system */ - if (uptr->flags & MODE_EOF) { - sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d set eof\n", u, chan); - chan_set_eof(chan); - uptr->flags &= ~MODE_EOF; - } uptr->u5 &= ~(URCSTA_ACTIVE); uptr->u5 |= URCSTA_EOF; chan_set_notrdy(chan); sim_activate(uptr, 500); break; - case SCPE_IOERR: + case CDSE_ERROR: chan_set_error(chan); uptr->u5 &= ~(URCSTA_ACTIVE); uptr->u5 |= URCSTA_EOF; chan_set_end(chan); break; - case SCPE_OK: + case CDSE_OK: uptr->u5 |= URCSTA_CARD; sim_activate(uptr, 500); break; @@ -356,22 +358,18 @@ cdr_srv(UNIT *uptr) { /* Copy next column over */ if (uptr->u5 & URCSTA_CARD && uptr->u4 < ((uptr->u5 & URCSTA_BIN) ? 160 : 80)) { - struct _card_data *data; uint8 ch = 0; int u = (uptr - cdr_unit); - data = (struct _card_data *)uptr->up7; - if (uptr->u5 & URCSTA_BIN) { - ch = (data->image[uptr->u4 >> 1] >> - ((uptr->u4 & 1)? 0 : 6)) & 077; + ch = (image[uptr->u4 >> 1] >> ((uptr->u4 & 1)? 0 : 6)) & 077; } else { - ch = sim_hol_to_bcd(data->image[uptr->u4]); + ch = sim_hol_to_bcd(image[uptr->u4]); /* Remap some characters from 029 to BCL */ switch(ch) { case 0: ch = 020; break; /* Translate blanks */ case 10: /* Check if 0 punch of 82 punch */ - if (data->image[uptr->u4] != 0x200) { + if (image[uptr->u4] != 0x200) { ch = 0; if (uptr->u4 == 0) chan_set_parity(chan); @@ -439,6 +437,8 @@ cdr_attach(UNIT * uptr, CONST char *file) if ((r = sim_card_attach(uptr, file)) != SCPE_OK) return r; + if (uptr->up7 == 0) + uptr->up7 = malloc(sizeof(uint16)*80); uptr->u5 &= URCSTA_BUSY; uptr->u4 = 0; uptr->u6 = 0; @@ -451,6 +451,9 @@ cdr_detach(UNIT * uptr) { int u = uptr-cdr_unit; + if (uptr->up7 != 0) + free(uptr->up7); + uptr->up7 = 0; iostatus &= ~(CARD1_FLAG << u); return sim_card_detach(uptr); } @@ -462,10 +465,6 @@ cdr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) fprintf (st, "The system supports up to two card readers, the second one is disabled\n"); fprintf (st, "by default. To have the card reader return the EOF flag when the deck\n"); fprintf (st, "has finished reading do:\n"); - fprintf (st, " sim> SET CRn EOF\n"); - fprintf (st, "This flag is cleared each time a deck has been read, so it must be set\n"); - fprintf (st, "again after each deck. MCP does not require this to be set as long as\n"); - fprintf (st, "the deck includes a ?END card\n"); fprint_set_help(st, dptr); fprint_show_help(st, dptr); return SCPE_OK; @@ -484,27 +483,39 @@ cdr_description(DEVICE *dptr) #if NUM_DEVS_CDR > 0 | NUM_DEVS_CDP > 0 /* Handle transfer of data for card punch */ +t_stat +cdp_ini(DEVICE *dptr) { + int i; + + for(i = 0; i < NUM_DEVS_CDP; i++) { + cdp_unit[i].u5 = 0; + sim_cancel(&cdp_unit[i]); + } + return SCPE_OK; +} + t_stat cdp_srv(UNIT *uptr) { int chan = URCSTA_CHMASK & uptr->u5; int u = (uptr - cdp_unit); + uint16 *image = (uint16 *)(uptr->up7); if (uptr->u5 & URCSTA_BUSY) { /* Done waiting, punch card */ if (uptr->u5 & URCSTA_FULL) { sim_debug(DEBUG_DETAIL, &cdp_dev, "cdp %d %d punch\n", u, chan); - switch(sim_punch_card(uptr, NULL)) { - case SCPE_EOF: - case SCPE_UNATT: + switch(sim_punch_card(uptr, image)) { + case CDSE_EOF: + case CDSE_EMPTY: sim_debug(DEBUG_DETAIL, &cdp_dev, "cdp %d %d set eof\n", u, chan); chan_set_eof(chan); break; /* If we get here, something is wrong */ - case SCPE_IOERR: + case CDSE_ERROR: chan_set_error(chan); break; - case SCPE_OK: + case CDSE_OK: break; } uptr->u5 &= ~URCSTA_FULL; @@ -515,18 +526,15 @@ cdp_srv(UNIT *uptr) { /* Copy next column over */ if (uptr->u5 & URCSTA_ACTIVE && uptr->u4 < 80) { - struct _card_data *data; uint8 ch = 0; - data = (struct _card_data *)uptr->up7; - if(chan_read_char(chan, &ch, 0)) { uptr->u5 |= URCSTA_BUSY|URCSTA_FULL; uptr->u5 &= ~URCSTA_ACTIVE; } else { sim_debug(DEBUG_DATA, &cdp_dev, "cdp %d: Char %d < %02o\n", u, uptr->u4, ch); - data->image[uptr->u4++] = sim_bcd_to_hol(ch & 077); + image[uptr->u4++] = sim_bcd_to_hol(ch & 077); } sim_activate(uptr, 10); } @@ -547,16 +555,24 @@ cdp_attach(UNIT * uptr, CONST char *file) if ((r = sim_card_attach(uptr, file)) != SCPE_OK) return r; - uptr->u5 = 0; - iostatus |= PUNCH_FLAG; + if (uptr->up7 == 0) { + uptr->up7 = calloc(80, sizeof(uint16)); + uptr->u5 = 0; + iostatus |= PUNCH_FLAG; + } return SCPE_OK; } t_stat cdp_detach(UNIT * uptr) { + uint16 *image = (uint16 *)(uptr->up7); + if (uptr->u5 & URCSTA_FULL) - sim_punch_card(uptr, NULL); + sim_punch_card(uptr, image); + if (uptr->up7 != 0) + free(uptr->up7); + uptr->up7 = 0; iostatus &= ~PUNCH_FLAG; return sim_card_detach(uptr); } @@ -583,6 +599,16 @@ cdp_description(DEVICE *dptr) /* Line printer routines */ +t_stat +lpr_ini(DEVICE *dptr) { + int i; + + for(i = 0; i < NUM_DEVS_LPR; i++) { + lpr_unit[i].u5 = 0; + sim_cancel(&lpr_unit[i]); + } + return SCPE_OK; +} #if NUM_DEVS_LPR > 0 t_stat diff --git a/doc/b5500_doc.doc b/doc/b5500_doc.doc index fa1cd387ba4e5c3c8c94df11204442e0c4d82035..46818d726c74118a6c5fbd131c9120e3fc8717bc 100644 GIT binary patch delta 12110 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 delta 11972 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 LW^?f{x1#?CfJy5k