1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-04-25 03:35:58 +00:00
Files
rcornwell.sims/Ridge32/ridge32_flp.c
2020-12-03 21:44:14 -05:00

1014 lines
33 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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, 0};
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);
sim_debug(DEBUG_EXP, &flp_dev, "print '%c'\n", isprint(cmd)? 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];
/* Fall through */
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)
{
uint32 flags;
uint32 len;
uint32 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 */
#if 0
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 */
#endif
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_RDDEL: /* Read delete */
case CMD_RDSEC: /* Read sector */
/* cyl head sect */
flags = 0;
if (sectRead((DISK_INFO *)uptr->up7, flp_dcb.cmd[2],
flp_dcb.cmd[3], flp_dcb.cmd[4],
&flp_buf[0], sizeof(flp_buf), &flags, &len) != SCPE_OK) {
uptr->PHASE = PHASE_RES;
flp_dcb.stat[0] = 0x40;
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.cmd[3], flp_dcb.cmd[2], flp_dcb.cmd[4], 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.cmd[4] == flp_dcb.cmd[6]) {
flp_dcb.cmd[4] = 1;
if(flp_dcb.cmd[3]) {
flp_dcb.cmd[2]++;
flp_dcb.cmd[3] = 0;
} else {
flp_dcb.cmd[3] = 1;
}
} else {
flp_dcb.cmd[4]++;
}
if (flp_dcb.count == 0) {
// flp_dcb.stat[0] = 0x20 | (flp_dcb.cmd[1] & 0x7);
uptr->PHASE = PHASE_RES;
sim_activate(uptr, 10);
} else {
sim_activate(uptr, 100);
}
return SCPE_OK;
case CMD_WRDEL: /* Write delete */
case CMD_WRSEC: /* Write sector */
flags = ((flp_dcb.cmd[0] & 0xf) == CMD_WRDEL) ?
IMD_DISK_IO_DELETED_ADDR_MARK : 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.cmd[2],
flp_dcb.cmd[3], flp_dcb.cmd[4],
&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.cmd[4] == flp_dcb.cmd[6]) {
flp_dcb.cmd[4] = 1;
if(flp_dcb.cmd[3]) {
flp_dcb.cmd[2]++;
flp_dcb.cmd[3] = 0;
} else {
flp_dcb.cmd[3] = 1;
}
} else {
flp_dcb.cmd[4]++;
}
if (flp_dcb.count == 0) {
uptr->PHASE = PHASE_RES;
flp_dcb.stat[0] = 0x20 | (flp_dcb.cmd[1] & 0x7);
sim_activate(uptr, 10);
} else {
sim_activate(uptr, 100);
}
return SCPE_OK;
case CMD_RDTRK: /* Read track */
/* 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:
switch(flp_dcb.cmd[0] & 0xf) {
case CMD_RDSEC: /* Read sector */
case CMD_WRSEC: /* Write sector */
case CMD_RDTRK: /* Read track */
case CMD_WRDEL: /* Write delete */
case CMD_RDDEL: /* Read delete */
/* Make sure cylinder is correct */
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;
break;
case CMD_FIXDR: /* Fix drive */
case CMD_RDSID: /* Read sector ID */
case CMD_FMTTK: /* Format track */
default: /* Invalid command */
break;
}
/* 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_addr(uptr, 0xC5, flp_dcb.addr);
// io_dcbwrite_byte(uptr, 0xc4, 0);
io_dcbwrite_half(uptr, 0xC8, flp_dcb.count);
io_dcbwrite_half(uptr, 0xca, flp_dcb.xcount);
io_dcbwrite_byte(uptr, 0xCD, (flp_dcb.stat[4] << 2) | 0);
io_dcbwrite_byte(uptr, 0xCE, flp_dcb.stat[3]);
io_dcbwrite_byte(uptr, 0xCF, flp_dcb.stat[5]);
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;
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;
}