| /* i7090_disk.c: IBM 7090 Disk | |
| Copyright (c) 2005-2016, Richard Cornwell | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| RICHARD CORNRWELL 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. | |
| Support for 1301/1302/2302 disks and 7238 drums | |
| Disks are represented in files as follows: | |
| Since these drives supported variable format for each cylinder the | |
| format is represented as one track per cylinder as follows: | |
| 0 data | |
| 1 header | |
| 2 Home Address | |
| 3 end of track | |
| These codes are packed 4 per byte and used to control read/write of | |
| data. | |
| After one format track per cylinder there is one record of bytes per | |
| track data for each track. First bytes are home address 2, followed | |
| by record address, and record data to cover number in format. All data | |
| is stored with the top 2 bits as zero. | |
| Limitiation of this are that the address field for each record can not | |
| be more then 16 bytes. | |
| */ | |
| #include "i7000_defs.h" | |
| #ifdef NUM_DEVS_DSK | |
| #define UNIT_DSK UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX | |
| #define FORMAT_OK (1 << (UNIT_V_LOCAL+0)) | |
| #define HA2_OK (1 << (UNIT_V_LOCAL+1)) | |
| #define CTSS_BOOT (1 << (UNIT_V_MODE)) | |
| /* Device status information stored in u5 */ | |
| #define DSKSTA_CMD 0x0000100 /* Unit has recieved a cmd */ | |
| #define DSKSTA_DATA 0x0000200 /* Unit has finished cmd */ | |
| #define DSKSTA_WRITE 0x0000400 /* Last command was a write */ | |
| #define DSKSTA_CHECK 0x0000800 /* Doing a write check */ | |
| #define DSKSTA_CMSK 0x00000ff /* Command mask */ | |
| #define DSKSTA_ARGMSK 0x0fff000 /* Command argument */ | |
| #define DSKSTA_ARGSHFT 12 | |
| #define DSKSTA_SCAN 0x1000000 /* Scanning for header */ | |
| #define DSKSTA_SKIP 0x2000000 /* Skiping record */ | |
| #define DSKSTA_XFER 0x4000000 /* Tranfser current record */ | |
| #define DSKSTA_DIRTY 0x8000000 /* Buffer needs to be written */ | |
| #define FMT_DATA 0 /* Data */ | |
| #define FMT_HDR 1 /* Header */ | |
| #define FMT_HA2 2 /* Home address 2 */ | |
| #define FMT_END 3 /* End of track */ | |
| /* Disk commands */ | |
| #define DNOP 0x00 /* Nop */ | |
| #define DREL 0x04 /* Release */ | |
| #define DEBM 0x08 /* Eight Bit mode */ | |
| #define DSBM 0x09 /* Six bit mode */ | |
| #define DSEK 0x80 /* Seek */ | |
| #define DVSR 0x82 /* Prepare to Verify single record */ | |
| #define DWRF 0x83 /* Prepare to Format */ | |
| #define DVTN 0x84 /* Prepare to Verify track no addr */ | |
| #define DVCY 0x85 /* Prepare to Verify Cyl */ | |
| #define DWRC 0x86 /* Prepare to Write Check */ | |
| #define DSAI 0x87 /* Set Access Inoperative */ | |
| #define DVTA 0x88 /* Prepare to Verify track addr */ | |
| #define DVHA 0x89 /* Prepare to Verify home addr */ | |
| /* Disk sense codes */ /*01234 */ | |
| #define STAT_SIXBIT 0x00004 /* Disk in 6bit more. */ | |
| #define EXPT_FILECHK 0x10010 /* File control check error */ | |
| #define EXPT_DSKCHK 0x10020 /* Disk storage error */ | |
| #define STAT_NOTRDY 0x10040 /* Disk no ready */ | |
| #define STAT_OFFLINE 0x10080 /* Disk offline */ | |
| #define DATA_PARITY 0x20100 /* Data parity error */ | |
| #define DATA_CHECK 0x20200 /* Compare error */ | |
| #define DATA_RESPONSE 0x20400 /* Response check */ | |
| #define PROG_INVADDR 0x40800 /* Invalid seek address */ | |
| #define PROG_NOREC 0x41000 /* No record found */ | |
| #define PROG_FMTCHK 0x42000 /* Format check */ | |
| #define PROG_INVCODE 0x44000 /* Invalid code */ | |
| #define PROG_INVSEQ 0x48000 /* Invalid sequence */ | |
| #define MAXTRACK 6020 /* Max size per track */ | |
| uint32 dsk_cmd(UNIT *, uint16, uint16); | |
| t_stat dsk_srv(UNIT *); | |
| t_stat dsk_boot(int32, DEVICE *); | |
| void dsk_ini(UNIT *, t_bool); | |
| t_stat dsk_reset(DEVICE *); | |
| t_stat dsk_set_module(UNIT * uptr, int32 val, CONST char *cptr, | |
| void *desc); | |
| t_stat dsk_get_module(FILE * st, UNIT * uptr, int32 v, | |
| CONST void *desc); | |
| t_stat dsk_set_type(UNIT * uptr, int32 val, CONST char *cptr, | |
| void *desc); | |
| t_stat dsk_get_type(FILE * st, UNIT * uptr, int32 v, | |
| CONST void *desc); | |
| t_stat dsk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, | |
| const char *cptr); | |
| const char *dsk_description (DEVICE *dptr); | |
| int disk_rblock(UNIT * uptr, int track); | |
| int disk_wblock(UNIT * uptr); | |
| void disk_posterr(UNIT * uptr, uint32 error); | |
| void disk_cmderr(UNIT * uptr, uint32 error); | |
| int disk_cmd(UNIT * uptr); | |
| int disk_write(UNIT * uptr, uint8 data, int chan, | |
| int eor); | |
| int disk_read(UNIT * uptr, uint8 * data, int chan); | |
| int disk_format(UNIT * uptr, FILE * f, int cyl, | |
| UNIT * base); | |
| int bcd_to_track(uint32 addr); | |
| /* Data buffer for track */ | |
| uint8 dbuffer[NUM_DEVS_DSK * 4][MAXTRACK]; | |
| /* Format buffer for cylinder */ | |
| uint8 fbuffer[NUM_DEVS_DSK * 4][MAXTRACK / 4]; | |
| /* Currently loaded format record */ | |
| uint16 fmt_cyl[NUM_DEVS_DSK * 4]; | |
| /* Currently read in track in buffer */ | |
| uint16 dtrack[NUM_DEVS_DSK * 4]; | |
| /* Arm position */ | |
| uint16 arm_cyl[NUM_DEVS_DSK * 4]; | |
| uint32 sense[NUM_CHAN * 2]; | |
| uint32 sense_unit[NUM_CHAN * 2]; | |
| uint8 cmd_buffer[NUM_CHAN]; /* Command buffer per channel */ | |
| uint8 cmd_mod[NUM_CHAN]; /* Command module per channel */ | |
| uint32 cmd_option[NUM_CHAN]; /* Command option per channel */ | |
| uint16 cmd_count[NUM_CHAN]; /* Number of chars recieved */ | |
| #ifdef I7010 | |
| extern uint8 chan_seek_done[NUM_CHAN]; /* Seek finished flag */ | |
| extern uint8 chan_io_status[NUM_CHAN]; /* Channel status flags */ | |
| #endif | |
| /* Macro to help build the disk size table */ | |
| #define DISK_DEF(name, cyl, cylpertrk, acc, charpertrk, overhd, mod, dr) \ | |
| { name, cyl, cylpertrk, ((charpertrk/128) + 1) * 128, \ | |
| acc, (((charpertrk/128) + 1) * 128)/4, \ | |
| acc * cyl * ((((charpertrk/128) + 1) * 128)/4), \ | |
| overhd, mod, dr } | |
| struct disk_t | |
| { | |
| const char *name; /* Type Name */ | |
| int cyl; /* Number of cylinders */ | |
| int track; /* Number of tracks/cylinder */ | |
| unsigned int bpt; /* Max bytes per track */ | |
| int arms; /* Number of access arms */ | |
| int fbpt; /* Number of format bytes per track */ | |
| int fmtsz; /* Format size */ | |
| int overhd; /* Number of characters overhead on HA/RA */ | |
| int mods; /* Number of modules */ | |
| int datarate; /* us per chars */ | |
| } | |
| disk_type[] = | |
| { | |
| DISK_DEF("1301", 254, 40, 1, 2880, 4, 1, 15), | |
| DISK_DEF("1301-2", 254, 40, 1, 2880, 4, 2, 15), | |
| DISK_DEF("1302", 254, 40, 2, 5940, 7, 1, 10), | |
| DISK_DEF("1302-2", 254, 40, 2, 5940, 7, 2, 10), | |
| DISK_DEF("2302", 254, 40, 2, 5940, 7, 2, 10), | |
| DISK_DEF("7238", 1, 404, 1, 3270, 4, 1, 10), | |
| DISK_DEF("7238-2", 1, 404, 1, 3270, 4, 2, 10), { | |
| NULL, 0} | |
| }; | |
| int unit_bit[] = { | |
| /*0 1 2 3 4 5 6 7 8 9 A B C D E F */ | |
| 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 30, 30, 30, 30, 30, 30, | |
| 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 30, 30, 30, 30, 30, 30 | |
| }; | |
| #define DSKSTA_BSY 0x02 /* Controller busy. */ | |
| #define DSKSTA_EIGHT 0x04 /* Controller in 8 bit mode */ | |
| #ifdef I7090 | |
| #define CH1 4 | |
| #define CH2 6 | |
| #endif | |
| #ifdef I7070 | |
| #define CH1 5 | |
| #define CH2 6 | |
| #endif | |
| #ifdef I7080 | |
| #define CH1 5 | |
| #define CH2 6 | |
| #endif | |
| #ifndef CH1 | |
| #define CH1 1 | |
| #endif | |
| #ifndef CH2 | |
| #define CH2 2 | |
| #endif | |
| UNIT dsk_unit[] = { | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x000, 0}, | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x102, 0}, | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x204, 0}, | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x306, 0}, | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x408, 0}, | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x500, 0}, | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x602, 0}, | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x704, 0}, | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x806, 0}, | |
| {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x908, 0}, | |
| /* Second set for arm two of each unit */ | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| /* Third set for arm one of second module */ | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| /* Fourth set for arm two of second module */ | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, | |
| }; | |
| MTAB dsk_mod[] = { | |
| {FORMAT_OK, 0, 0, "NOFORMAT", NULL, NULL, NULL, "Format not allowed"}, | |
| {FORMAT_OK, FORMAT_OK, "FORMAT", "FORMAT", NULL, NULL, NULL, | |
| "Format allowed"}, | |
| {HA2_OK, 0, 0, "NOHA2", NULL, NULL, NULL, "No writing of Home Address"}, | |
| {HA2_OK, HA2_OK, "HA2", "HA2", NULL, NULL, NULL, | |
| "Allow writing of Home Address"}, | |
| #ifdef I7090 | |
| {CTSS_BOOT, 0, 0, "IBSYS", NULL, NULL, NULL, "IBSYS Boot Card"}, | |
| {CTSS_BOOT, CTSS_BOOT, "CTSS", "CTSS", NULL, NULL, NULL, "CTSS Boot Card"}, | |
| #endif | |
| {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE", | |
| &dsk_set_type, &dsk_get_type, NULL, "Type of disk"}, | |
| {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "MODULE", "MODULE", | |
| &dsk_set_module, &dsk_get_module, NULL, "Module number"}, | |
| {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", | |
| &set_chan, &get_chan, NULL, "Channel number"}, | |
| #ifndef I7010 | |
| {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "SELECT", "SELECT", | |
| &chan9_set_select, &chan9_get_select, NULL, "Unit select"}, | |
| #endif | |
| {0} | |
| }; | |
| DEVICE dsk_dev = { | |
| "DK", dsk_unit, NULL /* Registers */ , dsk_mod, | |
| NUM_DEVS_DSK, 8, 15, 1, 8, 8, | |
| NULL, NULL, &dsk_reset, &dsk_boot, NULL, NULL, | |
| &dsk_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &dsk_help, NULL, NULL, &dsk_description | |
| }; | |
| uint32 dsk_cmd(UNIT * uptr, uint16 cmd, uint16 dev) | |
| { | |
| int chan; | |
| int u = (uptr->u3 >> 8) & 0xf; | |
| UNIT *base = &dsk_unit[u]; | |
| int sel; | |
| chan = UNIT_G_CHAN(dsk_unit[u].flags); | |
| sel = (base->flags & UNIT_SELECT) ? 1 : 0; | |
| #ifdef I7010 | |
| if (cmd & 0x100) | |
| sense[(chan * 2) + sel] |= STAT_SIXBIT; | |
| else | |
| sense[(chan * 2) + sel] &= ~STAT_SIXBIT; | |
| cmd_buffer[chan] = cmd & 0xff; | |
| cmd_count[chan] = 2; | |
| sim_debug(DEBUG_CHAN, &dsk_dev, "unit %d = cmd=%02x\n\r", | |
| dev, cmd & 0xff); | |
| #else | |
| cmd_buffer[chan] = 0; | |
| cmd_count[chan] = 0; | |
| sim_debug(DEBUG_CHAN, &dsk_dev, "unit=%d cmd\n\r", dev); | |
| #endif | |
| cmd_option[chan] = 0; | |
| cmd_mod[chan] = 0; | |
| chan_clear(chan, DEV_SEL); | |
| /* If device is not active start it working */ | |
| if (!sim_is_active(uptr)) | |
| sim_activate(uptr, us_to_ticks(50)); | |
| return SCPE_OK; | |
| } | |
| t_stat dsk_srv(UNIT * uptr) | |
| { | |
| int chan; | |
| int sel; | |
| int schan; | |
| int dev = uptr->u3 & 0xff; | |
| int u = (uptr->u3 >> 8) & 0xf; | |
| struct disk_t *dsk = &disk_type[uptr->u4]; | |
| UNIT *base = &dsk_unit[u]; | |
| uint8 ch = 0; | |
| int eor = 0; | |
| chan = UNIT_G_CHAN(base->flags); | |
| sel = (base->flags & UNIT_SELECT) ? 1 : 0; | |
| schan = (chan * 2) + sel; | |
| /* Make sure channel is talking to us */ | |
| if (sel != chan_test(chan, CTL_SEL)) | |
| goto seeking; | |
| /* Channel has disconnected, abort current operation. */ | |
| if (chan_test(chan, DEV_DISCO)) { | |
| int i; | |
| /* Scan all units and stop them if they are on this channel */ | |
| for (i = 0; i < NUM_DEVS_DSK; i++) { | |
| int xchan = UNIT_G_CHAN(dsk_unit[i].flags); | |
| int j; | |
| if (xchan != chan) | |
| continue; | |
| for (j = 3 * NUM_DEVS_DSK + i; j >= 0; j -= NUM_DEVS_DSK) { | |
| disk_wblock(&dsk_unit[j]); /* Flush block */ | |
| if (dsk_unit[j].u5 & DSKSTA_CMD) { | |
| dsk_unit[j].u5 &= | |
| ~(DSKSTA_CMD | DSKSTA_XFER | DSKSTA_SCAN | DSKSTA_DATA); | |
| sim_cancel(&dsk_unit[j]); | |
| } | |
| } | |
| } | |
| chan_clear(chan, (DEV_DISCO | DEV_WEOR | DEV_SEL)); | |
| sim_debug(DEBUG_CHAN, &dsk_dev, "unit=%d disconnecting\n\r", dev); | |
| if (uptr->wait > 0) | |
| sim_activate(uptr, us_to_ticks(100)); | |
| return SCPE_OK; | |
| } | |
| /* Call channel proccess to make sure all date is available. */ | |
| chan_proc(); | |
| /* Handle sending sense data */ | |
| if (chan_test(chan, CTL_SNS)) { | |
| chan9_clear_error(chan, sel); | |
| switch(cmd_count[chan]) { | |
| case 0: | |
| sim_debug(DEBUG_SNS, &dsk_dev, "unit=%d chan sense=%05x\n", dev, | |
| sense[schan]); | |
| /* fall through */ | |
| case 1: case 2: case 3: case 4: | |
| ch = (sense[schan] >> (4 * (4 - cmd_count[chan]))) & 0xF; | |
| break; | |
| case 9: | |
| sim_debug(DEBUG_SNS, &dsk_dev, "unit=%d unit sense=%08x\n", dev, | |
| sense_unit[schan]); | |
| eor = DEV_REOR; | |
| /* fall through */ | |
| case 5: case 6: case 7: case 8: | |
| ch = (sense_unit[schan] >> (4 * (9 - cmd_count[chan]))) & 0xF; | |
| break; | |
| } | |
| if (ch & 010) | |
| ch ^= 030; | |
| sim_debug(DEBUG_SNS, &dsk_dev, "unit=%d sense %d %02o\n\r", dev, | |
| cmd_count[chan], ch); | |
| cmd_count[chan]++; | |
| switch(chan_write_char(chan, &ch, eor)) { | |
| case DATA_OK: | |
| if (eor == 0) | |
| break; | |
| case TIME_ERROR: | |
| case END_RECORD: | |
| sense[chan] &= STAT_SIXBIT; | |
| sense_unit[chan] = 0; | |
| chan_set(chan, CTL_END); | |
| chan_clear(chan, DEV_SEL); | |
| sim_debug(DEBUG_CHAN, &dsk_dev, "unit=%d sense eor\n\r", dev); | |
| break; | |
| } | |
| sim_activate(uptr, us_to_ticks(20)); | |
| return SCPE_OK; | |
| } | |
| if (chan_test(chan, CTL_CNTL) && disk_cmd(uptr)) { | |
| sim_activate(uptr, us_to_ticks(50)); | |
| return SCPE_OK; | |
| } | |
| /* Handle writing of data */ | |
| if (chan_test(chan, CTL_WRITE) && uptr->u5 & DSKSTA_CMD) { | |
| /* Channel asking for end of record? */ | |
| if (chan_stat(chan, DEV_WEOR)) { | |
| sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d -> weor\n\r", chan); | |
| while (disk_write(uptr, 0x40, chan, 1) == 0) ; | |
| disk_wblock(uptr); /* Flush block */ | |
| uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); | |
| uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); | |
| chan_set(chan, CTL_END|DEV_REOR); | |
| sim_activate(uptr, us_to_ticks(100)); | |
| return SCPE_OK; | |
| } | |
| uptr->u5 |= DSKSTA_WRITE; /* Flag as write */ | |
| switch(chan_read_char(chan, &ch, 0)) { | |
| case TIME_ERROR: | |
| disk_posterr(uptr, DATA_RESPONSE); | |
| sim_activate(uptr, us_to_ticks(100)); | |
| return SCPE_OK; | |
| case END_RECORD: | |
| /* If last word, fill with zeros until we get eor */ | |
| sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d eor\n\r",chan); | |
| uptr->u5 |= DSKSTA_DATA; | |
| while (disk_write(uptr, 0x40, chan, 1) == 0) ; | |
| disk_wblock(uptr); /* Flush block */ | |
| uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); | |
| chan_set(chan, CTL_END); | |
| sim_activate(uptr, us_to_ticks(100)); | |
| break; | |
| case DATA_OK: | |
| eor = disk_write(uptr, ch, chan, 0); | |
| uptr->u5 |= DSKSTA_DATA; | |
| if (eor == 1) { | |
| /* Handle end of record */ | |
| sim_debug(DEBUG_CHAN, &dsk_dev, | |
| "Disk chan %d end of track\n\r", chan); | |
| if ((uptr->u5 & DSKSTA_CMSK) == DVSR && | |
| (uptr->u5 & DSKSTA_XFER) == 0) | |
| disk_posterr(uptr, PROG_NOREC); | |
| uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); | |
| chan_set(chan, DEV_REOR|CTL_END); | |
| } | |
| sim_activate(uptr, us_to_ticks(dsk->datarate)); | |
| return SCPE_OK; | |
| } | |
| } | |
| /* Handle reading of data */ | |
| if (chan_test(chan, CTL_READ) && uptr->u5 & DSKSTA_CMD) { | |
| /* Channel asking for end of record? */ | |
| if (chan_stat(chan, DEV_WEOR)) { | |
| sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d -> weor\n\r", chan); | |
| uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); | |
| chan_set(chan, CTL_END|DEV_REOR); | |
| sim_activate(uptr, us_to_ticks(100)); | |
| return SCPE_OK; | |
| } | |
| eor = disk_read(uptr, &ch, chan); | |
| /* Check if we got error during read */ | |
| if (eor == -1) { | |
| sim_activate(uptr, us_to_ticks(100)); | |
| return SCPE_OK; | |
| } | |
| switch(chan_write_char(chan, &ch, (eor)?DEV_REOR:0)) { | |
| case TIME_ERROR: | |
| /* Flag as timming error */ | |
| disk_posterr(uptr, DATA_RESPONSE); | |
| break; | |
| case END_RECORD: | |
| /* Fill the buffer until end of record */ | |
| sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d eor\n\r", chan); | |
| if ((uptr->u5 & DSKSTA_CMSK) == DVSR && | |
| (uptr->u5 & DSKSTA_XFER) == 0) | |
| disk_posterr(uptr, PROG_NOREC); | |
| uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); | |
| chan_set(chan, CTL_END); | |
| uptr->u5 |= DSKSTA_DATA; | |
| break; | |
| case DATA_OK: | |
| uptr->u5 |= DSKSTA_DATA; | |
| break; | |
| } | |
| sim_activate(uptr, us_to_ticks(dsk->datarate)); | |
| return SCPE_OK; | |
| } | |
| /* Handle read/write without a command */ | |
| if (chan_test(chan, CTL_WRITE|CTL_READ) && | |
| (uptr->u3 & 0xff) == cmd_mod[chan] && | |
| (uptr->u5 & (DSKSTA_DATA|DSKSTA_CMD)) == 0) | |
| disk_posterr(uptr, PROG_INVSEQ); | |
| seeking: | |
| if (uptr->wait || uptr->u5 & DSKSTA_CMD) | |
| sim_activate(uptr, us_to_ticks(100)); | |
| /* Handle seeking */ | |
| if (uptr->wait > 0) { | |
| uptr->wait--; | |
| if (uptr->wait == 0) { | |
| sim_debug(DEBUG_EXP, &dsk_dev, "Seek done dev=%d\n", dev); | |
| sense_unit[schan] |= 1 << unit_bit[dev]; | |
| #ifdef I7010 | |
| chan_seek_done[chan] = 1; | |
| #else | |
| chan9_set_attn(chan, sel); | |
| #endif | |
| } | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Post a error on a given unit. */ | |
| void | |
| disk_posterr(UNIT * uptr, uint32 error) | |
| { | |
| int chan; | |
| int schan; | |
| int sel; | |
| int u = (uptr->u3 >> 8) & 0xf; | |
| uptr = &dsk_unit[u]; | |
| uptr->u5 &= ~(DSKSTA_CMD | DSKSTA_XFER | DSKSTA_SCAN); | |
| chan = UNIT_G_CHAN(uptr->flags); | |
| sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; | |
| schan = (chan * 2) + sel; | |
| sense[schan] |= error; | |
| if (error != 0) | |
| chan9_set_error(chan, SNS_UEND); | |
| chan_set(chan, DEV_REOR|CTL_END); | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "post err dev=%d err=%08x\n", u,error); | |
| #ifdef I7010 | |
| if ((error & STAT_OFFLINE) == STAT_OFFLINE) | |
| chan_io_status[chan] |= 01; | |
| if ((error & STAT_NOTRDY) == STAT_NOTRDY) | |
| chan_io_status[chan] |= 02; | |
| if (error & (EXPT_FILECHK|EXPT_DSKCHK|DATA_PARITY|DATA_CHECK| | |
| DATA_RESPONSE|PROG_INVADDR) & 0xFFFF) | |
| chan_io_status[chan] |= 04; | |
| if (error & (PROG_NOREC|PROG_FMTCHK|PROG_INVCODE|PROG_INVSEQ) & 0xffff) | |
| chan_io_status[chan] |= 010; | |
| #endif | |
| } | |
| /* Post error for command that could not be completed */ | |
| void | |
| disk_cmderr(UNIT * uptr, uint32 error) | |
| { | |
| int chan; | |
| int schan; | |
| int sel; | |
| int u = (uptr->u3 >> 8) & 0xf; | |
| uptr = &dsk_unit[u]; | |
| uptr->u5 &= ~(DSKSTA_CMSK | DSKSTA_CMD | DSKSTA_CHECK | DSKSTA_WRITE); | |
| chan = UNIT_G_CHAN(uptr->flags); | |
| sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; | |
| schan = (chan * 2) + sel; | |
| sense[schan] |= error; | |
| if (error != 0) | |
| chan9_set_error(chan, SNS_UEND); | |
| chan_set(chan, DEV_REOR|CTL_END); | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "cmd err dev=%d err=%08x\n", u,error); | |
| #ifdef I7010 | |
| if ((error & STAT_OFFLINE) == STAT_OFFLINE) | |
| chan_io_status[chan] |= 01; | |
| if ((error & STAT_NOTRDY) == STAT_NOTRDY) | |
| chan_io_status[chan] |= 02; | |
| if (error & (EXPT_FILECHK|EXPT_DSKCHK|DATA_PARITY|DATA_CHECK| | |
| DATA_RESPONSE|PROG_INVADDR) & 0xFFFF) | |
| chan_io_status[chan] |= 04; | |
| if (error & (PROG_NOREC|PROG_FMTCHK|PROG_INVCODE|PROG_INVSEQ) & 0xffff) | |
| chan_io_status[chan] |= 010; | |
| #endif | |
| } | |
| /* Process command */ | |
| int | |
| disk_cmd(UNIT * uptr) | |
| { | |
| uint8 ch; | |
| UNIT *base; | |
| UNIT *up; | |
| int chan; | |
| int schan; | |
| int sel; | |
| int i; | |
| int u; | |
| int t; | |
| int trk; | |
| int cyl; | |
| /* Get information on unit */ | |
| u = uptr - dsk_unit; | |
| base = &dsk_unit[(uptr->u3 >> 8) & 0xf]; | |
| chan = UNIT_G_CHAN(base->flags); | |
| sel = (base->flags & UNIT_SELECT) ? 1 : 0; | |
| schan = (chan * 2) + sel; | |
| /* Check if we have a command yet */ | |
| switch(chan_read_char(chan, &ch, 0)) { | |
| case TIME_ERROR: | |
| disk_cmderr(uptr, DATA_RESPONSE); | |
| case END_RECORD: | |
| return 0; | |
| case DATA_OK: | |
| sim_debug(DEBUG_DATA, &dsk_dev, "unit=%d data=%02o\n", u, ch); | |
| break; | |
| } | |
| /* Place in command buffer */ | |
| switch(cmd_count[chan]) { | |
| case 0: | |
| case 1: | |
| if (ch != 012) | |
| cmd_buffer[chan] |= (ch & 0xf) << (4 * (1 - cmd_count[chan])); | |
| break; | |
| case 2: | |
| case 3: | |
| if (ch != 012) | |
| cmd_mod[chan] |= (ch & 0xf) << (4 * (3 - cmd_count[chan])); | |
| break; | |
| case 4: | |
| case 5: | |
| case 6: | |
| case 7: | |
| if (ch != 012) | |
| cmd_option[chan] |= (ch & 0xf) << (16 + (4 * (7 - cmd_count[chan]))); | |
| break; | |
| case 8: | |
| case 9: | |
| cmd_option[chan] |= (ch & 0x3f) << (6 * (9 - cmd_count[chan])); | |
| break; | |
| } | |
| /* Need at least two chars to determine command */ | |
| if (++cmd_count[chan] == 1) | |
| return 1; | |
| /* Check if we have enough digits */ | |
| switch(cmd_buffer[chan]) { | |
| case DNOP: /* Nop */ | |
| case DREL: /* Release */ | |
| case DEBM: /* Eight Bit mode */ | |
| case DSBM: /* Six bit mode */ | |
| break; /* Yes */ | |
| case DSAI: /* Set Access Inoperative */ | |
| if (cmd_count[chan] <= 3) | |
| return 1; /* Need more */ | |
| break; | |
| case DSEK: /* Seek */ | |
| case DWRF: /* Prepare to Format */ | |
| case DVHA: /* Prepare to Verify home addr */ | |
| if (cmd_count[chan] <= 7) | |
| return 1; /* Need more */ | |
| break; | |
| case DVTA: /* Prepare to Verify track addr */ | |
| case DVTN: /* Prepare to Verify track no addr */ | |
| case DVCY: /* Prepare to Verify Cyl */ | |
| case DVSR: /* Prepare to Verify single record */ | |
| case DWRC: /* Prepare to Write Check */ | |
| if (cmd_count[chan] < 10) | |
| return 1; | |
| break; | |
| } | |
| /* Flag last item recieved */ | |
| chan_set(chan, DEV_REOR); | |
| chan9_clear_error(chan, sel); | |
| sim_debug(DEBUG_CMD, &dsk_dev, "unit=%d cmd=%02x %02x %04x %04o ",u, | |
| cmd_buffer[chan], cmd_mod[chan], cmd_option[chan] >> 16, | |
| cmd_option[chan] & 07777); | |
| /* 40 36 32 2824 2016 6 0 */ | |
| /* 01 2 3 4 5 6 7 8 9 */ | |
| /* op | ac mod | t t | t t | HA2 | HA2 */ | |
| up = NULL; | |
| sense[schan] &= STAT_SIXBIT; | |
| switch (cmd_buffer[chan]) { | |
| case DNOP: /* Nop */ | |
| case DREL: /* Release */ | |
| /* Should not happen, but if read or write, cpy will be | |
| * expected so turn on command flag to catch disconnect */ | |
| sim_debug(DEBUG_CMD, &dsk_dev, "nop\n"); | |
| goto clear_drive; | |
| case DEBM: /* Eight Bit mode */ | |
| /* Should not happen, but if read or write, cpy will be | |
| * expected so turn on command flag to catch disconnect */ | |
| sim_debug(DEBUG_CMD, &dsk_dev, "eight bit mode\n"); | |
| sense[schan] &= ~STAT_SIXBIT; | |
| goto clear_drive; | |
| case DSBM: /* Six bit mode */ | |
| /* Should not happen, but if read or write, cpy will be | |
| * expected so turn on command flag to catch disconnect */ | |
| sim_debug(DEBUG_CMD, &dsk_dev, "six bit mode\n"); | |
| sense[schan] |= STAT_SIXBIT; | |
| clear_drive: | |
| /* Scan all units and clear command */ | |
| for (i = 0; i < NUM_DEVS_DSK; i++) { | |
| int xchan = UNIT_G_CHAN(dsk_unit[i].flags); | |
| int j; | |
| if (xchan != chan) | |
| continue; | |
| for (j = 3 * NUM_DEVS_DSK + i; j >= 0; j -= NUM_DEVS_DSK) | |
| dsk_unit[j].u5 &= ~ DSKSTA_CMSK; | |
| } | |
| sim_activate(uptr, us_to_ticks(100)); | |
| return 1; | |
| case DWRC: /* Prepare to Write Check */ | |
| sim_debug(DEBUG_CMD, &dsk_dev, "write check\n"); | |
| case DVSR: /* Prepare to Verify single record */ | |
| case DWRF: /* Prepare to Format */ | |
| case DVTN: /* Prepare to Verify track no addr */ | |
| case DVCY: /* Prepare to Verify Cyl */ | |
| case DVTA: /* Prepare to Verify track addr */ | |
| case DVHA: /* Prepare to Verify home addr */ | |
| case DSAI: /* Set Access Inoperative */ | |
| case DSEK: /* Seek */ | |
| break; /* Go find unit to operate on */ | |
| default: | |
| sim_debug(DEBUG_CMD, &dsk_dev, " Unknown Command\n\r"); | |
| /* Flag as invalid command */ | |
| disk_cmderr(uptr, PROG_INVCODE); | |
| return 1; | |
| } | |
| /* Find actual owner of this command */ | |
| for (i = 0; i < NUM_DEVS_DSK; i++) { | |
| if ((dsk_unit[i].flags & (UNIT_SELECT | UNIT_CHAN)) != | |
| (base->flags & (UNIT_SELECT | UNIT_CHAN))) | |
| continue; | |
| /* Adjust for unit */ | |
| if ((0xff & dsk_unit[i].u3) == cmd_mod[chan]) | |
| up = &dsk_unit[i]; | |
| else if ((0xff & dsk_unit[i + NUM_DEVS_DSK].u3) == cmd_mod[chan]) | |
| up = &dsk_unit[i + NUM_DEVS_DSK]; | |
| else if ((0xff & dsk_unit[i + (NUM_DEVS_DSK * 2)].u3) == cmd_mod[chan]) | |
| up = &dsk_unit[i + (NUM_DEVS_DSK * 2)]; | |
| else if ((0xff & dsk_unit[i + (NUM_DEVS_DSK * 3)].u3) == cmd_mod[chan]) | |
| up = &dsk_unit[i + (NUM_DEVS_DSK * 3)]; | |
| else | |
| continue; | |
| /* Check if unit is busy */ | |
| if (cmd_buffer[chan] != DWRC && (up->u5 & DSKSTA_CMD || up->wait > 0)) { | |
| sim_debug(DEBUG_CMD, &dsk_dev, "unit=%d busy\n", u); | |
| if (up->wait > 5) | |
| up->wait = 5; | |
| disk_cmderr(uptr, STAT_NOTRDY); | |
| return 1; | |
| } else { | |
| if (cmd_buffer[chan] == DWRC) { | |
| if (up->u5 & DSKSTA_CMSK) { | |
| up->u5 |= DSKSTA_CHECK; | |
| up->u5 &= ~DSKSTA_DATA; | |
| cmd_buffer[chan] = up->u5 & DSKSTA_CMSK; | |
| } else { | |
| disk_cmderr(up, PROG_INVSEQ); | |
| return 0; | |
| } | |
| } else { | |
| up->u5 &= ~(DSKSTA_CMSK|DSKSTA_CHECK|DSKSTA_WRITE|DSKSTA_DATA); | |
| up->u5 |= cmd_buffer[chan]; | |
| } | |
| break; | |
| } | |
| } | |
| if (up == NULL) { | |
| sim_debug(DEBUG_CMD, &dsk_dev, "invalid unit\n"); | |
| /* Flag as invalid command */ | |
| disk_cmderr(uptr, STAT_OFFLINE); | |
| return 1; | |
| } | |
| /* Adjust to new unit */ | |
| u = up - dsk_unit; | |
| base = &dsk_unit[(up->u3 >> 8) & 0xf]; | |
| chan = UNIT_G_CHAN(base->flags); | |
| sel = (base->flags & UNIT_SELECT) ? 1 : 0; | |
| schan = (chan * 2) + sel; | |
| /* Clear unit attention */ | |
| sense_unit[schan] &= ~(1 << unit_bit[up->u3 & 0xff]); | |
| /* Check if there is a unit here */ | |
| if ((base->flags & UNIT_ATT) == 0) { | |
| disk_cmderr(uptr, STAT_OFFLINE); | |
| return 1; | |
| } | |
| /* Find out track and cylinder of this operation */ | |
| trk = bcd_to_track(cmd_option[chan]); | |
| if (chan_test(chan, CTL_PWRITE)) | |
| sim_debug(DEBUG_CMD, &dsk_dev, "write "); | |
| if (chan_test(chan, CTL_PREAD)) | |
| sim_debug(DEBUG_CMD, &dsk_dev, "read "); | |
| /* Do command */ | |
| switch (cmd_buffer[chan]) { | |
| case DSAI: /* Set Access Inoperative */ | |
| detach_unit(base); | |
| disk_cmderr(up, 0); | |
| return 1; | |
| case DSEK: /* Seek */ | |
| cyl = trk / disk_type[base->u4].track; | |
| /* Check how far we have to move */ | |
| t = cyl - arm_cyl[u]; | |
| if (t < 0) | |
| t = -t; | |
| sim_debug(DEBUG_CMD, &dsk_dev, | |
| "DSEK unit=%d %d cylinders to %d trk=%d\n", u, t, cyl, trk); | |
| up->u5 &= ~(DSKSTA_CMSK | DSKSTA_CMD | DSKSTA_CHECK | DSKSTA_WRITE); | |
| arm_cyl[u] = cyl; /* And cylinder */ | |
| if (cyl > disk_type[base->u4].cyl) { | |
| disk_cmderr(up, PROG_INVADDR); | |
| return 1; | |
| } | |
| /* Read in track buffer */ | |
| disk_rblock(up, trk); | |
| /* Set up for seek wait */ | |
| up->wait = 0; | |
| /* From documentation, it looks like seeks were a fixed time | |
| * based on movement between cylinder groups */ | |
| if (t == 0) /* At cylinder, give attention */ | |
| up->wait = 2; /* Electronic select time */ | |
| else if (t > 50) | |
| up->wait = (1800); | |
| else if (t > 10) | |
| up->wait = (1200); | |
| else | |
| up->wait = (300); | |
| break; | |
| case DWRF: /* Prepare to Format */ | |
| /* Verify ok to format */ | |
| if ((base->flags & FORMAT_OK) == 0) { | |
| disk_cmderr(uptr, PROG_FMTCHK); | |
| return 1; | |
| } | |
| cyl = trk / disk_type[base->u4].track; | |
| /* Make sure positioned to correct track */ | |
| if (arm_cyl[u] != cyl) { | |
| disk_cmderr(uptr, PROG_INVSEQ); | |
| return 1; | |
| } | |
| /* Make sure head on valid cylinder */ | |
| if (cyl > disk_type[base->u4].cyl) { | |
| disk_cmderr(up, PROG_INVADDR); | |
| return 1; | |
| } | |
| fmt_cyl[u] = cyl; | |
| sim_debug(DEBUG_CMD, &dsk_dev, "FMT unit=%d\n", u); | |
| up->u5 |= DSKSTA_SCAN | DSKSTA_CMD | DSKSTA_WRITE; /* Flag as write */ | |
| up->u6 = 0; | |
| chan_set(chan, DEV_SEL); | |
| break; | |
| case DVHA: /* Prepare to Verify home addr */ | |
| /* Verify HA2 ok to write */ | |
| if ((base->flags & HA2_OK) == 0) { | |
| disk_cmderr(up, PROG_FMTCHK); | |
| return 1; | |
| } | |
| /* fall through */ | |
| case DVTA: /* Prepare to Verify track addr */ | |
| case DVTN: /* Prepare to Verify track no addr */ | |
| case DVCY: /* Prepare to Verify Cyl */ | |
| switch (cmd_buffer[chan]) { | |
| case DVTA: | |
| sim_debug(DEBUG_CMD, &dsk_dev, "DVTA unit=%d ", u); | |
| break; | |
| case DVTN: | |
| sim_debug(DEBUG_CMD, &dsk_dev, "DVTN unit=%d ", u); | |
| break; | |
| case DVCY: | |
| sim_debug(DEBUG_CMD, &dsk_dev, "DVCY unit=%d ", u); | |
| break; | |
| case DVHA: | |
| sim_debug(DEBUG_CMD, &dsk_dev, "DVHA unit=%d ", u); | |
| break; | |
| } | |
| sim_debug(DEBUG_CMD, &dsk_dev, "trk=%d\n\r", trk); | |
| /* Make sure head on valid cylinder */ | |
| if ((trk / disk_type[base->u4].track) > disk_type[base->u4].cyl) { | |
| disk_cmderr(up, PROG_INVADDR); | |
| return 1; | |
| } | |
| /* Make sure we have correct format for this cylinder in buffer */ | |
| disk_rblock(up, trk); | |
| /* Start actual operations */ | |
| up->u5 |= DSKSTA_SCAN | DSKSTA_CMD; | |
| up->u6 = 0; | |
| chan_set(chan, DEV_SEL); | |
| break; | |
| case DVSR: /* Prepare to Verify single record */ | |
| /* Start actual operations */ | |
| up->u5 |= DSKSTA_SCAN | DSKSTA_CMD; | |
| up->u6 = 0; | |
| chan_set(chan, DEV_SEL); | |
| break; | |
| } | |
| sim_activate(up, us_to_ticks(50)); | |
| return 0; | |
| } | |
| /* Print a format pattern */ | |
| void | |
| print_format(UNIT * uptr) | |
| { | |
| struct disk_t *dsk = &disk_type[uptr->u4]; | |
| int i, j; | |
| int u = uptr - dsk_unit; | |
| int flag; | |
| int lflag = -1; | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d (%s) format: ", u, dsk->name); | |
| i = 0; | |
| j = 0; | |
| do { | |
| flag = fbuffer[u][i / 4]; | |
| flag >>= (i & 03) * 2; | |
| flag &= 03; | |
| if (lflag != flag) { | |
| if (j != 0) { | |
| switch(lflag) { | |
| case FMT_DATA: | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "DA(%d) ", j); | |
| break; | |
| case FMT_HDR: | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "RA(%d) ", j); | |
| break; | |
| case FMT_HA2: | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "HA2(%d) ", j); | |
| break; | |
| } | |
| } | |
| j = 1; | |
| lflag = flag; | |
| } else { | |
| j++; | |
| } | |
| i++; | |
| } while (flag != FMT_END); | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "total=%d\n", i); | |
| } | |
| int | |
| disk_rblock(UNIT * uptr, int trk) | |
| { | |
| int u = uptr - dsk_unit; | |
| struct disk_t *dsk = &disk_type[uptr->u4]; | |
| UNIT *base = &dsk_unit[(uptr->u3 >> 8) & 0xf]; | |
| FILE *f = base->fileref; | |
| int offset = 0; | |
| int fbase = 0; | |
| offset = dsk->cyl * dsk->track * dsk->bpt; | |
| fbase = dsk->fmtsz; | |
| offset *= u / (NUM_DEVS_DSK); | |
| fbase *= u / (NUM_DEVS_DSK); | |
| offset += dsk->fmtsz * dsk->mods * dsk->arms; | |
| if (uptr->u5 & DSKSTA_DIRTY) { | |
| disk_wblock(uptr); | |
| } | |
| if (arm_cyl[u] != fmt_cyl[u]) { | |
| (void)sim_fseek(f, fbase + arm_cyl[u] * dsk->fbpt, SEEK_SET); | |
| (void)sim_fread(fbuffer[u], 1, dsk->fbpt, f); | |
| fmt_cyl[u] = arm_cyl[u]; | |
| print_format(uptr); | |
| } | |
| /* Read in actualy track data */ | |
| if (dtrack[u] != trk) { | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d Read track %d\n", u, | |
| trk); | |
| (void)sim_fseek(f, offset + trk * dsk->bpt, SEEK_SET); | |
| if (sim_fread(dbuffer[u], 1, dsk->bpt, f) != dsk->bpt) | |
| memset(dbuffer[u], 0, dsk->bpt); | |
| dtrack[u] = trk; | |
| } | |
| return 1; | |
| } | |
| int | |
| disk_wblock(UNIT * uptr) | |
| { | |
| int u = uptr - dsk_unit; | |
| struct disk_t *dsk = &disk_type[uptr->u4]; | |
| UNIT *base = &dsk_unit[(uptr->u3 >> 8) & 0xf]; | |
| FILE *f = base->fileref; | |
| int offset = 0; | |
| offset = dsk->cyl * dsk->track * dsk->bpt; | |
| offset *= u / (NUM_DEVS_DSK); | |
| offset += dsk->fmtsz * dsk->mods * dsk->arms; | |
| /* Check if new format data */ | |
| if ((uptr->u5 & DSKSTA_CMSK) == DWRF) { | |
| if (uptr->u5 & (DSKSTA_CHECK|DSKSTA_DIRTY)) { | |
| switch (disk_format(uptr, f, arm_cyl[u], base)) { | |
| case 2: | |
| if (uptr->u5 & DSKSTA_CHECK) { | |
| disk_posterr(uptr, PROG_FMTCHK|EXPT_DSKCHK); | |
| break; | |
| } | |
| case 1: | |
| disk_posterr(uptr, PROG_FMTCHK); | |
| break; | |
| case 0: | |
| break; | |
| } | |
| uptr->u5 &= ~(DSKSTA_CHECK); | |
| } | |
| return 1; | |
| } | |
| if (uptr->u5 & DSKSTA_CHECK) { | |
| uptr->u5 &= ~(DSKSTA_CHECK); | |
| if ((uptr->u5 & DSKSTA_DIRTY) == 0) | |
| return 1; | |
| } else if ((uptr->u5 & DSKSTA_DIRTY) == 0) | |
| return 1; | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d Write track %d\n", | |
| u, dtrack[u]); | |
| /* Write in actualy track data */ | |
| (void)sim_fseek(f, offset + dtrack[u] * dsk->bpt, SEEK_SET); | |
| (void)sim_fwrite(dbuffer[u], 1, dsk->bpt, f); | |
| uptr->u5 &= ~DSKSTA_DIRTY; | |
| return 1; | |
| } | |
| /* Convert a format pattern into a format track */ | |
| int | |
| disk_format(UNIT * uptr, FILE * f, int cyl, UNIT * base) | |
| { | |
| uint8 tbuffer[MAXTRACK]; | |
| struct disk_t *dsk = &disk_type[uptr->u4]; | |
| int i, j; | |
| int out = 0; | |
| int u = uptr - dsk_unit; | |
| uint8 ch; | |
| int offset; | |
| f = base->fileref; | |
| offset = dsk->fmtsz; | |
| offset *= u / (NUM_DEVS_DSK); | |
| uptr->u5 &= ~(DSKSTA_DIRTY); | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d (%s) format: ", u, dsk->name); | |
| /* Scan over new format */ | |
| /* Convert format specification in place in dbuffer, then | |
| * pack it into fbuffer and write to file */ | |
| /* Skip initial gap */ | |
| for (i = 0; i < MAXTRACK && dbuffer[u][i] == 04; i++) ; | |
| if (i == MAXTRACK) | |
| return 2; /* Failed if we hit end */ | |
| /* HA1 Gap */ | |
| for (j = i; i < MAXTRACK && dbuffer[u][i] == 03; i++) ; | |
| if ((i - j) > 12) | |
| return 1; /* HA1 too big */ | |
| if (dbuffer[u][i++] != 04) | |
| return 2; /* Not gap */ | |
| for (j = i; i < MAXTRACK && dbuffer[u][i] == 03; i++) ; | |
| if (i == MAXTRACK) | |
| return 2; /* Failed if we hit end */ | |
| if (dbuffer[u][i++] != 04) | |
| return 2; /* Not gap */ | |
| /* Size up HA2 gap */ | |
| for (j = i; | |
| i < MAXTRACK && (dbuffer[u][i] == 03 || dbuffer[u][i] == 01); | |
| i++) ; | |
| j = i - j; | |
| if (j < 6) | |
| return 2; | |
| j -= dsk->overhd; /* Remove overhead */ | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "HA2(%d) ", j); | |
| for (; j > 0; j--) | |
| tbuffer[out++] = FMT_HA2; | |
| /* Now grab records */ | |
| while (i < MAXTRACK) { | |
| ch = dbuffer[u][i++]; | |
| if (ch == 0x40) | |
| break; /* End of record */ | |
| if (ch != 04 && ch != 02) | |
| return 2; /* Not a gap */ | |
| for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ; | |
| ch = dbuffer[u][i]; /* Should be RA */ | |
| /* Gap not long enough or eor */ | |
| if (ch == 0x40 || (i - j) < 11) | |
| break; | |
| /* Size up RA gap */ | |
| if (ch != 01 && ch != 03) | |
| return 1; /* Not header */ | |
| for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ; | |
| j = i - j; | |
| if (j < 10) | |
| return 2; | |
| j -= dsk->overhd; /* Remove overhead */ | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "RA(%d) ", j); | |
| for (; j > 0; j--) | |
| tbuffer[out++] = FMT_HDR; | |
| ch = dbuffer[u][i++]; | |
| if (ch == 0x40) | |
| break; /* End of record */ | |
| if (ch != 04 && ch != 02) | |
| return 1; /* End of RA Field */ | |
| ch = dbuffer[u][i]; | |
| if (ch != 01 && ch != 03) | |
| return 1; /* Not gap */ | |
| for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ; | |
| if ((i - j) < 10) | |
| return 2; /* Gap not large enough */ | |
| ch = dbuffer[u][i++]; | |
| if (ch != 04 && ch != 02) | |
| return 2; /* End of RA Gap */ | |
| ch = dbuffer[u][i]; /* Should be DA */ | |
| if (ch == 0x40) | |
| break; /* End of record */ | |
| if (ch != 01 && ch != 03) | |
| return 1; /* Not Record data */ | |
| for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ; | |
| j = i - j; | |
| if (j < 10) | |
| return 2; | |
| j -= dsk->overhd; /* Remove overhead */ | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "DA(%d) ", j); | |
| for (; j > 0; j--) | |
| tbuffer[out++] = FMT_DATA; | |
| } | |
| sim_debug(DEBUG_DETAIL, &dsk_dev, "total=%d\n", out); | |
| /* If checking, don't update */ | |
| if (uptr->u5 & DSKSTA_CHECK) | |
| return 0; | |
| /* Put four END chars to end of buffer */ | |
| for (j = 4; j > 0; j--) | |
| tbuffer[out++] = FMT_END; | |
| /* Now grab every four characters and place them in next format location */ | |
| for (j = i = 0; j < out; i++) { | |
| uint8 temp; | |
| temp = (tbuffer[j++] & 03); | |
| temp |= (tbuffer[j++] & 03) << 2; | |
| temp |= (tbuffer[j++] & 03) << 4; | |
| temp |= (tbuffer[j++] & 03) << 6; | |
| fbuffer[u][i] = temp; | |
| if (i > dsk->fbpt) | |
| break; | |
| } | |
| fbuffer[u][dsk->fbpt-1] = (FMT_END<<6)|(FMT_END<<4)|(FMT_END<<2)|FMT_END; | |
| /* Now write the buffer to the file */ | |
| (void)sim_fseek(f, offset + cyl * dsk->fbpt, SEEK_SET); | |
| (void)sim_fwrite(fbuffer[u], 1, dsk->fbpt, f); | |
| /* Make sure we did not pass size of track */ | |
| if (out > (int)dsk->bpt) | |
| return 1; /* Too big for track */ | |
| return 0; | |
| } | |
| /* Handle writing of one character to disk */ | |
| int | |
| disk_write(UNIT * uptr, uint8 data, int chan, int eor) | |
| { | |
| int u = uptr - dsk_unit; | |
| UNIT *base = (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr; | |
| int skip = 1; | |
| int flag = -1; | |
| uint8 cmd; | |
| int schan; | |
| schan = (chan * 2) + ((base->flags & UNIT_SELECT) ? 1 : 0); | |
| cmd = (uptr->u5) & DSKSTA_CMSK; | |
| if (uptr->u6 == 0 && cmd != DWRF) { | |
| int cyl = dtrack[u] / disk_type[base->u4].track; | |
| if (arm_cyl[u] != cyl) { | |
| sim_debug(DEBUG_CMD, &dsk_dev, "cyl not equal %d %d %d\n\r", | |
| u, arm_cyl[u], cyl); | |
| disk_posterr(uptr, PROG_INVADDR); | |
| return -1; | |
| } | |
| /* Verify that the home address matches */ | |
| if (cmd != DVHA && cmd != DVSR) { | |
| uint16 t = cmd_option[chan] & 01717; | |
| uint16 ha; | |
| ha = (077 & dbuffer[u][0]) << 6; | |
| ha |= 077 & dbuffer[u][1]; | |
| /* Mask out bits we ignore */ | |
| ha &= 01717; | |
| /* Convert BCD 0 to Binary 0 */ | |
| if ((ha & 01700) == 01200) | |
| ha &= 077; | |
| if ((ha & 017) == 012) | |
| ha &= 07700; | |
| if ((t & 01700) == 01200) | |
| t &= 077; | |
| if ((t & 017) == 012) | |
| t &= 07700; | |
| sim_debug(DEBUG_CMD, &dsk_dev, "HA %04o(c) %04o(d)\n", t, ha); | |
| if (ha != t) { | |
| disk_posterr(uptr, PROG_NOREC); | |
| return -1; | |
| } | |
| } else { | |
| sim_debug(DEBUG_CMD, &dsk_dev, "HA ignored\n"); | |
| } | |
| } | |
| while (skip) { | |
| flag = fbuffer[u][uptr->u6 / 4]; | |
| flag >>= (uptr->u6 & 03) * 2; | |
| flag &= 03; | |
| switch (uptr->u5 & DSKSTA_CMSK) { | |
| case DWRF: /* Format */ | |
| if ((uint32)uptr->u6 > disk_type[uptr->u4].bpt) { | |
| return 1; | |
| } | |
| if (uptr->u5 & DSKSTA_CHECK) { | |
| if ((dbuffer[u][uptr->u6++] & 077) != (data & 077)) { | |
| disk_posterr(uptr, DATA_CHECK); | |
| return -1; | |
| } | |
| } else { | |
| dbuffer[u][uptr->u6++] = data; | |
| uptr->u5 |= DSKSTA_DIRTY; | |
| } | |
| return 0; | |
| case DVTN: /* Verify track no addr */ | |
| if (flag == FMT_END) { | |
| return 1; | |
| } | |
| if (flag == FMT_DATA) | |
| skip = 0; | |
| else | |
| uptr->u6++; | |
| break; | |
| case DVTA: /* Verify track addr */ | |
| if (flag == FMT_END) { | |
| return 1; | |
| } | |
| if (flag != FMT_HA2) | |
| skip = 0; | |
| else | |
| uptr->u6++; | |
| break; | |
| case DVHA: /* Verify home addr */ | |
| if (flag == FMT_END) { | |
| return 1; | |
| } | |
| skip = 0; | |
| break; | |
| case DVCY: /* Verify Cyl */ | |
| if (flag == FMT_END) { | |
| /* Move to next track */ | |
| int trk = dtrack[u]; | |
| int cyl = trk / disk_type[uptr->u4].track; | |
| uptr->u6 = 0; | |
| if (eor) | |
| return 1; | |
| if ((++trk) / disk_type[uptr->u4].track != cyl) | |
| return 1; | |
| disk_rblock(uptr, trk); | |
| } else if (flag != FMT_DATA) | |
| uptr->u6++; | |
| else | |
| skip = 0; | |
| break; | |
| case DVSR: /* Verify single record */ | |
| if (flag == FMT_DATA && uptr->u5 & DSKSTA_XFER) { | |
| skip = 0; | |
| } else if (uptr->u5 & DSKSTA_XFER) { | |
| /* Not in a Data record, and we were transfering, all done */ | |
| uptr->u5 &= ~DSKSTA_XFER; | |
| uptr->u6 = 0; | |
| return 1; | |
| } else if (flag == FMT_END) { | |
| /* If we hit the end, then no record found */ | |
| disk_posterr(uptr, PROG_NOREC); | |
| return -1; | |
| } else if (flag == FMT_HDR) { | |
| uint8 ch; | |
| uint32 match = 0; | |
| int i; | |
| for (i = 0; i < 4 && flag == FMT_HDR; i++) { | |
| ch = dbuffer[u][uptr->u6++]; | |
| match <<= 4; | |
| if (ch != 012) | |
| match |= ch & 0xf; | |
| flag = fbuffer[u][uptr->u6 / 4]; | |
| flag >>= (uptr->u6 & 03) * 2; | |
| flag &= 03; | |
| } | |
| /* Short header, skip it, but should not have been made */ | |
| if (flag != FMT_HDR) | |
| break; | |
| match <<= 16; | |
| /* Grab last two Characters */ | |
| while (flag == FMT_HDR) { | |
| ch = dbuffer[u][uptr->u6++]; | |
| match = (match & 0xFFFF0000) | ((match & 0x3f) << 6) | | |
| (ch & 077); | |
| flag = fbuffer[u][uptr->u6 / 4]; | |
| flag >>= (uptr->u6 & 03) * 2; | |
| flag &= 03; | |
| } | |
| if (flag != FMT_DATA) | |
| break; | |
| /* Compare values. */ | |
| if (match != cmd_option[chan]) | |
| break; | |
| /* Got a match, exit loop */ | |
| uptr->u5 &= ~DSKSTA_SCAN; | |
| uptr->u5 |= DSKSTA_XFER; | |
| skip = 0; | |
| } else { | |
| uptr->u6++; | |
| } | |
| break; | |
| } | |
| } | |
| data &= (sense[schan] & STAT_SIXBIT)?077:0277; | |
| if (uptr->u5 & DSKSTA_CHECK) { | |
| if (dbuffer[u][uptr->u6++] != data) { | |
| fprintf(stderr, "Mismatch %d %03o != %03o\n\r", | |
| uptr->u6-1, dbuffer[u][uptr->u6-1], data); | |
| disk_posterr(uptr, DATA_CHECK); | |
| } | |
| } else { | |
| dbuffer[u][uptr->u6++] = data; | |
| uptr->u5 |= DSKSTA_DIRTY; | |
| } | |
| return 0; | |
| } | |
| /* Handle reading of one character to disk */ | |
| int | |
| disk_read(UNIT * uptr, uint8 * data, int chan) | |
| { | |
| int u = uptr - dsk_unit; | |
| UNIT *base = (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr; | |
| int skip = 1; | |
| int flag; | |
| uint8 cmd; | |
| int schan; | |
| schan = (chan * 2) + ((base->flags & UNIT_SELECT) ? 1 : 0); | |
| cmd = (uptr->u5) & DSKSTA_CMSK; | |
| if (uptr->u6 == 0) { | |
| int cyl = dtrack[u] / disk_type[base->u4].track; | |
| if (arm_cyl[u] != cyl) { | |
| disk_posterr(uptr, PROG_INVADDR); | |
| return -1; | |
| } | |
| /* Verify that the home address matches */ | |
| if (cmd != DVHA && cmd != DVSR) { | |
| uint16 t = cmd_option[chan] & 01717; | |
| uint16 ha; | |
| ha = (077 & dbuffer[u][0]) << 6; | |
| ha |= 077 & dbuffer[u][1]; | |
| /* Mask out bits we ignore */ | |
| ha &= 01717; | |
| /* Convert BCD 0 to Binary 0 */ | |
| if ((ha & 07700) == 01200) | |
| ha &= 077; | |
| if ((ha & 077) == 012) | |
| ha &= 07700; | |
| if ((t & 07700) == 01200) | |
| t &= 077; | |
| if ((t & 077) == 012) | |
| t &= 07700; | |
| sim_debug(DEBUG_CMD, &dsk_dev, "HA %04o(c) %04o(d)\n", t, ha); | |
| if (ha != t) { | |
| disk_posterr(uptr, PROG_NOREC); | |
| return -1; | |
| } | |
| } else { | |
| sim_debug(DEBUG_CMD, &dsk_dev, "HA ignored\n"); | |
| } | |
| } | |
| while (skip) { | |
| flag = fbuffer[u][uptr->u6 / 4]; | |
| flag >>= (uptr->u6 & 03) * 2; | |
| flag &= 03; | |
| switch (cmd) { | |
| case DWRF: /* Format */ | |
| disk_posterr(uptr, PROG_FMTCHK); | |
| return 1; | |
| case DVTN: /* Verify track no addr */ | |
| if (flag == FMT_END) { | |
| uptr->u6 = 0; | |
| return 1; | |
| } | |
| if (flag == FMT_DATA) | |
| skip = 0; | |
| else | |
| uptr->u6++; | |
| break; | |
| case DVTA: /* Verify track addr */ | |
| if (flag == FMT_END) { | |
| uptr->u6 = 0; | |
| return 1; | |
| } | |
| if (flag != FMT_HA2) | |
| skip = 0; | |
| else | |
| uptr->u6++; | |
| break; | |
| case DVHA: /* Prepare to Verify home addr */ | |
| if (flag == FMT_END) { | |
| uptr->u6 = 0; | |
| return 1; | |
| } | |
| skip = 0; | |
| break; | |
| case DVCY: /* Verify Cyl */ | |
| if (flag == FMT_END) { | |
| /* Move to next track */ | |
| int trk = dtrack[u]; | |
| UNIT *base = | |
| (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr; | |
| int cyl = trk / disk_type[base->u4].track; | |
| uptr->u6 = 0; | |
| if ((++trk) / disk_type[base->u4].track != cyl) | |
| return 1; | |
| disk_rblock(uptr, trk); | |
| } else if (flag != FMT_DATA) | |
| uptr->u6++; | |
| else | |
| skip = 0; | |
| break; | |
| case DVSR: /* Verify single record */ | |
| if (flag == FMT_DATA && uptr->u5 & DSKSTA_XFER) { | |
| skip = 0; | |
| } else if (uptr->u5 & DSKSTA_XFER) { | |
| /* Not in a Data record, and we were transfering, all done */ | |
| uptr->u5 &= ~DSKSTA_XFER; | |
| uptr->u6 = 0; | |
| return 1; | |
| } else if (flag == FMT_END) { | |
| /* If we hit the end, then no record found */ | |
| disk_posterr(uptr, PROG_NOREC); | |
| return -1; | |
| } else if (flag == FMT_HDR) { | |
| uint8 ch; | |
| uint32 match = 0; | |
| int i; | |
| for (i = 0; i < 4 && flag == FMT_HDR; i++) { | |
| ch = dbuffer[u][uptr->u6++]; | |
| match <<= 4; | |
| if (ch != 012) | |
| match |= ch & 0xf; | |
| flag = fbuffer[u][uptr->u6 / 4]; | |
| flag >>= (uptr->u6 & 03) * 2; | |
| flag &= 03; | |
| } | |
| /* Short header, skip it, but should not have been made */ | |
| if (flag != FMT_HDR) | |
| break; | |
| match <<= 16; | |
| /* Grab last two Characters */ | |
| while (flag == FMT_HDR) { | |
| ch = dbuffer[u][uptr->u6++]; | |
| match = (match & 0xFFFF0000) | ((match & 0x3f) << 6) | | |
| (ch & 077); | |
| flag = fbuffer[u][uptr->u6 / 4]; | |
| flag >>= (uptr->u6 & 03) * 2; | |
| flag &= 03; | |
| } | |
| if (flag != FMT_DATA) | |
| break; | |
| /* Compare values. */ | |
| if (match != cmd_option[chan]) | |
| break; | |
| /* Got a match, exit loop */ | |
| uptr->u5 &= ~DSKSTA_SCAN; | |
| uptr->u5 |= DSKSTA_XFER; | |
| skip = 0; | |
| } else { | |
| uptr->u6++; | |
| } | |
| break; | |
| } | |
| } | |
| *data = dbuffer[u][uptr->u6++] & ((sense[schan] & STAT_SIXBIT)?077:0277); | |
| /* Check if character is last in record */ | |
| flag = fbuffer[u][uptr->u6 / 4]; | |
| flag >>= (uptr->u6 & 03) * 2; | |
| flag &= 03; | |
| switch (cmd) { | |
| case DVTN: /* Verify track no addr */ | |
| case DVTA: /* Verify track addr */ | |
| case DVHA: /* Prepare to Verify home addr */ | |
| if (flag == FMT_END) { | |
| sim_debug(DEBUG_DATA, &dsk_dev, "eor\n"); | |
| return 1; | |
| } | |
| break; | |
| case DVCY: /* Verify Cyl */ | |
| if (flag == FMT_END) { | |
| /* Move to next track */ | |
| UNIT *base = | |
| (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr; | |
| if (((dtrack[u]+1) / disk_type[base->u4].track) != | |
| (dtrack[u] / disk_type[base->u4].track)) { | |
| sim_debug(DEBUG_DATA, &dsk_dev, "eor\n"); | |
| return 1; | |
| } | |
| } | |
| break; | |
| case DVSR: /* Verify single record */ | |
| if (flag != FMT_DATA) { | |
| sim_debug(DEBUG_DATA, &dsk_dev, "eor\n"); | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| /* Convert BCD track address to binary address */ | |
| int | |
| bcd_to_track(uint32 addr) | |
| { | |
| int trk = 0; | |
| int i; | |
| for (i = 28; i >= 16; i -= 4) { | |
| trk = (trk * 10) + ((addr >> i) & 0xf); | |
| } | |
| return trk; | |
| } | |
| /* Boot disk. Build boot card loader in memory and transfer to it */ | |
| t_stat | |
| dsk_boot(int unit_num, DEVICE * dptr) | |
| { | |
| #ifdef I7090 | |
| UNIT *uptr = &dptr->units[unit_num]; | |
| int chan = UNIT_G_CHAN(uptr->flags) - 1; | |
| int sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; | |
| int dev = uptr->u3 & 0xff; | |
| int msk = (chan / 2) | ((chan & 1) << 11); | |
| extern uint16 IC; | |
| if ((uptr->flags & UNIT_ATT) == 0) | |
| return SCPE_UNATT; /* attached? */ | |
| if (dev == 0) | |
| dev = 012; | |
| if (uptr->flags & CTSS_BOOT) { | |
| /* Build CTSS boot program in memory */ | |
| /* Read first cylinder into B-Core */ | |
| M[0] = 0377777000100LL; /* IORT BOTTOM,,-1 */ | |
| M[1] = 0006000000001LL; /* TCOA * */ | |
| M[2] = 0007400400100LL; /* START TSX ENTER,4 */ | |
| M[0100] = 0076000000350LL; /* ENTER RICU */ | |
| M[0100] |= (chan + 1) << 9; | |
| M[0101] = 0054000000120LL; /* RSCU READ */ | |
| M[0101] |= ((t_uint64) (msk)) << 24; | |
| M[0102] = 0006000000102LL; /* TCOU * */ | |
| M[0102] |= ((t_uint64) (chan)) << 24; | |
| M[0103] = 0476100000042LL; /* SEB */ | |
| M[0104] = 0450000000000LL; /* CAL 0 */ | |
| M[0105] = 0036100477777LL; /* ACL 32767,4 */ | |
| M[0106] = 0200001400105LL; /* TIX *-1,4,1 */ | |
| M[0107] = 0476100000041LL; /* SEA */ | |
| M[0110] = 0032200000131LL; /* ERA CHKSUM */ | |
| M[0111] = 0450100000046LL; /* ORA ULOC */ | |
| M[0112] = 0010000000132LL; /* TZE EXIT */ | |
| M[0113] = 0000000000002LL; /* HTR START */ | |
| M[0114] = 0101212001212LL; | |
| M[0114] |= ((t_uint64) (dev)) << 12; | |
| M[0115] = 0121212121212LL; | |
| M[0116] = 0100512001212LL; | |
| M[0116] |= ((t_uint64) (dev)) << 12; | |
| M[0117] = 0121267671212LL; | |
| M[0120] = 0700000000004LL; /* READ SMS 4 */ | |
| M[0120] |= sel; | |
| M[0121] = 0200000000114LL; /* CTL SEEK */ | |
| M[0122] = 0500000200122LL; /* TCM *,,0 */ | |
| M[0123] = 0200000200116LL; /* CTLR CYLOP */ | |
| M[0124] = 0400007000125LL; /* CPYP *+1,,N */ | |
| IC = 02; | |
| } else { | |
| /* Build IBSYS Boot program in memory */ | |
| M[0] = 0000025000101LL; /* IOCD RSCQ,,21 */ | |
| M[1] = 0006000000001LL; /* TCOA * */ | |
| M[2] = 0002000000101LL; /* TRA RSCQ */ | |
| M[0101] = 0054000000115LL; /* RSCQ RSCC SMSQ Mod */ | |
| M[0101] |= ((t_uint64) (msk)) << 24; | |
| M[0102] = 0064400000000LL; /* SCDQ SCDC 0 Mod */ | |
| M[0102] |= ((t_uint64) (msk)) << 24; | |
| M[0103] = 0044100000000LL; /* LDI 0 */ | |
| M[0104] = 0405400007100LL; /* LFT 7100 */ | |
| M[0105] = 0002000000110LL; /* TRA *+3 */ | |
| M[0106] = 0006000000102LL; /* TCOQ TCOC SCDQ Mod */ | |
| M[0106] |= ((t_uint64) (chan)) << 24; | |
| M[0107] = 0002000000003LL; /* TRA 3 Enter IBSYS */ | |
| M[0110] = 0076000000350LL; /* RICQ RICC ** Mod */ | |
| M[0110] |= (chan + 1) << 9; | |
| M[0111] = 0500512001212LL; /*LDVCY DVCY Mod */ | |
| M[0111] |= ((t_uint64) (dev)) << 12; | |
| M[0112] = 0121222440000LL; /* * */ | |
| M[0113] = 0501212001212LL; /*LDSEK DSEEK Mod */ | |
| M[0113] |= ((t_uint64) (dev)) << 12; | |
| M[0114] = 0121200000000LL; /* * */ | |
| M[0115] = 0700000000016LL; /* SMSQ SMS 14 */ | |
| M[0115] |= sel; | |
| M[0116] = 0200000000113LL; /* CTL LDSEK */ | |
| M[0117] = 0500000200117LL; /* TCM *,,, */ | |
| M[0120] = 0200000200111LL; /* CTLR LDVCY */ | |
| M[0121] = 0400001000122LL; /* CPYP *+1,,1 */ | |
| M[0122] = 0000000000122LL; /* WTR * */ | |
| M[0123] = 0100000000121LL; /* TCH *-2 */ | |
| M[0124] = 0500000000000LL; /* CPYD 0,,0 */ | |
| M[0125] = 0340000000125LL; /* TWT * */ | |
| IC = 0101; | |
| } | |
| return SCPE_OK; | |
| #else | |
| return SCPE_NOFNC; | |
| #endif | |
| } | |
| void | |
| dsk_ini(UNIT * uptr, t_bool f) | |
| { | |
| uptr->u5 = 0; | |
| } | |
| t_stat | |
| dsk_reset(DEVICE * dptr) | |
| { | |
| int i; | |
| int t; | |
| for (i = 0; i < NUM_CHAN; i++) { | |
| sense[i * 2] = sense[i * 2 + 1] = STAT_SIXBIT; | |
| sense_unit[i * 2] = sense_unit[i * 2 + 1] = 0; | |
| } | |
| for (i = 0; i < NUM_DEVS_DSK; i++) { | |
| dtrack[i] = 077777; | |
| fmt_cyl[i] = 077777; | |
| arm_cyl[i] = 0; | |
| dtrack[i + NUM_DEVS_DSK] = 077777; | |
| fmt_cyl[i + NUM_DEVS_DSK] = 077777; | |
| arm_cyl[i + NUM_DEVS_DSK] = 0; | |
| dtrack[i + (NUM_DEVS_DSK * 2)] = 077777; | |
| fmt_cyl[i + (NUM_DEVS_DSK * 2)] = 077777; | |
| arm_cyl[i + (NUM_DEVS_DSK * 2)] = 0; | |
| dtrack[i + (NUM_DEVS_DSK * 3)] = 077777; | |
| fmt_cyl[i + (NUM_DEVS_DSK * 3)] = 077777; | |
| arm_cyl[i + (NUM_DEVS_DSK * 3)] = 0; | |
| t = dptr->units[i].u4; | |
| /* Fill in max capacity */ | |
| dptr->units[i + NUM_DEVS_DSK].u3 = 0xff; | |
| dptr->units[i + (NUM_DEVS_DSK * 2)].u3 = 0xff; | |
| dptr->units[i + (NUM_DEVS_DSK * 3)].u3 = 0xff; | |
| dptr->units[i].capac = disk_type[t].mods * disk_type[t].bpt * | |
| disk_type[t].arms * disk_type[t].track * disk_type[t].cyl; | |
| if (disk_type[t].arms > 1) | |
| dptr->units[i + NUM_DEVS_DSK].u3 = | |
| 0x10 | dptr->units[i].u3 | (i << 8); | |
| if (disk_type[t].mods > 1) { | |
| dptr->units[i + (NUM_DEVS_DSK * 2)].u3 = (i<<8) | (dptr->units[i].u3 + 1); | |
| if (disk_type[t].arms > 1) | |
| dptr->units[i + (NUM_DEVS_DSK * 3)].u3 = (i<<8) | 0x10 | | |
| (dptr->units[i].u3 + 1); | |
| } | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Disk option setting commands */ | |
| t_stat | |
| dsk_set_type(UNIT * uptr, int32 val, CONST char *cptr, void *desc) | |
| { | |
| int i, u; | |
| if (cptr == NULL) | |
| return SCPE_ARG; | |
| if (uptr == NULL) | |
| return SCPE_IERR; | |
| if (uptr->flags & UNIT_ATT) | |
| return SCPE_ALATT; | |
| for (i = 0; disk_type[i].name != 0; i++) { | |
| if (strcmp(disk_type[i].name, cptr) == 0) { | |
| uptr->u4 = i; | |
| uptr[NUM_DEVS_DSK].u4 = i; | |
| uptr[NUM_DEVS_DSK].u3 = 0xff; | |
| uptr[NUM_DEVS_DSK * 2].u4 = i; | |
| uptr[NUM_DEVS_DSK * 2].u3 = 0xff; | |
| uptr[NUM_DEVS_DSK * 3].u4 = i; | |
| uptr[NUM_DEVS_DSK * 3].u3 = 0xff; | |
| uptr->capac = disk_type[i].mods * disk_type[i].bpt * | |
| disk_type[i].arms * disk_type[i].track * disk_type[i].cyl; | |
| /* Adjust other disks in case changed number arms/modules */ | |
| u = uptr->u3 & 0xf0f; | |
| if (disk_type[uptr->u4].arms > 1) | |
| uptr[NUM_DEVS_DSK].u3 = u | 0x10; | |
| if (disk_type[uptr->u4].mods > 1) { | |
| uptr[NUM_DEVS_DSK * 2].u3 = u + 1; | |
| if (disk_type[uptr->u4].arms > 1) | |
| uptr[NUM_DEVS_DSK * 3].u3 = (u + 1) | 0x10; | |
| } | |
| return SCPE_OK; | |
| } | |
| } | |
| return SCPE_ARG; | |
| } | |
| t_stat | |
| dsk_get_type(FILE * st, UNIT * uptr, int32 v, CONST void *desc) | |
| { | |
| if (uptr == NULL) | |
| return SCPE_IERR; | |
| fputs(disk_type[uptr->u4].name, st); | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| dsk_set_module(UNIT * uptr, int32 val, CONST char *cptr, void *desc) | |
| { | |
| int u; | |
| if (cptr == NULL) | |
| return SCPE_ARG; | |
| if (uptr == NULL) | |
| return SCPE_IERR; | |
| if (uptr->flags & UNIT_ATT) | |
| return SCPE_ALATT; | |
| if (*cptr == '\0' || cptr[1] != '\0') | |
| return SCPE_ARG; | |
| if (*cptr < '0' || *cptr > '8') | |
| return SCPE_ARG; | |
| if (*cptr & 1) | |
| return SCPE_ARG; | |
| u = uptr->u3 & 0xf00; | |
| uptr->u3 = u | (*cptr - '0'); | |
| uptr[NUM_DEVS_DSK].u3 = 0xff; | |
| uptr[NUM_DEVS_DSK * 2].u3 = 0xff; | |
| uptr[NUM_DEVS_DSK * 3].u3 = 0xff; | |
| if (disk_type[uptr->u4].arms > 1) | |
| uptr[NUM_DEVS_DSK].u3 = u | 0x10 | (*cptr - '0'); | |
| if (disk_type[uptr->u4].mods > 1) { | |
| uptr[NUM_DEVS_DSK * 2].u3 = u | ((*cptr - '0') + 1); | |
| if (disk_type[uptr->u4].arms > 1) | |
| uptr[NUM_DEVS_DSK * 3].u3 = u | 0x10 | ((*cptr - '0') + 1); | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| dsk_get_module(FILE * st, UNIT * uptr, int32 v, CONST void *desc) | |
| { | |
| if (uptr == NULL) | |
| return SCPE_IERR; | |
| fprintf(st, "Module=%d", uptr->u3 & 0xff); | |
| return SCPE_OK; | |
| } | |
| t_stat dsk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, | |
| const char *cptr) | |
| { | |
| int i; | |
| fprintf (st, "IBM 7631 Disk File Controller\n\n"); | |
| fprintf (st, "The IBM 7631 Disk File Controller supports several types of "); | |
| fprintf (st, "disk drives and\ndrums. The drive must be formatted for use "); | |
| fprintf (st, "of the system. This is handled by\nutilities provided by the "); | |
| fprintf (st, "operating system. This will write a special format\ntrack.\n\n"); | |
| fprintf (st, "Use:\n\n"); | |
| fprintf (st, " sim> SET DKn TYPE=type\n"); | |
| fprintf (st, "Type can be: "); | |
| for (i = 0; disk_type[i].name != 0; i++) { | |
| fprintf(st, "%s", disk_type[i].name); | |
| if (disk_type[i+1].name != 0) | |
| fprintf(st, ", "); | |
| } | |
| fprintf (st, ".\nEach drive has the following storage capacity:\n\n"); | |
| for (i = 0; disk_type[i].name != 0; i++) { | |
| int32 size = disk_type[i].mods * disk_type[i].bpt * | |
| disk_type[i].arms * disk_type[i].track * disk_type[i].cyl; | |
| char sm = 'K'; | |
| size /= 1024; | |
| if (size > 5000) { | |
| size /= 1024; | |
| sm = 'M'; | |
| } | |
| fprintf(st, " %-8s %4d%cB %d modules\n", disk_type[i].name, size, sm, | |
| disk_type[i].mods); | |
| } | |
| fprintf (st, "\nTo enable formating the format switch must be set "); | |
| fprintf (st, "to enable, and the Home\nAddress 2 write must be enabled.\n"); | |
| fprintf (st, "To do this:\n\n"); | |
| fprintf (st, " sim> SET DKn FORMAT HA2\n\n"); | |
| fprintf (st, "To prevent accidental formating of the drive use:\n\n"); | |
| fprintf (st, " sim> SET DKn NOFORMAT NOHA2\n\n"); | |
| help_set_chan_type(st, dptr, "IBM 7631 Disk File"); | |
| fprint_set_help (st, dptr); | |
| fprint_show_help (st, dptr); | |
| return SCPE_OK; | |
| } | |
| const char *dsk_description (DEVICE *dptr) | |
| { | |
| return "IBM 7631 disk file controller"; | |
| } | |
| #endif |