diff --git a/3B2/3b2_id.c b/3B2/3b2_id.c index fd87e129..5213cd8b 100644 --- a/3B2/3b2_id.c +++ b/3B2/3b2_id.c @@ -164,11 +164,11 @@ MTAB id_mod[] = { }; DEVICE id_dev = { - "ID", id_unit, id_reg, id_mod, + "IDISK", id_unit, id_reg, id_mod, ID_NUM_UNITS, 16, 32, 1, 16, 8, NULL, NULL, &id_reset, NULL, &id_attach, &id_detach, NULL, - DEV_DEBUG|DEV_SECTORS, 0, sys_deb_tab, + DEV_DEBUG|DEV_DISK|DEV_SECTORS, 0, sys_deb_tab, NULL, NULL, &id_help, NULL, NULL, &id_description }; @@ -937,21 +937,21 @@ void id_after_dma() CONST char *id_description(DEVICE *dptr) { - return "MFM Hard Disk Controller"; + return "Integrated Hard Disk"; } t_stat id_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { - fprintf(st, "Integrated Hard Disk (ID)\n\n"); - fprintf(st, "The ID device implements the integrated MFM hard disk controller\n"); - fprintf(st, "of the 3B2/400. Up to two drives are supported on a single controller.\n\n"); + fprintf(st, "Integrated Hard Disk (IDISK)\n\n"); + fprintf(st, "The IDISK device implements the integrated MFM hard disk of the\n"); + fprintf(st, "3B2/400. Up to two drives are supported on a single controller.\n\n"); fprintf(st, "Supported device types are:\n\n"); fprintf(st, " Name Size ID Cyl Head Sec Byte/Sec Description\n"); fprintf(st, " ---- -------- -- ---- ---- --- -------- ----------------------\n"); fprintf(st, " HD30 30.6 MB 3 697 5 18 512 CDC Wren 94155-36\n"); fprintf(st, " HD72 73.2 MB 5 925 9 18 512 CDC Wren II 94156-86\n"); fprintf(st, " HD72C 72.9 MB 8 754 11 18 512 Fujitsu M2243AS\n"); - fprintf(st, " HD135 135.0 MB 11 1024 15 18 512 Maxtor XT1190 (SVR2)\n\n"); + fprintf(st, " HD135 135.0 MB 11 1024 15 18 512 Maxtor XT1190 (SVR2)\n"); fprintf(st, " HD161 161.4 MB 11 1224 15 18 512 Maxtor XT1190 (SVR3+)\n\n"); fprintf(st, "The drive ID and geometry values are used when low-level formatting a\n"); fprintf(st, "drive using the AT&T 'idtools' utility.\n"); diff --git a/3B2/3b2_if.c b/3B2/3b2_if.c index 3fccd5bb..7abfcc83 100644 --- a/3B2/3b2_if.c +++ b/3B2/3b2_if.c @@ -34,7 +34,7 @@ static SIM_INLINE void if_set_irq(); static SIM_INLINE void if_clear_irq(); static SIM_INLINE void if_cancel_pending_irq(); -static SIM_INLINE uint32 if_buf_offset(); +static SIM_INLINE uint32 if_lba(); /* * Disk Format: @@ -57,8 +57,8 @@ static SIM_INLINE uint32 if_buf_offset(); #define IF_HSW_DELAY 40000 /* us */ UNIT if_unit = { - UDATA (&if_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ - UNIT_MUSTBUF+UNIT_BINK, IF_DSK_SIZE) + UDATA (&if_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BINK+UNIT_ROABLE, + IF_DSK_SIZE_SECS) }; REG if_reg[] = { @@ -66,14 +66,17 @@ REG if_reg[] = { }; DEVICE if_dev = { - "IF", &if_unit, if_reg, NULL, + "IFLOPPY", &if_unit, if_reg, NULL, 1, 16, 8, 1, 16, 8, NULL, NULL, &if_reset, - NULL, NULL, NULL, NULL, - DEV_DEBUG, 0, sys_deb_tab + NULL, &if_attach, &if_detach, NULL, + DEV_DEBUG|DEV_DISK|DEV_SECTORS, 0, sys_deb_tab, + NULL, NULL, &if_help, NULL, NULL, + &if_description }; IF_STATE if_state; +uint8 if_buf[IF_SEC_SIZE]; uint32 if_sec_ptr = 0; t_bool if_irq = FALSE; @@ -103,6 +106,9 @@ static SIM_INLINE void if_cancel_pending_irq() t_stat if_svc(UNIT *uptr) { + uint32 lba; /* Logical block address for write */ + t_seccnt sectswritten; + if_state.status &= ~(IF_BUSY); switch(if_state.cmd & 0xf0) { @@ -114,6 +120,23 @@ t_stat if_svc(UNIT *uptr) if (if_state.track == 0) { if_state.status |= IF_TK_0; } + break; + case IF_WRITE_SEC: + lba = if_lba(); + + /* If we're read-only, don't actually do anything. */ + if (if_unit.flags & UNIT_RO) { + break; + } + + if (sim_disk_wrsect(&if_unit, lba, if_buf, §swritten, 1) == SCPE_OK) { + if (sectswritten != 1) { + sim_debug(EXECUTE_MSG, &if_dev, + "ERROR: ASKED TO wRITE ONE SECTOR, WROTE %d\n", + sectswritten); + } + } + break; } @@ -132,19 +155,28 @@ t_stat if_reset(DEVICE *dptr) if_state.track = 0; if_state.sector = 1; if_sec_ptr = 0; + return SCPE_OK; } +t_stat if_attach(UNIT *uptr, CONST char *cptr) +{ + return sim_disk_attach(uptr, cptr, 512, 1, TRUE, 0, "IF", 0, 0); +} + +t_stat if_detach(UNIT *uptr) +{ + return sim_disk_detach(uptr); +} + uint32 if_read(uint32 pa, size_t size) { uint8 reg, data; - uint32 pos, pc; + uint32 pc; UNIT *uptr; - uint8 *fbuf; uptr = &(if_dev.units[0]); reg = (uint8)(pa - IFBASE); pc = R[NUM_PC]; - fbuf = (uint8 *)uptr->filebuf; switch (reg) { case IF_STATUS_REG: @@ -205,11 +237,10 @@ uint32 if_read(uint32 pa, size_t size) { return if_state.data; } - pos = if_buf_offset(); - data = fbuf[pos + if_sec_ptr++]; + data = if_buf[if_sec_ptr++]; sim_debug(READ_MSG, &if_dev, "\tDATA\t%02x\n", data); - if (if_sec_ptr >= IF_SECTOR_SIZE) { + if (if_sec_ptr >= IF_SEC_SIZE) { if_sec_ptr = 0; } @@ -228,6 +259,8 @@ void if_handle_command() uint32 delay_ms = 0; uint32 head_switch_delay = 0; uint32 head_load_delay = 0; + uint32 lba; /* Logical block address */ + t_seccnt sectsread; if_sec_ptr = 0; @@ -285,6 +318,10 @@ void if_handle_command() /* Reset HLT */ if_state.status &= ~IF_HEAD_LOADED; + if (if_unit.flags & UNIT_RO) { + if_state.status |= IF_WP; + } + /* If head should be loaded immediately, do so now */ if (if_state.cmd & IF_H_FLAG) { if_state.status |= IF_HEAD_LOADED; @@ -310,12 +347,18 @@ void if_handle_command() case IF_STEP: case IF_STEP_T: sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep\n", if_state.cmd); + if (if_unit.flags & UNIT_RO) { + if_state.status |= IF_WP; + } if_activate(IF_STEP_DELAY); 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 (if_unit.flags & UNIT_RO) { + if_state.status |= IF_WP; + } if_state.step_dir = IF_STEP_IN_DIR; if_state.track = (uint8) MAX((int) if_state.track + if_state.step_dir, 0); if_activate(IF_STEP_DELAY); @@ -323,6 +366,9 @@ void if_handle_command() 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 (if_unit.flags & UNIT_RO) { + if_state.status |= IF_WP; + } if_state.step_dir = IF_STEP_OUT_DIR; if_state.track = (uint8) MIN((int) if_state.track + if_state.step_dir, 0x4f); if_activate(IF_STEP_DELAY); @@ -333,6 +379,10 @@ void if_handle_command() /* Reset HLT */ if_state.status &= ~IF_HEAD_LOADED; + if (if_unit.flags & UNIT_RO) { + if_state.status |= IF_WP; + } + /* If head should be loaded immediately, do so now */ if (if_state.cmd & IF_H_FLAG) { if_state.status |= IF_HEAD_LOADED; @@ -373,16 +423,27 @@ void if_handle_command() break; case IF_READ_SEC: - sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Sector %d/%d/%d\n", - if_state.cmd, if_state.track, if_state.side, if_state.sector); - /* We set DRQ right away to request the transfer. */ - if_state.drq = TRUE; - if_state.status |= IF_DRQ; - if (if_state.cmd & IF_E_FLAG) { - if_activate(IF_R_DELAY + IF_VERIFY_DELAY + head_switch_delay); - } else { - if_activate(IF_R_DELAY + head_switch_delay); + lba = if_lba(); + + sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Sector %d/%d/%d (lba=%d)\n", + if_state.cmd, if_state.track, if_state.side, if_state.sector, lba); + + if (sim_disk_rdsect(&if_unit, lba, if_buf, §sread, 1) == SCPE_OK) { + if (sectsread != 1) { + sim_debug(EXECUTE_MSG, &if_dev, + "ERROR: ASKED TO READ ONE SECTOR, READ %d\n", + sectsread); + } + /* We set DRQ right away to request the transfer. */ + if_state.drq = TRUE; + if_state.status |= IF_DRQ; + if (if_state.cmd & IF_E_FLAG) { + if_activate(IF_R_DELAY + IF_VERIFY_DELAY + head_switch_delay); + } else { + if_activate(IF_R_DELAY + head_switch_delay); + } } + break; case IF_READ_SEC_M: /* Not yet implemented. Halt the emulator. */ @@ -392,9 +453,24 @@ void if_handle_command() stop_reason = STOP_ERR; break; case IF_WRITE_SEC: - sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Sector %d/%d/%d\n", - if_state.cmd, if_state.track, if_state.side, if_state.sector); - /* We set DRQ right away to request the transfer. */ + lba = if_lba(); + + sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Sector %d/%d/%d (lba=%d)\n", + if_state.cmd, if_state.track, if_state.side, if_state.sector, lba); + + if (if_unit.flags & UNIT_RO) { + if_state.status |= IF_WP; + sim_debug(EXECUTE_MSG, &if_dev, "\tWON'T WRITE: WRITE PROTECTED.\n"); + /* Still cause an interrupt... */ + if_activate(IF_W_DELAY + head_switch_delay); + /* But don't set DRQ and ask for a transfer. */ + break; + } + + /* We set DRQ right away to request the transfer. Data will + * be written by the host into our buffer by 512 writes to the + * data register. When the IF device later activates, the data + * will actually be written. */ if_state.drq = TRUE; if_state.status |= IF_DRQ; if (if_state.cmd & IF_E_FLAG) { @@ -457,14 +533,11 @@ void if_write(uint32 pa, uint32 val, size_t size) { UNIT *uptr; uint8 reg; - uint32 pos; - uint8 *fbuf; val = val & 0xff; uptr = &(if_dev.units[0]); reg = (uint8) (pa - IFBASE); - fbuf = (uint8 *)uptr->filebuf; switch (reg) { case IF_CMD_REG: @@ -497,11 +570,10 @@ void if_write(uint32 pa, uint32 val, size_t size) * which we do not emulate. */ } else if ((if_state.cmd & 0xf0) == IF_WRITE_SEC || (if_state.cmd & 0xf0) == IF_WRITE_SEC_M) { - /* Find the right offset, and update the value. */ - pos = if_buf_offset(); - fbuf[pos + if_sec_ptr++] = (uint8) val; - if (if_sec_ptr >= IF_SECTOR_SIZE) { + if_buf[if_sec_ptr++] = (uint8) val; + + if (if_sec_ptr >= IF_SEC_SIZE) { if_sec_ptr = 0; } } @@ -512,22 +584,34 @@ void if_write(uint32 pa, uint32 val, size_t size) } } -/* - * Compute the offset of the currently selected C/H/S - */ -static SIM_INLINE uint32 if_buf_offset() +CONST char *if_description(DEVICE *dptr) { - uint32 pos; + return "Integrated Floppy Disk"; +} - pos = IF_TRACK_SIZE * if_state.track * 2; +t_stat if_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf(st, "Integrated Floppy Disk (IFLOPPY)\n\n"); + fprintf(st, "The IFLOPPY device implements the integrated 720 KB floppy disk\n"); + fprintf(st, "of the 3B2/400. A single floppy disk is supported on the controller.\n\n"); + fprintf(st, "The format of the diskette media is as follows:\n\n"); + fprintf(st, " Size Sides Tracks/Side Sectors/Track Bytes/Track\n"); + fprintf(st, " ------ ----- ----------- ------------- -----------\n"); + fprintf(st, " 720 KB 2 80 9 512\n\n"); + fprintf(st, "Physical media is Double Sided/Quad Density, 96 tpi, 250kbps MFM encoding.\n"); + return SCPE_OK; +} - if (if_state.side == 1) { - pos += IF_TRACK_SIZE; - } - - pos += IF_SECTOR_SIZE * (if_state.sector - 1); - - return pos; +/* + * Compute the offset of the currently selected C/H/S (in # of sectors) + */ +static SIM_INLINE uint32 if_lba() +{ + /* Reminder that sectors are numbered 1-9 instead + * of being numbered 0-8 */ + return((if_state.track * IF_SEC_COUNT * 2) + + (if_state.side * IF_SEC_COUNT) + + (if_state.sector - 1)); } void if_after_dma() diff --git a/3B2/3b2_if.h b/3B2/3b2_if.h index 1c794a07..8774ebef 100644 --- a/3B2/3b2_if.h +++ b/3B2/3b2_if.h @@ -113,22 +113,27 @@ extern t_bool if_irq; /* Constants */ #define IF_SIDES 2 +#define IF_SEC_COUNT 9 +#define IF_SEC_SIZE 512 #define IF_TRACK_SIZE 4608 -#define IF_SECTOR_SIZE 512 #define IF_TRACK_COUNT 80 #define IF_STEP_IN_DIR 1 #define IF_STEP_OUT_DIR -1 -#define IF_DSK_SIZE (IF_SIDES * IF_TRACK_SIZE * IF_TRACK_COUNT) +#define IF_DSK_SIZE_SECS (IF_SIDES * IF_TRACK_COUNT * IF_SEC_COUNT) /* Function prototypes */ t_stat if_svc(UNIT *uptr); t_stat if_reset(DEVICE *dptr); +t_stat if_attach(UNIT *uptr, CONST char *cptr); +t_stat if_detach(UNIT *uptr); uint32 if_read(uint32 pa, size_t size); void if_write(uint32 pa, uint32 val, size_t size); void if_handle_command(); void if_after_dma(); +CONST char *if_description(DEVICE *dptr); +t_stat if_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); #endif diff --git a/3B2/README.md b/3B2/README.md index 55118b73..ab073a89 100644 --- a/3B2/README.md +++ b/3B2/README.md @@ -3,6 +3,10 @@ AT&T 3B2 Simulator This module contains a simulator for the AT&T 3B2 Model 400 microcomputer. +Full documentation for the 3B2 simulator is available here: + + - https://loomcom.com/3b2/emulator.html + Devices ------- @@ -15,16 +19,19 @@ devices are given in parentheses: - PD8253 Interval Timer (TIMER) - AM9517 DMA controller (DMAC) - SCN2681A Integrated DUART (IU) - - TMS2793 Integrated Floppy Controller (IF) - - uPD7261A Integrated MFM Fixed Disk Controller (ID) + - TMS2793 Integrated Floppy Controller (IFLOPPY) + - uPD7261A Integrated MFM Fixed Disk Controller (IDISK) + - Non-Volatile Memory (NVRAM) - MM58174A Time Of Day Clock (TOD) + - CM195B 4-port Serial MUX (PORTS) + - CM195H Cartridge Tape Controller (CTC) Usage ----- To boot the 3B2 simulator into firmware mode, simply type: - sim> BOOT CPU + sim> BOOT You will be greeted with the message: @@ -73,8 +80,8 @@ UNIX SVR3 is the only operating system available for the 3B2. To boot UNIX, attach the first disk image from the 3B2 "Essential Utilities" distribution. - sim> ATTACH IF - sim> BOOT CPU + sim> ATTACH IFLOPPY + sim> BOOT Once you reach the `SYSTEM FAILURE` message, type `mcp` to enter firmware mode. When prompted for the name of a program to boot, enter @@ -94,15 +101,15 @@ Installing SVR3 --------------- To install SVR3 to the first hard disk, first, attach a new image -to the ID0 device: +to the IDISK0 device: - sim> ATTACH ID0 + sim> ATTACH IDISK0 Then, boot the file `idtools` from the "3B2 Maintenance Utilities - Issue 4.0" floppy diskette. From `idtools`, select the `formhard` option and low-level format -integrated disk 0. Parameters are: +integrated disk 0. Parameters for the default 72MB hard disk are: Drive Id: 5 Number cylinders: 925