This document presents examples of the HORIZON simulator P= option which
defines I/O devices.

To understand these examples, you need to know some details of the Horizon
Hardware which is being simulated. The Horizon implements 8 I/O addresses
in the Z80 I/O range of 00-07 :
    00 = Parallel I/O
    01 = mirror of 00 (not fully decoded)
    02 = Primary 8251 serial port data register
    03 = Primary 8251 serial port control/status regiter
    04 = Secondary 8251 serial port data register
    05 = Secondary 8251 serial port control/status register
    06 = Mainboard control/status register
    07 = mirror of 06 (not fully decoded)
There is also a "bank select" register on some NorthStar RAM cards
    C0 = Ram bank select

The simulator does not emulate the parallel I/O or mainboard status registers
other than to insure that the simulation does not stop if they are read or
written - this hardware was rarely used in Horizon installations, and is not
required to run N*DOS or CP/M. Most horizons used serial devices.

Also note that although most of these examples show Horizon devices, the
simulator is NOT limited to Horizon hardware. You can define additional
8251's at whatever addresses you like, or simulate other UART devices by
changing the initial registers, bit sense and bit toggle/mask definitions.

;
;----------------------------------------------------------------
; This section shows the default definitions which are built into
; the HORIZON simulator if no other I/O is defined
;----------------------------------------------------------------
;

; Definitions for primary 8251 serial port
P=I2 2              <=  IN 02 = Read data from simulator keyboard buffer
    No options are required
P=O2 2              <= OUT 02 = Write data to simulator video display
    No options are required
P=I3 1 5 2          <=  IN 03 = Video/Keyboard status
    opt1 = 05 : Sets TxEmpty and TxReady bits in 8051 status
                (XMIT always ready on video output)
    opt2 = 02 : Sets RxReady bit in 8051 status register
                if a key is available from the virtual console.
P=O3 1              <= OUT 03 = Video/Keyboard control
    No options are required
    This is ignored by the simulator (no programmable options on the
    video/keyboard), however the definition is required to prevent
    an "Unknown I/O port" exception.

; Definitions for secondary 8251 serial port mapped to a file
P=I4 6 1A 1A        <=  IN 04 = Read data from mounted input file
    opt1 = 1A : return a 1A character if no file mounted.
    opt2 = 1A : return a 1A character at end of input file.
P=O4 6              <= OUT 04 = Write data to mounted output file
    No options are required
P=I5 5 5 80 2 1     <=  IN 05 = Input/Output file status
    opt1 = 05 : Sets TxEmpty and TxReady in 8051 status     (*)
    opt2 = 80 : Set DSR bit if an output file is mounted    (*)
    opt3 = 02 : Set RxReady bit if input file data is available.
    opt4 = 01 : Insert an EOF character when the file hits EOF.
    opt5 = 00 : (default value) No gaps between input bytes.
    (*) Although it would seem to make sense to set TxEmpty and TxReady
        bits only when an output file is available (preventing writes to
        the port if no file is mounted) in practice this proves annoying
        as some programs (NorthStar CP/M for one) write a byte to this
        address when they start, causing the simulator to appear to
        "hang" until an output file is mounted. The default config was
        changed to allow writes "anytime" (data is tossed if not file
        is mounted).
P=O5 5              <= OUT 05 = Input/Output file control
    No options are requried
    This is ignored by the simulator (no programmable options
    file I/O), however the definition is required to prevent an
    "Unknown I/O port" exception.

; Definitions to ignore other Horizon I/O ports so that the simulator
; will not trap on accesses to them
P=I0 7              <=  IN 00 = Ignore read  or parallel port)
P=I1 7              <=  IN 01 = Same as 00 (mirrored address)
    opt1 = 00 : (default value) value return if this port is read
P=O0 7              <= OUT 00 = Ignore write to parallel port
P=O1 7              <= OUT 01 = Same as 00 (mirrored address)
    No options are required
P=I6 7              <=  IN 06 = Ignore read of Mainboard status register
P=I7 7              <=  IN 07 = Same as 06 (mirrored address)
    opt1 = 00 : (default value) - value returned if this port is read
