/* s100_icom.c: iCOM FD3712/FD3812 Flexible Disk System Created by Patrick Linstruth (patrick@deltecent.com) 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 PETER SCHORN 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 Patrick Linstruth shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Patrick Linstruth. */ /* These functions support simulated iCOM FD3712 and FD3812 floppy disk systems. The FD3712 supports IBM Diskette type 1 single-density and the FD3812 also supports IBM Diskette type 2D double-density. The density of the media attached is determined by file size. If a read or write is made where the media doesn't match the density setting of the controller, a CRC error status will be set. The interface board provides 2 I/O ports: Command Register Port C0 Output Data In Register Port C0 Input Data Out Register Port C1 Output +---------------------------------------------------------+ | | | COMMAND SET | | | +---------------------------------------------------------+ | COMMAND | 7 6 5 4 3 2 1 0 | BUSY | HEX CODE | +---------------------------------------------------------+ | EXAMINE STATUS | 0 0 0 0 0 0 0 0 | No | 00 | | READ | 0 0 0 0 0 0 1 1 | Yes | 03 | | WRITE | 0 0 0 0 0 1 0 1 | Yes | 05 | | READ CRC | 0 0 0 0 0 1 1 1 | Yes | 07 | | SEEK | 0 0 0 0 1 0 0 1 | Yes | 09 | | CLEAR ERROR FLAGS | 0 0 0 0 1 0 1 1 | No | 0B | | SEEK TRACK ZERO | 0 0 0 0 1 1 0 1 | Yes | 0D | | WRITE DEL DATA MARK | 0 0 0 0 1 1 1 1 | Yes | 0F | | LOAD TRACK ADDRESS | 0 0 0 1 0 0 0 1 | No | 11 | | LOAD UNIT/SECTOR | 0 0 1 0 0 0 0 1 | No | 21 | | LOAD WRITE BUFFER* | 0 0 1 1 0 0 0 1 | No | 30 | | LOAD WRITE BUFFER+ | 0 0 1 1 0 0 0 1 | No | 31 | | EXAMINE READ BUFFER | 0 1 0 0 0 0 0 0 | No | 40 | | SHIFT READ BUFFER | 0 1 0 0 0 0 0 1 | No | 41 | | CLEAR CONTROLLER | 1 0 0 0 0 0 0 1 | No | 81 | | LOAD CONFIGURATION* | 0 0 0 1 1 0 0 1 | No | 15 | +---------------------------------------------------------+ | + FD3712 Only | | * FD3812 Only | +---------------------------------------------------------+ +---------------------------------------------------------------+ | | | DISK STATUS BITS | | | +---------------------------------------------------------------+ | BIT | STATUS SIGNAL | DESCRIPTION | +---------------------------------------------------------------+ | 7 | DELETED DATA MARK | The simulator does not implement | | | | this bit. | +---------------------------------------------------------------+ | 6 | MEDIA STATUS | This bit is always set. | +---------------------------------------------------------------+ | 5 | DRIVE FAIL | This bit is set if any if a drive | | | | is not attached using the | | | | "ATTACH" command or there is a | | | | problem reading from or writing | | | | to the attached file. | +---------------------------------------------------------------+ | 4 | WRITE PROTECT | This bit is set if the selected | | | | drive contains a write protected | | | | diskette. This condition should | | | | not be tested if the selected | | | | drive has a "DRIVE FAIL" status. | | | | Use "SET ICOM WRTPROT" to write | | | | protect an attached diskette and | | | | "SET ICOM WRTENB" to enable | | | | writing. | +---------------------------------------------------------------+ | 3 | CRC ERROR | This bit is set when an error has | | | | occurred during the previous | | | | command. This bit must be tested | | | | after all read, write, and seek | | | | operations. The simulator does | | | | not implement this bit. | +---------------------------------------------------------------+ | 2 | UNIT SELECT MSB | Bits 2 and 1 contain the address | +---------------------------| of the drive currently being | | 1 | UNIT SELECT LSB | selected by the controller. | +---------------------------------------------------------------+ | 0 | BUSY | This bit is set when a read, | | | | write, seek command is sent to | | | | the controller. | +---------------------------------------------------------------+ B = Memory Size - 16K 32K: B = 32K - 16K = 16K = 04000H 48K: B = 48K = 16K = 32K = 08000H 62K: B = 62K = 16K = 46K = 0B800H 64K: B = 64K = 16K = 48K = 0C000H +----------------------------------------------------------------------+ | | | CP/M 1.41 Single Density Disk Layout | | | +----------------------------------------------------------------------+ | Track | Sector | Image Offset | Memory Address | Module | +----------------------------------------------------------------------+ | 00 | 01 | 0000-007FH | 0080H | SD Disk Boot Loader | +----------------------------------------------------------------------+ | 00 | 02-17 | 0080-087FH | 2900H+B | CCP | +----------------------------------------------------------------------+ | 00 | 18-26 | 0880-0CFFH | 3100H+B | BDOS | | 01 | 01-17 | 0D00-157FH | 3580H+B | BDOS | +----------------------------------------------------------------------+ | 01 | 18-21 | 1580-177FH | 3E00H+B | BIOS | +----------------------------------------------------------------------+ | 01 | 22-26 | | Not Used | +----------------------------------------------------------------------+ +----------------------------------------------------------------------+ | | | CP/M 1.41 Double Density Disk Layout | | | +----------------------------------------------------------------------+ | Track | Sector | Image Offset | Memory Address | Module | +----------------------------------------------------------------------+ | 00 | 01 | 0000-007FH | 0080H | DD Disk Boot Loader | +----------------------------------------------------------------------+ | 00 | 02-26 | | | Not Used | +----------------------------------------------------------------------+ | 01 | 01-09 | 0D00-14FFH | 2900H+B | CCP | +----------------------------------------------------------------------+ | 01 | 10-21 | 1500-21FFH | 3100H+B | BDOS | +----------------------------------------------------------------------+ | 01 | 22-23 | 2200-23FFH | 3E00H+B | BIOS | +----------------------------------------------------------------------+ | 01 | 24-26 | | | Not Used | +----------------------------------------------------------------------+ */ /* #define DBG_MSG */ #include "altairz80_defs.h" #include "sim_imd.h" #ifdef DBG_MSG #define DBG_PRINT(args) sim_printf args #else #define DBG_PRINT(args) #endif extern t_stat set_membase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); extern t_stat set_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, int32 (*routine)(const int32, const int32, const int32), const char* name, uint8 unmap); #define ICOM_MAX_DRIVES 4 #define ICOM_SD_SECTOR_LEN 128 #define ICOM_DD_SECTOR_LEN 256 #define ICOM_SPT 26 #define ICOM_TRACKS 77 #define ICOM_SD_CAPACITY (256256) /* Default iCOM Single Density Disk Capacity */ #define ICOM_DD_CAPACITY (509184) /* Default iCOM Double Density Disk Capacity */ #define ICOM_IO_BASE 0xc0 #define ICOM_IO_SIZE 2 #define ICOM_PROM_BASE 0xf000 #define ICOM_PROM_SIZE 1024 #define ICOM_PROM_MASK (ICOM_PROM_SIZE-1) #define ICOM_MEM_BASE 0xf400 #define ICOM_MEM_SIZE 256 /* Must be on a page boundary */ #define ICOM_MEM_MASK (ICOM_MEM_SIZE-1) static uint8 icom_mem[ICOM_MEM_SIZE]; /* iCOM PROMs are 1024 bytes */ static uint8 icom_3712_prom[ICOM_PROM_SIZE] = { 0xc3, 0x73, 0xf0, 0x20, 0x41, 0x4c, 0x54, 0x41, 0x49, 0x52, 0x43, 0x20, 0xc3, 0x85, 0xf0, 0x15, 0xc3, 0xa6, 0xf0, 0xc3, 0xc7, 0xf0, 0xc3, 0x06, 0xf4, 0xc3, 0x09, 0xf4, 0xc3, 0x0c, 0xf4, 0xc3, 0x0f, 0xf4, 0xc3, 0x12, 0xf4, 0xc3, 0x15, 0xf4, 0xc3, 0x6b, 0xf1, 0xc3, 0x73, 0xf1, 0xc3, 0x6e, 0xf1, 0xc3, 0x7d, 0xf1, 0xc3, 0x82, 0xf1, 0xc3, 0x88, 0xf1, 0xc3, 0xc5, 0xf1, 0xc9, 0x00, 0x00, 0xc3, 0x64, 0xf1, 0xc3, 0x5a, 0xf2, 0x20, 0x33, 0x37, 0x31, 0x32, 0x2d, 0x56, 0x32, 0x31, 0x20, 0x28, 0x43, 0x29, 0x20, 0x4c, 0x49, 0x46, 0x45, 0x42, 0x4f, 0x41, 0x54, 0x20, 0x41, 0x53, 0x53, 0x4f, 0x43, 0x49, 0x41, 0x54, 0x45, 0x53, 0x20, 0x31, 0x39, 0x37, 0x39, 0x20, 0x21, 0xe0, 0xf3, 0xc3, 0x7f, 0xf0, 0x21, 0xf0, 0xf3, 0xc3, 0x7f, 0xf0, 0x21, 0x68, 0xf3, 0xc3, 0x7f, 0xf0, 0x31, 0x80, 0x00, 0xcd, 0x8f, 0xf2, 0x31, 0x80, 0x00, 0xcd, 0x5a, 0xf2, 0x0e, 0x00, 0xcd, 0x6e, 0xf1, 0x01, 0x80, 0x00, 0xcd, 0x82, 0xf1, 0xcd, 0x88, 0xf1, 0xc2, 0x88, 0xf0, 0x21, 0x00, 0xf4, 0xeb, 0x21, 0x10, 0xf0, 0xc3, 0x80, 0x00, 0x22, 0x40, 0xf4, 0x11, 0xf0, 0xff, 0x19, 0x11, 0x20, 0xf4, 0x06, 0x10, 0xcd, 0x86, 0xf2, 0x11, 0x80, 0xff, 0x19, 0xaf, 0x32, 0x48, 0xf4, 0xcd, 0x4f, 0xf1, 0xaf, 0x32, 0x04, 0x00, 0xc3, 0x28, 0xf1, 0x31, 0x00, 0x01, 0xcd, 0x5a, 0xf2, 0x0e, 0x00, 0xcd, 0x6e, 0xf1, 0x2a, 0x40, 0xf4, 0x11, 0x00, 0xeb, 0x19, 0x24, 0x3e, 0x04, 0xcd, 0xf7, 0xf0, 0x0e, 0x01, 0xcd, 0x6e, 0xf1, 0x2a, 0x40, 0xf4, 0x11, 0x00, 0xeb, 0x19, 0x11, 0x80, 0x0c, 0x19, 0x3e, 0x01, 0xcd, 0xf7, 0xf0, 0xc3, 0x28, 0xf1, 0x32, 0x32, 0xf4, 0x22, 0x33, 0xf4, 0x3a, 0x41, 0xf4, 0x3d, 0xbc, 0xda, 0x0b, 0xf1, 0xcd, 0x88, 0xf1, 0xc2, 0xc7, 0xf0, 0x2a, 0x33, 0xf4, 0x11, 0x80, 0x01, 0x19, 0x3a, 0x32, 0xf4, 0xc6, 0x03, 0xfe, 0x1b, 0xda, 0xf7, 0xf0, 0xd6, 0x1a, 0x11, 0x00, 0xf3, 0x19, 0xfe, 0x01, 0xc2, 0xf7, 0xf0, 0xc9, 0x01, 0x80, 0x00, 0xcd, 0x82, 0xf1, 0x3e, 0xc3, 0x32, 0x00, 0x00, 0x32, 0x05, 0x00, 0x2a, 0x40, 0xf4, 0x23, 0x23, 0x23, 0x22, 0x01, 0x00, 0x11, 0x03, 0xf3, 0x19, 0x22, 0x06, 0x00, 0x3a, 0x04, 0x00, 0x4f, 0x11, 0xfa, 0xf7, 0x19, 0xe9, 0x7e, 0xb7, 0xc8, 0x4e, 0x23, 0xe5, 0xcd, 0x5c, 0xf1, 0xe1, 0xc3, 0x4f, 0xf1, 0x2a, 0x40, 0xf4, 0x11, 0x0c, 0x00, 0x19, 0xe9, 0x21, 0x00, 0xf4, 0x06, 0x00, 0x09, 0xc9, 0xc3, 0x67, 0xf2, 0x79, 0x32, 0x31, 0xf4, 0xc9, 0x79, 0x32, 0x30, 0xf4, 0x3e, 0xff, 0x32, 0x27, 0xf4, 0xc9, 0x79, 0x32, 0x32, 0xf4, 0xc9, 0x60, 0x69, 0x22, 0x33, 0xf4, 0xc9, 0xcd, 0x0a, 0xf2, 0xc2, 0x06, 0xf2, 0x0e, 0x0a, 0x3e, 0x03, 0xcd, 0x71, 0xf2, 0xe6, 0x28, 0xca, 0xa4, 0xf1, 0xcd, 0x7e, 0xf2, 0x0d, 0xc2, 0x90, 0xf1, 0xc3, 0x06, 0xf2, 0x2a, 0x33, 0xf4, 0x0e, 0x80, 0x3e, 0x40, 0xd3, 0xc0, 0xdb, 0xc0, 0x77, 0x23, 0xaf, 0xd3, 0xc0, 0x0d, 0x3e, 0x41, 0xd3, 0xc0, 0xdb, 0xc0, 0x77, 0x23, 0xaf, 0xd3, 0xc0, 0x0d, 0xc2, 0xb5, 0xf1, 0xc9, 0xcd, 0x0a, 0xf2, 0xc2, 0x06, 0xf2, 0x2a, 0x33, 0xf4, 0x0e, 0x80, 0x7e, 0xd3, 0xc1, 0x3e, 0x31, 0xd3, 0xc0, 0xaf, 0xd3, 0xc0, 0x23, 0x0d, 0xc2, 0xd0, 0xf1, 0x0e, 0x0a, 0x3e, 0x05, 0xcd, 0x71, 0xf2, 0xe6, 0x20, 0xca, 0xf1, 0xf1, 0xcd, 0x7e, 0xf2, 0xc3, 0x06, 0xf2, 0x3a, 0x2f, 0xf4, 0xe6, 0x40, 0xc8, 0x3e, 0x07, 0xcd, 0x71, 0xf2, 0xe6, 0x28, 0xc8, 0xcd, 0x7e, 0xf2, 0x0d, 0xc2, 0xe1, 0xf1, 0x3e, 0x01, 0xb7, 0xc9, 0xaf, 0xd3, 0xc1, 0x3e, 0x15, 0xcd, 0x80, 0xf2, 0xcd, 0x19, 0xf2, 0xcd, 0x2d, 0xf2, 0xc9, 0x3a, 0x30, 0xf4, 0xe6, 0x03, 0x0f, 0x0f, 0x4f, 0x3a, 0x32, 0xf4, 0xb1, 0xd3, 0xc1, 0x3e, 0x21, 0xcd, 0x80, 0xf2, 0xc9, 0x0e, 0x02, 0x3a, 0x31, 0xf4, 0x21, 0x27, 0xf4, 0xbe, 0xc8, 0x77, 0x3a, 0x31, 0xf4, 0xd3, 0xc1, 0x3e, 0x11, 0xcd, 0x80, 0xf2, 0x3e, 0x09, 0xcd, 0x71, 0xf2, 0xe6, 0x28, 0xc8, 0xcd, 0x7e, 0xf2, 0x36, 0xff, 0x0d, 0xc2, 0x2d, 0xf2, 0xcd, 0x62, 0xf2, 0x3e, 0x02, 0xb7, 0xc9, 0xaf, 0x32, 0x30, 0xf4, 0x3c, 0x32, 0x32, 0xf4, 0x3e, 0x81, 0xcd, 0x80, 0xf2, 0xcd, 0x19, 0xf2, 0x3e, 0xff, 0x32, 0x27, 0xf4, 0x3e, 0x0d, 0xcd, 0x80, 0xf2, 0xdb, 0xc0, 0xe6, 0x01, 0xc2, 0x74, 0xf2, 0xdb, 0xc0, 0xc9, 0x3e, 0x0b, 0xd3, 0xc0, 0xaf, 0xd3, 0xc0, 0xc9, 0x7e, 0x12, 0x23, 0x13, 0x05, 0xc2, 0x86, 0xf2, 0xc9, 0x11, 0x00, 0xf4, 0x06, 0x08, 0x3e, 0xc3, 0x12, 0x13, 0x7e, 0x12, 0x23, 0x13, 0x7e, 0x12, 0x23, 0x13, 0x05, 0xc2, 0x94, 0xf2, 0xc9, 0x3e, 0x03, 0xd3, 0x10, 0x3e, 0x11, 0xd3, 0x10, 0xc9, 0xdb, 0x10, 0xe6, 0x01, 0x3e, 0x00, 0xc8, 0x2f, 0xc9, 0xdb, 0x10, 0xe6, 0x01, 0xca, 0xb7, 0xf2, 0xdb, 0x11, 0xe6, 0x7f, 0xca, 0xb7, 0xf2, 0xc9, 0xdb, 0x10, 0xe6, 0x02, 0xca, 0xc6, 0xf2, 0x79, 0xd3, 0x11, 0xc9, 0xc9, 0xc9, 0xdb, 0x00, 0xe6, 0x01, 0x3e, 0x00, 0xc0, 0x2f, 0xc9, 0xdb, 0x00, 0xe6, 0x01, 0xc2, 0xdc, 0xf2, 0xdb, 0x01, 0xe6, 0x7f, 0xca, 0xdc, 0xf2, 0xc9, 0xdb, 0x00, 0xe6, 0x80, 0xc2, 0xeb, 0xf2, 0x79, 0xd3, 0x01, 0xc9, 0x3a, 0x48, 0xf4, 0xb7, 0xc2, 0x0c, 0xf3, 0x3e, 0x11, 0xd3, 0x03, 0xaf, 0xd3, 0x02, 0x32, 0x47, 0xf4, 0x3e, 0x84, 0x32, 0x48, 0xf4, 0x79, 0xfe, 0x0a, 0xc2, 0x1a, 0xf3, 0x32, 0x49, 0xf4, 0x3a, 0x47, 0xf4, 0xb7, 0xc8, 0x79, 0xfe, 0x08, 0xca, 0x4f, 0xf3, 0xfe, 0x09, 0xca, 0x5a, 0xf3, 0xfe, 0x0d, 0xca, 0x38, 0xf3, 0xd8, 0x3a, 0x47, 0xf4, 0x3c, 0xe5, 0x21, 0x48, 0xf4, 0xbe, 0xe1, 0xc2, 0x48, 0xf3, 0x3a, 0x47, 0xf4, 0xb7, 0xc2, 0x47, 0xf3, 0x3a, 0x49, 0xf4, 0xfe, 0x0d, 0xc8, 0x0e, 0x0a, 0xaf, 0x32, 0x47, 0xf4, 0x79, 0x32, 0x49, 0xf4, 0xdb, 0x02, 0xe6, 0x11, 0xca, 0x4f, 0xf3, 0x79, 0xd3, 0x03, 0xc9, 0x0e, 0x20, 0xcd, 0x0c, 0xf3, 0x3a, 0x47, 0xf4, 0xe6, 0x07, 0xc2, 0x5a, 0xf3, 0xc9, 0xa8, 0xf3, 0xd2, 0xf2, 0xa1, 0xf3, 0x78, 0xf3, 0x8e, 0xf3, 0xf6, 0xf2, 0x8e, 0xf3, 0x78, 0xf3, 0xcd, 0x84, 0xf3, 0xca, 0x78, 0xf3, 0x7e, 0xe6, 0x7f, 0x36, 0x00, 0xc9, 0x21, 0x4b, 0xf4, 0x7e, 0xb7, 0xcc, 0x1f, 0xc0, 0x77, 0xc9, 0x3a, 0x4a, 0xf4, 0xfe, 0x0d, 0xc2, 0x98, 0xf3, 0xb9, 0xc8, 0x79, 0x32, 0x4a, 0xf4, 0x41, 0xcd, 0x19, 0xc0, 0xc9, 0xcd, 0x84, 0xf3, 0xc8, 0x3e, 0xff, 0xc9, 0x21, 0x00, 0x00, 0x22, 0x4a, 0xf4, 0xc9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd1, 0xf2, 0xd2, 0xf2, 0xd3, 0xf2, 0xdc, 0xf2, 0xeb, 0xf2, 0xf6, 0xf2, 0xeb, 0xf2, 0xdc, 0xf2, 0xa5, 0xf2, 0xd2, 0xf2, 0xae, 0xf2, 0xb7, 0xf2, 0xc6, 0xf2, 0xf6, 0xf2, 0xc6, 0xf2, 0xb7, 0xf2, }; static uint8 icom_3812_prom[ICOM_PROM_SIZE] = { 0xc3, 0x46, 0xf0, 0x06, 0x80, 0x7e, 0x12, 0x23, 0x13, 0x05, 0xc2, 0x05, 0xf0, 0xc9, 0xff, 0x3a, 0xc3, 0x6d, 0xf0, 0xc3, 0x8a, 0xf0, 0x79, 0x32, 0x31, 0xf4, 0xc9, 0x79, 0x32, 0x32, 0xf4, 0xc9, 0x60, 0x69, 0x22, 0x33, 0xf4, 0xc9, 0xff, 0xff, 0xc3, 0x08, 0xf1, 0xc3, 0x14, 0xf1, 0xc3, 0x16, 0xf0, 0xc3, 0x1b, 0xf0, 0xc3, 0x20, 0xf0, 0xc3, 0x30, 0xf1, 0xc3, 0x7b, 0xf1, 0xc3, 0x21, 0xf1, 0xc3, 0x61, 0xf3, 0xc3, 0xa4, 0xf3, 0x31, 0x80, 0x00, 0xcd, 0xa4, 0xf3, 0x21, 0x00, 0x00, 0x22, 0x30, 0xf4, 0x0e, 0x01, 0xcd, 0x1b, 0xf0, 0x21, 0x80, 0x00, 0x22, 0x33, 0xf4, 0xcd, 0x30, 0xf1, 0xc2, 0x46, 0xf0, 0x21, 0x00, 0xf4, 0xeb, 0x21, 0x10, 0xf0, 0xc3, 0x80, 0x00, 0x22, 0x40, 0xf4, 0x11, 0xf0, 0xff, 0x19, 0x11, 0x20, 0xf4, 0x06, 0x10, 0xcd, 0x05, 0xf0, 0x11, 0x80, 0xff, 0x19, 0xcd, 0xdf, 0xf3, 0xaf, 0x32, 0x04, 0x00, 0xc3, 0xe1, 0xf0, 0x31, 0x00, 0x01, 0xcd, 0xa4, 0xf3, 0x21, 0x00, 0x01, 0x22, 0x30, 0xf4, 0x2a, 0x40, 0xf4, 0x11, 0x00, 0xeb, 0x19, 0x3e, 0x01, 0x4f, 0xc5, 0x32, 0x32, 0xf4, 0x22, 0x33, 0xf4, 0x7c, 0x2a, 0x40, 0xf4, 0xbc, 0xd2, 0xb5, 0xf0, 0xcd, 0x30, 0xf1, 0xc2, 0x8a, 0xf0, 0xc1, 0x79, 0x0f, 0x79, 0x2a, 0x33, 0xf4, 0xda, 0xc3, 0xf0, 0xc6, 0x04, 0x24, 0x24, 0x3c, 0x11, 0x80, 0x00, 0x19, 0xfe, 0x35, 0xda, 0xdc, 0xf0, 0xd6, 0x34, 0xfe, 0x03, 0x2a, 0x40, 0xf4, 0x11, 0x00, 0xec, 0x19, 0xca, 0xdc, 0xf0, 0x24, 0xfe, 0x01, 0xc2, 0x9f, 0xf0, 0x01, 0x80, 0x00, 0xcd, 0x20, 0xf0, 0x3e, 0xc3, 0x32, 0x00, 0x00, 0x32, 0x05, 0x00, 0x2a, 0x40, 0xf4, 0x23, 0x23, 0x23, 0x22, 0x01, 0x00, 0x11, 0x03, 0xf3, 0x19, 0x22, 0x06, 0x00, 0x3a, 0x04, 0x00, 0x4f, 0x11, 0xfa, 0xf7, 0x19, 0xe9, 0xcd, 0x21, 0xf1, 0x3a, 0x30, 0xf4, 0x32, 0x3d, 0xf4, 0xc3, 0xb6, 0xf3, 0x79, 0x32, 0x30, 0xf4, 0xcd, 0x21, 0xf1, 0x3e, 0xff, 0x32, 0x27, 0xf4, 0xc9, 0x3a, 0x39, 0xf4, 0x3c, 0xc8, 0xcd, 0x6f, 0xf2, 0xc5, 0xcd, 0xf2, 0xf1, 0xc1, 0xc9, 0x11, 0xcd, 0x6f, 0xf2, 0xcd, 0x57, 0xf3, 0xca, 0x5a, 0xf1, 0x21, 0x30, 0xf4, 0x11, 0x39, 0xf4, 0xcd, 0x2b, 0xf2, 0xc2, 0x4e, 0xf1, 0x1a, 0xbe, 0xc2, 0x4e, 0xf1, 0xcd, 0xf2, 0xf1, 0xc0, 0x21, 0x30, 0xf4, 0x11, 0x35, 0xf4, 0xcd, 0x2b, 0xf2, 0xca, 0x64, 0xf1, 0x21, 0x30, 0xf4, 0xcd, 0x22, 0xf2, 0xcd, 0x46, 0xf2, 0xc0, 0xcd, 0x57, 0xf3, 0xca, 0x71, 0xf1, 0x3a, 0x32, 0xf4, 0x3c, 0x0f, 0xe6, 0x80, 0x2a, 0x33, 0xf4, 0xeb, 0xcd, 0x9d, 0xf2, 0xc8, 0xc3, 0x11, 0xcd, 0x6f, 0xf2, 0xcd, 0x57, 0xf3, 0x2a, 0x33, 0xf4, 0xca, 0xb0, 0xf1, 0x21, 0x30, 0xf4, 0x11, 0x39, 0xf4, 0xcd, 0x2b, 0xf2, 0xc2, 0xbf, 0xf1, 0x1a, 0xbe, 0xca, 0xc3, 0xf1, 0x3e, 0xff, 0x32, 0x39, 0xf4, 0x2a, 0x33, 0xf4, 0xe5, 0x2a, 0x2c, 0xf4, 0x3a, 0x3b, 0xf4, 0x0f, 0xda, 0xac, 0xf1, 0xe3, 0xcd, 0xf7, 0xf2, 0xe1, 0xcd, 0xf7, 0xf2, 0x21, 0x30, 0xf4, 0xcd, 0x22, 0xf2, 0xcd, 0x63, 0xf2, 0xc9, 0x2f, 0xfe, 0xcd, 0xf2, 0xf1, 0xc0, 0x21, 0x30, 0xf4, 0x11, 0x39, 0xf4, 0xcd, 0x25, 0xf2, 0x2a, 0x2c, 0xf4, 0xeb, 0x2a, 0x33, 0xf4, 0xcd, 0x03, 0xf0, 0x2a, 0x40, 0xf4, 0x11, 0x09, 0xf5, 0x19, 0x11, 0xf2, 0xf1, 0xd5, 0x7e, 0xfe, 0x10, 0xc8, 0xfe, 0x13, 0xc8, 0xfe, 0x16, 0xc8, 0xfe, 0x17, 0xc8, 0xd1, 0xaf, 0xc9, 0x0e, 0x21, 0x39, 0xf4, 0x7e, 0x3c, 0xc8, 0xcd, 0x22, 0xf2, 0x3e, 0xff, 0x32, 0x39, 0xf4, 0xcd, 0x46, 0xf2, 0xc0, 0x3a, 0x3b, 0xf4, 0x0f, 0xd2, 0x18, 0xf2, 0xcd, 0xf4, 0xf2, 0xcd, 0xb8, 0xf2, 0xcd, 0x0a, 0xf3, 0xca, 0x1e, 0xf2, 0x11, 0xcd, 0x0a, 0xf3, 0xcd, 0xf4, 0xf2, 0xcd, 0x63, 0xf2, 0xc9, 0x11, 0x3d, 0xf4, 0x06, 0x03, 0xc3, 0x05, 0xf0, 0x06, 0x1a, 0xb7, 0xf8, 0xbe, 0xc0, 0x23, 0x13, 0x1a, 0xbe, 0xc0, 0x23, 0x13, 0x7e, 0x3c, 0x0f, 0xe6, 0x7f, 0x4f, 0x1a, 0x3c, 0x0f, 0xe6, 0x7f, 0xb9, 0xc9, 0xfe, 0x21, 0x3e, 0xff, 0x32, 0x35, 0xf4, 0xaf, 0x32, 0x38, 0xf4, 0xcd, 0x82, 0xf2, 0x3e, 0x01, 0xc0, 0x21, 0x3d, 0xf4, 0x11, 0x35, 0xf4, 0xcd, 0x25, 0xf2, 0x78, 0xc8, 0xc3, 0x7a, 0xf1, 0x3e, 0xff, 0x32, 0x35, 0xf4, 0xcd, 0xcf, 0xf2, 0xc8, 0x3e, 0x01, 0xc9, 0xd1, 0x21, 0x00, 0x00, 0x39, 0x31, 0x80, 0xf4, 0xe5, 0x21, 0x7e, 0xf2, 0xe5, 0xeb, 0xe9, 0xe1, 0xf9, 0xc9, 0x21, 0xcd, 0x28, 0xf3, 0xc2, 0x99, 0xf2, 0x0e, 0x05, 0x3e, 0x03, 0xcd, 0xca, 0xf3, 0xe6, 0x08, 0xc8, 0xcd, 0xd7, 0xf3, 0x0d, 0xc2, 0x8a, 0xf2, 0x3e, 0x01, 0xb7, 0xc9, 0x21, 0x38, 0xf4, 0xbe, 0xc4, 0xb8, 0xf2, 0x06, 0x80, 0x3e, 0x40, 0xd3, 0xc0, 0xdb, 0xc0, 0x12, 0x13, 0x34, 0x05, 0xc2, 0xaa, 0xf2, 0xaf, 0xd3, 0xc0, 0xc8, 0x11, 0x06, 0x80, 0x21, 0x38, 0xf4, 0x3e, 0x40, 0xd3, 0xc0, 0xdb, 0xc0, 0x34, 0x05, 0xc2, 0xc1, 0xf2, 0x78, 0xd3, 0xc0, 0xc8, 0xcd, 0x17, 0xf2, 0xcd, 0x28, 0xf3, 0xc2, 0x99, 0xf2, 0x0e, 0x05, 0x3e, 0x05, 0xcd, 0xca, 0xf3, 0x3a, 0x2f, 0xf4, 0xe6, 0x40, 0xc8, 0x3e, 0x07, 0xcd, 0xca, 0xf3, 0xe6, 0x08, 0xc8, 0xcd, 0xd7, 0xf3, 0x0d, 0xc2, 0xd7, 0xf2, 0xc3, 0x99, 0xf2, 0x2a, 0x2c, 0xf4, 0x06, 0x80, 0x3e, 0x30, 0xd3, 0xc0, 0x7e, 0xd3, 0xc1, 0x23, 0x05, 0xc2, 0xfd, 0xf2, 0x78, 0xd3, 0xc0, 0xc8, 0x0e, 0x06, 0x80, 0x3e, 0x40, 0xd3, 0xc0, 0xdb, 0xc0, 0x4f, 0xaf, 0xd3, 0xc0, 0x3e, 0x30, 0xd3, 0xc0, 0x79, 0xd3, 0xc1, 0xaf, 0xd3, 0xc0, 0x05, 0xc2, 0x0c, 0xf3, 0xc9, 0xcd, 0xb7, 0xf2, 0x16, 0x05, 0xcd, 0x3f, 0xf3, 0xd3, 0xc1, 0x3e, 0x21, 0xcd, 0xd9, 0xf3, 0xcd, 0x6b, 0xf3, 0xc8, 0x15, 0xc2, 0x2a, 0xf3, 0xc3, 0x99, 0xf2, 0x2a, 0x3d, 0xf4, 0x7d, 0x0f, 0x0f, 0x5f, 0xcd, 0x5a, 0xf3, 0x3a, 0x3f, 0xf4, 0xca, 0x53, 0xf3, 0x3c, 0x0f, 0xe6, 0x3f, 0xb3, 0xc9, 0x06, 0x0b, 0x2a, 0x30, 0xf4, 0x7c, 0xb7, 0xc8, 0x3e, 0x28, 0x85, 0x4f, 0x21, 0x00, 0xf4, 0x06, 0x00, 0x09, 0x7e, 0xe6, 0x02, 0xc9, 0x3a, 0x3e, 0xf4, 0x21, 0x27, 0xf4, 0xbe, 0xc8, 0x77, 0x5f, 0x2a, 0x3d, 0xf4, 0xcd, 0x5a, 0xf3, 0xca, 0x80, 0xf3, 0x3e, 0x10, 0xd3, 0xc1, 0x3e, 0x15, 0xcd, 0xd9, 0xf3, 0x7b, 0xb7, 0x3e, 0x0d, 0xca, 0x98, 0xf3, 0x7b, 0xd3, 0xc1, 0x3e, 0x11, 0xcd, 0xd9, 0xf3, 0x3e, 0x09, 0xcd, 0xca, 0xf3, 0xe6, 0x28, 0xc8, 0xcd, 0xb1, 0xf3, 0xc3, 0x99, 0xf2, 0x3e, 0xff, 0x32, 0x39, 0xf4, 0xaf, 0x32, 0x3d, 0xf4, 0x3c, 0x32, 0x3f, 0xf4, 0x3e, 0x81, 0xcd, 0xd9, 0xf3, 0xcd, 0x3f, 0xf3, 0xd3, 0xc1, 0x3e, 0x21, 0xcd, 0xd9, 0xf3, 0x3e, 0xff, 0x32, 0x27, 0xf4, 0x32, 0x35, 0xf4, 0x3e, 0x0d, 0xcd, 0xd9, 0xf3, 0xdb, 0xc0, 0xe6, 0x01, 0xc2, 0xcd, 0xf3, 0xdb, 0xc0, 0xc9, 0x3e, 0x0b, 0xd3, 0xc0, 0xaf, 0xd3, 0xc0, 0xc9, 0x7e, 0xb7, 0xc8, 0x4e, 0xe5, 0xcd, 0xec, 0xf3, 0xe1, 0x23, 0xc3, 0xdf, 0xf3, 0x2a, 0x40, 0xf4, 0x2e, 0x0c, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static uint8 *icom_prom = icom_3812_prom; /* default is 3812 */ /* ** ICOM Registers and Interface Controls */ typedef struct { uint8 status; /* Status Register */ uint8 track; /* Track Register */ uint8 sector; /* Sector Register */ uint8 command; /* Command Register */ uint8 rData; /* Read Data Register */ uint32 rDataBuf; /* Read buffer index */ uint8 wData; /* Write Data Register */ uint32 wDataBuf; /* Write buffer index */ uint8 formatMode; /* format mode */ uint16 bytesPerSec; /* bytes per sector */ } ICOM_REG; /* iCOM Registers */ #define ICOM_REG_COMMAND 0x00 #define ICOM_REG_DATAI 0x00 #define ICOM_REG_DATAO 0x01 /* iCOM Commands */ #define ICOM_CMD_STATUS 0x00 #define ICOM_CMD_CMDMSK 0x01 #define ICOM_CMD_READ 0x03 #define ICOM_CMD_WRITE 0x05 #define ICOM_CMD_READCRC 0x07 #define ICOM_CMD_SEEK 0x09 #define ICOM_CMD_CLRERRFLGS 0x0b #define ICOM_CMD_TRACK0 0x0d #define ICOM_CMD_WRITEDDM 0x0f #define ICOM_CMD_LDTRACK 0x11 #define ICOM_CMD_LDUNITSEC 0x21 #define ICOM_CMD_LDWRITEBUFNOP 0x30 #define ICOM_CMD_LDWRITEBUF 0x31 #define ICOM_CMD_EXREADBUF 0x40 #define ICOM_CMD_SHREADBUF 0x41 #define ICOM_CMD_CLEAR 0x81 #define ICOM_CMD_LDCONF 0x15 #define ICOM_STAT_BUSY 0x01 #define ICOM_STAT_UNITMSK 0x06 #define ICOM_STAT_CRC 0x08 #define ICOM_STAT_WRITEPROT 0x10 #define ICOM_STAT_DRVFAIL 0x20 #define ICOM_STAT_MEDIASTAT 0x40 #define ICOM_STAT_DDM 0x80 #define ICOM_CONF_DD 0x10 /* Double Density */ #define ICOM_CONF_FM 0x20 /* Format Mode */ #define ICOM_TYPE_3712 0x00 #define ICOM_TYPE_3812 0x01 typedef struct { uint32 mem_base; /* Memory Base Address */ uint32 mem_size; /* Memory Address space requirement */ uint32 io_base; /* I/O Base Address */ uint32 io_size; /* I/O Address Space requirement */ uint32 prom_base; /* Boot PROM Base Address */ uint32 prom_size; /* Boot PROM Address space requirement */ uint8 promEnabled; /* PROM is enabled */ uint8 boardType; /* Interface Board Type */ uint8 rwsMs; /* Read/Write Sector ms */ uint8 seekMs; /* Seek ms */ uint8 currentDrive; /* Currently selected drive */ uint8 currentTrack[ICOM_MAX_DRIVES]; uint8 mediaDen[ICOM_MAX_DRIVES]; uint32 msTime; /* MS time for BUSY */ ICOM_REG ICOM; /* ICOM Registers and Data */ UNIT *uptr[ICOM_MAX_DRIVES]; } ICOM_INFO; static ICOM_INFO icom_info_data = { ICOM_MEM_BASE, ICOM_MEM_SIZE, ICOM_IO_BASE, ICOM_IO_SIZE, ICOM_PROM_BASE, ICOM_PROM_SIZE, TRUE, ICOM_TYPE_3812, 6, 10 }; static ICOM_INFO *icom_info = &icom_info_data; /* ** Read and Write Data Ring Buffers */ #define DATA_MASK ICOM_DD_SECTOR_LEN-1 static uint8 rdata[ICOM_DD_SECTOR_LEN]; static uint8 wdata[ICOM_DD_SECTOR_LEN]; /* Local function prototypes */ static t_stat icom_reset(DEVICE *icom_dev); static t_stat icom_svc(UNIT *uptr); static t_stat icom_attach(UNIT *uptr, CONST char *cptr); static t_stat icom_detach(UNIT *uptr); static t_stat icom_boot(int32 unitno, DEVICE *dptr); static t_stat icom_set_prom(UNIT *uptr, int32 val, CONST char *cptr, void *desc); static t_stat icom_show_prom(FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat icom_set_membase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); static t_stat icom_show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat icom_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); static t_stat icom_show_type(FILE *st, UNIT *uptr, int32 val, CONST void *desc); static uint32 calculate_icom_sec_offset(ICOM_REG *pICOM, uint8 track, uint8 sector); static void icom_set_busy(uint32 msec); static int icom_set_crc(uint8 drive); static uint8 ICOM_Read(uint32 Addr); static uint8 ICOM_Write(uint32 Addr, int32 data); static const char * ICOM_CommandString(uint8 command); static uint8 ICOM_Command(UNIT *uptr, ICOM_REG *pICOM, int32 data); static uint32 ICOM_ReadSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer); static uint32 ICOM_WriteSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer); static uint32 ICOM_FormatTrack(UNIT *uptr, uint8 track, uint8 *buffer); static uint8 ICOM_DriveNotReady(UNIT *uptr, ICOM_REG *pICOM); static const char* icom_description(DEVICE *dptr); static void showReadSec(void); static void showWriteSec(void); static int32 icomdev(int32 Addr, int32 rw, int32 data); static int32 icomprom(int32 Addr, int32 rw, int32 data); static int32 icommem(int32 Addr, int32 rw, int32 data); static UNIT icom_unit[ICOM_MAX_DRIVES] = { { UDATA (icom_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, ICOM_DD_CAPACITY), 10000 }, { UDATA (icom_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, ICOM_DD_CAPACITY), 10000 }, { UDATA (icom_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, ICOM_DD_CAPACITY), 10000 }, { UDATA (icom_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, ICOM_DD_CAPACITY), 10000 } }; static REG icom_reg[] = { { DRDATAD (DRIVE, icom_info_data.currentDrive, 8, "Current drive register"), }, { HRDATAD (STATUS, icom_info_data.ICOM.status, 8, "Status register"), }, { HRDATAD (COMMAND, icom_info_data.ICOM.command, 8, "Command register"), }, { HRDATAD (RDATA, icom_info_data.ICOM.rData, 8, "Read Data register"), }, { HRDATAD (WDATA, icom_info_data.ICOM.wData, 8, "Write Data register"), }, { DRDATAD (TRACK, icom_info_data.ICOM.track, 8, "Track register"), }, { DRDATAD (SECTOR, icom_info_data.ICOM.sector, 8, "Sector register"), }, { DRDATAD (RBUF, icom_info_data.ICOM.rDataBuf, 16, "Read data buffer index register"), }, { DRDATAD (WBUF, icom_info_data.ICOM.wDataBuf, 16, "Write data buffer index register"), }, { DRDATAD (FORMAT, icom_info_data.ICOM.formatMode, 8, "Current format mode register"), }, { DRDATAD (DENSITY, icom_info_data.ICOM.bytesPerSec, 16, "Current density register"), }, { FLDATAD (PROM, icom_info_data.promEnabled, 0, "PROM enabled bit"), }, { DRDATAD (RWSMS, icom_info_data.rwsMs, 8, "Read/Write sector time (ms)"), }, { DRDATAD (SEEKMS, icom_info_data.seekMs, 8, "Seek track to track time (ms)"), }, { NULL } }; #define ICOM_NAME "iCOM 3712/3812 Floppy Disk System" #define ICOM_SNAME "ICOM" static const char* icom_description(DEVICE *dptr) { return ICOM_NAME; } #define UNIT_V_ICOM_WPROTECT (UNIT_V_UF + 1) /* WRTENB / WRTPROT */ #define UNIT_ICOM_WPROTECT (1 << UNIT_V_ICOM_WPROTECT) static MTAB icom_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL, "Sets interface board I/O base address" }, { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &icom_set_membase, &icom_show_membase, NULL, "Shows interface board memory base address" }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "PROM", "PROM={ENABLE|DISABLE}", &icom_set_prom, &icom_show_prom, NULL, "Set/Show PROM enabled/disabled status"}, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "TYPE", "TYPE={3712|3812}", &icom_set_type, &icom_show_type, NULL, "Set/Show the current controller type" }, { UNIT_ICOM_WPROTECT, 0, "WRTENB", "WRTENB", NULL, NULL, NULL, "Enables " ICOM_SNAME "n for writing" }, { UNIT_ICOM_WPROTECT, UNIT_ICOM_WPROTECT, "WRTPROT", "WRTPROT", NULL, NULL, NULL, "Protects " ICOM_SNAME "n from writing" }, { 0 } }; /* Debug flags */ #define VERBOSE_MSG (1 << 0) #define ERROR_MSG (1 << 1) #define RBUF_MSG (1 << 2) #define WBUF_MSG (1 << 3) #define CMD_MSG (1 << 4) #define RD_DATA_MSG (1 << 5) #define WR_DATA_MSG (1 << 6) #define STATUS_MSG (1 << 7) #define RD_DATA_DETAIL_MSG (1 << 8) #define WR_DATA_DETAIL_MSG (1 << 9) /* Debug Flags */ static DEBTAB icom_dt[] = { { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, { "ERROR", ERROR_MSG, "Error messages" }, { "CMD", CMD_MSG, "Command messages" }, { "RBUF", RBUF_MSG, "Read Buffer messages" }, { "WBUF", WBUF_MSG, "Write Buffer messages" }, { "READ", RD_DATA_MSG, "Read messages" }, { "WRITE", WR_DATA_MSG, "Write messages" }, { "STATUS", STATUS_MSG, "Status messages" }, { "RDDETAIL", RD_DATA_DETAIL_MSG, "Read detail messages" }, { "WRDETAIL", WR_DATA_DETAIL_MSG, "Write detail messages" }, { NULL, 0 } }; DEVICE icom_dev = { ICOM_SNAME, /* name */ icom_unit, /* unit */ icom_reg, /* registers */ icom_mod, /* modifiers */ ICOM_MAX_DRIVES, /* # units */ 10, /* address radix */ 31, /* address width */ 1, /* addr increment */ ICOM_MAX_DRIVES, /* data radix */ ICOM_MAX_DRIVES, /* data width */ NULL, /* examine routine */ NULL, /* deposit routine */ &icom_reset, /* reset routine */ &icom_boot, /* boot routine */ &icom_attach, /* attach routine */ &icom_detach, /* detach routine */ &icom_info_data, /* context */ (DEV_DISABLE | DEV_DIS | DEV_DEBUG), /* flags */ ERROR_MSG, /* debug control */ icom_dt, /* debug flags */ NULL, /* mem size routine */ NULL, /* logical name */ NULL, /* help */ NULL, /* attach help */ NULL, /* context for help */ &icom_description /* description */ }; /* Reset routine */ static t_stat icom_reset(DEVICE *dptr) { uint8 i; if (dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ sim_map_resource(icom_info->prom_base, icom_info->prom_size, RESOURCE_TYPE_MEMORY, &icomprom, "icomprom", TRUE); sim_map_resource(icom_info->mem_base, icom_info->mem_size, RESOURCE_TYPE_MEMORY, &icommem, "icommem", TRUE); sim_map_resource(icom_info->io_base, icom_info->io_size, RESOURCE_TYPE_IO, &icomdev, "icomdev", TRUE); } else { if (sim_map_resource(icom_info->prom_base, icom_info->prom_size, RESOURCE_TYPE_MEMORY, &icomprom, "icomprom", FALSE) != 0) { sim_debug(ERROR_MSG, &icom_dev, "Error mapping PROM resource at 0x%04x\n", icom_info->prom_base); return SCPE_ARG; } if (sim_map_resource(icom_info->mem_base, icom_info->mem_size, RESOURCE_TYPE_MEMORY, &icommem, "icommem", FALSE) != 0) { sim_debug(ERROR_MSG, &icom_dev, "Error mapping MEM resource at 0x%04x\n", icom_info->mem_base); return SCPE_ARG; } /* Connect I/O Ports at base address */ if (sim_map_resource(icom_info->io_base, icom_info->io_size, RESOURCE_TYPE_IO, &icomdev, "icomdev", FALSE) != 0) { sim_debug(ERROR_MSG, &icom_dev, "Error mapping I/O resource at 0x%02x\n", icom_info->io_base); return SCPE_ARG; } } icom_info->currentDrive = 0; icom_info->ICOM.track = 0; icom_info->ICOM.sector = 1; icom_info->ICOM.command = 0; icom_info->ICOM.status = 0; icom_info->ICOM.rData = 0; icom_info->ICOM.wData = 0; icom_info->ICOM.rDataBuf = 0; icom_info->ICOM.wDataBuf = 0; icom_info->ICOM.bytesPerSec = ICOM_SD_SECTOR_LEN; icom_info->ICOM.formatMode = 0; /* Reset Registers and Interface Controls */ for (i=0; i < ICOM_MAX_DRIVES; i++) { if (icom_info->uptr[i] == NULL) { icom_info->uptr[i] = &icom_dev.units[i]; } icom_info->currentTrack[i] = 0; } sim_debug(STATUS_MSG, &icom_dev, "reset controller.\n"); return SCPE_OK; } /* Service routine */ static t_stat icom_svc(UNIT *uptr) { icom_info->ICOM.status &= ~ICOM_STAT_BUSY; return SCPE_OK; } /* Attach routine */ static t_stat icom_attach(UNIT *uptr, CONST char *cptr) { t_stat r; unsigned int i = 0; r = attach_unit(uptr, cptr); /* attach unit */ if (r != SCPE_OK) { /* error? */ sim_debug(ERROR_MSG, &icom_dev, "ATTACH error=%d\n", r); return r; } /* Determine length of this disk */ if (sim_fsize(uptr->fileref) != 0) { uptr->capac = sim_fsize(uptr->fileref); } else { uptr->capac = ICOM_SD_CAPACITY; } for (i = 0; i < ICOM_MAX_DRIVES; i++) { if (icom_dev.units[i].fileref == uptr->fileref) { break; } } if (i >= ICOM_MAX_DRIVES) { icom_detach(uptr); return SCPE_ARG; } /* used for the generation of CRC errors */ icom_info->mediaDen[i] = (uptr->capac == ICOM_DD_CAPACITY); /* Default for new file is DSK */ uptr->u3 = IMAGE_TYPE_DSK; sim_debug(VERBOSE_MSG, uptr->dptr, "unit %d, attached to '%s' size=%d interface=%s\n", i, cptr, uptr->capac, (icom_info->boardType == ICOM_TYPE_3712) ? "FD3712" : "FD3812"); return SCPE_OK; } /* Detach routine */ static t_stat icom_detach(UNIT *uptr) { t_stat r; int8 i; for (i = 0; i < ICOM_MAX_DRIVES; i++) { if (icom_dev.units[i].fileref == uptr->fileref) { break; } } if (i >= ICOM_MAX_DRIVES) { return SCPE_ARG; } DBG_PRINT(("Detach ICOM%d\n", i)); r = detach_unit(uptr); /* detach unit */ if (r != SCPE_OK) { return r; } icom_dev.units[i].fileref = NULL; sim_debug(VERBOSE_MSG, uptr->dptr, "unit %d detached.\n", i); return SCPE_OK; } /* ** If membase is 0, remove from system */ static t_stat icom_set_membase(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { uint32 newba; t_stat r; if (cptr == NULL) { sim_debug(ERROR_MSG, &icom_dev, "cptr=NULL\n"); return SCPE_ARG; } newba = get_uint(cptr, 16, 0xFFFF, &r); if (r != SCPE_OK) { sim_debug(ERROR_MSG, &icom_dev, "get_uint=%d\n", r); return r; } if (newba) { r = set_membase(uptr, val, cptr, desc); if (r) { sim_debug(ERROR_MSG, &icom_dev, "Error setting MEM resource at 0x%04x\n", icom_info->mem_base); icom_info->mem_base = 0; } else { sim_debug(VERBOSE_MSG, &icom_dev, "memory now at 0x%04x\n", icom_info->mem_base); icom_info->mem_base = newba; } } else if (icom_info->mem_base) { sim_map_resource(icom_info->mem_base, icom_info->mem_size, RESOURCE_TYPE_MEMORY, &icommem, "icommem", TRUE); icom_info->mem_base = 0; sim_debug(VERBOSE_MSG, &icom_dev, "disabled memory at 0x%04x\n", icom_info->mem_base); } return r; } /* Show Base Address routine */ t_stat icom_show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc) { if (icom_info->mem_base) { fprintf(st, "MEM=0x%04X-0x%04X", icom_info->mem_base, icom_info->mem_base+icom_info->mem_size-1); } if (icom_info->promEnabled) { if (icom_info->mem_base) { fprintf(st, ", "); } fprintf(st, "PROM=0x%04X-0x%04X", icom_info->prom_base, icom_info->prom_base+icom_info->prom_size-1); } return SCPE_OK; } static t_stat icom_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { if (!cptr) return SCPE_IERR; if (!strcmp(cptr, "3812")) { icom_info->boardType = ICOM_TYPE_3812; icom_info->ICOM.status |= ICOM_STAT_MEDIASTAT; icom_prom = icom_3812_prom; } else if (!strcmp(cptr, "3712")) { icom_info->boardType = ICOM_TYPE_3712; icom_info->ICOM.status &= ~ICOM_STAT_MEDIASTAT; icom_info->ICOM.bytesPerSec = ICOM_SD_SECTOR_LEN; icom_prom = icom_3712_prom; } else { return SCPE_ARG; } return SCPE_OK; } static t_stat icom_show_type(FILE *st, UNIT *uptr, int32 val, CONST void *desc) { fprintf(st, "TYPE=%s", (icom_info->boardType == ICOM_TYPE_3812) ? "3812" : "3712"); return SCPE_OK; } static t_stat icom_set_prom(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { if (!cptr) return SCPE_IERR; if (!strlen(cptr)) return SCPE_ARG; /* this assumes that the parameter has already been upcased */ if (!strncmp(cptr, "ENABLE", strlen(cptr))) { if (sim_map_resource(icom_info->prom_base, icom_info->prom_size, RESOURCE_TYPE_MEMORY, &icomprom, "icomprom", FALSE) != 0) { sim_debug(ERROR_MSG, &icom_dev, "Error mapping MEM resource at 0x%04x\n", icom_info->prom_base); return SCPE_ARG; } icom_info->promEnabled = TRUE; } else if (!strncmp(cptr, "DISABLE", strlen(cptr))) { sim_map_resource(icom_info->prom_base, icom_info->prom_size, RESOURCE_TYPE_MEMORY, &icomprom, "icomprom", TRUE); icom_info->promEnabled = FALSE; } else { return SCPE_ARG; } return SCPE_OK; } static t_stat icom_show_prom(FILE *st, UNIT *uptr, int32 val, CONST void *desc) { fprintf(st, "%s", (icom_info->promEnabled) ? "PROM" : "NOPROM"); return SCPE_OK; } static t_stat icom_boot(int32 unitno, DEVICE *dptr) { sim_debug(VERBOSE_MSG, dptr, "Booting using PROM at 0x%04x\n", icom_info->prom_base); *((int32 *) sim_PC->loc) = icom_info->prom_base; return SCPE_OK; } static void icom_set_busy(uint32 msec) { icom_info->ICOM.status |= ICOM_STAT_BUSY; if (!msec) { msec = 1; } icom_info->msTime = sim_os_msec(); sim_activate_after_abs(icom_info->uptr[icom_info->currentDrive], msec * 1000); /* activate timer */ } /* Set CRC flag if trying to read/write wrong density */ static int icom_set_crc(uint8 drive) { uint8 track; track = icom_info->currentTrack[drive]; icom_info->ICOM.status &= ~ICOM_STAT_CRC; /* If formatting, CRC is always good */ if (icom_info->ICOM.formatMode) { return 0; } /* If reading double density from track 0, set CRC error */ /* Else, if reading a density different than attached disk, set CRC error */ if (track == 0 && icom_info->ICOM.bytesPerSec != ICOM_SD_SECTOR_LEN) { icom_info->ICOM.status |= ICOM_STAT_CRC; } else if (track > 0) { if ((icom_info->mediaDen[drive] && icom_info->ICOM.bytesPerSec != ICOM_DD_SECTOR_LEN) || (!icom_info->mediaDen[drive] && icom_info->ICOM.bytesPerSec != ICOM_SD_SECTOR_LEN)) { icom_info->ICOM.status |= ICOM_STAT_CRC; } } return (icom_info->ICOM.status & ICOM_STAT_CRC); } static int32 icomdev(int32 Addr, int32 rw, int32 data) { if (rw == 0) { return(ICOM_Read(Addr)); } else { return(ICOM_Write(Addr, data)); } } static void showReadSec(void) { int i; ICOM_REG *pICOM; pICOM = &icom_info->ICOM; sim_debug(RD_DATA_DETAIL_MSG, &icom_dev, "rdata unit %d track/sector %02d/%02d:\n", icom_info->currentDrive, pICOM->track, pICOM->sector); for (i=0; i < pICOM->bytesPerSec; i++) { if (((i) & 0xf) == 0) { sim_debug(RD_DATA_DETAIL_MSG, &icom_dev, "\t"); } sim_debug(RD_DATA_DETAIL_MSG, &icom_dev, "%02X ", rdata[i]); if (((i+1) & 0xf) == 0) { sim_debug(RD_DATA_DETAIL_MSG, &icom_dev, "\n"); } } } static void showWriteSec(void) { int i; ICOM_REG *pICOM; pICOM = &icom_info->ICOM; sim_debug(WR_DATA_DETAIL_MSG, &icom_dev, "wdata unit %d track/sector %02d/%02d:\n", icom_info->currentDrive, pICOM->track, pICOM->sector); for (i=0; i < pICOM->bytesPerSec; i++) { if (((i) & 0xf) == 0) { sim_debug(WR_DATA_DETAIL_MSG, &icom_dev, "\t"); } sim_debug(WR_DATA_DETAIL_MSG, &icom_dev, "%02X ", wdata[i]); if (((i+1) & 0xf) == 0) { sim_debug(WR_DATA_DETAIL_MSG, &icom_dev, "\n"); } } } static uint32 calculate_icom_sec_offset(ICOM_REG *pICOM, uint8 track, uint8 sector) { uint32 offset; uint16 bps; bps = pICOM->bytesPerSec; /* ** Calculate track offset */ if (track==0) { offset=0; } else { offset=ICOM_SPT * ICOM_SD_SECTOR_LEN; /* Track 0 always SD */ offset+=(track-1) * ICOM_SPT * bps; /* Track 1-77 SD or DD */ } /* ** Add sector offset to track offset */ offset += (sector-1) * bps; DBG_PRINT(("ICOM: offset calc drive=%d bps=%d track=%d sector=%d offset=%04x\n", icom_info->currentDrive, bps, track, sector, offset)); return (offset); } static uint8 ICOM_Read(uint32 Addr) { uint8 cData; uint8 driveNum; ICOM_REG *pICOM; UNIT *uptr; driveNum = icom_info->currentDrive; uptr = icom_info->uptr[driveNum]; pICOM = &icom_info->ICOM; switch(Addr & 0x01) { case ICOM_REG_DATAI: if (pICOM->command & ICOM_CMD_EXREADBUF) { pICOM->rData = rdata[pICOM->rDataBuf]; sim_debug(RBUF_MSG, &icom_dev, "read buffer[%d]=%02x\n", pICOM->rDataBuf, pICOM->rData); if (icom_info->boardType == ICOM_TYPE_3812) { ICOM_Command(uptr, pICOM, ICOM_CMD_SHREADBUF); } cData = pICOM->rData; } else { cData = pICOM->status; } break; default: sim_debug(ERROR_MSG, &icom_dev, "READ Invalid I/O Address %02x (%02x)\n", Addr & 0xFF, Addr & 0x01); cData = 0xff; break; } return (cData); } static uint8 ICOM_Write(uint32 Addr, int32 Data) { uint8 cData; uint8 driveNum; UNIT *uptr; ICOM_REG *pICOM; cData = 0; driveNum = icom_info->currentDrive; uptr = icom_info->uptr[driveNum]; pICOM = &icom_info->ICOM; switch(Addr & 0x01) { case ICOM_REG_COMMAND: cData = ICOM_Command(uptr, pICOM, Data); break; case ICOM_REG_DATAO: pICOM->wData = Data; if (pICOM->command == ICOM_CMD_LDWRITEBUFNOP && icom_info->boardType == ICOM_TYPE_3812) { ICOM_Command(uptr, pICOM, ICOM_CMD_LDWRITEBUF); } break; default: sim_debug(ERROR_MSG, &icom_dev, "WRITE Invalid I/O Address %02x (%02x)\n", Addr & 0xFF, Addr & 0x01); cData = 0xff; break; } return(cData); } static uint32 ICOM_ReadSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer) { uint32 sec_offset; uint32 rtn = 0; ICOM_REG *pICOM; pICOM = &icom_info->ICOM; if (uptr->fileref == NULL) { sim_debug(ERROR_MSG, &icom_dev, "uptr.fileref is NULL!\n"); return 0; } sec_offset = calculate_icom_sec_offset(pICOM, track, sector); sim_debug(RD_DATA_MSG, &icom_dev, "track %d sector %d at offset %04X\n", track, sector, sec_offset); if (sim_fseek(uptr->fileref, sec_offset, SEEK_SET) != 0) { sim_debug(ERROR_MSG, &icom_dev, "sim_fseek error.\n"); return 0; } rtn = sim_fread(buffer, 1, pICOM->bytesPerSec, uptr->fileref); sim_debug(RD_DATA_MSG, &icom_dev, "read %d bytes at offset %04X\n", rtn, sec_offset); return rtn; } static uint32 ICOM_WriteSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer) { uint32 sec_offset; uint32 rtn = 0; ICOM_REG *pICOM; pICOM = &icom_info->ICOM; if (uptr->fileref == NULL) { sim_debug(ERROR_MSG, &icom_dev, "uptr.fileref is NULL!\n"); return 0; } sec_offset = calculate_icom_sec_offset(pICOM, track, sector); sim_debug(WR_DATA_MSG, &icom_dev, "track %d sector %d bytes %d at offset %04X\n", track, sector, pICOM->bytesPerSec, sec_offset); if (sim_fseek(uptr->fileref, sec_offset, SEEK_SET) != 0) { sim_debug(ERROR_MSG, &icom_dev, "sim_fseek error.\n"); return 0; } rtn = sim_fwrite(buffer, 1, pICOM->bytesPerSec, uptr->fileref); sim_debug(WR_DATA_MSG, &icom_dev, "wrote %d bytes at offset %04X\n", rtn, sec_offset); return rtn; } static uint32 ICOM_FormatTrack(UNIT *uptr, uint8 track, uint8 *buffer) { uint8 sector; uint32 rtn; for (sector = 1; sector <= ICOM_SPT; sector++) { rtn = ICOM_WriteSector(uptr, track, sector, buffer); sim_debug(WR_DATA_MSG, &icom_dev, "FORMAT track %d sector %d\n", track, sector); } /* update disk density when formatting */ icom_info->mediaDen[icom_info->currentDrive] = (icom_info->ICOM.bytesPerSec == ICOM_DD_SECTOR_LEN); return rtn; } static uint8 ICOM_DriveNotReady(UNIT *uptr, ICOM_REG *pICOM) { pICOM->status &= ~ICOM_STAT_DRVFAIL; if ((uptr == NULL) || (uptr->fileref == NULL)) { pICOM->status |= ICOM_STAT_DRVFAIL; sim_debug(STATUS_MSG, &icom_dev, "Drive: %d not attached.\n", icom_info->currentDrive); } return (pICOM->status & ICOM_STAT_DRVFAIL); } static const char * ICOM_CommandString(uint8 command) { switch (command) { case ICOM_CMD_STATUS: return "STATUS"; case ICOM_CMD_READ: return "READ"; case ICOM_CMD_WRITE: return "WRITE"; case ICOM_CMD_READCRC: return "READ CRC"; case ICOM_CMD_SEEK: return "SEEK"; case ICOM_CMD_CLRERRFLGS: return "CLR ERR FLAGS"; case ICOM_CMD_TRACK0: return "TRACK 0"; case ICOM_CMD_WRITEDDM: return "WRITE DDM"; case ICOM_CMD_LDTRACK: return "LD TRACK"; case ICOM_CMD_LDUNITSEC: return "LD UNIT/SEC"; case ICOM_CMD_LDWRITEBUFNOP: return "LD WR BUF NOP"; case ICOM_CMD_LDWRITEBUF: return "LD WR BUF"; case ICOM_CMD_EXREADBUF: return "EX RD BUF"; case ICOM_CMD_SHREADBUF: return "SHFT RD BUF"; case ICOM_CMD_CLEAR: return "CLEAR"; case ICOM_CMD_LDCONF: return "LD CONFIG"; default: break; } return "UNRECOGNIZED COMMAND"; } static uint8 ICOM_Command(UNIT *uptr, ICOM_REG *pICOM, int32 Data) { uint8 cData; uint8 newTrack; int32 rtn; cData = 0; if (uptr == NULL) { return cData; } pICOM->command = Data; ICOM_DriveNotReady(uptr, pICOM); /* Update not ready status */ switch(pICOM->command) { case ICOM_CMD_STATUS: pICOM->rData = pICOM->status; break; case ICOM_CMD_READ: if (pICOM->status & ICOM_STAT_DRVFAIL || icom_set_crc(icom_info->currentDrive)) { break; } rtn = ICOM_ReadSector(uptr, pICOM->track, pICOM->sector, rdata); if (rtn == pICOM->bytesPerSec) { showReadSec(); icom_set_busy(icom_info->rwsMs); } else { sim_debug(ERROR_MSG, &icom_dev, "sim_fread errno=%d\n", errno); pICOM->status |= ICOM_STAT_DRVFAIL; } pICOM->rDataBuf = 0; // Reset read buffer address break; case ICOM_CMD_WRITEDDM: sim_debug(VERBOSE_MSG, &icom_dev, "DDM writes not supported. Performing standard write.\n"); /* fall into ICOM_CMD_WRITE */ case ICOM_CMD_WRITE: if (uptr->flags & UNIT_ICOM_WPROTECT) { sim_debug(ERROR_MSG, &icom_dev, "Disk '%s' write protected.\n", uptr->filename); break; } if (pICOM->status & ICOM_STAT_DRVFAIL || icom_set_crc(icom_info->currentDrive)) { break; } /* ** If format mode, format entire track with wdata */ if (pICOM->formatMode) { rtn = ICOM_FormatTrack(uptr, pICOM->track, wdata); } else { rtn = ICOM_WriteSector(uptr, pICOM->track, pICOM->sector, wdata); } if (rtn == pICOM->bytesPerSec) { showWriteSec(); icom_set_busy(icom_info->rwsMs); } else { sim_debug(ERROR_MSG, &icom_dev, "sim_fwrite errno=%d\n", errno); pICOM->status |= ICOM_STAT_DRVFAIL; } pICOM->wDataBuf = 0; // Reset write buffer address break; case ICOM_CMD_READCRC: if (pICOM->status & ICOM_STAT_DRVFAIL) { break; } icom_set_crc(icom_info->currentDrive); icom_set_busy(icom_info->rwsMs); break; case ICOM_CMD_SEEK: if (pICOM->status & ICOM_STAT_DRVFAIL) { break; } icom_set_busy(icom_info->seekMs * abs((int8) pICOM->track - (int8) icom_info->currentTrack[icom_info->currentDrive])); icom_info->currentTrack[icom_info->currentDrive] = pICOM->track; break; case ICOM_CMD_CLRERRFLGS: pICOM->status &= ~ICOM_STAT_BUSY; pICOM->status &= ~ICOM_STAT_DDM; break; case ICOM_CMD_TRACK0: if (pICOM->status & ICOM_STAT_DRVFAIL) { break; } pICOM->track = 0; icom_set_busy(icom_info->seekMs * abs((int8) pICOM->track - (int8) icom_info->currentTrack[icom_info->currentDrive])); icom_info->currentTrack[icom_info->currentDrive] = 0; break; case ICOM_CMD_LDTRACK: newTrack = pICOM->wData; if (newTrack < ICOM_TRACKS) { pICOM->track = newTrack; } break; case ICOM_CMD_LDUNITSEC: pICOM->sector = pICOM->wData & 0x1f; icom_info->currentDrive = pICOM->wData >> 6; pICOM->status &= ~ICOM_STAT_UNITMSK; pICOM->status |= icom_info->currentDrive << 1; break; case ICOM_CMD_LDWRITEBUFNOP: sim_debug(WBUF_MSG, &icom_dev, "LOAD WRITE BUF NOP index=%04x\n", pICOM->wDataBuf); break; case ICOM_CMD_LDWRITEBUF: sim_debug(WBUF_MSG, &icom_dev, "LOAD WRITE BUF %d=%02x\n", pICOM->wDataBuf, pICOM->wData); wdata[pICOM->wDataBuf] = pICOM->wData; pICOM->wDataBuf++; pICOM->wDataBuf &= DATA_MASK; break; case ICOM_CMD_EXREADBUF: sim_debug(RBUF_MSG, &icom_dev, "EXAMINE READ BUF index=%04x\n", pICOM->rDataBuf); break; case ICOM_CMD_SHREADBUF: pICOM->rDataBuf++; pICOM->rDataBuf &= DATA_MASK; sim_debug(RBUF_MSG, &icom_dev, "SHIFT READ BUF index=%04x\n", pICOM->rDataBuf); break; case ICOM_CMD_CLEAR: pICOM->status &= ~ICOM_STAT_BUSY; pICOM->status &= ~ICOM_STAT_DRVFAIL; pICOM->status &= ~ICOM_STAT_CRC; pICOM->status &= ~ICOM_STAT_DDM; pICOM->rDataBuf = 0; pICOM->wDataBuf = 0; break; case ICOM_CMD_LDCONF: pICOM->formatMode = (pICOM->wData & ICOM_CONF_FM); pICOM->bytesPerSec = (pICOM->wData & ICOM_CONF_DD) ? ICOM_DD_SECTOR_LEN : ICOM_SD_SECTOR_LEN; break; default: cData=0xFF; break; } /* Set WRITE PROTECT bit */ pICOM->status &= ~ICOM_STAT_WRITEPROT; pICOM->status |= (uptr->flags & UNIT_ICOM_WPROTECT) ? ICOM_STAT_WRITEPROT : 0; /* Set data register to status if command bit 6 is 0 */ if (!(pICOM->command & 0x40)) { pICOM->rData = pICOM->status; } /* Clear command bit 0 */ pICOM->command &= ~ICOM_CMD_CMDMSK; sim_debug(CMD_MSG, &icom_dev, "%-13.13s (%02Xh) unit=%d trk=%02d sec=%02d stat=%02Xh mediaDen=%d density=%d formatMode=%s\n", ICOM_CommandString(Data), Data, icom_info->currentDrive, pICOM->track, pICOM->sector, pICOM->status, icom_info->mediaDen[icom_info->currentDrive], pICOM->bytesPerSec, (pICOM->formatMode) ? "TRUE" : "FALSE"); return(cData); } static int32 icomprom(int32 Addr, int32 rw, int32 Data) { /* ** The iCOM controller PROM occupies 1024 bytes (1K) of RAM at ** location F000H. */ if (icom_info->promEnabled == TRUE) { return(icom_prom[Addr & ICOM_PROM_MASK]); } return 0xff; } static int32 icommem(int32 Addr, int32 rw, int32 Data) { if (rw) { icom_mem[Addr & ICOM_MEM_MASK] = Data & 0xff; } return(icom_mem[Addr & ICOM_MEM_MASK]); }