/*  isbc208.c: Intel iSBC208 Floppy Disk adapter

    Copyright (c) 2011, William A. Beech

        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
        WILLIAM A. BEECH 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 William A. Beech shall not be
        used in advertising or otherwise to promote the sale, use or other dealings
        in this Software without prior written authorization from William A. Beech.

    MODIFICATIONS:

        ?? ??? 11 - Original file.
        24 Apr 15 -- Modified to use simh_debug

    NOTES:

        These functions support a simulated iSBC208 interface to 4 each 8-, 5 1/4-, or 
        3 1/2-inch floppy disk drives.  Commands are setup with programmed I/O to the 
        simulated ports of an i8237 DMA controller and an i8272 FDC.  Data transfer 
        to/from the simulated disks is performed directly with the multibus memory.

        The iSBC-208 can be configured for 8- or 16-bit addresses.  It defaults to 8-bit 
        addresses for the 8080/8085 processors.  It can be configured for I/O port
        addresses with 3-bits (8-bit address) or 11-bits (16-bit address).  Default is 
        3-bits set to 0. This defines the port offset to be used to determine the actual
        port address. Bus priority can be configured for parallel or serial mode.  Default is 
        serial. The multibus interface interrupt can be configured for interrupt 0-7.  
        Default is none.  Since all channel registers in the i8237 are 16-bit, transfers 
        are done as two 8-bit operations, low- then high-byte.

        Port addressing is as follows (Port offset = 0):

        Port    Mode    Command Function

        00      Write       Load DMAC Channel 0 Base and Current Address Regsiters
                Read        Read DMAC Channel 0 Current Address Register
        01      Write       Load DMAC Channel 0 Base and Current Word Count Registers
                Read        Read DMAC Channel 0 Current Word Count Register
        04      Write       Load DMAC Channel 2 Base and Current Address Regsiters
                Read        Read DMAC Channel 2 Current Address Register
        05      Write       Load DMAC Channel 2 Base and Current Word Count Registers
                Read        Read DMAC Channel 2 Current Word Count Register
        06      Write       Load DMAC Channel 3 Base and Current Address Regsiters
                Read        Read DMAC Channel 3 Current Address Register
        07      Write       Load DMAC Channel 3 Base and Current Word Count Registers
                Read        Read DMAC Channel 3 Current Word Count Register
        08      Write       Load DMAC Command Register
                Read        Read DMAC Status Register
        09      Write       Load DMAC Request Register
        OA      Write       Set/Reset DMAC Mask Register
        OB      Write       Load DMAC Mode Register
        OC      Write       Clear DMAC First/Last Flip-Flop
        0D      Write       DMAC Master Clear
        OF      Write       Load DMAC Mask Register
        10      Read        Read FDC Status Register
        11      Write       Load FDC Data Register
                Read        Read FDC Data Register
        12      Write       Load Controller Auxiliary Port
                Read        Poll Interrupt Status
        13      Write       Controller Reset
        14      Write       Load Controller Low-Byte Segment Address Register
        15      Write       Load Controller High-Byte Segment Address Register
        20-2F   Read/Write  Reserved for iSBX Multimodule Board 

        Register usage is defined in the following paragraphs.

        Read/Write DMAC Address Registers

            Used to simultaneously load a channel's current-address register and base-address 
            register with the memory address of the first byte to be transferred. (The Channel 
            0 current/base address register must be loaded prior to initiating a diskette read 
            or write operation.)  Since each channel's address registers are 16 bits in length
            (64K address range), two "write address register" commands must be executed in 
            order to load the complete current/base address registers for any channel.

        Read/Write DMAC Word Count Registers

            The Write DMAC Word Count Register command is used to simultaneously load a 
            channel's current and base word-count registers with the number of bytes 
            to be transferred during a subsequent DMA operation.  Since the word-count 
            registers are 16-bits in length, two commands must be executed to load both 
            halves of the registers.

        Write DMAC Command Register

            The Write DMAC Command Register command loads an 8-bit byte into the 
            DMAC's command register to define the operating characteristics of the 
            DMAC. The functions of the individual bits in the command register are 
            defined in the following diagram. Note that only two bits within the 
            register are applicable to the controller; the remaining bits select 
            functions that are not supported and, accordingly, must always be set 
            to zero.

              7   6   5   4   3   2   1   0
            +---+---+---+---+---+---+---+---+
            | 0   0   0       0       0   0 |
            +---+---+---+---+---+---+---+---+
                          |       |
                          |       +---------- 0 CONTROLLER ENABLE
                          |                   1 CONTROLLER DISABLE
                          |
                          +------------------ 0 FIXED PRIORITY
                                              1 ROTATING PRIORITY

        Read DMAC Status Register Command

            The Read DMAC Status Register command accesses an 8-bit status byte that 
            identifies the DMA channels that have reached terminal count or that 
            have a pending DMA request.

              7   6   5   4   3   2   1   0
            +---+---+---+---+---+---+---+---+
            |         0               0     |
            +---+---+---+---+---+---+---+---+
              |   |       |   |   |       |
              |   |       |   |   |       +-- CHANNEL 0 TC
              |   |       |   |   +---------- CHANNEL 2 TC
              |   |       |   +-------------- CHANNEL 3 TC
              |   |       +------------------ CHANNEL 0 DMA REQUEST
              |   +-------------------------- CHANNEL 2 DMA REQUEST
              +------------------------------ CHANNEL 3 DMA REQUEST

        Write DMAC Request Register
          
            The data byte associated with the Write DMAC Request Register command 
            sets or resets a channel's associated request bit within the DMAC's 
            internal 4-bit request register.

              7   6   5   4   3   2   1   0
            +---+---+---+---+---+---+---+---+
            | X   X   X   X   X             |
            +---+---+---+---+---+---+---+---+
                                  |   |   | 
                                  |   +---+-- 00 SELECT CHANNEL 0
                                  |           01 SELECT CHANNEL 1
                                  |           10 SELECT CHANNEL 2
                                  |           11 SELECT CHANNEL 3
                                  |
                                  +---------- 0 RESET REQUEST BIT
                                              1 SET REQUEST BIT

        Set/Reset DMAC Mask Register

            Prior to a DREQ-initiated DMA transfer, the channel's mask bit must 
            be reset to enable recognition of the DREQ input. When the transfer 
            is complete (terminal count reached or external EOP applied) and 
            the channel is not programmed to autoinitialize, the channel's 
            mask bit is automatically set (disabling DREQ) and must be reset 
            prior to a subsequent DMA transfer. All four bits of the mask 
            register are set (disabling the DREQ inputs) by a DMAC master 
            clear or controller reset. Additionally, all four bits can be 
            set/reset by a single Write DMAC Mask Register command.


              7   6   5   4   3   2   1   0
            +---+---+---+---+---+---+---+---+
            | X   X   X   X   X             |
            +---+---+---+---+---+---+---+---+
                                  |   |   | 
                                  |   +---+-- 00 SELECT CHANNEL 0
                                  |           01 SELECT CHANNEL 1
                                  |           10 SELECT CHANNEL 2
                                  |           11 SELECT CHANNEL 3
                                  |
                                  +---------- 0 RESET REQUEST BIT
                                              1 SET REQUEST BIT

        Write DMAC Mode Register

            The Write DMAC Mode Register command is used to define the 
            operating mode characteristics for each DMA channel. Each 
            channel has an internal 6-bit mode register; the high-order 
            six bits of the associated data byte are written into the 
            mode register addressed by the two low-order bits.


              7   6   5   4   3   2   1   0
            +---+---+---+---+---+---+---+---+
            |                               |
            +---+---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              |   |   |   |   |   |   +---+-- 00 SELECT CHANNEL 0
              |   |   |   |   |   |           01 SELECT CHANNEL 1
              |   |   |   |   |   |           10 SELECT CHANNEL 2
              |   |   |   |   |   |           11 SELECT CHANNEL 3
              |   |   |   |   |   |
              |   |   |   |   +---+---------- 00 VERIFY TRANSFER
              |   |   |   |                   01 WRITE TRANSFER
              |   |   |   |                   10 READ TRANSFER
              |   |   |   |   
              |   |   |   +------------------ 0 AUTOINITIALIZE DISABLE
              |   |   |                       1 AUTOINITIALIZE ENABLE
              |   |   |
              |   |   +---------------------- 0 ADDRESS INCREMENT
              |   |                           1 ADDRESS DECREMENT
              |   |
              +---+-------------------------- 00 DEMAND MODE
                                              01 SINGLE MODE
                                              10 BLOCK MODE

        Clear DMAC First/Last Flip-Flop

                The Clear DMAC First/Last Flip-Flop command initializes 
                the DMAC's internal first/last flip-flop so that the 
                next byte written to or re~d from the 16-bit address 
                or word-count registers is the low-order byte.  The 
                flip-flop is toggled with each register access so that 
                a second register read or write command accesses the 
                high-order byte.

        DMAC Master Clear

            The DMAC Master Clear command clears the DMAC's command, status, 
            request, and temporary registers to zero, initializes the 
            first/last flip-flop, and sets the four channel mask bits in 
            the mask register to disable all DMA requests (i.e., the DMAC 
            is placed in an idle state).

        Write DMAC Mask Register

            The Write DMAC Mask Register command allows all four bits of the 
            DMAC's mask register to be written with a single command.

              7   6   5   4   3   2   1   0
            +---+---+---+---+---+---+---+---+
            | X   X   X   X           X     |
            +---+---+---+---+---+---+---+---+
                              |   |       |
                              |   |       +-- 0 CLEAR CHANNEL 0 MASK BIT
                              |   |           1 SET CHANNEL 0 MASK BIT
                              |   |    
                              |   +---------- 0 CLEAR CHANNEL 2 MASK BIT
                              |               1 SET CHANNEL 2 MASK BIT
                              |
                              +-------------- 0 CLEAR CHANNEL 3 MASK BIT
                                              1 SET CHANNEL 3 MASK BIT

        Read FDC Status Register

            The Read FDC Status Register command accesses the FDC's main 
            status register. The individual status register bits are as 
            follows:

              7   6   5   4   3   2   1   0
            +---+---+---+---+---+---+---+---+
            |                               |
            +---+---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              |   |   |   |   |   |   |   +-- FDD 0 BUSY
              |   |   |   |   |   |   +------ FDD 1 BUSY
              |   |   |   |   |   +---------- FDD 2 BUSY 
              |   |   |   |   +-------------- FDD 3 BUSY
              |   |   |   +------------------ FDC BUSY
              |   |   +---------------------- NON-DMA MODE
              |   +-------------------------- DATA INPUT/OUTPUT
              +------------------------------ REQUEST FOR MASTER

        Read/Write FDC Data Register

            The Read and Write FDC Data Register commands are used to write 
            command and parameter bytes to the FDC in order to specify the 
            operation to be performed (referred to as the "command phase") 
            and to read status bytes from the FDC following the operation 
            (referred to as the "result phase"). During the command and 
            result phases, the 8-bit data register is actually a series of 
            8-bit registers in a stack. Each register is accessed in 
            sequence; the number of registers accessed and the individual 
            register contents are defined by the specific disk command.

        Write Controller Auxiliary Port

            The Write Controller Auxiliary Port command is used to set or 
            clear individual bits within the controller's auxiliary port. 
            The four low-order port bits are dedicated to auxiliary drive 
            control functions (jumper links are required to connect the 
            desired port bit to an available pin on the drive interface 
            connectors). The most common application for these bits is 
            the "Motor-On" control function for mini-sized drives.

              7   6   5   4   3   2   1   0
            +---+---+---+---+---+---+---+---+
            |                               |
            +---+---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              |   |   |   |   +---+---+---+-- DRIVE CONTROL
              |   |   |   +------------------ ADDR 20
              |   |   +---------------------- ADDR 21
              |   +-------------------------- ADDR 22
              +------------------------------ ADDR 23

        Poll Interrupt Status

            The Poll Interrupt Status command presents the interrupt 
            status of the controller and the two interrupt status 
            lines dedicated to the iSBX Multimodule board.
              7   6   5   4   3   2   1   0
            +---+---+---+---+---+---+---+---+
            | X   X   X   X   X             |
            +---+---+---+---+---+---+---+---+
                                  |   |   | 
                                  |   |   +-- CONTROLLER INTERRUPT
                                  |   +------ MULTIMODULE BOARD INTERRUPT 0
                                  +---------- MULTIMODULE BOARD INTERRUPT 1

        Controller Reset

            The Controller Reset command is the software reset for the 
            controller. This command clears the controller's auxiliary 
            port and segment address register, provides a reset signal 
            to the iSBX Multimodule board and initializes the bus 
            controller (releases the bus), the DMAC (clears the internal 
            registers and masks the DREQ inputs), and the FDC (places 
            the FDC in an idle state and disables the output control 
            lines to the diskette drive).

        Write Controller Low- And High-Byte Segment Address Registers

            The Write Controller Low- and High-Byte Address Registers 
            commands are required when the controller uses 20-bit 
            addressing (memory address range from 0 to OFFFFFH). These 
            commands are issued prior to initiating a diskette read or 
            write operation to specify the 16-bit segment address.

        FDC Commands

            The 8272/D765 is capable of performing 15 different 
            commands. Each command is initiated by a multibyte transfer 
            from the processor, and the result after execution of the 
            command may also be a multibyte transfer back to the processor. 
            Because of this multibyte interchange of information between 
            the FDC and the processor, it is convenient to consider each 
            command as consisting of three phases:

                Command Phase: The FDC receives all information required to 
                    perform a particular operation from the processor.

                Execution Phase: The FDC performs the operation it was 
                    instructed to do.

                Result Phase: After completion of the operation, status 
                    and other housekeeping information are made available 
                    to the processor.

            Not all the FDC commands are supported by this emulation.  Only the subset
            of commands required to build an operable CP/M BIOS are supported.  They are:

                Read - Read specified data from the selected FDD.

                Write - Write specified data to the selected FDD.

                Seek - Move the R/W head to the specified cylinder on the specified FDD.

                Specify - Set the characteristics for all the FDDs.

                Sense Interrupt - Sense change in FDD Ready line or and of Seek/Recalibrate
                    command.

                Sense Drive - Returns status of all the FDDs.

                Recalibrate - Move the R/W head to cylinder 0 on the specified FDD.

                Format Track - Format the current track on the specified FDD.

                Read ID - Reads the first address mark it finds.

        Simulated Floppy Disk Drives

            The units in this device simulate an 8- or 5 1/4- or 3 1/2 inch drives.  The 
            drives can emulate SSSD, SSDD, and DSDD.  Drives can be attached to files up 
            to 1.44MB in size.  Drive configuration is selected when a disk is logged onto 
            the system.  An identity sector or identity byte contains information to 
            configure the OS drivers for the type of drive to emulate.

        uptr->u3 - 
        uptr->u4 - 
        uptr->u5 - 
        uptr->u6 - unit number (0-FDD_NUM)
*/

