;
; File:
;                             io.asm
; Description:
;                       DOS-C I/O Subsystem
;
;                       Copyright (c) 1998
;                       Pasquale J. Villani
;                       All Rights Reserved
;
; This file is part of DOS-C.
;
; DOS-C is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version
; 2, or (at your option) any later version.
;
; DOS-C is distributed in the hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
; the GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public
; License along with DOS-C; see the file COPYING.  If not,
; write to the Free Software Foundation, 675 Mass Ave,
; Cambridge, MA 02139, USA.
;
; $Header$
;

                %include "segs.inc"
                %include "stacks.inc"

                extern   ConTable
                extern   LptTable
                extern   ComTable
                extern   uPrtNo
                extern   CommonNdRdExit
;!!                extern   _NumFloppies
                extern   blk_stk_top
                extern   clk_stk_top
                extern   _reloc_call_blk_driver
                extern   _reloc_call_clk_driver

                extern   _TEXT_DGROUP

;---------------------------------------------------
;
; Device entry points
;
; This really should be a struct and go into a request.inc file
;
cmdlen  equ     0                       ; Length of this command
unit    equ     1                       ; Subunit Specified
cmd     equ     2                       ; Command Code
status  equ     3                       ; Status
media   equ     13                      ; Media Descriptor
trans   equ     14                      ; Transfer Address
count   equ     18                      ; Count of blocks or characters
start   equ     20                      ; First block to transfer
vid     equ     22                      ; Volume id pointer
huge    equ     26                      ; First block (32-bit) to transfer

;
; The following is the "array" of device driver headers for the internal
; devices.  There is one header per device including special aux: and prn: 
; pseudo devices.  These psuedo devices are necessary for printer 
; redirection, i.e., serial or parallel ports, and com port aux selection.
;
; The devices are linked into each other and terminate with a -1 next 
; pointer.  This saves some time on boot up and also allows us to throw all
; device initialization into a single io_init function that may be placed 
; into a discardable code segmemnt.
;
segment	_IO_FIXED_DATA

                ;
                ; The "CON" device
                ;
                ; This device is the standard console device used by
                ; DOS-C and kernel
                ;
                global  _con_dev
_con_dev        equ     $
                dw      _prn_dev,LGROUP
                dw      8013h           ; con device (stdin & stdout)
                dw      GenStrategy
                dw      ConIntr
                db      'CON     '

                ;
                ; Generic prn device that can be redirected via mode
                ;
                global  _prn_dev
_prn_dev        dw      _aux_dev,LGROUP
                dw      0A040h
                dw      GenStrategy
                dw      PrnIntr
                db      'PRN     '

                ;
                ; Generic aux device that can be redirected via mode
                ;
                global  _aux_dev
_aux_dev        dw      _Lpt1Dev,LGROUP
                dw      8000h
                dw      GenStrategy
                dw      AuxIntr
                db      'AUX     '

                ;
                ; Printer device drivers
                ;
_Lpt1Dev        dw      _Lpt2Dev,LGROUP
                dw      0A040h
                dw      GenStrategy
                dw      Lpt1Intr
                db      'LPT1    '
_Lpt2Dev        dw      _Lpt3Dev,LGROUP
                dw      0A040h
                dw      GenStrategy
                dw      Lpt2Intr
                db      'LPT2    '
_Lpt3Dev        dw      _Com1Dev,LGROUP
                dw      0A040h
                dw      GenStrategy
                dw      Lpt3Intr
                db      'LPT3    '

                ;
                ; Com device drivers
                ;
_Com1Dev        dw      _Com2Dev,LGROUP
                dw      8000h
                dw      GenStrategy
                dw      AuxIntr
                db      'COM1    '
_Com2Dev        dw      _Com3Dev,LGROUP
                dw      8000h
                dw      GenStrategy
                dw      Com2Intr
                db      'COM2    '
_Com3Dev        dw      _Com4Dev,LGROUP
                dw      8000h
                dw      GenStrategy
                dw      Com3Intr
                db      'COM3    '
_Com4Dev        dw      _clk_dev,LGROUP
                dw      8000h
                dw      GenStrategy
                dw      Com4Intr
                db      'COM4    '

                ;
                ; Header for clock device
                ;
                global  _clk_dev
_clk_dev        equ     $
                dw      _blk_dev,LGROUP
                dw      8008h           ; clock device
                dw      GenStrategy
                dw      clk_entry
                db      'CLOCK$  '

                ;
                ; Header for device
                ;
                global  _blk_dev
_blk_dev        equ     $
                dd      -1
                dw      08c2h           ; block device with ioctl
                dw      GenStrategy
                dw      blk_entry
		db      4
                db      0,0,0,0,0,0,0


;
; Temporary table until next release
;
segment	_IO_FIXED_DATA
DiskTable       db      0


;
; Local storage
;
%if 0
segment	_BSS
blk_dos_stk	resw	1
blk_dos_seg	resw	1
clk_dos_stk	resw	1
clk_dos_seg	resw	1
%endif

