| /* | |
| Work derived from Copyright (c) 2004-2012, Robert M. Supnik | |
| Copyright (c) 2013 Holger Veit | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the names of Robert M Supnik and Holger Veit | |
| shall not be used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Robert M Supnik and Holger Veit. | |
| 20130920 hv initial version, moved some code from pdq3_cpu.c | |
| */ | |
| #include "pdq3_defs.h" | |
| /* the memory */ | |
| uint16 M[MAXMEMSIZE]; | |
| /****************************************************************************** | |
| * IO dispatcher | |
| *****************************************************************************/ | |
| static t_bool initio = FALSE; | |
| #define IOSIZE 4096 | |
| #define IOPAGEMASK 0x0fff | |
| IOREAD ioreaders[IOSIZE]; | |
| IOWRITE iowriters[IOSIZE]; | |
| /* I/O devices are implemented this way: | |
| * a unit will register its own I/O addresses together with its handler | |
| * in a hash which allows simple lookup of memory mapped I/O addresses | |
| */ | |
| t_stat pdq3_ioinit() { | |
| int i; | |
| if (!initio) { | |
| for (i=0; i < IOSIZE; i++) { | |
| ioreaders[i] = NULL; | |
| iowriters[i] = NULL; | |
| } | |
| for (i=8; i < 32; i++) | |
| cpu_setIntVec(NIL, i); | |
| initio = TRUE; | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat add_ioh(IOINFO* ioi) { | |
| while (ioi) { | |
| int i; | |
| for (i=0; i<ioi->iosize; i++) { | |
| int idx = (ioi->iobase + i) & IOPAGEMASK; | |
| ioreaders[idx] = ioi->read; | |
| iowriters[idx] = ioi->write; | |
| } | |
| ioi = ioi->next; | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat del_ioh(IOINFO* ioi) { | |
| while (ioi) { | |
| int i; | |
| for (i=0; i<ioi->iosize; i++) { | |
| int idx = (ioi->iobase + i) & IOPAGEMASK; | |
| ioreaders[idx] = NULL; | |
| iowriters[idx] = NULL; | |
| } | |
| ioi = ioi->next; | |
| } | |
| return SCPE_OK; | |
| } | |
| /****************************************************************************** | |
| * configuration | |
| *****************************************************************************/ | |
| t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, CONST void *desc) { | |
| DEVICE* dptr; | |
| DEVCTXT* ctxt; | |
| IOINFO* ioi; | |
| t_bool first = TRUE; | |
| if (!uptr) return SCPE_IERR; | |
| if ((dptr = find_dev_from_unit(uptr)) == 0) return SCPE_IERR; | |
| ctxt = (DEVCTXT*)dptr->ctxt; | |
| ioi = ctxt->ioi; | |
| while (ioi) { | |
| if (ioi->iobase) { | |
| if (ioi->iobase > 0xfc00) { | |
| fprintf(st, first ? "IOBASE=$%04x":",$%04x", ioi->iobase); | |
| first = FALSE; | |
| } | |
| } | |
| ioi = ioi->next; | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat set_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { | |
| t_stat rc; | |
| DEVICE* dptr; | |
| DEVCTXT* ctxt; | |
| IOINFO* ioi; | |
| if (!cptr) return SCPE_ARG; | |
| if (!uptr) return SCPE_IERR; | |
| if ((dptr = find_dev_from_unit(uptr)) == 0) return SCPE_IERR; | |
| ctxt = (DEVCTXT*)dptr->ctxt; | |
| ioi = ctxt->ioi; | |
| if (ioi->next) | |
| return SCPE_ARG; /* note: fixed devices on mainboard cannot be changed */ | |
| ioi->iobase = get_uint(cptr, 16, 0xffff, &rc); | |
| return rc; | |
| } | |
| t_stat set_iovec(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { | |
| t_stat rc; | |
| DEVICE* dptr; | |
| DEVCTXT* ctxt; | |
| IOINFO* ioi; | |
| if (!cptr) return SCPE_ARG; | |
| if (!uptr) return SCPE_IERR; | |
| if ((dptr = find_dev_from_unit(uptr)) == 0) return SCPE_IERR; | |
| ctxt = (DEVCTXT*)dptr->ctxt; | |
| ioi = ctxt->ioi; | |
| if (ioi->next) | |
| return SCPE_ARG; /* note: fixed devices on mainboard cannot be changed */ | |
| ioi->qvector = get_uint(cptr, 16, 0xff, &rc); | |
| return rc; | |
| } | |
| t_stat show_iovec(FILE *st, UNIT *uptr, int value, CONST void *desc) { | |
| DEVICE* dptr; | |
| DEVCTXT* ctxt; | |
| IOINFO* ioi; | |
| t_bool first = TRUE; | |
| if (!uptr) return SCPE_IERR; | |
| if ((dptr = find_dev_from_unit(uptr)) == 0) return SCPE_IERR; | |
| ctxt = (DEVCTXT*)dptr->ctxt; | |
| ioi = ctxt->ioi; | |
| while (ioi) { | |
| if (ioi->qprio < 32) { | |
| fprintf(st, first ? "VECTOR=$%04x":",$%04x", ioi->qvector); | |
| first = FALSE; | |
| } | |
| ioi = ioi->next; | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat set_ioprio(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { | |
| t_stat rc; | |
| DEVICE* dptr; | |
| DEVCTXT* ctxt; | |
| IOINFO* ioi; | |
| if (!cptr) return SCPE_ARG; | |
| if (!uptr) return SCPE_IERR; | |
| if ((dptr = find_dev_from_unit(uptr)) == 0) return SCPE_IERR; | |
| ctxt = (DEVCTXT*)dptr->ctxt; | |
| ioi = ctxt->ioi; | |
| if (ioi->next) | |
| return SCPE_ARG; /* note: fixed devices on mainboard cannot be changed */ | |
| ioi->qprio = get_uint(cptr, 16, 31, &rc); | |
| return rc; | |
| } | |
| t_stat show_ioprio(FILE *st, UNIT *uptr, int value, CONST void *desc) { | |
| DEVICE* dptr; | |
| DEVCTXT* ctxt; | |
| IOINFO* ioi; | |
| t_bool first = TRUE; | |
| if (!uptr) return SCPE_IERR; | |
| if ((dptr = find_dev_from_unit(uptr)) == 0) return SCPE_IERR; | |
| ctxt = (DEVCTXT*)dptr->ctxt; | |
| ioi = ctxt->ioi; | |
| while (ioi) { | |
| if (ioi->qprio < 32) { | |
| fprintf(st, first ? "PRIO=%d":",%d", ioi->qprio); | |
| first = FALSE; | |
| } | |
| ioi = ioi->next; | |
| } | |
| return SCPE_OK; | |
| } | |
| /****************************************************************************** | |
| * central memory handling | |
| *****************************************************************************/ | |
| t_stat Read(t_addr base, t_addr woffset, uint16 *data, uint32 dctrl) { | |
| t_stat rc; | |
| uint16 ea = base + woffset; | |
| /* Note: the PRIAM driver attempts to read the ready bit from FF25 (bit 9) which should be 1. | |
| * As long as we don't have a HDP device, the invalid value should be 0x0000 */ | |
| *data = 0x0000; /* preload invalid data value */ | |
| if (ea < 0xf000 || (ea == 0xfffe && cpu_unit.capac > 65535)) { | |
| *data = M[ea]; /* normal memory */ | |
| rc = SCPE_OK; | |
| } else { | |
| IOREAD reader = ioreaders[ea & IOPAGEMASK]; | |
| rc = reader ? (*reader)(ea, data) : SCPE_NXM; | |
| } | |
| if (rc != SCPE_OK) { | |
| cpu_buserror(); | |
| sim_debug(DBG_CPU_READ, &cpu_dev, DBG_PCFORMAT1 "Invalid Mem read from $%04x\n", DBG_PC, ea); | |
| printf("read buserror: ea=$%04x at $%x:#%x\n",ea,reg_segb,reg_ipc); | |
| return rc; | |
| } | |
| if (dctrl & DBG_CPU_PICK) { | |
| sim_debug(DBG_CPU_PICK, &cpu_dev, DBG_PCFORMAT1 "Pick %04x at SP=$%04x\n", DBG_PC, *data, ea); | |
| } else if (dctrl & DBG_CPU_POP) { | |
| sim_debug(DBG_CPU_POP, &cpu_dev, DBG_PCFORMAT2 "Pop %04x from SP=$%04x\n", DBG_PC, *data, ea); | |
| } else { | |
| sim_debug(dctrl, &cpu_dev, DBG_PCFORMAT2 "Word read %04x from $%04x\n", DBG_PC, *data, ea); | |
| } | |
| return rc; | |
| } | |
| /* read routine that does not generate bus errors, for SIMH Examine | |
| * will read 0x0000 for unknown memory */ | |
| t_stat ReadEx(t_addr base, t_addr woffset, uint16 *data) { | |
| t_stat rc; | |
| uint16 ea = base + woffset; | |
| *data = 0x0000; /* preload invalid data value */ | |
| if (ea < 0xf000) { | |
| *data = M[ea]; /* normal memory */ | |
| rc = SCPE_OK; | |
| } else { | |
| IOREAD reader = ioreaders[ea & IOPAGEMASK]; | |
| rc = reader ? (*reader)(ea, data) : SCPE_NXM; | |
| } | |
| return rc; | |
| } | |
| t_stat Write(t_addr base, t_addr woffset, uint16 data, uint32 dctrl) { | |
| t_stat rc; | |
| uint16 ea = base + woffset; | |
| if (ea < 0xf000) { | |
| M[ea] = data; | |
| rc = SCPE_OK; | |
| } else { | |
| IOWRITE write = iowriters[ea & IOPAGEMASK]; | |
| rc = write ? (*write)(ea, data) : SCPE_NXM; | |
| } | |
| if (rc != SCPE_OK) { | |
| cpu_buserror(); | |
| sim_debug(DBG_CPU_WRITE, &cpu_dev, DBG_PCFORMAT0 "Invalid Mem write to $%04x\n", DBG_PC, ea); | |
| printf("write buserror %x at %x:%x\n",ea,reg_segb,reg_ipc); | |
| //exit(1); | |
| return rc; | |
| } | |
| if (dctrl & DBG_CPU_STACK) | |
| sim_debug(DBG_CPU_PUSH, &cpu_dev, DBG_PCFORMAT1 "Push %04x to SP=$%04x\n", DBG_PC, data, ea); | |
| else | |
| sim_debug(dctrl, &cpu_dev, DBG_PCFORMAT2 "Word write %04x to $%04x\n", DBG_PC, data, ea); | |
| return rc; | |
| } | |
| t_stat ReadB(t_addr base, t_addr boffset, uint16 *data, uint32 dctrl) | |
| { | |
| t_stat rc; | |
| t_addr ea = base + boffset/2; | |
| if ((rc=Read(ea, 0, data, DBG_NONE)) != SCPE_OK) return rc; | |
| if (boffset & 1) | |
| *data >>= 8; | |
| *data &= 0xff; | |
| if (dctrl & DBG_CPU_FETCH) | |
| sim_debug(DBG_CPU_FETCH, &cpu_dev, DBG_PCFORMAT0 "Fetch %02x from SEGB:%04x\n", | |
| DBG_PC, *data, reg_ipc); | |
| else | |
| sim_debug(dctrl, &cpu_dev, DBG_PCFORMAT2 "Byte[%d] read %02x from $%04x\n", | |
| DBG_PC, boffset & 1, *data, ea); | |
| return SCPE_OK; | |
| } | |
| t_stat ReadBEx(t_addr base, t_addr boffset, uint16 *data) | |
| { | |
| t_stat rc; | |
| t_addr ea = base + boffset/2; | |
| if ((rc=ReadEx(ea, 0, data)) != SCPE_OK) return rc; | |
| if (boffset & 1) | |
| *data >>= 8; | |
| *data &= 0xff; | |
| return SCPE_OK; | |
| } | |
| t_stat WriteB(t_addr base, t_addr boffset, uint16 data, uint32 dctrl) | |
| { | |
| uint16 wdata; | |
| t_addr ea = base + boffset/2; | |
| if (ea < 0xfc00) { | |
| sim_debug(dctrl, &cpu_dev, DBG_PCFORMAT2 "Byte[%d] write %02x to $%04x\n", | |
| DBG_PC, boffset & 1, data, ea); | |
| wdata = M[ea]; | |
| } else { | |
| printf(DBG_PCFORMAT0 "Invalid byte[%d] write %02x to I/O addr $%04x\n", DBG_PC, boffset & 1, data, ea); | |
| return STOP_ERRIO; | |
| } | |
| if (boffset & 1) { | |
| wdata = (wdata & 0xff) | (data<<8); | |
| } else { | |
| wdata = (wdata & 0xff00) | (data & 0xff); | |
| } | |
| return Write(ea, 0, wdata, 0); | |
| } | |
| t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
| { | |
| int32 mc; | |
| t_addr i; | |
| if (val < 0 || val > 1) | |
| return SCPE_ARG; | |
| val = val ? 65536 : 32768; | |
| for (mc = 0, i = val; i < memorysize; i++) | |
| mc = mc | M[i]; | |
| if (mc && !get_yn ("Really truncate memory [N]?", FALSE)) | |
| return SCPE_OK; | |
| memorysize = val; | |
| for (i = memorysize; i < MAXMEMSIZE; i++) | |
| M[i] = 0; | |
| return SCPE_OK; | |
| } | |
| t_stat rom_read(t_addr ea, uint16 *data) | |
| { | |
| *data = M[ea]; | |
| return SCPE_OK; | |
| } | |
| t_stat rom_write(t_addr ea, uint16 data) { | |
| M[ea] = data; | |
| return SCPE_OK; | |
| } | |