#include "system_defs.h"

#define UNIT_V_WPMODE   (UNIT_V_UF)     /* Write protect */
#define UNIT_WPMODE     (1 << UNIT_V_WPMODE)

/* master status register definitions */

#define RQM             0x80            /* Request for master */
#define DIO             0x40            /* Data I/O Direction 0=W, 1=R */
#define NDM             0x20            /* Non-DMA mode */
#define CB              0x10            /* FDC busy */
#define D3B             0x08            /* FDD 3 busy */`
#define D2B             0x04            /* FDD 2 busy */`
#define D1B             0x02            /* FDD 1 busy */`
#define D0B             0x01            /* FDD 0 busy */`

/* status register 0 definitions */

#define IC              0xC0            /* Interrupt code */
#define IC_NORM         0x00            /* normal completion */
#define IC_ABNORM       0x40            /* abnormal completion */
#define IC_INVC         0x80            /* invalid command */
#define IC_RC           0xC0            /* drive not ready */
#define SE              0x20            /* Seek end */
#define EC              0x10            /* Equipment check */
#define NR              0x08            /* Not ready */
#define HD              0x04            /* Head selected */
#define US              0x03            /* Unit selected */
#define US_0            0x00            /* Unit 0 */
#define US_1            0x01            /* Unit 1 */
#define US_2            0x02            /* Unit 2 */
#define US_3            0x03            /* Unit 3 */

