From 3abd69b166cd0d63afcfa8f1402e804049c46e60 Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Wed, 19 Aug 2020 21:59:27 -0400 Subject: [PATCH] Ridge32: Initial commit. --- Ridge32/ridge32_cpu.c | 1779 +++++++++++++++++++++++++++++++++++++++ Ridge32/ridge32_defs.h | 170 ++++ Ridge32/ridge32_dsk.c | 604 +++++++++++++ Ridge32/ridge32_flp.c | 978 +++++++++++++++++++++ Ridge32/ridge32_iobus.c | 428 ++++++++++ Ridge32/ridge32_sys.c | 821 ++++++++++++++++++ 6 files changed, 4780 insertions(+) create mode 100644 Ridge32/ridge32_cpu.c create mode 100644 Ridge32/ridge32_defs.h create mode 100644 Ridge32/ridge32_dsk.c create mode 100644 Ridge32/ridge32_flp.c create mode 100644 Ridge32/ridge32_iobus.c create mode 100644 Ridge32/ridge32_sys.c diff --git a/Ridge32/ridge32_cpu.c b/Ridge32/ridge32_cpu.c new file mode 100644 index 0000000..befa0e7 --- /dev/null +++ b/Ridge32/ridge32_cpu.c @@ -0,0 +1,1779 @@ +/* Ridge32_cpu.c: Ridge 32 cpu simulator. + + Copyright (c) 2019, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + +*/ + +#include "ridge32_defs.h" /* simulator defns */ + + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy mask */ +#define UNIT_MSIZE (0xf << UNIT_V_MSIZE) +#define MEMAMOUNT(x) (x << UNIT_V_MSIZE) +#define UNIT_V_LDENA (UNIT_V_UF + 4) +#define UNIT_LDENA (0x1 << UNIT_V_LDENA) + +#define TMR_RTC 0 + +#define HIST_MAX 5000000 +#define HIST_MIN 64 +#define HIST_PC 0x1000000 +#define HIST_TRAP 0x2000000 +#define HIST_USER 0x4000000 + +uint32 *M = NULL; +uint32 regs[16]; /* CPU Registers */ +uint32 PC; /* Program counter */ +uint32 sregs[16]; /* Special registers */ +uint32 tlb[32]; /* Translation look aside buffer */ +uint32 vrt[32]; /* VRT address for Modify */ +uint8 user; /* Set when in user mode */ +uint8 wait; /* Wait for interrupt */ +uint8 ext_irq; /* External interrupt pending */ +uint32 trapwd; /* Current trap word */ +uint16 trapcode; /* Current trap code + 0x8000 indicating trap */ +int timer1_irq = 0; /* Timer1 irq */ +int timer2_irq = 0; /* Timer2 irq */ +int boot_sw; /* Boot device */ + +#define TRAP 0x8000 +#define DATAAL 0x8100 /* Data alignment trap */ +#define ILLINS 0x8101 /* Illegal instruction trap */ +#define DBLPRY 0x8102 /* Double bit parity error code fetch - not on simulator */ +#define DBLEXC 0x8103 /* Double bit parity error execute - not on simulator */ +#define PGFLT 0x8104 /* Page fault */ +#define KERVOL 0x8105 /* Kernel Violation */ +#define CHKTRP 0x8106 /* Check trap */ +#define TRPWD 0x8107 /* General trap */ +#define EXTIRQ 0x8108 /* External Interrupt */ +#define SW0IRQ 0x8109 /* Switch 0 Interrupt */ +#define PWRFAL 0x810A /* Power fail - not on simulator */ +#define PWRGLT 0x810B /* Power glitch - not on simulator */ +#define TIMER1 0x810C /* Timer 1 interrupt */ +#define TIMER2 0x810D /* Timer 2 interrupt */ + +#define INTOVR 0x8000 /* Integer overflow */ +#define DIVZER 0x4000 /* Divide by zero */ +#define FPOVER 0x2000 /* Floating point overflow */ +#define FPUNDR 0x1000 /* Floating point underflow */ +#define FPDVZR 0x0800 /* Floating point divide by zero. */ + +#define FMASK 0xffffffff +#define AMASK 0x00ffffff +#define MSIGN 0x80000000 +#define WMASK 0x00fffffe + +int hst_lnt; +int hst_p; +struct InstHistory +{ + t_addr pc; + t_addr addr; + uint32 src1; + uint32 src1h; + uint32 src2; + uint32 dest; + t_value inst[6]; +}; + + +struct InstHistory *hst = NULL; + +/* Forward and external declarations */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cpu_description (DEVICE *dptr); + + +t_bool build_dev_tab (void); + +/* Interval timer option */ +t_stat rtc_srv(UNIT * uptr); +t_stat rtc_reset(DEVICE * dptr); +int32 rtc_tps = 1000; +int32 tmxr_poll; + + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (&rtc_srv, UNIT_IDLE|UNIT_BINK|UNIT_FIX, MAXMEMSIZE), 1000 }; + +REG cpu_reg[] = { + { HRDATA (PC, PC, 24) }, + { HRDATA (R0, regs[00], 32) }, + { HRDATA (R1, regs[01], 32) }, + { HRDATA (R2, regs[02], 32) }, + { HRDATA (R3, regs[03], 32) }, + { HRDATA (R4, regs[04], 32) }, + { HRDATA (R5, regs[05], 32) }, + { HRDATA (R6, regs[06], 32) }, + { HRDATA (R7, regs[07], 32) }, + { HRDATA (R8, regs[010], 32) }, + { HRDATA (R9, regs[011], 32) }, + { HRDATA (R10, regs[012], 32) }, + { HRDATA (R11, regs[013], 32) }, + { HRDATA (R12, regs[014], 32) }, + { HRDATA (R13, regs[015], 32) }, + { HRDATA (R14, regs[016], 32) }, + { HRDATA (R15, regs[017], 32) }, + { BRDATA (R, regs, 16, 32, 16) }, + { HRDATA (SR0, sregs[00], 32) }, + { HRDATA (SR1, sregs[01], 32) }, + { HRDATA (SR2, sregs[02], 32) }, + { HRDATA (SR3, sregs[03], 32) }, + { HRDATA (SR4, sregs[04], 32) }, + { HRDATA (SR5, sregs[05], 32) }, + { HRDATA (SR6, sregs[06], 32) }, + { HRDATA (SR7, sregs[07], 32) }, + { HRDATA (SR8, sregs[010], 32) }, + { HRDATA (SR9, sregs[011], 32) }, + { HRDATA (SR10, sregs[012], 32) }, + { HRDATA (SR11, sregs[013], 32) }, + { HRDATA (SR12, sregs[014], 32) }, + { HRDATA (SR13, sregs[015], 32) }, + { HRDATA (SR14, sregs[016], 32) }, + { HRDATA (SR15, sregs[017], 32) }, + { BRDATA (SR, sregs, 16, 32, 16) }, + { HRDATA (USER, user, 1) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { UNIT_MSIZE, MEMAMOUNT(1), "1M", "1M", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(2), "2M", "2M", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(4), "4M", "4M", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(8), "8M", "8M", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(12), "12M", "12M", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(16), "16M", "16M", &cpu_set_size }, + { UNIT_LDENA, 0, NULL, "NOLOAD", NULL, NULL, NULL, "Turns off load enable switch"}, + { UNIT_LDENA, UNIT_LDENA, "LOAD", "LOAD", NULL, NULL, NULL, "Turns on load enable switch"}, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 16, 24, 1, 16, 8, + &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, dev_debug, +// NULL, NULL, &cpu_help, NULL, NULL, &cpu_description + }; + +/* + * Translate an address from virtual to physical. + */ +int TransAddr(t_addr va, t_addr *pa, uint8 code, uint wr) { + + if (user) { + /* Tlb has virtual address + 12 bit page */ + /* VRT has valid bit, modify, vrt address / 4 */ + uint32 page = va >> 12; + int entry = (page & 0xf) + (code ? 0x10 : 0); + uint32 seg = ((code) ? sregs[8] : sregs[9]) & 0xFFFF; + uint32 mat = (seg << 16) | (va >> 16); + uint32 addr; + /* If not the same, walk through VRT to find correct page */ + if ((vrt[entry] & 0x7000) != 0x7000 || tlb[entry] != mat) { + uint32 ntag = (((seg + page) & sregs[13]) << 3); + uint32 tag; + uint32 link; + do { + tag = (ntag + sregs[12]) >> 2; + addr = M[tag++]; + link = M[tag]; + ntag = (link >> 16); + } while (addr != mat && ntag != 0); + /* Did we find entry? */ + if (addr != mat || (link & 0x7000) != 0x7000) { + /* Nope this is a fault */ + sregs[1] = -1; + sregs[2] = seg; + sregs[3] = va; + trapcode = PGFLT; + sim_debug(DEBUG_EXP, &cpu_dev, + "Page fault: %08x %08x -> %08x %08x %08x\n", seg, va, tag, addr, link); + return 1; + } + /* Update reference and modify bits */ + sim_debug(DEBUG_EXP, &cpu_dev, + "Load Tlb: %08x %08x -> %08x %08x %08x\n", seg, va, tag, addr, link); + link |= 0x8000; + if (wr) + link |= 0x800; + /* Update the tlb entry */ + M[tag] = link; + tag -= sregs[12] >> 2; /* Subtract offset. */ + tag <<= 16; /* Move to upper half */ + tag |= link & 0xFFFF; /* Copy link over */ + vrt[entry] = tag; /* Save for furture */ + tlb[entry] = mat; + addr = link; + } else { + /* Update modify bit if not already set */ + addr = vrt[entry]; /* Tag and flag bits */ + if (wr && (addr & 0x800) == 0) { + uint32 link = (vrt[entry] >> 16) & 0xffff; + link += sregs[12] >> 2; + M[link] |= 0x800; + vrt[entry] |= 0x800; + sim_debug(DEBUG_EXP, &cpu_dev, + "Mod Tlb: %08x %08x -> %08x %08x\n", seg, va, link, vrt[entry]); + } + } + *pa = ((addr & 0x7ff) << 12) | (va & 0xfff); + } else { + *pa = va & 0x7fffff; + } + return 0; +} + +/* + * Read a full word from memory, checking protection + * and alignment restrictions. Return 1 if failure, 0 if + * success. + */ +int ReadFull(t_addr addr, uint32 *data, uint8 code) { + uint32 temp; + t_addr pa; + + /* Check alignment */ + if ((addr & 3) != 0) { + trapcode = DATAAL; + sregs[2] = code ? sregs[8] : sregs[9]; + sregs[3] = addr; + return 1; + } + + /* Validate address */ + if (TransAddr(addr, &pa, code, 0)) + return 1; + + /* Check against memory size */ + if (pa >= MEMSIZE) + return 0; + + /* Actual read */ + pa >>= 2; + *data = M[pa]; + return 0; +} + +int WriteFull(t_addr addr, uint32 data) { + int offset; + t_addr pa; + + /* Check alignment */ + if ((addr & 3) != 0) { + trapcode = DATAAL; + sregs[2] = sregs[9]; + sregs[3] = addr; + return 1; + } + + /* Validate address */ + if (TransAddr(addr, &pa, 0, 1)) + return 1; + + /* Check against memory size */ + if (pa >= MEMSIZE) + return 0; + + /* Actual write */ + pa >>= 2; + M[pa] = data; + return 0; +} + +int WriteHalf(t_addr addr, uint32 data) { + uint32 mask; + t_addr pa; + int offset; + + /* Check alignment */ + if ((addr & 1) != 0) { + trapcode = DATAAL; + sregs[2] = sregs[9]; + sregs[3] = addr; + return 1; + } + + /* Validate address */ + if (TransAddr(addr, &pa, 0, 1)) + return 1; + + /* Check against memory size */ + if (pa >= MEMSIZE) + return 0; + + /* Do actual write. */ + data &= 0xffff; + if (pa & 0x2) { + mask = 0xffff0000; + } else { + data <<= 16; + mask = 0xffff; + } + pa >>= 2; + M[pa] &= mask; + M[pa] |= data; + return 0; +} + +int WriteByte(t_addr addr, uint32 data) { + uint32 mask; + t_addr pa; + int offset; + + /* Validate address */ + if (TransAddr(addr, &pa, 0, 1)) + return 1; + + /* Check against memory size */ + if (pa >= MEMSIZE) + return 0; + + /* Do actual write. */ + offset = 8 * (3 - (pa & 0x3)); + pa >>= 2; + mask = 0xff; + data &= mask; + data <<= offset; + mask <<= offset; + M[pa] &= ~mask; + M[pa] |= data; + return 0; +} + + +t_stat +sim_instr(void) +{ + t_stat reason; + uint32 src1; + uint32 src1h; + uint32 src2; + uint32 src2h; + uint32 dest; /* Results of operation */ + uint32 desth; + uint32 nPC; /* Next PC */ + t_addr disp; /* Computed address for memory ref */ + uint8 op; /* Current opcode */ + uint8 reg1; /* First register */ + uint8 reg2; /* Second register */ + uint16 irq; + int temp, temp2; + uint16 ops[3]; + int code_seg; + + reason = SCPE_OK; + chan_set_devs(); + + while (reason == SCPE_OK) { + +wait_loop: + + if (sim_interval <= 0) { + reason = sim_process_event(); + if (reason != SCPE_OK) + return reason; + } + + if (sim_brk_summ && sim_brk_test(PC, SWMASK('E'))) { + return STOP_IBKPT; + } + +trap: + + /* If in user mode and no PCB, just wait */ + if (user && (sregs[11] == 1 || sregs[14] == 1)) { + sim_interval--; + if (trapcode == 0 && ext_irq == 0) + goto wait_loop; + sim_debug(DEBUG_CMD, &cpu_dev, "Exit wait %4x %d\n", trapcode, ext_irq); + } + + if (trapcode) { + uint32 ccb = sregs[11] >> 2; + if (user) { + sregs[0] = 1; + sregs[15] = PC; + } else { + sregs[0] = PC; + } + PC = M[ccb + (trapcode & 0x1FF)]; + sim_debug(DEBUG_TRAP, &cpu_dev, "Trap %04x\n", trapcode & 0x1FF); + if (hst_lnt) { + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = PC | HIST_TRAP; + hst[hst_p].addr = trapcode << 2; + } + trapcode = 0; + user = 0; + } else if (user && io_rd(&sregs[0])) { + uint32 ccb = sregs[11] >> 2; + sregs[15] = PC; + if (sregs[11] != 1) + PC = M[ccb + (EXTIRQ & 0x1FF)]; + else + PC = 0x3e000; + sim_debug(DEBUG_TRAP, &cpu_dev, "IRQ %08x\n", sregs[0]); + if (hst_lnt) { + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = PC | HIST_TRAP; + hst[hst_p].addr = EXTIRQ << 2; + } + user = 0; + } else if (timer1_irq) { + uint32 ccb = sregs[11] >> 2; + if (user) { + sregs[0] = 1; + sregs[15] = PC; + PC = M[ccb + (TIMER1 & 0x1FF)]; + sim_debug(DEBUG_TRAP, &cpu_dev, "TIMER1\n"); + if (hst_lnt) { + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = PC | HIST_TRAP; + hst[hst_p].addr = TIMER1 << 2; + } + user = 0; + } + timer1_irq = 0; + } else if (timer2_irq) { + uint32 ccb = sregs[11] >> 2; + if (user) { + sregs[0] = 1; + sregs[15] = PC; + PC = M[ccb + (TIMER2 & 0x1FF)]; + sim_debug(DEBUG_TRAP, &cpu_dev, "TIMER2\n"); + if (hst_lnt) { + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = PC | HIST_TRAP; + hst[hst_p].addr = TIMER2 << 2; + } + user = 0; + } + timer2_irq = 0; + } + + if (hst_lnt) { + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = PC | HIST_PC | (user?HIST_USER:0); + } + + /* Fetch the operator and possible displacement */ + if (ReadFull(PC & ~3, &dest, 1)) { + goto trap; + } + nPC = PC + 2; + /* Check which half */ + if (PC & 0x2) { + op = (dest >> 8) & 0xff; + reg1 = (dest >> 4) & 0xf; + reg2 = dest & 0xf; + /* Check if need displacment */ + if (op & 0x80) { + /* In next word */ + if (ReadFull((PC + 2) & ~3, &disp, 1)) { + goto trap; + } + /* Check if short displacement */ + if ((op & 0x10) == 0) + /* Move high half to lower */ + disp = (disp >> 16) & 0xffff; + } + } else { + op = (dest >> 24) & 0xff; + reg1 = (dest >> 20) & 0xf; + reg2 = (dest >> 16) & 0xf; + /* Check if long half of displacment */ + if ((op & 0x90) == 0x90) { + /* Rest in high part of next word */ + if (ReadFull((PC + 4) & ~3, &disp, 1)) { + goto trap; + } + /* Merge current lower and next upper */ + disp >>= 16; + disp |= (dest & 0xffff) << 16; + } else { + disp = dest & 0xffff; + } + } + + /* Print instruction trace in debug log */ + if (sim_deb && (cpu_dev.dctrl & DEBUG_INST)) { + t_value inst[8]; + inst[0] = op; + inst[1] = (reg1 << 4) | reg2; + sim_debug(DEBUG_INST, &cpu_dev, + "R00=%08x R01=%08x R02=%08x R03=%08x\n", + regs[0], regs[1], regs[2], regs[3]); + sim_debug(DEBUG_INST, &cpu_dev, + "R04=%08x R05=%08x R06=%08x R07=%08x\n", + regs[4], regs[5], regs[6], regs[7]); + sim_debug(DEBUG_INST, &cpu_dev, + "R08=%08x R09=%08x R10=%08x R11=%08x\n", + regs[8], regs[9], regs[10], regs[11]); + sim_debug(DEBUG_INST, &cpu_dev, + "R12=%08x R13=%08x R14=%08x R15=%08x\n", + regs[12], regs[13], regs[14], regs[15]); + if (user == 0) { + sim_debug(DEBUG_INST, &cpu_dev, + "SR00=%08x SR01=%08x SR02=%08x SR03=%08x\n", + sregs[0], sregs[1], sregs[2], sregs[3]); + sim_debug(DEBUG_INST, &cpu_dev, + "SR04=%08x SR05=%08x SR06=%08x SR07=%08x\n", + sregs[4], sregs[5], sregs[6], sregs[7]); + sim_debug(DEBUG_INST, &cpu_dev, + "SR08=%08x SR09=%08x SR10=%08x SR11=%08x\n", + sregs[8], sregs[9], sregs[10], sregs[11]); + sim_debug(DEBUG_INST, &cpu_dev, + "SR12=%08x SR13=%08x SR14=%08x SR15=%08x\n", + sregs[12], sregs[13], sregs[14], sregs[15]); + } + sim_debug(DEBUG_INST, &cpu_dev, "PC=%06x %c INST=%02x%02x ", PC, + (user) ? 'u': 'k', inst[0], inst[1]); + if (op & 0x80) { + if (op & 0x10) { + sim_debug(DEBUG_INST, &cpu_dev, "%08x", disp); + inst[2] = (disp >> 24) & 0xff; + inst[3] = (disp >> 16) & 0xff; + inst[4] = (disp >> 8) & 0xff; + inst[5] = disp & 0xff; + } else { + sim_debug(DEBUG_INST, &cpu_dev, "%04x ", disp & 0xffff); + inst[2] = (disp >> 8) & 0xff; + inst[3] = disp & 0xff; + } + } else { + sim_debug(DEBUG_INST, &cpu_dev, " "); + } + sim_debug(DEBUG_INST, &cpu_dev, " "); + fprint_inst(sim_deb, PC & WMASK, inst); + sim_debug(DEBUG_INST, &cpu_dev, "\n"); + } + + /* Check if memory reference */ + if (op & 0x80) { + /* Move high half to lower and extend */ + if (op & 0x10) { + nPC = PC + 6; + } else { + if (disp & 0x8000) + disp |= 0xffff0000; + nPC = PC + 4; + } + } + + if (hst_lnt) { + hst[hst_p].inst[0] = op; + hst[hst_p].inst[1] = (reg1 << 4) | reg2; + if (op & 0x80) { + if (op & 0x10) { + hst[hst_p].inst[2] = (disp >> 24) & 0xff; + hst[hst_p].inst[3] = (disp >> 16) & 0xff; + hst[hst_p].inst[4] = (disp >> 8) & 0xff; + hst[hst_p].inst[5] = disp & 0xff; + } else { + hst[hst_p].inst[2] = (disp >> 8) & 0xff; + hst[hst_p].inst[3] = disp & 0xff; + } + } + } + + /* Load the two registers */ + src1 = regs[reg1]; + if ((op & 0xF0) == 0x10 || (op & 0xF0) == 0x70) + src2 = reg2; + else + src2 = regs[reg2]; + + if (op & 0x80) { /* Memory reference */ + code_seg = (op & 0x60) == 0x60; + + /* Check if indexed load or store or laddr */ + if (op > 0xA0 && (op & 0x81) == 0x81) + disp += regs[reg2]; + + /* Check if code segment access */ + if (((op ^ (op << 1)) & 0x40) == 0) + disp += PC; + } + + if (hst_lnt) { + hst[hst_p].src1 = src1; + hst[hst_p].src2 = src2; + hst[hst_p].addr = disp; + } + + /* Preform opcode */ + switch (op) { + case OP_MOVEI: + case OP_MOVE: + regs[reg1] = src2; + break; + + case OP_NOP: + break; + + case OP_NEG: + if (src2 == MSIGN && trapwd & INTOVR) { + sregs[2] = 16; + trapcode = TRPWD; + } + regs[reg1] = -src2; + break; + + case OP_SUBI: + case OP_SUB: + src2 = -src2; + /* Fall through */ + case OP_ADDI: + case OP_ADD: + dest = src1 + src2; + src1 = (src1 & MSIGN) != 0; + src2 = (src2 & MSIGN) != 0; + if ((src1 && src2 && (dest & MSIGN) == 0) || + (!src1 && !src2 && (dest & MSIGN) != 0)) { + if (trapwd & INTOVR) { + sregs[2] = 16; + trapcode = TRPWD; + } + } + regs[reg1] = dest; + break; + + case OP_ESUB: + src2 = -src2; + /* Fall through */ + case OP_EADD: + temp = regs[0] & 1; + regs[0] = 0; + dest = src1 + src2; + src1 = (src1 & MSIGN) != 0; + src2 = (src2 & MSIGN) != 0; + if ((src1 && src2 && (dest & MSIGN) == 0) || + (!src1 && !src2 && (dest & MSIGN) != 0)) { + regs[0] = 2; + } + if (src1 < src2) { + regs[0] |= 1; + } + if (temp) { + if (dest == FMASK) + regs[0] = 3; + dest++; + } + regs[reg1] = dest; + break; + + case OP_NOTI: + case OP_NOT: + regs[reg1] = ~src2; + break; + + case OP_OR: + regs[reg1] = src1 | src2; + break; + + case OP_XOR: + regs[reg1] = src1 ^ src2; + break; + + case OP_ANDI: + case OP_AND: + regs[reg1] = src1 & src2; + break; + + case OP_EMPY: + case OP_MPYI: + case OP_MPY: + reg2 = 0; + if (src1 & MSIGN) { + reg2 = 1; + src1 = -src1; + } + if (src2 & MSIGN) { + reg2 ^= 1; + src2 = -src2; + } + dest = desth = 0; + if (src1 != 0 && src2 != 0) { + for (temp = 32; temp > 0; temp--) { + if (src1 & 1) + desth += src2; + src1 >>= 1; + dest >>= 1; + if (desth & 1) + dest |= MSIGN; + desth >>= 1; + } + } + if (op != OP_EMPY && desth != 0) { + if (trapwd & INTOVR) { + sregs[2] = 16; + trapcode = TRPWD; + } + } + if (reg2) { + desth ^= FMASK; + dest ^= FMASK; + if (dest == FMASK) + desth ++; + dest++; + } + regs[reg1] = dest; + if (op == OP_EMPY) + regs[(reg1+1) & 0xf] = desth; + break; + + case OP_EDIV: + src1h = regs[(reg1+1) & 0xf]; + if (hst_lnt) { + hst[hst_p].src1h = src1h; + } + if (src2 == 0) { + if (trapwd & DIVZER) { + sregs[2] = 17; + trapcode = TRPWD; + } + break; + } + dest = 0; + for (temp = 0; temp < 32; temp++) { + /* Shift left by one */ + src1 <<= 1; + if (src1h & MSIGN) + src1 |= 1; + src1h <<= 1; + /* Subtract remainder from divisor */ + desth = src1 - src2; + + /* Shift quotent left one bit */ + dest <<= 1; + + /* If remainder larger then divisor replace */ + if ((desth & MSIGN) == 0) { + src1 = desth; + dest |= 1; + } + } + /* Check for overflow */ + if ((dest & MSIGN) != 0) { + if (trapwd & INTOVR) { + sregs[2] = 16; + trapcode = TRPWD; + } + } + regs[reg1] = dest; + regs[reg2] = desth; + break; + + case OP_DIV: + case OP_REM: + if (src2 == 0) { + if (trapwd & DIVZER) { + sregs[2] = 17; + trapcode = TRPWD; + } + break; + } + reg2 = 0; + if (src1 & MSIGN) { + reg2 = 3; + src1 = -src1; + } + src1h = src1; + src1 = 0; + if (src2 & MSIGN) { + reg2 ^= 1; + src2 = -src2; + } + dest = 0; + for (temp = 0; temp < 32; temp++) { + /* Shift left by one */ + src1 <<= 1; + if (src1h & MSIGN) + src1 |= 1; + src1h <<= 1; + /* Subtract remainder to dividend */ + desth = src1 - src2; + + /* Shift quotent left one bit */ + dest <<= 1; + + /* If remainder larger then divisor replace */ + if ((desth & MSIGN) == 0) { + src1 = desth; + dest |= 1; + } + } + /* Check for overflow */ + if ((dest & MSIGN) != 0) { + if (trapwd & INTOVR) { + sregs[2] = 16; + trapcode = TRPWD; + } + goto trap; + } + if (op & 1) { /* REM */ + dest = (reg2 & 2) ? -src1 : src1; + } else if (reg2 & 1) /* DIV */ + dest = -dest; + regs[reg1] = dest; + break; + + case OP_CBIT: + case OP_SBIT: + dest = (MSIGN >> (src2 & 037)); + if (src2 & 040) { + if (op & 1) + regs[(reg1+1)& 0xf] |= dest; + else + regs[(reg1+1)&0xf] &= ~dest; + } else { + if (op & 1) + regs[reg1] |= dest; + else + regs[reg1] &= ~dest; + } + break; + + case OP_TBIT: + dest = (MSIGN >> (src2 & 037)); + if (src2 & 040) { + dest &= regs[(reg1+1)& 0xf]; + } else { + dest &= regs[reg1]; + } + regs[reg1] = dest != 0; + break; + + case OP_CHK: + if ((int32)src1 > (int32)src2) { + sregs[2] = reg1; + sregs[3] = reg2; + trapcode = CHKTRP; + } + break; + + case OP_CHKI: + if (!((src1 & MSIGN) == 0 && src1 <= (int32)src2)) { + sregs[2] = reg1; + sregs[3] = reg2; + trapcode = CHKTRP; + } + break; + + case OP_LCOMP: + if ((int32)src1 < (int32)src2) + dest = -1; + else if (src1 != src2) + dest = 1; + else + dest = 0; + regs[reg1] = dest; + break; + + case OP_DCOMP: + if (src1 == src2) { + src1 = regs[(reg1+1) & 0xf]; + src2 = regs[(reg2+1) & 0xf]; + if ((uint32)src1 < (uint32)src2) + dest = FMASK; + else if (src1 != src2) + dest = 1; + else + dest = 0; + } else if ((int32)src1 < (int32)src2) + dest = FMASK; + else + dest = 1; + regs[reg1] = dest; + break; + + case OP_LSLI: + case OP_LSL: + dest = src1 << (src2 & 037); + regs[reg1] = dest; + break; + + case OP_LSRI: + case OP_LSR: + dest = src1 >> (src2 & 037); + regs[reg1] = dest; + break; + + case OP_ASRI: + case OP_ASR: + dest = (int32)src1 >> (src2 & 037); + regs[reg1] = dest; + break; + + case OP_ASLI: + case OP_ASL: + src2h = src1 & MSIGN; + dest = src1 & ~MSIGN; + src2 &= 037; + while (src2 > 0) { + dest <<= 1; + if ((dest & MSIGN) != src2h && trapwd & INTOVR) { + sregs[2] = 16; + trapcode = TRPWD; + } + src2--; + } + regs[reg1] = dest; + break; + + case OP_DLSRI: + case OP_DLSR: + src2 &= 077; + while(src2 > 0) { + src1h >>= 1; + if (src1 & 1) + src1h |= MSIGN; + src1 >>= 1; + src2--; + } + regs[reg1] = src1; + regs[(reg1 + 1) & 0xf] = src1h; + break; + + case OP_DLSLI: + case OP_DLSL: + src2 &= 077; + while(src2 > 0) { + src1 <<= 1; + if (src1h & MSIGN) + src1 |= 1; + src1h <<= 1; + src2--; + } + regs[reg1] = src1; + regs[(reg1 + 1) & 0xf] = src1h; + break; + + case OP_CSLI: + case OP_CSL: + src2 &= 037; + dest = ((src1 << src2) | (src1 >> (32 - src2))); + regs[reg1] = dest; + break; + + case OP_SEH: + dest = regs[reg2] & 0xffff; + if (dest & 0x8000) + dest |= 0xffff0000; + regs[reg1] = dest; + break; + + case OP_SEB: + dest = regs[reg2] & 0xff; + if (dest & 0x80) + dest |= 0xffffff00; + regs[reg1] = dest; + break; + + case 0x54: /* TESTI > */ + src2 = reg2; + /* Fall through */ + case 0x50: /* TEST > */ + regs[reg1] = ((int32)src1) > ((int32)src2); + break; + + case 0x56: /* TESTI = */ + src2 = reg2; + /* Fall through */ + case 0x52: /* TEST = */ + regs[reg1] = ((int32)src1) == ((int32)src2); + break; + + case 0x55: /* TESTI < */ + src2 = reg2; + /* Fall through */ + case 0x51: /* TEST < */ + regs[reg1] = ((int32)src1) < ((int32)src2); + break; + + case 0x5C: /* TESTI <= */ + src2 = reg2; + /* Fall through */ + case 0x58: /* TEST <= */ + regs[reg1] = ((int32)src1) <= ((int32)src2); + break; + + case 0x5e: /* TESTI <> */ + src2 = reg2; + /* Fall through */ + case 0x5a: /* TEST <> */ + regs[reg1] = ((int32)src1) != ((int32)src2); + break; + + case 0x5d: /* TESTI >= */ + src2 = reg2; + /* Fall through */ + case 0x59: /* TEST >= */ + regs[reg1] = ((int32)src1) >= ((int32)src2); + break; + + case OP_FIXT: /* Fix with truncate to integer */ + case OP_FIXR: /* Fix with round to integer */ + case OP_RNEG: /* Negate real */ + case OP_RADD: /* Real add */ + case OP_RSUB: /* Real subtract */ + case OP_RMPY: /* Real product */ + case OP_RDIV: /* Real divide */ + case OP_FLOAT: /* Make integer real */ + case OP_MAKERD: /* Convert real to double */ + case OP_RCOMP: /* Compare two reals */ + case OP_DFIXT: /* Fix with trancate to integer */ + case OP_DFIXR: /* Fix with round to integer */ + case OP_DRNEG: /* Negate real */ + case OP_DRADD: /* Double add */ + case OP_DRSUB: /* Double subtract */ + case OP_DRMPY: /* Double product */ + case OP_DRDIV: /* Double divide */ + case OP_MAKEDR: /* Convert double to real */ + case OP_DFLOAT: /* Convert integer to double */ + case OP_DRCOMP: /* Compare two doubles */ + break; + + case OP_TRAP: + if ((MSIGN >> reg2) & trapwd) { + trapcode = TRPWD; + sregs[2] = reg2; + } + break; + + case OP_SUS: + if (user) { + goto priv_trap; + } else { + if ((sregs[14] & 0x1) == 0) { + uint32 pcb = sregs[14] >> 2; + do { + M[pcb + reg1] = regs[reg1]; + reg1++; + } while (reg1 <= reg2); + M[pcb + 16] = sregs[15]; + } + } + break; + + case OP_LUS: + if (user) { + goto priv_trap; + } else { + if ((sregs[14] & 0x1) == 0) { + uint32 pcb = sregs[14] >> 2; + do { + regs[reg1] = M[pcb + reg1]; + reg1++; + } while (reg1 <= reg2); + sregs[8] = (M[pcb + 17] >> 16) & 0xFFFF; + sregs[9] = M[pcb + 17] & 0xFFFF; + sregs[10] = M[pcb + 19]; + sregs[15] = M[pcb + 16]; + for (reg1 = 0; + reg1 < (sizeof(tlb)/sizeof(uint32)); + reg1++) + vrt[reg1] = 0; + } + } + break; + + case OP_RUM: + if (user) { + goto priv_trap; + } else { + nPC = sregs[15]; + user = 1; + } + break; + + case OP_LDREGS: + if (user) { + goto priv_trap; + } else { + if ((sregs[14] & 0x1) == 0) { + uint32 pcb = sregs[14] >> 2; + do { + regs[reg1] = M[pcb + reg1]; + reg1++; + } while (reg1 <= reg2); + } + } + break; + + case OP_TRANS: + case OP_DIRT: + if (user) { + goto priv_trap; + } else { + uint32 seg = regs[reg2]; /* Segment */ + uint32 page = regs[(reg2 + 1) & 0xf] >> 12; /* Address = page */ + uint32 mat = (seg << 16) | (page >> 4); /* Match word */ + uint32 na = (((seg + page) & sregs[13]) << 3); /* Next address */ + uint32 a; /* Current address */ + uint32 link; /* Link word */ + uint32 e; /* Entry */ + src1 = regs[(reg2 + 1) & 0xf]; + do { + a = (na + sregs[12]) >> 2; + link = M[a++]; + e = M[a]; + na = (link >> 16); + } while (link != mat && na != 0); + /* Did we find entry? */ + if (link != mat || (e & 0x7000) != 0x7000) { + regs[reg1] = FMASK; + } else { + /* Update reference and modify bits */ + e |= 0x8000; + if ((op & 1) != 0) + e |= 0x800; + M[a] = e; + regs[reg1] = ((e & 0x7ff) << 12) | (src1 & 0xfff); + } + } + break; + + case OP_MOVESR: + if (user) { + goto priv_trap; + } else { + sregs[reg1] = regs[reg2]; + } + break; + + case OP_MOVERS: + if (user) { + goto priv_trap; + } else { + regs[reg1] = sregs[reg2]; + } + break; + + case OP_MAINT: + /* Reg2 determines actual opcode */ + if (user && (sregs[10] & 1) == 0) { +priv_trap: sregs[1] = op; + sregs[2] = reg1; + sregs[3] = reg2; + trapcode = KERVOL; + break; + } + switch (reg2) { + case 0: /* ELOGR */ + /* 1 = Load enable */ + /* 2 = Secondary boot device */ + /* 4 = External interrupt */ + dest = 0x8000 | (ext_irq << 4); + if (cpu_unit.flags & UNIT_LDENA) + dest |= 1; + if (boot_sw != 0) + dest |= 2; + regs[reg1] = dest; + break; + + case 1: /* ELOGW */ + break; + + case 5: /* TWRITED */ + break; + + case 6: /* FLUSH */ + for (reg1 = 0; + reg1 < (sizeof(tlb)/sizeof(uint32)); + reg1++) + vrt[reg1] = 0; + break; + + case 7: /* TRAPEXIT */ + if (user) + goto priv_trap; + nPC = sregs[0]; + break; + + case 8: /* ITEST */ + if (user) + goto priv_trap; + regs[reg1] = !io_rd(®s[(reg1 + 1) & 0xf]); + break; + + case 10: /* MACHINEID */ + regs[reg1] = 0; + break; + + case 11: /* Version */ + case 12: /* CREG */ + case 13: /* RDLOG */ + fprintf(stderr, "Maint %d %d %08x\n\r", reg1, reg2, src1); + regs[reg1] = 0; + break; + + default: + break; + } + break; + + case OP_READ: + if (user && (sregs[10] & 1) == 0) { + goto priv_trap; + } else { + src2 = regs[reg2]; + dest = io_read(src2, ®s[(reg1+1) & 0xf]); + regs[reg1] = dest; + } + break; + + case OP_WRITE: + if (user && (sregs[10] & 1) == 0) { + goto priv_trap; + } else { + src2 = regs[reg2]; + dest = io_write(src2, regs[reg1]); + regs[reg1] = dest; + } + break; + + case OP_KCALL: + if (user) { + trapcode = TRAP | (reg1 << 4) | reg2; + PC = nPC & WMASK; /* Low order bit can't be set */ + } else { + trapcode =KERVOL; + sregs[1] = op; + sregs[2] = reg1; + sregs[3] = reg2; + } + break; + + case OP_RET: + regs[reg1] = nPC; + nPC = src2 & WMASK; + break; + + case OP_CALLR: + regs[reg1] = nPC; + nPC = (PC + src2) & WMASK; + break; + + case 0x93: /* CALL */ + case 0x83: /* CALL */ + regs[reg1] = nPC; + nPC = disp & WMASK; + break; + + case 0x87: /* LOOP */ + case 0x97: /* LOOP */ + dest = src1 + reg2; + if (dest & MSIGN) + nPC = disp & WMASK; + regs[reg1] = dest; + break; + + case 0x8b: /* BR */ + case 0x9b: /* BR */ + nPC = disp & WMASK; + break; + + case 0x80: /* BR > */ + case 0x90: /* BR > */ + if (((int32)src1) > ((int32)src2)) + nPC = disp & WMASK; + break; + + case 0x84: /* BRI > */ + case 0x94: /* BRI > */ + if (((int32)src1) > ((int32)reg2)) + nPC = disp & WMASK; + break; + + case 0x82: /* BR = */ + case 0x92: /* BR = */ + if (((int32)src1) == ((int32)src2)) + nPC = disp & WMASK; + break; + + case 0x86: /* BRI = */ + case 0x96: /* BRI = */ + if (((int32)src1) == ((int32)reg2)) + nPC = disp & WMASK; + break; + + case 0x85: /* BRI < */ + case 0x95: /* BRI < */ + if (((int32)src1) < ((int32)reg2)) + nPC = disp & WMASK; + break; + + case 0x88: /* BR <= */ + case 0x98: /* BR <= */ + if (((int32)src1) <= ((int32)src2)) + nPC = disp & WMASK; + break; + + case 0x8c: /* BRI <= */ + case 0x9c: /* BRI <= */ + if (((int32)src1) <= ((int32)reg2)) + nPC = disp & WMASK; + break; + + case 0x8a: /* BR <> */ + case 0x9a: /* BR <> */ + if (((int32)src1) != ((int32)src2)) + nPC = disp & WMASK; + break; + + case 0x8e: /* BRI <> */ + case 0x9e: /* BRI <> */ + if (((int32)src1) != ((int32)reg2)) + nPC = disp & WMASK; + break; + case 0x8d: /* BRI >= */ + case 0x9d: /* BRI >= */ + if (((int32)src1) >= ((int32)reg2)) + nPC = disp & WMASK; + break; + + case 0xa0: /* StoreB */ + case 0xa1: /* StoreB */ + case 0xb0: /* StoreB */ + case 0xb1: /* StoreB */ + if (WriteByte(disp, src1)) + break; + break; + + case 0xa2: /* StoreH */ + case 0xa3: /* StoreH */ + case 0xb2: /* StoreH */ + case 0xb3: /* StoreH */ + /* Check if we handle unaligned access */ + if (WriteHalf(disp, src1)) + break; + break; + + case 0xa6: /* Store */ + case 0xa7: /* Store */ + case 0xb6: /* Store */ + case 0xb7: /* Store */ + if (WriteFull(disp, src1)) + break; + break; + + case 0xa8: /* StoreD */ + case 0xa9: /* StoreD */ + case 0xb8: /* StoreD */ + case 0xb9: /* StoreD */ + /* Check if we handle unaligned access */ + src1h = regs[(reg1 + 1) & 0xf]; + if (hst_lnt) { + hst[hst_p].src2 = src1h; + } + if (WriteFull(disp, src1)) + break; + if (WriteFull(disp+4, src1h)) + break; + break; + + case 0xe0: /* LoadB */ + case 0xe1: /* LoadB */ + case 0xf0: /* LoadB */ + case 0xf1: /* LoadB */ + case 0xc0: /* LoadB */ + case 0xc1: /* LoadB */ + case 0xd0: /* LoadB */ + case 0xd1: /* LoadB */ + dest = 0; + if (ReadFull(disp & ~(3), &dest, code_seg)) + break; + dest >>= 8 * (3 - (disp & 0x3)); + regs[reg1] = dest & 0xff; + break; + + case 0xe2: /* LoadH */ + case 0xe3: /* LoadH */ + case 0xf2: /* LoadH */ + case 0xf3: /* LoadH */ + case 0xc2: /* LoadH */ + case 0xc3: /* LoadH */ + case 0xd2: /* LoadH */ + case 0xd3: /* LoadH */ + /* Check if we handle unaligned access */ + dest = 0; + if ((disp & 1) != 0) { + trapcode = DATAAL; + sregs[2] = code_seg ? sregs[8] : sregs[9]; + sregs[3] = disp; + break; + } + if (ReadFull(disp & ~(3), &dest, code_seg)) + break; + if ((disp & 2) == 0) + dest >>= 16; + regs[reg1] = dest & 0xffff; + break; + + case 0xe6: /* Load */ + case 0xe7: /* Load */ + case 0xf6: /* Load */ + case 0xf7: /* Load */ + case 0xc6: /* Load */ + case 0xc7: /* Load */ + case 0xd6: /* Load */ + case 0xd7: /* Load */ + dest = 0; + if (ReadFull(disp & ~(0x3), &dest, code_seg)) + break; + regs[reg1] = dest; + break; + + case 0xe8: /* LoadD */ + case 0xe9: /* LoadD */ + case 0xf8: /* LoadD */ + case 0xf9: /* LoadD */ + case 0xc8: /* LoadD */ + case 0xc9: /* LoadD */ + case 0xd8: /* LoadD */ + case 0xd9: /* LoadD */ + dest = 0; + desth = 0; + if (ReadFull(disp, &dest, code_seg)) + break; + if (ReadFull(disp+4, &desth, code_seg)) + break; + regs[reg1] = dest; + regs[(reg1+1) &0xf] = desth; + if (hst_lnt) { + hst[hst_p].src1 = dest; + hst[hst_p].src2 = desth; + } + break; + + case 0xee: /* Laddr */ + case 0xef: /* Laddr */ + case 0xfe: /* Laddr */ + case 0xff: /* Laddr */ + case 0xce: /* Laddr */ + case 0xcf: /* Laddr */ + case 0xde: /* Laddr */ + case 0xdf: /* Laddr */ + regs[reg1] = disp; + break; + + default: +ill_inst: + trapcode = ILLINS; + sregs[1] = op; + sregs[2] = reg1; + if (op & 0x80) + sregs[3] = disp; + else + sregs[3] = reg2; + } + + if (trapcode == 0) + PC = nPC & WMASK; /* Low order bit can't be set */ + if (hst_lnt) { + hst[hst_p].dest = regs[reg1]; + } + sim_interval--; + + } + PC = nPC & WMASK; /* Low order bit can't be set */ +} + + +/* Reset */ + +t_stat +cpu_reset (DEVICE *dptr) +{ + if (M == NULL) { /* first time init? */ + sim_brk_types = sim_brk_dflt = SWMASK ('E'); + M = (uint32 *) calloc (((uint32) MEMSIZE) >> 2, sizeof (uint32)); + if (M == NULL) + return SCPE_MEM; + } + sregs[2] = MEMSIZE; + sregs[4] = 0xff; + sregs[11] = 1; + sregs[14] = 1; + trapcode = 0; + timer1_irq = timer2_irq = 0; + ext_irq = 0; + sim_rtcn_init_unit (&cpu_unit, cpu_unit.wait, TMR_RTC); + sim_activate(&cpu_unit, cpu_unit.wait); + return SCPE_OK; +} + +void +cpu_boot (int sw) +{ + sregs[2] = MEMSIZE; + sregs[4] = 0xff; + sregs[11] = 1; + sregs[14] = 1; + timer1_irq = timer2_irq = 0; + user = 1; + boot_sw = sw; + trapcode = 0; + ext_irq = 0; +} + + +/* Interval timer routines */ +t_stat +rtc_srv(UNIT * uptr) +{ + int32 t; + t = sim_rtcn_calb (rtc_tps, TMR_RTC); + sim_activate_after(uptr, 1000000/rtc_tps); + tmxr_poll = t/2; + if ((sregs[11] & 1) == 0) { + uint32 ccb = sregs[11] >> 2; + uint32 s; + if ((sregs[14] & 1) == 0) { + M[(sregs[14] + 0x80) >> 2] ++; + } else { + M[ccb + 0x10F]++; + } + M[ccb + 0x110]--; + M[ccb + 0x111]--; + if ((M[ccb + 0x110] & MSIGN) != 0) /* Timer 1 */ + timer1_irq = 1; + if ((M[ccb + 0x111] & MSIGN) != 0) /* Timer 2 */ + timer2_irq = 1; + s = M[ccb + 0x113] + 1000000; /* Bump by 1 ns */ + if (s < M[ccb + 0x113]) + M[ccb + 0x112] ++; + M[ccb + 0x113] = s; + sim_debug(DEBUG_EXP, &cpu_dev, "Timer: %08x t1=%08x t2=%08x d=%08x %08x\n", + M[ccb + 0x10F], M[ccb + 0x110], M[ccb + 0x111], M[ccb + 0x112], M[ccb+0x113]); + } + return SCPE_OK; +} + + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 addr = (uint32) exta; +uint32 byte; +uint32 offset = 8 * (3 - (addr & 0x3)); + +if (vptr == NULL) + return SCPE_ARG; +/* Ignore high order bits */ +addr &= AMASK; +if (addr >= MEMSIZE) + return SCPE_NXM; +addr >>= 2; +byte = M[addr] >> offset; +byte &= 0xff; +*vptr = byte; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 addr = (uint32) exta; +uint32 offset = 8 * (3 - (addr & 0x3)); +uint32 word; +uint32 mask; + +/* Ignore high order bits */ +addr &= AMASK; +if (addr >= MEMSIZE) + return SCPE_NXM; +addr >>= 2; +mask = 0xff << offset; +word = M[addr]; +word &= ~mask; +word |= (val & 0xff) << offset; +M[addr] = word; +return SCPE_OK; +} + +/* Memory allocation */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 mc = 0; +int32 i, clim; +uint32 *nM = NULL; +int32 max = MEMSIZE >> 2; + +val = val >> UNIT_V_MSIZE; +val = 1024 * 1024 * val; +if ((val <= 0) || (val > MAXMEMSIZE)) + return SCPE_ARG; +for (i = val>>2; i < max; i++) + mc = mc | M[i]; +if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) + return SCPE_OK; +nM = (uint32 *) calloc (val >> 2, sizeof (uint32)); +if (nM == NULL) + return SCPE_MEM; +clim = (val < MEMSIZE)? val >> 2: max; +for (i = 0; i < clim; i++) + nM[i] = M[i]; +free (M); +M = nM; +fprintf(stderr, "Mem size=%x\n\r", val); +MEMSIZE = val; +cpu_unit.flags &= ~UNIT_MSIZE; +cpu_unit.flags |= (val / (1024 * 1024)) << UNIT_V_MSIZE; +reset_all (0); +return SCPE_OK; +} + +/* Handle execute history */ + +/* Set history */ +t_stat +cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 i, lnt; + t_stat r; + + if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } + lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r); + if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; + hst_p = 0; + if (hst_lnt) { + free(hst); + hst_lnt = 0; + hst = NULL; + } + if (lnt) { + hst = (struct InstHistory *)calloc(sizeof(struct InstHistory), lnt); + + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + } + return SCPE_OK; +} + +/* Show history */ + +t_stat +cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc) +{ + int32 k, di, lnt; + const char *cptr = (const char *) desc; + t_stat r; + t_value sim_eval; + struct InstHistory *h; + + if (hst_lnt == 0) + return SCPE_NOFNC; /* enabled? */ + if (cptr) { + lnt = (int32) get_uint(cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } else + lnt = hst_lnt; + di = hst_p - lnt; /* work forward */ + if (di < 0) + di = di + hst_lnt; + fprintf(st, "PC A1 D1 D2 RESULT\n\n"); + for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + int i; + fprintf(st, "%06x%c %06x %08x %08x %08x %02x%02x ", + h->pc & AMASK, (h->pc & HIST_USER) ? 'v':' ', + h->addr & AMASK, + h->src1, h->src2, h->dest, h->inst[0], h->inst[1]); + if ((h->inst[0] & 0x80) != 0) + fprintf(st, "%02x%02x ", h->inst[2], h->inst[3]); + else + fprintf(st, " "); + if ((h->inst[0] & 0x90) == 0x90) + fprintf(st, "%02x%02x ", h->inst[4], h->inst[5]); + else + fprintf(st, " "); + fprintf(st, " "); + fprint_inst(st, h->pc & AMASK, h->inst); + fputc('\n', st); /* end line */ + } /* end else instruction */ + else if (h->pc & HIST_TRAP) { /* Trap */ + int i; + fprintf(st, "%06x %06x\n", + h->pc & AMASK, h->addr & AMASK); + } /* end else trap */ + } /* end for */ + return SCPE_OK; +} + + +t_stat +cpu_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf(st, "Ridge 32 CPU\n\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +cpu_description (DEVICE *dptr) +{ + return "Ridge 32 CPU"; +} + diff --git a/Ridge32/ridge32_defs.h b/Ridge32/ridge32_defs.h new file mode 100644 index 0000000..160a174 --- /dev/null +++ b/Ridge32/ridge32_defs.h @@ -0,0 +1,170 @@ +/* ridge32_defs.h: Ridge 32 simulator definitions + + Copyright (c) 2019, Richard Cornwell + + 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. + +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ +#define STOP_HALT 1 /* halted */ +#define STOP_IBKPT 2 /* breakpoint */ + +/* Conditional error returns */ + +/* Memory */ +#define MAXMEMSIZE (16*1024*1024) /* max memory size */ +#define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((x)) < MEMSIZE) +extern uint32 *M; + + +/* Opcode definitions */ +#define OP_MOVE 0x01 +#define OP_NEG 0x02 +#define OP_ADD 0x03 +#define OP_SUB 0x04 +#define OP_MPY 0x05 +#define OP_DIV 0x06 +#define OP_REM 0x07 +#define OP_NOT 0x08 +#define OP_OR 0x09 +#define OP_XOR 0x0A +#define OP_AND 0x0B +#define OP_CBIT 0x0C +#define OP_SBIT 0x0D +#define OP_TBIT 0x0E +#define OP_CHK 0x0F +#define OP_NOP 0x10 +#define OP_MOVEI 0x11 +#define OP_ADDI 0x13 +#define OP_SUBI 0x14 +#define OP_MPYI 0x15 +#define OP_NOTI 0x18 +#define OP_ANDI 0x1B +#define OP_CHKI 0x1F +#define OP_FIXT 0x20 +#define OP_FIXR 0x21 +#define OP_RNEG 0x22 +#define OP_RADD 0x23 +#define OP_RSUB 0x24 +#define OP_RMPY 0x25 +#define OP_RDIV 0x26 +#define OP_MAKERD 0x27 +#define OP_LCOMP 0x28 +#define OP_FLOAT 0x29 +#define OP_RCOMP 0x2A +#define OP_EADD 0x2C +#define OP_ESUB 0x2D +#define OP_EMPY 0x2E +#define OP_EDIV 0x2F +#define OP_DFIXT 0x30 +#define OP_DFIXR 0x31 +#define OP_DRNEG 0x32 +#define OP_DRADD 0x33 +#define OP_DRSUB 0x34 +#define OP_DRMPY 0x35 +#define OP_DRDIV 0x36 +#define OP_MAKEDR 0x37 +#define OP_DCOMP 0x38 +#define OP_DFLOAT 0x39 +#define OP_DRCOMP 0x3A +#define OP_TRAP 0x3B +#define OP_SUS 0x40 +#define OP_LUS 0x41 +#define OP_RUM 0x42 +#define OP_LDREGS 0x43 +#define OP_TRANS 0x44 +#define OP_DIRT 0x45 +#define OP_MOVESR 0x46 +#define OP_MOVERS 0x47 +#define OP_MAINT 0x4C +#define OP_READ 0x4E +#define OP_WRITE 0x4F +#define OP_CALLR 0x53 +#define OP_RET 0x57 +#define OP_KCALL 0x5B +#define OP_LSL 0x60 +#define OP_LSR 0x61 +#define OP_ASL 0x62 +#define OP_ASR 0x63 +#define OP_DLSL 0x64 +#define OP_DLSR 0x65 +#define OP_CSL 0x68 +#define OP_SEB 0x6A +#define OP_LSLI 0x70 +#define OP_LSRI 0x71 +#define OP_ASLI 0x72 +#define OP_ASRI 0x73 +#define OP_DLSLI 0x74 +#define OP_DLSRI 0x75 +#define OP_CSLI 0x78 +#define OP_SEH 0x7A + +/* Device context block */ +struct ridge_dib { + uint8 dev_num; /* device address */ + uint8 slot_num; /* Slot number */ + int (*io_read)(uint32 dev, uint32 *data); + int (*io_write)(uint32 dev, uint32 data); + int (*io_iord)(uint32 *data); +}; + +typedef struct ridge_dib DIB; + +/* Debuging controls */ +#define DEBUG_CMD 0x0000001 /* Show device commands */ +#define DEBUG_DATA 0x0000002 /* Show data transfers */ +#define DEBUG_DETAIL 0x0000004 /* Show details */ +#define DEBUG_EXP 0x0000008 /* Show error conditions */ +#define DEBUG_TRAP 0x0000010 /* Show Trap requests */ +#define DEBUG_INST 0x0000020 /* Show instruction execution */ + +#define DCB u3 /* DCB pointer */ + +extern DEBTAB dev_debug[]; +extern DEVICE cpu_dev; +extern DEVICE flp_dev; +extern DEVICE dsk_dev; +extern uint8 ext_irq; +extern UNIT cpu_unit; +extern int32 tmxr_poll; + +void cpu_boot(int); +uint8 io_dcbread_byte(UNIT *uptr, int off); +uint16 io_dcbread_half(UNIT *uptr, int off); +uint32 io_dcbread_addr(UNIT *uptr, int off); +void io_dcbread_blk(UNIT *uptr, int off, uint8 *data, int sz); +void io_read_blk(int addr, uint8 *data, int sz); +void io_dcbwrite_byte(UNIT *uptr, int off, uint8 data); +void io_dcbwrite_half(UNIT *uptr, int off, uint16 data); +void io_dcbwrite_blk(UNIT *uptr, int off, uint8 *data, int sz); +void io_write_blk(int addr, uint8 *data, int sz); +int io_read(uint32 dev_data, uint32 *data); +int io_write(uint32 dev_data, uint32 data); +int io_rd(uint32 *data); +t_stat chan_set_devs(); +t_stat set_dev_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc); +t_stat show_dev_addr(FILE * st, UNIT * uptr, int32 v, CONST void *desc); +t_stat set_slot_num(UNIT * uptr, int32 val, CONST char *cptr, void *desc); +t_stat show_slot_num(FILE * st, UNIT * uptr, int32 v, CONST void *desc); +void fprint_inst(FILE *, t_addr addr, t_value *); + diff --git a/Ridge32/ridge32_dsk.c b/Ridge32/ridge32_dsk.c new file mode 100644 index 0000000..0ee4a4e --- /dev/null +++ b/Ridge32/ridge32_dsk.c @@ -0,0 +1,604 @@ +/* Ridge32_dsk.c: Ridge 32 Priam disk controller. + + Copyright (c) 2020, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + + +*/ + +#include "ridge32_defs.h" + +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ +#define UNIT_M_DTYPE 1 +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) + + +#define DCB u3 /* DCB pointer */ +#define STATUS u4 /* Last status */ +#define CYL u5 /* Current cylinder */ +#define CMD u6 + + +#define WR_INH 0x8000 +#define DSK_RD 0 /* Read command */ +#define DSK_WR 1 /* Write command */ +#define DSK_VFY 2 /* Verify data */ +#define DSK_FMT 3 /* Format a track */ +#define DSK_SEEK 4 /* Seek to cylinder */ +#define DSK_RDH 5 /* Read Highest sector address */ +#define DSK_RDF 6 /* Read full sector */ +#define DSK_WRF 7 /* Write full sector */ +#define DSK_HDR 0xE /* Read headers */ + +/* DCB control block. + + DCB + 0C0 + + 0 GORDER + 0 - Read + 1 - Write + 2 - Verify + 3 - Format track + 4 - Seek + 5 - Return highest sector address. + 6 - Read full Sector. + 7 - Write full sector. + + 1 SORDER Not used. + + 2 GSTAT + 0 - Ok. + 1 - Not Ready. + 2 - Timeout + 3 - Equipment Fault. + 4 - Write protect + 5 - Ridge double bit error. + 6 - Data overrun + 7 - Missing AM. + 8 - Can't find header. + 9 - header CRC + A - data CRC + B - Seek error. + FF - Illegal parameter in DCB order. + + 3 SSTAT reserved. + + 4 RETRIES + + 5 RIDGE ADDR + 6 + 7 + + 8 BYTE COUNT + 9 + + A BYTE COUNT READ. + B + + C Not used + D Header 4(bits), Cylinder (4bits). + E Cylinder + F Sector 0-17 + 10 -1B Data Labels + */ + + +#define SECT_SZ 1024 +#define LBL_SZ 12 + +#define P142_DTYPE 0 +#define P60_DTYPE 1 + +struct _dsk_type { + uint32 cyl; + uint16 hds; + uint16 sect; + uint16 bpt; + char *model; +} dsk_type[] = { + {1121, 7, 18, 20160, "Priam142"}, + {1121, 3, 18, 20160, "Priam60"}, + {0, 0, 0, 0, NULL}, +}; + + +int dsk_read(uint32 dev, uint32 *data); +int dsk_write(uint32 dev, uint32 data); +int dsk_iord(uint32 *data); +void dsk_start(UNIT *uptr, int drive); +t_stat dsk_svc (UNIT *uptr); +t_stat dsk_boot (int32, DEVICE *); +t_stat dsk_set_type(UNIT *, int32, CONST char *, void *); +t_stat dsk_attach(UNIT *uptr, CONST char *cptr); +t_stat dsk_detach(UNIT *uptr); +t_stat dsk_reset(DEVICE *dp); + +uint8 dsk_sect_lab[LBL_SZ]; +uint8 dsk_buf[SECT_SZ]; + +/* Device context block */ +struct ridge_dib dsk_dib = {2, 2, dsk_read, dsk_write, dsk_iord}; + +MTAB dsk_mod[] = { + {UNIT_DTYPE, (P142_DTYPE << UNIT_V_DTYPE), "P142", "P142", &dsk_set_type }, + {UNIT_DTYPE, (P60_DTYPE << UNIT_V_DTYPE), "P60", "P60", &dsk_set_type }, + {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "SLOT", "SLOT", &set_slot_num, + &show_slot_num, NULL}, + {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL}, + { 0 } + }; + + +UNIT dsk_unit[] = { + {UDATA(&dsk_svc, UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX | + (P142_DTYPE << UNIT_V_DTYPE), 0)}, + {UDATA(&dsk_svc, UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX | + (P142_DTYPE << UNIT_V_DTYPE), 0)}, + {UDATA(&dsk_svc, UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX | + (P142_DTYPE << UNIT_V_DTYPE), 0)}, + {UDATA(&dsk_svc, UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX | + (P142_DTYPE << UNIT_V_DTYPE), 0)}, +}; + + +DEVICE dsk_dev = { + "DSK", dsk_unit, NULL, dsk_mod, + 4, 16, 24, 1, 16, 8, + NULL, NULL, &dsk_reset, &dsk_boot, &dsk_attach, &dsk_detach, + &dsk_dib, DEV_DEBUG, 0, dev_debug, NULL, NULL, +}; + + + + +struct _dcb { + uint8 retries; + uint32 addr; /* transfer address */ + uint16 count; /* byte count */ + uint16 xcount; /* transfer count */ + uint8 hd; /* Head */ + uint16 cyl; /* Cylinder */ + uint8 sect; /* Sector */ +} dsk_dcb[4]; + + +int +dsk_read(uint32 dev, uint32 *data) +{ + UNIT *uptr = &dsk_unit[0]; + + *data = uptr->STATUS & 0x03FF00; + *data |= (dsk_dib.dev_num << 24) & 0xff000000; + *data |= 0x400000; + sim_debug(DEBUG_EXP, &dsk_dev, "read status %8x\n\r", *data); + return (uptr->STATUS & 0x2) ? 1 : 0; +} + +int +dsk_write(uint32 dev, uint32 data) +{ + UNIT *uptr = &dsk_unit[0]; + UNIT *duptr; + int cmd = (data >> 24) & 0xff; + int drive = cmd & 3; + int offset = drive << 6;; + int i; + + /* Check if command can be accepted */ + if (uptr->STATUS & 1) { + return 1; + } + sim_debug(DEBUG_CMD, &dsk_dev, "Dsk start %02x\n", cmd); + switch (cmd) { + case 0x80: + case 0x81: + case 0x82: + case 0x83: + + /* Find actual unit */ + duptr = &dsk_unit[drive]; + /* Read first word of DCB + 0xC0 */ + duptr->CMD &= WR_INH; + duptr->CMD |= io_dcbread_byte(uptr, offset); + dsk_dcb[drive].addr = io_dcbread_addr(uptr, offset + 0x5); + dsk_dcb[drive].count = io_dcbread_half(uptr, offset + 0x8); + dsk_dcb[drive].xcount = 0; + dsk_dcb[drive].hd = io_dcbread_byte(uptr, offset + 0xd); + dsk_dcb[drive].cyl = io_dcbread_byte(uptr, offset + 0xe); + dsk_dcb[drive].sect = io_dcbread_byte(uptr, offset + 0xf); + dsk_dcb[drive].cyl |= (dsk_dcb[drive].hd & 0xf) << 4; + dsk_dcb[drive].hd = (dsk_dcb[drive].hd >> 4) & 0xf; + + cmd = duptr->CMD & 0xf; + sim_debug(DEBUG_DETAIL, &dsk_dev, + "Start Disk %2x %6x %4x c=%4d h=%d s=%2d\n", + duptr->CMD, dsk_dcb[drive].addr, dsk_dcb[drive].count, + dsk_dcb[drive].cyl, dsk_dcb[drive].hd, dsk_dcb[drive].sect); + if (cmd > 7 && cmd != 0xe) { + /* Invalid */ + io_dcbwrite_byte(uptr, offset + 0x2, 0xff); + uptr->STATUS = 0x40FF01 | (drive << 16); + ext_irq = 1; + return 0; + } + sim_activate(duptr, 100); + break; + + case 0xc0: /* Boot floppy left */ + (void)dsk_boot(0, &dsk_dev); + break; + + case 0xc1: /* Update DCB */ + uptr->DCB = (M[0x3c13c >> 2] & 0xffff) << 8; + uptr->STATUS = 0x400001; + ext_irq = 1; + break; + + case 0xc2: /* Set write inhibit */ + uptr->CMD |= WR_INH; + uptr->STATUS = 0x400001; + ext_irq = 1; + break; + + case 0xc3: /* Clear write inhibit */ + uptr->CMD &= ~WR_INH; + uptr->STATUS = 0x400001; + ext_irq = 1; + break; + + case 0xc4: /* Update DCB */ + M[0x3c13c >> 2] &= 0xffff0000; + M[0x3c13c >> 2] = (uptr->DCB >> 8) & 0xffff; + uptr->STATUS = 0x400001; + ext_irq = 1; + break; + } + return 0; +} + +int +dsk_iord(uint32 *data) +{ + UNIT *uptr = &dsk_unit[0]; + *data = uptr->STATUS & 0x003FF00; + *data |= ((uint32)dsk_dib.dev_num) << 24; + *data |= 0x400000; + /* Check if irq pending */ + if (uptr->STATUS & 1) { + uptr->STATUS &= ~1; + return 1; + } + return 0; +} + + +int +dsk_incsect(UNIT *uptr, int drive) +{ + int type = GET_DTYPE(uptr->flags); + + dsk_dcb[drive].sect++; + if (dsk_dcb[drive].sect >= dsk_type[type].sect) { + dsk_dcb[drive].sect = 0; + dsk_dcb[drive].hd++; + if (dsk_dcb[drive].hd >= dsk_type[type].hds) { + dsk_dcb[drive].hd = 0; + dsk_dcb[drive].cyl++; + if (dsk_dcb[drive].hd >= dsk_type[type].cyl) { + return 1; + } + } + } + return 0; +} + +t_stat +dsk_svc (UNIT *uptr) +{ + int drive = uptr - dsk_unit; + int offset = drive << 6;; + int type = GET_DTYPE(uptr->flags); + int da; + int flags; + int len; + int i; + int sc; + + if (uptr->CYL != dsk_dcb[drive].cyl) { + /* Step in/out based on current cylinder, requested cylinder */ + if (uptr->CYL < dsk_dcb[drive].cyl) + uptr->CYL++; + else if (uptr->CYL > dsk_dcb[drive].cyl) + uptr->CYL--; + sim_activate(uptr, 1000); + return SCPE_OK; + } + + da = ((dsk_dcb[drive].cyl * dsk_type[type].hds + dsk_dcb[drive].hd) + * dsk_type[type].sect) + dsk_dcb[drive].sect; + switch (uptr->CMD & 0xf) { + case DSK_RD: /* Read command */ + sim_debug(DEBUG_DETAIL, &dsk_dev, + "read sector %6x %4x %4d %d %2d\n", + dsk_dcb[drive].addr, dsk_dcb[drive].count, dsk_dcb[drive].cyl, + dsk_dcb[drive].hd, dsk_dcb[drive].sect); + (void)sim_fseek(uptr->fileref, da * SECT_SZ, SEEK_SET); + len = sim_fread(&dsk_buf, 1, sizeof(dsk_buf), uptr->fileref); + while (len < sizeof(dsk_buf)) { + dsk_buf[len++] = 0; + } + if (len > dsk_dcb[drive].count) + len = dsk_dcb[drive].count; + sim_debug(DEBUG_DETAIL, &dsk_dev, "Disk Read: %d bytes\n", len); + for (i = 0; i < len; i++) { + sim_debug(DEBUG_DATA, &dsk_dev, "%02x ", dsk_buf[i]); + if ((i & 0xf) == 0xf) + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + } + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + io_write_blk(dsk_dcb[drive].addr, &dsk_buf[0], len); + dsk_dcb[drive].count -= len; + dsk_dcb[drive].xcount += len; + dsk_dcb[drive].addr += len; + if (dsk_dcb[drive].count != 0) { + if (dsk_incsect(uptr, drive)) { + io_dcbwrite_half(uptr, offset + 0xa, dsk_dcb[drive].xcount); + io_dcbwrite_byte(uptr, offset + 0x2, 0xb); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + } + sim_activate(uptr, 100); + return SCPE_OK; + } + io_dcbwrite_half(uptr, offset + 0xa, dsk_dcb[drive].xcount); + io_dcbwrite_byte(uptr, offset + 0x2, 0); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + + case DSK_WR: /* Write command */ + sim_debug(DEBUG_DETAIL, &dsk_dev, + "write sector %6x %4x %4d %d %2d\n", + dsk_dcb[drive].addr, dsk_dcb[drive].count, dsk_dcb[drive].cyl, + dsk_dcb[drive].hd, dsk_dcb[drive].sect); + (void)sim_fseek(uptr->fileref, da * SECT_SZ, SEEK_SET); + len = sizeof(dsk_buf); + if (len > dsk_dcb[drive].count) + len = dsk_dcb[drive].count; + io_read_blk(dsk_dcb[drive].addr, &dsk_buf[0], len); + while (len < sizeof(dsk_buf)) { + dsk_buf[len++] = 0; + } + sim_debug(DEBUG_DETAIL, &dsk_dev, "Disk Write: %d bytes\n", len); + for (i = 0; i < len; i++) { + sim_debug(DEBUG_DATA, &dsk_dev, "%02x ", dsk_buf[i]); + if ((i & 0xf) == 0xf) + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + } + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + len = sim_fwrite(&dsk_buf, 1, sizeof(dsk_buf), uptr->fileref); + if (len > dsk_dcb[drive].count) + len = dsk_dcb[drive].count; + dsk_dcb[drive].count -= len; + dsk_dcb[drive].xcount += len; + dsk_dcb[drive].addr += len; + if (dsk_dcb[drive].count != 0) { + if (dsk_incsect(uptr, drive)) { + io_dcbwrite_half(uptr, offset + 0xa, dsk_dcb[drive].xcount); + io_dcbwrite_byte(uptr, offset + 0x2, 0xb); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + } + sim_activate(uptr, 100); + return SCPE_OK; + } + io_dcbwrite_half(uptr, offset + 0xa, dsk_dcb[drive].xcount); + io_dcbwrite_byte(uptr, offset + 0x2, 0); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + + case DSK_VFY: /* Verify data */ + io_dcbwrite_half(uptr, offset + 0xa, dsk_dcb[drive].xcount); + io_dcbwrite_byte(uptr, offset + 0x2, 0); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + + case DSK_FMT: /* Format a track */ + sim_debug(DEBUG_DETAIL, &dsk_dev, + "Format %6x %4x %2x %2x %2x\n", + dsk_dcb[drive].addr, dsk_dcb[drive].count, dsk_dcb[drive].hd, + dsk_dcb[drive].cyl, dsk_dcb[drive].sect); + da = ((dsk_dcb[drive].cyl * 7 + dsk_dcb[drive].hd) * 18); + (void)sim_fseek(uptr->fileref, da * SECT_SZ, SEEK_SET); + memset(&dsk_buf[0], 0, sizeof(dsk_buf)); + for(sc = 0; sc < 18; sc ++) { + len = sim_fwrite(&dsk_buf, 1, sizeof(dsk_buf), uptr->fileref); + } + io_dcbwrite_half(uptr, offset + 0xa, dsk_dcb[drive].count); + io_dcbwrite_byte(uptr, offset + 0x2, 0); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + case DSK_SEEK: /* Seek to cylinder */ + io_dcbwrite_byte(uptr, offset + 0x2, 0); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + case DSK_RDH: /* Read Highest sector address */ + io_dcbwrite_byte(uptr, offset + 0xd, ((dsk_type[type].hds - 1) << 4) | + (((dsk_type[type].cyl - 1) >> 8) & 0xF)); + io_dcbwrite_byte(uptr, offset + 0xe, (dsk_type[type].cyl - 1) & 0xFF); + io_dcbwrite_byte(uptr, offset + 0xf, dsk_type[type].sect - 1); + io_dcbwrite_half(uptr, offset + 0xa, 20160 / 18); + io_dcbwrite_byte(uptr, offset + 0x2, 0x00); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + case DSK_RDF: /* Read full sector */ + sim_debug(DEBUG_DETAIL, &dsk_dev, + "read fsector %6x %4x %4d %d %2d\n", + dsk_dcb[drive].addr, dsk_dcb[drive].count, dsk_dcb[drive].cyl, + dsk_dcb[drive].hd, dsk_dcb[drive].sect); + (void)sim_fseek(uptr->fileref, da * SECT_SZ, SEEK_SET); + memset(&dsk_buf[0], 0, LBL_SZ); + io_write_blk(dsk_dcb[drive].addr, &dsk_buf[0], LBL_SZ); + dsk_dcb[drive].addr += len; + len = sim_fread(&dsk_buf, 1, sizeof(dsk_buf), uptr->fileref); + while (len < sizeof(dsk_buf)) { + dsk_buf[len++] = 0; + } + sim_debug(DEBUG_DATA, &dsk_dev, "Disk Readfull\n"); + for (i = 0; i < SECT_SZ; i++) { + sim_debug(DEBUG_DATA, &dsk_dev, "%02x ", dsk_buf[i]); + if ((i & 0xf) == 0xf) + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + } + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + io_write_blk(dsk_dcb[drive].addr, &dsk_buf[0], len); + dsk_dcb[drive].xcount += sizeof(dsk_sect_lab) + sizeof(dsk_buf) + 4; + io_dcbwrite_half(uptr, offset + 0xa, dsk_dcb[drive].xcount); + io_dcbwrite_byte(uptr, offset + 0x2, 0); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + + case DSK_WRF: /* Write full sector */ + sim_debug(DEBUG_DETAIL, &dsk_dev, + "Write fsector %6x %4x %4d %d %2d\n", + dsk_dcb[drive].addr, dsk_dcb[drive].count, dsk_dcb[drive].cyl, + dsk_dcb[drive].hd, dsk_dcb[drive].sect); + (void)sim_fseek(uptr->fileref, da * SECT_SZ, SEEK_SET); + io_read_blk(dsk_dcb[drive].addr, &dsk_buf[0], sizeof(dsk_sect_lab)); + dsk_dcb[drive].addr += LBL_SZ; + io_read_blk(dsk_dcb[drive].addr, &dsk_buf[0], sizeof(dsk_buf)); + sim_debug(DEBUG_DATA, &dsk_dev, "Disk Writefull\n"); + for (i = 0; i < SECT_SZ; i++) { + sim_debug(DEBUG_DATA, &dsk_dev, "%02x ", dsk_buf[i]); + if ((i & 0xf) == 0xf) + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + } + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + len = sim_fwrite(&dsk_buf, 1, sizeof(dsk_buf), uptr->fileref); + dsk_dcb[drive].xcount += len; + io_dcbwrite_half(uptr, offset + 0xa, dsk_dcb[drive].xcount); + io_dcbwrite_byte(uptr, offset + 0x2, 0); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + + case DSK_HDR: /* Read headers */ + io_dcbwrite_byte(uptr, offset + 0x2, 0); + dsk_unit[0].STATUS = 0x400001 | (drive << 16); + ext_irq = 1; + break; + } + return SCPE_OK; +} + + +t_stat +dsk_boot(int32 unit_num, DEVICE *dptr) +{ + UNIT *dkuptr = &dptr->units[unit_num]; + int i = 0; + + if (unit_num != 0) + return SCPE_ARG; + + if ((dkuptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + dkuptr->up8 = (void *)dkuptr; + /* Set up for generic read of track 2 */ + dkuptr->CMD = 0; + dsk_dcb[unit_num].addr = 0x3e000; + dsk_dcb[unit_num].count = 4096; + dsk_dcb[unit_num].xcount = 0; + dsk_dcb[unit_num].hd = 0; + dsk_dcb[unit_num].cyl = 0; + dsk_dcb[unit_num].sect = 4; + + /* Set CPU into idle state */ + cpu_boot(1); + sim_activate(dkuptr, 20); + return SCPE_OK; +} + + + +t_stat +dsk_reset(DEVICE *dptr) +{ + int i,t; + + dsk_unit[0].DCB = 0x3c100; + for (i = 0; i < dptr->numunits; i++) { + t = GET_DTYPE(dsk_unit[i].flags); + dsk_unit[i].capac = dsk_type[t].cyl * dsk_type[t].hds * + dsk_type[t].sect * 1024; + } + return SCPE_OK; +} + +t_stat +dsk_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + + if (uptr == NULL) return SCPE_IERR; + i = GET_DTYPE(val); + uptr->capac = dsk_type[i].cyl * dsk_type[i].hds * + dsk_type[i].sect * 1024; + return SCPE_OK; +} + + +/* Attach routine */ +t_stat +dsk_attach(UNIT *uptr, CONST char *cptr) +{ + char header[4]; + t_stat r; + + r = attach_unit(uptr, cptr); /* attach unit */ + if ( r != SCPE_OK) /* error? */ + return r; + + /* Determine length of this disk */ + uptr->capac = sim_fsize(uptr->fileref); + return SCPE_OK; +} + + +/* Detach routine */ +t_stat dsk_detach(UNIT *uptr) +{ + t_stat r; + + r = detach_unit(uptr); /* detach unit */ + return r; +} + + + diff --git a/Ridge32/ridge32_flp.c b/Ridge32/ridge32_flp.c new file mode 100644 index 0000000..e704b23 --- /dev/null +++ b/Ridge32/ridge32_flp.c @@ -0,0 +1,978 @@ +/* Ridge32_flp.c: Ridge 32 765 floppy disk controller. + + Copyright (c) 2019, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + + +*/ + +#include "ridge32_defs.h" +#include "sim_imd.h" + + +#define DCB u3 /* DCB pointer */ +#define STATUS u4 /* Last status */ +#define CYL u5 /* Current cylinder */ +#define PHASE u6 /* Command phase */ + +#define PHASE_IDLE 0 /* No commands */ +#define PHASE_SEEK 1 /* Seek to desired cylinder */ +#define PHASE_CMD 2 /* Decode commands */ +#define PHASE_EXEC 3 /* Execute the command */ +#define PHASE_RES 4 /* Place results */ +#define PHASE_IRQ 5 /* Post interrupt */ + +#define CMD_RDTRK 0x2 /* Read track */ +#define CMD_FIXDR 0x3 /* Fix drive */ +#define CMD_CHKDR 0x4 /* Check drive */ +#define CMD_WRSEC 0x5 /* Write sector */ +#define CMD_RDSEC 0x6 /* Read sector */ +#define CMD_RECAL 0x7 /* Recalibrate drive */ +#define CMD_CHKIR 0x8 /* Check IRQ */ +#define CMD_WRDEL 0x9 /* Write delete */ +#define CMD_RDSID 0xa /* Read sector ID */ +#define CMD_RDDEL 0xc /* Read delete */ +#define CMD_FMTTK 0xd /* Format track */ +#define CMD_SEEK 0xf /* Seek */ + +#define BLD_OP 0x01 +#define SEEK_OP 0x02 +#define READ_OP 0x04 +#define WRIT_OP 0x08 +#define SK_OP 0x10 +#define ST_OP 0x20 +#define INV_OP 0x80 + +/* + * Read Track 0FS00010/xxxxxHDD/Cyl/Head/Sec/Sectsize/tl/lgap3/dataleng. + * Write sec MF000101/same. + * Read sec MFS00110/same. + * Write del MF001001/smae. + * Read Del MF001100/same. + * Fmt Track 0F001101/xxxxxHDD/Sect/Track/Lgap3/fill + * Data per sector: Track/Head/Sect Num/Sect size. + * Fix Drive 00000011/step,hd ult/hd lt,ndm No status + * Chk Drive 00000100/xxxxxHDD ST3 + * Cali 00000111/xxxxx0DD No status + * Chk Irq 00001000 ST0,Cyl + * Rd Sec ID 0F001010/xxxxxHDD + * Seek 00001111/xxxxxHDD/cyl + */ + +/* M = Multi track operations. + F = FM/MFM 0=single/1=double + S = skip deleted. + H = Head number. + DD = drive. + Sectsize = 128 * 2^x... 2=512. + lgap3 = std 42, min = 32, std =27 3.5. + tl = track length/#sectors. + */ + +/* Result phase. + + ST0 + IC 00= normal. + 01 = abnormal. + 10 = invalid + 11 = abnomral, polling. + SE Seek end. + UC Unit Check + NR Drive not ready + HD head + US drive number + ST1 + EN End Cyl + 0 + DE Data error. + TO Timeout/DMA error. + NDAT No data. + NW Not writable + NID No address mark. + ST2 + 0 + DADM Deleted address mark + CRCE Crc error. + WCYL Wrong cylinder + SEQ Seek equal + SERR Seek error. + BCYL Back cylinder. + NDAM: No data. + Cyl + Hd + S# + SS. + + ST3 + ESIG Error + WPDR Write protect + RDY Controller ready. + T00 Track zero. + DSDR Double sided. + HD Head select. + DD Drive selected. + +*/ + +/* DCB control block. + + DCB + 0C0 + + 0 GORDER + 0 - Read, FDLP Build, implied seek. + 1 - Write, FDLP Build, implied seek. + 2 - Read, implied seek. + 3 - Write, implied seek. + 4 - Read, FDLP Build. + 5 - Write, FDLP Build. + 6 - Read. + 7 - Write. + 8 - Seek. + 9 - Recalibrate. + A - Drive status. + + 1 SORDER bit 0,1,2 Len 0 read/write 6 for format. + 3,4,5 Density + 0 DD 512x16 + 1 SD 128x26 + 2 SD 256x15 + 3 DD 256x26 + 4 DD 1024x8 + 5 DD 512x15 + 6 DD 2048x4 + 7 DD 4096x2. + 6 - 1= write. + 7 - 1= nodma. + + 0 EOT 16 MFM 1 N = 2 GPL = 1b + 1 EOT 26 MFM 0 N = 0 GPL = 07 + 2 EOT 15 MFM 0 N = 1 GPL = 0e + 3 EOT 26 MFM 1 N = 1 GPL = 0e + 4 EOT 8 MFM 1 N = 3 GPL = 35 + 5 EOT 15 MFM 1 N = 2 GPL = 35 + 6 EOT 4 MFM 1 N = 4 GPL = 35 + 7 EOT 2 MFM 1 N = 6 GPL = 35 + + 2 GSTAT + 0 - Ok. + 1 - Not Ready. + 2 - Timeout + 3 - Equipment Fault. + 4 - Write protect + 5 - Ridge double bit error. + 6 - Data overrun + 7 - Missing AM. + 8 - Can't find header. + 9 - header CRC + A - data CRC + FF - Illegal parameter in DCB order. + + 3 SSTAT + bit 01 - unit + 2 - head + 3 - two sided + 4 - track 0 + 5 - ready + 6 - write protect. + 7 - fault. + + 4 RETRIES + + 5 RIDGE ADDR + 6 + 7 + + 8 BYTE COUNT + 9 + + A BYTE COUNT READ. + B + + C NEC ORDER + D Head/unit + E Cylinder + F Sector + 10 -18 CMD 00-08 Command built by controller. + 19 -1F STATUS 00-06 Status returned by the drive. + */ + +int flp_read(uint32 dev, uint32 *data); +int flp_write(uint32 dev, uint32 data); +int flp_iord(uint32 *data); +void flp_start(UNIT *uptr, int drive); +t_stat flp_svc (UNIT *uptr); +t_stat flp_boot (int32, DEVICE *); +t_stat flp_attach(UNIT *uptr, CONST char *cptr); +t_stat flp_detach(UNIT *uptr); +void com_write_char(int line, uint8 ch); +t_stat flp_reset(DEVICE *dp); +t_stat con_svc(UNIT *uptr); + +uint8 flp_buf[4096]; + +/* Device context block */ +struct ridge_dib flp_dib = {1, 1, flp_read, flp_write, flp_iord}; + +MTAB flp_mod[] = { + {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "SLOT", "SLOT", &set_slot_num, + &show_slot_num, NULL}, + {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL}, + { 0 } + }; + + +UNIT flp_unit[] = { + {UDATA(&flp_svc, UNIT_ATTABLE, 0)}, + {UDATA(&flp_svc, UNIT_ATTABLE, 0)}, + {UDATA(&con_svc, UNIT_DIS, 0)}, +}; + + +DEVICE flp_dev = { + "FLP", flp_unit, NULL, flp_mod, + 3, 16, 24, 1, 16, 8, + NULL, NULL, &flp_reset, &flp_boot, &flp_attach, &flp_detach, + &flp_dib, DEV_DEBUG, 0, dev_debug, NULL, NULL, +}; + + + + + +int +flp_read(uint32 dev, uint32 *data) +{ + UNIT *uptr = &flp_unit[0]; + + *data = uptr->STATUS & 0xFFFF02; + *data |= (flp_dib.dev_num << 24) & 0xff000000; + if (uptr->STATUS & 0x4) + uptr->STATUS &= ~7; + return (uptr->STATUS & 0x2) ? 1 : 0; +} + +int +flp_write(uint32 dev, uint32 data) +{ + UNIT *uptr = &flp_unit[0]; + int cmd = (data >> 24) & 0xff; + + /* Check if command can be accepted */ + if (uptr->STATUS & 3) { + return 1; + } + sim_debug(DEBUG_EXP, &flp_dev, "Start cmd %2x\n", cmd); + if (cmd < 0x80) { + com_write_char(0, cmd); + uptr->STATUS = (0x80 << 16); + } else if ((cmd & 0xc0) == 0xc0) { + switch (cmd) { + case 0xc0: /* Boot floppy left */ + (void)flp_boot(0, &flp_dev); + break; + + case 0xc1: /* Update DCB */ + uptr->DCB = M[0x3c0fc >> 2]; + case 0xc3: /* Read one char port 0 no irq */ + uptr->STATUS = (0x80 << 16) | 1; + ext_irq = 1; + break; + + case 0xc2: /* Read one char port 0 no irq */ + uptr->STATUS = 2; + /* Fall through */ + case 0xff: /* Read one char port 0 irq */ + flp_unit[2].u3 = cmd; + break; + } + return 0; + } else if ((cmd & 0xe0) == 0x80) { + switch (cmd & 0x1f) { + case 0x06: /* Start left floppy */ + case 0x07: /* Start right floppy */ + flp_start(uptr, cmd & 1); + sim_activate(uptr, 20); + break; + + case 0x00: /* Write on port 0 */ + case 0x01: /* Write on port 1 */ + case 0x02: /* Write on port 2 */ + case 0x03: /* Write on port 3 */ + case 0x04: /* Write on printer */ + case 0x05: /* Write on plotter */ + case 0x08: /* Read on port 0 */ + case 0x09: /* Read on port 1 */ + case 0x0a: /* Read on port 2 */ + case 0x0b: /* Read on port 3 */ + case 0x0c: /* Control on port 0 */ + case 0x0d: /* Control on port 1 */ + case 0x0e: /* Control on port 2 */ + case 0x0f: /* Control on port 3 */ + case 0x18: /* Read one char on port 0 */ + case 0x19: /* Read one char on port 1 */ + case 0x1a: /* Read one char on port 2 */ + case 0x1b: /* Read one char on port 3 */ + break; + } + } else { + /* undefined */ + } + return 0; +} + +int +flp_iord(uint32 *data) +{ + UNIT *uptr = &flp_unit[0]; + *data = uptr->STATUS & 0x0FFFF02; + *data |= ((uint32)flp_dib.dev_num) << 24; + /* Check if irq pending */ + if (uptr->STATUS & 1) { + uptr->STATUS &= ~1; + return 1; + } + return 0; +} + + + + + /* Write port DCB. + + 0 GORDER + 1 - Write block of up to 256 chars. + 3 - Single Char (SORDER) + 5 - assert break. + + 1 SORDER char for 3. + + 2 GSTAT not used. + 3 SSTAT not used. + 4 RETRIES not used. + + 5 ADDRESS + 6 + 7 + + 8 REQUEST BYTE COUNT + 9 + + A BYTES TRANSFERED + B + + Read port DCB. + + 0 GORDER + 0 - Read term by count or MAP + + 1 SORDER not used. + 2 GSTAT not used. + 3 SSTAT not used. + 4 RETRIES not used. + + 5 ADDRESS + 6 + 7 + + 8 REQUEST BYTE COUNT + 9 + + A BYTES TRANSFERED + B + + Control port DCB. + + 0 GORDER + 0 - Return termtype in SORDER + 1 - Set termtype from SORDER + 2 - Return Baud rate in SORDER + 3 - Set Baud rate from SORDER + 4 - Return SIO status + 5 - Set SIO registers + 6 - Abort read in progress + 7 - abort write in progress. + 8 - Read map into memory + 9 - Write map from memory. + + 1 SORDER PARM1 + 2 GSTAT PARM2 + 3 SSTAT PARM3 + + Termtype: + Bit 0 - Uses XOFF. + 1 - Generate XOFF + 2 - Clear DTR. + 3 - Send IRQ when port stat changes. + + Map... bit 0 - port 0. + 1 - port 1. + 2 - port 2. + 3 - port 3. + + Initial zero except 3 and D. If char received on line has bit set, stop read. +*/ + +struct _dcb { + uint8 gorder; /* Order code */ + uint8 sorder; + uint8 gstat; /* Output status */ + uint8 sstat; + uint32 addr; /* transfer address */ + uint16 count; /* byte count */ + uint16 xcount; /* transfer count */ + uint8 retries; + uint8 order; /* NEC 765 order code */ + uint8 hd; /* Head */ + uint8 cyl; /* Cylinder */ + uint8 sect; /* Sector */ + uint8 cmd[9]; /* NEC 765 command sequenece */ + uint8 stat[7]; /* Status */ + int stat_len; /* Size of status */ + int sect_sz; /* Size of sector */ +} flp_dcb; + +static uint8 order[] = { + BLD_OP|SEEK_OP|READ_OP, /* 0 - Read, FDLP Build, implied seek. */ + BLD_OP|SEEK_OP|WRIT_OP, /* 1 - Write, FDLP Build, implied seek. */ + SEEK_OP|READ_OP, /* 2 - Read, implied seek. */ + SEEK_OP|WRIT_OP, /* 3 - Write, implied seek. */ + BLD_OP|READ_OP, /* 4 - Read, FDLP Build. */ + BLD_OP|WRIT_OP, /* 5 - Write, FDLP Build. */ + READ_OP, /* 6 - Read. */ + WRIT_OP, /* 7 - Write. */ + BLD_OP|SEEK_OP|SK_OP, /* 8 - Seek. */ + BLD_OP|SEEK_OP|SK_OP, /* 9 - Recalibrate. */ + BLD_OP|ST_OP, /* A - Drive status. */ +}; + + +struct _flp_data { + uint8 eot; + uint8 n; + uint8 gpl; + uint8 dtl; + uint8 mfm; + int ssz; +} flp_data[] = { + {16, 2, 0x1b, 0xff, 1, 512}, + {26, 0, 0x07, 0x80, 0, 128}, + {15, 1, 0x0e, 0xff, 0, 256}, + {26, 1, 0x0e, 0xff, 1, 256}, + { 8, 3, 0x35, 0xff, 1, 1024}, + {15, 2, 0x35, 0xff, 1, 512}, + { 4, 4, 0x35, 0xff, 1, 2048}, + { 2, 6, 0x35, 0xff, 1, 4096}, +}; + + +void +flp_start(UNIT *uptr, int drive) +{ + UNIT *fluptr; + int gorder; + + /* Find actual unit */ + fluptr = &flp_unit[drive & 1]; + fluptr->up8 = (void *)uptr; + /* Read first word of DCB + 0xC0 */ + flp_dcb.gorder = io_dcbread_byte(uptr, 0xC0); + flp_dcb.sorder = io_dcbread_byte(uptr, 0xC1); + flp_dcb.addr = io_dcbread_addr(uptr, 0xC5); + flp_dcb.count = io_dcbread_half(uptr, 0xC8); + flp_dcb.xcount = 0; + flp_dcb.order = io_dcbread_byte(uptr, 0xCC); + flp_dcb.hd = io_dcbread_byte(uptr, 0xCD); + flp_dcb.cyl = io_dcbread_byte(uptr, 0xCE); + flp_dcb.sect = io_dcbread_byte(uptr, 0xCF); + sim_debug(DEBUG_DETAIL, &flp_dev, + "Start floppy go=%2x so=%2x a=%6x c=%4x o=%2x h=%d t=%d s=%d\n\r", + flp_dcb.gorder, flp_dcb.sorder, flp_dcb.addr, flp_dcb.count, flp_dcb.order, + flp_dcb.hd, flp_dcb.cyl, flp_dcb.sect); + if (flp_dcb.gorder > 10) { + /* Invalid */ + io_dcbwrite_byte(uptr, 0xc2, 0xff); + uptr->STATUS = ((0x86 + drive) << 16) | (0xff << 8) | 1; + ext_irq = 1; + return; + } + gorder = order[flp_dcb.gorder]; + if ((gorder & BLD_OP) != 0) { /* Build command */ + int den = (flp_dcb.sorder >> 3) & 7; + int i = 0; + if (gorder & SK_OP) { + flp_dcb.cmd[i++] = CMD_SEEK; + flp_dcb.cmd[i++] = flp_dcb.hd; + flp_dcb.cmd[i++] = flp_dcb.cyl; + if (flp_dcb.gorder & 1) + flp_dcb.cyl = 0; /* Force recalbirate */ + } else if (gorder & ST_OP) { + flp_dcb.cmd[i++] = CMD_CHKDR; + flp_dcb.cmd[i++] = flp_dcb.hd; + } else { + flp_dcb.cmd[i++] = flp_dcb.order; /* 0 */ + flp_dcb.cmd[i++] = flp_dcb.hd; /* 1 */ + if ((flp_dcb.cmd[0] & 0x0f) != 0xd) { + flp_dcb.cmd[i++] = flp_dcb.cyl; /* 2 */ + flp_dcb.cmd[i++] = (flp_dcb.hd & 0x4) != 0; /* 3 */ + flp_dcb.cmd[i++] = flp_dcb.sect; /* 4 */ + } + flp_dcb.cmd[i++] = flp_data[den].n; /* N */ /* 2 or 5 */ + flp_dcb.cmd[i++] = flp_data[den].eot; /* EOT */ + flp_dcb.cmd[i++] = flp_data[den].gpl; /* GPL */ + flp_dcb.cmd[i++] = flp_data[den].dtl; /* DTL */ + if (flp_data[den].mfm) + flp_dcb.cmd[0] |= 0x40; + } + } else { + io_dcbread_blk(uptr, 0xD0, &flp_dcb.cmd[0], 9); + } + if ((gorder & SEEK_OP) != 0) /* Seek first */ + fluptr->PHASE = PHASE_SEEK; + else + fluptr->PHASE = PHASE_CMD; + flp_dcb.stat[0] = (flp_dcb.cmd[1] & 0x7); + flp_dcb.stat_len = 0; + if ((fluptr->flags & UNIT_ATT) == 0) { + flp_dcb.stat[0] |= 0xc8; + fluptr->PHASE = PHASE_RES; + } + flp_dcb.gstat = 0; + sim_activate(fluptr, 200); +} + +t_stat +flp_svc (UNIT *uptr) +{ + int flags; + int len; + int i; + /* Read status to decide action */ + switch (uptr->PHASE) { + case PHASE_IDLE: /* Done with commands, just idle out */ + return SCPE_OK; + case PHASE_SEEK: + /* Step in/out based on current cylinder, requested cylinder */ + /* Match, PHASE = PHASE_CMD */ + if (uptr->CYL < flp_dcb.cyl) + uptr->CYL++; + else if (uptr->CYL > flp_dcb.cyl) + uptr->CYL--; + else + uptr->PHASE = PHASE_CMD; + sim_debug(DEBUG_DETAIL, &flp_dev, "Seek n=%2d c=%2d\n\r", flp_dcb.cyl, uptr->CYL); + sim_activate(uptr, 2000); + return SCPE_OK; + + case PHASE_CMD: + /* Decode command */ + switch(flp_dcb.cmd[0] & 0xf) { + case CMD_RECAL: /* Recalibrate drive */ + flp_dcb.cmd[2] = 0; + /* Fall through */ + case CMD_SEEK: /* Seek */ + if (uptr->CYL == flp_dcb.cmd[2]) { + flp_dcb.stat[0] |= 0x20; + uptr->PHASE = PHASE_RES; + + sim_activate(uptr, 10); + return SCPE_OK; + } + flp_dcb.cyl = flp_dcb.cmd[2]; + uptr->PHASE = PHASE_SEEK; + sim_activate(uptr, 10); + return SCPE_OK; + + case CMD_CHKIR: /* Check IRQ */ + flp_dcb.stat[0] = 0; + flp_dcb.stat_len = 2; + uptr->PHASE = PHASE_RES; + sim_activate(uptr, 10); + return SCPE_OK; + + case CMD_CHKDR: /* Check drive */ + uptr->PHASE = PHASE_RES; + sim_activate(uptr, 10); + return SCPE_OK; + + case CMD_RDSEC: /* Read sector */ + case CMD_RDTRK: /* Read track */ + case CMD_WRSEC: /* Write sector */ + case CMD_WRDEL: /* Write delete */ + case CMD_RDDEL: /* Read delete */ + /* Make sure cylinder is correct */ + flp_dcb.stat[1] = 0; + flp_dcb.stat[2] = 0; + flp_dcb.stat[3] = flp_dcb.cmd[2]; /* C */ + flp_dcb.stat[4] = flp_dcb.cmd[3]; /* H */ + flp_dcb.stat[5] = flp_dcb.cmd[4]; /* R */ + flp_dcb.stat[6] = flp_dcb.cmd[5]; /* N */ + flp_dcb.stat_len = 7; + if (uptr->CYL != flp_dcb.cmd[2]) { + flp_dcb.stat[0] |= 0x40; + flp_dcb.stat[1] = 0; + flp_dcb.stat[2] = 0x10; + uptr->PHASE = PHASE_RES; + } else { + uptr->PHASE = PHASE_EXEC; + } + sim_activate(uptr, 100); + return SCPE_OK; + + case CMD_FIXDR: /* Fix drive */ + case CMD_RDSID: /* Read sector ID */ + case CMD_FMTTK: /* Format track */ + uptr->PHASE = PHASE_EXEC; + sim_activate(uptr, 10); + return SCPE_OK; + default: /* Invalid command */ + return SCPE_OK; + } + case PHASE_EXEC: + /* Transfer data to/from memory */ + switch(flp_dcb.cmd[0] & 0xf) { + case CMD_RDSEC: /* Read sector */ + /* cyl head sect */ + flags = 0; + if (sectRead((DISK_INFO *)uptr->up7, flp_dcb.stat[3], + flp_dcb.stat[4], flp_dcb.stat[5], + &flp_buf[0], sizeof(flp_buf), &flags, &len) != SCPE_OK) { + uptr->PHASE = PHASE_RES; + sim_activate(uptr, 1000); + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, &flp_dev, "Read a=%6x c=%4x h=%x t=%d s=%d l=%d\n\r", + flp_dcb.addr, flp_dcb.count, flp_dcb.stat[4], flp_dcb.stat[3], flp_dcb.stat[5], len); + + sim_debug(DEBUG_DATA, &dsk_dev, "Disk Read: %d bytes\n", len); + for (i = 0; i < len; i++) { + sim_debug(DEBUG_DATA, &dsk_dev, "%02x ", flp_buf[i]); + if ((i & 0xf) == 0xf) + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + } + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + + if (len > flp_dcb.count) + len = flp_dcb.count; + io_write_blk(flp_dcb.addr, &flp_buf[0], len); + flp_dcb.count -= len; + flp_dcb.xcount += len; + flp_dcb.addr += len; + if (flp_dcb.stat[5] == flp_dcb.cmd[6]) { + flp_dcb.stat[5] = 1; + if(flp_dcb.stat[4]) { + flp_dcb.stat[3]++; + flp_dcb.stat[4] = 0; + } else { + flp_dcb.stat[4] = 1; + } + } else { + flp_dcb.stat[5]++; + } + if (flp_dcb.count == 0) { + uptr->PHASE = PHASE_RES; + sim_activate(uptr, 10); + } else { + sim_activate(uptr, 100); + } + return SCPE_OK; + + case CMD_WRSEC: /* Write sector */ + flags = 0; + len = flp_dcb.cmd[5]; + if (len == 0) { + len = flp_dcb.cmd[8]; + } else { + len *= 512; + } + if (len > flp_dcb.count) + len = flp_dcb.count; + io_read_blk(flp_dcb.addr, &flp_buf[0], len); + sim_debug(DEBUG_DATA, &dsk_dev, "Disk Write: %d bytes\n", len); + for (i = 0; i < len; i++) { + sim_debug(DEBUG_DATA, &dsk_dev, "%02x ", flp_buf[i]); + if ((i & 0xf) == 0xf) + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + } + sim_debug(DEBUG_DATA, &dsk_dev, "\n"); + if (sectWrite((DISK_INFO *)uptr->up7, flp_dcb.stat[3], + flp_dcb.stat[4], flp_dcb.stat[5], + &flp_buf[0], sizeof(flp_buf), &flags, &len) != SCPE_OK) { + uptr->PHASE = PHASE_RES; + sim_activate(uptr, 1000); + return SCPE_OK; + } + flp_dcb.count -= len; + flp_dcb.xcount += len; + flp_dcb.addr += len; + if (flp_dcb.stat[5] == flp_dcb.cmd[6]) { + flp_dcb.stat[5] = 1; + if(flp_dcb.stat[4]) { + flp_dcb.stat[3]++; + flp_dcb.stat[4] = 0; + } else { + flp_dcb.stat[4] = 1; + } + } else { + flp_dcb.stat[5]++; + } + if (flp_dcb.count == 0) { + uptr->PHASE = PHASE_RES; + sim_activate(uptr, 10); + } else { + sim_activate(uptr, 100); + } + return SCPE_OK; + + case CMD_RDTRK: /* Read track */ + case CMD_WRDEL: /* Write delete */ + case CMD_RDDEL: /* Read delete */ + /* Make sure cylinder is correct */ + flp_dcb.stat[1] = 0; + flp_dcb.stat[2] = 0; + flp_dcb.stat[3] = flp_dcb.cmd[2]; /* C */ + flp_dcb.stat[4] = flp_dcb.cmd[3]; /* H */ + flp_dcb.stat[5] = flp_dcb.cmd[4]; /* R */ + flp_dcb.stat[6] = flp_dcb.cmd[5]; /* N */ + flp_dcb.stat_len = 7; + if (uptr->CYL != flp_dcb.cmd[2]) { + flp_dcb.stat[0] |= 0x40; + flp_dcb.stat[1] = 0; + flp_dcb.stat[2] = 0x10; + uptr->PHASE = PHASE_RES; + } else { + uptr->PHASE = PHASE_EXEC; + } + sim_activate(uptr, 10); + return SCPE_OK; + + case CMD_FIXDR: /* Fix drive */ + case CMD_RDSID: /* Read sector ID */ + case CMD_FMTTK: /* Format track */ + uptr->PHASE = PHASE_EXEC; + sim_activate(uptr, 10); + return SCPE_OK; + default: /* Invalid command */ + return SCPE_OK; + } + case PHASE_RES: + /* Save results back to memory */ + io_dcbwrite_blk(uptr, 0xD9, &flp_dcb.stat[0], flp_dcb.stat_len); + io_dcbwrite_byte(uptr, 0xc2, flp_dcb.gstat); + flags = flp_dcb.hd & 7; + flags |= 0x28; /* Ready & Two sided */ + if (uptr->CYL == 0) + flags |= 0x10; + io_dcbwrite_byte(uptr, 0xc3, flags); + io_dcbwrite_half(uptr, 0xca, flp_dcb.xcount); + sim_debug(DEBUG_DETAIL, &flp_dev,"Stop floppy %2x %4x %2x\n\r", + flags, flp_dcb.xcount, flp_dcb.gstat); + uptr->PHASE = PHASE_IRQ; + /* Fall Through */ + + case PHASE_IRQ: + if (((UNIT *)(uptr->up8))->STATUS & 1) { + sim_activate(uptr, 100); + return SCPE_OK; + } + ((UNIT *)(uptr->up8))->STATUS = ((0x86 + (flp_dcb.hd & 1)) << 16) | + (flp_dcb.gstat << 8) | 1; + uptr->PHASE = PHASE_IDLE; + ext_irq = 1; + } + return SCPE_OK; +} + +void +com_write_char(int line, uint8 ch) +{ + sim_putchar(ch); +} + +t_stat +con_svc(UNIT *uptr) +{ + t_stat r; + uint8 ch; + + sim_clock_coschedule (uptr, 1000); + r = sim_poll_kbd (); + if (r & SCPE_KFLAG) { + ch = r & 0377; + if (uptr->u3 != 0) { + if (uptr->u3 == 0xFF) { + flp_unit[0].STATUS = (0x88 << 16) | (ch << 8) | 1; + ext_irq = 1; + } else { + flp_unit[0].STATUS = ((0x80 | ch) << 16) | 6; + } + sim_debug(DEBUG_CMD, &flp_dev, "Read cmd %2x %2x\n", uptr->u3, ch); + uptr->u3 = 0; + } else { + if (ch == '\003') + cpu_unit.flags ^= (0x1 << (UNIT_V_UF + 4)); + } + return SCPE_OK; + } + return r; +} + + +/* + * 00-01 Invalid + * 02 Read Diag + * 03 specify + * 04 sense drive status + * 05 Read + * 06 Write + * x7 Recalibrate + * 08 sense irq + * 09 write deleted + * 0a read id + * 0b Invalid + * 0c read delated + * 0d format + * 0e Invalid + * 0f seek + * 10 version + * 11 scan equal + * 12-6 Invalid + * 17 recalibrate + * 18 invalid + * 19 scan low or equal + * 1a-c invalid + * 1d scan high or equal + * 1e invalid + * 1f recalibrate + */ + +t_stat +flp_boot(int32 unit_num, DEVICE *dptr) +{ + UNIT *fluptr = &dptr->units[unit_num]; + int i = 0; + + if (unit_num != 0) + return SCPE_ARG; + + if ((fluptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + fluptr->up8 = (void *)fluptr; + /* Set up for generic read of track 2 */ + flp_dcb.gorder = 0; + flp_dcb.sorder = 0; + flp_dcb.addr = 0x3e000; + flp_dcb.count = 8192; + flp_dcb.xcount = 0; + flp_dcb.order = 0x46; + flp_dcb.hd = 0; + flp_dcb.cyl = 2; + flp_dcb.sect = 1; + sim_debug(DEBUG_DETAIL, dptr, + "Boot floppy go=%2x so=%2x a=%6x c=%4x o=%2x h=%2x t=%2x s=%2x\n", + flp_dcb.gorder, flp_dcb.sorder, flp_dcb.addr, flp_dcb.count, flp_dcb.order, flp_dcb.hd, + flp_dcb.cyl, flp_dcb.sect); + flp_dcb.cmd[i++] = flp_dcb.order; + flp_dcb.cmd[i++] = flp_dcb.hd; + flp_dcb.cmd[i++] = flp_dcb.cyl; + flp_dcb.cmd[i++] = 0; + flp_dcb.cmd[i++] = flp_dcb.sect; + flp_dcb.cmd[i++] = flp_data[0].n; /* N */ + flp_dcb.cmd[i++] = flp_data[0].eot; /* EOT */ + flp_dcb.cmd[i++] = flp_data[0].gpl; /* GPL */ + flp_dcb.cmd[i++] = flp_data[0].dtl; /* DTL */ + fluptr->PHASE = PHASE_SEEK; + flp_dcb.stat[0] = (flp_dcb.cmd[1] & 0x7); + flp_dcb.stat_len = 0; + + /* Set CPU into idle state */ + cpu_boot(0); + sim_activate(fluptr, 20); + return SCPE_OK; +} + + + +t_stat +flp_reset(DEVICE *dp) +{ + flp_unit[0].DCB = 0x3c000; + flp_unit[0].STATUS = 0; + flp_unit[0].PHASE = PHASE_IDLE; + flp_unit[1].PHASE = PHASE_IDLE; + flp_unit[2].u3 = 0; + sim_clock_coschedule (&flp_unit[2], 1000); + return SCPE_OK; +} + +/* Attach routine */ +t_stat +flp_attach(UNIT *uptr, CONST char *cptr) +{ + char header[4]; + t_stat r; + + r = attach_unit(uptr, cptr); /* attach unit */ + if ( r != SCPE_OK) /* error? */ + return r; + + /* Determine length of this disk */ + uptr->capac = sim_fsize(uptr->fileref); + + if(uptr->capac > 0) { + char *rtn = fgets(header, 4, uptr->fileref); + if((rtn != NULL) && strncmp(header, "IMD", 3)) { + sim_printf("Only IMD disk images are supported\n"); + return SCPE_OPENERR; + } + } else { + /* create a disk image file in IMD format. */ + if (diskCreate(uptr->fileref, "Ridge32") != SCPE_OK) { + sim_printf("Failed to create IMD disk.\n"); + return SCPE_OPENERR; + } + uptr->capac = sim_fsize(uptr->fileref); + } + + uptr->up7 = (void *)diskOpen(uptr->fileref, TRUE); + uptr->CYL = 0; + return SCPE_OK; +} + + +/* Detach routine */ +t_stat flp_detach(UNIT *uptr) +{ + t_stat r; + int8 i; + + r = diskClose((DISK_INFO **)&uptr->up7); + if (r != SCPE_OK) + return r; + + uptr->up7 = NULL; + r = detach_unit(uptr); /* detach unit */ + if (r != SCPE_OK) + return r; + + return SCPE_OK; +} + + + diff --git a/Ridge32/ridge32_iobus.c b/Ridge32/ridge32_iobus.c new file mode 100644 index 0000000..cc00a73 --- /dev/null +++ b/Ridge32/ridge32_iobus.c @@ -0,0 +1,428 @@ +/* Ridge32_iobus.c: Ridge 32 I/O bus simulation. + + Copyright (c) 2019, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + + The Ridge 32 system allowed for several different I/O controllers to be put + on the bus. Priority was determined by position on the bus. Because some of + the devices actually controlled several different unit types, the iobus module + is used to tell the simulator what boards are installed and what device + address they are at. + + Devices addressed as 1 and 2 are special as they were the only two that + could be bootstraped. + + The simulator supports up to 8 I/O boards consisting of: + + FLP0/1: Floppy disk/terminal/line printer. + flp: 4 Units. + term: 8 Units. Unit zero is console. + lpt: 2 Units. + DSK0/1: Priam disk controller 60 and 142MB devices. + dsk0: 4 units. + dsk1: 4 units. + SMD0/1: SMD disk controller. + smd0: 4 units. + smd1: 4 units. + DISP: Monochrome display. + TAPE: Tape Controller. + tp: 4 units. + DRNIU: DR11 interface to NIU-150 network interface. + niu: 1 unit. + Empty: No card installed. + +*/ + +#include "ridge32_defs.h" + + + +int empty_read(uint32 dev, uint32 *data); +int empty_write(uint32 dev, uint32 data); +int empty_iord(uint32 *data); + +DIB *dev_table[256]; /* Device table */ +uint8 slot_dev[8]; /* Device in slot */ + +DIB null_dev = {0, 0, &empty_read, &empty_write, &empty_iord}; + + + +uint8 +io_dcbread_byte(UNIT *uptr, int off) +{ + uint32 word; + + word = M[(uptr->DCB + off) >> 2]; + word >>= 8 * (3 - (off & 0x3)); + word &= 0xff; + return (uint8) word; +} + +uint16 +io_dcbread_half(UNIT *uptr, int off) +{ + uint32 word1; + uint32 word2; + + word1 = M[(uptr->DCB + off) >> 2]; + word1 >>= 8 * (3 - (off & 0x3)); + word1 &= 0xff; + word2 = M[(uptr->DCB + off + 1) >> 2]; + word2 >>= 8 * (3 - ((off + 1) & 0x3)); + word2 &= 0xff; + return (uint16) ((word1 << 8) | word2); +} + +uint32 +io_dcbread_addr(UNIT *uptr, int off) +{ + uint32 word1; + uint32 word2; + uint32 word3; + + word1 = M[(uptr->DCB + off) >> 2]; + word1 >>= 8 * (3 - (off & 0x3)); + word1 &= 0xff; + word2 = M[(uptr->DCB + off + 1) >> 2]; + word2 >>= 8 * (3 - ((off + 1) & 0x3)); + word2 &= 0xff; + word3 = M[(uptr->DCB + off + 2) >> 2]; + word3 >>= 8 * (3 - ((off + 2) & 0x3)); + word3 &= 0xff; + return ((word1 << 16) | (word2 << 8) | word3); +} + +void +io_dcbread_blk(UNIT *uptr, int off, uint8 *data, int sz) +{ + io_read_blk(uptr->DCB + off, data, sz); +} + +void +io_read_blk(int addr, uint8 *data, int sz) +{ + uint32 word; + int i; + + for (i = 0; sz > 0; sz--, addr++) { + word = M[(addr & 0xffffff) >> 2]; + word >>= 8 * (3 - (addr & 0x3)); + data[i++] = (uint8)(word & 0xff); + if ((addr & 3) == 0) { + word = M[addr >> 2]; + sim_debug(DEBUG_DETAIL, &cpu_dev, "Read %06x, data=%08x '%c%c%c%c'\n", + addr, word, isprint((word >> 24) & 0xff)?(word >> 24)& 0xff:'.', + isprint((word >> 16) & 0xff)?(word >> 16)& 0xff:'.', + isprint((word >> 8) & 0xff)?(word >> 8)& 0xff:'.', + isprint(word & 0xff)?word& 0xff:'.'); + } + } +} + + +void +io_dcbwrite_byte(UNIT *uptr, int off, uint8 data) +{ + int offset; + int addr = uptr->DCB + off; + + offset = 8 * (3 - (addr & 0x3)); + M[addr >> 2] &= ~(0xff << offset); + M[addr >> 2] |= ((uint32)(data)) << offset; + return; +} + +void +io_dcbwrite_half(UNIT *uptr, int off, uint16 data) +{ + io_dcbwrite_byte(uptr, off, (uint8)((data >> 8) & 0xff)); + io_dcbwrite_byte(uptr, off+1, (uint8)(data & 0xff)); + return; +} + +void +io_dcbwrite_blk(UNIT *uptr, int off, uint8 *data, int sz) +{ + io_write_blk(uptr->DCB + off, data, sz); +} + +void +io_write_blk(int addr, uint8 *data, int sz) +{ + int offset; + int i; + + sim_debug(DEBUG_DETAIL, &cpu_dev, "blkWrite %06x, len=%4x\n", addr, sz); + for (i = 0; sz > 0; sz--, addr++) { + offset = 8 * (3 - (addr & 0x3)); + M[addr >> 2] &= ~(0xff << offset); + M[addr >> 2] |= ((uint32)(data[i++])) << offset; + if ((addr & 3) == 3) { + uint32 word = M[addr >> 2]; + sim_debug(DEBUG_DETAIL, &cpu_dev, "Write %06x, data=%08x '%c%c%c%c'\n", + addr, word, isprint((word >> 24) & 0xff)?(word >> 24)& 0xff:'.', + isprint((word >> 16) & 0xff)?(word >> 16)& 0xff:'.', + isprint((word >> 8) & 0xff)?(word >> 8)& 0xff:'.', + isprint(word & 0xff)?word& 0xff:'.'); + } + } +} + +int +io_read(uint32 dev_data, uint32 *data) +{ + int dev; + int r; + + dev = (dev_data >> 24) & 0xff; + r = dev_table[dev]->io_read(dev_data, data); + sim_debug(DEBUG_CMD, &cpu_dev, "Read %02x, data=%08x\n", dev, *data); + return r; +} + +int +io_write(uint32 dev_data, uint32 data) +{ + int dev; + int r; + + dev = (dev_data >> 24) & 0xff; + sim_debug(DEBUG_CMD, &cpu_dev, "Write %02x, data=%08x\n", dev, data); + r = dev_table[dev]->io_write(dev_data, data); + return r; +} + + +/* + * Check if interrupt pending. if not return 0. + * If pending, find device and set IORD into data. + * then return 1. + */ +int +io_rd(uint32 *data) +{ + int i; + + if (ext_irq) { + for (i = 0; i < 8; i++) { + if (slot_dev[i] != 0) { + if (dev_table[slot_dev[i]]->io_iord(data)) { + return 1; + } + } + } + ext_irq = 0; + } + return 0; +} + + +int +empty_read(uint32 dev, uint32 *data) +{ + return 2; +} + +int +empty_write(uint32 dev, uint32 data) +{ + return 2; +} + +int +empty_iord(uint32 *data) +{ + /* Should never occur */ + return 0; +} + + +t_stat +chan_set_devs() +{ + int i, j; + + for (i = 0; i < 256; i++) { + dev_table[i] = &null_dev; /* Empty device */ + } + for (i = 0; i < 8; i++) { /* Slots are empty */ + slot_dev[i] = 0; + } + + /* Build channel array */ + for (i = 0; sim_devices[i] != NULL; i++) { + DEVICE *dptr = sim_devices[i]; + DIB *dibp = (DIB *) dptr->ctxt; + int addr; + int slot; + + /* If no DIB, not channel device */ + if (dibp == NULL) + continue; + /* Skip disabled devices */ + if (dptr->flags & DEV_DIS) + continue; + addr = dibp->dev_num; + slot = dibp->slot_num; + if (dev_table[addr] != &null_dev) { + fprintf(stderr, "Device conflict\n\r"); + return SCPE_IERR; + } + if (slot_dev[slot] != 0) { + fprintf(stderr, "Slot error\n\r"); + return SCPE_IERR; + } + dev_table[addr] = dibp; + slot_dev[slot] = addr; + } + return SCPE_OK; +} + +/* Sets the address of a device */ +t_stat +set_dev_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + t_value newdev; + t_stat r; + int addr; + int slot; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + + newdev = get_uint (cptr, 16, 0xff, &r); + + if (r != SCPE_OK) + return r; + + addr = dibp->dev_num; + slot = dibp->slot_num; + + /* Clear out existing entry */ + dev_table[addr] = &null_dev; + + /* Check if device already at newdev */ + if (dev_table[newdev] != &null_dev) + r = SCPE_ARG; + + /* If not, point to new dev, else restore old */ + if (r == SCPE_OK) + addr = newdev; + + /* Update device entry */ + dev_table[addr] = dibp; + return r; +} + +t_stat +show_dev_addr(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + DEVICE *dptr; + DIB *dibp; + int addr; + + + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + + addr = dibp->dev_num; + fprintf(st, "dev=%02x", addr); + return SCPE_OK; +} + + + +/* Put a device at a specific slot */ +t_stat +set_slot_num(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + t_value newslot; + t_stat r; + int addr; + int slot; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + + newslot = get_uint (cptr, 16, 0x8, &r); + + if (r != SCPE_OK) + return r; + + if (newslot > 8) + return SCPE_ARG; + + addr = dibp->dev_num; + slot = dibp->slot_num; + + slot_dev[slot] = 0; + slot_dev[newslot] = addr; + return r; +} + +t_stat +show_slot_num(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + DEVICE *dptr; + DIB *dibp; + int addr; + int slot; + + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + slot = dibp->slot_num; + fprintf(st, "slot=%d", slot); + return SCPE_OK; +} + diff --git a/Ridge32/ridge32_sys.c b/Ridge32/ridge32_sys.c new file mode 100644 index 0000000..e9f1411 --- /dev/null +++ b/Ridge32/ridge32_sys.c @@ -0,0 +1,821 @@ +/* ridge32_sys.c: Ridge 32 Simulator system interface. + + Copyright (c) 2019, Richard Cornwell + + 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. + +*/ + +#include "ridge32_defs.h" +#include +#include "sim_imd.h" + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern uint32 *M; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "Ridge 32"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 16; + +DEVICE *sim_devices[] = { + &cpu_dev, + &flp_dev, + &dsk_dev, + NULL +}; + + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"TRAP", DEBUG_TRAP, "Show trap information"}, + {"EXP", DEBUG_EXP, "Show exception information"}, + {"INST", DEBUG_INST, "Show instruction execution"}, + {0, 0} +}; + + +const char *sim_stop_messages[] = { + "Unknown error", + "HALT", + "Breakpoint", +}; + + + +/* Load a image file into memory. */ + +t_stat sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + DISK_INFO *disk; + uint8 buf[1024]; + int i, sect; + uint32 flags; + uint32 len; + uint32 addr = 0x3e000; + uint32 data; + uint32 mask; + uint32 pa; + int offset; + + disk = diskOpen(fileref, TRUE); + + fprintf(stderr, " %06x ", addr); + for (sect = 1; sect < 17; sect++) { + if (sectRead(disk, 2, 0, sect, buf, 1024, &flags, &len) == SCPE_OK) { + for (i = 0; i > 2; + mask = 0xff; + data = buf[i]; + data <<= offset; + mask <<= offset; + M[pa] &= ~mask; + M[pa] |= data; + fprintf(stderr, "%02x ", buf[i]); + addr++; + if ((i & 0xf) == 0xf) + fprintf(stderr, "\n %06x ", addr); + } + } else { + fprintf(stderr, "Read error %d\n", sect); + } + } + + + diskClose(&disk); + +return SCPE_NOFNC; +} + +/* Symbol tables */ +typedef struct _opcode { + uint8 opbase; + char *name; + uint8 type; +} t_opcode; + +#define RZ 0 /* Zero register */ +#define R1 1 /* One register */ +#define RR 2 /* Register to register */ +#define RI 3 /* Short immediate to register */ +#define RX 4 /* Register index */ +#define RN 5 /* Number */ +#define M 6 /* Maint instruction */ +#define IND 0x8 /* Indexed */ +#define COND 0x10 /* Conditional */ +#define PCREL 0x20 /* PC Relative */ +#define SHORT 0x40 /* Short displacement */ +#define LONG 0x80 /* Long displacment */ + +t_opcode optab[] = { + { OP_MOVE, "MOVE", RR, }, + { OP_NEG, "NEG", RR, }, + { OP_ADD, "ADD", RR, }, + { OP_SUB, "SUB", RR, }, + { OP_MPY, "MPY", RR, }, + { OP_DIV, "DIV", RR, }, + { OP_REM, "REM", RR, }, + { OP_NOT, "NOT", RR, }, + { OP_OR, "OR", RR, }, + { OP_XOR, "XOR", RR, }, + { OP_AND, "AND", RR, }, + { OP_CBIT, "CBIT", RR, }, + { OP_SBIT, "SBIT", RR, }, + { OP_TBIT, "TBIT", RR, }, + { OP_CHK, "CHK", RR, }, + { OP_NOP, "NOP", RR, }, + { OP_MOVEI, "MOVEI", RI, }, + { OP_ADDI, "ADDI", RI, }, + { OP_SUBI, "SUBI", RI, }, + { OP_MPYI, "MPYI", RI, }, + { OP_NOTI, "NOTI", RI, }, + { OP_ANDI, "ANDI", RI, }, + { OP_CHKI, "CHKI", RI, }, + { OP_FIXT, "FIXT", RR, }, + { OP_FIXR, "FIXR", RR, }, + { OP_RNEG, "RNEG", RR, }, + { OP_RADD, "RADD", RR, }, + { OP_RSUB, "RSUB", RR, }, + { OP_RMPY, "RMPY", RR, }, + { OP_RDIV, "RDIV", RR, }, + { OP_MAKERD, "MAKERD", RR, }, + { OP_LCOMP, "LCOMP", RR, }, + { OP_FLOAT, "FLOAT", RR, }, + { OP_RCOMP, "RCOMP", RR, }, + { OP_EADD, "EADD", RR, }, + { OP_ESUB, "ESUB", RR, }, + { OP_EMPY, "EMPY", RR, }, + { OP_EDIV, "EDIV", RR, }, + { OP_DFIXT, "DFIXT", RR, }, + { OP_DFIXR, "DFIXR", RR, }, + { OP_DRNEG, "DRNEG", RR, }, + { OP_DRADD, "DRADD", RR, }, + { OP_DRSUB, "DRSUB", RR, }, + { OP_DRMPY, "DRMPY", RR, }, + { OP_DRDIV, "DRDIV", RR, }, + { OP_MAKEDR, "MAKEDR", RR, }, + { OP_DCOMP, "DCOMP", RR, }, + { OP_DFLOAT, "DFLOAT", RR, }, + { OP_DRCOMP, "DRCOMP", RR, }, + { OP_TRAP, "TRAP", RN, }, + { OP_SUS, "SUS", RR, }, + { OP_LUS, "LUS", RR, }, + { OP_RUM, "RUM", RZ, }, + { OP_LDREGS, "LDREGS", RR, }, + { OP_TRANS, "TRANS", RR, }, + { OP_DIRT, "DIRT", RR, }, + { OP_MOVESR, "MOVESR", RR, }, + { OP_MOVERS, "MOVERS", RR, }, + { OP_MAINT, "", M, }, + { OP_READ, "READ", RR, }, + { OP_WRITE, "WRITE", RR, }, + { 0x50, "TEST", RR|COND, }, + { 0x51, "TEST", RR|COND, }, + { 0x52, "TEST", RR|COND, }, + { 0x54, "TESTI", RI|COND, }, + { 0x55, "TESTI", RI|COND, }, + { 0x56, "TESTI", RI|COND, }, + { 0x58, "TEST", RR|COND, }, + { 0x59, "TEST", RR|COND, }, + { 0x5A, "TEST", RR|COND, }, + { 0x5C, "TESTI", RI|COND, }, + { 0x5D, "TESTI", RI|COND, }, + { 0x5E, "TESTI", RI|COND, }, + { OP_CALLR, "CALLR", RR, }, + { OP_RET, "RET", RR, }, + { OP_KCALL, "KCALL", RN, }, + { OP_LSL, "LSL", RR, }, + { OP_LSR, "LSR", RR, }, + { OP_ASL, "ASL", RR, }, + { OP_ASR, "ASR", RR, }, + { OP_DLSL, "DLSL", RR, }, + { OP_DLSR, "DLSR", RR, }, + { OP_CSL, "CSL", RR, }, + { OP_SEB, "SEB", RR, }, + { OP_LSLI, "LSLI", RI, }, + { OP_LSRI, "LSRI", RI, }, + { OP_ASLI, "ASLI", RI, }, + { OP_ASRI, "ASRI", RI, }, + { OP_DLSLI, "DLSLI", RI, }, + { OP_DLSRI, "DLSRI", RI, }, + { OP_CSLI, "CSLI", RI, }, + { OP_SEH, "SEH", RR, }, + { 0x80, "BR", RR|COND|PCREL|SHORT, }, + { 0x82, "BR", RR|COND|PCREL|SHORT, }, + { 0x83, "CALL", R1|PCREL|SHORT, }, + { 0x84, "BR", RR|COND|PCREL|SHORT, }, + { 0x85, "BR", RI|COND|PCREL|SHORT, }, + { 0x86, "BR", RI|COND|PCREL|SHORT, }, + { 0x87, "LOOP", RI|PCREL|SHORT, }, + { 0x88, "BR", RR|COND|PCREL|SHORT, }, + { 0x8A, "BR", RR|COND|PCREL|SHORT, }, + { 0x8B, "BR", RZ|PCREL|SHORT, }, + { 0x8C, "BR", RR|COND|PCREL|SHORT, }, + { 0x8D, "BR", RI|COND|PCREL|SHORT, }, + { 0x8E, "BR", RI|COND|PCREL|SHORT, }, + { 0x90, "BR", RR|COND|PCREL|LONG, }, + { 0x92, "BR", RR|COND|PCREL|LONG, }, + { 0x93, "CALL", R1|PCREL|LONG, }, + { 0x94, "BR", RR|COND|PCREL|LONG, }, + { 0x95, "BR", RI|COND|PCREL|LONG, }, + { 0x96, "BR", RI|COND|PCREL|LONG, }, + { 0x97, "LOOP", RI|PCREL|LONG, }, + { 0x98, "BR", RR|COND|PCREL|LONG, }, + { 0x9A, "BR", RR|COND|PCREL|LONG, }, + { 0x9B, "BR", RZ|PCREL|LONG, }, + { 0x9C, "BR", RR|COND|PCREL|LONG, }, + { 0x9D, "BR", RI|COND|PCREL|LONG, }, + { 0x9E, "BR", RI|COND|PCREL|LONG, }, + { 0xA0, "STOREB", RX|SHORT, }, + { 0xA1, "STOREB", RX|IND|SHORT, }, + { 0xA2, "STOREH", RX|SHORT, }, + { 0xA3, "STOREH", RX|IND|SHORT, }, + { 0xA6, "STORE", RX|SHORT, }, + { 0xA7, "STORE", RX|IND|SHORT, }, + { 0xA8, "STORED", RX|SHORT, }, + { 0xA9, "STORED", RX|IND|SHORT, }, + { 0xB0, "STOREB", RX|LONG, }, + { 0xB1, "STOREB", RX|IND|LONG, }, + { 0xB2, "STOREH", RX|LONG, }, + { 0xB3, "STOREH", RX|IND|LONG, }, + { 0xB6, "STORE", RX|LONG, }, + { 0xB7, "STORE", RX|IND|LONG, }, + { 0xB8, "STORED", RX|LONG, }, + { 0xB9, "STORED", RX|IND|LONG, }, + { 0xC0, "LOADB", RX|SHORT, }, + { 0xC1, "LOADB", RX|IND|SHORT, }, + { 0xC2, "LOADH", RX|SHORT, }, + { 0xC3, "LOADH", RX|IND|SHORT, }, + { 0xC6, "LOAD", RX|SHORT, }, + { 0xC7, "LOAD", RX|IND|SHORT, }, + { 0xC8, "LOADD", RX|SHORT, }, + { 0xC9, "LOADD", RX|IND|SHORT, }, + { 0xCE, "LADDR", RX|SHORT, }, + { 0xCF, "LADDR", RX|IND|SHORT, }, + { 0xD0, "LOADB", RX|LONG, }, + { 0xD1, "LOADB", RX|IND|LONG, }, + { 0xD2, "LOADH", RX|LONG, }, + { 0xD3, "LOADH", RX|IND|LONG, }, + { 0xD6, "LOAD", RX|LONG, }, + { 0xD7, "LOAD", RX|IND|LONG, }, + { 0xD8, "LOADD", RX|LONG, }, + { 0xD9, "LOADD", RX|IND|LONG, }, + { 0xDE, "LADDR", RX|LONG, }, + { 0xDF, "LADDR", RX|IND|LONG, }, + { 0xE0, "LOADBP", RX|PCREL|SHORT, }, + { 0xE1, "LOADBP", RX|IND|PCREL|SHORT, }, + { 0xE2, "LOADHP", RX|PCREL|SHORT, }, + { 0xE3, "LOADHP", RX|IND|PCREL|SHORT, }, + { 0xE6, "LOADP", RX|PCREL|SHORT, }, + { 0xE7, "LOADP", RX|IND|PCREL|SHORT, }, + { 0xE8, "LOADDP", RX|PCREL|SHORT, }, + { 0xE9, "LOADDP", RX|IND|PCREL|SHORT, }, + { 0xEE, "LADDRP", RX|PCREL|SHORT, }, + { 0xEF, "LADDRP", RX|IND|PCREL|SHORT, }, + { 0xF0, "LOADBP", RX|PCREL|LONG, }, + { 0xF1, "LOADBP", RX|IND|PCREL|LONG, }, + { 0xF2, "LOADHP", RX|PCREL|LONG, }, + { 0xF3, "LOADHP", RX|IND|PCREL|LONG, }, + { 0xF6, "LOADP", RX|PCREL|LONG, }, + { 0xF7, "LOADP", RX|IND|PCREL|LONG, }, + { 0xF8, "LOADDP", RX|PCREL|LONG, }, + { 0xF9, "LOADDP", RX|IND|PCREL|LONG, }, + { 0xFE, "LADDRP", RX|PCREL|LONG, }, + { 0xFF, "LADDRP", RX|IND|PCREL|LONG, }, + { 0, NULL, 0 } +}; + +CONST char *cond[] = { + ">", "<", "=", "", ">", "<", "=", "", + "<=", ">=", "<>", "", "<=", ">=", "<>", ""}; + +CONST char *cond_str[] = {"<=", ">=", "<>", ">", "<", "=", NULL }; + +CONST uint8 cond_val[] = { 0x8, 0x9, 0xA, 0x0, 0x1, 0x2, 0xff }; + +CONST char *rone[] = { + "ELOGR", "ELOGW", "MAINT2", "MAINT3", "MAINT4", "TWRITED", "FLUSH", + "TRAPEXIT", "ITEST", "MAINT9", "MACHINEID", "VERSION", "CREG", "RDLOG", + "MAINT14", "MAINT15"}; + + + +void fprint_inst(FILE *of, t_addr addr, t_value *val) { +uint8 inst = val[0]; +int i; +int l = 1; +t_opcode *tab; +uint32 disp = 0; + + for (tab = optab; tab->name != NULL; tab++) { + if (tab->opbase == inst) { + if ((tab->type & 0xf) == M) + fputs(rone[val[1] & 0xF], of); + else + fputs(tab->name, of); + fputc(' ', of); + if (tab->type & (SHORT|LONG)) { + disp = (val[2] << 8) | val[3]; + if (tab->type & LONG) { + disp <<= 16; + disp |= (val[4] << 8) | val[5]; + } else if (disp & 0x8000) { + disp |= 0xffff0000; + } + } + if (tab->type & PCREL) { + disp = (disp + addr) & 0xffffff; + } + switch (tab->type & 0x7) { + case RR: + fprintf(of, "R%d", (val[1] >> 4) & 0xF); + if (tab->type & COND) { + fputs(cond[inst & 0xF], of); + } else { + fputc(',',of); + } + fprintf(of, "R%d", val[1] & 0xF); + if (tab->type & PCREL) { + fputc(',',of); + } + break; + case R1: + fprintf(of, "R%d,", (val[1] >> 4) & 0xF); + break; + case RI: + fprintf(of, "R%d", (val[1] >> 4) & 0xF); + if (tab->type & COND) { + fputs(cond[inst & 0xF], of); + } else { + fputc(',',of); + } + fprintf(of, "%d", val[1] & 0xF); + if (tab->type & (LONG|SHORT)) + fputc(',',of); + /* Fall through */ + case RZ: + break; + case M: + fprintf(of, "R%d", (val[1] >> 4) & 0xF); + break; + case RX: + fprintf(of, "R%d,", (val[1] >> 4) & 0xF); + if (tab->type & IND) + fprintf(of, "R%d,", val[1] & 0xF); + break; + case RN: + fprintf(of, "%d", val[1] & 0xFF); + break; + } + if (tab->type & (LONG|PCREL)) { + fprint_val(of, disp, 16, 32, PV_RZRO); + if (tab->type & LONG) + fputs(",L", of); + } else if (tab->type & SHORT) { + fprint_val(of, disp, 16, 16, PV_RZRO); + } + } + } +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +uint8 inst = *val; +int i; +int l = 1; +int rdx = 16; +t_opcode *tab; +uint32 num; + +if (sw & SWMASK ('D')) + rdx = 10; +else if (sw & SWMASK ('O')) + rdx = 8; +else if (sw & SWMASK ('H')) + rdx = 16; +if (sw & SWMASK ('M')) { + l = 2; + if (inst & 0x80) { + l += 2; + if (inst & 0x10) + l += 2; + } + sw &= ~ SWMASK('F'); /* Can't do F and M at same time */ +} else if (sw & SWMASK('F')) { + l = 4; +} else if (sw & SWMASK('W')) { + l = 2; +} else if (sw & SWMASK('B')) { + l = 1; +} + +if (sw & SWMASK ('C')) { + fputc('\'', of); + for(i = 0; i < l; i++) { + char ch = val[i] & 0xff; + if (ch >= 0x20 && ch <= 0x7f) + fprintf(of, "%c", ch); + else + fputc('_', of); + } + fputc('\'', of); +} +if (sw & SWMASK ('M')) { + i = 0; + num = (uint32)(val[i++] << 8); + num |= (uint32)val[i++]; + fprint_val(of, num, 16, 16, PV_RZRO); + fputc(' ', of); + if (inst & 0x80) { + num = (uint32)(val[i++] << 8); + num |= (uint32)val[i++]; + fprint_val(of, num, 16, 16, PV_RZRO); + if ((inst & 0x10) != 0) { + num = (uint32)(val[i++] << 8); + num |= (uint32)val[i++]; + fprint_val(of, num, 16, 16, PV_RZRO); + } else { + fputs(" ", of); + } + } else { + fputs(" ", of); + } + fputc(' ', of); + fprint_inst(of, addr, val); +} else { + num = 0; + for (i = 0; i < l && i < 4; i++) + num |= (uint32)val[i] << ((l-i-1) * 8); + fprint_val(of, num, rdx, l*8, PV_RZRO); +} + +return -(l-1); +} + +/* + * Collect register name. + */ +t_stat get_reg (CONST char *cptr, CONST char **tptr, int *reg) +{ + while (sim_isspace (*cptr)) cptr++; + if ((*cptr == 'R') || (*cptr == 'r')) /* Skip R */ + cptr++; + if ((*cptr >= '0') && (*cptr <= '9')) { + *reg = *cptr++ - '0'; + if ((*cptr >= '0') && (*cptr <= '9')) + *reg = (*reg * 10) + (*cptr++ - '0'); + if (*reg > 0xf) + return SCPE_ARG; + } else if ((*cptr >= 'a') && (*cptr <= 'f')) + *reg = (*cptr++ - 'a') + 10; + else if ((*cptr >= 'A') && (*cptr <= 'F')) + *reg = (*cptr++ - 'A') + 10; + else + return SCPE_ARG; + while (sim_isspace (*cptr)) cptr++; + *tptr = cptr; + return SCPE_OK;; +} + +/* + * Collect disp in radix. + */ +t_stat get_disp (CONST char *cptr, CONST char **tptr, uint32 radix, uint32 *val) +{ +t_stat r; + +r = SCPE_OK; +*val = (uint32)strtotv (cptr, tptr, radix); +if (cptr == *tptr) + r = SCPE_ARG; +else { + cptr = *tptr; + while (sim_isspace (*cptr)) cptr++; + *tptr = cptr; +} +return r; +} +/* + * Collect n in radix. + */ +t_stat get_n (CONST char *cptr, CONST char **tptr, uint32 radix, uint32 *val) +{ +t_stat r; + +r = SCPE_OK; +*val = (uint32)strtotv (cptr, tptr, radix); +if ((cptr == *tptr) || (*val > 0xff)) + r = SCPE_ARG; +else { + cptr = *tptr; + while (sim_isspace (*cptr)) cptr++; + *tptr = cptr; +} +return r; +} + +/* + * Collect immediate in radix. + */ +t_stat get_imm (CONST char *cptr, CONST char **tptr, uint32 radix, uint32 *val) +{ +t_stat r; + +r = SCPE_OK; +*val = (uint32)strtotv (cptr, tptr, radix); +if ((cptr == *tptr) || (*val > 0xf)) + r = SCPE_ARG; +else { + cptr = *tptr; + while (sim_isspace (*cptr)) cptr++; + *tptr = cptr; +} +return r; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int i; +int l = 1; +int rdx = 16; +char mod = 0; +t_opcode *tab; +t_stat r; +uint32 num; +uint32 max[5] = { 0, 0xff, 0xffff, 0, 0xffffffff }; +CONST char *tptr; +char gbuf[CBUFSIZE]; + +if (sw & SWMASK ('D')) + rdx = 10; +else if (sw & SWMASK ('O')) + rdx = 8; +else if (sw & SWMASK ('H')) + rdx = 16; +if (sw & SWMASK('F')) { + l = 4; +} else if (sw & SWMASK('W')) { + l = 2; +} + +if (sw & SWMASK ('C')) { + cptr = get_glyph_quoted(cptr, gbuf, 0); /* Get string */ + for(i = 0; gbuf[i] != 0; i++) { + val[i] = gbuf[i]; + } + return -(i - 1); +} + +if (sw & SWMASK ('M')) { + cptr = get_glyph(cptr, gbuf, 0); /* Get opcode */ + for (tab = optab; tab->name != NULL; tab++) { + if (sim_strcasecmp(tab->name, gbuf) == 0) + break; + } + if (tab->name == NULL) { + int j; + for (j = 0; j <= 0xF; j++) { + if (sim_strcasecmp(rone[j], gbuf) == 0) { + if ((r = get_reg(cptr, &tptr, &i)) != SCPE_OK) + return r; + val[0] = OP_MAINT; + val[1] = (j << 4) | i; + return -1; + } + } + return SCPE_ARG; + } + val[0] = tab->opbase; + l = 0; + switch (tab->type & 7) { + case RR: + if ((r = get_reg(cptr, &tptr, &i)) != SCPE_OK) + return r; + cptr = tptr; + val[1] = i << 4; + if (tab->type & COND) { + int j; + for (j = 0; cond_str[j] != NULL; j++) { + if (sim_strcasecmp(cond_str[j], gbuf) == 0) { + val[0] |= cond_val[j]; + cptr += (cond_val[j] & 0x8) ? 2 : 1; + break; + } + } + if (cond_val[j] == 0xff) + return SCPE_ARG; + } else { + if (*cptr != ',') + return SCPE_ARG; + cptr++; + } + while (sim_isspace (*cptr)) cptr++; + if ((r = get_reg(cptr, &tptr, &i)) != SCPE_OK) + return r; + val[1] |= i; + cptr = tptr; + if (tab->type & PCREL) { + if (*cptr != ',') + return SCPE_ARG; + cptr++; + while (sim_isspace (*cptr)) cptr++; + if ((r = get_disp(cptr, &tptr, rdx, &num)) != SCPE_OK) + return SCPE_ARG; + cptr = tptr; + if (*cptr == ',') { + cptr++; + while (sim_isspace (*cptr)) cptr++; + if (*cptr != 'L' || *cptr != 'l') + return SCPE_ARG; + val[0] |= 0x10; + l = 4; + } else { + l = 2; + if (num > 0x10000) + return SCPE_ARG; + } + for (i = 0; i < l && i < 4; i++) + val[i+2] = (num >> (((l - 1) - i) * 8)) & 0xff; + } + return -(l-1); + + case R1: + return SCPE_ARG; + + case RI: + if ((r = get_reg(cptr, &tptr, &i)) != SCPE_OK) + return r; + val[1] = i << 4; + cptr = tptr; + if (tab->type & COND) { + int j; + for (j = 0; cond_str[j] != NULL; j++) { + if (sim_strcasecmp(cond_str[j], gbuf) == 0) { + val[0] |= cond_val[j]; + cptr += (cond_val[j] & 0x8) ? 2 : 1; + break; + } + } + if (cond_val[j] == 0xff) + return SCPE_ARG; + } else { + if (*cptr != ',') + return SCPE_ARG; + cptr++; + } + while (sim_isspace (*cptr)) cptr++; + if ((r = get_imm(cptr, &tptr, rdx, &i)) != SCPE_OK) + return r; + val[1] |= i; + cptr = tptr; + if (tab->type & PCREL) { + if (*cptr != ',') + return SCPE_ARG; + cptr++; + while (sim_isspace (*cptr)) cptr++; + if ((r = get_disp(cptr, &tptr, rdx, &num)) != SCPE_OK) + return SCPE_ARG; + cptr = tptr; + if (*cptr == ',') { + cptr++; + while (sim_isspace (*cptr)) cptr++; + if (*cptr != 'L' || *cptr != 'l') + return SCPE_ARG; + val[0] |= 0x10; + l = 4; + } else { + l = 2; + if (num > 0x10000) + return SCPE_ARG; + } + for (i = 0; i < l && i < 4; i++) + val[i+2] = (num >> (((l - 1) - i) * 8)) & 0xff; + } + return -(l-1); + case RZ: + if (tab->type & PCREL) { + if ((r = get_disp(cptr, &tptr, rdx, &num)) != SCPE_OK) + return SCPE_ARG; + cptr = tptr; + if (*cptr == ',') { + cptr++; + while (sim_isspace (*cptr)) cptr++; + if (*cptr != 'L' || *cptr != 'l') + return SCPE_ARG; + val[0] |= 0x10; + l = 4; + } else { + l = 2; + if (num > 0x10000) + return SCPE_ARG; + } + for (i = 0; i < l && i < 4; i++) + val[i+2] = (num >> (((l - 1) - i) * 8)) & 0xff; + } + return -(l-1); + + case RX: + if ((r = get_reg(cptr, &tptr, &i)) != SCPE_OK) + return r; + val[1] = i << 4; + cptr = tptr; + while (sim_isspace (*cptr)) cptr++; + if (*cptr != ',') + return SCPE_ARG; + cptr++; + r = get_reg(cptr, &tptr, &i); + if (r == SCPE_OK) { + val[1] |= num; + cptr = tptr; + if (*cptr != ',') + return SCPE_ARG; + cptr++; + } + if ((r = get_disp(cptr, &tptr, rdx, &num)) != SCPE_OK) + return SCPE_ARG; + cptr = tptr; + if (*cptr == ',') { + cptr++; + while (sim_isspace (*cptr)) cptr++; + if (*cptr != 'L' || *cptr != 'l') + return SCPE_ARG; + val[0] |= 0x10; + l = 4; + } else { + l = 2; + if (num > 0x10000) + return SCPE_ARG; + } + for (i = 0; i < l && i < 4; i++) + val[i+2] = (num >> (((l - 1) - i) * 8)) & 0xff; + return -(l-1); + case RN: + if ((r = get_n(cptr, &tptr, rdx, &i)) != SCPE_OK) + return r; + val[1] = i; + return -1; + } +} +num = get_uint(cptr, rdx, max[l], &r); +for (i = 0; i < l && i < 4; i++) + val[i] = (num >> (((l - 1) - i) * 8)) & 0xff; +return -(l-1); +} +