1
0
mirror of https://github.com/simh/simh.git synced 2026-05-15 17:59:58 +00:00
Files
simh.simh/Altair8800/icom_fd3x12.c
Patrick Linstruth 9e3d414ef2 Altair8800: New Devices and Bug Fixes
This update to the Altair8800 simulator includes:

Adds MITS hard disk controller device
Adds FarmTek FDC+ disk controller device (1.5MB)
Adds iCOM 3712/3812 disk controller device
Adds Processor Technology VDM1 video device
Fixes boot bug in TARBELL and VFII devices
TARBELL returns 0xff for DMA status if 1011
Fixes DAZZLER vertical blank status timing
Adds DDT command to control DDT-style output
Improves MEMORY dump display
2026-03-31 16:16:10 -10:00

1452 lines
56 KiB
C

/* icom_37x2.c: iCOM FD3712/FD3812 Flexible Disk System
Copyright (c) 2026 Patrick A. Linstruth
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.
History:
29-Mar-2026 Initial version
==================================================================
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 0 | 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 |
+---------------------------------------------------------+
| * FD3812 Only |
+---------------------------------------------------------+
Bit 0 of the command byte is actually the clock/strobe bit that activates
processing of a command byte in the controller when it is asserted to one.
The bit must be returned to zero before another command can be recognized.
With the early interface boards, de-assertion was done in software. The
program had to write a command byte with bit 0 cleared in between each
command. This was typically by writing the Examine Staus or Examine Read
Buffer "commands."
To speed up the data transfer loop, later interface boards included a
one-shot that drove bit 0 of the command byte to the controller. The one-
shot was triggered by data input and output operations which then executed
the command byte (already present) by temporarily asserting command bit 0.
The one-shot also automatically asserted and cleared bit 0 to the controller
for any command written that had bit 0 set to 1. This meant the software did
not have to follow every command with an examine command to clear bit 0.
+---------------------------------------------------------------+
| |
| 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 "sim_defs.h"
#include "altair8800_sys.h"
#include "altair8800_dsk.h"
#include "s100_bus.h"
#include "s100_cpu.h"
#ifdef DBG_MSG
#define DBG_PRINT(args) sim_printf args
#else
#define DBG_PRINT(args)
#endif
#define ICOM_NUM_DRIVES 4
#define ICOM_SD_SECTOR_LEN 128
#define ICOM_DD_SECTOR_LEN 256
#define ICOM_SPT 26
#define ICOM_TRACKS 77
#define ICOM_HEADS 1
#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 /* Must be on a page boundary */
#define ICOM_MEM_SIZE 256 /* Must occupy entire page */
#define ICOM_MEM_MASK (ICOM_MEM_SIZE-1)
static int32 poc = TRUE;
static RES icom_res = { ICOM_IO_BASE, ICOM_IO_SIZE, ICOM_PROM_BASE, ICOM_PROM_SIZE };
static MDEV mdev = { NULL, NULL };
static DSK_INFO dsk_info[ICOM_NUM_DRIVES];
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 */
int denMode; /* denMode SD or DD */
} 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_LDWRITEBUFOS 0x30 /* 3812 One-Shot Write */
#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 /* 3812 Load Configuration */
#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 /* 3812 Double Density */
#define ICOM_CONF_FM 0x20 /* 3812 Format Mode */
#define ICOM_TYPE_3712 0x00
#define ICOM_TYPE_3812 0x01
static int32 prom_enabled = TRUE;
static int32 board_type = ICOM_TYPE_3812;
typedef struct {
uint8 rwsMs; /* Read/Write Sector ms */
uint8 seekMs; /* Seek ms */
uint8 drvSel; /* Currently selected drive */
uint8 currentTrack[ICOM_NUM_DRIVES];
uint8 mediaDen[ICOM_NUM_DRIVES];
ICOM_REG ICOM; /* ICOM Registers and Data */
} ICOM_INFO;
static ICOM_INFO icom_info;
/*
** 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 void icom_enable_prom(void);
static void icom_disable_prom(void);
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 void icom_set_busy(uint32 msec);
static int icom_set_crc(uint8 drive);
static uint8 icom_io_read(uint32 Addr);
static uint8 icom_io_write(uint32 Addr, int32 data);
static const char * ICOM_CommandString(uint8 command);
static uint8 ICOM_Command(UNIT *uptr, ICOM_REG *pICOM, int32 data);
static int32 ICOM_ReadSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer);
static int32 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 icom_io(int32 Addr, int32 rw, int32 data);
static int32 icom_promio(int32 Addr, int32 rw, int32 data);
static int32 icom_memio(int32 Addr, int32 rw, int32 data);
static t_stat icom_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
static UNIT icom_unit[ICOM_NUM_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.drvSel, 8, "Current drive register"), },
{ HRDATAD (STATUS, icom_info.ICOM.status, 8, "Status register"), },
{ HRDATAD (COMMAND, icom_info.ICOM.command, 8, "Command register"), },
{ HRDATAD (RDATA, icom_info.ICOM.rData, 8, "Read Data register"), },
{ HRDATAD (WDATA, icom_info.ICOM.wData, 8, "Write Data register"), },
{ DRDATAD (TRACK, icom_info.ICOM.track, 8, "Track register"), },
{ DRDATAD (SECTOR, icom_info.ICOM.sector, 8, "Sector register"), },
{ DRDATAD (RBUF, icom_info.ICOM.rDataBuf, 16, "Read data buffer index register"), },
{ DRDATAD (WBUF, icom_info.ICOM.wDataBuf, 16, "Write data buffer index register"), },
{ DRDATAD (FORMAT, icom_info.ICOM.formatMode, 8, "Current format mode register"), },
{ DRDATAD (DENSITY, icom_info.ICOM.denMode, 16, "Current density register"), },
{ FLDATAD (PROM, prom_enabled, 0, "PROM enabled bit"), },
{ DRDATAD (RWSMS, icom_info.rwsMs, 8, "Read/Write sector time (ms)"), },
{ DRDATAD (SEEKMS, icom_info.seekMs, 8, "Seek track to track time (ms)"), },
{ NULL }
};
#define ICOM_NAME "iCOM 3712/3812 Floppy Disk System"
#define DEV_NAME "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|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 " DEV_NAME "n for writing" },
{ UNIT_ICOM_WPROTECT, UNIT_ICOM_WPROTECT, "WRTPROT", "WRTPROT", NULL, NULL, NULL,
"Protects " DEV_NAME "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 = {
DEV_NAME, /* name */
icom_unit, /* unit */
icom_reg, /* registers */
icom_mod, /* modifiers */
ICOM_NUM_DRIVES, /* # units */
ADDRRADIX, /* address radix */
ADDRWIDTH, /* address width */
1, /* addr increment */
DATARADIX, /* data radix */
DATAWIDTH, /* 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_res, /* context */
(DEV_DISABLE | DEV_DIS | DEV_DEBUG), /* flags */
ERROR_MSG, /* debug control */
icom_dt, /* debug flags */
NULL, /* mem size routine */
NULL, /* logical name */
&icom_show_help, /* help */
NULL, /* attach help */
NULL, /* context for help */
&icom_description /* description */
};
/* Reset routine */
static t_stat icom_reset(DEVICE *dptr)
{
RES *res;
int i;
if ((res = (RES *) dptr->ctxt) == NULL) {
sim_printf("CTX is NULL!\n");
return SCPE_IERR;
}
sim_debug(STATUS_MSG, &icom_dev, "reset controller.\n");
if (dptr->flags & DEV_DIS) { /* Remove from bus */
s100_bus_remio(res->io_base, res->io_size, &icom_io);
s100_bus_remmem(res->mem_base, res->mem_size, &icom_promio);
s100_bus_remmem(ICOM_MEM_BASE, ICOM_MEM_SIZE, &icom_memio);
poc = TRUE;
} else {
if (poc) { /* Powerup? */
sim_debug(STATUS_MSG, &icom_dev, "Power on Clear.\n");
/* Connect I/O at base address */
s100_bus_addio(res->io_base, res->io_size, &icom_io, DEV_NAME);
/* Add memory */
s100_bus_addmem(ICOM_MEM_BASE, ICOM_MEM_SIZE, &icom_memio, DEV_NAME" (RAM)");
if (prom_enabled) {
icom_enable_prom();
}
for (i = 0; i < ICOM_NUM_DRIVES; i++) {
icom_unit[i].dptr = dptr;
dsk_init(&dsk_info[i], &icom_unit[i], ICOM_TRACKS, ICOM_HEADS, 0);
dsk_set_verbose_flag(&dsk_info[i], VERBOSE_MSG);
}
poc = FALSE;
}
icom_info.drvSel = 0;
icom_info.rwsMs = 0;
icom_info.seekMs = 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.formatMode = 0;
/* Reset Registers and Interface Controls */
for (i=0; i < ICOM_NUM_DRIVES; i++) {
icom_info.currentTrack[i] = 0;
}
}
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;
int d;
/* Determine drive number */
d = uptr - &icom_unit[0];
if (d < 0 || d >= ICOM_NUM_DRIVES) {
return SCPE_IERR;
}
if ((r = attach_unit(uptr, cptr)) != SCPE_OK) { /* attach unit */
sim_printf(DEV_NAME ": ATTACH error=%d\n", r);
return r;
}
/* Determine length of this disk */
if ((uptr->capac = sim_fsize(uptr->fileref)) == 0) {
uptr->capac = ICOM_SD_CAPACITY;
}
/* init format based on file size */
switch (uptr->capac) {
case ICOM_DD_CAPACITY:
dsk_init_format(&dsk_info[d], 0, 1, 0, 0, DSK_DENSITY_SD, ICOM_SPT, ICOM_SD_SECTOR_LEN, 1);
dsk_init_format(&dsk_info[d], 1, 76, 0, 0, DSK_DENSITY_DD, ICOM_SPT, ICOM_DD_SECTOR_LEN, 1);
break;
default:
uptr->capac = ICOM_SD_CAPACITY;
dsk_init_format(&dsk_info[d], 0, 76, 0, 0, DSK_DENSITY_SD, ICOM_SPT, ICOM_SD_SECTOR_LEN, 1);
break;
}
sim_debug(VERBOSE_MSG, uptr->dptr, "unit %d, attached to '%s' size=%d interface=%s\n",
d, cptr, uptr->capac, (board_type == ICOM_TYPE_3712) ? "FD3712" : "FD3812");
// dsk_show(&dsk_info[d]);
return SCPE_OK;
}
/* Detach routine */
static t_stat icom_detach(UNIT *uptr)
{
t_stat r;
int8 i;
for (i = 0; i < ICOM_NUM_DRIVES; i++) {
if (icom_dev.units[i].fileref == uptr->fileref) {
break;
}
}
if (i >= ICOM_NUM_DRIVES) {
return SCPE_ARG;
}
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;
}
static t_stat icom_set_type(UNIT *uptr, int32 val, const char *cptr, void *desc)
{
if (!cptr) return SCPE_IERR;
if (!strcmp(cptr, "3812")) {
board_type = ICOM_TYPE_3812;
icom_info.ICOM.status |= ICOM_STAT_MEDIASTAT;
icom_prom = icom_3812_prom;
} else if (!strcmp(cptr, "3712")) {
board_type = ICOM_TYPE_3712;
icom_info.ICOM.status &= ~ICOM_STAT_MEDIASTAT;
icom_info.ICOM.denMode = 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", (board_type == ICOM_TYPE_3812) ? "3812" : "3712");
return SCPE_OK;
}
static t_stat icom_boot(int32 unitno, DEVICE *dptr)
{
sim_debug(STATUS_MSG, &icom_dev, DEV_NAME ": Booting Controller at 0x%04x\n", icom_res.mem_base);
cpu_set_pc_loc(icom_res.mem_base);
return SCPE_OK;
}
static void icom_set_busy(uint32 msec)
{
icom_info.ICOM.status |= ICOM_STAT_BUSY;
sim_activate_after_abs(&icom_unit[icom_info.drvSel], (msec * 100) + 100); /* activate timer */
}
/* Set CRC flag if trying to read/write wrong density */
static int icom_set_crc(uint8 drive)
{
uint8 track;
int32 secsize;
/* If formatting, CRC is always good */
if (icom_info.ICOM.formatMode) {
return 0;
}
track = icom_info.currentTrack[drive];
secsize = dsk_sector_size(&dsk_info[icom_info.drvSel], track, 0);
icom_info.ICOM.status &= ~ICOM_STAT_CRC;
/* 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.denMode != secsize || secsize != ICOM_SD_SECTOR_LEN)) {
icom_info.ICOM.status |= ICOM_STAT_CRC;
}
else if (track > 0) {
if (icom_info.ICOM.denMode != secsize) {
icom_info.ICOM.status |= ICOM_STAT_CRC;
}
}
return (icom_info.ICOM.status & ICOM_STAT_CRC);
}
static int32 icom_io(int32 Addr, int32 rw, int32 data)
{
if (rw == S100_IO_READ) {
return(icom_io_read(Addr));
} else {
return(icom_io_write(Addr, data));
}
}
static uint8 icom_io_read(uint32 Addr)
{
ICOM_REG *pICOM;
UNIT *uptr;
uint8 cData;
uptr = &icom_unit[icom_info.drvSel];
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 (board_type == 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_io_write(uint32 Addr, int32 Data)
{
uint8 cData;
UNIT *uptr;
ICOM_REG *pICOM;
cData = 0;
uptr = &icom_unit[icom_info.drvSel];
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_LDWRITEBUFOS && board_type == 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 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.drvSel, pICOM->track, pICOM->sector);
for (i=0; i < dsk_sector_size(&dsk_info[icom_info.drvSel], pICOM->track, 0); 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.drvSel, pICOM->track, pICOM->sector);
for (i=0; i < dsk_sector_size(&dsk_info[icom_info.drvSel], pICOM->track, 0); 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 int32 ICOM_ReadSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer)
{
int32 bytes;
ICOM_REG *pICOM;
pICOM = &icom_info.ICOM;
if (uptr->fileref == NULL) {
sim_debug(ERROR_MSG, &icom_dev, "uptr.fileref is NULL!\n");
return 0;
}
sim_debug(RD_DATA_MSG, &icom_dev, "drive %d track %d sector %d\n", icom_info.drvSel, track, sector);
dsk_read_sector(&dsk_info[icom_info.drvSel], track, 0, sector, buffer, &bytes);
sim_debug(RD_DATA_MSG, &icom_dev, "read %d bytes\n", bytes);
return bytes;
}
static int32 ICOM_WriteSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer)
{
int32 bytes;
ICOM_REG *pICOM;
pICOM = &icom_info.ICOM;
if (uptr->fileref == NULL) {
sim_debug(ERROR_MSG, &icom_dev, "uptr.fileref is NULL!\n");
return 0;
}
sim_debug(WR_DATA_MSG, &icom_dev, "drive %d track %d sector %d bytes %d\n", icom_info.drvSel, track, sector, dsk_sector_size(&dsk_info[icom_info.drvSel], track, 0));
dsk_write_sector(&dsk_info[icom_info.drvSel], track, 0, sector, buffer, &bytes);
sim_debug(WR_DATA_MSG, &icom_dev, "wrote %d bytes\n", bytes);
return bytes;
}
/* 3812 Only */
static uint32 ICOM_FormatTrack(UNIT *uptr, uint8 track, uint8 *buffer)
{
uint8 sector;
uint32 rtn;
/* Update format for this track */
dsk_init_format(&dsk_info[icom_info.drvSel], track, track, 0, 0, (icom_info.ICOM.denMode == ICOM_SD_SECTOR_LEN)? DSK_DENSITY_SD : DSK_DENSITY_DD, ICOM_SPT, icom_info.ICOM.denMode, 1);
// dsk_show(&dsk_info[d]);
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.drvSel] = (dsk_sector_size(&dsk_info[icom_info.drvSel], track, 0) == 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.drvSel);
}
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_LDWRITEBUFOS:
return "LD WR BUF ONE-SHOT";
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.drvSel)) {
break;
}
rtn = ICOM_ReadSector(uptr, pICOM->track, pICOM->sector, rdata);
if (rtn == dsk_sector_size(&dsk_info[icom_info.drvSel], pICOM->track, 0)) {
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.drvSel)) {
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 == dsk_sector_size(&dsk_info[icom_info.drvSel], pICOM->track, 0)) {
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.drvSel);
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.drvSel]));
icom_info.currentTrack[icom_info.drvSel] = 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.drvSel]));
icom_info.currentTrack[icom_info.drvSel] = 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.drvSel = (pICOM->wData >> 6) & 0x03;
pICOM->status &= ~ICOM_STAT_UNITMSK;
pICOM->status |= icom_info.drvSel << 1;
sim_debug(STATUS_MSG, &icom_dev, "LOAD UNIT: D:%d S:%d [%02X]\n", icom_info.drvSel, pICOM->sector, pICOM->wData);
break;
case ICOM_CMD_LDWRITEBUFOS:
sim_debug(WBUF_MSG, &icom_dev, "LOAD WRITE BUF ONE-SHOT 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;
break;
case ICOM_CMD_LDCONF: /* 3812 Only */
pICOM->formatMode = (pICOM->wData & ICOM_CONF_FM);
pICOM->denMode = (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.drvSel,
pICOM->track, pICOM->sector, pICOM->status,
icom_info.mediaDen[icom_info.drvSel],
dsk_sector_size(&dsk_info[icom_info.drvSel], pICOM->track, 0), (pICOM->formatMode) ? "TRUE" : "FALSE");
return(cData);
}
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)) && prom_enabled == FALSE) {
icom_enable_prom();
} else if (!strncmp(cptr, "DISABLE", strlen(cptr)) && prom_enabled == TRUE) {
icom_disable_prom();
} 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", (prom_enabled) ? "PROM" : "NOPROM");
return SCPE_OK;
}
static void icom_enable_prom(void)
{
/* Save existing memory device */
if (mdev.routine == NULL) {
s100_bus_get_mdev(icom_res.mem_base, &mdev);
}
/* Add PROM to bus */
s100_bus_addmem(icom_res.mem_base, icom_res.mem_size, &icom_promio, DEV_NAME" (PROM)");
prom_enabled = TRUE;
}
static void icom_disable_prom(void)
{
/* Restore memory device */
if (mdev.routine != NULL) {
s100_bus_addmem(icom_res.mem_base, icom_res.mem_size, mdev.routine, mdev.name);
mdev.routine = NULL;
mdev.name = NULL;
}
else {
s100_bus_remmem(icom_res.mem_base, icom_res.mem_size, &icom_promio);
}
prom_enabled = FALSE;
}
static int32 icom_promio(int32 Addr, int32 rw, int32 Data)
{
/*
** The iCOM controller PROM occupies 1024 bytes (1K) at
** location F000H.
*/
if (prom_enabled == TRUE) {
return(icom_prom[Addr & ICOM_PROM_MASK]);
}
return 0xff;
}
static int32 icom_memio(int32 Addr, int32 rw, int32 Data)
{
/*
** The iCOM controller RAM occupies 256 bytes at
** location F400H.
*/
if (rw == S100_IO_WRITE) {
icom_mem[Addr & ICOM_MEM_MASK] = Data & 0xff;
}
return(icom_mem[Addr & ICOM_MEM_MASK]);
}
static t_stat icom_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "\n%s (%s)\n", icom_description(dptr), dptr->name);
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprint_reg_help (st, dptr);
return SCPE_OK;
}