/* status register 1 definitions */

#define EN              0x80            /* End of cylinder */
#define DE              0x20            /* Data error */
#define OR              0x10            /* Overrun */
#define ND              0x04            /* No data */
#define NW              0x02            /* Not writable */
#define MA              0x01            /* Missing address mark */

/* status register 2 definitions */

#define CM              0x40            /* Control mark */
#define DD              0x20            /* Data error in data field */
#define WC              0x10            /* Wrong cylinder */
#define BC              0x02            /* Bad cylinder */
#define MD              0x01            /* Missing address mark in data field */

/* status register 3/fddst definitions */

#define FT              0x80            /* Fault */
#define WP              0x40            /* Write protect */
#define RDY             0x20            /* Ready */
#define T0              0x10            /* Track 0 */
#define TS              0x08            /* Two sided */
//#define HD              0x04            /* Head selected */
//#define US              0x03            /* Unit selected */

/* FDC command definitions */

#define READTRK         0x02
#define SPEC            0x03
#define SENDRV          0x04
#define WRITE           0x05
#define READ            0x06
#define HOME            0x07
#define SENINT          0x08
#define WRITEDEL        0x09
#define READID          0x0A
#define READDEL         0x0C
#define FMTTRK          0x0D
#define SEEK            0x0F
#define SCANEQ          0x11
#define SCANLOEQ        0x19
#define SCANHIEQ        0x1D

#define FDD_NUM          4

/* internal function prototypes */

