3b2: CIO PORTS and CTC devices
This change adds Common-IO implementations of the PORTS 4-port serial line card and the CTC tape controller card.
This commit is contained in:
parent
584147fb64
commit
ba9d8626e9
17 changed files with 2647 additions and 347 deletions
|
@ -127,8 +127,8 @@ static DEBTAB cpu_deb_tab[] = {
|
|||
{ "EXECUTE", EXECUTE_MSG, "Instruction execute" },
|
||||
{ "INIT", INIT_MSG, "Initialization" },
|
||||
{ "IRQ", IRQ_MSG, "Interrupt Handling" },
|
||||
{ "IO", IO_D_MSG, "I/O Dispatch" },
|
||||
{ "TRACE", TRACE_MSG, "Call Trace" },
|
||||
{ "IO", IO_DBG, "I/O Dispatch" },
|
||||
{ "TRACE", TRACE_DBG, "Call Trace" },
|
||||
{ "ERROR", ERR_MSG, "Error" },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
@ -138,6 +138,11 @@ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX|UNIT_BINK|UNIT_IDLE, MAXMEMSIZE) };
|
|||
#define UNIT_V_EXHALT (UNIT_V_UF + 0) /* halt to console */
|
||||
#define UNIT_EXHALT (1u << UNIT_V_EXHALT)
|
||||
|
||||
const char *cio_names[8] = {
|
||||
"", "*VOID*", "*VOID*", "PORTS",
|
||||
"*VOID*", "CTC", "*VOID*", "*VOID*"
|
||||
};
|
||||
|
||||
MTAB cpu_mod[] = {
|
||||
{ UNIT_MSIZE, (1u << 20), NULL, "1M",
|
||||
&cpu_set_size, NULL, NULL, "Set Memory to 1M bytes" },
|
||||
|
@ -149,8 +154,10 @@ MTAB cpu_mod[] = {
|
|||
&cpu_set_hist, &cpu_show_hist, NULL, "Displays instruction history" },
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL,
|
||||
NULL, &cpu_show_virt, NULL, "Show translation for virtual address" },
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "STACK", "STACK",
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "STACK", NULL,
|
||||
NULL, &cpu_show_stack, NULL, "Display the current stack with optional depth" },
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "CIO", NULL,
|
||||
NULL, &cpu_show_cio, NULL, "Display CIO configuration" },
|
||||
{ MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle },
|
||||
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL },
|
||||
{ UNIT_EXHALT, UNIT_EXHALT, "Halt on Exception", "EXHALT",
|
||||
|
@ -532,6 +539,19 @@ t_stat cpu_show_stack(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat cpu_show_cio(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
||||
{
|
||||
uint32 i;
|
||||
|
||||
fprintf(st, " SLOT DEVICE\n");
|
||||
fprintf(st, "---------------------\n");
|
||||
for (i = 0; i < CIO_SLOTS; i++) {
|
||||
fprintf(st, " %d %s\n", i, cio_names[cio[i].id & 0x7]);
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
void cpu_load_rom()
|
||||
{
|
||||
uint32 i, index, sc, mask, val;
|
||||
|
@ -988,13 +1008,18 @@ t_stat cpu_show_virt(FILE *of, UNIT *uptr, int32 val, CONST void *desc)
|
|||
if (r == SCPE_OK) {
|
||||
fprintf(of, "Virtual %08x = Physical %08x\n", va, pa);
|
||||
return SCPE_OK;
|
||||
} else {
|
||||
fprintf(of, "Translation not possible for virtual address.\n");
|
||||
return SCPE_ARG;
|
||||
}
|
||||
} else {
|
||||
fprintf(of, "Illegal address format.\n");
|
||||
return SCPE_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(of, "Translation not possible.\n");
|
||||
|
||||
return SCPE_OK;
|
||||
fprintf(of, "Address argument required.\n");
|
||||
return SCPE_ARG;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1557,6 +1582,10 @@ void cpu_on_interrupt(uint16 vec)
|
|||
{
|
||||
uint32 new_pcbp;
|
||||
|
||||
sim_debug(IRQ_MSG, &cpu_dev,
|
||||
"[%08x] [cpu_on_interrupt] vec=%02x (%d)\n",
|
||||
R[NUM_PC], vec, vec);
|
||||
|
||||
/*
|
||||
* "If a nonmaskable interrupt request is received, an auto-vector
|
||||
* interrupt acknowledge cycle is performed (as if an autovector
|
||||
|
@ -1733,6 +1762,10 @@ t_stat sim_instr(void)
|
|||
if (cio[i].intr &&
|
||||
cio[i].ipl == cpu_int_ipl &&
|
||||
cio[i].ivec == cpu_int_vec) {
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[%08x] [IRQ] Handling CIO interrupt for card %d\n",
|
||||
R[NUM_PC], i);
|
||||
|
||||
cio[i].intr = FALSE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -397,6 +397,7 @@ t_stat cpu_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|||
t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||
t_stat cpu_show_virt(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||
t_stat cpu_show_stack(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||
t_stat cpu_show_cio(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||
t_stat cpu_set_halt(UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||
t_stat cpu_clear_halt(UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||
t_stat cpu_boot(int32 unit_num, DEVICE *dptr);
|
||||
|
|
817
3B2/3b2_ctc.c
Normal file
817
3B2/3b2_ctc.c
Normal file
|
@ -0,0 +1,817 @@
|
|||
/* 3b2_ctc.c: AT&T 3B2 Model 400 "CTC" feature card
|
||||
|
||||
Copyright (c) 2018, Seth J. Morabito
|
||||
|
||||
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 AUTHORS OR COPYRIGHT HOLDERS
|
||||
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 of the author 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.
|
||||
*/
|
||||
|
||||
#include "3b2_ctc.h"
|
||||
|
||||
extern CIO_STATE cio[CIO_SLOTS];
|
||||
extern UNIT cio_unit;
|
||||
|
||||
#define CTQRESIZE 20
|
||||
#define CTQCESIZE 16
|
||||
|
||||
#define DELAY_SYSGEN 2500
|
||||
#define DELAY_FMT 1000000
|
||||
#define DELAY_RW 10000
|
||||
#define DELAY_OPEN 2500
|
||||
#define DELAY_CLOSE 2500
|
||||
#define DELAY_CONFIG 2500
|
||||
#define DELAY_DLM 1000
|
||||
#define DELAY_ULM 1000
|
||||
#define DELAY_FCF 1000
|
||||
#define DELAY_DOS 1000
|
||||
#define DELAY_DSD 1000
|
||||
#define DELAY_UNK 1000
|
||||
#define DELAY_CATCHUP 10000
|
||||
|
||||
#define TAPE_DEV 0 /* CTAPE device */
|
||||
#define XMF_DEV 1 /* XM Floppy device */
|
||||
|
||||
#define VTOC_BLOCK 0
|
||||
|
||||
#define ATOW(arr,i) ((uint32)arr[i+3] + ((uint32)arr[i+2] << 8) + \
|
||||
((uint32)arr[i+1] << 16) + ((uint32)arr[i] << 24))
|
||||
|
||||
static uint8 int_cid; /* Interrupting card ID */
|
||||
static uint8 int_subdev; /* Interrupting subdevice */
|
||||
static t_bool ctc_conf = FALSE; /* Has a CTC card been configured? */
|
||||
|
||||
struct partition vtoc_table[VTOC_PART] = {
|
||||
{ 2, 0, 5272, 8928 }, /* 00 */
|
||||
{ 3, 1, 126, 5146 }, /* 01 */
|
||||
{ 4, 0, 14200, 31341 }, /* 02 */
|
||||
{ 0, 0, 2, 45539 }, /* 03 */
|
||||
{ 0, 1, 0, 0 }, /* 04 */
|
||||
{ 0, 1, 0, 0 }, /* 05 */
|
||||
{ 5, 1, 0, 45541 }, /* 06 */
|
||||
{ 1, 1, 0, 126 }, /* 07 */
|
||||
{ 0, 1, 0, 0 }, /* 08 */
|
||||
{ 0, 1, 0, 0 }, /* 09 */
|
||||
{ 0, 1, 0, 0 }, /* 10 */
|
||||
{ 0, 1, 0, 0 }, /* 11 */
|
||||
{ 0, 1, 0, 0 }, /* 12 */
|
||||
{ 0, 1, 0, 0 }, /* 13 */
|
||||
{ 0, 1, 0, 0 }, /* 14 */
|
||||
{ 0, 1, 0, 0 } /* 15 */
|
||||
};
|
||||
|
||||
/* State. Although we technically have two devices (tape and floppy),
|
||||
* only the tape drive is supported at this time. */
|
||||
|
||||
CTC_STATE ctc_state[2];
|
||||
|
||||
UNIT ctc_unit = {
|
||||
UDATA (&ctc_svc, UNIT_FIX|UNIT_ATTABLE|UNIT_DISABLE|
|
||||
UNIT_ROABLE|UNIT_BINK, CTC_CAPACITY)
|
||||
};
|
||||
|
||||
MTAB ctc_mod[] = {
|
||||
{ UNIT_WLK, 0, "write enabled", "WRITEENABLED",
|
||||
NULL, NULL, NULL, "Write enabled tape drive" },
|
||||
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED",
|
||||
NULL, NULL, NULL, "Write lock tape drive" },
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL,
|
||||
NULL, &ctc_show_rqueue, NULL, "Display Request Queue for card n" },
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL,
|
||||
NULL, &ctc_show_cqueue, NULL, "Display Completion Queue for card n" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static DEBTAB ctc_debug[] = {
|
||||
{ "IO", IO_DBG, "I/O" },
|
||||
{ "TRACE", TRACE_DBG, "Call Trace" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
DEVICE ctc_dev = {
|
||||
"CTC", /* name */
|
||||
&ctc_unit, /* units */
|
||||
NULL, /* registers */
|
||||
ctc_mod, /* modifiers */
|
||||
1, /* #units */
|
||||
16, /* address radix */
|
||||
32, /* address width */
|
||||
1, /* address incr. */
|
||||
16, /* data radix */
|
||||
8, /* data width */
|
||||
NULL, /* examine routine */
|
||||
NULL, /* deposit routine */
|
||||
&ctc_reset, /* reset routine */
|
||||
NULL, /* boot routine */
|
||||
&ctc_attach, /* attach routine */
|
||||
&ctc_detach, /* detach routine */
|
||||
NULL, /* context */
|
||||
DEV_DISABLE|DEV_DIS|DEV_DEBUG|DEV_SECTORS, /* flags */
|
||||
0, /* debug control flags */
|
||||
ctc_debug, /* debug flag names */
|
||||
NULL, /* memory size change */
|
||||
NULL, /* logical name */
|
||||
NULL, /* help routine */
|
||||
NULL, /* attach help routine */
|
||||
NULL, /* help context */
|
||||
NULL, /* device description */
|
||||
};
|
||||
|
||||
static void cio_irq(uint8 cid, uint8 dev, int32 delay)
|
||||
{
|
||||
int_cid = cid;
|
||||
int_subdev = dev & 0x3f;
|
||||
sim_activate_after(&ctc_unit, delay);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a VTOC and pdinfo to the tape file
|
||||
*/
|
||||
static t_stat ctc_write_vtoc(struct vtoc *vtoc, struct pdinfo *pdinfo, uint32 maxpass)
|
||||
{
|
||||
uint8 buf[PD_BYTES];
|
||||
uint32 wr, offset;
|
||||
|
||||
memcpy(buf, vtoc, sizeof(struct vtoc));
|
||||
offset = sizeof(struct vtoc);
|
||||
memcpy(buf + offset, pdinfo, sizeof(struct pdinfo));
|
||||
offset += sizeof(struct pdinfo);
|
||||
memcpy(buf + offset, &maxpass, sizeof(uint32));
|
||||
|
||||
return sim_disk_wrsect(&ctc_unit, VTOC_BLOCK, buf, &wr, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load a VTOC and pdinfo from the tape file
|
||||
*/
|
||||
static t_stat ctc_read_vtoc(struct vtoc *vtoc, struct pdinfo *pdinfo, uint32 *maxpass)
|
||||
{
|
||||
uint8 buf[PD_BYTES];
|
||||
uint32 wr, offset;
|
||||
t_stat result;
|
||||
|
||||
result = sim_disk_rdsect(&ctc_unit, VTOC_BLOCK, buf, &wr, 1);
|
||||
|
||||
if (result != SCPE_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
memcpy(vtoc, buf, sizeof(struct vtoc));
|
||||
offset = sizeof(struct vtoc);
|
||||
memcpy(pdinfo, buf + offset, sizeof(struct pdinfo));
|
||||
offset += sizeof(struct pdinfo);
|
||||
memcpy(maxpass, buf + offset, sizeof(uint32));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the host's in-memory copy of the VTOC and pdinfo
|
||||
*/
|
||||
static void ctc_update_vtoc(uint32 maxpass,
|
||||
uint32 vtoc_addr, uint32 pdinfo_addr,
|
||||
struct vtoc *vtoc, struct pdinfo *pdinfo)
|
||||
{
|
||||
uint32 i;
|
||||
|
||||
pwrite_w(vtoc_addr + 12, VTOC_VALID);
|
||||
pwrite_w(vtoc_addr + 16, vtoc->version);
|
||||
for (i = 0; i < 8; i++) {
|
||||
pwrite_b(vtoc_addr + 20 + i, (uint8)(vtoc->volume[i]));
|
||||
}
|
||||
pwrite_h(vtoc_addr + 28, vtoc->sectorsz);
|
||||
pwrite_h(vtoc_addr + 30, vtoc->nparts);
|
||||
|
||||
for (i = 0; i < VTOC_PART; i++) {
|
||||
pwrite_h(vtoc_addr + 72 + (i * 12) + 0, vtoc_table[i].id);
|
||||
pwrite_h(vtoc_addr + 72 + (i * 12) + 2, vtoc_table[i].flag);
|
||||
pwrite_w(vtoc_addr + 72 + (i * 12) + 4, vtoc_table[i].sstart);
|
||||
pwrite_w(vtoc_addr + 72 + (i * 12) + 8, vtoc_table[i].ssize);
|
||||
}
|
||||
|
||||
/* Write the pdinfo */
|
||||
pwrite_w(pdinfo_addr, pdinfo->driveid);
|
||||
pwrite_w(pdinfo_addr + 4, pdinfo->sanity);
|
||||
pwrite_w(pdinfo_addr + 8, pdinfo->version);
|
||||
for (i = 0; i < 12; i++) {
|
||||
pwrite_b(pdinfo_addr + 12 + i, pdinfo->serial[i]);
|
||||
}
|
||||
pwrite_w(pdinfo_addr + 24, pdinfo->cyls);
|
||||
pwrite_w(pdinfo_addr + 28, pdinfo->tracks);
|
||||
pwrite_w(pdinfo_addr + 32, pdinfo->sectors);
|
||||
pwrite_w(pdinfo_addr + 36, pdinfo->bytes);
|
||||
pwrite_w(pdinfo_addr + 40, pdinfo->logicalst);
|
||||
pwrite_w(pdinfo_addr + 44, pdinfo->errlogst);
|
||||
pwrite_w(pdinfo_addr + 48, pdinfo->errlogsz);
|
||||
pwrite_w(pdinfo_addr + 52, pdinfo->mfgst);
|
||||
pwrite_w(pdinfo_addr + 56, pdinfo->mfgsz);
|
||||
pwrite_w(pdinfo_addr + 60, pdinfo->defectst);
|
||||
pwrite_w(pdinfo_addr + 64, pdinfo->defectsz);
|
||||
pwrite_w(pdinfo_addr + 68, pdinfo->relno);
|
||||
pwrite_w(pdinfo_addr + 72, pdinfo->relst);
|
||||
pwrite_w(pdinfo_addr + 76, pdinfo->relsz);
|
||||
pwrite_w(pdinfo_addr + 80, pdinfo->relnext);
|
||||
|
||||
/* Now something horrible happens. We sneak RIGHT off the end of
|
||||
* the pdinfo struct and reach deep into the pdsector struct that
|
||||
* it is part of. */
|
||||
|
||||
pwrite_w(pdinfo_addr + 128, maxpass);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a single request taken from the Request Queue.
|
||||
*
|
||||
* Note that the driver stuffs parameters into various different
|
||||
* fields of the Request Queue entry seemingly at random, and also
|
||||
* expects response parameters to be placed in specific fields of the
|
||||
* Completion Queue entry. It can be confusing to follow.
|
||||
*/
|
||||
static void ctc_cmd(uint8 cid,
|
||||
cio_entry *rqe, uint8 *rapp_data,
|
||||
cio_entry *cqe, uint8 *capp_data)
|
||||
{
|
||||
uint32 vtoc_addr, pdinfo_addr, ctjob_addr;
|
||||
uint32 maxpass, blkno, delay;
|
||||
uint8 dev;
|
||||
uint8 sec_buf[512];
|
||||
int32 secrw = 0;
|
||||
int32 b;
|
||||
struct vtoc vtoc = {0};
|
||||
struct pdinfo pdinfo = {0};
|
||||
|
||||
uint32 lba; /* Logical Block Address */
|
||||
|
||||
dev = rqe->subdevice & 1; /* Tape or Floppy device */
|
||||
|
||||
capp_data[7] = rqe->opcode;
|
||||
cqe->subdevice = rqe->subdevice;
|
||||
|
||||
switch(rqe->opcode) {
|
||||
case CIO_DLM:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CIO Download Memory: bytecnt=%04x "
|
||||
"addr=%08x return_addr=%08x subdev=%02x\n",
|
||||
rqe->byte_count, rqe->address,
|
||||
rqe->address, rqe->subdevice);
|
||||
delay = DELAY_DLM;
|
||||
cqe->address = rqe->address + rqe->byte_count;
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
break;
|
||||
case CIO_ULM:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CIO Upload Memory: return opcode 0\n");
|
||||
delay = DELAY_ULM;
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
break;
|
||||
case CIO_FCF:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CIO Force Function Call: return opcode 0\n");
|
||||
delay = DELAY_FCF;
|
||||
|
||||
/* This is to pass diagnostics. TODO: Figure out how to parse
|
||||
* the given test x86 code and determine how to respond
|
||||
* correctly */
|
||||
pwrite_h(0x200f000, 0x1); /* Test success */
|
||||
pwrite_h(0x200f002, 0x0); /* Test Number */
|
||||
pwrite_h(0x200f004, 0x0); /* Actual */
|
||||
pwrite_h(0x200f006, 0x0); /* Expected */
|
||||
pwrite_b(0x200f008, 0x1); /* Success flag again */
|
||||
pwrite_b(0x200f009, 0x30); /* ??? */
|
||||
|
||||
/* An interesting (?) side-effect of FORCE FUNCTION CALL is
|
||||
* that it resets the card state such that a new SYSGEN is
|
||||
* required in order for new commands to work. In fact, an
|
||||
* INT0/INT1 combo _without_ a RESET can sysgen the board. So,
|
||||
* we reset the command bits here. */
|
||||
cio[cid].sysgen_s = 0;
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
break;
|
||||
case CIO_DOS:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CIO_DOS (%d)\n",
|
||||
rqe->opcode);
|
||||
delay = DELAY_DOS;
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
break;
|
||||
case CIO_DSD:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CTC_DSD (%d)\n",
|
||||
rqe->opcode);
|
||||
delay = DELAY_DSD;
|
||||
/* The system wants us to write sub-device structures at the
|
||||
* supplied address, but we have nothing to write. */
|
||||
pwrite_h(rqe->address, 0x0);
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
break;
|
||||
case CTC_FORMAT:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CTC_FORMAT (%d)\n",
|
||||
rqe->opcode);
|
||||
|
||||
delay = DELAY_FMT;
|
||||
|
||||
/* FORMAT stores the job pointer in the jio_start field of the
|
||||
* completion queue entry's application data */
|
||||
capp_data[0] = rapp_data[4];
|
||||
capp_data[1] = rapp_data[5];
|
||||
capp_data[2] = rapp_data[6];
|
||||
capp_data[3] = rapp_data[7];
|
||||
|
||||
if (dev == XMF_DEV) {
|
||||
cqe->opcode = CTC_NOTREADY;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ctc_unit.flags & UNIT_ATT) == 0) {
|
||||
cqe->opcode = CTC_NOMEDIA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctc_unit.flags & UNIT_WLK) {
|
||||
cqe->opcode = CTC_RDONLY;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write a valid VTOC and pdinfo to the tape */
|
||||
|
||||
vtoc.sanity = VTOC_VALID;
|
||||
vtoc.version = 1;
|
||||
strcpy(vtoc.volume, "ctctape");
|
||||
vtoc.sectorsz = PD_BYTES;
|
||||
vtoc.nparts = VTOC_PART;
|
||||
|
||||
pdinfo.driveid = PD_DRIVEID;
|
||||
pdinfo.sanity = PD_VALID;
|
||||
pdinfo.version = 0;
|
||||
memset(pdinfo.serial, 0, 12);
|
||||
pdinfo.cyls = PD_CYLS;
|
||||
pdinfo.tracks = PD_TRACKS;
|
||||
pdinfo.sectors = PD_SECTORS;
|
||||
pdinfo.bytes = PD_BYTES;
|
||||
pdinfo.logicalst = PD_LOGICALST;
|
||||
pdinfo.errlogst = 0xffffffff;
|
||||
pdinfo.errlogsz = 0xffffffff;
|
||||
pdinfo.mfgst = 0xffffffff;
|
||||
pdinfo.mfgsz = 0xffffffff;
|
||||
pdinfo.defectst = 0xffffffff;
|
||||
pdinfo.defectsz = 0xffffffff;
|
||||
pdinfo.relno = 0xffffffff;
|
||||
pdinfo.relst = 0xffffffff;
|
||||
pdinfo.relsz = 0xffffffff;
|
||||
pdinfo.relnext = 0xffffffff;
|
||||
|
||||
maxpass = rqe->address;
|
||||
|
||||
ctc_write_vtoc(&vtoc, &pdinfo, maxpass);
|
||||
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
|
||||
/* The address field holds the total amount of time (in 25ms
|
||||
* chunks) used during this format session. We'll fudge and
|
||||
* say 1 minute for formatting. */
|
||||
cqe->address = 2400;
|
||||
|
||||
break;
|
||||
case CTC_OPEN:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CTC_OPEN (%d)\n",
|
||||
rqe->opcode);
|
||||
|
||||
delay = DELAY_OPEN;
|
||||
|
||||
ctc_state[dev].time = 0; /* Opening always resets session time to 0 */
|
||||
|
||||
vtoc_addr = rqe->address;
|
||||
pdinfo_addr = ATOW(rapp_data, 4);
|
||||
ctjob_addr = ATOW(rapp_data, 8);
|
||||
|
||||
/* For OPEN commands, the Completion Queue Entry's address
|
||||
* field contains a pointer to the ctjobstat. */
|
||||
cqe->address = ctjob_addr;
|
||||
|
||||
if (dev == XMF_DEV) {
|
||||
cqe->opcode = CTC_NOTREADY;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ctc_unit.flags & UNIT_ATT) == 0) {
|
||||
cqe->opcode = CTC_NOMEDIA;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Load the vtoc, pdinfo, and maxpass from the tape */
|
||||
ctc_read_vtoc(&vtoc, &pdinfo, &maxpass);
|
||||
|
||||
ctc_update_vtoc(maxpass, vtoc_addr, pdinfo_addr, &vtoc, &pdinfo);
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
break;
|
||||
case CTC_CLOSE:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CTC_CLOSE (%d)\n",
|
||||
rqe->opcode);
|
||||
|
||||
delay = DELAY_CLOSE;
|
||||
|
||||
/* The Request Queue Entry's address field contains the
|
||||
* ctjobstat pointer, which the driver will want to find in
|
||||
* the first word of our Completion Queue Entry's application
|
||||
* data. This must be in place whether we have media attached
|
||||
* or not. */
|
||||
capp_data[3] = rqe->address & 0xff;
|
||||
capp_data[2] = (rqe->address & 0xff00) >> 8;
|
||||
capp_data[1] = (rqe->address & 0xff0000) >> 16;
|
||||
capp_data[0] = (rqe->address & 0xff000000) >> 24;
|
||||
|
||||
/* The Completion Queue Entry's address field holds the total
|
||||
* tape time used in this session. */
|
||||
cqe->address = ctc_state[dev].time;
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
|
||||
break;
|
||||
case CTC_WRITE:
|
||||
case CTC_VWRITE:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CTC_WRITE or CTC_VWRITE (%d)\n",
|
||||
rqe->opcode);
|
||||
|
||||
delay = DELAY_RW;
|
||||
|
||||
cqe->byte_count = rqe->byte_count;
|
||||
cqe->subdevice = rqe->subdevice;
|
||||
cqe->address = ATOW(rapp_data, 4);
|
||||
|
||||
if (dev == XMF_DEV) {
|
||||
cqe->opcode = CTC_NOTREADY;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ctc_unit.flags & UNIT_ATT) == 0) {
|
||||
cqe->opcode = CTC_NOMEDIA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctc_unit.flags & UNIT_WLK) {
|
||||
cqe->opcode = CTC_RDONLY;
|
||||
break;
|
||||
}
|
||||
|
||||
blkno = ATOW(rapp_data, 0);
|
||||
|
||||
for (b = 0; b < rqe->byte_count / 512; b++) {
|
||||
ctc_state[dev].time += 10;
|
||||
for (int j = 0; j < 512; j++) {
|
||||
/* Fill the buffer */
|
||||
sec_buf[j] = pread_b(rqe->address + (b * 512) + j);
|
||||
}
|
||||
lba = blkno + b;
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] ... CTC_WRITE: 512 bytes at block %d (0x%x)\n",
|
||||
lba, lba);
|
||||
sim_disk_wrsect(&ctc_unit, lba, sec_buf, &secrw, 1);
|
||||
}
|
||||
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
break;
|
||||
case CTC_READ:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CTC_READ (%d)\n",
|
||||
rqe->opcode);
|
||||
delay = DELAY_RW;
|
||||
cqe->byte_count = rqe->byte_count;
|
||||
cqe->subdevice = rqe->subdevice;
|
||||
cqe->address = ATOW(rapp_data, 4);
|
||||
|
||||
if (dev == XMF_DEV) {
|
||||
cqe->opcode = CTC_NOTREADY;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ctc_unit.flags & UNIT_ATT) == 0) {
|
||||
cqe->opcode = CTC_NOMEDIA;
|
||||
break;
|
||||
}
|
||||
|
||||
blkno = ATOW(rapp_data, 0);
|
||||
|
||||
for (b = 0; b < rqe->byte_count / 512; b++) {
|
||||
ctc_state[dev].time += 10;
|
||||
lba = blkno + b;
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] ... CTC_READ: 512 bytes from block %d (0x%x)\n",
|
||||
lba, lba);
|
||||
sim_disk_rdsect(&ctc_unit, lba, sec_buf, &secrw, 1);
|
||||
for (int j = 0; j < 512; j++) {
|
||||
/* Drain the buffer */
|
||||
pwrite_b(rqe->address + (b * 512) + j, sec_buf[j]);
|
||||
}
|
||||
}
|
||||
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
break;
|
||||
case CTC_CONFIG:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] CTC_CONFIG (%d)\n",
|
||||
rqe->opcode);
|
||||
delay = DELAY_CONFIG;
|
||||
cqe->opcode = CTC_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] UNHANDLED OP: %d (0x%02x)\n",
|
||||
rqe->opcode, rqe->opcode);
|
||||
delay = DELAY_UNK;
|
||||
cqe->opcode = CTC_HWERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
cio_irq(cid, rqe->subdevice, delay);
|
||||
}
|
||||
|
||||
void ctc_sysgen(uint8 cid)
|
||||
{
|
||||
cio_entry cqe = {0};
|
||||
uint8 rapp_data[12] = {0};
|
||||
|
||||
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] Handling Sysgen.\n");
|
||||
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqp=%08x\n", cio[cid].rqp);
|
||||
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqp=%08x\n", cio[cid].cqp);
|
||||
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqs=%d\n", cio[cid].rqs);
|
||||
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqs=%d\n", cio[cid].cqs);
|
||||
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] ivec=%d\n", cio[cid].ivec);
|
||||
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] no_rque=%d\n", cio[cid].no_rque);
|
||||
|
||||
cqe.opcode = 3; /* Sysgen success! */
|
||||
|
||||
cio_cexpress(cid, CTQCESIZE, &cqe, rapp_data);
|
||||
cio_cqueue(cid, CIO_STAT, CTQCESIZE, &cqe, rapp_data);
|
||||
|
||||
int_cid = cid;
|
||||
sim_activate_after(&ctc_unit, DELAY_SYSGEN);
|
||||
}
|
||||
|
||||
void ctc_express(uint8 cid)
|
||||
{
|
||||
cio_entry rqe, cqe;
|
||||
uint8 rapp_data[12] = {0};
|
||||
uint8 capp_data[8] = {0};
|
||||
|
||||
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_express] Handling Express Request\n");
|
||||
|
||||
cio_rexpress(cid, CTQRESIZE, &rqe, rapp_data);
|
||||
ctc_cmd(cid, &rqe, rapp_data, &cqe, capp_data);
|
||||
|
||||
dump_entry(TRACE_DBG, &ctc_dev, "COMPLETION",
|
||||
CTQCESIZE, &cqe, capp_data);
|
||||
|
||||
cio_cexpress(cid, CTQCESIZE, &cqe, capp_data);
|
||||
}
|
||||
|
||||
void ctc_full(uint8 cid)
|
||||
{
|
||||
cio_entry rqe, cqe;
|
||||
uint8 rapp_data[12] = {0};
|
||||
uint8 capp_data[8] = {0};
|
||||
|
||||
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_full] Handling Full Request\n");
|
||||
|
||||
while (cio_cqueue_avail(cid, CTQCESIZE) &&
|
||||
cio_rqueue(cid, TAPE_DEV, CTQRESIZE, &rqe, rapp_data) == SCPE_OK) {
|
||||
ctc_cmd(cid, &rqe, rapp_data, &cqe, capp_data);
|
||||
}
|
||||
cio_cqueue(cid, CIO_STAT, CTQCESIZE, &cqe, capp_data);
|
||||
}
|
||||
|
||||
t_stat ctc_reset(DEVICE *dptr)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 cid, end_slot;
|
||||
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_reset] Resetting CTC device\n");
|
||||
|
||||
memset(ctc_state, 0, 2 * sizeof(CTC_STATE));
|
||||
|
||||
if (dptr->flags & DEV_DIS) {
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_reset] REMOVING CARD\n");
|
||||
|
||||
for (cid = 0; cid < CIO_SLOTS; cid++) {
|
||||
if (cio[cid].id == CTC_ID) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cid == CIO_SLOTS) {
|
||||
/* No card was ever attached */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
cio[cid].id = 0;
|
||||
cio[cid].ipl = 0;
|
||||
cio[cid].ivec = 0;
|
||||
cio[cid].exp_handler = NULL;
|
||||
cio[cid].full_handler = NULL;
|
||||
cio[cid].sysgen = NULL;
|
||||
|
||||
ctc_conf = FALSE;
|
||||
} else if (!ctc_conf) {
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_reset] ATTACHING CARD\n");
|
||||
|
||||
/* Find the first avaialable slot */
|
||||
for (cid = 0; cid < CIO_SLOTS; cid++) {
|
||||
if (cio[cid].id == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do we have room? */
|
||||
if (cid == CIO_SLOTS) {
|
||||
return SCPE_NXM;
|
||||
}
|
||||
|
||||
cio[cid].id = CTC_ID;
|
||||
cio[cid].ipl = CTC_IPL;
|
||||
cio[cid].exp_handler = &ctc_express;
|
||||
cio[cid].full_handler = &ctc_full;
|
||||
cio[cid].sysgen = &ctc_sysgen;
|
||||
|
||||
ctc_conf = TRUE;
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat ctc_svc(UNIT *uptr)
|
||||
{
|
||||
uint16 lp, ulp;
|
||||
|
||||
if (cio[int_cid].ivec > 0) {
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[cio_svc] IRQ for board %d (VEC=%d)\n",
|
||||
int_cid, cio[int_cid].ivec);
|
||||
cio[int_cid].intr = TRUE;
|
||||
}
|
||||
|
||||
/* Check to see if the completion queue has more work in it. We
|
||||
* need to schedule an interrupt for each job if we've fallen
|
||||
* behind (this should be rare) */
|
||||
lp = cio_c_lp(int_cid, CTQCESIZE);
|
||||
ulp = cio_c_ulp(int_cid, CTQCESIZE);
|
||||
|
||||
if ((ulp + CTQCESIZE) % (CTQCESIZE * cio[int_cid].cqs) != lp) {
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[cio_svc] Completion queue has fallen behind (lp=%04x ulp=%04x)\n",
|
||||
lp, ulp);
|
||||
/* Schedule a catch-up interrupt */
|
||||
sim_activate_abs(&ctc_unit, DELAY_CATCHUP);
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat ctc_attach(UNIT *uptr, CONST char *cptr)
|
||||
{
|
||||
return sim_disk_attach(uptr, cptr, 512, 1, TRUE, 0, "CIPHER23", 0, 0);
|
||||
}
|
||||
|
||||
t_stat ctc_detach(UNIT *uptr)
|
||||
{
|
||||
return sim_disk_detach(uptr);
|
||||
}
|
||||
|
||||
t_stat ctc_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
||||
{
|
||||
return ctc_show_queue_common(st, uptr, val, desc, TRUE);
|
||||
}
|
||||
|
||||
t_stat ctc_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
||||
{
|
||||
return ctc_show_queue_common(st, uptr, val, desc, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static t_stat ctc_show_queue_common(FILE *st, UNIT *uptr, int32 val,
|
||||
CONST void *desc, t_bool rq)
|
||||
{
|
||||
uint8 cid;
|
||||
char *cptr = (char *) desc;
|
||||
t_stat result;
|
||||
uint32 ptr, size, no_rque, i, j;
|
||||
uint8 op, dev, seq, cmdstat;
|
||||
|
||||
if (cptr) {
|
||||
cid = (uint8) get_uint(cptr, 10, 12, &result);
|
||||
if (result != SCPE_OK) {
|
||||
return SCPE_ARG;
|
||||
}
|
||||
} else {
|
||||
return SCPE_ARG;
|
||||
}
|
||||
|
||||
/* If the card is not sysgen'ed, give up */
|
||||
if (cio[cid].sysgen_s != CIO_SYSGEN) {
|
||||
fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid);
|
||||
return SCPE_ARG;
|
||||
}
|
||||
|
||||
if (rq) {
|
||||
ptr = cio[cid].rqp;
|
||||
size = cio[cid].rqs;
|
||||
no_rque = cio[cid].no_rque;
|
||||
fprintf(st, "Dumping %d Request Queues\n", no_rque);
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
fprintf(st, "EXPRESS ENTRY:\n");
|
||||
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
|
||||
fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2));
|
||||
fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3));
|
||||
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
|
||||
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
|
||||
ptr += CTQRESIZE;
|
||||
|
||||
for (i = 0; i < no_rque; i++) {
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
fprintf(st, "REQUEST QUEUE %d\n", i);
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / CTQRESIZE);
|
||||
fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / CTQRESIZE);
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
ptr += 4;
|
||||
for (j = 0; j < size; j++) {
|
||||
dev = pread_b(ptr + 2);
|
||||
op = pread_b(ptr + 3);
|
||||
seq = (dev & 0x40) >> 6;
|
||||
cmdstat = (dev & 0x80) >> 7;
|
||||
fprintf(st, "REQUEST ENTRY %d\n", j);
|
||||
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
|
||||
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
|
||||
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
|
||||
fprintf(st, " Seqbit: %d\n", seq);
|
||||
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
|
||||
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
|
||||
fprintf(st, " App Data: 0x%08x 0x%08x 0x%08x\n",
|
||||
pread_w(ptr + 8), pread_w(ptr + 12), pread_w(ptr + 16));
|
||||
ptr += CTQRESIZE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ptr = cio[cid].cqp;
|
||||
size = cio[cid].cqs;
|
||||
no_rque = 0; /* Not used */
|
||||
fprintf(st, "Dumping Completion Queue\n");
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
fprintf(st, "EXPRESS ENTRY:\n");
|
||||
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
|
||||
fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2));
|
||||
fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3));
|
||||
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
|
||||
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
|
||||
ptr += CTQCESIZE;
|
||||
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / CTQCESIZE);
|
||||
fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / CTQCESIZE);
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
ptr += 4;
|
||||
for (i = 0; i < size; i++) {
|
||||
dev = pread_b(ptr + 2);
|
||||
op = pread_b(ptr + 3);
|
||||
seq = (dev & 0x40) >> 6;
|
||||
cmdstat = (dev & 0x80) >> 7;
|
||||
fprintf(st, "COMPLETION ENTRY %d\n", i);
|
||||
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
|
||||
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
|
||||
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
|
||||
fprintf(st, " Seqbit: %d\n", seq);
|
||||
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
|
||||
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
|
||||
fprintf(st, " App Data: 0x%08x 0x%08x\n",
|
||||
pread_w(ptr + 8), pread_w(ptr + 12));
|
||||
ptr += CTQCESIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
158
3B2/3b2_ctc.h
Normal file
158
3B2/3b2_ctc.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
/* 3b2_ctc.h: AT&T 3B2 Model 400 "CTC" feature card
|
||||
|
||||
Copyright (c) 2018, Seth J. Morabito
|
||||
|
||||
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 AUTHORS OR COPYRIGHT HOLDERS
|
||||
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 of the author 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CTC is an intelligent feature card for the 3B2 that supports a
|
||||
* Cipher "FloppyTape(tm)" 525 drive that can read and write 23MB
|
||||
* DC600A cartridges.
|
||||
*
|
||||
* The CTC card is based on the Common I/O (CIO) platform.
|
||||
*
|
||||
* Notes:
|
||||
* ------
|
||||
*
|
||||
* The Cipher FloppyTape is an odd beast. Although it's a tape drive,
|
||||
* it is controlled by a floppy controller. It is divided into virtual
|
||||
* sectors that can be addressed by Cylinder / Track / Sector.
|
||||
* Stepping and head select pulses dictate where on the tape to read
|
||||
* from or write to. Moreover, System V maps a filesystem onto the
|
||||
* tape, and a properly formatted tape drive will have a VTOC on
|
||||
* partition 0.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _3B2_CTC_H_
|
||||
#define _3B2_CTC_H_
|
||||
|
||||
#include "3b2_defs.h"
|
||||
#include "3b2_io.h"
|
||||
|
||||
#define UNIT_V_WLK (DKUF_V_UF + 0) /* Write-locked tape */
|
||||
#define UNIT_WLK (1 << UNIT_V_WLK)
|
||||
|
||||
#define CTC_ID 0x0005
|
||||
#define CTC_IPL 12
|
||||
#define CTC_VERSION 1
|
||||
|
||||
/* Request Opcodes */
|
||||
#define CTC_CONFIG 30
|
||||
#define CTC_CLOSE 31
|
||||
#define CTC_FORMAT 32
|
||||
#define CTC_OPEN 33
|
||||
#define CTC_READ 34
|
||||
#define CTC_WRITE 35
|
||||
#define CTC_VWRITE 36
|
||||
|
||||
/* Completion Opcodes */
|
||||
#define CTC_SUCCESS 0
|
||||
#define CTC_HWERROR 32
|
||||
#define CTC_RDONLY 33
|
||||
#define CTC_NOTREADY 36
|
||||
#define CTC_NOMEDIA 42
|
||||
|
||||
/* VTOC values */
|
||||
#define VTOC_VERSION 1
|
||||
#define VTOC_SECSZ 512
|
||||
#define VTOC_PART 16 /* Number of "partitions" on tape */
|
||||
#define VTOC_VALID 0x600DDEEE /* Magic number for valid VTOC */
|
||||
|
||||
/* Physical Device Info (pdinfo) values */
|
||||
#define PD_VALID 0xCA5E600D /* Magic number for valid PDINFO */
|
||||
#define PD_DRIVEID 5
|
||||
#define PD_VERSION 0
|
||||
#define PD_CYLS 6
|
||||
#define PD_TRACKS 245
|
||||
#define PD_SECTORS 31
|
||||
#define PD_BYTES 512
|
||||
#define PD_LOGICALST 29
|
||||
|
||||
#define CTC_CAPACITY (PD_CYLS * PD_TRACKS * PD_SECTORS) /* In blocks */
|
||||
|
||||
struct partition {
|
||||
uint16 id; /* Partition ID */
|
||||
uint16 flag; /* Permission Flags */
|
||||
uint32 sstart; /* Starting Sector */
|
||||
uint32 ssize; /* Size in Sectors */
|
||||
};
|
||||
|
||||
struct vtoc {
|
||||
uint32 bootinfo[3]; /* n/a */
|
||||
uint32 sanity; /* magic number */
|
||||
uint32 version; /* layout version */
|
||||
uint8 volume[8]; /* volume name */
|
||||
uint16 sectorsz; /* sector size in bytes */
|
||||
uint16 nparts; /* number of partitions */
|
||||
uint32 reserved[10]; /* free space */
|
||||
struct partition part[VTOC_PART]; /* partition headers */
|
||||
uint32 timestamp[VTOC_PART]; /* partition timestamp */
|
||||
};
|
||||
|
||||
struct pdinfo {
|
||||
uint32 driveid; /* identifies the device type */
|
||||
uint32 sanity; /* verifies device sanity */
|
||||
uint32 version; /* version number */
|
||||
uint8 serial[12]; /* serial number of the device */
|
||||
uint32 cyls; /* number of cylinders per drive */
|
||||
uint32 tracks; /* number tracks per cylinder */
|
||||
uint32 sectors; /* number sectors per track */
|
||||
uint32 bytes; /* number of bytes per sector */
|
||||
uint32 logicalst; /* sector address of logical sector 0 */
|
||||
uint32 errlogst; /* sector address of error log area */
|
||||
uint32 errlogsz; /* size in bytes of error log area */
|
||||
uint32 mfgst; /* sector address of mfg. defect info */
|
||||
uint32 mfgsz; /* size in bytes of mfg. defect info */
|
||||
uint32 defectst; /* sector address of the defect map */
|
||||
uint32 defectsz; /* size in bytes of defect map */
|
||||
uint32 relno; /* number of relocation areas */
|
||||
uint32 relst; /* sector address of relocation area */
|
||||
uint32 relsz; /* size in sectors of relocation area */
|
||||
uint32 relnext; /* address of next avail reloc sector */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32 time; /* Time used during a tape session (in 25ms chunks) */
|
||||
} CTC_STATE;
|
||||
|
||||
extern DEVICE ctc_dev;
|
||||
|
||||
t_stat ctc_reset(DEVICE *dptr);
|
||||
t_stat ctc_svc(UNIT *uptr);
|
||||
t_stat ctc_attach(UNIT *uptr, CONST char *cptr);
|
||||
t_stat ctc_detach(UNIT *uptr);
|
||||
void ctc_sysgen(uint8 cid);
|
||||
void ctc_express(uint8 cid);
|
||||
void ctc_full(uint8 cid);
|
||||
|
||||
/* Largely here for debugging purposes */
|
||||
static t_stat ctc_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||
static t_stat ctc_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||
static t_stat ctc_show_queue_common(FILE *st, UNIT *uptr, int32 val, CONST void *desc, t_bool rq);
|
||||
|
||||
#endif /* _3B2_CTC_H_ */
|
|
@ -83,10 +83,10 @@ noret __libc_longjmp (jmp_buf buf, int val);
|
|||
#define PHYS_MEM_BASE 0x2000000
|
||||
|
||||
#define ROM_BASE 0
|
||||
#define IO_BASE 0x40000
|
||||
#define IO_SIZE 0x10000
|
||||
#define IOB_BASE 0x200000
|
||||
#define IOB_SIZE 0x1E00000
|
||||
|
||||
/* IO area */
|
||||
#define IO_BOTTOM 0x40000
|
||||
#define IO_TOP 0x50000
|
||||
|
||||
/* Register numbers */
|
||||
#define NUM_FP 9
|
||||
|
@ -140,8 +140,8 @@ noret __libc_longjmp (jmp_buf buf, int val);
|
|||
#define EXECUTE_MSG 0x008
|
||||
#define INIT_MSG 0x010
|
||||
#define IRQ_MSG 0x020
|
||||
#define IO_D_MSG 0x040
|
||||
#define TRACE_MSG 0x080
|
||||
#define IO_DBG 0x040
|
||||
#define TRACE_DBG 0x080
|
||||
#define ERR_MSG 0x100
|
||||
|
||||
/* Data types operated on by instructions. NB: These integer values
|
||||
|
@ -307,33 +307,18 @@ noret __libc_longjmp (jmp_buf buf, int val);
|
|||
#define MEMSIZE_REG 0x4C003
|
||||
#define CIO_BOTTOM 0x200000
|
||||
#define CIO_TOP 0x2000000
|
||||
#define CIO_SLOTS 12
|
||||
|
||||
#define CIO_CSBIT 0x80
|
||||
#define CIO_CMDSTAT 0x80
|
||||
#define CIO_SEQBIT 0x40
|
||||
|
||||
#define CIO_INT_DELAY 8000
|
||||
|
||||
|
||||
/* Timer definitions */
|
||||
|
||||
#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */
|
||||
#define TPS_CLK 100 /* 100 ticks per second */
|
||||
|
||||
#define CLK_MIN_TICKS 500 /* No fewer than 500 sim steps between ticks */
|
||||
|
||||
|
||||
/* TIMING SECTION */
|
||||
/* ----------------------------------------------- */
|
||||
/* Calculate delays (in simulator steps) for times */
|
||||
/* System clock runs at 10MHz; 100ns period. */
|
||||
|
||||
#define US_PER_INST 1.6
|
||||
|
||||
#define INST_PER_MS (1000.0 / US_PER_INST)
|
||||
|
||||
#define DELAY_US(us) ((uint32)((us) / US_PER_INST))
|
||||
#define DELAY_MS(ms) ((uint32)(((ms) * 1000) / US_PER_INST))
|
||||
|
||||
/* global symbols from the CPU */
|
||||
|
||||
extern jmp_buf save_env;
|
||||
|
@ -415,6 +400,7 @@ extern void cpu_clear_irq(uint8 ipl, uint16 csr_flags);
|
|||
/* global symbols from the IO system */
|
||||
extern uint32 io_read(uint32 pa, size_t size);
|
||||
extern void io_write(uint32 pa, uint32 val, size_t size);
|
||||
extern void cio_clear(uint8 cid);
|
||||
extern void cio_xfer();
|
||||
extern uint8 cio_int;
|
||||
extern uint16 cio_ipl;
|
||||
|
|
68
3B2/3b2_id.c
68
3B2/3b2_id.c
|
@ -44,27 +44,14 @@
|
|||
|
||||
#include "3b2_id.h"
|
||||
|
||||
/* Wait times, in CPU steps, for various actions */
|
||||
|
||||
/* Each step is 50 us in buffered mode */
|
||||
#define ID_SEEK_WAIT 100 /* us */
|
||||
#define ID_SEEK_BASE 700 /* us */
|
||||
#define ID_RECAL_WAIT 6000 /* us */
|
||||
|
||||
/* Reading data takes about 8ms per sector */
|
||||
#define ID_RW_WAIT 8000 /* us */
|
||||
|
||||
/* Sense Unit Status completes in about 200 us */
|
||||
#define ID_SUS_WAIT 200 /* us */
|
||||
|
||||
/* Specify takes a bit longer, 1.25 ms */
|
||||
#define ID_SPEC_WAIT 1250 /* us */
|
||||
|
||||
/* Sense Interrupt Status is about 142 us */
|
||||
#define ID_SIS_WAIT 142 /* us */
|
||||
|
||||
/* The catch-all command wait time is about 140 us */
|
||||
#define ID_CMD_WAIT 140 /* us */
|
||||
#define ID_SEEK_WAIT 50
|
||||
#define ID_SEEK_BASE 700
|
||||
#define ID_RECAL_WAIT 6000
|
||||
#define ID_RW_WAIT 1000
|
||||
#define ID_SUS_WAIT 200
|
||||
#define ID_SPEC_WAIT 1250
|
||||
#define ID_SIS_WAIT 142
|
||||
#define ID_CMD_WAIT 140
|
||||
|
||||
/* Data FIFO pointer - Read */
|
||||
uint8 id_dpr = 0;
|
||||
|
@ -198,10 +185,9 @@ static SIM_INLINE void id_clear_fifo()
|
|||
id_dpw = 0;
|
||||
}
|
||||
|
||||
/* TODO: Remove after debugging */
|
||||
static SIM_INLINE void id_activate(UNIT *uptr, int32 delay)
|
||||
{
|
||||
sim_activate(uptr, delay);
|
||||
sim_activate_abs(uptr, delay);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -282,7 +268,7 @@ t_stat id_unit_svc(UNIT *uptr)
|
|||
"[%08x]\tINTR\t\tCOMPLETING Recal/Seek SEEK_0 UNIT %d\n",
|
||||
R[NUM_PC], unit);
|
||||
id_seek_state[unit] = ID_SEEK_1;
|
||||
id_activate(uptr, DELAY_US(8000)); /* TODO: Correct Delay based on steps */
|
||||
id_activate(uptr, 8000); /* TODO: Correct Delay based on steps */
|
||||
break;
|
||||
case ID_SEEK_1:
|
||||
sim_debug(EXECUTE_MSG, &id_dev,
|
||||
|
@ -738,7 +724,7 @@ void id_handle_command(uint8 val)
|
|||
"[%08x]\tCOMMAND\t%02x\tSense Int. Status\n",
|
||||
R[NUM_PC], val);
|
||||
id_status &= ~ID_STAT_SRQ; /* SIS immediately de-asserts SRQ */
|
||||
id_activate(id_ctlr_unit, DELAY_US(ID_SIS_WAIT));
|
||||
id_activate(id_ctlr_unit, ID_SIS_WAIT);
|
||||
break;
|
||||
case ID_CMD_SPEC:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
|
@ -748,19 +734,19 @@ void id_handle_command(uint8 val)
|
|||
id_etn = id_data[3];
|
||||
id_esn = id_data[4];
|
||||
id_polling = (id_dtlh & ID_DTLH_POLL) == 0;
|
||||
id_activate(id_ctlr_unit, DELAY_US(ID_SPEC_WAIT));
|
||||
id_activate(id_ctlr_unit, ID_SPEC_WAIT);
|
||||
break;
|
||||
case ID_CMD_SUS:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tSense Unit Status - %d\n",
|
||||
R[NUM_PC], val, id_ua);
|
||||
id_activate(id_sel_unit, DELAY_US(ID_SUS_WAIT));
|
||||
id_activate(id_sel_unit, ID_SUS_WAIT);
|
||||
break;
|
||||
case ID_CMD_DERR:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tDetect Error\n",
|
||||
R[NUM_PC], val);
|
||||
id_activate(id_ctlr_unit, DELAY_US(ID_CMD_WAIT));
|
||||
id_activate(id_ctlr_unit, ID_CMD_WAIT);
|
||||
break;
|
||||
case ID_CMD_RECAL:
|
||||
time = id_cyl[id_unit_num];
|
||||
|
@ -770,12 +756,12 @@ void id_handle_command(uint8 val)
|
|||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tRecalibrate - %d - POLLING\n",
|
||||
R[NUM_PC], val, id_ua);
|
||||
id_activate(id_sel_unit, DELAY_US(1000));
|
||||
id_activate(id_sel_unit, 1000);
|
||||
} else {
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tRecalibrate - %d - NORMAL\n",
|
||||
R[NUM_PC], val, id_ua);
|
||||
id_activate(id_sel_unit, DELAY_US(ID_RECAL_WAIT + (time * ID_SEEK_WAIT)));
|
||||
id_activate(id_sel_unit, (ID_RECAL_WAIT + (time * ID_SEEK_WAIT)));
|
||||
}
|
||||
break;
|
||||
case ID_CMD_SEEK:
|
||||
|
@ -790,12 +776,12 @@ void id_handle_command(uint8 val)
|
|||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tSeek - %d - POLLING\n",
|
||||
R[NUM_PC], val, id_ua);
|
||||
id_activate(id_sel_unit, DELAY_US(1000));
|
||||
id_activate(id_sel_unit, 4000);
|
||||
} else {
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tSeek - %d - NORMAL\n",
|
||||
R[NUM_PC], val, id_ua);
|
||||
id_activate(id_sel_unit, DELAY_US(ID_SEEK_BASE + (time * ID_SEEK_WAIT)));
|
||||
id_activate(id_sel_unit, ID_SEEK_BASE + (time * ID_SEEK_WAIT));
|
||||
}
|
||||
break;
|
||||
case ID_CMD_FMT:
|
||||
|
@ -838,7 +824,7 @@ void id_handle_command(uint8 val)
|
|||
|
||||
id_data[1] = id_scnt;
|
||||
|
||||
id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT));
|
||||
id_activate(id_sel_unit, ID_CMD_WAIT);
|
||||
break;
|
||||
case ID_CMD_VID:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
|
@ -846,7 +832,7 @@ void id_handle_command(uint8 val)
|
|||
R[NUM_PC], val, id_ua);
|
||||
id_data[0] = 0;
|
||||
id_data[1] = 0x05; /* What do we put here? */
|
||||
id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT));
|
||||
id_activate(id_sel_unit, ID_CMD_WAIT);
|
||||
break;
|
||||
case ID_CMD_RID:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
|
@ -867,13 +853,13 @@ void id_handle_command(uint8 val)
|
|||
"[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ ID.\n",
|
||||
R[NUM_PC], id_ua);
|
||||
}
|
||||
id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT));
|
||||
id_activate(id_sel_unit, ID_CMD_WAIT);
|
||||
break;
|
||||
case ID_CMD_RDIAG:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tRead Diag - %d\n",
|
||||
R[NUM_PC], val, id_ua);
|
||||
id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT));
|
||||
id_activate(id_sel_unit, ID_CMD_WAIT);
|
||||
break;
|
||||
case ID_CMD_RDATA:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
|
@ -895,25 +881,25 @@ void id_handle_command(uint8 val)
|
|||
"[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ DATA.\n",
|
||||
R[NUM_PC], id_ua);
|
||||
}
|
||||
id_activate(id_sel_unit, DELAY_US(ID_RW_WAIT));
|
||||
id_activate(id_sel_unit, ID_RW_WAIT);
|
||||
break;
|
||||
case ID_CMD_CHECK:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tCheck - %d\n",
|
||||
R[NUM_PC], val, id_ua);
|
||||
id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT));
|
||||
id_activate(id_sel_unit, ID_CMD_WAIT);
|
||||
break;
|
||||
case ID_CMD_SCAN:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tScan - %d\n",
|
||||
R[NUM_PC], val, id_ua);
|
||||
id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT));
|
||||
id_activate(id_sel_unit, ID_CMD_WAIT);
|
||||
break;
|
||||
case ID_CMD_VDATA:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
"[%08x]\tCOMMAND\t%02x\tVerify Data - %d\n",
|
||||
R[NUM_PC], val, id_ua);
|
||||
id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT));
|
||||
id_activate(id_sel_unit, ID_CMD_WAIT);
|
||||
break;
|
||||
case ID_CMD_WDATA:
|
||||
sim_debug(WRITE_MSG, &id_dev,
|
||||
|
@ -935,7 +921,7 @@ void id_handle_command(uint8 val)
|
|||
"[%08x]\tUNIT %d NOT ATTACHED, CANNOT WRITE.\n",
|
||||
R[NUM_PC], id_ua);
|
||||
}
|
||||
id_activate(id_sel_unit, DELAY_US(ID_RW_WAIT));
|
||||
id_activate(id_sel_unit, ID_RW_WAIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
33
3B2/3b2_if.c
33
3B2/3b2_if.c
|
@ -35,13 +35,11 @@
|
|||
*/
|
||||
double if_start_time;
|
||||
|
||||
#define IF_START_TIME() { if_start_time = sim_gtime(); }
|
||||
#define IF_DIFF_MS() ((sim_gtime() - if_start_time) / INST_PER_MS)
|
||||
#ifndef max
|
||||
#define max(x,y) ((x) > (y) ? (x) : (y))
|
||||
#ifndef MAX
|
||||
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
||||
#endif
|
||||
#ifndef min
|
||||
#define min(x,y) ((x) < (y) ? (x) : (y))
|
||||
#ifndef MIN
|
||||
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -59,12 +57,12 @@ double if_start_time;
|
|||
* step is 6ms and head settling time is 30ms.
|
||||
*/
|
||||
|
||||
#define IF_STEP_DELAY 6 /* ms */
|
||||
#define IF_R_DELAY 85 /* ms */
|
||||
#define IF_W_DELAY 90 /* ms */
|
||||
#define IF_VERIFY_DELAY 30 /* ms */
|
||||
#define IF_HLD_DELAY 80 /* ms */
|
||||
#define IF_HSW_DELAY 60 /* ms */
|
||||
#define IF_STEP_DELAY 6000 /* us */
|
||||
#define IF_R_DELAY 85000 /* us */
|
||||
#define IF_W_DELAY 90000 /* us */
|
||||
#define IF_VERIFY_DELAY 30000 /* us */
|
||||
#define IF_HLD_DELAY 80000 /* us */
|
||||
#define IF_HSW_DELAY 60000 /* us */
|
||||
|
||||
UNIT if_unit = {
|
||||
UDATA (&if_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+
|
||||
|
@ -103,8 +101,7 @@ static SIM_INLINE void if_clear_irq()
|
|||
|
||||
static SIM_INLINE void if_activate(uint32 delay)
|
||||
{
|
||||
IF_START_TIME();
|
||||
sim_activate_abs(&if_unit, (int32) DELAY_MS(delay));
|
||||
sim_activate_abs(&if_unit, delay);
|
||||
}
|
||||
|
||||
static SIM_INLINE void if_cancel_pending_irq()
|
||||
|
@ -131,7 +128,7 @@ t_stat if_svc(UNIT *uptr)
|
|||
if_state.cmd = 0;
|
||||
|
||||
/* Request an interrupt */
|
||||
sim_debug(IRQ_MSG, &if_dev, "\tINTR\t\tDELTA=%f ms\n", IF_DIFF_MS());
|
||||
sim_debug(IRQ_MSG, &if_dev, "\tINTR\n");
|
||||
if_set_irq();
|
||||
|
||||
return SCPE_OK;
|
||||
|
@ -322,20 +319,20 @@ void if_handle_command()
|
|||
case IF_STEP_T:
|
||||
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep\n", if_state.cmd);
|
||||
if_activate(IF_STEP_DELAY);
|
||||
if_state.track = (uint8) min(max((int) if_state.track + if_state.step_dir, 0), 0x4f);
|
||||
if_state.track = (uint8) MIN(MAX((int) if_state.track + if_state.step_dir, 0), 0x4f);
|
||||
break;
|
||||
case IF_STEP_IN:
|
||||
case IF_STEP_IN_T:
|
||||
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep In\n", if_state.cmd);
|
||||
if_state.step_dir = IF_STEP_IN_DIR;
|
||||
if_state.track = (uint8) max((int) if_state.track + if_state.step_dir, 0);
|
||||
if_state.track = (uint8) MAX((int) if_state.track + if_state.step_dir, 0);
|
||||
if_activate(IF_STEP_DELAY);
|
||||
break;
|
||||
case IF_STEP_OUT:
|
||||
case IF_STEP_OUT_T:
|
||||
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep Out\n", if_state.cmd);
|
||||
if_state.step_dir = IF_STEP_OUT_DIR;
|
||||
if_state.track = (uint8) min((int) if_state.track + if_state.step_dir, 0x4f);
|
||||
if_state.track = (uint8) MIN((int) if_state.track + if_state.step_dir, 0x4f);
|
||||
if_activate(IF_STEP_DELAY);
|
||||
break;
|
||||
case IF_SEEK:
|
||||
|
|
392
3B2/3b2_io.c
392
3B2/3b2_io.c
|
@ -49,15 +49,32 @@ struct iolink iotable[] = {
|
|||
{ 0, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
void cio_clear(uint8 cid)
|
||||
{
|
||||
cio[cid].id = 0;
|
||||
cio[cid].exp_handler = NULL;
|
||||
cio[cid].full_handler = NULL;
|
||||
cio[cid].sysgen = NULL;
|
||||
cio[cid].rqp = 0;
|
||||
cio[cid].cqp = 0;
|
||||
cio[cid].rqs = 0;
|
||||
cio[cid].cqs = 0;
|
||||
cio[cid].ivec = 0;
|
||||
cio[cid].no_rque = 0;
|
||||
cio[cid].ipl = 0;
|
||||
cio[cid].intr = FALSE;
|
||||
cio[cid].sysgen_s = 0;
|
||||
cio[cid].seqbit = 0;
|
||||
cio[cid].op = 0;
|
||||
}
|
||||
|
||||
void cio_sysgen(uint8 cid)
|
||||
{
|
||||
uint32 sysgen_p;
|
||||
uint32 cq_exp;
|
||||
cio_entry cqe;
|
||||
|
||||
sysgen_p = pread_w(SYSGEN_PTR);
|
||||
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[%08x] [SYSGEN] Starting sysgen for card %d. sysgen_p=%08x\n",
|
||||
R[NUM_PC], cid, sysgen_p);
|
||||
|
||||
|
@ -71,75 +88,68 @@ void cio_sysgen(uint8 cid)
|
|||
cio[cid].ivec = pread_b(sysgen_p + 10);
|
||||
cio[cid].no_rque = pread_b(sysgen_p + 11);
|
||||
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[SYSGEN] sysgen rqp = %08x\n",
|
||||
cio[cid].rqp);
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[SYSGEN] sysgen cqp = %08x\n",
|
||||
cio[cid].cqp);
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[SYSGEN] sysgen rqs = %02x\n",
|
||||
cio[cid].rqs);
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[SYSGEN] sysgen cqs = %02x\n",
|
||||
cio[cid].cqs);
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[SYSGEN] sysgen ivec = %02x\n",
|
||||
cio[cid].ivec);
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[SYSGEN] sysgen no_rque = %02x\n",
|
||||
cio[cid].no_rque);
|
||||
|
||||
cq_exp = cio[cid].cqp;
|
||||
|
||||
cqe.byte_count = 0;
|
||||
cqe.subdevice = 0;
|
||||
cqe.opcode = 3;
|
||||
cqe.address = 0;
|
||||
cqe.app_data = 0;
|
||||
|
||||
cio_cexpress(cid, &cqe);
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[SYSGEN] Sysgen complete. Completion Queue written.\n");
|
||||
|
||||
/* If the card has a custom sysgen handler, run it */
|
||||
if (cio[cid].sysgen != NULL) {
|
||||
cio[cid].sysgen(cid);
|
||||
} else {
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[%08x] [cio_sysgen] Not running custom sysgen.\n",
|
||||
R[NUM_PC]);
|
||||
}
|
||||
}
|
||||
|
||||
void cio_cexpress(uint8 cid, cio_entry *cqe)
|
||||
void cio_cexpress(uint8 cid, uint16 esize, cio_entry *cqe, uint8 *app_data)
|
||||
{
|
||||
uint32 cqp;
|
||||
uint32 cqp, i;
|
||||
|
||||
cqp = cio[cid].cqp;
|
||||
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[%08x] [cio_cexpress] cqp = %08x seqbit = %d\n",
|
||||
R[NUM_PC], cqp, cio[cid].seqbit);
|
||||
|
||||
cio[cid].seqbit ^= 1;
|
||||
|
||||
if (cio[cid].seqbit) {
|
||||
cqe->subdevice |= CIO_SEQBIT;
|
||||
}
|
||||
cqe->subdevice |= (cio[cid].seqbit << 6);
|
||||
|
||||
pwrite_h(cqp, cqe->byte_count);
|
||||
pwrite_b(cqp + 2, cqe->subdevice);
|
||||
pwrite_b(cqp + 3, cqe->opcode);
|
||||
pwrite_w(cqp + 4, cqe->address);
|
||||
pwrite_w(cqp + 8, cqe->app_data);
|
||||
|
||||
/* Write application-specific data. */
|
||||
for (i = 0; i < (esize - QESIZE); i++) {
|
||||
pwrite_b(cqp + 8 + i, app_data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write an entry into the Completion Queue */
|
||||
void cio_cqueue(uint8 cid, cio_entry *cqe)
|
||||
void cio_cqueue(uint8 cid, uint8 cmd_stat, uint16 esize,
|
||||
cio_entry *cqe, uint8 *app_data)
|
||||
{
|
||||
uint32 cqp, top;
|
||||
uint16 lp;
|
||||
uint32 cqp, top, i;
|
||||
uint16 lp, ulp;
|
||||
|
||||
/* Apply the CMD/STAT bit */
|
||||
cqe->subdevice |= (cmd_stat << 7);
|
||||
|
||||
/* Get the physical address of the completion queue
|
||||
* in main memory */
|
||||
|
@ -147,90 +157,163 @@ void cio_cqueue(uint8 cid, cio_entry *cqe)
|
|||
|
||||
/* Get the physical address of the first entry in
|
||||
* the completion queue */
|
||||
top = cqp + QUE_OFFSET;
|
||||
top = cqp + esize + LUSIZE;
|
||||
|
||||
/* Get the load pointer. This is a 16-bit absolute offset
|
||||
* from the top of the queue to the start of the entry. */
|
||||
lp = pread_h(cqp + LOAD_OFFSET);
|
||||
lp = pread_h(cqp + esize);
|
||||
ulp = pread_h(cqp + esize + 2);
|
||||
|
||||
/* Load the entry at the supplied address */
|
||||
pwrite_h(top + lp, cqe->byte_count);
|
||||
pwrite_b(top + lp + 2, cqe->subdevice);
|
||||
pwrite_b(top + lp + 3, cqe->opcode);
|
||||
pwrite_w(top + lp + 4, cqe->address);
|
||||
pwrite_w(top + lp + 8, cqe->app_data);
|
||||
|
||||
/* Write application-specific data. */
|
||||
for (i = 0; i < (esize - QESIZE); i++) {
|
||||
pwrite_b(top + lp + 8 + i, app_data[i]);
|
||||
}
|
||||
|
||||
/* Increment the load pointer to the next queue location.
|
||||
* If we go past the end of the queue, wrap around to the
|
||||
* start of the queue */
|
||||
if (cio[cid].cqs > 0) {
|
||||
lp = (lp + QUE_E_SIZE) % (QUE_E_SIZE * cio[cid].cqs);
|
||||
lp = (lp + esize) % (esize * cio[cid].cqs);
|
||||
|
||||
/* Store it back to the correct location */
|
||||
pwrite_h(cqp + LOAD_OFFSET, lp);
|
||||
pwrite_h(cqp + esize, lp);
|
||||
} else {
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[%08x] [cio_cqueue] ERROR! Completion Queue Size is 0!",
|
||||
R[NUM_PC]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Retrieve an entry from the Request Queue */
|
||||
void cio_rqueue(uint8 cid, cio_entry *cqe)
|
||||
/*
|
||||
* Retrieve the Express Entry from the Request Queue
|
||||
*/
|
||||
void cio_rexpress(uint8 cid, uint16 esize, cio_entry *rqe, uint8 *app_data)
|
||||
{
|
||||
uint32 rqp, i;
|
||||
|
||||
rqp = cio[cid].rqp;
|
||||
|
||||
/* Unload the express entry from the request queue */
|
||||
rqe->byte_count = pread_h(rqp);
|
||||
rqe->subdevice = pread_b(rqp + 2);
|
||||
rqe->opcode = pread_b(rqp + 3);
|
||||
rqe->address = pread_w(rqp + 4);
|
||||
|
||||
for (i = 0; i < (esize - QESIZE); i++) {
|
||||
app_data[i] = pread_b(rqp + 8 + i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve an entry from the Request Queue. This function
|
||||
* returns the load pointer that points to the NEXT available slot.
|
||||
* This may be used by callers to determine which queue(s) need to
|
||||
* be serviced.
|
||||
*
|
||||
* Returns SCPE_OK on success, or SCPE_NXM if no entry was found.
|
||||
*/
|
||||
t_stat cio_rqueue(uint8 cid, uint8 qnum, uint16 esize,
|
||||
cio_entry *rqe, uint8 *app_data)
|
||||
{
|
||||
uint32 rqp, top, i;
|
||||
uint16 ulp;
|
||||
uint16 lp, ulp;
|
||||
|
||||
/* Get the physical address of the request queue in main memory */
|
||||
rqp = cio[cid].rqp + 12; /* Skip past the Express Queue Entry */
|
||||
rqp = cio[cid].rqp +
|
||||
esize +
|
||||
(qnum * (LUSIZE + (esize * cio[cid].rqs)));
|
||||
|
||||
/* Scan each queue until we find one with a command in it. */
|
||||
for (i = 0; i < cio[cid].no_rque; i++) {
|
||||
/* Get the physical address of the first entry in the request
|
||||
* queue */
|
||||
top = rqp + 4;
|
||||
lp = pread_h(rqp);
|
||||
ulp = pread_h(rqp + 2);
|
||||
|
||||
/* Check to see what we've got in the queue. */
|
||||
ulp = pread_h(rqp + 2);
|
||||
|
||||
cqe->opcode = pread_b(top + ulp + 3);
|
||||
|
||||
if (cqe->opcode > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
rqp += 4 + (12 * cio[cid].rqs);
|
||||
if (lp == ulp) {
|
||||
return SCPE_NXM;
|
||||
}
|
||||
|
||||
if (i >= cio[cid].no_rque) {
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[%08x] [cio_rque] FAILURE! NO MORE QUEUES TO EXAMINE.\n",
|
||||
R[NUM_PC]);
|
||||
return;
|
||||
}
|
||||
top = rqp + LUSIZE;
|
||||
|
||||
/* Retrieve the entry at the supplied address */
|
||||
cqe->byte_count = pread_h(top + ulp);
|
||||
cqe->subdevice = pread_b(top + ulp + 2);
|
||||
cqe->address = pread_w(top + ulp + 4);
|
||||
cqe->app_data = pread_w(top + ulp + 8);
|
||||
rqe->byte_count = pread_h(top + ulp);
|
||||
rqe->subdevice = pread_b(top + ulp + 2);
|
||||
rqe->opcode = pread_b(top + ulp + 3);
|
||||
rqe->address = pread_w(top + ulp + 4);
|
||||
|
||||
dump_entry("REQUEST", cqe);
|
||||
/* Read application-specific data. */
|
||||
for (i = 0; i < (esize - QESIZE); i++) {
|
||||
app_data[i] = pread_b(top + ulp + 8 + i);
|
||||
}
|
||||
|
||||
/* Increment the unload pointer to the next queue location. If we
|
||||
* go past the end of the queue, wrap around to the start of the
|
||||
* queue */
|
||||
if (cio[cid].rqs > 0) {
|
||||
ulp = (ulp + QUE_E_SIZE) % (QUE_E_SIZE * cio[cid].rqs);
|
||||
|
||||
/* Store it back to the correct location */
|
||||
ulp = (ulp + esize) % (esize * cio[cid].rqs);
|
||||
pwrite_h(rqp + 2, ulp);
|
||||
} else {
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[%08x] [cio_rqueue] ERROR! Request Queue Size is 0!",
|
||||
R[NUM_PC]);
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the Load Pointer for the given request queue
|
||||
*/
|
||||
uint16 cio_r_lp(uint8 cid, uint8 qnum, uint16 esize)
|
||||
{
|
||||
uint32 rqp;
|
||||
|
||||
rqp = cio[cid].rqp +
|
||||
esize +
|
||||
(qnum * (LUSIZE + (esize * cio[cid].rqs)));
|
||||
|
||||
return pread_h(rqp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the Unload Pointer for the given request queue
|
||||
*/
|
||||
uint16 cio_r_ulp(uint8 cid, uint8 qnum, uint16 esize)
|
||||
{
|
||||
uint32 rqp;
|
||||
|
||||
rqp = cio[cid].rqp +
|
||||
esize +
|
||||
(qnum * (LUSIZE + (esize * cio[cid].rqs)));
|
||||
|
||||
return pread_h(rqp + 2);
|
||||
}
|
||||
|
||||
uint16 cio_c_lp(uint8 cid, uint16 esize)
|
||||
{
|
||||
uint32 cqp;
|
||||
cqp = cio[cid].cqp + esize;
|
||||
return pread_h(cqp);
|
||||
}
|
||||
|
||||
uint16 cio_c_ulp(uint8 cid, uint16 esize)
|
||||
{
|
||||
uint32 cqp;
|
||||
cqp = cio[cid].cqp + esize;
|
||||
return pread_h(cqp + 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if there is room in the completion queue
|
||||
* for a new entry.
|
||||
*/
|
||||
t_bool cio_cqueue_avail(uint cid, uint16 esize)
|
||||
{
|
||||
uint32 lp, ulp;
|
||||
|
||||
lp = pread_h(cio[cid].cqp + esize);
|
||||
ulp = pread_h(cio[cid].cqp + esize + 2);
|
||||
|
||||
return(((lp + esize) % (cio[cid].cqs * esize)) != ulp);
|
||||
}
|
||||
|
||||
uint32 io_read(uint32 pa, size_t size)
|
||||
|
@ -241,7 +324,7 @@ uint32 io_read(uint32 pa, size_t size)
|
|||
/* Special devices */
|
||||
if (pa == MEMSIZE_REG) {
|
||||
|
||||
/* It appears that the following values map to memory sizes:
|
||||
/* The following values map to memory sizes:
|
||||
0x00: 512KB ( 524,288 B)
|
||||
0x01: 2MB (2,097,152 B)
|
||||
0x02: 1MB (1,048,576 B)
|
||||
|
@ -261,13 +344,16 @@ uint32 io_read(uint32 pa, size_t size)
|
|||
}
|
||||
}
|
||||
|
||||
/* IO Board Area - Unimplemented */
|
||||
/* CIO board area */
|
||||
if (pa >= CIO_BOTTOM && pa < CIO_TOP) {
|
||||
cid = CID(pa);
|
||||
reg = pa - CADDR(cid);
|
||||
|
||||
if (cio[cid].id == 0) {
|
||||
/* Nothing lives here */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] No card at cid=%d reg=%d\n",
|
||||
R[NUM_PC], cid, reg);
|
||||
csr_data |= CSRTIMO;
|
||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||
return 0;
|
||||
|
@ -282,10 +368,11 @@ uint32 io_read(uint32 pa, size_t size)
|
|||
switch (reg) {
|
||||
case IOF_ID:
|
||||
case IOF_VEC:
|
||||
switch(cio[cid].cmdbits) {
|
||||
case 0x00: /* We've never seen an INT0 or INT1 */
|
||||
case 0x01: /* We've seen an INT0 but not an INT1. */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
switch(cio[cid].sysgen_s) {
|
||||
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
|
||||
case CIO_INT0: /* We've seen an INT0 but not an INT1. */
|
||||
cio[cid].sysgen_s |= CIO_INT0;
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT0) ID\n",
|
||||
R[NUM_PC], cid);
|
||||
/* Return the correct byte of our board ID */
|
||||
|
@ -295,15 +382,17 @@ uint32 io_read(uint32 pa, size_t size)
|
|||
data = (cio[cid].id & 0xff);
|
||||
}
|
||||
break;
|
||||
case 0x02: /* We've seen an INT1 but not an INT0. Time to sysgen */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */
|
||||
cio[cid].sysgen_s |= CIO_INT0;
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT0) SYSGEN\n",
|
||||
R[NUM_PC], cid);
|
||||
cio_sysgen(cid);
|
||||
data = cio[cid].ivec;
|
||||
break;
|
||||
case 0x03: /* We've already sysgen'ed */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
case CIO_SYSGEN: /* We've already sysgen'ed */
|
||||
cio[cid].sysgen_s |= CIO_INT0; /* This must come BEFORE the exp_handler */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT0) EXPRESS JOB\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].exp_handler(cid);
|
||||
|
@ -312,55 +401,60 @@ uint32 io_read(uint32 pa, size_t size)
|
|||
default:
|
||||
/* This should never happen */
|
||||
stop_reason = STOP_ERR;
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE cmdbits=%02x\n",
|
||||
R[NUM_PC], cid, cio[cid].cmdbits);
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n",
|
||||
R[NUM_PC], cid, cio[cid].sysgen_s);
|
||||
data = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Record that we've seen an INT0 */
|
||||
cio[cid].cmdbits |= CIO_INT0;
|
||||
return data;
|
||||
case IOF_CTRL:
|
||||
switch(cio[cid].cmdbits) {
|
||||
case 0x00: /* We've never seen an INT0 or INT1 */
|
||||
case 0x02: /* We've seen an INT1 but not an INT0 */
|
||||
switch(cio[cid].sysgen_s) {
|
||||
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
|
||||
case CIO_INT1: /* We've seen an INT1 but not an INT0 */
|
||||
/* There's nothing to do in this instance */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT1) IGNORED\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].sysgen_s |= CIO_INT1;
|
||||
break;
|
||||
case 0x01: /* We've seen an INT0 but not an INT1. Time to sysgen */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT1) SYSGEN\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].sysgen_s |= CIO_INT1;
|
||||
cio_sysgen(cid);
|
||||
break;
|
||||
case 0x03: /* We've already sysgen'ed */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
case CIO_SYSGEN: /* We've already sysgen'ed */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT1) FULL\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].sysgen_s |= CIO_INT1; /* This must come BEFORE the full handler */
|
||||
cio[cid].full_handler(cid);
|
||||
break;
|
||||
default:
|
||||
/* This should never happen */
|
||||
stop_reason = STOP_ERR;
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT1) ERROR IN STATE MACHINE cmdbits=%02x\n",
|
||||
R[NUM_PC], cid, cio[cid].cmdbits);
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n",
|
||||
R[NUM_PC], cid, cio[cid].sysgen_s);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Record that we've seen an INT1 */
|
||||
cio[cid].cmdbits |= CIO_INT1;
|
||||
return 0; /* Data returned is arbitrary */
|
||||
case IOF_STAT:
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] (%d RESET)\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].cmdbits = 0;
|
||||
cio[cid].sysgen_s = 0;
|
||||
return 0; /* Data returned is arbitrary */
|
||||
default:
|
||||
/* We should never reach here, but if we do, there's
|
||||
* nothing listening. */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[READ] [%08x] No card at cid=%d reg=%d\n",
|
||||
R[NUM_PC], cid, reg);
|
||||
csr_data |= CSRTIMO;
|
||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||
return 0;
|
||||
|
@ -375,7 +469,7 @@ uint32 io_read(uint32 pa, size_t size)
|
|||
}
|
||||
|
||||
/* Not found. */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[%08x] [io_read] ADDR=%08x: No device found.\n",
|
||||
R[NUM_PC], pa);
|
||||
csr_data |= CSRTIMO;
|
||||
|
@ -395,6 +489,9 @@ void io_write(uint32 pa, uint32 val, size_t size)
|
|||
|
||||
if (cio[cid].id == 0) {
|
||||
/* Nothing lives here */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] No card at cid=%d reg=%d\n",
|
||||
R[NUM_PC], cid, reg);
|
||||
csr_data |= CSRTIMO;
|
||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||
return;
|
||||
|
@ -409,79 +506,84 @@ void io_write(uint32 pa, uint32 val, size_t size)
|
|||
switch (reg) {
|
||||
case IOF_ID:
|
||||
case IOF_VEC:
|
||||
switch(cio[cid].cmdbits) {
|
||||
case 0x00: /* We've never seen an INT0 or INT1 */
|
||||
case 0x01: /* We've seen an INT0 but not an INT1. */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
switch(cio[cid].sysgen_s) {
|
||||
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
|
||||
case CIO_INT0: /* We've seen an INT0 but not an INT1. */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT0) ID\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].sysgen_s |= CIO_INT0;
|
||||
break;
|
||||
case 0x02: /* We've seen an INT1 but not an INT0. Time to sysgen */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT0) SYSGEN\n",
|
||||
case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT0) SYSGEN\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].sysgen_s |= CIO_INT0;
|
||||
cio_sysgen(cid);
|
||||
break;
|
||||
case 0x03: /* We've already sysgen'ed */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT0) EXPRESS JOB\n",
|
||||
case CIO_SYSGEN: /* We've already sysgen'ed */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT0) EXPRESS JOB\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].sysgen_s |= CIO_INT0;
|
||||
cio[cid].exp_handler(cid);
|
||||
break;
|
||||
default:
|
||||
/* This should never happen */
|
||||
stop_reason = STOP_ERR;
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE cmdbits=%02x\n",
|
||||
R[NUM_PC], cid, cio[cid].cmdbits);
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n",
|
||||
R[NUM_PC], cid, cio[cid].sysgen_s);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Record that we've seen an INT0 */
|
||||
cio[cid].cmdbits |= CIO_INT0;
|
||||
return;
|
||||
case IOF_CTRL:
|
||||
switch(cio[cid].cmdbits) {
|
||||
case 0x00: /* We've never seen an INT0 or INT1 */
|
||||
case 0x02: /* We've seen an INT1 but not an INT0 */
|
||||
switch(cio[cid].sysgen_s) {
|
||||
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
|
||||
case CIO_INT1: /* We've seen an INT1 but not an INT0 */
|
||||
/* There's nothing to do in this instance */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT1)\n",
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT1) IGNORED\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].sysgen_s |= CIO_INT1;
|
||||
break;
|
||||
case 0x01: /* We've seen an INT0 but not an INT1. Time to sysgen */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT1) SYSGEN\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].sysgen_s |= CIO_INT1;
|
||||
cio_sysgen(cid);
|
||||
break;
|
||||
case 0x03: /* We've already sysgen'ed */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
case CIO_SYSGEN: /* We've already sysgen'ed */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT1) FULL\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].sysgen_s |= CIO_INT1;
|
||||
cio[cid].full_handler(cid);
|
||||
break;
|
||||
default:
|
||||
/* This should never happen */
|
||||
stop_reason = STOP_ERR;
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT1) ERROR IN STATE MACHINE cmdbits=%02x\n",
|
||||
R[NUM_PC], cid, cio[cid].cmdbits);
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n",
|
||||
R[NUM_PC], cid, cio[cid].sysgen_s);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Record that we've seen an INT1 */
|
||||
cio[cid].cmdbits |= CIO_INT1;
|
||||
return;
|
||||
case IOF_STAT:
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] (%d RESET)\n",
|
||||
R[NUM_PC], cid);
|
||||
cio[cid].cmdbits = 0;
|
||||
cio[cid].sysgen_s = 0;
|
||||
return;
|
||||
default:
|
||||
/* We should never reach here, but if we do, there's
|
||||
* nothing listening. */
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[WRITE] [%08x] No card at cid=%d reg=%d\n",
|
||||
R[NUM_PC], cid, reg);
|
||||
csr_data |= CSRTIMO;
|
||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||
return;
|
||||
|
@ -497,7 +599,7 @@ void io_write(uint32 pa, uint32 val, size_t size)
|
|||
}
|
||||
|
||||
/* Not found. */
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
sim_debug(IO_DBG, &cpu_dev,
|
||||
"[%08x] [io_write] ADDR=%08x: No device found.\n",
|
||||
R[NUM_PC], pa);
|
||||
csr_data |= CSRTIMO;
|
||||
|
@ -506,11 +608,13 @@ void io_write(uint32 pa, uint32 val, size_t size)
|
|||
|
||||
|
||||
/* For debugging only */
|
||||
void dump_entry(CONST char *type, cio_entry *entry)
|
||||
void dump_entry(uint32 dbits, DEVICE *dev, CONST char *type,
|
||||
uint16 esize, cio_entry *entry, uint8 *app_data)
|
||||
{
|
||||
sim_debug(IO_D_MSG, &cpu_dev,
|
||||
"*** %s ENTRY: byte_count=%04x, subdevice=%02x,\n"
|
||||
" opcode=%d, address=%08x, app_data=%08x\n",
|
||||
uint32 i;
|
||||
|
||||
sim_debug(dbits, dev,
|
||||
"*** %s ENTRY: byte_count=%04x, subdevice=%02x, opcode=%d, address=%08x\n",
|
||||
type, entry->byte_count, entry->subdevice,
|
||||
entry->opcode, entry->address, entry->app_data);
|
||||
entry->opcode, entry->address);
|
||||
}
|
||||
|
|
87
3B2/3b2_io.h
87
3B2/3b2_io.h
|
@ -79,19 +79,33 @@
|
|||
*
|
||||
* The Queue structures (one for request, one for completion) hold:
|
||||
* - An express entry
|
||||
*
|
||||
* And then one or more queues, each queue consiting of
|
||||
* - A set of pointers for load and unload from the queue
|
||||
* - Zero or more Queue Entries
|
||||
* - One or more Queue Entries
|
||||
*
|
||||
* | Address | Size | Contents |
|
||||
* +---------------+------+-----------------------------------------+
|
||||
* | QUEUE_P | 12 | Express Queue Entry [1] |
|
||||
* | QUEUE_P + 12 | 2 | Load Pointer |
|
||||
* | QUEUE_P + 14 | 2 | Unload Pointer |
|
||||
* | QUEUE_P + 16 | 12 | Entry 0 [1] |
|
||||
* | QUEUE_P + 28 | 12 | Entry 1 [1] |
|
||||
* +---------------+------+-----------------------------------------+
|
||||
* | QUEUE_P + 12 | 2 | Load Pointer for Queue 0 |
|
||||
* | QUEUE_P + 14 | 2 | Unload Pointer for Queue 0 |
|
||||
* | QUEUE_P + 16 | 12 | Queue 0 Entry 0 [1] |
|
||||
* | QUEUE_P + 28 | 12 | Queue 0 Entry 1 [1] |
|
||||
* | ... | ... | ... |
|
||||
* +---------------+------+-----------------------------------------+
|
||||
* | QUEUE_P + n | 2 | Load Pointer for Queue 1 |
|
||||
* | QUEUE_P + n | 2 | Unload Pointer for Queue 1 |
|
||||
* | QUEUE_P + n | 12 | Queue 1 Entry 0 [1] |
|
||||
* | QUEUE_P + n | 12 | Queue 1 Entry 1 [1] |
|
||||
* | ... | ... | ... |
|
||||
*
|
||||
* [1] See Queue Entry above
|
||||
*
|
||||
* NB: There are multiple Request queues, usually one per subdevice,
|
||||
* and EACH Request queue starts with a Load Pointer, an Unload
|
||||
* Pointer, and then 'n' Queue Entries.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _3B2_IO_H_
|
||||
|
@ -111,34 +125,33 @@
|
|||
#define IOF_CTRL 3
|
||||
#define IOF_STAT 5
|
||||
|
||||
#define SYSGEN_PTR PHYS_MEM_BASE
|
||||
#define CIO_LOAD_SIZE 0x4
|
||||
#define CIO_ENTRY_SIZE 0x0c
|
||||
#define CIO_QUE_OFFSET 0x10
|
||||
#define CIO_SLOTS 12
|
||||
#define SYSGEN_PTR PHYS_MEM_BASE
|
||||
|
||||
/* CIO opcodes */
|
||||
#define CIO_DLM 1
|
||||
#define CIO_ULM 2
|
||||
#define CIO_FCF 3
|
||||
#define CIO_DOS 4
|
||||
#define CIO_DSD 5
|
||||
#define CIO_DLM 1
|
||||
#define CIO_ULM 2
|
||||
#define CIO_FCF 3
|
||||
#define CIO_DOS 4
|
||||
#define CIO_DSD 5
|
||||
|
||||
/* Map a physical address to a card ID */
|
||||
#define CID(pa) (((((pa) >> 0x14) & 0x1f) / 2) - 1)
|
||||
/* Map a card ID to a base address */
|
||||
#define CADDR(bid) (((((bid) + 1) * 2) << 0x14))
|
||||
|
||||
#define CIO_INT0 0x1
|
||||
#define CIO_INT1 0x2
|
||||
|
||||
/* Offsets into the request/completion queues of various values */
|
||||
#define LOAD_OFFSET 12
|
||||
#define ULOAD_OFFSET 14
|
||||
#define QUE_OFFSET 16
|
||||
#define QUE_E_SIZE 12
|
||||
#define LUSIZE 4 /* Load/Unload pointers size */
|
||||
#define QESIZE 8 /* Queue entry is 8 bytes + application data */
|
||||
|
||||
#define CIO_STAT 0
|
||||
#define CIO_CMD 1
|
||||
|
||||
/* Sysgen State */
|
||||
#define CIO_INT_NONE 0
|
||||
#define CIO_INT0 1
|
||||
#define CIO_INT1 2
|
||||
#define CIO_SYSGEN 3
|
||||
|
||||
#define CIO_SYGEN_MASK 0x3
|
||||
|
||||
typedef struct {
|
||||
uint16 id; /* Card ID */
|
||||
|
@ -153,10 +166,9 @@ typedef struct {
|
|||
uint8 no_rque; /* Number of request queues */
|
||||
uint8 ipl; /* IPL that this card uses */
|
||||
t_bool intr; /* Card needs to interrupt */
|
||||
uint8 cmdbits; /* Commands received since RESET */
|
||||
uint8 sysgen_s; /* Sysgen state */
|
||||
uint8 seqbit; /* Squence Bit */
|
||||
uint8 op; /* Last received opcode */
|
||||
TMLN *lines[4]; /* Terminal Multiplexer lines */
|
||||
} CIO_STATE;
|
||||
|
||||
typedef struct {
|
||||
|
@ -164,7 +176,6 @@ typedef struct {
|
|||
uint8 subdevice;
|
||||
uint8 opcode;
|
||||
uint32 address;
|
||||
uint32 app_data;
|
||||
} cio_entry;
|
||||
|
||||
struct iolink {
|
||||
|
@ -208,15 +219,21 @@ extern CIO_STATE cio[CIO_SLOTS];
|
|||
t_stat cio_reset(DEVICE *dptr);
|
||||
t_stat cio_svc(UNIT *uptr);
|
||||
|
||||
/* Put an entry into the Completion Queue's Express entry */
|
||||
void cio_cexpress(uint8 cid, cio_entry *cqe);
|
||||
/* Put an entry into the Completion Queue */
|
||||
void cio_cqueue(uint8 cid, cio_entry *cqe);
|
||||
/* Get an entry from the Request Queue */
|
||||
void cio_rqueue(uint8 cid, cio_entry *cqe);
|
||||
/* Perform a Sysgen */
|
||||
void cio_clear(uint8 cid);
|
||||
void cio_cexpress(uint8 cid, uint16 esize, cio_entry *cqe, uint8 *app_data);
|
||||
void cio_cqueue(uint8 cid, uint8 cmd_stat, uint16 esize,
|
||||
cio_entry *cqe, uint8 *app_data);
|
||||
void cio_rexpress(uint8 cid, uint16 esize, cio_entry *rqe, uint8 *app_data);
|
||||
t_stat cio_rqueue(uint8 cid, uint8 qnum, uint16 esize,
|
||||
cio_entry *rqe, uint8 *app_data);
|
||||
t_bool cio_cqueue_avail(uint cid, uint16 esize);
|
||||
uint16 cio_r_lp(uint8 cid, uint8 qnum, uint16 esize);
|
||||
uint16 cio_r_ulp(uint8 cid, uint8 qnum, uint16 esize);
|
||||
uint16 cio_c_lp(uint8 cid, uint16 esize);
|
||||
uint16 cio_c_ulp(uint8 cid, uint16 esize);
|
||||
void cio_sysgen(uint8 cid);
|
||||
/* Debugging only */
|
||||
void dump_entry(CONST char *type, cio_entry *entry);
|
||||
|
||||
void dump_entry(uint32 dbits, DEVICE *dev, CONST char *type,
|
||||
uint16 esize, cio_entry *entry, uint8 *app_data);
|
||||
|
||||
#endif
|
||||
|
|
100
3B2/3b2_iu.c
100
3B2/3b2_iu.c
|
@ -324,7 +324,7 @@ t_stat tti_reset(DEVICE *dptr)
|
|||
|
||||
/* Start the Console TTI polling loop */
|
||||
if (!sim_is_active(&tti_unit)) {
|
||||
sim_activate(&tti_unit, tti_unit.wait);
|
||||
sim_activate_after(&tti_unit, tti_unit.wait);
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
|
@ -347,6 +347,10 @@ t_stat contty_reset(DEVICE *dtpr)
|
|||
|
||||
memset(&iu_state, 0, sizeof(IU_STATE));
|
||||
memset(&iu_contty, 0, sizeof(IU_PORT));
|
||||
|
||||
/* DCD is off (inverted logic, 1 means off) */
|
||||
iu_state.inprt |= IU_DCDB;
|
||||
|
||||
brg_reg = 0;
|
||||
brg_clk = BRG_DEFAULT;
|
||||
parity_sel = IU_PARITY_EVEN;
|
||||
|
@ -361,7 +365,7 @@ t_stat contty_reset(DEVICE *dtpr)
|
|||
|
||||
/* Start the CONTTY polling loop */
|
||||
if (!sim_is_active(contty_rcv_unit)) {
|
||||
sim_activate(contty_rcv_unit, contty_rcv_unit->wait);
|
||||
sim_activate_after(contty_rcv_unit, contty_rcv_unit->wait);
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
|
@ -393,7 +397,7 @@ t_stat iu_svc_tti(UNIT *uptr)
|
|||
*/
|
||||
|
||||
if ((temp = sim_poll_kbd()) < SCPE_KFLAG) {
|
||||
return temp;
|
||||
return temp;
|
||||
}
|
||||
|
||||
if (iu_console.conf & RX_EN) {
|
||||
|
@ -438,22 +442,26 @@ t_stat iu_svc_contty_rcv(UNIT *uptr)
|
|||
return SCPE_OK;
|
||||
}
|
||||
|
||||
ln = tmxr_poll_conn(&contty_desc);
|
||||
if (ln >= 0) {
|
||||
/* Check for connect */
|
||||
if ((ln = tmxr_poll_conn(&contty_desc)) >= 0) {
|
||||
contty_ldsc[ln].rcve = 1;
|
||||
/* Set DCD */
|
||||
iu_state.inprt &= ~(IU_DCDB);
|
||||
iu_state.ipcr |= IU_DCDB;
|
||||
/* Cause an interrupt */
|
||||
csr_data |= CSRUART;
|
||||
}
|
||||
|
||||
tmxr_poll_rx(&contty_desc);
|
||||
/* Check for disconnect */
|
||||
if (!contty_ldsc[0].conn && (iu_state.inprt & IU_DCDB) == 0) {
|
||||
contty_ldsc[0].rcve = 0;
|
||||
iu_state.inprt |= IU_DCDB;
|
||||
iu_state.ipcr |= IU_DCDB;
|
||||
csr_data |= CSRUART;
|
||||
} else if (iu_contty.conf & RX_EN) {
|
||||
tmxr_poll_rx(&contty_desc);
|
||||
|
||||
if (contty_ldsc[0].conn) {
|
||||
temp = tmxr_getc_ln(&contty_ldsc[0]);
|
||||
if (temp && !(temp & SCPE_BREAK)) {
|
||||
if (iu_contty.conf & RX_EN) {
|
||||
if (contty_ldsc[0].conn) {
|
||||
temp = tmxr_getc_ln(&contty_ldsc[0]);
|
||||
if (temp && !(temp & SCPE_BREAK)) {
|
||||
if ((iu_contty.stat & STS_FFL) == 0) {
|
||||
iu_contty.rxbuf[iu_contty.w_p] = (temp & 0xff);
|
||||
iu_contty.w_p = (iu_contty.w_p + 1) % IU_BUF_SIZE;
|
||||
|
@ -530,7 +538,7 @@ t_stat iu_svc_timer(UNIT *uptr)
|
|||
uint32 iu_read(uint32 pa, size_t size)
|
||||
{
|
||||
uint8 reg, modep;
|
||||
uint32 data, delay;
|
||||
uint32 data;
|
||||
|
||||
reg = (uint8) (pa - IUBASE);
|
||||
|
||||
|
@ -544,15 +552,17 @@ uint32 iu_read(uint32 pa, size_t size)
|
|||
data = iu_console.stat;
|
||||
break;
|
||||
case RHRA:
|
||||
data = iu_console.rxbuf[iu_console.r_p];
|
||||
iu_console.r_p = (iu_console.r_p + 1) % IU_BUF_SIZE;
|
||||
/* If the FIFO is not empty, we must cause another interrupt
|
||||
* to continue reading */
|
||||
if (iu_console.r_p == iu_console.w_p) {
|
||||
iu_console.stat &= ~(STS_RXR|STS_FFL);
|
||||
iu_state.istat &= ~ISTS_RAI;
|
||||
} else {
|
||||
csr_data |= CSRUART;
|
||||
if (iu_console.conf & RX_EN) {
|
||||
data = iu_console.rxbuf[iu_console.r_p];
|
||||
iu_console.r_p = (iu_console.r_p + 1) % IU_BUF_SIZE;
|
||||
/* If the FIFO is not empty, we must cause another interrupt
|
||||
* to continue reading */
|
||||
if (iu_console.r_p == iu_console.w_p) {
|
||||
iu_console.stat &= ~(STS_RXR|STS_FFL);
|
||||
iu_state.istat &= ~ISTS_RAI;
|
||||
} else if (iu_state.imr & IMR_RXRA) {
|
||||
csr_data |= CSRUART;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCR:
|
||||
|
@ -579,15 +589,17 @@ uint32 iu_read(uint32 pa, size_t size)
|
|||
data = iu_contty.stat;
|
||||
break;
|
||||
case RHRB:
|
||||
data = iu_contty.rxbuf[iu_contty.r_p];
|
||||
iu_contty.r_p = (iu_contty.r_p + 1) % IU_BUF_SIZE;
|
||||
/* If the FIFO is not empty, we must cause another interrupt
|
||||
* to continue reading */
|
||||
if (iu_contty.r_p == iu_contty.w_p) {
|
||||
iu_contty.stat &= ~(STS_RXR|STS_FFL);
|
||||
iu_state.istat &= ~ISTS_RBI;
|
||||
} else {
|
||||
csr_data |= CSRUART;
|
||||
if (iu_contty.conf & RX_EN) {
|
||||
data = iu_contty.rxbuf[iu_contty.r_p];
|
||||
iu_contty.r_p = (iu_contty.r_p + 1) % IU_BUF_SIZE;
|
||||
/* If the FIFO is not empty, we must cause another interrupt
|
||||
* to continue reading */
|
||||
if (iu_contty.r_p == iu_contty.w_p) {
|
||||
iu_contty.stat &= ~(STS_RXR|STS_FFL);
|
||||
iu_state.istat &= ~ISTS_RBI;
|
||||
} else if (iu_state.imr & IMR_RXRB) {
|
||||
csr_data |= CSRUART;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case INPRT:
|
||||
|
@ -596,8 +608,7 @@ uint32 iu_read(uint32 pa, size_t size)
|
|||
case START_CTR:
|
||||
data = 0;
|
||||
iu_state.istat &= ~ISTS_CRI;
|
||||
delay = (uint32) (IU_TIMER_STP * iu_timer_state.c_set);
|
||||
sim_activate_abs(&iu_timer_unit, (int32) DELAY_US(delay));
|
||||
sim_activate_abs(&iu_timer_unit, iu_timer_state.c_set * IU_TIMER_RATE);
|
||||
break;
|
||||
case STOP_CTR:
|
||||
data = 0;
|
||||
|
@ -607,12 +618,6 @@ uint32 iu_read(uint32 pa, size_t size)
|
|||
break;
|
||||
case 17: /* Clear DMAC interrupt */
|
||||
data = 0;
|
||||
/*
|
||||
iu_console.drq = FALSE;
|
||||
iu_console.dma = FALSE;
|
||||
iu_contty.drq = FALSE;
|
||||
iu_contty.dma = FALSE;
|
||||
*/
|
||||
csr_data &= ~CSRDMA;
|
||||
break;
|
||||
default:
|
||||
|
@ -639,7 +644,18 @@ void iu_write(uint32 pa, uint32 val, size_t size)
|
|||
iu_increment_a = TRUE;
|
||||
break;
|
||||
case CSRA:
|
||||
/* Set baud rate - not yet implemented on console */
|
||||
if (brg_rates[brg_reg][brg_clk] != NULL) {
|
||||
sprintf(line_config, "%s-%d%s1",
|
||||
brg_rates[brg_reg][brg_clk],
|
||||
bits_per_char,
|
||||
parity[parity_sel]);
|
||||
|
||||
sim_debug(EXECUTE_MSG, &contty_dev,
|
||||
"Setting CONTTY line to %s\n",
|
||||
line_config);
|
||||
|
||||
tmxr_set_config_line(&contty_ldsc[0], line_config);
|
||||
}
|
||||
break;
|
||||
case CRA: /* Command A */
|
||||
iu_w_cmd(PORT_A, bval);
|
||||
|
@ -756,8 +772,8 @@ t_stat iu_tx(uint8 portno, uint8 val)
|
|||
}
|
||||
|
||||
p->stat |= STS_RXR;
|
||||
iu_state.istat |= ists;
|
||||
if (iu_state.imr & imr_mask) {
|
||||
iu_state.istat |= ists;
|
||||
csr_data |= CSRUART;
|
||||
}
|
||||
|
||||
|
@ -775,7 +791,7 @@ t_stat iu_tx(uint8 portno, uint8 val)
|
|||
sim_debug(EXECUTE_MSG, &tto_dev,
|
||||
"[iu_tx] CONSOLE transmit %02x (%c)\n",
|
||||
(uint8) c, (char) c);
|
||||
status = sim_putchar(c);
|
||||
status = sim_putchar_s(c);
|
||||
} else {
|
||||
sim_debug(EXECUTE_MSG, &contty_dev,
|
||||
"[iu_tx] CONTTY transmit %02x (%c)\n",
|
||||
|
|
21
3B2/3b2_iu.h
21
3B2/3b2_iu.h
|
@ -140,28 +140,9 @@ extern DEVICE tto_dev;
|
|||
extern DEVICE contty_dev;
|
||||
extern DEVICE iu_timer_dev;
|
||||
|
||||
extern int32 tmxr_poll;
|
||||
|
||||
#define IUBASE 0x49000
|
||||
#define IUSIZE 0x100
|
||||
|
||||
/* The UART is driven by a 3.6864 MHz crystal. This is divided by 16
|
||||
to clock the timer. (One peculiarity: 3.6864 MHz /16 is 230400 Hz,
|
||||
but the SVR3 source code claims the /16 clock is actually 230525
|
||||
Hz. So, we'll go with 230525 Hz until proven otherwise.)
|
||||
|
||||
UART clock period = 4338ns
|
||||
System clock period = 100ns
|
||||
|
||||
That means the system ticks 43.3792 times for every one tick of the
|
||||
UART clock.
|
||||
|
||||
But this is a simulated system, where each simulator step is
|
||||
CYCLES_PER_INST long. So we take that into account.
|
||||
*/
|
||||
|
||||
#define IU_TIMER_STP 4.33792
|
||||
|
||||
#define IU_BUF_SIZE 3
|
||||
|
||||
#define IU_DCDA 0x01
|
||||
|
@ -177,6 +158,8 @@ extern int32 tmxr_poll;
|
|||
/* Default baud rate generator (9600 baud) */
|
||||
#define BRG_DEFAULT 11
|
||||
|
||||
#define IU_TIMER_RATE 2.114 /* microseconds per step */
|
||||
|
||||
|
||||
typedef struct iu_port {
|
||||
uint8 stat; /* Port Status */
|
||||
|
|
|
@ -236,8 +236,8 @@ t_bool addr_is_mem(uint32 pa)
|
|||
|
||||
t_bool addr_is_io(uint32 pa)
|
||||
{
|
||||
return ((pa >= IO_BASE && pa < IO_BASE + IO_SIZE) ||
|
||||
(pa >= IOB_BASE && pa < IOB_BASE + IOB_SIZE));
|
||||
return ((pa >= IO_BOTTOM && pa < IO_TOP) ||
|
||||
(pa >= CIO_BOTTOM && pa < CIO_TOP));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -631,7 +631,7 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa)
|
|||
uint8 pd_acc;
|
||||
t_stat sd_cached, pd_cached;
|
||||
|
||||
if (!mmu_state.enabled || addr_is_io(va)) {
|
||||
if (!mmu_state.enabled) {
|
||||
*pa = va;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
|
964
3B2/3b2_ports.c
Normal file
964
3B2/3b2_ports.c
Normal file
|
@ -0,0 +1,964 @@
|
|||
/* 3b2_ports.c: AT&T 3B2 Model 400 "PORTS" feature card
|
||||
|
||||
Copyright (c) 2018, Seth J. Morabito
|
||||
|
||||
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 AUTHORS OR COPYRIGHT HOLDERS
|
||||
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 of the author 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PORTS is an intelligent feature card for the 3B2 that supports four
|
||||
* serial lines and one Centronics parallel port.
|
||||
*
|
||||
* The PORTS card is based on the Common I/O (CIO) platform. It uses
|
||||
* two SCN2681A DUARTs to supply the four serial lines, and uses the
|
||||
* SCN2681A parallel I/O pins for the Centronics parallel port.
|
||||
*
|
||||
* We make no attempt to emulate a PORTS card's internal workings
|
||||
* precisely. Instead, we treat it as a black box as seen from the 3B2
|
||||
* system board's point of view.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "3b2_ports.h"
|
||||
|
||||
extern CIO_STATE cio[CIO_SLOTS];
|
||||
extern UNIT cio_unit;
|
||||
|
||||
/* Device and units for PORTS cards
|
||||
* --------------------------------
|
||||
*
|
||||
* A 3B2/400 system can have up to 12 PORTS cards installed. Each
|
||||
* card, in turn, has 5 TTY devices - four serial TTYs and one
|
||||
* parallel printer port (the printer port is not supported at this
|
||||
* time, and is a no-op).
|
||||
*
|
||||
* The PORTS emulator is backed by a terminal multiplexer with up to
|
||||
* 48 (12 * 4) serial lines. Lines can be specified with:
|
||||
*
|
||||
* SET PORTS LINES=n
|
||||
*
|
||||
* Lines must be specified in multiples of 4.
|
||||
*
|
||||
* Implementation8
|
||||
* --------------
|
||||
*
|
||||
* Each set of 4 lines is mapped to a CIO_STATE struct in the "cio"
|
||||
* CIO_STATE structure.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#define PPQESIZE 12
|
||||
#define DELAY_ASYNC 25
|
||||
#define DELAY_DLM 100
|
||||
#define DELAY_ULM 100
|
||||
#define DELAY_FCF 100
|
||||
#define DELAY_DOS 100
|
||||
#define DELAY_DSD 100
|
||||
#define DELAY_OPTIONS 100
|
||||
#define DELAY_VERS 100
|
||||
#define DELAY_CONN 100
|
||||
#define DELAY_XMIT 50
|
||||
#define DELAY_RECV 25
|
||||
#define DELAY_DEVICE 25
|
||||
#define DELAY_STD 100
|
||||
|
||||
#define LN(cid,port) ((PORTS_LINES * ((cid) - base_cid)) + (port))
|
||||
#define LCID(ln) (((ln) / PORTS_LINES) + base_cid)
|
||||
#define LPORT(ln) ((ln) % PORTS_LINES)
|
||||
|
||||
t_bool ports_conf = FALSE; /* Have PORTS cards been configured? */
|
||||
int8 base_cid; /* First cid in our contiguous block */
|
||||
uint8 int_cid; /* Interrupting card ID */
|
||||
uint8 int_subdev; /* Interrupting subdevice */
|
||||
|
||||
/* PORTS-specific state for each slot */
|
||||
PORTS_LINE_STATE *ports_state = NULL;
|
||||
|
||||
/* Baud rates determined by the low nybble
|
||||
* of the PORT_OPTIONS cflag */
|
||||
CONST char *ports_baud[16] = {
|
||||
"75", "110", "134", "150",
|
||||
"300", "600", "1200", "2000",
|
||||
"2400", "4800", "1800", "9600",
|
||||
"19200", "9600", "9600", "9600"
|
||||
};
|
||||
|
||||
TMLN *ports_ldsc = NULL;
|
||||
TMXR ports_desc = { 0, 0, 0, NULL };
|
||||
|
||||
/* Three units service the Receive, Transmit, and CIO */
|
||||
UNIT ports_unit[3] = {
|
||||
{ UDATA(&ports_rcv_svc, UNIT_IDLE|UNIT_ATTABLE|TT_MODE_8B, 0) },
|
||||
{ UDATA(&ports_xmt_svc, UNIT_DIS, 0), SERIAL_OUT_WAIT },
|
||||
{ UDATA(&ports_cio_svc, UNIT_DIS, 0) }
|
||||
};
|
||||
|
||||
MTAB ports_mod[] = {
|
||||
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 bit mode" },
|
||||
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL, NULL, NULL, "8 bit mode" },
|
||||
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL, "7 bit mode - non printing suppressed" },
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL,
|
||||
NULL, &ports_show_rqueue, NULL, "Display Request Queue for card n" },
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL,
|
||||
NULL, &ports_show_cqueue, NULL, "Display Completion Queue for card n" },
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n",
|
||||
&ports_setnl, &tmxr_show_lines, (void *) &ports_desc, "Display number of lines" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static DEBTAB ports_debug[] = {
|
||||
{ "IO", IO_DBG, "I/O Character Trace" },
|
||||
{ "TRACE", TRACE_DBG, "Call Trace" },
|
||||
{ "XMT", TMXR_DBG_XMT, "TMXR Transmit Data" },
|
||||
{ "RCV", TMXR_DBG_RCV, "TMXR Received Data" },
|
||||
{ "RET", TMXR_DBG_RET, "TMXR Returned Received Data" },
|
||||
{ "MDM", TMXR_DBG_XMT, "TMXR Modem Signals" },
|
||||
{ "CON", TMXR_DBG_XMT, "TMXR Connection Activity" },
|
||||
{ "ASY", TMXR_DBG_ASY, "TMXR Async Activity" },
|
||||
{ "PXMT", TMXR_DBG_PXMT, "TMXR Transmit Packets" },
|
||||
{ "PRCV", TMXR_DBG_PRCV, "TMXR Received Packets" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
DEVICE ports_dev = {
|
||||
"PORTS", /* name */
|
||||
ports_unit, /* units */
|
||||
NULL, /* registers */
|
||||
ports_mod, /* modifiers */
|
||||
3, /* #units */
|
||||
16, /* address radix */
|
||||
32, /* address width */
|
||||
1, /* address incr. */
|
||||
16, /* data radix */
|
||||
8, /* data width */
|
||||
NULL, /* examine routine */
|
||||
NULL, /* deposit routine */
|
||||
&ports_reset, /* reset routine */
|
||||
NULL, /* boot routine */
|
||||
&ports_attach, /* attach routine */
|
||||
&ports_detach, /* detach routine */
|
||||
NULL, /* context */
|
||||
DEV_DISABLE|DEV_DIS|DEV_DEBUG|DEV_MUX, /* flags */
|
||||
0, /* debug control flags */
|
||||
ports_debug, /* debug flag names */
|
||||
NULL, /* memory size change */
|
||||
NULL, /* logical name */
|
||||
NULL, /* help routine */
|
||||
NULL, /* attach help routine */
|
||||
(void *)&ports_desc, /* help context */
|
||||
NULL, /* device description */
|
||||
};
|
||||
|
||||
|
||||
static void cio_irq(uint8 cid, uint8 dev, int32 delay)
|
||||
{
|
||||
int_cid = cid;
|
||||
int_subdev = dev & 0xf;
|
||||
sim_activate(&ports_unit[2], delay);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the number of lines for the PORTS mux. This will add or remove
|
||||
* cards as necessary. The number of lines must be a multiple of 4.
|
||||
*/
|
||||
t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
||||
{
|
||||
int32 newln, newcards, i, t;
|
||||
t_stat r = SCPE_OK;
|
||||
|
||||
if (cptr == NULL) {
|
||||
return SCPE_ARG;
|
||||
}
|
||||
|
||||
newln = (int32) get_uint(cptr, 10, (MAX_PORTS_CARDS * PORTS_LINES), &r);
|
||||
|
||||
if ((r != SCPE_OK) || (newln == ports_desc.lines)) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((newln == 0) || LPORT(newln) != 0) {
|
||||
return SCPE_ARG;
|
||||
}
|
||||
|
||||
if (newln < ports_desc.lines) {
|
||||
for (i = newln, t = 0; i < ports_desc.lines; i++) {
|
||||
t = t | ports_ldsc[i].conn;
|
||||
}
|
||||
|
||||
if (t && !get_yn("This will disconnect users; proceed [N]?", FALSE)) {
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
for (i = newln; i < ports_desc.lines; i++) {
|
||||
if (ports_ldsc[i].conn) {
|
||||
tmxr_linemsg(&ports_ldsc[i], "\r\nOperator disconnected line\r\n");
|
||||
tmxr_send_buffered_data(&ports_ldsc[i]);
|
||||
}
|
||||
/* completely reset line */
|
||||
tmxr_detach_ln(&ports_ldsc[i]);
|
||||
if (LPORT(i) == (PORTS_LINES - 1)) {
|
||||
/* Also drop the corresponding card from the CIO array */
|
||||
cio_clear(LCID(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ports_desc.ldsc = ports_ldsc = (TMLN *)realloc(ports_ldsc, newln*sizeof(*ports_ldsc));
|
||||
ports_state = (PORTS_LINE_STATE *)realloc(ports_state, newln*sizeof(*ports_state));
|
||||
|
||||
if (ports_desc.lines < newln) {
|
||||
memset(ports_ldsc + ports_desc.lines, 0, sizeof(*ports_ldsc)*(newln-ports_desc.lines));
|
||||
memset(ports_state + ports_desc.lines, 0, sizeof(*ports_state)*(newln-ports_desc.lines));
|
||||
}
|
||||
|
||||
ports_desc.lines = newln;
|
||||
|
||||
/* setup lines and auto config */
|
||||
ports_conf = FALSE;
|
||||
return ports_reset(&ports_dev);
|
||||
}
|
||||
|
||||
|
||||
static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data)
|
||||
{
|
||||
cio_entry centry = {0};
|
||||
uint32 i, ln;
|
||||
char c;
|
||||
PORTS_OPTIONS opts;
|
||||
char line_config[16];
|
||||
uint8 app_data[4] = {0};
|
||||
|
||||
centry.address = rentry->address;
|
||||
cio[cid].op = rentry->opcode;
|
||||
ln = LN(cid, rentry->subdevice & 0xf);
|
||||
|
||||
switch(rentry->opcode) {
|
||||
case CIO_DLM:
|
||||
centry.address = rentry->address + rentry->byte_count;
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] CIO Download Memory: bytecnt=%04x "
|
||||
"addr=%08x return_addr=%08x subdev=%02x\n",
|
||||
R[NUM_PC],
|
||||
rentry->byte_count, rentry->address,
|
||||
centry.address, centry.subdevice);
|
||||
cio_cexpress(cid, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_DLM);
|
||||
break;
|
||||
case CIO_ULM:
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] CIO Upload Memory\n",
|
||||
R[NUM_PC]);
|
||||
cio_cexpress(cid, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_ULM);
|
||||
break;
|
||||
case CIO_FCF:
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] CIO Force Function Call\n",
|
||||
R[NUM_PC]);
|
||||
|
||||
/* This is to pass diagnostics. TODO: Figure out how to parse the
|
||||
* given test x86 code and determine how to respond correctly */
|
||||
|
||||
pwrite_h(0x200f000, 0x1); /* Test success */
|
||||
pwrite_h(0x200f002, 0x0); /* Test Number */
|
||||
pwrite_h(0x200f004, 0x0); /* Actual */
|
||||
pwrite_h(0x200f006, 0x0); /* Expected */
|
||||
pwrite_b(0x200f008, 0x1); /* Success flag again */
|
||||
pwrite_b(0x200f009, 0x30); /* ??? */
|
||||
|
||||
/* An interesting (?) side-effect of FORCE FUNCTION CALL is
|
||||
* that it resets the card state such that a new SYSGEN is
|
||||
* required in order for new commands to work. In fact, an
|
||||
* INT0/INT1 combo _without_ a RESET can sysgen the board. So,
|
||||
* we reset the command bits here. */
|
||||
|
||||
cio[cid].sysgen_s = 0;
|
||||
cio_cexpress(cid, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_FCF);
|
||||
break;
|
||||
case CIO_DOS:
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] CIO Determine Op Status\n",
|
||||
R[NUM_PC]);
|
||||
cio_cexpress(cid, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_DOS);
|
||||
break;
|
||||
case CIO_DSD:
|
||||
/* Determine Sub-Devices. We have none. */
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] Determine Sub-Devices.\n",
|
||||
R[NUM_PC]);
|
||||
|
||||
/* The system wants us to write sub-device structures
|
||||
* at the supplied address */
|
||||
|
||||
pwrite_h(rentry->address, 0x0);
|
||||
cio_cexpress(cid, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_DSD);
|
||||
break;
|
||||
case PPC_OPTIONS:
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] PPC Options Operation\n",
|
||||
R[NUM_PC]);
|
||||
|
||||
opts.line = pread_h(rentry->address);
|
||||
opts.iflag = pread_h(rentry->address + 4);
|
||||
opts.oflag = pread_h(rentry->address + 6);
|
||||
opts.cflag = pread_h(rentry->address + 8);
|
||||
opts.lflag = pread_h(rentry->address + 10);
|
||||
opts.cerase = pread_b(rentry->address + 11);
|
||||
opts.ckill = pread_b(rentry->address + 12);
|
||||
opts.cinter = pread_b(rentry->address + 13);
|
||||
opts.cquit = pread_b(rentry->address + 14);
|
||||
opts.ceof = pread_b(rentry->address + 15);
|
||||
opts.ceol = pread_b(rentry->address + 16);
|
||||
opts.itime = pread_b(rentry->address + 17);
|
||||
opts.vtime = pread_b(rentry->address + 18);
|
||||
opts.vcount = pread_b(rentry->address + 19);
|
||||
|
||||
sim_debug(TRACE_DBG, &ports_dev, " PPC Options: iflag=%04x\n", opts.iflag);
|
||||
sim_debug(TRACE_DBG, &ports_dev, " PPC Options: oflag=%04x\n", opts.oflag);
|
||||
sim_debug(TRACE_DBG, &ports_dev, " PPC Options: cflag=%04x\n", opts.cflag);
|
||||
sim_debug(TRACE_DBG, &ports_dev, " PPC Options: lflag=%04x\n", opts.lflag);
|
||||
sim_debug(TRACE_DBG, &ports_dev, " PPC Options: itime=%02x\n", opts.itime);
|
||||
sim_debug(TRACE_DBG, &ports_dev, " PPC Options: vtime=%02x\n", opts.vtime);
|
||||
sim_debug(TRACE_DBG, &ports_dev, " PPC Options: vcount=%02x\n", opts.vcount);
|
||||
|
||||
ports_state[ln].iflag = opts.iflag;
|
||||
ports_state[ln].oflag = opts.oflag;
|
||||
|
||||
if ((rentry->subdevice & 0xf) < PORTS_LINES) {
|
||||
/* Adjust baud rate */
|
||||
sprintf(line_config, "%s-8N1",
|
||||
ports_baud[opts.cflag&0xf]);
|
||||
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"Setting PORTS line %d to %s\n",
|
||||
ln, line_config);
|
||||
|
||||
tmxr_set_config_line(&ports_ldsc[ln], line_config);
|
||||
}
|
||||
|
||||
centry.byte_count = sizeof(PPC_OPTIONS);
|
||||
centry.opcode = PPC_OPTIONS;
|
||||
centry.subdevice = rentry->subdevice;
|
||||
centry.address = rentry->address;
|
||||
cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_OPTIONS);
|
||||
break;
|
||||
case PPC_VERS:
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] PPC Version\n",
|
||||
R[NUM_PC]);
|
||||
|
||||
/* Write the version number at the supplied address */
|
||||
pwrite_b(rentry->address, PORTS_VERSION);
|
||||
|
||||
centry.opcode = CIO_ULM;
|
||||
|
||||
/* TODO: It's unknown what the value 0x50 means, but this
|
||||
* is what a real board sends. */
|
||||
app_data[0] = 0x50;
|
||||
cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_VERS);
|
||||
break;
|
||||
case PPC_CONN:
|
||||
/* CONNECT - Full request and completion queues */
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] PPC CONNECT - subdevice = %02x\n",
|
||||
R[NUM_PC], rentry->subdevice);
|
||||
|
||||
ports_state[ln].conn = TRUE;
|
||||
|
||||
centry.opcode = PPC_CONN;
|
||||
centry.subdevice = rentry->subdevice;
|
||||
centry.address = rentry->address;
|
||||
cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_CONN);
|
||||
break;
|
||||
case PPC_XMIT:
|
||||
/* XMIT - Full request and completion queues */
|
||||
|
||||
/* The port being referred to is in the subdevice. */
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] PPC XMIT - subdevice = %02x, address=%08x, byte_count=%d\n",
|
||||
R[NUM_PC], rentry->subdevice, rentry->address, rentry->byte_count);
|
||||
|
||||
/* Set state for xmit */
|
||||
ports_state[ln].tx_addr = rentry->address;
|
||||
ports_state[ln].tx_req_addr = rentry->address;
|
||||
ports_state[ln].tx_chars = rentry->byte_count + 1;
|
||||
ports_state[ln].tx_req_chars = rentry->byte_count + 1;
|
||||
|
||||
sim_activate_after(&ports_unit[1], ports_unit[1].wait);
|
||||
|
||||
break;
|
||||
case PPC_DEVICE:
|
||||
/* DEVICE Control - Express request and completion queues */
|
||||
/* The port being referred to is in the subdevice. */
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] PPC DEVICE - subdevice = %02x\n",
|
||||
R[NUM_PC], rentry->subdevice);
|
||||
centry.subdevice = rentry->subdevice;
|
||||
centry.opcode = PPC_DEVICE;
|
||||
cio_cexpress(cid, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_DEVICE);
|
||||
break;
|
||||
case PPC_RECV:
|
||||
/* RECV - Full request and completion queues */
|
||||
|
||||
/* The port being referred to is in the subdevice. */
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_cmd] PPC RECV - subdevice = %02x addr=%08x\n",
|
||||
R[NUM_PC], rentry->subdevice, rentry->address);
|
||||
|
||||
break;
|
||||
case PPC_DISC:
|
||||
/* Disconnect */
|
||||
centry.subdevice = rentry->subdevice;
|
||||
centry.opcode = PPC_DISC;
|
||||
ports_ldsc[ln].rcve = 0;
|
||||
cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_STD);
|
||||
break;
|
||||
case PPC_BRK:
|
||||
case PPC_CLR:
|
||||
default:
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
">>> Op %d Not Handled Yet\n",
|
||||
rentry->opcode);
|
||||
|
||||
cio_cexpress(cid, PPQESIZE, ¢ry, app_data);
|
||||
cio_irq(cid, rentry->subdevice, DELAY_STD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the connection status of the given port.
|
||||
*/
|
||||
static void ports_update_conn(uint32 ln)
|
||||
{
|
||||
cio_entry centry;
|
||||
uint8 cid;
|
||||
uint8 app_data[4] = {0};
|
||||
|
||||
cid = LCID(ln);
|
||||
|
||||
/* If the card hasn't sysgened, there's no way to write a
|
||||
* completion queue entry */
|
||||
if (cio[cid].sysgen_s != CIO_SYSGEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ports_ldsc[ln].conn) {
|
||||
app_data[0] = AC_CON;
|
||||
ports_state[ln].conn = TRUE;
|
||||
} else {
|
||||
if (ports_state[ln].conn) {
|
||||
app_data[0] = AC_DIS;
|
||||
ports_state[ln].conn = FALSE;
|
||||
} else {
|
||||
app_data[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
centry.opcode = PPC_ASYNC;
|
||||
centry.subdevice = LPORT(ln);
|
||||
cio_cqueue(cid, CIO_CMD, PPQESIZE, ¢ry, app_data);
|
||||
|
||||
/* Interrupt */
|
||||
if (cio[cid].ivec > 0) {
|
||||
cio[cid].intr = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void ports_sysgen(uint8 cid)
|
||||
{
|
||||
cio_entry cqe;
|
||||
uint8 app_data[4] = {0};
|
||||
|
||||
cqe.opcode = 3; /* Sysgen success! */
|
||||
|
||||
/* It's not clear why we put a response in both the express
|
||||
* and the full queue. */
|
||||
cio_cexpress(cid, PPQESIZE, &cqe, app_data);
|
||||
cio_cqueue(cid, CIO_STAT, PPQESIZE, &cqe, app_data);
|
||||
|
||||
int_cid = cid;
|
||||
sim_activate(&ports_unit[2], DELAY_STD);
|
||||
}
|
||||
|
||||
void ports_express(uint8 cid)
|
||||
{
|
||||
cio_entry rqe;
|
||||
uint8 app_data[4] = {0};
|
||||
cio_rexpress(cid, PPQESIZE, &rqe, app_data);
|
||||
ports_cmd(cid, &rqe, app_data);
|
||||
}
|
||||
|
||||
void ports_full(uint8 cid)
|
||||
{
|
||||
uint32 i, ln;
|
||||
cio_entry rqe;
|
||||
uint8 app_data[4] = {0};
|
||||
|
||||
for (i = 0; i < PORTS_LINES; i++) {
|
||||
if (cio_rqueue(cid, i, PPQESIZE, &rqe, app_data) == SCPE_OK) {
|
||||
ports_cmd(cid, &rqe, app_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t_stat ports_reset(DEVICE *dptr)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 cid, line, cards, ln, end_slot;
|
||||
TMLN *lp;
|
||||
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[ports_reset] Resetting PORTS device\n");
|
||||
|
||||
if ((dptr->flags & DEV_DIS)) {
|
||||
for (cid = 0; cid < CIO_SLOTS; cid++) {
|
||||
if (cio[cid].id == PORTS_ID) {
|
||||
cio[cid].id = 0;
|
||||
cio[cid].ipl = 0;
|
||||
cio[cid].ivec = 0;
|
||||
cio[cid].exp_handler = NULL;
|
||||
cio[cid].full_handler = NULL;
|
||||
cio[cid].sysgen = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ports_conf = FALSE;
|
||||
} else if (!ports_conf) {
|
||||
|
||||
/* Clear out any old cards, we're starting fresh */
|
||||
for (cid = 0; cid < CIO_SLOTS; cid++) {
|
||||
if (cio[cid].id == PORTS_ID) {
|
||||
cio[cid].id = 0;
|
||||
cio[cid].ipl = 0;
|
||||
cio[cid].ivec = 0;
|
||||
cio[cid].exp_handler = NULL;
|
||||
cio[cid].full_handler = NULL;
|
||||
cio[cid].sysgen = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the first avaialable slot */
|
||||
for (cid = 0; cid < CIO_SLOTS; cid++) {
|
||||
if (cio[cid].id == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do we have room? */
|
||||
if (cid >= CIO_SLOTS || cid > (CIO_SLOTS - (ports_desc.lines/PORTS_LINES))) {
|
||||
return SCPE_NXM;
|
||||
}
|
||||
|
||||
/* Remember the base card slot */
|
||||
base_cid = cid;
|
||||
|
||||
end_slot = (cid + (ports_desc.lines/PORTS_LINES));
|
||||
|
||||
for (; cid < end_slot; cid++) {
|
||||
/* Set up the ports structure */
|
||||
cio[cid].id = PORTS_ID;
|
||||
cio[cid].ipl = PORTS_IPL;
|
||||
cio[cid].exp_handler = &ports_express;
|
||||
cio[cid].full_handler = &ports_full;
|
||||
cio[cid].sysgen = &ports_sysgen;
|
||||
|
||||
for (line = 0; line < PORTS_LINES; line++) {
|
||||
ln = LN(cid, line);
|
||||
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
">>> Setting up lp %d (card %d, line %d)\n",
|
||||
ln, cid, line);
|
||||
|
||||
lp = &ports_ldsc[ln];
|
||||
tmxr_set_get_modem_bits(lp, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ports_conf = TRUE;
|
||||
|
||||
if (ports_ldsc == NULL) {
|
||||
ports_desc.lines = DEF_PORTS_CARDS * PORTS_LINES;
|
||||
ports_desc.ldsc = ports_ldsc =
|
||||
(TMLN *)calloc(ports_desc.lines, sizeof(*ports_ldsc));
|
||||
}
|
||||
|
||||
if (ports_state == NULL) {
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[ports_reset] calloc for ports_state...\n");
|
||||
ports_state = (PORTS_LINE_STATE *)calloc(ports_desc.lines, sizeof(*ports_state));
|
||||
}
|
||||
|
||||
memset(ports_state, 0, ports_desc.lines*sizeof(*ports_state));
|
||||
|
||||
tmxr_set_port_speed_control(&ports_desc);
|
||||
|
||||
for (i = 0; i < ports_desc.lines; i++) {
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[ports_reset] Setting up line %d...\n", i);
|
||||
tmxr_set_line_unit(&ports_desc, i, &ports_unit[0]);
|
||||
tmxr_set_line_output_unit(&ports_desc, i, &ports_unit[1]);
|
||||
if (!ports_ldsc[i].conn) {
|
||||
ports_ldsc[i].xmte = 1;
|
||||
}
|
||||
ports_ldsc[i].rcve = 0;
|
||||
tmxr_set_config_line(&ports_ldsc[i], "9600-8N1");
|
||||
}
|
||||
}
|
||||
|
||||
if (!sim_is_active(&ports_unit[0])) {
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[ports_reset] starting receive polling...\n");
|
||||
sim_activate(&ports_unit[0], ports_unit[0].wait);
|
||||
}
|
||||
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[ports_reset] returning scpe_ok\n");
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat ports_cio_svc(UNIT *uptr)
|
||||
{
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[ports_cio_svc] IRQ for board %d device %d\n",
|
||||
int_cid, int_subdev);
|
||||
|
||||
if (cio[int_cid].ivec > 0) {
|
||||
cio[int_cid].intr = TRUE;
|
||||
}
|
||||
|
||||
switch (cio[int_cid].op) {
|
||||
case PPC_CONN:
|
||||
cio[int_cid].op = PPC_ASYNC;
|
||||
ports_ldsc[LN(int_cid, int_subdev)].rcve = 1;
|
||||
sim_activate(&ports_unit[2], DELAY_ASYNC);
|
||||
break;
|
||||
case PPC_ASYNC:
|
||||
ports_update_conn(LN(int_cid, int_subdev));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat ports_rcv_svc(UNIT *uptr)
|
||||
{
|
||||
uint8 cid, subdev;
|
||||
int32 temp, ln;
|
||||
char c;
|
||||
cio_entry rentry = {0};
|
||||
cio_entry centry = {0};
|
||||
uint8 rapp_data[4] = {0};
|
||||
uint8 capp_data[4] = {0};
|
||||
|
||||
if ((uptr->flags & UNIT_ATT) == 0) {
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
ln = tmxr_poll_conn(&ports_desc);
|
||||
if (ln >= 0) {
|
||||
ports_update_conn(ln);
|
||||
}
|
||||
|
||||
tmxr_poll_rx(&ports_desc);
|
||||
|
||||
for (ln = 0; ln < ports_desc.lines; ln++) {
|
||||
cid = LCID(ln);
|
||||
subdev = LPORT(ln);
|
||||
|
||||
if (!ports_ldsc[ln].conn && ports_state[ln].conn) {
|
||||
ports_update_conn(ln);
|
||||
} else if (ports_ldsc[ln].conn && ports_state[ln].conn) {
|
||||
temp = tmxr_getc_ln(&ports_ldsc[ln]);
|
||||
|
||||
if (temp && !(temp & SCPE_BREAK)) {
|
||||
|
||||
c = (char) (temp & 0xff);
|
||||
|
||||
sim_debug(IO_DBG, &ports_dev,
|
||||
"[LINE %d RECEIVE] char = %02x (%c)\n",
|
||||
ln, c, c);
|
||||
|
||||
if (c == 0xd && (ports_state[ln].iflag & ICRNL)) {
|
||||
c = 0xa;
|
||||
}
|
||||
|
||||
if (cio[cid].ivec > 0 &&
|
||||
cio_rqueue(cid, PORTS_RCV_QUEUE,
|
||||
PPQESIZE, &rentry, rapp_data) == SCPE_OK) {
|
||||
cio[cid].intr = TRUE;
|
||||
|
||||
/* Write the character to the memory address */
|
||||
pwrite_b(rentry.address, c);
|
||||
centry.subdevice = LPORT(ln);
|
||||
centry.opcode = PPC_RECV;
|
||||
centry.address = rentry.address;
|
||||
capp_data[3] = RC_TMR;
|
||||
|
||||
cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, capp_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmxr_clock_coschedule(uptr, tmxr_poll);
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat ports_xmt_svc(UNIT *uptr)
|
||||
{
|
||||
uint8 cid, ln;
|
||||
char c;
|
||||
t_bool tx = FALSE; /* Did a tx ever occur? */
|
||||
cio_entry centry;
|
||||
uint8 app_data[4] = {0};
|
||||
uint32 wait = 0x7fffffff;
|
||||
|
||||
/* Scan all lines for output */
|
||||
for (ln = 0; ln < ports_desc.lines; ln++) {
|
||||
cid = LCID(ln);
|
||||
if (ports_ldsc[ln].conn && ports_state[ln].tx_chars > 0) {
|
||||
tx = TRUE; /* Even an attempt at TX counts for rescheduling */
|
||||
c = sim_tt_outcvt(pread_b(ports_state[ln].tx_addr),
|
||||
TT_GET_MODE(ports_unit[0].flags));
|
||||
|
||||
/* The PORTS card optionally handles NL->CRLF */
|
||||
if (c == 0xa &&
|
||||
(ports_state[ln].oflag & ONLCR) &&
|
||||
!(ports_state[ln].crlf)) {
|
||||
if (tmxr_putc_ln(&ports_ldsc[ln], 0xd) == SCPE_OK) {
|
||||
wait = MIN(wait, ports_ldsc[ln].txdelta);
|
||||
sim_debug(IO_DBG, &ports_dev,
|
||||
"[%08x] [ports_xmt_svc] [LINE %d] XMIT (crlf): %02x (%c)\n",
|
||||
R[NUM_PC], ln, 0xd, 0xd);
|
||||
/* Indicate that we're in a CRLF translation */
|
||||
ports_state[ln].crlf = TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ports_state[ln].crlf = FALSE;
|
||||
|
||||
if (tmxr_putc_ln(&ports_ldsc[ln], c) == SCPE_OK) {
|
||||
wait = MIN(wait, ports_ldsc[ln].txdelta);
|
||||
ports_state[ln].tx_chars--;
|
||||
ports_state[ln].tx_addr++;
|
||||
sim_debug(IO_DBG, &ports_dev,
|
||||
"[%08x] [ports_xmt_svc] [LINE %d] XMIT: %02x (%c)\n",
|
||||
R[NUM_PC], ln, c, c);
|
||||
}
|
||||
|
||||
if (ports_state[ln].tx_chars == 0) {
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[%08x] [ports_xmt_svc] Done with xmit, card=%d port=%d. Interrupting.\n",
|
||||
R[NUM_PC], cid, LPORT(ln));
|
||||
centry.byte_count = ports_state[ln].tx_req_chars;
|
||||
centry.subdevice = LPORT(ln);
|
||||
centry.opcode = PPC_XMIT;
|
||||
centry.address = ports_state[ln].tx_req_addr;
|
||||
app_data[0] = RC_FLU;
|
||||
cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data);
|
||||
cio[cid].intr = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tmxr_poll_tx(&ports_desc);
|
||||
|
||||
if (tx) {
|
||||
tmxr_activate_after(uptr, wait);
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat ports_attach(UNIT *uptr, CONST char *cptr)
|
||||
{
|
||||
t_stat r;
|
||||
|
||||
sim_debug(TRACE_DBG, &ports_dev, "ports_attach()\n");
|
||||
|
||||
tmxr_set_modem_control_passthru(&ports_desc);
|
||||
|
||||
r = tmxr_attach(&ports_desc, uptr, cptr);
|
||||
if (r != SCPE_OK) {
|
||||
tmxr_clear_modem_control_passthru(&ports_desc);
|
||||
return r;
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat ports_detach(UNIT *uptr)
|
||||
{
|
||||
t_stat r;
|
||||
|
||||
r = tmxr_detach(&ports_desc, uptr);
|
||||
|
||||
if (r != SCPE_OK) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (sim_is_active(&ports_unit[0])) {
|
||||
sim_debug(TRACE_DBG, &ports_dev,
|
||||
"[ports_detach] Stopping receive polling...\n");
|
||||
sim_cancel(&ports_unit[0]);
|
||||
}
|
||||
|
||||
tmxr_clear_modem_control_passthru(&ports_desc);
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Useful routines for debugging request and completion queues
|
||||
*/
|
||||
|
||||
t_stat ports_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
||||
{
|
||||
return ports_show_queue_common(st, uptr, val, desc, TRUE);
|
||||
}
|
||||
|
||||
t_stat ports_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
||||
{
|
||||
return ports_show_queue_common(st, uptr, val, desc, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val,
|
||||
CONST void *desc, t_bool rq)
|
||||
{
|
||||
uint8 cid;
|
||||
char *cptr = (char *) desc;
|
||||
t_stat result;
|
||||
uint32 ptr, size, no_rque, i, j;
|
||||
uint8 op, dev, seq, cmdstat;
|
||||
|
||||
if (cptr) {
|
||||
cid = (uint8) get_uint(cptr, 10, 12, &result);
|
||||
if (result != SCPE_OK) {
|
||||
return SCPE_ARG;
|
||||
}
|
||||
} else {
|
||||
return SCPE_ARG;
|
||||
}
|
||||
|
||||
/* If the card is not sysgen'ed, give up */
|
||||
if (cio[cid].sysgen_s != CIO_SYSGEN) {
|
||||
fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid);
|
||||
return SCPE_ARG;
|
||||
}
|
||||
|
||||
/* Get the top of the queue */
|
||||
if (rq) {
|
||||
ptr = cio[cid].rqp;
|
||||
size = cio[cid].rqs;
|
||||
no_rque = cio[cid].no_rque;
|
||||
} else {
|
||||
ptr = cio[cid].cqp;
|
||||
size = cio[cid].cqs;
|
||||
no_rque = 0; /* Not used */
|
||||
}
|
||||
|
||||
if (rq) {
|
||||
fprintf(st, "Dumping %d Request Queues\n", no_rque);
|
||||
} else {
|
||||
fprintf(st, "Dumping Completion Queue\n");
|
||||
}
|
||||
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
fprintf(st, "EXPRESS ENTRY:\n");
|
||||
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
|
||||
fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2));
|
||||
fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3));
|
||||
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
|
||||
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
|
||||
ptr += 12;
|
||||
|
||||
if (rq) {
|
||||
for (i = 0; i < no_rque; i++) {
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
fprintf(st, "REQUEST QUEUE %d\n", i);
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / 12);
|
||||
fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / 12);
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
ptr += 4;
|
||||
for (j = 0; j < size; j++) {
|
||||
dev = pread_b(ptr + 2);
|
||||
op = pread_b(ptr + 3);
|
||||
seq = (dev & 0x40) >> 6;
|
||||
cmdstat = (dev & 0x80) >> 7;
|
||||
fprintf(st, "REQUEST ENTRY %d\n", j);
|
||||
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
|
||||
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
|
||||
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
|
||||
fprintf(st, " Seqbit: %d\n", seq);
|
||||
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
|
||||
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
|
||||
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
|
||||
ptr += 12;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / 12);
|
||||
fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / 12);
|
||||
fprintf(st, "---------------------------------------------------------\n");
|
||||
ptr += 4;
|
||||
for (i = 0; i < size; i++) {
|
||||
dev = pread_b(ptr + 2);
|
||||
op = pread_b(ptr + 3);
|
||||
seq = (dev & 0x40) >> 6;
|
||||
cmdstat = (dev & 0x80) >> 7;
|
||||
fprintf(st, "COMPLETION ENTRY %d\n", i);
|
||||
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
|
||||
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
|
||||
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
|
||||
fprintf(st, " Seqbit: %d\n", seq);
|
||||
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
|
||||
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
|
||||
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
|
||||
ptr += 12;
|
||||
}
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
237
3B2/3b2_ports.h
Normal file
237
3B2/3b2_ports.h
Normal file
|
@ -0,0 +1,237 @@
|
|||
/* 3b2_ports.h: AT&T 3B2 Model 400 "PORTS" feature card
|
||||
|
||||
Copyright (c) 2018, Seth J. Morabito
|
||||
|
||||
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 AUTHORS OR COPYRIGHT HOLDERS
|
||||
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 of the author 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PORTS is an intelligent feature card for the 3B2 that supports four
|
||||
* serial lines and one Centronics parallel port.
|
||||
*
|
||||
* The PORTS card is based on the Common I/O (CIO) platform. It uses
|
||||
* two SCN2681A DUARTs to supply the four serial lines, and uses the
|
||||
* SCN2681A parallel I/O pins for the Centronics parallel port.
|
||||
*
|
||||
* This file implements the required logic for the PORTS CIO
|
||||
* interface. The SCN2681A functionality is implemented in the file
|
||||
* 3b2_duart.c, and is used by both this feature card and the System
|
||||
* Board console/contty functionality.
|
||||
*/
|
||||
|
||||
#ifndef _3B2_PORTS_H_
|
||||
#define _3B2_PORTS_H_
|
||||
|
||||
#include "3b2_defs.h"
|
||||
#include "3b2_io.h"
|
||||
|
||||
#define PORTS_ID 0x0003
|
||||
#define PORTS_IPL 10
|
||||
#define PORTS_VERSION 1
|
||||
|
||||
#define DEF_PORTS_CARDS 1
|
||||
#define MAX_PORTS_CARDS 12
|
||||
#define PORTS_LINES 4
|
||||
#define PORTS_RCV_QUEUE 5
|
||||
|
||||
/*
|
||||
* Sub-field values for the PPC_DEVICE request entry; these are placed
|
||||
* in app_data.bt[0] in the PPC_DEVICE application field. The prefix
|
||||
* DR indicates that this is a code for use in "device" request
|
||||
* entries only.
|
||||
*/
|
||||
|
||||
#define DR_ENA 1 /* enable a device */
|
||||
#define DR_DIS 2 /* disable a device */
|
||||
#define DR_ABR 3 /* abort reception on a device */
|
||||
#define DR_ABX 4 /* abort transmission on a device */
|
||||
#define DR_BRK 5 /* transmit "break" on a device */
|
||||
#define DR_SUS 6 /* suspend xmit on a device */
|
||||
#define DR_RES 7 /* resume xmit on a device */
|
||||
#define DR_BLK 8 /* transmit STOP character */
|
||||
#define DR_UNB 9 /* transmit START character */
|
||||
|
||||
/*
|
||||
* Sub-field values for the PPC_DEVICE completion entry; these appear
|
||||
* in app_data.bt[0] in the PPC_DEVICE application field. These are
|
||||
* mutually exclusive and cannot be combined. The prefix DC indicates
|
||||
* that this is a code for use in "device" completion entries only.
|
||||
*/
|
||||
|
||||
#define DC_NORM 0x00 /* command executed as requested */
|
||||
#define DC_DEV 0x01 /* bad device number */
|
||||
#define DC_NON 0x02 /* bad sub-code on request */
|
||||
#define DC_FAIL 0x03 /* failed to read express entry */
|
||||
|
||||
/*
|
||||
* Sub-field values for the PPC_RECV completion entry; these appear in
|
||||
* app_data.bt[0] in the PPC_RECV application field. These are NOT
|
||||
* mutually exclusive and may appear in combination. The prefix RC
|
||||
* indicates that this is a code for use in "read" completion entries
|
||||
* only.
|
||||
*/
|
||||
|
||||
#define RC_DSR 0x01 /* disruption of service */
|
||||
#define RC_FLU 0x02 /* buffer flushed */
|
||||
#define RC_TMR 0x04 /* inter-character timer expired */
|
||||
#define RC_BQO 0x08 /* PPC buffer queue overflow */
|
||||
#define RC_UAO 0x10 /* uart overrun */
|
||||
#define RC_PAR 0x20 /* parity error */
|
||||
#define RC_FRA 0x40 /* framing error */
|
||||
#define RC_BRK 0x80 /* break received */
|
||||
|
||||
/*
|
||||
* The following codes are included on the DISC (disconnect) command.
|
||||
* They are "or"ed into the app_data.bt[1] application field in a
|
||||
* request. These codes are NOT mutually exclusive and can be used in
|
||||
* any combination.
|
||||
*/
|
||||
|
||||
#define GR_DTR 0x01
|
||||
#define GR_CREAD 0x02
|
||||
|
||||
/*
|
||||
* Sub-field values for the PPC_XMIT and PPC_OPTIONS completion
|
||||
* entries; these appear in app_data.bt[0] in the application fields.
|
||||
* These are NOT mutually exclusive and may appear in combination.
|
||||
* The prefix GC indicates that this is a code for use in "general"
|
||||
* completion entries only.
|
||||
*/
|
||||
|
||||
#define GC_DSR 0x01 /* disruption of service */
|
||||
#define GC_FLU 0x02 /* buffer flushed */
|
||||
|
||||
/*
|
||||
* Sub-field values for the PPC_ASYNC completion entry; these appear
|
||||
* in app_data.bt[0] in the PPC_ASYNC application field. These are
|
||||
* mutually exclusive and cannot be combined. The prefix AC indicates
|
||||
* that this is a code for use in "asynchronous" completion entries
|
||||
* only.
|
||||
*/
|
||||
|
||||
#define AC_CON 0x01 /* connection detected */
|
||||
#define AC_DIS 0x02 /* disconnection detected */
|
||||
#define AC_BRK 0x03 /* asynchronous "break" */
|
||||
#define AC_FLU 0x04 /* xmit flush complete */
|
||||
|
||||
/* Line Discipline flags (input and output) */
|
||||
|
||||
#define IGNBRK 0x0001
|
||||
#define BRKINT 0x0002
|
||||
#define IGNPAR 0x0004
|
||||
#define PARMRK 0x0008
|
||||
#define INPCK 0x0010
|
||||
#define ISTRIP 0x0020
|
||||
#define INLCR 0x0040
|
||||
#define IGNCR 0x0080
|
||||
#define ICRNL 0x0100
|
||||
#define IUCLC 0x0200
|
||||
#define IXON 0x0400
|
||||
#define IXANY 0x0800
|
||||
|
||||
#define OPOST 0x0001
|
||||
#define OLCUC 0x0002
|
||||
#define ONLCR 0x0004
|
||||
#define OCRNL 0x0008
|
||||
#define ONOCR 0x0010
|
||||
#define ONLRET 0x0020
|
||||
#define OFILL 0x0040
|
||||
#define OFDEL 0x0080
|
||||
#define ONLDLY 0x0100
|
||||
#define OCRDLY 0x0600
|
||||
#define OTABDLY 0x1800
|
||||
#define OBSDLY 0x2000
|
||||
#define OVTDLY 0x4000
|
||||
#define OFFDLY 0x8000
|
||||
|
||||
/* Opcodes for PORTS card */
|
||||
|
||||
#define PPC_OPTIONS 32 /* GEN, COMP queues: set PPC options */
|
||||
#define PPC_XMIT 33 /* GEN, COMP queues: transmit a buffer */
|
||||
#define PPC_CONN 34 /* GEN, COMP queues: connect a device */
|
||||
#define PPC_DISC 35 /* GEN, COMP queues: disconnect a device */
|
||||
#define PPC_BRK 36 /* GEN, COMP queues: ioctl break */
|
||||
#define PPC_DEVICE 40 /* EXP, ECOMP entries: device control command */
|
||||
#define PPC_CLR 41 /* EXP, ECOMP entries: board clear */
|
||||
#define PPC_RECV 50 /* RECV, COMP queues: receive request */
|
||||
#define PPC_ASYNC 60 /* Asynchronous request */
|
||||
#define CFW_CONFIG 70 /* GEN, COMP queues: set PPC port 0 hardware options */
|
||||
#define CFW_IREAD 71 /* GEN, COMP queues: read immediate one to four bytes */
|
||||
#define CFW_IWRITE 72 /* GEN, COMP queues: write immediate one to four bytes */
|
||||
#define CFW_WRITE 73 /* GEN, COMP queues: write */
|
||||
#define PPC_VERS 80 /* EXP, COMP queues: Version */
|
||||
|
||||
typedef struct {
|
||||
uint32 tx_addr; /* Address to next read from */
|
||||
uint32 tx_req_addr; /* Original request address */
|
||||
uint32 tx_chars; /* Number of chars left to transfer */
|
||||
uint32 tx_req_chars; /* Original number of chars */
|
||||
uint8 rlp; /* Last known load pointer */
|
||||
uint16 iflag; /* Line Discipline: Input flags */
|
||||
uint16 oflag; /* Line Discipline: Output flags */
|
||||
t_bool crlf; /* Indicates we are in a CRLF output transform */
|
||||
t_bool conn; /* TRUE if connected, FALSE otherwise */
|
||||
} PORTS_LINE_STATE;
|
||||
|
||||
typedef struct {
|
||||
uint16 line; /* line discipline */
|
||||
uint16 pad1;
|
||||
uint16 iflag; /* input options word */
|
||||
uint16 oflag; /* output options word */
|
||||
uint16 cflag; /* hardware options */
|
||||
uint16 lflag; /* line discipline options */
|
||||
uint8 cerase; /* "erase" character */
|
||||
uint8 ckill; /* "kill" character */
|
||||
uint8 cinter; /* "interrupt" character */
|
||||
uint8 cquit; /* "quit" character */
|
||||
uint8 ceof; /* "end of file" character */
|
||||
uint8 ceol; /* "end of line" character */
|
||||
uint8 itime; /* inter character timer multiplier */
|
||||
uint8 vtime; /* user-specified inter char timer */
|
||||
uint8 vcount; /* user-specified maximum buffer char count */
|
||||
uint8 pad2;
|
||||
uint16 pad3;
|
||||
} PORTS_OPTIONS;
|
||||
|
||||
extern DEVICE ports_dev;
|
||||
|
||||
t_stat ports_reset(DEVICE *dptr);
|
||||
t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||||
t_stat ports_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||
t_stat ports_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||
t_stat ports_rcv_svc(UNIT *uptr);
|
||||
t_stat ports_xmt_svc(UNIT *uptr);
|
||||
t_stat ports_cio_svc(UNIT *uptr);
|
||||
t_stat ports_attach(UNIT *uptr, CONST char *cptr);
|
||||
t_stat ports_detach(UNIT *uptr);
|
||||
void ports_sysgen(uint8 cid);
|
||||
void ports_express(uint8 cid);
|
||||
void ports_full(uint8 cid);
|
||||
|
||||
static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val,
|
||||
CONST void *desc, t_bool rq);
|
||||
|
||||
#endif /* _3B2_PORTS_H_ */
|
|
@ -35,6 +35,8 @@
|
|||
#include "3b2_if.h"
|
||||
#include "3b2_id.h"
|
||||
#include "3b2_mmu.h"
|
||||
#include "3b2_ctc.h"
|
||||
#include "3b2_ports.h"
|
||||
#include "3b2_sysdev.h"
|
||||
|
||||
char sim_name[] = "AT&T 3B2 Model 400";
|
||||
|
@ -61,6 +63,8 @@ DEVICE *sim_devices[] = {
|
|||
&dmac_dev,
|
||||
&if_dev,
|
||||
&id_dev,
|
||||
&ports_dev,
|
||||
&ctc_dev,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -87,6 +91,8 @@ void full_reset()
|
|||
if_reset(&if_dev);
|
||||
id_reset(&id_dev);
|
||||
csr_reset(&csr_dev);
|
||||
ports_reset(&ports_dev);
|
||||
ctc_reset(&ctc_dev);
|
||||
}
|
||||
|
||||
t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
|
||||
|
|
|
@ -49,7 +49,7 @@ DEBTAB sys_deb_tab[] = {
|
|||
{ "WRITE", WRITE_MSG, "Write activity" },
|
||||
{ "EXECUTE", EXECUTE_MSG, "Execute activity" },
|
||||
{ "IRQ", IRQ_MSG, "Interrupt activity"},
|
||||
{ "TRACE", TRACE_MSG, "Detailed activity" },
|
||||
{ "TRACE", TRACE_DBG, "Detailed activity" },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
|
@ -481,7 +481,7 @@ t_stat timer0_svc(UNIT *uptr)
|
|||
time = TIMER_STP_US;
|
||||
}
|
||||
|
||||
sim_activate_abs(uptr, (int32) DELAY_US(time));
|
||||
sim_activate_after_abs(uptr, time);
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
@ -489,7 +489,7 @@ t_stat timer0_svc(UNIT *uptr)
|
|||
t_stat timer1_svc(UNIT *uptr)
|
||||
{
|
||||
struct timer_ctr *ctr;
|
||||
int32 ticks, t;
|
||||
int32 t;
|
||||
|
||||
ctr = (struct timer_ctr *)uptr->tmr;
|
||||
|
||||
|
@ -498,14 +498,8 @@ t_stat timer1_svc(UNIT *uptr)
|
|||
csr_data |= CSRCLK;
|
||||
}
|
||||
|
||||
ticks = ctr->divider / TIMER_STP_US;
|
||||
|
||||
if (ticks < CLK_MIN_TICKS) {
|
||||
ticks = TPS_CLK;
|
||||
}
|
||||
|
||||
t = sim_rtcn_calb(ticks, TMR_CLK);
|
||||
sim_activate_after(uptr, (uint32) (1000000 / ticks));
|
||||
t = sim_rtcn_calb(TPS_CLK, TMR_CLK);
|
||||
sim_activate_after_abs(uptr, 1000000/TPS_CLK);
|
||||
tmxr_poll = t;
|
||||
|
||||
return SCPE_OK;
|
||||
|
@ -524,7 +518,7 @@ t_stat timer2_svc(UNIT *uptr)
|
|||
time = TIMER_STP_US;
|
||||
}
|
||||
|
||||
sim_activate_abs(uptr, (int32) DELAY_US(time));
|
||||
sim_activate_after_abs(uptr, time);
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
@ -598,7 +592,7 @@ void handle_timer_write(uint8 ctrnum, uint32 val)
|
|||
ctr->enabled = TRUE;
|
||||
ctr->stime = sim_gtime();
|
||||
sim_cancel(timer_clk_unit);
|
||||
sim_activate_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
|
||||
sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
|
||||
break;
|
||||
case 0x20:
|
||||
ctr->divider &= 0x00ff;
|
||||
|
@ -608,7 +602,7 @@ void handle_timer_write(uint8 ctrnum, uint32 val)
|
|||
ctr->stime = sim_gtime();
|
||||
/* Kick the timer to get the new divider value */
|
||||
sim_cancel(timer_clk_unit);
|
||||
sim_activate_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
|
||||
sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
|
||||
break;
|
||||
case 0x30:
|
||||
if (ctr->lmb) {
|
||||
|
@ -622,7 +616,7 @@ void handle_timer_write(uint8 ctrnum, uint32 val)
|
|||
R[NUM_PC], ctrnum, val & 0xff);
|
||||
/* Kick the timer to get the new divider value */
|
||||
sim_cancel(timer_clk_unit);
|
||||
sim_activate_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
|
||||
sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
|
||||
} else {
|
||||
ctr->lmb = TRUE;
|
||||
ctr->divider = (ctr->divider & 0xff00) | (val & 0xff);
|
||||
|
|
3
makefile
3
makefile
|
@ -1730,7 +1730,8 @@ ATT3B2 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_mmu.c \
|
|||
${ATT3B2D}/3b2_iu.c ${ATT3B2D}/3b2_if.c \
|
||||
${ATT3B2D}/3b2_id.c ${ATT3B2D}/3b2_dmac.c \
|
||||
${ATT3B2D}/3b2_sys.c ${ATT3B2D}/3b2_io.c \
|
||||
${ATT3B2D}/3b2_sysdev.c
|
||||
${ATT3B2D}/3b2_ports.c ${ATT3B2D}/3b2_ctc.c \
|
||||
${ATT3B2D}/3b2_sysdev.c
|
||||
ATT3B2_OPT = -I ${ATT3B2D} -DUSE_INT64 -DUSE_ADDR64
|
||||
#
|
||||
# Build everything (not the unsupported/incomplete or experimental simulators)
|
||||
|
|
Loading…
Add table
Reference in a new issue