segment _IO_TEXT
		global	_ReqPktPtr
_ReqPktPtr	dd	0
uUnitNumber	dw	0


;
; Name:
;       GenStrategy
;
; Function:
;       Store the pointer to the request packet passed in es:bx
;
; Description:
;       Generic strategy routine.  Unlike the original multitasking versions, 
;       this version assumes that no more thank one device driver is active
;       at any time.  The request is stored into memory in the one and only
;       location available for that purpose.
;
                global GenStrategy
GenStrategy:
                mov     word [cs:_ReqPktPtr],bx
                mov     word [cs:_ReqPktPtr+2],es
                retf


;
; Name:
;       XXXXIntr
;
; Function:
;       Individual Interrupt routines for each device driver
;
; Description:
;       This is actually a single routine with entry points for each device. 
;       The name used for the entry point is the device name with Intr 
;       appended to it.
;
;       Funtionally, each device driver has an entry and an associated 
;       table.  The table is a structure that consists of a control byte 
;       followed by an array of pointers to C functions or assembly 
;       subroutines that implement the individual device driver functions.  
;       This allows the usage of common error dummy filler code to be used.  
;       It also allows standardization of the calling procedure for these 
;       internal device driver functions.
;
;       Assembler call/return convention:
;       Each driver function is entered by a jump into the function and 
;       exits by a jump to the appropriate success or error exit routine.  
;       This speeds up the call and return and helps to minimize the stack 
;       useage.  The contents of the request packet are passed to each 
;       routine in registers as follows:
;
;             Register  Function                Description
;             --------  --------                -----------
;               al      unit                    Subunit Specified
;               ah      media                   Media Descriptor
;               cx      count                   Count of blocks or characters
;               dx      start                   First block to transfer
;               es:di   trans                   Transfer Address
;               ds:bx   reqptr                  Request pointer
;               cs      kernel code segment
;               ds      kernel data segment
;
;       The exit routines generally set the status based on the individual 
;       routine.  For example, _IOSuccess will clear the count where 
;       _IOErrCnt will subtract the remaining amount in cx from the original 
;       count.  See each utility routine for expectations.
;
;       C call/return convention:
;       The C calling convention simply sets up the C stack and passes the
;       request packet pointer as a far pointer to the function.  Although 
;       the utility routine names are such that they are accesible from the 
;       C name space, they are cannot used.  Instead, the common interrupt 
;       code expects a return status to set in the request packet.  It is up 
;       to the device driver function to set the appropriate fields such as 
;       count when an error occurs.
;
;       How to differntiate between the two calling conventions:
;       This code is entirely table driven.  The table is a structure that 
;       is generally in the _IO_FIXED_DATA segment.  It consists of a flag 
;       byte followed by short pointers to the driver functions.  Selecting 
;       a driver type is accomplished by setting the type bit in the flag 
;       (see below).
;
;         7   6   5   4   3   2   1   0
;       +---+---+---+---+---+---+---+---+
;       |   |   |   |   |   |   |   |   |
;       +---+---+---+---+---+---+---+---+
;         | |       |                   |---    Number of table entries
;         | |       +-------------------+
;         | |       |-----------------------    Reserved
;         | +-------+
;         +---------------------------------    type bit (1 == C / 0 == asm)
;
ConIntr:
                push    si
                mov     si,ConTable
                jmp     short CharIntrEntry

PrnIntr:
                push    si
                push    ax
                xor     ax,ax
                jmp     short LptCmnIntr

Lpt1Intr:
                push    si
                push    ax
                xor     al,al
                mov     ah,1
                jmp     short LptCmnIntr

Lpt2Intr:
                push    si
                push    ax
                mov     al,1
                mov     ah,2
                jmp     short LptCmnIntr

Lpt3Intr:
                push    si
                push    ax
                mov     al,2
                mov     ah,3

LptCmnIntr:
                mov     si,LptTable
                mov     [cs:uPrtNo],ah
                jmp     short DiskIntrEntry


AuxIntr:
                push    si
                push    ax
                xor     al,al
                jmp     short ComCmnIntr

Com2Intr:
                push    si
                push    ax
                mov     al,1
                jmp     short ComCmnIntr

Com3Intr:
                push    si
                push    ax
                mov     al,2
                jmp     short ComCmnIntr

Com4Intr:
                push    si
                push    ax
                mov     al,3
                jmp     short ComCmnIntr

ComCmnIntr:
                mov     si,ComTable
                jmp     short DiskIntrEntry


DskIntr:
                push    si
                mov     si,DiskTable
CharIntrEntry:
                push    ax
DiskIntrEntry:
                push    cx
                push    dx
                push    di
                push    bp
                push    ds
                push    es
                push    bx
                mov     byte [cs:uUnitNumber],al
                lds     bx,[cs:_ReqPktPtr]
                test    byte [cs:si],80h
                je      AsmType

                mov     al,[bx+cmd]
                cmp     al,[cs:si]
                ja      _IOCommandError
                cbw
                shl     ax,1
                add     si,ax
                xchg    di,ax

                push    ds
                push    bx
                mov     bp,sp
                mov	ds,[cs:_TEXT_DGROUP]
                cld
                call    word [cs:si+1]
                pop     cx
                pop     cx
                jmp     short StoreStatus