t_stat isbc208_svc (UNIT *uptr);
t_stat isbc208_reset (DEVICE *dptr);
void isbc208_reset1 (void);
t_stat isbc208_attach (UNIT *uptr, CONST char *cptr);
t_stat isbc208_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
uint8 isbc208_r0(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r1(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r2(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r3(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r4(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r5(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r6(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r7(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r8(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r9(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_rA(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_rB(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_rC(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_rD(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_rE(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_rF(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r10(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r11(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r12(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r13(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r14(t_bool io, uint8 data, uint8 devnum);
uint8 isbc208_r15(t_bool io, uint8 data, uint8 devnum);

/* external function prototypes */

extern void set_irq(int32 int_num);
extern void clr_irq(int32 int_num);
extern uint8 reg_dev(uint8 (*routine)(t_bool, uint8, uint8), uint16 port, uint8 devnum);
extern void multibus_put_mbyte(uint16 addr, uint8 val);
extern uint8 multibus_get_mbyte(uint16 addr);

/* 8237 physical register definitions */

uint16 i8237_r0;                        // 8237 ch 0 address register
uint16 i8237_r1;                        // 8237 ch 0 count register
uint16 i8237_r2;                        // 8237 ch 1 address register
uint16 i8237_r3;                        // 8237 ch 1 count register
uint16 i8237_r4;                        // 8237 ch 2 address register
uint16 i8237_r5;                        // 8237 ch 2 count register
uint16 i8237_r6;                        // 8237 ch 3 address register
uint16 i8237_r7;                        // 8237 ch 3 count register
uint8 i8237_r8;                         // 8237 status register
uint8 i8237_r9;                         // 8237 command register
uint8 i8237_rA;                         // 8237 mode register
uint8 i8237_rB;                         // 8237 mask register
uint8 i8237_rC;                         // 8237 request register
uint8 i8237_rD;                         // 8237 first/last ff
uint8 i8237_rE;                         // 8237 
uint8 i8237_rF;                         // 8237 

/* 8272 physical register definitions */ 
/* 8272 command register stack*/

uint8 i8272_w0;                         // MT+MFM+SK+command 
uint8 i8272_w1;                         // HDS [HDS=H << 2] + DS1 + DS0
uint8 i8272_w2;                         // cylinder # (0-XX)
uint8 i8272_w3;                         // head # (0 or 1)
uint8 i8272_w4;                         // sector # (1-XX)                         
uint8 i8272_w5;                         // number of bytes (128 << N)
uint8 i8272_w6;                         // End of track (last sector # on cylinder)
uint8 i8272_w7;                         // Gap length
uint8 i8272_w8;                         // Data length (when N=0, size to read or write)

/* 8272 status register stack */

uint8 i8272_msr;                        // main status                         
uint8 i8272_r0;                         // ST 0                       
uint8 i8272_r1;                         // ST 1
uint8 i8272_r2;                         // ST 2
uint8 i8272_r3;                         // ST 3

/* iSBC-208 physical register definitions */

uint16 isbc208_sr;                      // isbc-208 segment register
uint8 isbc208_i;                        // iSBC-208 interrupt register
uint8 isbc208_a;                        // iSBC-208 auxillary port register

/* data obtained from analyzing command registers/attached file length */

int32 wsp = 0, rsp = 0;                 // indexes to write and read stacks (8272 data)
int32 cyl;                              // current cylinder
int32 hed;                              // current head [ h << 2]
int32 h;                                // current head
int32 sec;                              // current sector
int32 drv;                              // current drive
uint8 cmd, pcmd;                        // current command
int32 secn;                             // N 0-128, 1-256, etc
int32 spt;                              // sectors per track
int32 ssize;                            // sector size (128 << N)

uint8 *isbc208_buf[FDD_NUM] = {         /* FDD buffer pointers */
    NULL,
    NULL,
    NULL,
    NULL
};

int32 fddst[FDD_NUM] = {                // in ST3 format
    0,                                  // status of FDD 0
    0,                                  // status of FDD 1
    0,                                  // status of FDD 2
    0                                   // status of FDD 3
};

int8 maxcyl[FDD_NUM] = { 
    0,                                  // last cylinder + 1 of FDD 0
    0,                                  // last cylinder + 1 of FDD 1
    0,                                  // last cylinder + 1 of FDD 2
    0                                   // last cylinder + 1 of FDD 3
};

/* isbc208 Standard SIMH Device Data Structures - 4 units */

UNIT isbc208_unit[] = { 
    { UDATA (&isbc208_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 }, 
    { UDATA (&isbc208_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 }, 
    { UDATA (&isbc208_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 }, 
    { UDATA (&isbc208_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 } 
};

REG isbc208_reg[] = {
    { HRDATA (CH0ADR, i8237_r0, 16) },
    { HRDATA (CH0CNT, i8237_r1, 16) },
    { HRDATA (CH1ADR, i8237_r2, 16) },
    { HRDATA (CH1CNT, i8237_r3, 16) },
    { HRDATA (CH2ADR, i8237_r4, 16) },
    { HRDATA (CH2CNT, i8237_r5, 16) },
    { HRDATA (CH3ADR, i8237_r6, 16) },
    { HRDATA (CH3CNT, i8237_r7, 16) },
    { HRDATA (STAT37, i8237_r8, 8) },
    { HRDATA (CMD37, i8237_r9, 8) },
    { HRDATA (MODE, i8237_rA, 8) },
    { HRDATA (MASK, i8237_rB, 8) },
    { HRDATA (REQ, i8237_rC, 8) },
    { HRDATA (FF, i8237_rD, 8) },
    { HRDATA (STAT72, i8272_msr, 8) },
    { HRDATA (STAT720, i8272_r0, 8) },
    { HRDATA (STAT721, i8272_r1, 8) },
    { HRDATA (STAT722, i8272_r2, 8) },
    { HRDATA (STAT723, i8272_r3, 8) },
    { HRDATA (CMD720, i8272_w0, 8) },
    { HRDATA (CMD721, i8272_w1, 8) },
    { HRDATA (CMD722, i8272_w2, 8) },
    { HRDATA (CMD723, i8272_w3, 8) },
    { HRDATA (CMD724, i8272_w4, 8) },
    { HRDATA (CMD725, i8272_w5, 8) },
    { HRDATA (CMD726, i8272_w6, 8) },
    { HRDATA (CMD727, i8272_w7, 8) },
    { HRDATA (CMD728, i8272_w8, 8) },
    { HRDATA (FDD0, fddst[0], 8) },
    { HRDATA (FDD1, fddst[1], 8) },
    { HRDATA (FDD2, fddst[2], 8) },
    { HRDATA (FDD3, fddst[3], 8) },
    { HRDATA (SEGREG, isbc208_sr, 8) },
    { HRDATA (AUX, isbc208_a, 8) },
    { HRDATA (INT, isbc208_i, 8) },
    { NULL }
};

MTAB isbc208_mod[] = {
    { UNIT_WPMODE, 0, "RW", "RW", &isbc208_set_mode },
    { UNIT_WPMODE, UNIT_WPMODE, "WP", "WP", &isbc208_set_mode },
    { 0 }
};

DEBTAB isbc208_debug[] = {
    { "ALL", DEBUG_all },
    { "FLOW", DEBUG_flow },
    { "READ", DEBUG_read },
    { "WRITE", DEBUG_write },
    { "LEV1", DEBUG_level1 },
    { "LEV2", DEBUG_level2 },
    { "REG", DEBUG_reg },
    { NULL }
};

DEVICE isbc208_dev = {
    "SBC208",                   //name 
    isbc208_unit,               //units 
    isbc208_reg,                //registers 
    isbc208_mod,                //modifiers
    FDD_NUM,                    //numunits 
    16,                         //aradix  
    32,                         //awidth  
    1,                          //aincr  
    16,                         //dradix  
    8,                          //dwidth
    NULL,                       //examine  
    NULL,                       //deposit  
    &isbc208_reset,             //deposit 
    NULL,                       //boot
    &isbc208_attach,            //attach  
    NULL,                       //detach
    NULL,                       //ctxt     
    DEV_DEBUG+DEV_DISABLE+DEV_DIS, //flags 
    0,                          //dctrl 
//    DEBUG_flow + DEBUG_read + DEBUG_write,              //dctrl 
    isbc208_debug,              //debflags
    NULL,                       //msize
    NULL                        //lname
};

/* Service routines to handle simulator functions */

/* service routine - actually does the simulated disk I/O */

t_stat isbc208_svc (UNIT *uptr)
{
    uint32 i;
    int32 imgadr, data;
    int c;
    int32 bpt, bpc;
    FILE *fp;

    if ((i8272_msr & CB) && cmd && (uptr->u6 == drv)) { /* execution phase */
        sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: Entered execution phase\n");
        switch (cmd) {
        case READ:                  /* 0x06 */
//            sim_printf("READ-e: fddst=%02X", fddst[uptr->u6]);
            h = i8272_w3;           // h = 0 or 1 
            hed = i8272_w3 << 2;    // hed = 0 or 4 [h << 2] 
            sec = i8272_w4;         // sector number (1-XX)
            secn = i8272_w5;        // N (0-5)
            spt = i8272_w6;         // sectors/track
            ssize = 128 << secn;    // size of sector (bytes)
            bpt = ssize * spt;      // bytes/track
            bpc = bpt * 2;          // bytes/cylinder
//            sim_printf(" d=%d h=%d c=%d s=%d\n", drv, h, cyl, sec);
            sim_debug (DEBUG_flow, &isbc208_dev, 
                "208_svc: FDC read: h=%d, hed=%d, sec=%d, secn=%d, spt=%d, ssize=%04X, bpt=%04X, bpc=%04X\n",
                    h, hed, sec, secn, spt, ssize, bpt, bpc);
            sim_debug (DEBUG_flow, &isbc208_dev, 
                "208_svc: FDC read: d=%d h=%d c=%d s=%d N=%d spt=%d fddst=%02X\n",
                    drv, h, cyl, sec, secn, spt, fddst[uptr->u6]);
            sim_debug (DEBUG_read, &isbc208_dev, "208_svc: FDC read of d=%d h=%d c=%d s=%d\n",
                    drv, h, cyl, sec);
            if ((fddst[uptr->u6] & RDY) == 0) { // drive not ready
                i8272_r0 = IC_ABNORM + NR + hed + drv; /* command done - Not ready error*/
                i8272_r3 = fddst[uptr->u6];
                i8272_msr |= (RQM + DIO + CB); /* enter result phase */
                sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: FDC read: Not Ready\n"); 
            } else {                // get image addr for this d, h, c, s    
                imgadr = (cyl * bpc) + (h * bpt) + ((sec - 1) * ssize);
                sim_debug (DEBUG_read, &isbc208_dev, 
                    "208_svc: FDC read: DMA addr=%04X cnt=%04X imgadr=%04X\n", 
                        i8237_r0, i8237_r1, imgadr);
                for (i=0; i<=i8237_r1; i++) { /* copy selected sector to memory */
                    data = *(isbc208_buf[uptr->u6] + (imgadr + i));
                    multibus_put_mbyte(i8237_r0 + i, data);
                }
//*** need to step return results IAW table 3-11 in 143078-001
                i8272_w4 = ++sec;   /* next sector */
                i8272_r0 = hed + drv; /* command done - no error */
                i8272_r3 = fddst[uptr->u6];
            }
            i8272_r1 = 0;
            i8272_r2 = 0;
            i8272_w2 = cyl;         /* generate a current address mark */
            i8272_w3 = h;
            if (i8272_w4 > i8272_w6) { // beyond last sector of track?
                i8272_w4 = 1;       // yes, set to sector 1;
                if (h) {            // on head one?
                    i8272_w2++;     // yes, step cylinder
                    h = 0;          // back to head 0
                }
            }
            i8272_w5 = secn;
            i8272_msr |= (RQM + DIO + CB); /* enter result phase */
            rsp = wsp = 0;          /* reset indexes */
            set_irq(SBC208_INT);    /* set interrupt */
//                sim_printf("READ-x: fddst=%02X\n", fddst[uptr->u6]);
            break;
        case WRITE:                 /* 0x05 */
//                sim_printf("WRITE-e: fddst=%02X\n", fddst[uptr->u6]);
            h = i8272_w3;           // h = 0 or 1 
            hed = i8272_w3 << 2;    // hed = 0 or 4 [h << 2] 
            sec = i8272_w4;         // sector number (1-XX)
            secn = i8272_w5;        // N (0-5)
            spt = i8272_w6;         // sectors/track
            ssize = 128 << secn;    // size of sector (bytes)
            bpt = ssize * spt;      // bytes/track
            bpc = bpt * 2;          // bytes/cylinder
            sim_debug (DEBUG_flow, &isbc208_dev, 
                "208_svc: FDC write: hed=%d, sec=%d, secn=%d, spt=%d, ssize=%04X, bpt=%04X, bpc=%04X\n",
                    hed, sec, secn, spt, ssize, bpt, bpc);
            sim_debug (DEBUG_flow, &isbc208_dev, 
                "208_svc: FDC write: d=%d h=%d c=%d s=%d N=%d spt=%d fddst=%02X\n",
                    drv, h, cyl, sec, secn, spt, fddst[uptr->u6]);
            sim_debug (DEBUG_write, &isbc208_dev, "208_svc: FDC write of d=%d h=%d c=%d s=%d\n",
                    drv, h, cyl, sec);
            i8272_r1 = 0;           // clear ST1
            i8272_r2 = 0;           // clear ST2
            if ((fddst[uptr->u6] & RDY) == 0) {
                i8272_r0 = IC_ABNORM + NR + hed + drv; /* Not ready error*/
                i8272_r3 = fddst[uptr->u6];
                i8272_msr |= (RQM + DIO + CB); /* enter result phase */
                sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: FDC write: Not Ready\n"); 
//                } else if (fddst[uptr->u6] & WP) {
//                    i8272_r0 = IC_ABNORM + hed + drv; /* write protect error*/
//                    i8272_r1 = NW;      // set not writable in ST1
//                    i8272_r3 = fddst[uptr->u6] + WP;
//                    i8272_msr |= (RQM + DIO + CB); /* enter result phase */
//                    sim_printf("\nWrite Protected fddst[%d]=%02X\n", uptr->u6, fddst[uptr->u6]); 
//                    if (isbc208_dev.dctrl & DEBUG_flow)
//                        sim_printf("208_svc: FDC write: Write Protected\n"); 
            } else {                // get image addr for this d, h, c, s    
                imgadr = (cyl * bpc) + (h * bpt) + ((sec - 1) * ssize);
                sim_debug (DEBUG_write, &isbc208_dev, 
                    "208_svc: FDC write: DMA adr=%04X cnt=%04X imgadr=%04X\n", 
                        i8237_r0, i8237_r1, imgadr);
                for (i=0; i<=i8237_r1; i++) { /* copy selected memory to image */
                    data = multibus_get_mbyte(i8237_r0 + i);
                    *(isbc208_buf[uptr->u6] + (imgadr + i)) = data;
                }
                //*** quick fix. Needs more thought!
                fp = fopen(uptr->filename, "wb"); // write out modified image
                for (i=0; i<uptr->capac; i++) {
                    c = *(isbc208_buf[uptr->u6] + i) & 0xFF;
                    fputc(c, fp);
                }
                fclose(fp);
//*** need to step return results IAW table 3-11 in 143078-001
                i8272_w2 = cyl;     /* generate a current address mark */
                i8272_w3 = hed >> 2;
                i8272_w4 = ++sec;   /* next sector */
                i8272_w5 = secn;
                i8272_r0 = hed + drv; /* command done - no error */
                i8272_r3 = fddst[uptr->u6];
                i8272_msr |= (RQM + DIO + CB); /* enter result phase */
            }
            rsp = wsp = 0;          /* reset indexes */
            set_irq(SBC208_INT);    /* set interrupt */
//                sim_printf("WRITE-x: fddst=%02X\n", fddst[uptr->u6]);
            break;
        case FMTTRK:                /* 0x0D */
            if ((fddst[uptr->u6] & RDY) == 0) {
                i8272_r0 = IC_ABNORM + NR + hed + drv; /* Not ready error*/
                i8272_msr |= (RQM + DIO + CB); /* enter result phase */
                sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: Not Ready\n"); 
            } else if (fddst[uptr->u6] & WP) {
                i8272_r0 = IC_ABNORM + hed + drv; /* write protect error*/
                i8272_r3 = fddst[uptr->u6] + WP;
                i8272_msr |= (RQM + DIO + CB); /* enter result phase */
                sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: Write Protected\n"); 
            } else {
                ;                   /* do nothing for now */
                i8272_msr |= (RQM + DIO + CB); /* enter result phase */
            }
            rsp = wsp = 0;          /* reset indexes */
            set_irq(SBC208_INT);    /* set interrupt */
            break;
        case SENINT:                /* 0x08 */
            i8272_msr |= (RQM + DIO + CB); /* enter result phase */
            i8272_r0 = hed + drv;   /* command done - no error */
            i8272_r1 = 0;
            i8272_r2 = 0;
            rsp = wsp = 0;          /* reset indexes */
            clr_irq(SBC208_INT);    /* clear interrupt */
            break;
        case SENDRV:                /* 0x04 */
            sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: FDC sense drive: d=%d fddst=%02X\n",
            drv, fddst[uptr->u6]);
            i8272_msr |= (RQM + DIO + CB); /* enter result phase */
            i8272_r0 = hed + drv;   /* command done - no error */
            i8272_r1 = 0;
            i8272_r2 = 0;
            i8272_r3 = fddst[drv];  /* drv status */
            rsp = wsp = 0;          /* reset indexes */
            break;
        case HOME:                  /* 0x07 */
//                sim_printf("HOME-e: fddst=%02X\n", fddst[uptr->u6]);
            sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: FDC home: d=%d fddst=%02X\n",
                drv, fddst[uptr->u6]);
            if ((fddst[uptr->u6] & RDY) == 0) {
                i8272_r0 = IC_ABNORM + NR + hed + drv; /* Not ready error*/
                i8272_r3 = fddst[uptr->u6];
                sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: Not Ready\n"); 
            } else {
                cyl = 0;            /* now on cylinder 0 */
                fddst[drv] |= T0;   /* set status flag */
                i8272_r0 = SE + hed + drv; /* seek end - no error */
            }
            i8272_r1 = 0;
            i8272_r2 = 0;
            i8272_msr &= ~(RQM + DIO + CB + hed + drv); /* execution phase done*/
            i8272_msr |= RQM;       /* enter COMMAND phase */
            rsp = wsp = 0;          /* reset indexes */
            set_irq(SBC208_INT);    /* set interrupt */
//                sim_printf("HOME-x: fddst=%02X\n", fddst[uptr->u6]);
            break;
        case SPEC:                  /* 0x03 */
            fddst[0] |= TS;         //*** bad, bad, bad!
            fddst[1] |= TS;
            fddst[2] |= TS;
            fddst[3] |= TS;
//                sim_printf("SPEC-e: fddst[%d]=%02X\n", uptr->u6, fddst[uptr->u6]);
            sim_debug (DEBUG_flow, &isbc208_dev, 
                "208_svc: FDC specify: SRT=%d ms HUT=%d ms HLT=%d ms \n", 
                    16 - (drv >> 4), 16 * (drv & 0x0f), i8272_w2 & 0xfe);
            i8272_r0 = hed + drv;   /* command done - no error */
            i8272_r1 = 0;
            i8272_r2 = 0;
            i8272_msr &= ~(RQM + DIO + CB); /* execution phase done*/
            i8272_msr = 0;          // force 0 for now, where does 0x07 come from?
            i8272_msr |= RQM;       /* enter command phase */
            rsp = wsp = 0;          /* reset indexes */
//                sim_printf("SPEC-x: fddst[%d]=%02X\n", uptr->u6, fddst[uptr->u6]);
            break;
        case READID:                /* 0x0A */
            if ((fddst[uptr->u6] & RDY) == 0) {
                i8272_r0 = IC_RC + NR + hed + drv; /* Not ready error*/
                i8272_r3 = fddst[uptr->u6];
                sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: Not Ready\n"); 
            } else {
                i8272_w2 = cyl;     /* generate a valid address mark */
                i8272_w3 = hed >> 2;
                i8272_w4 = 1;       /* always sector 1 */
                i8272_w5 = secn;
                i8272_r0 = hed + drv; /* command done - no error */
                i8272_msr &= ~(RQM + DIO + CB); /* execution phase done*/
                i8272_msr |= RQM;   /* enter command phase */
            }
            i8272_r1 = 0;
            i8272_r2 = 0;
            rsp = wsp = 0;          /* reset indexes */
            break;
        case SEEK:                  /* 0x0F */
//                sim_printf("SEEK-e: fddst=%02X\n", fddst[uptr->u6]);
            sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: FDC seek: d=%d c=%d fddst=%02X\n",
                drv, i8272_w2, fddst[uptr->u6]);
            if ((fddst[uptr->u6] & RDY) == 0) { /* Not ready? */
                i8272_r0 = IC_ABNORM + NR + hed + drv; /* error*/
                i8272_r3 = fddst[uptr->u6];
                sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: FDC seek: Not Ready\n"); 
            } else if (i8272_w2 >= maxcyl[uptr->u6]) {
                i8272_r0 = IC_ABNORM + RDY + hed + drv; /* seek error*/
                sim_debug (DEBUG_flow, &isbc208_dev, "208_svc: FDC seek: Invalid Cylinder %d\n", i8272_w2); 
            } else {
                i8272_r0 |= SE + hed + drv; /* command done - no error */
                cyl = i8272_w2;         /* new cylinder number */
                if (cyl == 0) {         /* if cyl 0, set flag */
                    fddst[drv] |= T0;   /* set T0 status flag */
                    i8272_r3 |= T0;
                } else {
                    fddst[drv] &= ~T0;   /* clear T0 status flag */
                    i8272_r3 &= ~T0;
                }
            }
            i8272_r1 = 0;
            i8272_r2 = 0;
            i8272_msr &= ~(RQM + DIO + CB + hed + drv); /* execution phase done*/
            i8272_msr |= RQM;       /* enter command phase */
            rsp = wsp = 0;          /* reset indexes */
//                set_irq(SBC208_INT);    /* set interrupt */
//                sim_printf("SEEK-x: fddst=%02X\n", fddst[uptr->u6]);
            break;
        default:
            i8272_msr &= ~(RQM + DIO + CB); /* execution phase done*/
            i8272_msr |= RQM;       /* enter command phase */
            i8272_r0 = IC_INVC + hed + drv; /* set bad command error */
            i8272_r1 = 0;
            i8272_r2 = 0;
            rsp = wsp = 0;          /* reset indexes */
            break;
        }
        pcmd = cmd;                     /* save for result phase */
        cmd = 0;                        /* reset command */
        sim_debug (DEBUG_flow, &isbc208_dev, 
            "208_svc: Exit: msr=%02X ST0=%02X ST1=%02X ST2=%02X ST3=%02X\n",
                i8272_msr, i8272_r0, i8272_r1, i8272_r2, i8272_r3); 
    }
    sim_activate (&isbc208_unit[uptr->u6], isbc208_unit[uptr->u6].wait);
    return SCPE_OK;
}

/* Reset routine */

t_stat isbc208_reset (DEVICE *dptr)
{
    reg_dev(isbc208_r0, SBC208_BASE + 0, 0); 
    reg_dev(isbc208_r1, SBC208_BASE + 1, 0); 
    reg_dev(isbc208_r2, SBC208_BASE + 2, 0); 
    reg_dev(isbc208_r3, SBC208_BASE + 3, 0); 
    reg_dev(isbc208_r4, SBC208_BASE + 4, 0); 
    reg_dev(isbc208_r5, SBC208_BASE + 5, 0); 
    reg_dev(isbc208_r6, SBC208_BASE + 6, 0); 
    reg_dev(isbc208_r7, SBC208_BASE + 7, 0); 
    reg_dev(isbc208_r8, SBC208_BASE + 8, 0); 
    reg_dev(isbc208_r9, SBC208_BASE + 9, 0); 
    reg_dev(isbc208_rA, SBC208_BASE + 10, 0); 
    reg_dev(isbc208_rB, SBC208_BASE + 11, 0); 
    reg_dev(isbc208_rC, SBC208_BASE + 12, 0); 
    reg_dev(isbc208_rD, SBC208_BASE + 13, 0); 
    reg_dev(isbc208_rE, SBC208_BASE + 14, 0); 
    reg_dev(isbc208_rF, SBC208_BASE + 15, 0); 
    reg_dev(isbc208_r10, SBC208_BASE + 16, 0); 
    reg_dev(isbc208_r11, SBC208_BASE + 17, 0); 
    reg_dev(isbc208_r12, SBC208_BASE + 18, 0); 
    reg_dev(isbc208_r13, SBC208_BASE + 19, 0); 
    reg_dev(isbc208_r14, SBC208_BASE + 20, 0); 
    reg_dev(isbc208_r15, SBC208_BASE + 21, 0); 
    if ((isbc208_dev.flags & DEV_DIS) == 0) 
        isbc208_reset1();
    return SCPE_OK;
}

void isbc208_reset1 (void)
{
    int32 i;
    UNIT *uptr;
    static int flag = 1;

    if (flag) sim_printf("iSBC 208: Initializing\n");
    for (i = 0; i < FDD_NUM; i++) {     /* handle all units */
        uptr = isbc208_dev.units + i;
        if (uptr->capac == 0) {         /* if not configured */
//            sim_printf("   SBC208%d: Not configured\n", i);
//            if (flag) {
//                sim_printf("      ALL: \"set isbc208 en\"\n");
//                sim_printf("      EPROM: \"att isbc2080 <filename>\"\n");
//                flag = 0;
//            }
            uptr->capac = 0;            /* initialize unit */
            uptr->u3 = 0; 
            uptr->u4 = 0;
            uptr->u5 = 0;
            uptr->u6 = i;               /* unit number - only set here! */
            fddst[i] = WP + T0 + i;     /* initial drive status */
            uptr->flags |= UNIT_WPMODE; /* set WP in unit flags */
            sim_activate (&isbc208_unit[uptr->u6], isbc208_unit[uptr->u6].wait);
        } else {
            fddst[i] = RDY + WP + T0 + i; /* initial attach drive status */
//            sim_printf("   SBC208%d: Configured, Attached to %s\n", i, uptr->filename);
        }
    }
    i8237_r8 = 0;                       /* status */
    i8237_r9 = 0;                       /* command */
    i8237_rB = 0x0F;                    /* mask */
    i8237_rC = 0;                       /* request */
    i8237_rD = 0;                       /* first/last FF */
    i8272_msr = RQM;                    /* 8272 ready for start of command */
    rsp = wsp = 0;                      /* reset indexes */
    cmd = 0;                            /* clear command */
    if (flag) {
        sim_printf("   8237 Reset\n");
        sim_printf("   8272 Reset\n");
    }
    flag = 0;
}

/* isbc208 attach - attach an .IMG file to a FDD */

t_stat isbc208_attach (UNIT *uptr, CONST char *cptr)
{
    t_stat r;
    FILE *fp;
    int32 i, c = 0;
    long flen;

    sim_debug (DEBUG_flow, &isbc208_dev, "   isbc208_attach: Entered with cptr=%s\n", cptr);
    if ((r = attach_unit (uptr, cptr)) != SCPE_OK) { 
        sim_printf("   isbc208_attach: Attach error\n");
        return r;
    }
    fp = fopen(uptr->filename, "rb");
    if (fp == NULL) {
        sim_printf("   Unable to open disk img file %s\n", uptr->filename);
        sim_printf("   No disk image loaded!!!\n");
    } else {
        sim_printf("iSBC 208: Attach\n");
        fseek(fp, 0, SEEK_END);         /* size disk image */
        flen = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        if (isbc208_buf[uptr->u6] == NULL) { /* no buffer allocated */
            isbc208_buf[uptr->u6] = (uint8 *)malloc(flen);
            if (isbc208_buf[uptr->u6] == NULL) {
                sim_printf("   iSBC208_attach: Malloc error\n");
                return SCPE_MEM;
            }
        }
        uptr->capac = flen;
        i = 0;
        c = fgetc(fp);                  // copy disk image into buffer
        while (c != EOF) {
            *(isbc208_buf[uptr->u6] + i++) = c & 0xFF;
            c = fgetc(fp);
        }
        fclose(fp);
        fddst[uptr->u6] |= RDY;         /* set unit ready */
        if (flen == 368640) {           /* 5" 360K DSDD */
            maxcyl[uptr->u6] = 40;
            fddst[uptr->u6] |= TS;      // two sided
        }
        else if (flen == 737280) {      /* 5" 720K DSQD / 3.5" 720K DSDD */
            maxcyl[uptr->u6] = 80;
            fddst[uptr->u6] |= TS;      // two sided
        }
        else if (flen == 1228800) {     /* 5" 1.2M DSHD */
            maxcyl[uptr->u6] = 80;
            fddst[uptr->u6] |= TS;      // two sided
        }
        else if (flen == 1474560) {     /* 3.5" 1.44M DSHD */
            maxcyl[uptr->u6] = 80;
            fddst[uptr->u6] |= TS;      // two sided
        }
        sim_printf("   Drive-%d: %d bytes of disk image %s loaded, fddst=%02X\n", 
            uptr->u6, i, uptr->filename, fddst[uptr->u6]);
    }
    sim_debug (DEBUG_flow, &isbc208_dev, "   iSBC208_attach: Done\n");
    return SCPE_OK;
}

/* isbc208 set mode = 8- or 16-bit data bus */
/* always 8-bit mode for current simulators */

t_stat isbc208_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    sim_debug (DEBUG_flow, &isbc208_dev, "   isbc208_set_mode: Entered with val=%08XH uptr->flags=%08X\n", 
        val, uptr->flags);
    if (val & UNIT_WPMODE) {            /* write protect */
        fddst[uptr->u6] |= WP;
        uptr->flags |= val;
    } else {                            /* read write */
        fddst[uptr->u6] &= ~WP;
        uptr->flags &= ~val;
    }
//    sim_printf("fddst[%d]=%02XH uptr->flags=%08X\n", uptr->u6, fddst[uptr->u6], uptr->flags);
    sim_debug (DEBUG_flow, &isbc208_dev, "   isbc208_set_mode: Done\n");
    return SCPE_OK;
}

/*  I/O instruction handlers, called from the CPU module when an
    IN or OUT instruction is issued.

    Each function is passed an 'io' flag, where 0 means a read from
    the port, and 1 means a write to the port.  On input, the actual
    input is passed as the return value, on output, 'data' is written
    to the device.
*/

uint8 isbc208_r0(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read current address CH 0 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r0(H) read as %04X\n", i8237_r0);
            return (i8237_r0 >> 8);
        } else {                        /* low byte */
            i8237_rD++;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r0(L) read as %04X\n", i8237_r0);
            return (i8237_r0 & 0xFF);
        }
    } else {                            /* write base & current address CH 0 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            i8237_r0 |= (data << 8);
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r0(H) set to %04X\n", i8237_r0);
        } else {                        /* low byte */
            i8237_rD++;
            i8237_r0 = data & 0xFF;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r0(L) set to %04X\n", i8237_r0);
        }
        return 0;
    }
}

uint8 isbc208_r1(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read current word count CH 0 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r1(H) read as %04X\n", i8237_r1);
            return (i8237_r1 >> 8);
        } else {                        /* low byte */
            i8237_rD++;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r1(L) read as %04X\n", i8237_r1);
            return (i8237_r1 & 0xFF);
        }
    } else {                            /* write base & current address CH 0 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            i8237_r1 |= (data << 8);
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r1(H) set to %04X\n", i8237_r1);
        } else {                        /* low byte */
            i8237_rD++;
            i8237_r1 = data & 0xFF;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r1(L) set to %04X\n", i8237_r1);
        }
        return 0;
    }
}

uint8 isbc208_r2(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read current address CH 1 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r2(H) read as %04X\n", i8237_r2);
            return (i8237_r2 >> 8);
        } else {                        /* low byte */
            i8237_rD++;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r2(L) read as %04X\n", i8237_r2);
            return (i8237_r2 & 0xFF);
        }
    } else {                            /* write base & current address CH 1 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            i8237_r2 |= (data << 8);
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r2(H) set to %04X\n", i8237_r2);
        } else {                        /* low byte */
            i8237_rD++;
            i8237_r2 = data & 0xFF;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r2(L) set to %04X\n", i8237_r2);
        }
        return 0;
    }
}

uint8 isbc208_r3(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read current word count CH 1 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r3(H) read as %04X\n", i8237_r3);
            return (i8237_r3 >> 8);
        } else {                        /* low byte */
            i8237_rD++;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r3(L) read as %04X\n", i8237_r3);
            return (i8237_r3 & 0xFF);
        }
    } else {                            /* write base & current address CH 1 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            i8237_r3 |= (data << 8);
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r3(H) set to %04X\n", i8237_r3);
        } else {                        /* low byte */
            i8237_rD++;
            i8237_r3 = data & 0xFF;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r3(L) set to %04X\n", i8237_r3);
        }
        return 0;
    }
}

uint8 isbc208_r4(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read current address CH 2 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r4(H) read as %04X\n", i8237_r4);
            return (i8237_r4 >> 8);
        } else {                        /* low byte */
            i8237_rD++;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r4(L) read as %04X\n", i8237_r4);
            return (i8237_r4 & 0xFF);
        }
    } else {                            /* write base & current address CH 2 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            i8237_r4 |= (data << 8);
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r4(H) set to %04X\n", i8237_r4);
        } else {                        /* low byte */
            i8237_rD++;
            i8237_r4 = data & 0xFF;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r4(L) set to %04X\n", i8237_r4);
        }
        return 0;
    }
}

uint8 isbc208_r5(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read current word count CH 2 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r5(H) read as %04X\n", i8237_r5);
            return (i8237_r5 >> 8);
        } else {                        /* low byte */
            i8237_rD++;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r5(L) read as %04X\n", i8237_r5);
            return (i8237_r5 & 0xFF);
        }
    } else {                            /* write base & current address CH 2 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            i8237_r5 |= (data << 8);
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r5(H) set to %04X\n", i8237_r5);
        } else {                        /* low byte */
            i8237_rD++;
            i8237_r5 = data & 0xFF;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r5(L) set to %04X\n", i8237_r5);
        }
        return 0;
    }
}

uint8 isbc208_r6(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read current address CH 3 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r6(H) read as %04X\n", i8237_r6);
            return (i8237_r6 >> 8);
        } else {                        /* low byte */
            i8237_rD++;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r6(L) read as %04X\n", i8237_r6);
            return (i8237_r6 & 0xFF);
        }
    } else {                            /* write base & current address CH 3 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            i8237_r6 |= (data << 8);
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r6(H) set to %04X\n", i8237_r6);
        } else {                        /* low byte */
            i8237_rD++;
            i8237_r6 = data & 0xFF;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r6(L) set to %04X\n", i8237_r6);
        }
        return 0;
    }
}

