1115 lines
36 KiB
C
1115 lines
36 KiB
C
/* 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 RAM (no registers)
|
|
sysd system devices
|
|
|
|
08-Nov-2012 MB First version
|
|
*/
|
|
|
|
#include "vax_defs.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, CONST 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 va_dev, 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 */
|
|
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, const char *cptr, const void *desc);
|
|
t_stat rom_show_diag (FILE *st, UNIT *uptr, int32 val, const 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, CONST 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 lnt);
|
|
int32 nvr_rd (int32 pa, int32 lnt);
|
|
void nvr_wr (int32 pa, int32 val, int32 lnt);
|
|
int32 ka_rd (int32 pa, int32 lnt);
|
|
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, int32 lnt);
|
|
extern void qbmap_wr (int32 pa, int32 val, int32 lnt);
|
|
extern int32 qbmem_rd (int32 pa, int32 lnt);
|
|
extern void qbmem_wr (int32 pa, int32 val, int32 lnt);
|
|
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[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_mapped_addr, (void *)ROMBASE, "Display base address" },
|
|
{ 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 }
|
|
};
|
|
|
|
MTAB nvr_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_mapped_addr, (void *)NVRBASE, "Display base address" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE nvr_dev = {
|
|
"NVR", &nvr_unit, nvr_reg, nvr_mod,
|
|
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") },
|
|
{ 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_rd (int32 pa, int32 lnt)
|
|
{
|
|
int32 rg = ((pa - ROMBASE) & ROMAMASK) >> 2;
|
|
int32 val = rom[rg];
|
|
|
|
if (rom_unit.flags & UNIT_NODELAY)
|
|
return val;
|
|
|
|
return sim_rom_read_with_delay (val);
|
|
}
|
|
|
|
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 lnt)
|
|
{
|
|
int32 rg = (pa + 1 - NVRBASE) >> 1;
|
|
int32 result;
|
|
|
|
if (rg < 14) { /* watch chip */
|
|
result = wtc_rd (rg);
|
|
if (rg & 1)
|
|
result = (result << 16); /* word aligned */
|
|
}
|
|
else {
|
|
result = (nvr[rg] & WMASK) | (((uint32)nvr[rg]) << 16);
|
|
if (pa & 1)
|
|
result = result << 8;
|
|
}
|
|
|
|
sim_debug (DBG_REG, &nvr_dev, "nvr_rd(pa=0x%X) nvr[0x%X] returns: 0x%X\n", pa, rg, result);
|
|
|
|
return result;
|
|
}
|
|
|
|
void nvr_wr (int32 pa, int32 val, int32 lnt)
|
|
{
|
|
int32 rg = (pa + 1 - NVRBASE) >> 1;
|
|
|
|
if (rg < 14) /* watch chip */
|
|
wtc_wr (rg, val);
|
|
else {
|
|
int32 orig_nvr = (int32)nvr[rg];
|
|
|
|
switch (pa & 03) {
|
|
case 0:
|
|
case 2:
|
|
nvr[rg] = (uint8)val;
|
|
break;
|
|
case 1:
|
|
nvr[rg] = 0;
|
|
break;
|
|
}
|
|
|
|
if (lnt > 1)
|
|
sim_debug (DBG_REG, &nvr_dev, "nvr_wr(pa=0x%X,val=0x%04X,lnt=%d) Unexpected write length\n", pa, val, lnt);
|
|
|
|
if (pa & 1)
|
|
sim_debug (DBG_REG, &nvr_dev, "nvr_wr(pa=0x%X,val=0x%04X,lnt=%d) Unexpected write address\n", pa, val, lnt);
|
|
|
|
sim_debug (DBG_REG, &nvr_dev, "nvr_wr(pa=0x%X,val=0x%04X,lnt=%d) nvr[0x%02X] was %04X now %04X\n", pa, val, lnt, rg, orig_nvr, nvr[rg]);
|
|
}
|
|
}
|
|
|
|
/* 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 >= NVRASIZE)
|
|
return SCPE_NXM;
|
|
*vptr = (t_value)(nvr[addr >> 1] | (nvr[(addr >> 1) + 1] << 16));
|
|
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 >= NVRASIZE)
|
|
return SCPE_NXM;
|
|
nvr[addr >> 1] = (uint8)val;
|
|
nvr[(addr >> 1) + 1] = (uint8)(val >> 16);
|
|
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, CONST char *cptr)
|
|
{
|
|
t_stat r;
|
|
int32 saved_sim_quiet = sim_quiet;
|
|
|
|
uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE);
|
|
sim_quiet = 1;
|
|
r = attach_unit (uptr, cptr);
|
|
sim_quiet = saved_sim_quiet;
|
|
if (r != SCPE_OK)
|
|
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE);
|
|
else {
|
|
if (uptr->hwmark == 0)
|
|
memcpy (nvr, nvr_empty_valid, NVRSIZE);
|
|
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(ReadIPR);
|
|
}
|
|
|
|
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(WriteIPR);
|
|
|
|
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(WriteIPR);
|
|
}
|
|
|
|
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, int32 lnt); /* 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 },
|
|
{ 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, lnt);
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct reglink *p;
|
|
int32 val;
|
|
|
|
for (p = ®table[0]; p->low != 0; p++) {
|
|
if ((pa >= p->low) && (pa < p->high) && p->read) {
|
|
if (lnt == L_BYTE)
|
|
val = p->read (pa & ~03, L_LONG);
|
|
else
|
|
val = (p->read (pa & ~03, L_LONG) & WMASK) | (p->read ((pa & ~03) + 2, L_LONG) & (WMASK << 16));
|
|
return val;
|
|
}
|
|
}
|
|
|
|
MACH_CHECK (MCHK_READ);
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* KA630 registers */
|
|
|
|
int32 ka_rd (int32 pa, int32 lnt)
|
|
{
|
|
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, CONST char *ptr)
|
|
{
|
|
char gbuf[CBUFSIZE];
|
|
|
|
if ((ptr = get_sim_sw (ptr)) == NULL) /* get switches */
|
|
return SCPE_INVSW;
|
|
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, CONST 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, CONST void *desc)
|
|
{
|
|
fprintf(st, "DIAG=%s", (ka_diag_full ? "full" :"min"));
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat sysd_set_halt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
ka_hltenab = val;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat sysd_show_halt (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
fprintf(st, "%s", ka_hltenab ? "NOAUTOBOOT" : "AUTOBOOT");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat sysd_show_leds (FILE *st, UNIT *uptr, int32 val, CONST 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, CONST 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 */
|
|
va_dev.flags = va_dev.flags | DEV_DIS; /* disable QDSS */
|
|
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 */
|
|
va_dev.flags = va_dev.flags | DEV_DIS; /* disable QDSS */
|
|
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\n");
|
|
#endif
|
|
}
|
|
else if (MATCH_CMD(gbuf, "VAXSTATIONGPX") == 0) {
|
|
#if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL)
|
|
sys_model = 2;
|
|
vc_dev.flags = vc_dev.flags | DEV_DIS; /* disable QVSS */
|
|
va_dev.flags = va_dev.flags & ~DEV_DIS; /* enable QDSS */
|
|
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/GPX (KA630)");
|
|
reset_all (0); /* reset everything */
|
|
#else
|
|
return sim_messagef(SCPE_ARG, "Simulator built without Graphic Device Support\n");
|
|
#endif
|
|
}
|
|
else
|
|
return SCPE_ARG;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat cpu_print_model (FILE *st)
|
|
{
|
|
#if defined(VAX_620)
|
|
fprintf (st, "rtVAX 1000");
|
|
#else
|
|
switch (sys_model) {
|
|
case 0:
|
|
fprintf (st, "MicroVAX II");
|
|
break;
|
|
case 1:
|
|
fprintf (st, "VAXstation II");
|
|
break;
|
|
case 2:
|
|
fprintf (st, "VAXstation II/GPX");
|
|
break;
|
|
}
|
|
#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, CONST void* desc)
|
|
{
|
|
uint32 memsize = (uint32)(MEMSIZE>>20);
|
|
uint32 baseaddr = 0;
|
|
struct {
|
|
uint32 capacity;
|
|
const 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;
|
|
}
|