AsmType:        mov     al,[bx+unit]
                mov     ah,[bx+media]
                mov     cx,[bx+count]
                mov     dx,[bx+start]
                xchg    di,ax
                mov     al,[bx+cmd]
                cmp     al,[cs:si]
                ja      _IOCommandError
                cbw
                shl     ax,1
                add     si,ax
                xchg    di,ax

                les     di,[bx+trans]
                mov     ds,[cs:_TEXT_DGROUP]
                cld
                jmp     word [cs:si+1]

;
; Name:
;       _IOXXXXXXX
;
; Function:
;       Exit routines for internal device drivers.
;
; Description:
;       These routines are the exit for internal device drivers.  _IOSuccess 
;       is for read/write functions and correctly returns for a successful 
;       read/write operation by setting the remainng count to zero.  _IOExit 
;       simply sets success bit and returns.  _IODone returns complete and 
;       busy status.  _IOCommandError returns and error status for invalid 
;       commands.  _IOErrCnt corrects the remaining bytes for errors that 
;       occurred during partial read/write operation.  _IOErrorExit is a 
;       generic error exit that sets done and error.
;
                global  _IOSuccess
_IOSuccess:
                lds     bx,[cs:_ReqPktPtr]
                xor     ax,ax
                mov     [bx+count],ax

                global  _IOExit
_IOExit:
                mov     ah,1

StoreStatus:
                lds     bx,[cs:_ReqPktPtr]
                mov     [bx+status],ax
                pop     bx
                pop     es
                pop     ds
                pop     bp
                pop     di
                pop     dx
                pop     cx
                pop     ax
                pop     si
                retf


                global  _IODone
_IODone:
                mov     ah,3
                jmp     short StoreStatus

                global  _IOCommandError
_IOCommandError:
                mov     al,3

                global  _IOErrCnt
_IOErrCnt: 
                lds     bx,[cs:_ReqPktPtr]
                sub     [bx+count],cx
                global  _IOErrorExit
_IOErrorExit:
                mov     ah,81h
                jmp     short StoreStatus

;
; Name:
;       GetUnitNum
;
; Function:
;       Return the internally set unit number.
;
; Description:
;       Simply return the contents of uUnitNumber.  This version relies on 
;       no segment registers and makes a safe call regardless of driver 
;       state.
;
                global  GetUnitNum
GetUnitNum:
                mov     dx,[cs:uUnitNumber]
                ret

                ;
                ; These are still old style DOS-C drivers.  I'll replace 
                ; them in the next release
                ;


                ;
                ; block device interrupt
                ;
                ; NOTE: This code is not standard device driver handlers
                ; It is written for sperate code and data space.
                ;

blk_driver_params:
                   dw  blk_stk_top
                   dw  _reloc_call_blk_driver
                   dw  DGROUP

clk_driver_params:
                   dw  clk_stk_top
                   dw  _reloc_call_clk_driver
                   dw  DGROUP

                ; clock device interrupt
clk_entry:
                pushf
                push    bx
                
                mov     bx, clk_driver_params
                
                jmp short clk_and_blk_common


                ; block device interrupt
blk_entry:
                pushf
                push    bx
                
                mov     bx, blk_driver_params
                
clk_and_blk_common:                
                
                push    ax
                push    cx
                push    dx

                
                ; small model
                mov     ax,sp	                    	; use internal stack
                mov     dx,ss
                pushf                                   ; put flags in cx
                pop     cx
                cli                                     ; no interrupts
                mov     ss,[cs:_TEXT_DGROUP]
                mov     sp,[cs:bx]
                
                push    cx
                popf                                    ; restore interrupt flag
                
                

                push    ax                          ; save old SS/SP
                push    dx
                
                                                    ; push these registers on
                push    ds                          ; BLK_STACK
                push    bp                          ; to save stack space
                push    si
                push    di
                push    es
                Protect386Registers

                mov     ds,[cs:_TEXT_DGROUP]        ; 
                
                
                push    word [cs:_ReqPktPtr+2]
                push    word [cs:_ReqPktPtr]
                call    far [cs:bx+2]
                pop     cx
                pop     cx
                
                les     bx,[cs:_ReqPktPtr]		; now return completion code
                mov     word [es:bx+status],ax  ; mark operation complete
                
                
                Restore386Registers
                pop     es
                pop     di
                pop     si
                pop     bp
                pop     ds
                

                pop    dx                       ; get back old SS/SP
                pop    ax
                
                cli                             ; no interrupts
                mov     ss,dx           		; use dos stack
                mov     sp,ax


                pop     dx
                pop     cx
                pop     ax
                pop     bx
                popf
                retf