uint8 isbc208_r7(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read current word count CH 3 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r7(H) read as %04X\n", i8237_r7);
            return (i8237_r7 >> 8);
        } else {                        /* low byte */
            i8237_rD++;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r7(L) read as %04X\n", i8237_r7);
            return (i8237_r7 & 0xFF);
        }
    } else {                            /* write base & current address CH 3 */
        if (i8237_rD) {                 /* high byte */
            i8237_rD = 0;
            i8237_r7 |= (data << 8);
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r7(H) set to %04X\n", i8237_r7);
        } else {                        /* low byte */
            i8237_rD++;
            i8237_r7 = data & 0xFF;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r7(L) set to %04X\n", i8237_r7);
        }
        return 0;
    }
}

uint8 isbc208_r8(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read status register */
        sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r8 (status) read as %02X\n", i8237_r8);
        return (i8237_r8);
    } else {                            /* write command register */
        i8237_r9 = data & 0xFF;
        sim_debug (DEBUG_reg, &isbc208_dev, "i8237_r9 (command) set to %02X\n", i8237_r9);
        return 0;
    }
}

uint8 isbc208_r9(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_r9\n");
        return 0;
    } else {                            /* write request register */
        i8237_rC = data & 0xFF;
        sim_debug (DEBUG_reg, &isbc208_dev, "i8237_rC (request) set to %02X\n", i8237_rC);
        return 0;
    }
}

