/* altairZ80_sio: MITS Altair serial I/O card Written by Peter Schorn, 2001 Based on work by Charles E Owen ((c) 1997, Commercial use prohibited) These functions support a simulated MITS 2SIO interface card. The card had two physical I/O ports which could be connected to any serial I/O device that would connect to a current loop, RS232, or TTY interface. Available baud rates were jumper selectable for each port from 110 to 9600. All I/O is via programmed I/O. Each each has a status port and a data port. A write to the status port can select some options for the device (0x03 will reset the port). A read of the status port gets the port status: +---+---+---+---+---+---+---+---+ | X X X X X X O I | +---+---+---+---+---+---+---+---+ I - A 1 in this bit position means a character has been received on the data port and is ready to be read. O - A 1 in this bit means the port is ready to receive a character on the data port and transmit it out over the serial line. A read to the data port gets the buffered character, a write to the data port writes the character to the device. */ #include #include #include "altairZ80_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #define UNIT_V_ANSI (UNIT_V_UF + 0) /* ANSI mode, strip bit 8 on output */ #define UNIT_ANSI (1 << UNIT_V_ANSI) #define UNIT_V_UPPER (UNIT_V_UF + 1) /* uppper case mode */ #define UNIT_UPPER (1 << UNIT_V_UPPER) #define UNIT_V_BS (UNIT_V_UF + 2) /* map delete to backspace */ #define UNIT_BS (1 << UNIT_V_BS) #define Terminals 1 /* lines per mux */ TMLN TerminalLines[Terminals] = { { 0 } }; /* we only need one line */ TMXR altairTMXR = {Terminals, 0, &TerminalLines[0] }; /* mux descriptor */ t_stat sio_svc (UNIT *uptr); t_stat sio_reset (DEVICE *dptr); t_stat sio_attach (UNIT *uptr, char *cptr); t_stat sio_detach (UNIT *uptr); t_stat ptr_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptp_svc (UNIT *uptr); t_stat ptp_reset (DEVICE *dptr); int32 nulldev(int32 io, int32 data); int32 simh_dev(int32 io, int32 data); int32 sio0d(int32 io, int32 data); int32 sio0s(int32 io, int32 data); int32 sio1d(int32 io, int32 data); int32 sio1s(int32 io, int32 data); void attachCPM(); extern t_stat sim_activate (UNIT *uptr, int32 interval); extern t_stat sim_cancel (UNIT *uptr); extern t_stat sim_poll_kbd (void); extern t_stat sim_putchar (int32 out); extern t_stat attach_unit (UNIT *uptr, char *cptr); extern t_bool rtc_avail; extern FILE *sim_log; extern int32 sim_switches; extern uint32 sim_os_msec (void); extern uint8 M[MAXMEMSIZE]; /* 2SIO Standard I/O Data Structures */ UNIT sio_unit = { UDATA (&sio_svc, UNIT_ATTABLE, 0), KBD_POLL_WAIT }; REG sio_reg[] = { { HRDATA (DATA, sio_unit.buf, 8) }, { HRDATA (STAT, sio_unit.u3, 8) }, { NULL } }; MTAB sio_mod[] = { { UNIT_ANSI, 0, "TTY", "TTY", NULL }, /* keep bit 8 as is for output */ { UNIT_ANSI, UNIT_ANSI, "ANSI", "ANSI", NULL }, /* set bit 8 to 0 before output */ { UNIT_UPPER, 0, "ALL", "ALL", NULL }, /* do not change case of input characters */ { UNIT_UPPER, UNIT_UPPER, "UPPER", "UPPER", NULL }, /* change input characters to upper case */ { UNIT_BS, 0, "BS", "BS", NULL }, /* map delete to backspace */ { UNIT_BS, UNIT_BS, "DEL", "DEL", NULL }, /* map backspace to delete */ { 0 } }; DEVICE sio_dev = { "SIO", &sio_unit, sio_reg, sio_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &sio_reset, NULL, &sio_attach, &sio_detach }; UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ + UNIT_ATTABLE + UNIT_ROABLE, 0), KBD_POLL_WAIT }; REG ptr_reg[] = { { HRDATA (DATA, ptr_unit.buf, 8) }, { HRDATA (STAT, ptr_unit.u3, 8) }, { DRDATA (POS, ptr_unit.pos, 31) }, { NULL } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, NULL, NULL, NULL }; UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ + UNIT_ATTABLE, 0), KBD_POLL_WAIT }; REG ptp_reg[] = { { HRDATA (DATA, ptp_unit.buf, 8) }, { HRDATA (STAT, ptp_unit.u3, 8) }, { DRDATA (POS, ptp_unit.pos, 31) }, { NULL } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, NULL, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL }; t_stat sio_attach (UNIT *uptr, char *cptr) { return tmxr_attach (&altairTMXR, uptr, cptr); /* attach mux */ } /* Detach */ t_stat sio_detach (UNIT *uptr) { sio_unit.u3 = 0x02; /* Status */ sio_unit.buf = 0; /* Data */ return tmxr_detach (&altairTMXR, uptr); } /* Service routines to handle simulator functions */ /* service routine - actually gets char & places in buffer */ t_stat sio_svc (UNIT *uptr) { int32 temp; sim_activate (&sio_unit, sio_unit.wait); /* continue poll */ if (sio_unit.flags & UNIT_ATT) { if (sim_poll_kbd () == SCPE_STOP) { /* listen for ^E */ return SCPE_STOP; } temp = tmxr_poll_conn(&altairTMXR, &sio_unit); /* poll connection */ if (temp >= 0) { altairTMXR.ldsc[temp] -> rcve = 1; /* enable receive */ } tmxr_poll_rx(&altairTMXR); /* poll input */ tmxr_poll_tx(&altairTMXR); /* poll output */ } else { if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) { return temp; /* no char or error? */ } sio_unit.buf = temp & 0xff; /* Save char */ sio_unit.u3 |= 0x01; /* Set status */ } return SCPE_OK; } t_stat ptr_svc (UNIT *uptr) { return SCPE_OK; } t_stat ptp_svc (UNIT *uptr) { return SCPE_OK; } /* Reset routine */ t_stat sio_reset (DEVICE *dptr) { if (sio_unit.flags & UNIT_ATT) { if (altairTMXR.ldsc[0]->conn > 0) { tmxr_reset_ln(altairTMXR.ldsc[0]); } sio_unit.u3 = 0; /* Status */ } else { sio_unit.u3 = 0x02; /* Status */ } sio_unit.buf = 0; /* Data */ sim_activate (&sio_unit, sio_unit.wait); /* activate unit */ return SCPE_OK; } t_stat ptr_reset (DEVICE *dptr) { ptr_unit.buf = 0; ptr_unit.u3 = 0; ptr_unit.pos = 0; if (ptr_unit.flags & UNIT_ATT) { /* attached? */ rewind(ptr_dev.units -> fileref); } sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } t_stat ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; ptp_unit.u3 = 0x02; sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } /* I/O instruction handlers, called from the CPU module when an IN or OUT instruction is issued. Each function is passed an 'io' flag, where 0 means a read from the port, and 1 means a write to the port. On input, the actual input is passed as the return value, on output, 'data' is written to the device. */ int32 sio0s(int32 io, int32 data) { if (io == 0) { /* IN */ if (sio_unit.flags & UNIT_ATT) { sio_unit.u3 = (((tmxr_rqln(altairTMXR.ldsc[0]) > 0 ? 0x01 : 0) | /* read possible if character available */ (altairTMXR.ldsc[0]->conn == 0 ? 0 : 0x02))); /* write possible if connected */ } return (sio_unit.u3); } else { /* OUT */ if (sio_unit.flags & UNIT_ATT) { if (data == 0x03) { /* reset port! */ sio_unit.u3 = 0; sio_unit.buf = 0; } } else { if (data == 0x03) { /* reset port! */ sio_unit.u3 = 0x02; sio_unit.buf = 0; } } return (0); /* ignored since OUT */ } } int32 sio0d(int32 io, int32 data) { if (io == 0) { /* IN */ if (sio_unit.flags & UNIT_ATT) { sio_unit.buf = tmxr_getc_ln(altairTMXR.ldsc[0]) & 0xff; } sio_unit.u3 = sio_unit.u3 & 0xFE; if (sio_unit.flags & UNIT_BS) { if (sio_unit.buf == BACKSPACE_CHAR) { sio_unit.buf = DELETE_CHAR; } } else { if (sio_unit.buf == DELETE_CHAR) { sio_unit.buf = BACKSPACE_CHAR; } } return ((sio_unit.flags & UNIT_UPPER) ? toupper(sio_unit.buf) : sio_unit.buf); } else { /* OUT */ if (sio_unit.flags & UNIT_ANSI) { data &= 0x7f; } if (sio_unit.flags & UNIT_ATT) { tmxr_putc_ln(altairTMXR.ldsc[0], data); } else { sim_putchar(data); } return (0); /* ignored since OUT */ } } /* Port 2 controls the PTR/PTP devices */ int32 sio1s(int32 io, int32 data) { if (io == 0) { /* reset I bit iff PTR unit not attached or no more data available. */ /* O bit is always set since write always possible. */ return ((ptr_unit.flags & UNIT_ATT) == 0) || (ptr_unit.u3 != 0) ? 0x02 : 0x03; } else { /* OUT */ if (data == 0x03) { ptr_unit.u3 = 0; ptr_unit.buf = 0; ptr_unit.pos = 0; ptp_unit.u3 = 0; ptp_unit.buf = 0; ptp_unit.pos = 0; } return (0); /* ignored since OUT */ } } int32 sio1d(int32 io, int32 data) { int32 temp; if (io == 0) { /* IN */ if (((ptr_unit.flags & UNIT_ATT) == 0) || (ptr_unit.u3 != 0)) return (0); /* not attached or no more data available */ if ((temp = getc(ptr_dev.units -> fileref)) == EOF) { /* end of file? */ ptr_unit.u3 = 0x01; return (CONTROLZ_CHAR); /* control Z denotes end of text file in CP/M */ } ptr_unit.pos++; return (temp & 0xFF); } else { /* OUT */ putc(data, ptp_dev.units -> fileref); ptp_unit.pos++; return (0); /* ignored since OUT */ } } int32 nulldev(int32 io, int32 data) { return (io == 0 ? 0xff : 0); } #define splimit 10 #define printTimeCmd 0 #define markTimeCmd 1 #define showTimeCmd 2 #define resetPtrCmd 3 #define attachCmd 4 #define detachCmd 5 #define resetCmd 6 #define cpmCommandLineLength 128 uint32 markTime[splimit]; int32 markTimeSP = 0; char version[] = "SIMH001"; int32 versionPos = 0; /* The CP/M commandline is used as the name of a file and PTR is attached to it */ void attachCPM() { char cpmCommandLine[cpmCommandLineLength]; uint32 i, len = (M[0x80] & 0x7f) - 1; /* 0x80 contains length of commandline, discard first char */ for (i = 0; i < len; i++) { cpmCommandLine[i] = (char)M[0x82+i]; /* the first char, typically ' ', is discarded */ } cpmCommandLine[i] = 0; /* make C string */ sim_switches = SWMASK ('R'); attach_unit(&ptr_unit, cpmCommandLine); } /* port 0xfe is a device for communication SIMH <--> Altair machine */ int32 simh_dev(int32 io, int32 data) { uint32 delta; int32 result; if (io == 0) { /* IN */ result = version[versionPos++]; if (result == 0) { versionPos = 0; } return (result); } else { /* OUT */ switch(data) { case printTimeCmd: /* print time */ if (rtc_avail) { printf("Current time in milliseconds = %d.\n", sim_os_msec ()); if (sim_log) { fprintf(sim_log, "Current time in milliseconds = %d.\n", sim_os_msec ()); } } break; case markTimeCmd: /* mark time */ if (rtc_avail) { if (markTimeSP < splimit) { markTime[markTimeSP++] = sim_os_msec (); } else { printf("Mark stack overflow.\n"); if (sim_log) { fprintf(sim_log, "Mark stack overflow.\n"); } } } break; case showTimeCmd: /* show time difference */ if (rtc_avail) { if (markTimeSP > 0) { delta = sim_os_msec () - markTime[--markTimeSP]; printf("Delta to mark in milliseconds = %d.\n", delta); if (sim_log) { fprintf(sim_log, "Delta to mark in milliseconds = %d.\n", delta); } } else { printf("Missing mark.\n"); if (sim_log) { fprintf(sim_log, "Missing mark.\n"); } } } break; case resetPtrCmd: /* reset ptr device */ ptr_reset(NULL); break; case attachCmd: /* attach ptr to the file with name at beginning of CP/M command line */ attachCPM(); break; case detachCmd: /* detach ptr */ detach_unit(&ptr_unit); break; case resetCmd: versionPos = 0; break; default:; } return 0; /* ignored, since OUT */ } }