P=O6 7              <= OUT 06 = Ignore write to Mainboard control register
P=O7 7              <= OUT 07 = Same as 06 (mirrored address)
    No options are required

P=OC0 7             <= OUT C0 = Ignore write to RAM bank select register
    No options are requried


;
;-------------------------------------------------------------------------
; This section shows an alternate I/O definition for the horizon secondary
; 8251 uart which connect it to the PC COM1 serial port.
;-------------------------------------------------------------------------
; This example shows PC COM1 at address 3F8 - to use COM2, COM3 or COM4
; change this address to: 2F8, 3E8 or 2E8 respectively.
;
; This example shows the Horizon secondary serial port at Z80 addresses
; 04 and 05. You can redirect the primary port to a PC COM port if you wish
; by using the same definitions but with Z80 addresses 02 and 03 (In this
; case, the virtual Horizon console will come up on a terminal connected to
; the PC serial port).
;

; The 'S' type sets up the PC COM port with the specified parameters.
; It does not actually define a Z80 I/O port
P=S3F8 9600 N81     <= Initialize COM1 at address 3F8
    addr = 3F8  : PC I/O address of COM1 serial device.
    opt1 = 9600 : Set baudrate to 9600
    opt2 = N81  : Set 'N'o parity, 8 data bits and 1 stop bit

; 8251 data simply read/writes PC 8250 data
P=I04 4 3F8         <=  IN 02 = Read serial port data
    opt1 = 3F8  : PC I/O address of COM1 serial device
    opt2 = 00   : (default value) XOR applied to data read (flip no bits)
P=O04 4 3F8         <= OUT 02 = Write serial port data
    opt1 = 3F8  : PC I/O address of COM1 serial device
    opt2 = 00   : (default value) XOR applied to write data (flip no bits)

; 8251 status register is translated from PC serial port status
P=I05 3 3F8 00 05 02 00 20 08 10 80 <=  IN 03 = Read uart status
    opt1 = 3F8  : PC I/O address of COM1 serial device
    opt2 = 00   : Begin with this value for 8251 status register
    opt3 = 05   : XOR (toggle) 8251 TxE and TxR if PC uart TxReady
    opt4 = 02   : XOR (toggle) 8251 TxReady if PC uart has data available
    opt5 = 00   : No toggle here - 8251 does not support BREAK detect
    opt6 = 20   : XOR (toggle) 8251 FE if PC reports Framing Error
    opt7 = 08   : XOR (toggle) 8251 PE if PC reports Parity Error
    opt8 = 10   : XOR (toggle) 8251 OE if PC reports Overrun Error
    opt9 = 80   : XOR (toggle) 8251 DSP if PC reports DSR raised
    opt10= 00   : (default value) No toggle - 8251 does not support CTS
    opt11= 00   : (default value) No toggle - 8251 does not support DCD
    opt12= 00   : (default value) No toggle - 8251 does not support RI

; 8251 control is translated into PC control of DTR and RTS
P=O 05 3 3F8 00 02 20       ; Console control (COM 1)
    opt1 = 3F8  : PC I/O address of COM1 serial device
    opt2 = 00   : XOR (toggle) no bits (already at correct sense)
    opt3 = 02   : Mask for 8251 DTR control - set COM1 DTR if set
    opt4 = 20   : Mask for 8251 RTS control - set COM1 RTS if set


;
;-------------------------------------------------------------------------
; This section shows the IO definitions which I use to simulate the serial
; port of my Vector 1+ system. This is NOT an 8251 - it is a "dumb" UART
; which is hard-jumpered for configuration and has it's TxR and RxR bits
; available as physical signals which are wired to Bit7 and Bit6 of an
; I/O port respectively.
;-------------------------------------------------------------------------
;
P=I0 1 80 40    <=  IN 00 = Read keyboard/video status
    opt1 = 80 = Begin status with this value (bit7 = TXready always)
    opt2 = 40 = XOR (Toggle) bit6 if PC keyboard has data available