uint8 isbc208_rA(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_rA\n");
        return 0;
    } else {                            /* write single mask register */
        switch(data & 0x03) {
        case 0:
            if (data & 0x04)
                i8237_rB |= 1;
            else
                i8237_rB &= ~1;
            break;
        case 1:
            if (data & 0x04)
                i8237_rB |= 2;
            else
                i8237_rB &= ~2;
            break;
        case 2:
            if (data & 0x04)
                i8237_rB |= 4;
            else
                i8237_rB &= ~4;
            break;
        case 3:
            if (data & 0x04)
                i8237_rB |= 8;
            else
                i8237_rB &= ~8;
            break;
        }
        sim_debug (DEBUG_reg, &isbc208_dev, "i8237_rB (mask) set to %02X\n", i8237_rB);
        return 0;
    }
}

uint8 isbc208_rB(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_rB\n");
        return 0;
    } else {                            /* write mode register */
        i8237_rA = data & 0xFF;
        sim_debug (DEBUG_reg, &isbc208_dev, "i8237_rA (mode) set to %02X\n", i8237_rA);
        return 0;
    }
}

uint8 isbc208_rC(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_rC\n");
        return 0;
    } else {                            /* clear byte pointer FF */
        i8237_rD = 0;
        sim_debug (DEBUG_reg, &isbc208_dev, "i8237_rD (FF) cleared\n");
        return 0;
    }
}

