/* vax630_sysdev.c: MicroVAX II system-specific logic | |
Copyright (c) 2009-2012, Matt Burke | |
This module incorporates code from SimH, Copyright (c) 1998-2008, Robert M Supnik | |
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 | |
THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name(s) of the author(s) shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from the author(s). | |
This module contains the MicroVAX II system-specific registers and devices. | |
rom bootstrap ROM (no registers) | |
nvr non-volatile ROM (no registers) | |
sysd system devices | |
08-Nov-2012 MB First version | |
*/ | |
#include "vax_defs.h" | |
#include <time.h> | |
#ifdef DONT_USE_INTERNAL_ROM | |
#if defined(VAX_620) | |
#define BOOT_CODE_FILENAME "ka620.bin" | |
#else | |
#define BOOT_CODE_FILENAME "ka320.bin" | |
#endif | |
#else /* !DONT_USE_INTERNAL_ROM */ | |
#if defined(VAX_620) | |
#include "vax_ka620_bin.h" /* Defines BOOT_CODE_FILENAME and BOOT_CODE_ARRAY, etc */ | |
#else | |
#include "vax_ka630_bin.h" /* Defines BOOT_CODE_FILENAME and BOOT_CODE_ARRAY, etc */ | |
#endif | |
#endif /* DONT_USE_INTERNAL_ROM */ | |
#define UNIT_V_NODELAY (UNIT_V_UF + 0) /* ROM access equal to RAM access */ | |
#define UNIT_NODELAY (1u << UNIT_V_NODELAY) | |
t_stat vax630_boot (int32 flag, char *ptr); | |
int32 sys_model = 0; /* MicroVAX or VAXstation */ | |
/* Special boot command, overrides regular boot */ | |
CTAB vax630_cmd[] = { | |
{ "BOOT", &vax630_boot, RU_BOOT, | |
"bo{ot} boot simulator\n", NULL, &run_cmd_message }, | |
{ NULL } | |
}; | |
/* KA630 boot/diagnostic register */ | |
#define BDR_DISP 0x0000000F /* LED display */ | |
#define BDR_V_BDC 8 /* boot/diag code */ | |
#define BDR_M_BDC 0x3 | |
#define BDR_BDC (BDR_M_BDC << BDR_V_BDC) | |
#define BDR_V_CPUC 11 /* cpu code */ | |
#define BDR_M_CPUC 0x3 | |
#define BDR_CPUC (BDR_M_CPUC << BDR_V_CPUC) | |
#define BDR_BRKENB 0x00004000 /* break enable */ | |
#define BDR_POK 0x00008000 /* power ok */ | |
#define BDR_RD (BDR_DISP | BDR_BDC | BDR_CPUC | BDR_BRKENB | BDR_POK) | |
#define BDR_WR (BDR_DISP) | |
/* BDR boot/diagnostic codes */ | |
#define BDC_NORM 0x0 /* normal startup */ | |
#define BDC_LNGI 0x1 /* language inquiry */ | |
#define BDC_TSTL 0x2 /* test loop */ | |
#define BDC_SKPM 0x3 /* skip mem test */ | |
/* BDR CPU codes */ | |
#define CPUC_ARB 0x0 /* arbiter */ | |
#define CPUC_AUX1 0x1 /* auxiliary 1 */ | |
#define CPUC_AUX2 0x2 /* auxiliary 2 */ | |
#define CPUC_AUX3 0x3 /* auxiliary 3 */ | |
/* KA630 Memory system error register */ | |
#define MSER_PE 0x00000001 /* Parity Enable */ | |
#define MSER_WWP 0x00000002 /* Write Wrong Parity */ | |
#define MSER_LEB 0x00000008 /* Lost Error Bit */ | |
#define MSER_DQPE 0x00000010 /* DMA Q22 Parity Err */ | |
#define MSER_CQPE 0x00000020 /* CPU Q22 Parity Err */ | |
#define MSER_CLPE 0x00000040 /* CPU Mem Parity Err */ | |
#define MSER_NXM 0x00000080 /* CPU NXM */ | |
#define MSER_MCD0 0x00000100 /* Mem Code 0 */ | |
#define MSER_MCD1 0x00000200 /* Mem Code 1 */ | |
#define MSER_MBZ 0xFFFFFC04 | |
#define MSER_RD (MSER_PE | MSER_WWP | MSER_LEB | \ | |
MSER_DQPE | MSER_CQPE | MSER_CLPE | \ | |
MSER_NXM | MSER_MCD0 | MSER_MCD1) | |
#define MSER_WR (MSER_PE | MSER_WWP) | |
#define MSER_RS (MSER_LEB | MSER_DQPE | MSER_CQPE | MSER_CLPE | MSER_NXM) | |
/* KA630 CPU error address reg */ | |
#define CEAR_LMADD 0x00007FFF /* local mem addr */ | |
#define CEAR_RD (CEAR_LMADD) | |
/* KA630 DMA error address reg */ | |
#define DEAR_LMADD 0x00007FFF /* local mem addr */ | |
#define DEAR_RD (DEAR_LMADD) | |
extern UNIT clk_unit; | |
extern int32 tmr_poll; | |
extern DEVICE vc_dev, lk_dev, vs_dev; | |
uint32 *rom = NULL; /* boot ROM */ | |
uint8 *nvr = NULL; /* non-volatile mem */ | |
int32 conisp, conpc, conpsl; /* console reg */ | |
int32 ka_bdr = BDR_BRKENB; /* KA630 boot diag */ | |
int32 ka_mser = 0; /* KA630 mem sys err */ | |
int32 ka_cear = 0; /* KA630 cpu err */ | |
int32 ka_dear = 0; /* KA630 dma err */ | |
static uint32 rom_delay = 0; | |
t_bool ka_diag_full = FALSE; | |
t_bool ka_hltenab = TRUE; /* Halt Enable / Autoboot flag */ | |
t_stat rom_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); | |
t_stat rom_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); | |
t_stat rom_reset (DEVICE *dptr); | |
t_stat rom_set_diag (UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat rom_show_diag (FILE *st, UNIT *uptr, int32 val, void *desc); | |
t_stat rom_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); | |
const char *rom_description (DEVICE *dptr); | |
t_stat nvr_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); | |
t_stat nvr_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); | |
t_stat nvr_reset (DEVICE *dptr); | |
t_stat nvr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); | |
t_stat nvr_attach (UNIT *uptr, char *cptr); | |
t_stat nvr_detach (UNIT *uptr); | |
const char *nvr_description (DEVICE *dptr); | |
t_stat sysd_reset (DEVICE *dptr); | |
t_stat sysd_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); | |
const char *sysd_description (DEVICE *dptr); | |
int32 rom_rd (int32 pa); | |
int32 nvr_rd (int32 pa); | |
void nvr_wr (int32 pa, int32 val, int32 lnt); | |
int32 ka_rd (int32 pa); | |
void ka_wr (int32 pa, int32 val, int32 lnt); | |
t_stat sysd_powerup (void); | |
int32 con_halt (int32 code, int32 cc); | |
extern int32 qbmap_rd (int32 pa); | |
extern void qbmap_wr (int32 pa, int32 val, int32 lnt); | |
extern int32 qbmem_rd (int32 pa); | |
extern void qbmem_wr (int32 pa, int32 val, int32 lnt); | |
extern int32 vc_mem_rd (int32 pa); | |
extern void vc_mem_wr (int32 pa, int32 val, int32 lnt); | |
extern int32 wtc_rd (int32 pa); | |
extern void wtc_wr (int32 pa, int32 val, int32 lnt); | |
extern void wtc_set_valid (void); | |
extern void wtc_set_invalid (void); | |
extern int32 iccs_rd (void); | |
extern int32 todr_rd (void); | |
extern int32 rxcs_rd (void); | |
extern int32 rxdb_rd (void); | |
extern int32 txcs_rd (void); | |
extern void iccs_wr (int32 dat); | |
extern void todr_wr (int32 dat); | |
extern void rxcs_wr (int32 dat); | |
extern void txcs_wr (int32 dat); | |
extern void txdb_wr (int32 dat); | |
extern void ioreset_wr (int32 dat); | |
/* debugging bitmaps */ | |
#define DBG_REG 0x0001 /* trace read/write registers */ | |
DEBTAB nvr_debug[] = { | |
{"REG", DBG_REG}, | |
{0} | |
}; | |
/* ROM data structures | |
rom_dev ROM device descriptor | |
rom_unit ROM units | |
rom_reg ROM register list | |
*/ | |
UNIT rom_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK, ROMSIZE) }; | |
REG rom_reg[] = { | |
{ NULL } | |
}; | |
MTAB rom_mod[] = { | |
{ UNIT_NODELAY, UNIT_NODELAY, "fast access", "NODELAY", NULL, NULL, NULL, "Disable calibrated ROM access speed" }, | |
{ UNIT_NODELAY, 0, "1usec calibrated access", "DELAY", NULL, NULL, NULL, "Enable calibrated ROM access speed" }, | |
{ 0 } | |
}; | |
DEVICE rom_dev = { | |
"ROM", &rom_unit, rom_reg, rom_mod, | |
1, 16, ROMAWIDTH, 4, 16, 32, | |
&rom_ex, &rom_dep, &rom_reset, | |
NULL, NULL, NULL, | |
NULL, 0, 0, NULL, NULL, NULL, &rom_help, NULL, NULL, | |
&rom_description | |
}; | |
/* NVR data structures | |
nvr_dev NVR device descriptor | |
nvr_unit NVR units | |
nvr_reg NVR register list | |
*/ | |
UNIT nvr_unit = | |
{ UDATA (NULL, UNIT_FIX+UNIT_BINK, NVRSIZE) }; | |
REG nvr_reg[] = { | |
{ NULL } | |
}; | |
DEVICE nvr_dev = { | |
"NVR", &nvr_unit, nvr_reg, NULL, | |
1, 16, NVRAWIDTH, 4, 16, 32, | |
&nvr_ex, &nvr_dep, &nvr_reset, | |
NULL, &nvr_attach, &nvr_detach, | |
NULL, DEV_DEBUG, 0, nvr_debug, NULL, NULL, &nvr_help, NULL, NULL, | |
&nvr_description | |
}; | |
/* SYSD data structures | |
sysd_dev SYSD device descriptor | |
sysd_unit SYSD units | |
sysd_reg SYSD register list | |
*/ | |
UNIT sysd_unit = { UDATA (NULL, 0, 0) }; | |
REG sysd_reg[] = { | |
{ HRDATAD (CONISP, conisp, 32, "console ISP") }, | |
{ HRDATAD (CONPC, conpc, 32, "console PD") }, | |
{ HRDATAD (CONPSL, conpsl, 32, "console PSL") }, | |
{ HRDATAD (BDR, ka_bdr, 16, "KA630 boot diag") }, | |
{ HRDATAD (MSER, ka_mser, 8, "KA630 mem sys err") }, | |
{ HRDATAD (CEAR, ka_cear, 8, "KA630 cpu err") }, | |
{ HRDATAD (DEAR, ka_dear, 8, "KA630 dma err") }, | |
{ HRDATAD (DEAR, ka_dear, 8, "KA630 dma err") }, | |
{ FLDATAD (DIAG, ka_diag_full, 0, "KA630 Full Boot diagnostics") }, | |
{ FLDATAD (HLTENAB, ka_hltenab, 0, "KA630 Autoboot/Halt Enable") }, | |
{ NULL } | |
}; | |
DEVICE sysd_dev = { | |
"SYSD", &sysd_unit, sysd_reg, NULL, | |
1, 16, 16, 1, 16, 8, | |
NULL, NULL, &sysd_reset, | |
NULL, NULL, NULL, | |
NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, | |
&sysd_description | |
}; | |
/* ROM: read only memory - stored in a buffered file | |
Register space access routines see ROM twice | |
ROM access has been 'regulated' to about 1Mhz to avoid issues | |
with testing the interval timers in self-test. Specifically, | |
the VAX boot ROM (ka630.bin) contains code which presumes that | |
the VAX runs at a particular slower speed when code is running | |
from ROM (which is not cached). These assumptions are built | |
into instruction based timing loops. As the host platform gets | |
much faster than the original VAX, the assumptions embedded in | |
these code loops are no longer valid. | |
Code has been added to the ROM implementation to limit CPU speed | |
to about 500K instructions per second. This heads off any future | |
issues with the embedded timing loops. | |
*/ | |
int32 rom_swapb(int32 val) | |
{ | |
return ((val << 24) & 0xff000000) | (( val << 8) & 0xff0000) | | |
((val >> 8) & 0xff00) | ((val >> 24) & 0xff); | |
} | |
volatile int32 rom_loopval = 0; | |
int32 rom_read_delay (int32 val) | |
{ | |
uint32 i, l = rom_delay; | |
if (rom_unit.flags & UNIT_NODELAY) | |
return val; | |
/* Calibrate the loop delay factor when first used. | |
Do this 4 times to and use the largest value computed. */ | |
if (rom_delay == 0) { | |
uint32 ts, te, c = 10000, samples = 0; | |
while (1) { | |
c = c * 2; | |
te = sim_os_msec(); | |
while (te == (ts = sim_os_msec ())); /* align on ms tick */ | |
/* This is merely a busy wait with some "work" that won't get optimized | |
away by a good compiler. loopval always is zero. To avoid smart compilers, | |
the loopval variable is referenced in the function arguments so that the | |
function expression is not loop invariant. It also must be referenced | |
by subsequent code or to avoid the whole computation being eliminated. */ | |
for (i = 0; i < c; i++) | |
rom_loopval |= (rom_loopval + ts) ^ rom_swapb (rom_swapb (rom_loopval + ts)); | |
te = sim_os_msec (); | |
if ((te - ts) < 50) /* sample big enough? */ | |
continue; | |
if (rom_delay < (rom_loopval + (c / (te - ts) / 1000) + 1)) | |
rom_delay = rom_loopval + (c / (te - ts) / 1000) + 1; | |
if (++samples >= 4) | |
break; | |
c = c / 2; | |
} | |
if (rom_delay < 5) | |
rom_delay = 5; | |
} | |
for (i = 0; i < l; i++) | |
rom_loopval |= (rom_loopval + val) ^ rom_swapb (rom_swapb (rom_loopval + val)); | |
return val + rom_loopval; | |
} | |
int32 rom_rd (int32 pa) | |
{ | |
int32 rg = ((pa - ROMBASE) & ROMAMASK) >> 2; | |
return rom_read_delay (rom[rg]); | |
} | |
void rom_wr_B (int32 pa, int32 val) | |
{ | |
int32 rg = ((pa - ROMBASE) & ROMAMASK) >> 2; | |
int32 sc = (pa & 3) << 3; | |
rom[rg] = ((val & 0xFF) << sc) | (rom[rg] & ~(0xFF << sc)); | |
return; | |
} | |
/* ROM examine */ | |
t_stat rom_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) | |
{ | |
uint32 addr = (uint32) exta; | |
if ((vptr == NULL) || (addr & 03)) | |
return SCPE_ARG; | |
if (addr >= ROMSIZE) | |
return SCPE_NXM; | |
*vptr = rom[addr >> 2]; | |
return SCPE_OK; | |
} | |
/* ROM deposit */ | |
t_stat rom_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) | |
{ | |
uint32 addr = (uint32) exta; | |
if (addr & 03) | |
return SCPE_ARG; | |
if (addr >= ROMSIZE) | |
return SCPE_NXM; | |
rom[addr >> 2] = (uint32) val; | |
return SCPE_OK; | |
} | |
/* ROM reset */ | |
t_stat rom_reset (DEVICE *dptr) | |
{ | |
if (rom == NULL) | |
rom = (uint32 *) calloc (ROMSIZE >> 2, sizeof (uint32)); | |
if (rom == NULL) | |
return SCPE_MEM; | |
return SCPE_OK; | |
} | |
t_stat rom_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "Read-only memory (ROM)\n\n"); | |
fprintf (st, "The boot ROM consists of a single unit, simulating the 64KB boot ROM. It has\n"); | |
fprintf (st, "no registers. The boot ROM is loaded with a binary byte stream using the \n"); | |
fprintf (st, "LOAD -r command:\n\n"); | |
fprintf (st, " LOAD -r KA630.BIN load ROM image KA630.BIN\n\n"); | |
fprintf (st, "When the simulator starts running (via the BOOT command), if the ROM has\n"); | |
fprintf (st, "not yet been loaded, an attempt will be made to automatically load the\n"); | |
fprintf (st, "ROM image from the file ka655x.bin in the current working directory.\n"); | |
fprintf (st, "If that load attempt fails, then a copy of the missing ROM file is\n"); | |
fprintf (st, "written to the current directory and the load attempt is retried.\n\n"); | |
fprintf (st, "ROM accesses a use a calibrated delay that slows ROM-based execution to\n"); | |
fprintf (st, "about 500K instructions per second. This delay is required to make the\n"); | |
fprintf (st, "power-up self-test routines run correctly on very fast hosts.\n"); | |
fprint_set_help (st, dptr); | |
fprintf (st, "By default the memory power-up self-tests are skipped as they take a long\n"); | |
fprintf (st, "time to complete. The self-test sequence can be controlled with the\n"); | |
fprintf (st, "following commands:\n\n"); | |
fprintf (st, " SET CPU DIAG=MIN Run minimal diagnostics (skip memory test)\n"); | |
fprintf (st, " SET CPU DIAG=FULL Run full diagnostics\n\n"); | |
return SCPE_OK; | |
} | |
const char *rom_description (DEVICE *dptr) | |
{ | |
return "read-only memory"; | |
} | |
/* NVR: non-volatile RAM - stored in a buffered file */ | |
int32 nvr_rd (int32 pa) | |
{ | |
int32 rg = (pa - NVRBASE) >> 1; | |
int32 result; | |
if (rg < 14) /* watch chip */ | |
result = wtc_rd (pa); | |
else | |
if (rg & 1) | |
result = ((int32)nvr[rg]) << 16; | |
else | |
result = nvr[rg] | (((int32)nvr[rg+1]) << 16); | |
sim_debug (DBG_REG, &nvr_dev, "nvr_rd(pa=0x%X) returns: 0x%X\n", pa, result); | |
return result; | |
} | |
void nvr_wr (int32 pa, int32 val, int32 lnt) | |
{ | |
int32 rg = (pa - NVRBASE) >> 1; | |
uint32 orig_nvr = nvr[rg] | (nvr[rg+1] << 8); | |
if (rg < 14) /* watch chip */ | |
wtc_wr (pa, val, lnt); | |
else { | |
int32 v = val; | |
int32 r = rg; | |
int32 l = lnt; | |
while (l > 0) { | |
nvr[r] = (uint8)v; | |
++r; | |
l -= 2; | |
v = (v >> 16); | |
} | |
sim_debug (DBG_REG, &nvr_dev, "nvr_wr(pa=0x%X,val=0x%04X,lnt=%d) nvr[%02X] was %04X now %04X\n", pa, val, lnt, rg, orig_nvr, nvr[rg] | (nvr[rg+1] << 8)); | |
} | |
} | |
/* NVR examine */ | |
t_stat nvr_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) | |
{ | |
uint32 addr = (uint32) exta; | |
if ((vptr == NULL) || (addr & 03)) | |
return SCPE_ARG; | |
if (addr >= NVRBASE+NVRASIZE) | |
return SCPE_NXM; | |
*vptr = nvr[addr >> 1] | (nvr[(addr >> 1) + 1] << 8); | |
return SCPE_OK; | |
} | |
/* NVR deposit */ | |
t_stat nvr_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) | |
{ | |
uint32 addr = (uint32) exta; | |
if (addr & 03) | |
return SCPE_ARG; | |
if (addr >= NVRBASE+NVRASIZE) | |
return SCPE_NXM; | |
nvr[addr >> 1] = (uint8) val; | |
nvr[(addr >> 1) + 1] = (uint8) (val >> 8); | |
return SCPE_OK; | |
} | |
/* NVR reset */ | |
t_stat nvr_reset (DEVICE *dptr) | |
{ | |
if (nvr == NULL) { | |
nvr = (uint8 *) calloc (NVRSIZE, sizeof (*nvr)); | |
nvr_unit.filebuf = (void *)nvr; | |
} | |
if (nvr == NULL) | |
return SCPE_MEM; | |
return SCPE_OK; | |
} | |
t_stat nvr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "Non-volatile Memory (NVR)\n\n"); | |
fprintf (st, "The NVR simulates %d bytes of battery-backed up memory.\n", NVRSIZE); | |
fprintf (st, "When the simulator starts, NVR is cleared to 0, and the battery-low indicator\n"); | |
fprintf (st, "is set. Alternately, NVR can be attached to a file. This allows the NVR\n"); | |
fprintf (st, "state to be preserved across simulator runs. Successfully attaching an NVR\n"); | |
fprintf (st, "image clears the battery-low indicator.\n\n"); | |
return SCPE_OK; | |
} | |
/* NVR attach */ | |
/* Valid NVRAM contents are required for the Boot ROM to respect the | |
watch chip's CSRD VRT bit. This empty NVRAM image avoids inconsistent | |
ROM behavior the first time the NVR device is attached (to an empty | |
file). Attaching a already existing file will overwrite this initial | |
contents with whatever the NVRAM file contains. */ | |
uint8 nvr_empty_valid[NVRSIZE] = { | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, | |
0x00, 0x00, 0x00, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, | |
0xFF, 0x00, 0x00, 0xFE, 0xFF, 0x00, 0x48, 0x45, 0x41, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
}; | |
t_stat nvr_attach (UNIT *uptr, char *cptr) | |
{ | |
t_stat r; | |
memcpy (nvr, nvr_empty_valid, NVRSIZE); | |
uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE); | |
r = attach_unit (uptr, cptr); | |
if (r != SCPE_OK) | |
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); | |
else { | |
uptr->hwmark = (uint32) uptr->capac; | |
wtc_set_valid (); | |
} | |
return r; | |
} | |
/* NVR detach */ | |
t_stat nvr_detach (UNIT *uptr) | |
{ | |
t_stat r; | |
r = detach_unit (uptr); | |
if ((uptr->flags & UNIT_ATT) == 0) { | |
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); | |
wtc_set_invalid (); | |
} | |
return r; | |
} | |
const char *nvr_description (DEVICE *dptr) | |
{ | |
return "non-volatile memory"; | |
} | |
/* Read KA630 specific IPR's */ | |
int32 ReadIPR (int32 rg) | |
{ | |
int32 val; | |
switch (rg) { | |
case MT_ICCS: /* ICCS */ | |
val = iccs_rd (); | |
break; | |
case MT_RXCS: /* RXCS */ | |
val = rxcs_rd (); | |
break; | |
case MT_RXDB: /* RXDB */ | |
val = rxdb_rd (); | |
break; | |
case MT_TXCS: /* TXCS */ | |
val = txcs_rd (); | |
break; | |
case MT_TXDB: /* TXDB */ | |
val = 0; | |
break; | |
case MT_CONISP: /* console ISP */ | |
val = conisp; | |
break; | |
case MT_CONPC: /* console PC */ | |
val = conpc; | |
break; | |
case MT_CONPSL: /* console PSL */ | |
val = conpsl; | |
break; | |
case MT_SID: /* SID */ | |
#if defined(VAX_620) | |
val = VAX620_SID; | |
#else | |
val = VAX630_SID; | |
#endif | |
break; | |
case MT_NICR: /* NICR */ | |
case MT_ICR: /* ICR */ | |
case MT_TODR: /* TODR */ | |
case MT_CSRS: /* CSRS */ | |
case MT_CSRD: /* CSRD */ | |
case MT_CSTS: /* CSTS */ | |
case MT_CSTD: /* CSTD */ | |
case MT_TBDR: /* TBDR */ | |
case MT_CADR: /* CADR */ | |
case MT_MCESR: /* MCESR */ | |
case MT_CAER: /* CAER */ | |
case MT_SBIFS: /* SBIFS */ | |
case MT_SBIS: /* SBIS */ | |
case MT_SBISC: /* SBISC */ | |
case MT_SBIMT: /* SBIMT */ | |
case MT_SBIER: /* SBIER */ | |
case MT_SBITA: /* SBITA */ | |
case MT_SBIQC: /* SBIQC */ | |
case MT_TBDATA: /* TBDATA */ | |
case MT_MBRK: /* MBRK */ | |
case MT_PME: /* PME */ | |
val = 0; | |
break; | |
default: | |
RSVD_OPND_FAULT; | |
} | |
return val; | |
} | |
/* Write KA630 specific IPR's */ | |
void WriteIPR (int32 rg, int32 val) | |
{ | |
switch (rg) { | |
case MT_ICCS: /* ICCS */ | |
iccs_wr (val); | |
break; | |
case MT_RXCS: /* RXCS */ | |
rxcs_wr (val); | |
break; | |
case MT_RXDB: /* RXDB */ | |
break; | |
case MT_TXCS: /* TXCS */ | |
txcs_wr (val); | |
break; | |
case MT_TXDB: /* TXDB */ | |
txdb_wr (val); | |
break; | |
case MT_IORESET: /* IORESET */ | |
ioreset_wr (val); | |
break; | |
case MT_SID: | |
case MT_CONISP: | |
case MT_CONPC: | |
case MT_CONPSL: /* halt reg */ | |
RSVD_OPND_FAULT; | |
case MT_NICR: /* NICR */ | |
case MT_ICR: /* ICR */ | |
case MT_TODR: /* TODR */ | |
case MT_CSRS: /* CSRS */ | |
case MT_CSRD: /* CSRD */ | |
case MT_CSTS: /* CSTS */ | |
case MT_CSTD: /* CSTD */ | |
case MT_TBDR: /* TBDR */ | |
case MT_CADR: /* CADR */ | |
case MT_MCESR: /* MCESR */ | |
case MT_CAER: /* CAER */ | |
case MT_SBIFS: /* SBIFS */ | |
case MT_SBIS: /* SBIS */ | |
case MT_SBISC: /* SBISC */ | |
case MT_SBIMT: /* SBIMT */ | |
case MT_SBIER: /* SBIER */ | |
case MT_SBITA: /* SBITA */ | |
case MT_SBIQC: /* SBIQC */ | |
case MT_TBDATA: /* TBDATA */ | |
case MT_MBRK: /* MBRK */ | |
case MT_PME: /* PME */ | |
break; | |
default: | |
RSVD_OPND_FAULT; | |
} | |
return; | |
} | |
/* Read/write I/O register space | |
These routines are the 'catch all' for address space map. Any | |
address that doesn't explicitly belong to memory, I/O, or ROM | |
is given to these routines for processing. | |
*/ | |
struct reglink { /* register linkage */ | |
uint32 low; /* low addr */ | |
uint32 high; /* high addr */ | |
int32 (*read)(int32 pa); /* read routine */ | |
void (*write)(int32 pa, int32 val, int32 lnt); /* write routine */ | |
}; | |
struct reglink regtable[] = { | |
{ QBMAPBASE, QBMAPBASE+QBMAPSIZE, &qbmap_rd, &qbmap_wr }, | |
{ ROMBASE, ROMBASE+ROMSIZE+ROMSIZE, &rom_rd, NULL }, | |
{ NVRBASE, NVRBASE+NVRASIZE, &nvr_rd, &nvr_wr }, | |
{ KABASE, KABASE+KASIZE, &ka_rd, &ka_wr }, | |
#if !defined(VAX_620) | |
{ QVMBASE, QVMBASE+QVMSIZE, &vc_mem_rd, &vc_mem_wr }, | |
#endif | |
{ QBMBASE, QBMBASE+QBMSIZE, &qbmem_rd, &qbmem_wr }, | |
{ 0, 0, NULL, NULL } | |
}; | |
/* ReadReg - read register space | |
Inputs: | |
pa = physical address | |
lnt = length (BWLQ) - ignored | |
Output: | |
longword of data | |
*/ | |
int32 ReadReg (uint32 pa, int32 lnt) | |
{ | |
struct reglink *p; | |
for (p = ®table[0]; p->low != 0; p++) { | |
if ((pa >= p->low) && (pa < p->high) && p->read) | |
return p->read (pa); | |
} | |
MACH_CHECK (MCHK_READ); | |
} | |
/* ReadRegU - read register space, unaligned | |
Inputs: | |
pa = physical address | |
lnt = length in bytes (1, 2, or 3) | |
Output: | |
returned data, not shifted | |
*/ | |
int32 ReadRegU (uint32 pa, int32 lnt) | |
{ | |
return ReadReg (pa & ~03, L_LONG); | |
} | |
/* WriteReg - write register space | |
Inputs: | |
pa = physical address | |
val = data to write, right justified in 32b longword | |
lnt = length (BWLQ) | |
Outputs: | |
none | |
*/ | |
void WriteReg (uint32 pa, int32 val, int32 lnt) | |
{ | |
struct reglink *p; | |
for (p = ®table[0]; p->low != 0; p++) { | |
if ((pa >= p->low) && (pa < p->high) && p->write) { | |
p->write (pa, val, lnt); | |
return; | |
} | |
} | |
MACH_CHECK (MCHK_WRITE); | |
} | |
/* WriteRegU - write register space, unaligned | |
Inputs: | |
pa = physical address | |
val = data to write, right justified in 32b longword | |
lnt = length (1, 2, or 3) | |
Outputs: | |
none | |
*/ | |
void WriteRegU (uint32 pa, int32 val, int32 lnt) | |
{ | |
int32 sc = (pa & 03) << 3; | |
int32 dat = ReadReg (pa & ~03, L_LONG); | |
dat = (dat & ~(insert[lnt] << sc)) | ((val & insert[lnt]) << sc); | |
WriteReg (pa & ~03, dat, L_LONG); | |
return; | |
} | |
/* KA630 registers */ | |
int32 ka_rd (int32 pa) | |
{ | |
int32 rg = (pa - KABASE) >> 2; | |
switch (rg) { | |
case 0: /* BDR */ | |
return ka_bdr & BDR_RD; | |
case 1: /* MSER */ | |
return ka_mser & MSER_RD; | |
case 2: /* CEAR */ | |
return ka_cear & CEAR_RD; | |
case 3: /* DEAR */ | |
return ka_dear & DEAR_RD; | |
} | |
return 0; | |
} | |
void ka_wr (int32 pa, int32 val, int32 lnt) | |
{ | |
int32 rg = (pa - KABASE) >> 2; | |
switch (rg) { | |
case 0: /* BDR */ | |
ka_bdr = (ka_bdr & ~BDR_WR) | (val & BDR_WR); | |
break; | |
case 1: /* MSER */ | |
ka_mser = (ka_mser & ~MSER_WR) | (val & MSER_WR); | |
ka_mser = ka_mser & ~(val & MSER_RS); | |
break; | |
case 2: /* CEAR */ | |
case 3: /* DEAR */ | |
break; | |
} | |
return; | |
} | |
int32 sysd_hlt_enb (void) | |
{ | |
return ka_bdr & BDR_BRKENB; | |
} | |
/* Machine check */ | |
int32 machine_check (int32 p1, int32 opc, int32 cc, int32 delta) | |
{ | |
int32 st, p2, acc; | |
if (in_ie) { | |
in_ie = 0; | |
return con_halt(CON_DBLMCK, cc); /* double machine check */ | |
} | |
if (p1 & 0x80) /* mref? set v/p */ | |
p1 = p1 + mchk_ref; | |
p2 = mchk_va + 4; /* save vap */ | |
st = 0; | |
cc = intexc (SCB_MCHK, cc, 0, IE_EXC); /* take normal exception */ | |
if (p1 & 0x80) { /* mref? */ | |
if (!(ka_mser & MSER_CQPE) && !(ka_mser & MSER_CLPE)) | |
ka_mser |= MSER_NXM; | |
} | |
acc = ACC_MASK (KERN); /* in kernel mode */ | |
in_ie = 1; | |
SP = SP - 16; /* push 4 words */ | |
Write (SP, 12, L_LONG, WA); /* # bytes */ | |
Write (SP + 4, p1, L_LONG, WA); /* mcheck type */ | |
Write (SP + 8, p2, L_LONG, WA); /* address */ | |
Write (SP + 12, st, L_LONG, WA); /* state */ | |
in_ie = 0; | |
return cc; | |
} | |
/* Console entry */ | |
int32 con_halt (int32 code, int32 cc) | |
{ | |
int32 temp; | |
conisp = IS; /* save ISP */ | |
conpc = PC; /* save PC */ | |
conpsl = ((PSL | cc) & 0xFFFF00FF) | code; /* PSL, param */ | |
temp = (PSL >> PSL_V_CUR) & 0x7; /* get is'cur */ | |
if (temp > 4) /* invalid? */ | |
conpsl = conpsl | CON_BADPSL; | |
else STK[temp] = SP; /* save stack */ | |
if (mapen) /* mapping on? */ | |
conpsl = conpsl | CON_MAPON; | |
mapen = 0; /* turn off map */ | |
SP = IS; /* set SP from IS */ | |
PSL = PSL_IS | PSL_IPL1F; /* PSL = 41F0000 */ | |
JUMP (ROMBASE); /* PC = 20040000 */ | |
return 0; /* new cc = 0 */ | |
} | |
/* Special boot command - linked into SCP by initial reset | |
Syntax: BOOT {CPU} | |
*/ | |
t_stat vax630_boot (int32 flag, char *ptr) | |
{ | |
char gbuf[CBUFSIZE]; | |
get_glyph (ptr, gbuf, 0); /* get glyph */ | |
if (gbuf[0] && strcmp (gbuf, "CPU")) | |
return SCPE_ARG; /* Only can specify CPU device */ | |
return run_cmd (flag, "CPU"); | |
} | |
/* Bootstrap */ | |
t_stat cpu_boot (int32 unitno, DEVICE *dptr) | |
{ | |
t_stat r; | |
PC = ROMBASE; | |
PSL = PSL_IS | PSL_IPL1F; | |
conisp = 0; | |
conpc = 0; | |
conpsl = PSL_IS | PSL_IPL1F | CON_PWRUP; | |
if (rom == NULL) | |
return SCPE_IERR; | |
if (*rom == 0) { /* no boot? */ | |
r = cpu_load_bootcode (BOOT_CODE_FILENAME, BOOT_CODE_ARRAY, BOOT_CODE_SIZE, TRUE, 0); | |
if (r != SCPE_OK) | |
return r; | |
} | |
return SCPE_OK; | |
} | |
t_stat sysd_set_diag (UNIT *uptr, int32 val, char *cptr, void *desc) | |
{ | |
if (cptr != NULL) ka_diag_full = strcmp(cptr, "MIN"); | |
return SCPE_OK; | |
} | |
t_stat sysd_show_diag (FILE *st, UNIT *uptr, int32 val, void *desc) | |
{ | |
fprintf(st, "DIAG=%s", (ka_diag_full ? "full" :"min")); | |
return SCPE_OK; | |
} | |
t_stat sysd_set_halt (UNIT *uptr, int32 val, char *cptr, void *desc) | |
{ | |
ka_hltenab = val; | |
return SCPE_OK; | |
} | |
t_stat sysd_show_halt (FILE *st, UNIT *uptr, int32 val, void *desc) | |
{ | |
fprintf(st, "%s", ka_hltenab ? "NOAUTOBOOT" : "AUTOBOOT"); | |
return SCPE_OK; | |
} | |
t_stat sysd_show_leds (FILE *st, UNIT *uptr, int32 val, void *desc) | |
{ | |
fprintf (st, "leds=(%s,%s,%s,%s)", ka_bdr&8 ? "ON" : "OFF", | |
ka_bdr&4 ? "ON" : "OFF", | |
ka_bdr&2 ? "ON" : "OFF", | |
ka_bdr&1 ? "ON" : "OFF"); | |
return SCPE_OK; | |
} | |
/* SYSD reset */ | |
t_stat sysd_reset (DEVICE *dptr) | |
{ | |
if (sim_switches & SWMASK ('P')) sysd_powerup (); /* powerup? */ | |
ka_bdr = (BDR_POK | \ | |
((ka_diag_full ? BDC_NORM : BDC_SKPM) << BDR_V_BDC) | \ | |
(CPUC_ARB << BDR_V_CPUC) | \ | |
(ka_hltenab ? BDR_BRKENB : 0) | \ | |
0xF); | |
ka_mser = 0; | |
ka_cear = 0; | |
ka_dear = 0; | |
sim_vm_cmd = vax630_cmd; | |
return SCPE_OK; | |
} | |
const char *sysd_description (DEVICE *dptr) | |
{ | |
return "system devices"; | |
} | |
/* SYSD powerup */ | |
t_stat sysd_powerup (void) | |
{ | |
ka_diag_full = 0; | |
return SCPE_OK; | |
} | |
t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) | |
{ | |
char gbuf[CBUFSIZE]; | |
if ((cptr == NULL) || (!*cptr)) | |
return SCPE_ARG; | |
cptr = get_glyph (cptr, gbuf, 0); | |
if (MATCH_CMD(gbuf, "MICROVAX") == 0) { | |
sys_model = 0; | |
#if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL) | |
vc_dev.flags = vc_dev.flags | DEV_DIS; /* disable QVSS */ | |
lk_dev.flags = lk_dev.flags | DEV_DIS; /* disable keyboard */ | |
vs_dev.flags = vs_dev.flags | DEV_DIS; /* disable mouse */ | |
#endif | |
strcpy (sim_name, "MicroVAX II (KA630)"); | |
reset_all (0); /* reset everything */ | |
} | |
else if (MATCH_CMD(gbuf, "VAXSTATION") == 0) { | |
#if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL) | |
sys_model = 1; | |
vc_dev.flags = vc_dev.flags & ~DEV_DIS; /* enable QVSS */ | |
lk_dev.flags = lk_dev.flags & ~DEV_DIS; /* enable keyboard */ | |
vs_dev.flags = vs_dev.flags & ~DEV_DIS; /* enable mouse */ | |
strcpy (sim_name, "VAXStation II (KA630)"); | |
reset_all (0); /* reset everything */ | |
#else | |
return sim_messagef(SCPE_ARG, "Simulator built without Graphic Device Support"); | |
#endif | |
} | |
else | |
return SCPE_ARG; | |
return SCPE_OK; | |
} | |
t_stat cpu_print_model (FILE *st) | |
{ | |
#if defined(VAX_620) | |
fprintf (st, "rtVAX 1000"); | |
#else | |
fprintf (st, (sys_model ? "VAXstation II" : "MicroVAX II")); | |
#endif | |
return SCPE_OK; | |
} | |
t_stat cpu_model_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "Initial memory size is 16MB.\n\n"); | |
fprintf (st, "The CPU supports the BOOT command and is the only VAX device to do so. Note\n"); | |
fprintf (st, "that the behavior of the bootstrap depends on the capabilities of the console\n"); | |
fprintf (st, "terminal emulator. If the terminal window supports full VT100 emulation\n"); | |
fprintf (st, "(including Multilanguage Character Set support), the bootstrap will ask the\n"); | |
fprintf (st, "user to specify the language; otherwise, it will default to English.\n\n"); | |
fprintf (st, "The simulator is booted with the BOOT command:\n\n"); | |
fprintf (st, " sim> BOOT\n\n"); | |
return SCPE_OK; | |
} | |
t_stat cpu_show_memory (FILE* st, UNIT* uptr, int32 val, void* desc) | |
{ | |
uint32 memsize = (uint32)(MEMSIZE>>20); | |
uint32 baseaddr = 0; | |
struct { | |
uint32 capacity; | |
char *option; | |
} boards[] = { | |
{ 16, "MS630-CA"}, | |
{ 4, "MS630-BB"}, | |
{ 2, "MS630-BA"}, | |
{ 1, "MS630-AA"}, | |
{ 0, NULL}}; | |
int32 i; | |
while (memsize > 1) { | |
for (i=0; boards[i].capacity > memsize; ++i) | |
; | |
if (memsize == 2) | |
i = 3; | |
fprintf(st, "Memory (@0x%08x): %3d Mbytes (%s)\n", baseaddr, boards[i].capacity, boards[i].option); | |
memsize -= boards[i].capacity; | |
baseaddr += boards[i].capacity<<20; | |
} | |
if (memsize) | |
fprintf(st, "Memory (0x%08x): 1 Mbytes (On-Board)\n", baseaddr); | |
return SCPE_OK; | |
} |