P=O0 1          <= OUT 01 = Write keyboard/video control (ignored)
P=I1 2          <=  IN 01 = Read keyboard data (no options required)
P=O1 2          <= OUT 01 = Write video data (no options required)


;
;--------------------------------------------------------------------
; This is sample 8086 code for a very simple P=E... I/O port handler.
; It initializes all I/O ports to return a value equal to their index.
; Writing to an I/O port will change it's value only (will read back
; what was written). This sample code can be assembled with Dunfield
; Development Services ASM86 assember:
;       (save from this section to a file called XIO.ASM)
;       ASM86 xio -f                    <= Assemble with full listing
;       HEXFMT xio.hex -bt w=xio.HIO    <= Convert to binary: XIO.HIO
; Load into simulator with:
;       HORIZON ... P=Exio
;--------------------------------------------------------------------
;
; General notes on external I/O handlers:
;
; All functions MUST preserve DS, SS, BP & SP
; All functions MUST return via RETF (8086 FAR return)
;
; Init function receives no parameters and returns no values. It is called
; once at load time to initialize the I/O system (if necessary).
;
; Read function is passed port number in BX (0000-00FF) and must return the
; port value in AX (0000-00FF) if AH contains !0 the simulator will trap
; with "Unknown I/O port"
;
; Write function is passed port number in BX (0000-00FF) and the value to
; write in AL. It must return with AX=0000 if AX contains !0 the simulator
; will trap with "Unknown I/O port"
;
; Note: on entry you have the simulators DS - DO NOT write into the data
; segment - if you need to use DS, save it, copy CS into it (tiny model)
; and restore it before you return. If you need more than a 300 bytes of
; stack, you should save, change and restore SP. You have the entire 64k
; segment to work with (which is NOT initialized other than loading the
; code module)
;
; Sample entry/exit code which provides local Stack and Data segment:
;
;       MOV     CX,CS           ; Get I/O module segment
;       MOV     DS,CX           ; DS = I/O module segment
;       MOV     SimSG,SS        ; Save Simulator segment
;       MOV     SimSP,SP        ; Save Simulator stack pointer
;       CLI                     ; No interrupt while moving stack
;       MOV     SS,CX           ; Set stack segment
;       MOV     SP,#$FFFE       ; Set stack pointer
;       STI                     ; Stack is stable again
;       ; ... your I/O handling code here ...
;       CLI                     ; No interrupt while moving stack
;       MOV     SS,SimSG        ; SP = simulator segment
;       MOV     SP,SimSP        ; Get simulator stack pointer
;       STI                     ; Stack is stable again
;       MOV     DS,SimSG        ; DS = simulator segment
;       RETF                    ; Back to simulator
;SimSG: DS      2               ; Save area for simulator segment
;SimSP: DS      2               ; Save area for simulator stack pointer
;
;
        ORG     $0000                   ; Jump table starts at 0
        JMP     INIT                    ; 0000 = Init function
        JMP     WRITE                   ; 0003 = Write function
        EQU     *                       ; 0006 = Read function
; Reading an IO port - get data from array in code segment
; Entry: BX=port number (0000-00FF)
; Exit :  AL=Data read,  AH=0 for success, !0 for bad-port
        MOV     AL,CS:data[BX]          ; Read data from array
        XOR     AH,AH                   ; Report success
        RETF
; Writing an IO port - put data into array in code segment
; Entry: AL=data, BX=port number (0000-00FF)
; Exit :  AX=0 for success, !0 for bad-port
WRITE:  MOV     CS:data[BX],AL          ; Write data to array
        XOR     AX,AX                   ; Report success
        RETF
; Initiaize array in code segment with value=offset
; Entry: nothing
; Exit : nothing
INIT:   XOR     BX,BX                   ; Start with zero
init1:  MOV     CS:data[BX],BL          ; Init with port address
        INC     BX                      ; Advance to next
        AND     BH,BH                   ; Overflow past FF?
        JZ      init1                   ; No, continue
        RETF
; Data array to hold port values
data    EQU     $100

