Can now boot the last test programs from floppy, and runs INSTRUCTION-C and PAGING-C without errors.
488 lines
16 KiB
C
488 lines
16 KiB
C
/*
|
|
* Copyright (c) 2023 Anders Magnusson.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "sim_defs.h"
|
|
|
|
#include "nd100_defs.h"
|
|
|
|
/*
|
|
* Floppy and Streamer Controller 3112.
|
|
* ND documentation ND-11.021.1
|
|
*
|
|
* Also support Floppy Controller 3027 (Z80 version).
|
|
* ND documentation ND-11.015.1
|
|
*
|
|
* Currently only floppies implemented (no streamer).
|
|
*
|
|
* The device uses eight IOX addresses, but the transfer commands
|
|
* are given in a command block of 12 words in memory.
|
|
*
|
|
* ND uses three floppy formats (but in theory can handle a lot of formats).
|
|
* - SS/SD 8" (type 0): 77*8*512= 315392 bytes.
|
|
* - DS/DD 8" (type 017): 77*8*1024*2= 1261568 bytes.
|
|
* - DS/DD 5 1/4 (type 017?): 80*8*1024*2= 1261568 bytes.
|
|
*/
|
|
|
|
t_stat floppy_svc(UNIT *uptr);
|
|
t_stat floppy_reset (DEVICE *dptr);
|
|
t_stat floppy_boot (int32 unitno, DEVICE *dptr);
|
|
t_stat floppy_attach (UNIT *uptr, CONST char *cptr);
|
|
static int floppy_excmd(void);
|
|
static int floppy_test(UNIT *up);
|
|
static int dtomem(FILE *fp, int daddr, int maddr, int wcnt, int how);
|
|
static int getmval(void);
|
|
|
|
#define FL_NTR 80 /* # tracks/side */
|
|
#define FL_NSC 8 /* # sectors/track */
|
|
#define FL_NSD 2 /* # sides */
|
|
#define FL_NBY 1024 /* # bytes/sector */
|
|
|
|
#define FL_SZ (FL_NTR*FL_NTR*FL_NSD*FL_NBY)
|
|
|
|
/* hardware status reg flags */
|
|
#define FL_ST_IE 0000002 /* interrupt enable (RFT) */
|
|
#define FL_ST_ACT 0000004 /* device active */
|
|
#define FL_ST_RDY 0000010 /* device ready for transfer (RFT) */
|
|
#define FL_ST_ERR 0000020 /* OR of errors */
|
|
#define FL_ST_HE 0000100 /* Hard error (DMA) */
|
|
#define FL_ST_DENS 0140000 /* Dual density ctlr */
|
|
|
|
/* hardware control word */
|
|
#define FL_CW_IE 0000002 /* interrupt enable (RFT) */
|
|
#define FL_CW_AUTO 0000004 /* Activate autoload */
|
|
#define FL_CW_TEST 0000010 /* Test mode */
|
|
#define FL_CW_CLR 0000020 /* Device clear */
|
|
#define FL_CW_ENSTR 0000040 /* Enable streamer */
|
|
#define FL_CW_FCE 0000400 /* Fetch Command and Execute */
|
|
|
|
static int fl_rdata; /* devno + 0, read data */
|
|
static int fl_rstatus; /* devno + 2 (+4) read status */
|
|
static int fl_lcw; /* devno + 3 load control word */
|
|
static int fl_lph; /* devno + 5 load pointer high */
|
|
static int fl_lpl; /* devno + 7 load pointer low */
|
|
|
|
/*
|
|
* The command block (CB) is DMAed from ND100 memory.
|
|
* Word 0-5 are the command part, 06-13 are the status part.
|
|
*
|
|
* 15 8 7 0
|
|
* +---------------------------------------------+
|
|
* 0 | Command word |
|
|
* +---------------------------------------------+
|
|
* 1 | Device address bit 15-0 |
|
|
* +----------------------+----------------------+
|
|
* 2 | Device addr bit 23-16| Memory addr bit 23-16|
|
|
* +----------------------+----------------------+
|
|
* 3 | Memory addr bit 15-0 |
|
|
* +----------------------+----------------------+
|
|
* 4 | Options | Word count bit 23-16 |
|
|
* +----------------------+----------------------+
|
|
* 5 | Word count (or record count) bit 15-0 |
|
|
* +---------------------------------------------+
|
|
* 6 | Status 1 |
|
|
* +---------------------------------------------+
|
|
* 7 | Status 2 |
|
|
* +---------------------------------------------+
|
|
* 10 | Empty | Last addr 23-16 |
|
|
* +---------------------------------------------+
|
|
* 11 | last memory address 15-0 |
|
|
* +---------------------------------------------+
|
|
* 12 | Empty | Rem. words 23-16 |
|
|
* +---------------------------------------------+
|
|
* 13 | Remaining words 15-0 |
|
|
* +---------------------------------------------+
|
|
*
|
|
*/
|
|
|
|
/* CB offsets */
|
|
#define CB_CW 000
|
|
#define CB_DAL 001
|
|
#define CB_DAHMAH 002
|
|
#define CB_MAL 003
|
|
#define CB_OPTWCH 004
|
|
#define CB_WCL 005
|
|
#define CB_ST1 006
|
|
#define CB_ST2 007
|
|
#define CB_LAH 010
|
|
#define CB_LAL 011
|
|
#define CB_REMWH 012
|
|
#define CB_REMWL 013
|
|
|
|
/* Options word (004) */
|
|
#define CB_OPT_WC 0100000 /* set if word count in 4/5, else record */
|
|
|
|
/* Command word (000) */
|
|
#define CW_FL_RD 0000000 /* Read data */
|
|
#define CW_FL_WR 0000001 /* Write data */
|
|
#define CW_FL_EXTST 0000036 /* Read extended status */
|
|
#define CW_FL_RDFMT 0000042 /* Read format */
|
|
#define CW_FL_CMDMSK 077 /* mask for command */
|
|
#define CW_FL_SELSH 6 /* shift for unit */
|
|
#define CW_FL_1K 0001400 /* 1K sectors */
|
|
#define CW_FL_DS 0002000 /* Double sided */
|
|
#define CW_FL_DD 0004000 /* Double density */
|
|
|
|
/* Status 2 */
|
|
#define ST2_FL_BS1K 0000003 /* 1k sectors */
|
|
#define ST2_FL_DS 0000004 /* Double sided */
|
|
#define ST2_FL_DD 0000010 /* Double density */
|
|
#define ST2_FL_514 0000020 /* 5 1/4" floppy */
|
|
|
|
/* soft data structures (for simh) */
|
|
#define state u3 /* current unit state */
|
|
#define U_RDY 00 /* unit idling */
|
|
#define U_READ 01 /* unit reading */
|
|
#define U_WRITE 02 /* unit writing */
|
|
#define U_RDFMT 03 /* Read format */
|
|
#define U_EXTST 04 /* Read extended status */
|
|
|
|
#define devaddr u4 /* unit offset (in words) */
|
|
#define wcnt u5 /* word count */
|
|
#define memaddr u6 /* place in memory */
|
|
|
|
UNIT floppy_unit[] = {
|
|
{ UDATA (&floppy_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
|
|
UNIT_ROABLE, FL_SZ) },
|
|
{ UDATA (&floppy_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
|
|
UNIT_ROABLE, FL_SZ) },
|
|
{ UDATA (&floppy_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
|
|
UNIT_ROABLE, FL_SZ) },
|
|
{ UDATA (&floppy_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
|
|
UNIT_ROABLE, FL_SZ) },
|
|
{ 0 }
|
|
};
|
|
|
|
REG floppy_reg[] = {
|
|
{ ORDATA (RDATA, fl_rdata, 16) },
|
|
{ ORDATA (RSTATUS, fl_rstatus, 16) },
|
|
{ ORDATA (LCW, fl_lcw, 16) },
|
|
{ ORDATA (LPH, fl_lph, 16) },
|
|
{ ORDATA (LPL, fl_lpl, 16) },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB floppy_mod[] = {
|
|
{ MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED",
|
|
&set_writelock, &show_writelock, NULL,
|
|
"Write enable floppy drive" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE floppy_dev = {
|
|
"FLOPPY", floppy_unit, floppy_reg, floppy_mod,
|
|
1, 8, 12, 1, 8, 16,
|
|
NULL, NULL, &floppy_reset,
|
|
&floppy_boot, NULL, NULL,
|
|
NULL, DEV_DISABLE
|
|
};
|
|
|
|
struct intr floppy0_int = { 0, 021 };
|
|
|
|
/*
|
|
* Floppy called via iox instruction.
|
|
*/
|
|
int
|
|
iox_floppy(int addr)
|
|
{
|
|
int n;
|
|
int rv = 0;
|
|
|
|
switch (addr & 07) {
|
|
case 0: /* read data */
|
|
regA = 0;
|
|
break;
|
|
|
|
case 2:
|
|
regA = fl_rstatus;
|
|
break;
|
|
|
|
case 3:
|
|
n = regA;
|
|
if (n & FL_CW_TEST) {
|
|
rv = floppy_test(&floppy_unit[0]);
|
|
break;
|
|
}
|
|
if (n & FL_CW_FCE) {
|
|
rv = floppy_excmd();
|
|
break;
|
|
}
|
|
if (n & FL_CW_IE) { /* Interrupt enable */
|
|
if ((fl_rstatus & (FL_CW_IE|FL_ST_RDY)) == FL_ST_RDY)
|
|
extint(11, &floppy0_int);
|
|
fl_rstatus |= FL_ST_IE;
|
|
break;
|
|
}
|
|
if (n & FL_CW_CLR) { /* reset */
|
|
break;
|
|
}
|
|
return STOP_UNHIOX;
|
|
break;
|
|
|
|
case 5:
|
|
fl_lph = regA;
|
|
break;
|
|
|
|
case 7:
|
|
fl_lpl = regA;
|
|
break;
|
|
|
|
default:
|
|
if ((addr & 1) == 0)
|
|
regA = 0;
|
|
break; /* Unused IOXes are ignored */
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
t_stat
|
|
floppy_reset(DEVICE *dptr)
|
|
{
|
|
fl_rstatus = FL_ST_DENS | FL_ST_RDY;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Estimate floppy format based on disk size and return status2 word.
|
|
* - SS/SD = 315392 bytes, c/h/s 77/1/8 (512b sectors)
|
|
* - DS/DD = 1261568 bytes, c/h/s 77/2/8 (1024b sectors)
|
|
* - DS/DD 5 1/4" = 1310720 bytes, c/h/s 80/2/8 (1024b sectors)
|
|
*/
|
|
static int
|
|
readfmt(UNIT *up)
|
|
{
|
|
if (sim_fseek(up->fileref, 0, SEEK_END) < 0)
|
|
return -1;
|
|
switch (sim_ftell(up->fileref)) {
|
|
case 315392:
|
|
return 0;
|
|
case 1261568:
|
|
return ST2_FL_DD|ST2_FL_DS|ST2_FL_BS1K;
|
|
case 1310720:
|
|
return /* ST2_FL_514| */ST2_FL_DD|ST2_FL_DS|ST2_FL_BS1K;
|
|
default:
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
t_stat
|
|
floppy_svc(UNIT *uptr)
|
|
{
|
|
int cbaddr = getmval();
|
|
int lah = 0, lal = 0;
|
|
int status2 = 0;
|
|
int remwh = 0, remwl = 0;
|
|
|
|
if ((fl_rstatus & FL_ST_ACT) == 0)
|
|
return STOP_UNHIOX;
|
|
|
|
switch (uptr->state) {
|
|
case U_READ:
|
|
dtomem(uptr->fileref, uptr->devaddr,
|
|
uptr->memaddr, uptr->wcnt, PM_DMA);
|
|
lah = (uptr->memaddr + uptr->wcnt) >> 16;
|
|
lal = (uptr->memaddr + uptr->wcnt) & 0177777;
|
|
break;
|
|
|
|
case U_RDFMT:
|
|
if ((status2 = readfmt(uptr)) < 0)
|
|
goto err;
|
|
break;
|
|
|
|
case U_EXTST: /* XXX no docs found...? */
|
|
lah = (uptr->memaddr + uptr->wcnt) >> 16;
|
|
lal = (uptr->memaddr + uptr->wcnt) & 0177777;
|
|
remwh = 0100000; /* XXX */
|
|
remwl = 1; /* XXX */
|
|
pwrmem(uptr->memaddr, 06201, PM_DMA); /* XXX */
|
|
break;
|
|
|
|
case U_WRITE:
|
|
default:
|
|
return STOP_UNHIOX;
|
|
}
|
|
|
|
pwrmem(cbaddr+CB_ST1, FL_ST_RDY, PM_DMA);
|
|
pwrmem(cbaddr+CB_ST2, status2, PM_DMA); /* set after error or rdfmt */
|
|
pwrmem(cbaddr+CB_LAH, lah, PM_DMA);
|
|
pwrmem(cbaddr+CB_LAL, lal, PM_DMA);
|
|
pwrmem(cbaddr+CB_REMWH, remwh, PM_DMA);
|
|
pwrmem(cbaddr+CB_REMWL, remwl, PM_DMA);
|
|
|
|
fl_rstatus &= ~FL_ST_ACT;
|
|
fl_rstatus |= FL_ST_RDY;
|
|
if (fl_rstatus & FL_ST_IE)
|
|
extint(11, &floppy0_int);
|
|
|
|
return SCPE_OK;
|
|
|
|
err:
|
|
return STOP_UNHIOX;
|
|
}
|
|
|
|
/*
|
|
* Get physical memory address for command block.
|
|
*/
|
|
static int
|
|
getmval(void)
|
|
{
|
|
return fl_lpl + ((fl_lph & 0377) << 8);
|
|
}
|
|
|
|
/*
|
|
* Read a block from file fp, position daddr (bytes) to ND100 memory maddr
|
|
* wcnt words.
|
|
* Return 0 or fail.
|
|
*/
|
|
static int
|
|
dtomem(FILE *fp, int daddr, int maddr, int wcnt, int how)
|
|
{
|
|
unsigned char *wp;
|
|
int bcnt = wcnt *2;
|
|
int i;
|
|
|
|
wp = malloc(wcnt * 2); /* bounce buffer */
|
|
if (sim_fseek(fp, daddr, SEEK_SET) < 0)
|
|
return STOP_UNHIOX;
|
|
if (sim_fread(wp, bcnt, 1, fp) < 0)
|
|
return STOP_UNHIOX;
|
|
for (i = 0; i < bcnt; i += 2)
|
|
wrmem(maddr++, (wp[i] << 8) | wp[i+1], how);
|
|
free(wp);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat
|
|
floppy_boot(int32 unitno, DEVICE *dptr)
|
|
{
|
|
printf("floppy_boot \n");
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* It seems that some of the boot programs uses the Z80 test programs
|
|
* on the controller card. We try to mimic the behaviour.
|
|
*
|
|
* This data structure (in ND100) is used in test T13 and T14.
|
|
*
|
|
* 15 8 7 0
|
|
* +---------------------------------------------+
|
|
* 0 | ND100 Load address |
|
|
* +---------------------------------------------+
|
|
* 1 | Z80 address |
|
|
* +---------------------------------------------+
|
|
* 2 | Byte count |
|
|
* +---------------------------------------------+
|
|
*/
|
|
static int
|
|
floppy_test(UNIT *up)
|
|
{
|
|
int cbaddr = getmval();
|
|
int n100addr, z80addr, bcnt;
|
|
int rv = 0;
|
|
|
|
if ((regA & FL_CW_FCE) == 0)
|
|
return STOP_UNHIOX; /* What to do? */
|
|
switch (regA >> 9) {
|
|
case 016: /* T14, load from Z80 to ND100 */
|
|
n100addr = prdmem(cbaddr, PM_CPU);
|
|
z80addr = prdmem(cbaddr+1, PM_CPU);
|
|
bcnt = prdmem(cbaddr+2, PM_CPU);
|
|
if (bcnt > 3584)
|
|
return STOP_UNHIOX;
|
|
printf("\r\n addr %06o nd100 %06o z80 %04x count %o\r\n",
|
|
cbaddr, prdmem(cbaddr, PM_CPU),
|
|
prdmem(cbaddr+1, PM_CPU), prdmem(cbaddr+2, PM_CPU));
|
|
dtomem(up->fileref, up->devaddr + z80addr - 0x2200,
|
|
n100addr, bcnt/2, PM_CPU);
|
|
break;
|
|
|
|
default:
|
|
return STOP_UNHIOX;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Execute. Fills in the unit local vars memaddr/wcnt/devaddr from the
|
|
* command block and setup for interrupt later.
|
|
*/
|
|
static int
|
|
floppy_excmd(void)
|
|
{
|
|
UNIT *unit;
|
|
int cw, u, cmd, i;
|
|
int cbaddr = getmval();
|
|
int status2, sectsz;
|
|
|
|
cw = prdmem(cbaddr+CB_CW, PM_CPU);
|
|
u = (cw >> CW_FL_SELSH) & 03;
|
|
cmd = cw & CW_FL_CMDMSK;
|
|
|
|
unit = &floppy_unit[u];
|
|
if ((unit->flags & UNIT_ATT) == 0)
|
|
goto err; /* floppy not inserted */
|
|
|
|
status2 = readfmt(unit);
|
|
sectsz = (status2 & ST2_FL_BS1K) == 0 ? 512 : 1024;
|
|
|
|
/* XXX check disk size, word count etc... */
|
|
unit->memaddr = ((prdmem(cbaddr+CB_DAHMAH, PM_CPU) & 0377) << 16) |
|
|
prdmem(cbaddr+CB_MAL, PM_CPU);
|
|
i = prdmem(cbaddr+CB_OPTWCH, PM_CPU);
|
|
unit->wcnt = ((i & 0377) << 16) | prdmem(cbaddr+CB_WCL, PM_CPU);
|
|
if ((i & CB_OPT_WC) == 0)
|
|
unit->wcnt *= (sectsz/2);
|
|
unit->devaddr = (((prdmem(cbaddr+CB_DAHMAH, PM_CPU) & 0177400) << 8) |
|
|
prdmem(cbaddr+CB_DAL, PM_CPU)) * sectsz;
|
|
|
|
switch (cmd) {
|
|
case CW_FL_RD:
|
|
unit->state = U_READ;
|
|
break;
|
|
case CW_FL_WR:
|
|
goto err; /* floppy write protected */
|
|
case CW_FL_EXTST:
|
|
unit->state = U_EXTST;
|
|
break;
|
|
case CW_FL_RDFMT:
|
|
unit->state = U_RDFMT;
|
|
break;
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
sim_activate(&floppy_unit[u], 10);
|
|
fl_rstatus &= ~FL_ST_RDY;
|
|
fl_rstatus |= FL_ST_ACT;
|
|
return SCPE_OK;
|
|
|
|
err:
|
|
return STOP_UNHIOX;
|
|
}
|