uint8 isbc208_rD(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read temporary register */
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_rD\n");
        return 0;
    } else {                            /* master clear */
        isbc208_reset1();
        sim_debug (DEBUG_reg, &isbc208_dev, "i8237 master clear\n");
        return 0;
    }
}

uint8 isbc208_rE(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_rE\n");
        return 0;
    } else {                            /* clear mask register */
        i8237_rB = 0;
        sim_debug (DEBUG_reg, &isbc208_dev, "i8237_rB (mask) cleared\n");
        return 0;
    }
}

uint8 isbc208_rF(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_rF\n");
        return 0;
    } else {                            /* write all mask register bits */
        i8237_rB = data & 0x0F;
        sim_debug (DEBUG_reg, &isbc208_dev, "i8237_rB (mask) set to %02X\n", i8237_rB);
        return 0;
    }
}

uint8 isbc208_r10(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read FDC status register */
        sim_debug (DEBUG_reg, &isbc208_dev, "i8272_msr read as %02X\n", i8272_msr);
        return i8272_msr;
    } else { 
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal write to isbc208_r10\n");
        return 0;
    }
}

// read/write FDC data register
uint8 isbc208_r11(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read FDC data register */
        wsp = 0;                        /* clear write stack index */
        switch (rsp) {                  /* read from next stack register */
        case 0:
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_r1 read as %02X\n", i8272_r1);
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_r3 read as %02X\n", i8272_r3);
            rsp++;                  /* step read stack index */
            clr_irq(SBC208_INT);    /* clear interrupt */
            if (pcmd == SENDRV) {
                i8272_msr = RQM;    /* result phase SENDRV done */
                return i8272_r1;    // SENDRV return ST1
            }
            return i8272_r0;        /* ST0 */
        case 1:
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_r2 read as %02X\n", i8272_r2);
            rsp++;                  /* step read stack index */
            if (pcmd == SENINT) {
                i8272_msr = RQM;    /* result phase SENINT done */
                return cyl;         // SENINT return current cylinder
            }
            return i8272_r1;        /* ST1 */
        case 2:
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_r3 read as %02X\n", i8272_r3);
            rsp++;                  /* step read stack index */
            return i8272_r2;        /* ST2 */
        case 3:
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w2 read as %02X\n", i8272_w2);
            rsp++;                  /* step read stack index */
            return i8272_w2;        /* C - cylinder */
        case 4:
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w3 read as %02X\n", i8272_w3);
            rsp++;                  /* step read stack index */
            return i8272_w3;        /* H - head */
        case 5:
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w4 read as %02X\n", i8272_w4);
            rsp++;                  /* step read stack index */
            return i8272_w4;        /* R - sector */
        case 6:
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w5 read as %02X\n", i8272_w5);
            i8272_msr = RQM;        /* result phase ALL OTHERS done */
            return i8272_w5;        /* N  - sector size*/
        }
    } else {                            /* write FDC data register */ 
        rsp = 0;                        /* clear read stack index */
        switch (wsp) {                  /* write to next stack register */
        case 0:
            i8272_w0 = data;        /* rws = MT + MFM + SK + cmd */
            cmd = data & 0x1F;      /* save the current command */
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w0 set to %02X\n", data);
            if (cmd == SENINT) {
                i8272_msr = CB;     /* command phase SENINT done */
                return 0;
            }
            wsp++;                  /* step write stack index */
            break;
        case 1:
            i8272_w1 = data;        /* rws = hed + drv */
            if (cmd != SPEC)
                drv = data & 0x03;
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w1 set to %02X\n", data);
            if (cmd == HOME || cmd == SENDRV || cmd == READID) {
                i8272_msr = CB + hed + drv; /* command phase HOME, READID and SENDRV done */
                return 0;
            }
            wsp++;                  /* step write stack index */
            break;
        case 2:
            i8272_w2 = data;        /* rws = C */
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w2 set to %02X\n", data);
            if (cmd == SPEC || cmd == SEEK) {
                i8272_msr = CB + hed + drv; /* command phase SPECIFY and SEEK done */
                return 0;
            }
            wsp++;                  /* step write stack index */
            break;
        case 3:
            i8272_w3 = data;        /* rw = H */
            hed = data;
            wsp++;                  /* step write stack index */
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w3 set to %02X\n", data);
            break;
        case 4:
            i8272_w4 = data;        /* rw = R */
            sec = data;
            wsp++;                  /* step write stack index */
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w4 set to %02X\n", data);
            break;
        case 5:
            i8272_w5 = data;        /* rw = N */
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w5 set to %02X\n", data);
            if (cmd == FMTTRK) {
                i8272_msr = CB + hed + drv; /* command phase FMTTRK done */
                return 0;
            }
            wsp++;                  /* step write stack index */
            break;
        case 6:
            i8272_w6 = data;        /* rw = last sector number */
            wsp++;                  /* step write stack index */
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w6 set to %02X\n", data);
            break;
        case 7:
            i8272_w7 = data;        /* rw = gap length */
            wsp++;                  /* step write stack index */
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w7 set to %02X\n", data);
            break;
        case 8:
            i8272_w8 = data;        /* rw = bytes to transfer */
            sim_debug (DEBUG_reg, &isbc208_dev, "i8272_w8 set to %02X\n", data);
            if (cmd == READ || cmd == WRITE)
                i8272_msr = CB + hed + drv; /* command phase all others done */
            break;
        }
    }
    return 0;
}

uint8 isbc208_r12(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {                      /* read interrupt status */
        sim_debug (DEBUG_reg, &isbc208_dev, "isbc208_r12 read as %02X\n", isbc208_i);
        return (isbc208_i);
    } else {                            /* write controller auxillary port */ 
        isbc208_a = data & 0xFF;
        sim_debug (DEBUG_reg, &isbc208_dev, "isbc208_r12 set to %02X\n", isbc208_a);
        return 0;
    }
}

uint8 isbc208_r13(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_r13\n");
        return 0;
    } else {                            /* reset controller */ 
        isbc208_reset1();
        sim_debug (DEBUG_reg, &isbc208_dev, "isbc208_r13 controller reset\n");
        return 0;
    }
}

uint8 isbc208_r14(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_r14\n");
        return 0;
    } else {                            /* Low-Byte Segment Address Register */ 
        isbc208_sr = data & 0xFF;
        sim_debug (DEBUG_reg, &isbc208_dev, "isbc208_sr(L) set to %02X\n", data & 0xFF);
        return 0;
    }
}

uint8 isbc208_r15(t_bool io, uint8 data, uint8 devnum)
{
    if (io == 0) {
        sim_debug (DEBUG_reg, &isbc208_dev, "Illegal read of isbc208_r15\n");
        return 0;
    } else {                            /* High-Byte Segment Address Register */ 
        isbc208_sr |= data << 8;
        sim_debug (DEBUG_reg, &isbc208_dev, "isbc208_sr(H) set to %02X\n", data);
        return 0;
    }
}

/* end of isbc208.c */