;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = CELLDRAW.ASM
;*
;* DESCRIPTIVE NAME = Draw from a LVB
;*
;*
;* VERSION      V2.0
;*
;* DATE         06/26/87
;*
;* DESCRIPTION  This module contains drawing functions which materialize the image 
;*              of a logical video buffer based on pseudocode placed in the stack
;*              by routine from the CELLSCAN.ASM module.                                      
;*
;*
;* FUNCTIONS   InvertClippedRect 
;*             InvertScreenRect  
;*             InvertScreenRect  
;*                              
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   02/27/87                     Ron Murray [ronm] Wrote VioDrawOpaqueNHC
;*   03/01/87                     Ron Murray [ronm] Wrote InterpretAttributes
;*                                Ron Murray [ronm] Wrote AVioDrawOpaqueNVC
;*                                Ron Murray [ronm] Wrote AVioDrawXparentNVC
;*                                Ron Murray [ronm] Wrote AVioDrawXparentHC
;*                                Ron Murray [ronm] Wrote SetDisplayDefaults
;*   03/12/87                     Ron Murray [ronm] Wrote AVioDrawOpaqueHC
;*   03/20/87                     Ron Murray [ronm] Added code to handle
;*                                Reverse Video in Opaque cells by simply
;*                                swapping foreground and background color
;*                                attributes.
;*                                Ron Murray [ronm] Adjusted the code to
;*                                not worry about Reverse Video.  That's
;*                                handled in InterpretAttributes for
;*                                opaque cells.
;*                                Ron Murray [ronm] Adjusted the DI
;*                                increments to match this routine's use
;*                                of xchg rather than movsb or stosb.
;*                                Ron Murray [ronm] Adjusted the code to
;*                                not worry about Reverse Video.  That's
;*                                handled by InterpretAttributes for
;*                                opaque cells.
;*   03/29/87                     Ron Murray [ronm] Restore the Reverse
;*                                Video code!
;*   05/01/87                     Ron Murray [ronm]     fix:  Unclipped
;*                                opaque colors were set incorrectly.
;*   06/20/87                     ronm Wrote InvertClippedRect
;*                                ronm Wrote InvertScreenRect
;*   06/26/87                     ronm Changed the name from
;*                                SetEgaDefaults to SetDisplayDefaults
;*   06/29/87                     ronm Removed the code for vertically
;*                                clipped cells, since this routine
;*                                handles that case now.
;*                                ronm Converted to handle variable cell
;*                                heights
;*   11/11/87                     ronm     Fix:  Increment to next
;*                                character image was being taken from CX
;*                                instead of BP.
;*                                ronm     Fix:  The height of the top
;*                                cell is not on the stack!
;*   12/15/87                     ronm Code to filter out underscore when
;*                                we're not drawing the last scan line in
;*                                the character cell image.
;*                                ronm     Fix:  wDest2ndIncr was not
;*                                being correctly reset on exit.  Ditto
;*                                for cx and dx.
;*   01/08/88                     ronm Adjustments to use the system font.
;*                                ronm Adjustments to use System Fonts.
;*                                ronm Adjustments to use System fonts
;*                                ronm Adjustments to use System fonts
;*                                ronm Adjustments to use System Font
;*                                ronm Adjustments to use System Font
;*   03/28/88                     ronm Moved InvertClippedRect to
;*                                CellDraw.asm.
;*   06/22/88                     Mitchell McLain [gssc!mmm] Wrote AVioDrawText
;*                                Mitchell McLain [gssc!mmm] Modified it
;*                                for the 8514.
;*   07/14/88                     Walt Moore [waltm] SS:BX now points to a
;*                                RECTL instead of a RECTS.  Only .lo
;*                                portion is guaranteed to be valid.
;*   11/01/90                     Ron Bowater completely re-wro te
;*                                AVioDrawText for VGA132
;*****************************************************************************/

        .xlist

        include cmacros.inc
INCL_FONTFILEFORMAT     equ     1
INCL_DEV                equ     1
        include pmgre.inc
        include driver.inc
        include cellblt.inc
        include egafam.inc
        include egamemf.inc
        include fontseg.inc

        .list

        ??_out  celldraw

        sBegin  VioSeg
        assumes cs,VioSeg

        PUBLIC  SetDisplayDefaults
        PUBLIC  InvertScreenRect
        PUBLIC  VioDrawOpaqueNHC
        PUBLIC  AVioDrawOpaqueNVC
        PUBLIC  AVioDrawXparentNVC
        PUBLIC  AVioDrawXparentHC
        PUBLIC  AVioDrawOpaqueHC


ifdef VGA
ColorMap    DB  0,1,2,3,4,5,6,8,7,9,10,11,12,13,14,15
else
ColorMap    DB  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
endif



EGAColorPlaneMask       EQU     0Fh


;/***************************************************************************
;*
;* PUBLIC ROUTINE  InterpretAttributes 
;*
;* DESCRIPTION   = Pops a word of attribute information off the stack, adjusts the
;*                 drawing environment as necessary, and jumps to an appropriate 
;*                 drawing function. It presumes the EBP stack environment created 
;*                 by DrawImageRect.
;*                 
;*                 This routine usually exits with a JMP rather than a RET! 
;*
;*                 Registers Destroyed:         
;*                       AH, AL, BH, BL, CX, DX 
;*                 Registers Preserved:         
;*                       None                   
;*
;* INPUT         = DI, DS, ES are assumed to be set as required by the EGA drawing 
;*                 functions. Assumes a WORD of descriptor information is at [SS:SP].
;* OUTPUT        = SS:SP advances to the next pseudo code word.
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ASSUME  es:EGAMem


define_CharRect_frame InterpretAttributes

cBegin  <nogen>

;/*
;**   A pseudo code plan for InterpretAttributes:
;**
;**   Set up initial destination address.
;**
;**   SP = pReturnAddress?
;**       ret
;**
;**   Opaque and Reverse Video?
;**       Switch Foreground/Background Attributes.
;**       Reset the Reverse Video Attribute bit.
;**
;**   Set registers
;**   Preserve registers
;**
;**   LCID Changed?
;**       Map the LCID to a Font Selector.
;**
;**       Zero Selector?
;**           Scan the pseudo code until the next control marker,
;**               replacing offsets by wUnusableSurrogate.
;**           Change the Selector to reference the base symbol set.
;**
;**       Load the selector into DS.
;**       Set up the glyph mapping attributes
;**
;**   Transparent or Opaque?
;**
;**
;**       Opaque:
;**
;**              HorizontalClipping?
;**                   Set Opaque Clipping Colors
;**                   Pass 1 if necessary
;**                   Pass 2 if necessary
;**
;**              If LastPass was Transparent or Colors are different,
;**                   Set Opaque Colors
;**
;**              Restore Regs
;**
;**         ;    Horizontal Clipping?
;**         ;         JMP to AVioDrawCellHC
;**
;**              Underscoring or Reverse Video?
;**                    JMP to AVioDrawOpaqueNVC
;**              ELSE: JMP to VioDrawOpaqueNHC
;**
;**       Transparent:
;**
;**               If LastPass was Opaque or Color is different,
;**                   Set Transparent Color.
;**
;**               Restore Regs
;**
;**               Horizontal Clipping?
;**                     JMP to AVioDrawCellHC
;**               Else: JMP to AVioDrawXparentNVC
;*/


ifdef VGA

        cmp     wCellWidth,8
        je      got8pel
        jmp     not8pel

got8pel:
endif

;/*
;**  InterpretAttributes begins with a prologue for establishing part
;**  of the environment assumed by the drawing functions.  The drawing
;**  functions are presumed to preserve or properly update this state
;**  information across cell clusters with a common set of attributes.
;*/

        mov     es,seg_lpBitmapBits     ; Set up addressability for
                                        ; the destination area.

        ASSUME  es:EGAMem

;/*
;**  If wVertLineCount is smaller than wCellWidth, we must construct a
;**  horizontal clipping mask.
;*/

        mov     ax,0ffffh
        mov     cx,wVertLineCount
        cmp     cx,wCellWidth
        je      no_horizontal_clipping
        shr     ax,cl
        not     ax
        mov     cx,wLeftLineOffset
        shr     ax,cl

        public no_horizontal_clipping
no_horizontal_clipping:

        mov     bHorizontalClipMask,ah

;/*
;**  The initial drawing position is defined by the pel coordinates
;**  xcoordStart and ycoordStart.  Here we convert that to the starting
;**  address.  Devices with interleaved bitmaps (viz. CGA and Hercules)
;**  would also set up phase control information here.
;*/

        mov     ax,ycoordStart
        mul     wDestBMWidth
        mov     di,xcoordStart
        shr     di,3
        add     di,ax                   ; Initial drawing address.

;/*
;** Now we construct three increments for the destination poiner:
;**
;**   1.  The distance to move to the next scan line in a cell image
;**       after a movsb or stosb instruction.
;**
;**   2.  The distance to move from the bottom scan line of a cell to
;**       the top scan line in the same cell.
;**
;**   3.  The distance to move from just to the right of the last scan
;**       line in the rightmost cell in a column set to the top scan
;**       line in the lefmots column in the next row of the same column
;**       set.
;*/

        mov     ax,wDestBMWidth
        mov     bx,ax
        dec     bx
        mov     wDest1stIncr,bx
        mov     bx,ax
        mul     wScanLineCount
        sub     bx,ax
        mov     wDest2ndIncr,bx
        sub     ax,wFullCols
        mov     wDest3rdIncr,ax

;/*
;**  The drawing functions use addresses in an upward sequence.  Hence
;**  the need to have the direction flag cleared.
;*/

        cld

;/*
;**  The following label is the return point for all the device-dependant
;**  drawing functions.  Most of them terminate with a jmp instead of a ret.
;*/

        public next_attribute_cluster
next_attribute_cluster:

        cmp     sp,pReturnAddress       ; Have we exhausted the pseudocode?
        jne     ProcessAStream

        ret_near                        ; If so return to the scanner.


        public  ProcessAStream
ProcessAStream:

        pop     si                      ; Get the new attribute set.

        xor     cx,cx                   ; Constructing a reference Zero value.

        mov     ax,si
        and     ax,(AVioReverseVideoMask OR AVioTransparentMask)
        cmp     ax,AVioReverseVideoMask ; Check for Opaque with Reverse Video.
        jnz     Filter_UnderScore

        mov     ax,si                   ; For that case we do the reversing
        mov     bl,ah                   ; by just swapping foreground and
        shr     ah,4                    ; background color attributes.
        shl     bx,4
        or      ah,bl
        and     ax,(NOT AVioReverseVideoMask)
        mov     si,ax

Filter_UnderScore:

        test    si,AVioUnderscoreMask
        jz      set_up_registers

        mov     ax,wTopLineOffset
        add     ax,wScanLineCount
        cmp     ax,wCellHeight
        jz      set_up_registers
        and     si,(NOT AVioUnderscoreMask)


        public  set_up_registers
set_up_registers:

        mov     ax,AVioUnderscoreMask   ; If underscoring, bl => 0FFh else 0.
        and     ax,si
        cmp     cx,ax
        sbb     bl,bl

        mov     ax,AVioReverseVideoMask ; If reverse video, bh => 0FFh else 0.
        and     ax,si
        cmp     cx,ax
        sbb     bh,bh

        mov     dx,wDest1stIncr         ; Set up the other registers we use.
        mov     cx,wDest2ndIncr
        mov     al,bHorizontalClipMask
        mov     ah,bScanLines

        push    ax                      ; Preserve the registers we
        push    bx                      ; use herein.
        push    cx
        push    dx

        mov     dx,ax
        mov     ax,si                   ; Get the previous attribute set
        mov     cx,si
        xchg    ax,wLastAttributes      ; and preserve this attribute set
                                        ; for the next InterpretAttributes
                                        ; call.

        mov     bx,ax                   ; Compare the old
        xor     bx,si                   ; new LCID bit fields.
        and     bx,AVioLCIDMask         ; Are they different?
        jnz     set_up_lcid

        mov     ds,wselfontsave
        ASSUME  ds:FontSeg

        jmp     SHORT SameSymbolSet


        public  set_up_lcid
set_up_lcid:

        and     si,AVioLCIDMask         ; If so set up DS for the new
        shl     si,1                    ; symbol set.
        mov     bx,wSymbolSetSelectors[si]
        or      bx,bx                   ; Is this selector usable?
        jnz     ValidSymbolSetSelector

;/*
;**  If we don't have a usable selector, we use the selector for the
;**  base symbol set, and map all of the glyph points for that LCID to
;**  a surrogate glyph.
;*/

        mov     bx,ss                   ; Set up to scan forward
        mov     ds,bx                   ; through the glyphs on the
        cld                             ; stack.

        mov     bx,SURROGATE_CHARACTER
        mov     si,sp
        add     si,4*2                  ;Skip over saved registers


check_next_syllable:

        lodsw
        cmp     ax,-2
        ja      found_a_control_marker  ; -1 => control marker
        je      check_next_syllable     ; -2 => end of row action

        mov     -2[si],bx
        jmp     SHORT check_next_syllable


found_a_control_marker:

        mov     bx,wSymbolSetSelectors[0]   ; Substitute the selector for
                                            ; the base symbol set.

ValidSymbolSetSelector:

        mov     wselfontsave,bx
        mov     ds,bx
        ASSUME  ds:FontSeg

;/*
;**  Here we set up the data which characterizes the font which is becoming
;**  active.
;*/

        mov     bx,fsMetrics.foca_usLastChar
        inc     bx
        mov     wGlyphSpan,bx
        mov     bx,fsMetrics.foca_usFirstChar
        mov     wFirstGlyph,bx
        mov     bx,fsMetrics.foca_usDefaultChar
        mov     wDefaultGlyph,bx

        mov     si,cx

SameSymbolSet:

        test    si,AVioTransparentMask  ; Transparent cells or
        jz      GoingOpaque             ; Opaque cells ?
        jmp     GoingTransparent

;/*
;**   GoingOpaque
;*/


        public  GoingOpaque
GoingOpaque:
        inc     dl                      ; Horizontally clipped?
        jnz     ClippedOpaque
        jmp     OpaqueNoHorzClippping


ClippedOpaque:

        dec     dl                      ; If so, get the clip mask
        mov     ah,dl                   ; in AH for the OUT below.

        mov     dh,ch                   ; Split the color information into
        mov     dl,dh                   ; two bytes:
        shr     dh,4                    ;              DH -- Background
        and     dx,0F0Fh                ;              DL -- Foreground

        xor     bx,bx

        mov     bl,dl                   ; Map the foreground/background
        mov     dl,cs:ColorMap[bx]      ; into the correct EGA planes.
        mov     bl,dh
        mov     dh,cs:ColorMap[bx]
        mov     bx,dx                   ; Free up DX for the OUT instructions.

        mov     dx,EGA_BASE+GRAF_ADDR   ; Bitmask <- bHorizontalClipping
        mov     al,GRAF_BIT_MASK
        out     dx,ax

        mov     al,GRAF_MODE
        mov     ah,M_PROC_WRITE OR M_DATA_READ
        out     dx,ax

        mov     al,GRAF_DATA_ROT
        mov     ah,DR_SET
        out     dx,ax

        mov     al,GRAF_SET_RESET       ; SetRest <- Foreground
        mov     ah,bl
        out     dx,ax

        xor     ah,bh                   ; SetResetEnable <- FullColors and
        mov     ch,ah                   ;         Foreground = Background
        not     ah
        mov     cl,ah
        and     ah,MM_ALL
        mov     al,GRAF_ENAB_SR
        out     dx,ax

        mov     dx,EGA_BASE+SEQ_ADDR    ; Set up to select the planes for
        mov     al,SEQ_MAP_MASK         ; each pass.

        mov     ah,ch                   ; Set AH to the planes that can't
        and     ah,bl                   ; be done on the second pass.
        jz      NoOHCPass1              ; If none, skip the first pass.

        mov     ah,cl                   ; The first pass always includes
        or      ah,bl                   ; the planes controlled by Set
        out     dx,ax                   ; Reset in addition to the ones
                                        ; for which fg and fg != bg.

        mov     si,ax                   ; Preserve AX for the PUSH below.

        pop     dx                      ; Restore the other registers
        pop     cx                      ; we'll need.
        pop     bx
        pop     ax

        mov     npinxGlyph,sp           ; Store a pointer to the base of the
                                        ; pseudo code sequence.

        push    di                      ; Save the destination pointer
                                        ; for a possible second pass.

        push    si                      ; Save our plane mask.
        call    AVioDrawOpaqueHC        ; Make the first pass.

        mov     si,ax                   ; Preserve AX so we can retrieve
        pop     ax                      ; the plane mask.
        not     ah                      ; AH now masks the planes to be
        or      ah,ah                   ; done on the second pass.
        jz      NoOHCPass2              ; If there aren't any we skip
                                        ; the second pass.

        pop     di                      ; Restore DI for the second pass.

        mov     cx,dx

        mov     dx,EGA_BASE+SEQ_ADDR    ; Select the planes for pass 2.
        out     dx,ax

        mov     dx,cx                   ; Set up the registers for pass 2.
        mov     npinxGlyph,sp           ; Store a pointer to the base of
                                        ; the code stream.
        mov     ax,si
        not     bh                      ; Toggle the reverse video byte.

        call    AVioDrawOpaqueHC        ; The second pass...
        jmp     SHORT NoOHCPass2


NoOHCPass1:

        not     ah                      ; Select the planes for the second
        mov     dx,EGA_BASE+SEQ_ADDR    ; pass.
        out     dx,ax

        pop     dx                      ; Set up the register environment
        pop     cx                      ; for the second pass.
        pop     bx
        pop     ax

        not     bh                      ; Toggle the reverse video byte.

        mov     npinxGlyph,sp           ; Store a pointer to the base of the
                                        ; code stream.
        call    AVioDrawOpaqueHC        ; The second pass...


NoOHCPass2:

        mov     sp,npinxGlyph           ; Move sp to the next control point
                                        ; in the pseudo code.

        jmp     next_attribute_cluster  ; Go interpret the control point.


OpaqueNoHorzClippping:

        test    ax,AVioTransparentMask  ; Was the last stream transparent?
        jnz     SetOpaqueColors
        cmp     ah,ch                   ; Were the fg/bg colors different?
        jz      OpaqueColorsOK


SetOpaqueColors:

        mov     dh,ch                   ; Split the color information into
        mov     dl,dh                   ; two bytes:
        shr     dh,4                    ;              DH -- Background
        and     dx,0F0Fh                ;              DL -- Foreground

        xor     bx,bx

        mov     bl,dl                   ; Map the foreground/background
        mov     dl,cs:ColorMap[bx]      ; into the correct EGA planes.
        mov     bl,dh
        mov     dh,cs:ColorMap[bx]
        mov     bx,dx                   ; Free up DX for the OUT instructions.

;/*
;**  Much of the following code consists of constants sent to the EGA
;**  I/O address interface.  It should be converted to a structure with
;**  fields for the variable information.
;**  Then the code can be reduced to filling in the variable fields and
;**  using REP OUTSW or its equivalent through a macro definition.
;*/


        mov     dx,EGA_BASE+SEQ_ADDR    ; Enable all the available EGA planes.
        mov     al,SEQ_MAP_MASK
        mov     ah,MM_ALL
        out     dx,ax

        mov     dx,EGA_BASE+GRAF_ADDR   ; Prepare to set the EGA graphics
                                        ; registers.


        mov     al,GRAF_MODE
        mov     ah,M_PROC_WRITE OR M_DATA_READ
        out     dx,ax

        mov     al,GRAF_DATA_ROT         ; Set up to write data without
        mov     ah,DR_SET                ; interacting with the latches.
        out     dx,ax


        mov     al,GRAF_SET_RESET        ; Put the background color in the
        mov     ah,bh                    ; Set/Reset register.
        out     dx,ax

        mov     al,GRAF_ENAB_SR          ; That color is used for all planes.
        mov     ah,MM_ALL
        out     dx,ax

        mov     al,GRAF_BIT_MASK        ; Use all pixel positions.
        mov     ah,0FFh
        out     dx,ax

        mov     tonys_bar_n_grill,ah    ; This gets our background color
        mov     ah,tonys_bar_n_grill    ; into the latches.

        xor     ax,ax                   ; Change the Set/Reset register to
        mov     al,GRAF_SET_RESET       ; be all zeroes.
        out     dx,ax

        mov     ah,bh                   ; The Set/Reset enable register now
        xor     ah,bl                   ; flags where the foreground/background
        not     ah                      ; colors are the same.
        and     ah,0Fh                  ; 
        mov     al,GRAF_ENAB_SR         ; 
        out     dx,ax

        mov     al,GRAF_DATA_ROT        ; Color differences will be xor'd
        mov     ah,DR_XOR               ; with the latches.
        out     dx,ax


OpaqueColorsOK:

        pop     dx                      ; Restore the register set for
        pop     cx                      ; the drawing function.
        pop     bx
        pop     ax

        test    si,AVioUnderscoreMask OR AVioReverseVideoMask
        jnz     OpaqueANVC              ; AVio flags set?

        jmp     VioDrawOpaqueNHC
OpaqueANVC:
        jmp     AVioDrawOpaqueNVC


        public  GoingTransparent
GoingTransparent:

        test    ax,AVioTransparentMask  ; Was the last stream transparent?
        jz      SetTransparentColor
        cmp     ah,ch                   ; Was its color different?
        je      TransparentColorOK


SetTransparentColor:

;/*
;**  Much of the following code consists of constants sent to the EGA
;**  I/O address interface.  It should be converted to a structure with
;**  fields for the variable information.
;**  Then the code can be reduced to filling in the variable fields and
;**  using REP OUTSW or its equivalent through a macro definition.
;/*

        mov     dx,EGA_BASE+SEQ_ADDR    ; Enable all the available EGA planes.
        mov     al,SEQ_MAP_MASK
        mov     ah,MM_ALL
        out     dx,ax

        mov     dx,EGA_BASE+GRAF_ADDR   ; Prepare to set the EGA graphics
                                        ; registers.


        mov     al,GRAF_DATA_ROT        ; Set up to write data without
        mov     ah,DR_SET               ; interacting with the latches.
        out     dx,ax


        xor     bx,bx                   ; Map the color to the EGA color
        mov     bl,ch                   ; planes and put that mapping into
        mov     al,GRAF_SET_RESET       ; the Set/Reset register.
        mov     ah,cs:ColorMap[bx]
        out     dx,ax

        mov     al,GRAF_ENAB_SR         ; That color goes to all planes.
        mov     ah,MM_ALL
        out     dx,ax

        mov     al,GRAF_BIT_MASK        ; Prepare for OUT's to GRAF_BIT_MASK.
        out     dx,al


TransparentColorOK:

        pop     dx                      ; Restore the register set for
        pop     cx                      ; the drawing function.
        pop     bx
        pop     ax

        inc     al
        jnz     Transparent_Clipped     ; Horizontal clipping?

        jmp     AVioDrawXparentNVC      ; All other transparent cases.


Transparent_Clipped:

        dec     al                      ; Restore the clip mask.
        jmp     AVioDrawXparentHC




cEnd    <nogen>


        ASSUME  es:Nothing

;/*
;**                      Ega HiRes Drawing Functions
;**                      ---------------------------
;**
;** The functions below draw text on the EGA adapter in its High Resolution
;** color mode.  They provide the low level semantics for materializing all
;** of the Vio and AVio text attributes on the screen.  The functions are
;** driven by a stream of pseudocode syllables placed on the stack by the
;** ScanCells routine, and by their initial register state as specified by
;** the InterpretAttributes routine.
;**
;** This table defines how the drawing functions use the register set:
;*/

;/*
;**---------------------------------------------------------------+
;** Reg |   VONHC    |   AONVC      |   AXNVC      |  OHC & XHC   |
;**---------------------------------------------------------------|
;** AH  | Scan Lines | Scan Lines   | Scan Lines   | Scan Lines   |
;**     |            |              |              |              |
;** AL  |            |  -------     |  -------     | Clip Mask    |
;**     |            |              |              |              |
;** BH  | ---------- | Reverse Video| Reverse Video| Reverse Video|
;**     |            |              |              |              |
;** BL  | ---------- | UnderScore   | UnderScore   | UnderScore   |
;**     |            |              |              |              |
;** CX  | 2nd Incr.  | 2nd Incr.    | 2nd Incr.    |  -------     |
;**     |            |              |              |              |
;** DX  | 1st Incr.  | 1st Incr.    | 1st Incr.    | 1st Incr.    |
;**     |            |              |              |              |
;** SI  |  -------   |  -------     |  -------     |  -------     |
;**     |            |              |              |              |
;** DI  | Dest. Addr | Dest. Addr.  | Dest. Addr.  | Dest. Addr.  |
;**     |            |              |              |              |
;** DS  | Symbol Seg.| Symbol Seg.  | Symbol Seg.  | Symbol Seg.  |
;**     |            |              |              |              |
;** ES  | Dest.  Seg.| Dest.  Seg.  | Dest.  Seg.  | Dest.  Seg.  |
;**---------------------------------------------------------------+
;*/

;/*
;**  The dashed lines marks registers whose content is destroyed by the function.
;**  The text strings denote quantities they expect in particular registers at
;**  entry time.  Except for DI, those initial values are preserved through exit
;**  time.
;** 
;**    Parameter     Interpretation
;**    ------------- -----------------------------------------------------------
;**    Scan Lines    The number of scan lines to draw for a single image cell.
;** 
;**    Clip Mask     Horizontal clipping mask applied against each scan line.
;** 
;**    Reverse Video 0 => Normal Video;  0FFh => Reversed Foreground/Background.
;** 
;**    UnderScore    0 => No Underscore; 0FFh => Underscore the cell image.
;** 
;**    2nd Incr.     DI increment to move to the top of the next cell image.
;** 
;**    1st Incr.     DI increment to move to the next scan line of current cell.
;** 
;**    Dest. Addr    Initial scan line address.  This will be altered by the code.
;** 
;**    Symbol Seg.   Segment which contains the symbol set(s) for drawing.
;** 
;**    Dest.  Seg.   Segment which contains the destination area.
;** 
;** 
;**  The full function names were abreviated for use as column headings.  The
;**  correspondence between the two is:
;** 
;**      Heading       Full Name            Domain
;**      -------       -------------------  ---------------------------------
;**       VONHC        VioDrawOpaqueNHC     Opaque Vio cells with no horizontal
;**                                         clipping, may be vertically clipped.
;** 
;**       AONVC        AVioDrawOpaqueNVC    Opaque AVio cells which may be
;**                                         clipped vertically.
;** 
;**       AXNVC        AVioDrawXparentNVC   Transparent AVio cells which may
;**                                         be clipped vertically
;** 
;**       OHC          AVioDrawOpaqueHC     Vio and AVio opaque cells which
;**                                         must be clipped horizontally.
;** 
;**       XHC          AVioDrawXparentHC    AVio transparent cells which must
;**                                         be clipped horizontally.
;** 
;** 
;**  Pseudo Code Conventions
;**  -----------------------
;** 
;**  Each of the drawing functions consumes a stream of pseudo code syllables by
;**  POPing words off the SS:SP stack.  Three kinds of syllables exist:
;** 
;** 
;**        CodeValue  >=  0    Assumed to be the starting address within a
;**                            symbol set of the scan lines for a character
;**                            image.
;** 
;**        CodeValue  <  -1    Request to apply a tertiary increment to DI.
;**                            This moves DI to the beginning of the next
;**                            cell row.
;** 
;**        CodeValue  == -1    This signals an attribute change.  It will
;**                            usually be followed by a word containing
;**                            the new attribute information in AVio format.
;**                            When a drawing function encounters this signal
;**                            it exits with a JMP to next_attribute_cluster.
;** 
;**  NB: This coding scheme requires that Symbol Set offsets must be less
;**      than 8000h.
;** 
;**  InterpretAttributes takes care of setting up the proper EGA environment
;**  for new attribute sets and invoking the proper drawing function.  Usually
;**  that invocation consists of a JMP to the appropriate function.  That
;**  function will, in turn JMP to next_attribute_cluster when it reaches the
;**  next -1 code syllable.
;** 
;**  The exception to this rule is DrawOpaqueHC.  There the invocation is
;**  by call/ret.  This is necessary to implement the two passes that are
;**  sometimes necessary to horizontally clip opaque text.
;** 
;**  When all the pseudo code has been exhausted, InterpretAttributes will
;**  do a RET to the scanning function which created the pseudo code.
;** 
;** 
;*/

ifndef VGA        

;/***************************************************************************
;*
;* FUNCTION NAME = InvertClippedRect 
;*
;* DESCRIPTION   = This routine does the display work for the InvertTextCursor 
;*                 routine.  It is driven by enumerate_clip_rects.             
;*                                                                             
;* INPUT         = SS:SI is the stack frame address for InvertTextCursor.
;*                 SS:BX is the address of a rectangle structure which defines a
;*                        component of the region to be inverted.                             
;*                                                                             
;*                 Registers Destroyed:                                                       
;*                       AX, BX, CX, DX, DI, ES                                               
;*                 Registers Preserved:                                                       
;*                       BP, SI                                                               
;*                 Calls:
;*                       exclude, unexclude, InvertScreenRect
;*                                                                             
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

sEnd    VioSeg

sBegin  Code
        assumes cs,Code

cProc   InvertClippedRect,<NEAR,PUBLIC>
cBegin
        call    far ptr far_InvertClippedRect
cEnd

sEnd    Code

sBegin  VioSeg
        assumes cs,VioSeg

define_Invert_Cursor_Frame far_InvertClippedRect


cBegin  <nogen>

;/*
;**  First we save the stack frame for enumerate_clip_rects
;**  and set the frame pointer to reference the local environment
;**  of InvertTextCursor.
;*/

        push    bp
        mov     bp,si

        mov     fSomethingDrawn,1       ; to denote a non-null vis region

;/*
;**  Now we retrieve the screen coordinates of the rectangle to invert.
;**  For convenience we map the coordinates to top left origin.
;*/

        mov     di,wDestHeight
        mov     dx,di

        sub     di,ss:[bx].rcl_yBottom.lo
        sub     dx,ss:[bx].rcl_yTop.lo
        mov     cx,ss:[bx].rcl_xLeft.lo
        mov     si,ss:[bx].rcl_xRight.lo

;/*
;** If the rectangle is empty we do nothing.
;*/

        cmp     dx,di
        jge     null_invert_rect

        cmp     cx,si
        jge     null_invert_rect

;/*
;**  Otherwise we save the coordinates on the stack
;**  and exclude the mouse cursor.
;*/

        push    si
        push    cx
        push    dx
        push    di

        call    far_exclude


;/*
;**  Now we retrieve the coordinates and map them into
;**  a set of parameters for driving the InvertRect routine.
;*/

        pop     di      ; ... Bottom ...
        pop     bx      ; ... Top    ...
        pop     cx      ; ... Left   ...
        pop     si      ; ... Right  ...


        mov     ax,wDestBMWidth
        mul     bx
        add     ax,off_lpBitmapBits
        sub     di,bx

;/*
;** DI now holds the number of scan lines involved.
;*/

        mov     bx,cx
        shr     bx,3
        add     ax,bx

;/*
;** AX now holds the offset to the first byte to be altered.
;*/

;/*
;**  Now we compute the mask for the byte column.  A key assumption
;**  here is that the rectangle will always fit within a single
;**  byte column.  That will always be valid for the text cursor.
;*/

        xor     bx,bx
        not     bx

        and     cx,7
        shr     bl,cl

        mov     cx,si
        dec     cx
        and     cx,7
        inc     cx
        shr     bh,cl
        not     bh

        and     bl,bh

;/*
;**  Now we've got all the information together.  After a little
;**  reshuffling to get things into the correct registers we
;**  invoke InvertScreenRect.
;*/

        mov     cx,di
        mov     di,ax
        mov     es,seg_lpBitmapBits
        mov     si,wDestBMWidth

        call    InvertScreenRect

        call    far_unexclude

null_invert_rect:

;/*
;**  Before we return to enumerate_clip_rects, we must restore SI and BP
;**  to the values they had on entry.
;*/

        mov     si,bp
        pop     bp

;/*
;**  enumerate_clip_rects requires a non-zero result to keep going.
;**  A zero result stops the enumeration.
;*/

        mov     ax,sp
        ret_far

cEnd    <nogen>

endif ; VGA

;/***************************************************************************
;*
;* PUBLIC ROUTINE  VioDrawOpaqueNHC  
;*
;* DESCRIPTION   = Interprets pseudo code on the stack to draw Vio character
;*                 cell image which are not horizontally clipped.  NB:  This
;*                 routine exits with a JMP Next_attribute_cluster rather
;*                 than a RET.
;*
;*                 Registers Destroyed:
;*                       SI BX                                                                
;*                 Registers Preserved:                                                       
;*                       none
;*
;* INPUT         =    AH -- The number of scan lines to draw vertically.
;*                    CX -- The secondary increment for DI.  It's used to move DI to
;*                          the top scan line of the next image cell on the right.
;*                    DX -- The primary increment for DI.  It's used to move DI down
;*                          to the next scan line in the current image cell.
;*                 ES:DI -- Destination pointer.  It points to the top byte of the
;*                          first image cell to be drawn over.
;*                    DS -- The Segment selector for the Symbol Set we're using.
;*                 SS:SP -- Pseudocode pointer.  This points to the next pseudo code
;*                          syllable to process.
;* OUTPUT        = ES:DI --> Top scan line of the next cell to be drawn.
;*                 SS:SP --  Moves according to the pseudo code consumed by this
;*                           function.
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

VioDrawOpaqueNHC PROC

        ASSUME  ds:FontSeg

;/*
;**  We first must set up the start address for the unrolled drawing code.
;**  That address is based on the number of scan lines we'll be drawing.
;*/

        mov     bx,MaxSymbolLines
        sub     bl,ah                   ; AH is scan lines to draw.

        mov     bh,bl                   ; Multiply bl by 3, the size of
        shl     bl,1                    ; movsw; add di,dx
        add     bl,bh

        xor     bh,bh
        add     bx,VioSegOFFSET Draw_Top_Scan

;/*
;**  For speed two versions of this code exist.  The first version
;**  (Draw_Top_Scan, ...) is used when wTopLineOffset is zero.  Otherwise
;**  we use the second version (Draw_Top_Scan2, ...).
;*/

        mov     si,wTopLineOffset
        or      si,si
        jnz     short vonhc_vertically_clipped

        mov     ss:[cb_BPSaveSlot],bp
        mov     si,wDefaultGlyph
        mov     ss:[cb_Default_Glyph],si
        mov     si,wDest3rdIncr
        mov     ss:[cb_Dest_3rd_Incr],si
        mov     ax,wFirstGlyph
        mov     bp,wGlyphSpan
        jmp     SHORT Process_Pseudo_Code

vonhc_vertically_clipped:

        add     bx,((VioSegOFFSET Draw_Top_Scan2)-(VioSegOFFSET Draw_Top_Scan))
        jmp     Process_Pseudo_Code2

Draw_Top_Scan:

        REPT    MaxSymbolLines-1

        movsb
        add     di,dx

        ENDM


Code_Pattern_Pref:

        movsb
        add     di,cx

Code_Pattern_Suff:

        .errnz   (Code_Pattern_Suff - Code_Pattern_Pref) - 3

;/*
;** That assumption is critical to the BX address calculations above.
;*/

Process_Pseudo_Code:

;/*
;**  If SI is non-negative, it's a glyph index.  Negative values denote
;**  control actions:  -1 => a signal to return control to InterpretAttributes.
;**                    -2 => we've come to the end of a row of cell images
;**                          and must move to the beginning of the next row.
;*/

        pop     si                      ; Get the next code syllable.

;/*
;**  A particular font includes a span of glyphs which may not be complete.
;**  That is, some valid glyph indices may not have defined pel images.
;**  The span of defined glyphs is characterized by wFirstGlyph and
;**  wGlyphSpan.  Glyph indices outside this range are mapped to wDefaultGlyph.
;*/

        sub     si,ax                   ; The lowest defined glyph
        cmp     si,bp                   ; The number of defined glyphs
        jnb     substitute_or_control


vonhc_default_substituted:

        imul    si,si,6                 ; Glyph descriptors are 6 bytes.
                                        ; The first two bytes are the offset.
        mov     si,WORD PTR fsCharOffset[si]
        jmp     bx


substitute_or_control:

        add     si,ax
        js      vonhc_Control_Syllable

        mov     si,ss:[cb_Default_Glyph]
        jmp     SHORT vonhc_default_substituted


vonhc_Control_Syllable:

        inc     si                      ; -1 => an attribute or return addr.
        jz      exit_VONHC

        add     di,ss:[cb_Dest_3rd_Incr]; Otherwise a cell image row boundary.
        jmp     short Process_Pseudo_Code


exit_VONHC:

        mov     bp,ss:[cb_BPSaveSlot]
        jmp     next_attribute_cluster


Draw_Top_Scan2:

        REPT    MaxSymbolLines-1

        movsb
        add     di,dx

        ENDM

        movsb
        add     di,cx


Process_Pseudo_Code2:

;/*
;**  If SI is non-negative, it's a glyph index.  Negative values denote
;**  control actions:  -1 => a signal to return control to InterpretAttributes.
;**                    -2 => we've come to the end of a row of cell images
;**                          and must move to the beginning of the next row.
;*/

        pop     si                      ; Get the next code syllable.

;/*
;**  A particular font includes a span of glyphs which may not be complete.
;**  That is, some valid glyph indices may not have defined pel images.
;**  The span of defined glyphs is characterized by wFirstGlyph and
;**  wGlyphSpan.  Glyph indices outside this range are mapped to wDefaultGlyph.
;*/

        sub     si,wFirstGlyph          ; The lowest defined glyph
        cmp     si,wGlyphSpan           ; The number of defined glyphs
        jnb     substitute_or_control2


vonhc_default_substituted2:

        imul    si,si,6                 ; Glyph descriptors are 6 bytes.
                                        ; The first two bytes are the offset.
        mov     si,WORD PTR fsCharOffset[si]
        add     si,wTopLineOffset
        jmp     bx


substitute_or_control2:

        add     si,wFirstGlyph
        js      vonhc_Control_Syllable2

        mov     si,wDefaultGlyph
        jmp     SHORT vonhc_default_substituted2


vonhc_Control_Syllable2:

        inc     si                      ; -1 => an attribute or return addr.
        jz      exit_VONHC2

        add     di,wDest3rdIncr         ; Otherwise a cell image row boundary.
        jmp     short Process_Pseudo_Code2


exit_VONHC2:

        jmp     next_attribute_cluster


VioDrawOpaqueNHC ENDP



;/***************************************************************************
;*
;* FUNCTION NAME = AVioDrawOpaqueNVC 
;*
;* DESCRIPTION   = This routine draws the image of a stream of AVio cells which
;*                 may require vertical clipping, but not horizontal clipping. 
;*                 NB:  This routine exits with a JMP Next_attribute_cluster   
;*                 rather than a RET.                                          
;*                                                                             
;*                 Registers Destroyed: 
;*                       AL CX SI                                              
;*                 Registers Preserved:                                        
;*                       BP                                                    
;*                                                                             
;* INPUT         = AH  --  The number of scan lines in a cell images.                  
;*                 BL  --  The Underscore Mask  --  OR'd with the last scan line.             
;*                 CX  --  The secondary increment for EDI.  It's used to move EDI to
;*                         the top scan line of the next image cell on the right.             
;*                 DX  --  The primary increment for EDI.  It's used to move EDI down
;*                         to the next scan line in the current image cell.                   
;*               ES:DI --> Destination.  It points to the top byte of the first image
;*                         cell to be drawn over.                                             
;*               SS:SP --> Pseudocode.  This points to the next pseudo code syllable to
;*                         process.                                                           
;*                                                                             
;* OUTPUT        = ES:DI --> Points to the top scan line of the next cell to be drawn.
;*                 SS:SP --> Moves according to the pseudocode consumed by this function.
;*                                                                             
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

AVioDrawOpaqueNVC   PROC

        ASSUME  ds:FontSeg

        xor     cx,cx


NextAVioOpaqueNVCCell:

        pop     si
        or      si,si
        js      AVioOpaqueNVCControl

;/*
;**  A particular font includes a span of glyphs which may not be complete.
;**  That is, some valid glyph indices may not have defined pel images.
;**  The span of defined glyphs is characterized by wFirstGlyph and
;**  wGlyphSpan.  Glyph indices outside this range are mapped to wDefaultGlyph.
;*/

        sub     si,wFirstGlyph          ; The lowest defined glyph
        cmp     si,wGlyphSpan           ; The number of defined glyphs
        jb      aonvc_no_substitution
        mov     si,wDefaultGlyph        ; Substitution relative to
                                        ; wFirstGlyph
aonvc_no_substitution:

        imul    si,si,6                 ; Glyph descriptors are 6 bytes.

        mov     si,WORD PTR fsCharOffset[si]     ; The first two bytes are the offset.
        add     si,wTopLineOffset

        mov     cl,ah

        dec     cx
        jcxz    LastAVioOpaqueNVCScanLine


NextAVioOpaqueNVCScanLine:

        movsb
        add     di,dx

        loop    NextAVioOpaqueNVCScanLine


LastAVioOpaqueNVCScanLine:

        lodsb
        or      al,bl
        stosb
        add     di,wDest2ndIncr

        jmp     NextAVioOpaqueNVCCell


AVioOpaqueNVCControl:

        inc     si                      ; -1 => an attribute or return addr.
        jz      exit_ANVC

        add     di,wDest3rdIncr         ; Otherwise a cell image row boundary.
        jmp     short NextAVioOpaqueNVCCell


exit_ANVC:

        jmp     next_attribute_cluster

AVioDrawOpaqueNVC   ENDP


;/***************************************************************************
;*
;* FUNCTION NAME = AVioDrawXparentNVC    
;*
;* DESCRIPTION   = This routine draws the image of a stream of transparent AVio 
;*                 cells whic may need to be clipped vertically, but not        
;*                 horizontally.  NB:  This routine exits with a JMP            
;*                 Next_attribute_cluster rather than a RET.                    
;*                                                                              
;*                 Registers Destroyed:                                                       
;*                       AL SI                                                                
;*                 Registers Preserved: 
;*                       BL CX          
;*
;* INPUT         = AH  --  The number of scan lines in a cell image.                   
;*                 BH  --  Reverse Video toggle -- XOR'd with each scan line.          
;*                 BL  --  The Underscore Mask -- Or'd with the last scan line.        
;*                 CX  --  The secondary increment for EDI.  It's used to move EDI to
;*                         the top scan line of the next image cell on the right.      
;*                 DX  --  The primary increment for EDI.  It's used to move EDI down
;*                         to the next scan line in the current image cell.            
;*               ES:DI --> Destination.  It points to the top byte of the first image
;*                         cell to be drawn over.                                      
;*               SS:SP --> Pseudocode.  This points to the next pseudo code syllable to
;*                         process.                                                    
;*
;*
;* OUTPUT        = ES:DI --> Top scan line of the next cell to be drawn.
;*                 SS:SP --> Moves according to the pseudocode consumed by this function.
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

AVioDrawXparentNVC  PROC

        ASSUME  ds:FontSeg

        inc     cx
        inc     dx
        xchg    cx,dx
        mov     wDest2ndIncr,dx
        mov     dx,EGA_BASE+GRAF_ADDR+1

        mov     bUnderscoreMask,bl


NextAVioXparentNVCCell:

        pop     si
        or      si,si
        js      AVioXparentNVCControl

;/*
;**  A particular font includes a span of glyphs which may not be complete.
;**  That is, some valid glyph indices may not have defined pel images.
;**  The span of defined glyphs is characterized by wFirstGlyph and
;**  wGlyphSpan.  Glyph indices outside this range are mapped to wDefaultGlyph.
;*/

        sub     si,wFirstGlyph          ; The lowest defined glyph
        cmp     si,wGlyphSpan           ; The number of defined glyphs
        jb      axnvc_no_substitution
        mov     si,wDefaultGlyph        ; Substitution relative to
                                        ; wFirstGlyph
axnvc_no_substitution:

        imul    si,si,6                 ; Glyph descriptors are 6 bytes.
        mov     si,WORD PTR fsCharOffset[si]     ; The first two bytes are the offset.
        add     si,wTopLineOffset
        mov     bl,ah


        EVEN

NextAVioXparentNVCScanLine:

        dec     bl
        jz      LastAVioXparentNVCScanLine

        lodsb
        xor     al,bh
        out     dx,al
        xchg    al,es:[di]
        add     di,cx

        jmp     NextAVioXparentNVCScanLine


LastAVioXparentNVCScanLine:

        lodsb
        xor     al,bh
        or      al,bUnderscoreMask
        out     dx,al
        xchg    al,es:[di]
        add     di,wDest2ndIncr

        jmp     NextAVioXparentNVCCell


AVioXparentNVCControl:

        inc     si                      ; -1 => an attribute or return addr.
        jz      exit_AXNVC

        add     di,wDest3rdIncr         ; Otherwise a cell image row boundary.

        jmp     short NextAVioXparentNVCCell


exit_AXNVC:

        mov     dx,cx
        dec     dx
        dec     wDest2ndIncr
        mov     cx,wDest2ndIncr
        mov     bl,bUnderscoreMask

        jmp     next_attribute_cluster

AVioDrawXparentNVC   ENDP


;/***************************************************************************
;*
;* FUNCTION NAME = AVioDrawXparentHC     
;*
;* DESCRIPTION   = This routine draws a stream of cells which need to be        
;*                 clipped horizontally.  NB:  This routine exits with a JMP    
;*                 Next_attribute_cluster rather than a RET.                    
;*                                                                              
;*                 Registers Destroyed:                                                       
;*                       AL SI                                                                
;*                 Registers Preserved: 
;*                       BL CX          
;*
;* INPUT         =    AH -- The number of scan lines in an image cell.                        
;*                    AL -- The horizontal clipping mask.                                     
;*                    BH -- Reverse Video Mask -- XOR'd with each scan line.                  
;*                    BL -- Underscore Mask    --  OR'd with the last scan line.              
;*                    DX -- The primary increment for DI.  It's used to move DI down          
;*                          to the next scan line in the current image cell.                  
;*                 ES:DI -- Destination pointer.  It points to the top byte of the            
;*                          first image cell to be drawn over.                                
;*                    DS -- The Segment selector for the Symbol Set we're using.              
;*                 SS:SP -- Pseudocode pointer.  This points to the next pseudo code          
;*                          syllable to process.                                              
;*                                                                                      
;*
;* OUTPUT        = ES:DI --> Top scan line of the next cell to be drawn.
;*                 SS:SP --> Moves according to the pseudocode consumed by this function.
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

AVioDrawXparentHC   PROC

        ASSUME  ds:FontSeg

        inc     wDest1stIncr            ; Adjust the DI increment to match
                                        ; the needs of this routine.

        mov     cl,ah                   ; LineCount for the top cell image.
        mov     ch,al                   ; Since we'll be using AL for
                                        ; image lines.

        mov     dx,EGA_BASE+GRAF_ADDR+1 ; Set up DX for OUT's to GRAF_BIT_MASK.
                                        ; We assume GRAF_ADDR has been set
                                        ; to point to GRAF_BIT_MASK.

NextHCCell:

        pop     si                      ; Get the next code syllable.
        or      si,si
        js      HCControl               ; Is it a control syllable?

;/*
;**  A particular font includes a span of glyphs which may not be complete.
;**  That is, some valid glyph indices may not have defined pel images.
;**  The span of defined glyphs is characterized by wFirstGlyph and
;**  wGlyphSpan.  Glyph indices outside this range are mapped to wDefaultGlyph.
;*/

        sub     si,wFirstGlyph          ; The lowest defined glyph
        cmp     si,wGlyphSpan           ; The number of defined glyphs
        jb      axhc_no_substitution
        mov     si,wDefaultGlyph        ; Substitution relative to
                                        ; wFirstGlyph
axhc_no_substitution:

        imul    si,si,6                 ; Glyph descriptors are 6 bytes.

        mov     si,WORD PTR fsCharOffset[si]     ; The first two bytes are the offset.
        add     si,wTopLineOffset

        EVEN
                                        ; If not copy a cell image to the
NextHCScanLine:                         ; destination bitmap.

        dec     cl
        jz      LastHCScanLine

        lodsb                           ; Load a scan line.

        xor     al,bh                   ; Invert it if we're doing
                                        ; reverse video.

        and     al,ch                   ; Clip the line horizontally.
        out     dx,al                   ; Define the pixels we're setting.
        xchg    al,es:[di]              ; Latch the target byte, merge it
                                        ; with our new stuff, and write it
                                        ; back.

        add     di,wDest1stIncr         ; Move DI to the next target byte.

        jmp     NextHCScanLine


LastHCScanLine:                         ; Processing for the last scan line.

        lodsb
        or      al,bl                   ; If we're underscoring.
        xor     al,bh
        and     al,ch
        out     dx,al
        xchg    al,es:[di]
        add     di,wDest1stIncr

        mov     cl,ah                   ; Set the loop counter for the next
                                        ; cell image.
        jmp     NextHCCell


HCControl:                              ; SI is a control value.

;/*
;**  Since a sequence of horizontally clipped cells always fits in a single
;**  column, the only control value we'll see is -1.  We won't see any -2
;**  row boundary marks.
;*/

        dec     wDest1stIncr            ; Restore the DI increment to its
                                        ; its initial value.
        mov     al,ch                   ; Restoring AL...

        jmp     next_attribute_cluster

AVioDrawXparentHC   ENDP


;/***************************************************************************
;*
;* FUNCTION NAME =  AVioDrawOpaqueHC     
;*
;* DESCRIPTION   = Each call to this routine performs one of the possible       
;*                 two passes required to draw opaque cells which must be       
;*                 clipped horizontally.  NB:  This routine exits with a RET    
;*                 rather than a JMP Next_attribute_cluster.  That's to         
;*                 allow for the two-pass logic within InterpretAttributes.     
;*                                                                              
;*                 Registers Destroyed:                                                       
;*                       AL CX SI                                                             
;*                 Registers Preserved: 
;*                       None           
;*
;* INPUT         =   AH -- The number of scan lines in an image cell.
;*                   BH -- Reverse Video Mask --  Used in the two pass drawing scheme.        
;*                   BL -- Underscore Mask    --  OR'd with the last scan line.               
;*                   CX -- The number of top lines to skip for each cell image.               
;*                   DX -- The primary increment for DI.  It's used to move DI down           
;*                         to the next scan line in the current image cell.                   
;*                ES:DI -- Destination pointer.  It points to the top byte of the             
;*                         first image cell to be drawn over.                                 
;*                   DS -- The Segment selector for the Symbol Set we're using.               
;*                SS:BP -- Pseudocode pointer.  This points to the next pseudo code           
;*                         syllable to process.  BP is used instead of SP to preserve         
;*                         the pseudo code for a possible second pass.                        
;*
;* OUTPUT        = ES:DI -- will point to the top scan line of the next cell to be            
;*                          drawn.
;*                 SS:BP -- will move according to the pseudo code processed by this          
;*                          function.                                                         
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

AVioDrawOpaqueHC    PROC

        ASSUME  ds:FontSeg

        mov     cx,wTopLineOffset       ; CL will hold the top line
                                        ; offset during the code
                                        ; below.

        inc     dx                      ; Adjust the DI increment to
                                        ; account for the use of xchg
                                        ; instead of movsb or stosb.
NextOHCSyllable:

        mov     si,npinxGlyph
        add     si,2
        xchg    si,npinxGlyph
        mov     si,ss:[si]

        or      si,si
        js      OHCControl

;/*
;**  A particular font includes a span of glyphs which may not be complete.
;**  That is, some valid glyph indices may not have defined pel images.
;**  The span of defined glyphs is characterized by wFirstGlyph and
;**  wGlyphSpan.  Glyph indices outside this range are mapped to wDefaultGlyph.
;*/

        sub     si,wFirstGlyph          ; The lowest defined glyph
        cmp     si,wGlyphSpan           ; The number of defined glyphs
        jb      aohc_no_substitution
        mov     si,wDefaultGlyph        ; Substitution relative to
                                        ; wFirstGlyph
aohc_no_substitution:

        imul    si,si,6                 ; Glyph descriptors are 6 bytes.

        mov     si,WORD PTR fsCharOffset[si]     ; The first two bytes are the offset.
        add     si,cx                   ; Top lines to skip.

        mov     ch,ah                   ; CH is the counter for the
                                        ; scan line loop.
NextOHCScanLine:

        dec     ch
        jz      LastOHCScanLine
        lodsb
        xor     al,bh
        xchg    al,es:[di]
        add     di,dx
        jmp     NextOHCScanLine


LastOHCScanLine:

        lodsb
        or      al,bl
        xor     al,bh
        xchg    al,es:[di]
        add     di,dx
        jmp     NextOHCSyllable


OHCControl:

        dec     dx

        ret

AVioDrawOpaqueHC    ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = SetDisplayDefaults 
;*
;* DESCRIPTION   =  Puts the EGA registers into the state expected by the rest of 
;*                  PM
;*
;*                  Registers Destroyed:  
;*                        AX DX           
;*                  Registers Preserved:  
;*                        None            
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

SetDisplayDefaults  PROC

        mov     dx,EGA_BASE+SEQ_ADDR
        mov     al,SEQ_MAP_MASK
        mov     ah,MM_ALL
        out     dx,ax

        mov     dx,EGA_BASE+GRAF_ADDR

        mov     al,GRAF_DATA_ROT
        mov     ah,DR_SET
        out     dx,ax

        mov     al,GRAF_MODE
        mov     ah,M_PROC_WRITE OR M_DATA_READ
        out     dx,ax

        mov     al,GRAF_SET_RESET
        mov     ah,0
        out     dx,ax

        mov     al,GRAF_ENAB_SR
        out     dx,ax

        mov     al,GRAF_BIT_MASK
        mov     ah,0FFh
        out     dx,ax

        ret

SetDisplayDefaults  ENDP

ifndef VGA    

;/***************************************************************************
;*
;* FUNCTION NAME = InvertScreenRect 
;*
;* DESCRIPTION   = This routine inverts a rectangle of pixels within a byte
;*                 column on the screen.  It's used to toggle the text     
;*                 cursor, which is guaranteed to fit within a byte column.
;*                                                                         
;*                 Registers Destroyed:                                                       
;*                       AX, CX, DI                                                           
;*                 Registers Preserved:                                                       
;*                       None                                                                 
;*                                                                         
;* INPUT         = ES:DI --  Address of the top byte in the column rectangle.
;*                 BL    --  A mask for the bits to invert within the column.
;*                 CX    --  The number of rows in the rectangle.
;*                 SI    --  Increment to advance EDI to the next row.
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

InvertScreenRect    PROC    NEAR

        mov     dx,EGA_BASE+SEQ_ADDR
        mov     al,SEQ_MAP_MASK
        mov     ah,MM_ALL
        out     dx,ax

        mov     dx,EGA_BASE+GRAF_ADDR
        mov     ah,bl
        mov     al,GRAF_BIT_MASK
        out     dx,ax

        mov     ah,MM_ALL
        mov     al,GRAF_SET_RESET
        out     dx,ax

        mov     al,GRAF_ENAB_SR
        out     dx,ax

        mov     al,GRAF_DATA_ROT
        mov     ah,DR_XOR
        out     dx,ax

Invert_Next_Line:

        xchg    al,es:[di]
        add     di,si
        loop    Invert_Next_Line

        ret

InvertScreenRect    ENDP
endif  ; not VGA
 
ifdef VGA

;/*
;**
;**  Alternative 'InterpretAttributes' for 8 pel wide characters only
;** 
;**  InterpretAttributes begins with a prologue for establishing part
;**  of the environment assumed by the drawing functions.  The drawing
;**  functions are presumed to preserve or properly update this state
;**  information across cell clusters with a common set of attributes.
;** 
;**  Set up the hardware clip rectangle first.  It remains constant
;**  throughout the display of the text.  Note that the clip rectangle is
;**  given as lower left/upper right instead of upper left/lower right as
;**  the 8514 would like it.
;** 
;**  It appears that the text clip rectangle is different than the normal
;**  PM clip rectangle in terms of which edges are inclusive and which are
;**  exclusive, just as in the strblt path.  In PM, the left and bottom
;**  edges are inclusive and the top and right edges are exclusive.  The
;**  text path, however, is based on         code, and in         the top
;**  and left edges are inclusive and the bottom and right edges are
;**  exclusive.  Set up the hardware clip rectangle with that in mind.
;*/

not8pel:

        assumes ds,nothing
        assumes es,nothing

        mov     es,seg_lpBitmapBits     ; Set up addressability for
        ASSUME  es:EGAMem

        mov     cx,wDestHeight          ; Height of destination bitmap
        mov     ax,cx

        sub     ax,rClipRect.rcs_pts2.pts_y  ; Top (flipped and inclusive)
        mov     wClipYmin,ax

        mov     ax,cx                   ; Height of destination bitmap
        sub     ax,rClipRect.rcs_pts1.pts_y  ; Bottom (flipped and exclusive)
        mov     wClipYmax,ax

        mov     ax,rClipRect.rcs_pts1.pts_x  ; Left (inclusive)
        mov     wClipXmin,ax

        mov     ax,rClipRect.rcs_pts2.pts_x  ; Right (exclusive)
        mov     wClipXmax,ax

;/*
;** Construct the horizontal clip flag.
;*/

        xor     ah,ah                   ; Assume there is no clipping
        mov     cx,wVertLineCount
        cmp     cx,wCellWidth
        je      @f
        not     ah                      ; All 1s mean there is clipping
@@:
        mov     bHorizontalClipMask,ah

        mov     bx,xcoordStart
        sub     bx,wLeftLineOffset
        mov     di,ycoordStart
        sub     di,wTopLineOffset
        add     di,wCellHeight
        dec     di

        mov     cx,ax
        mov     ax,wCellWidth
        or      ch,ch                   ; Clip flag set?
        jnz     @f                      ; Yes, partial column, use wCellWidth
        mul     wFullCols               ; Some number of full columns
@@:
        mov     wDest3rdIncr,ax

;/*
;**  The drawing functions use addresses in an upward sequence.  Hence
;**  the need to have the direction flag cleared.
;*/

        cld

;/*
;**  The following label is the return point for all the device-dependant
;**  drawing functions.  Most of them terminate with a jmp instead of a ret.
;*/

next_attribute_cluster2:

        cmp     sp,pReturnAddress       ; Have we exhausted the pseudocode?
        jne     ProcessAStream2

        cld                             ; direction must be clear on return
        retn                            ; If so return to the scanner.


ProcessAStream2:

        pop     si                      ; Get the new attribute set.

        push    bx                      ; save coordinate

        mov     ax,si
        and     ax,(AVioReverseVideoMask OR AVioTransparentMask)
        cmp     ax,AVioReverseVideoMask ; Check for Opaque with Reverse Video
        jnz     set_hardware_colors2

        xchg    ax,si                   ; For that case we do the reversing
        mov     bl,ah                   ;   by just swapping foreground and
        shr     ah,4                    ;   background color attributes.
        shl     bx,4
        or      ah,bl
        xchg    ax,si                   ; Put colors back in SI

set_hardware_colors2:

        mov     cx,si                   ; Colors to CH
        mov     cl,ch
        shr     ch,4                    ; CH = background color and
        and     cx,0f0fh                ;   CL = foreground color
        xor     bx,bx
        mov     bl,ch                   ; BX = background color
        mov     ah,ColorMap[bx]         ; Set hardware background color

        mov     bl,cl                   ; BX = foreground index
        mov     al,ColorMap[bx]         ; Set hardware foreground color

        mov     wColor,ax               ; save colours

        test    si,AVioUnderscoreMask
        jz      set_up_registers2

        mov     ax,wTopLineOffset
        add     ax,wScanLineCount
        cmp     ax,wCellHeight
        jz      set_up_registers2
        and     si,(NOT AVioUnderscoreMask)


set_up_registers2:
        xor     cx,cx                   ; Zero reference value
        mov     ax,AVioUnderscoreMask   ; If underscoring, bl => 0FFh else 0.
        and     ax,si
        cmp     cx,ax
        sbb     bx,bx
        mov     bUnderscoreMask,bl

        mov     ax,si                   ; Get the previous attribute set
        mov     cx,si
        xchg    ax,wLastAttributes      ; and preserve this attribute set
                                        ; for the next InterpretAttributes
                                        ; call.

        mov     bx,ax                   ; Compare the old
        xor     bx,si                   ; new LCID bit fields.
        and     bx,AVioLCIDMask         ; Are they different?
        jnz     set_up_lcid2

        mov     ds,wselfontsave
        assumes ds,FontSeg

        jmp     short SameSymbolSet2


set_up_lcid2:
        and     si,AVioLCIDMask         ; If so set up DS for the new
        shl     si,1                    ; symbol set.
        mov     bx,wSymbolSetSelectors[si]
        or      bx,bx                   ; Is this selector usable?
        jnz     ValidSymbolSetSelector2

;/*
;**  If we don't have a usable selector, we use the selector for the
;**  base symbol set, and map all of the glyph points for that LCID to
;**  a surrogate glyph.
;*/

        mov     bx,ss                   ; Set up to scan forward
        mov     ds,bx                   ; through the glyphs on the
        cld                             ; stack.

        mov     bx,SURROGATE_CHARACTER
        mov     si,sp
        add     si,2                    ;We pushed BX above, must counter!

check_next_syllable2:
        lodsw
        cmp     ax,-2
        ja      found_a_control_marker2  ; -1 => control marker
        je      check_next_syllable2     ; -2 => end of row action

        mov     [si-2],bx
        jmp     short check_next_syllable2


found_a_control_marker2:
        mov     bx,wSymbolSetSelectors[0]   ; Substitute the selector for
                                            ; the base symbol set.

ValidSymbolSetSelector2:
        mov     wselfontsave,bx
        mov     ds,bx
        assumes ds,FontSeg

;/*
;**  Here we set up the data which characterizes the font which is becoming
;**  active.
;*/

        mov     bx,fsMetrics.foca_usLastChar
        inc     bx
        mov     wGlyphSpan,bx
        mov     bx,fsMetrics.foca_usFirstChar
        mov     wFirstGlyph,bx
        mov     bx,fsMetrics.foca_usDefaultChar
        mov     wDefaultGlyph,bx

        mov     si,cx

SameSymbolSet2:

        mov     ax,1                    ; Assume opaque cells.
        test    si,AVioTransparentMask  ; Opaque cells?
        jz      set_modes2               ; Yes.
        mov     ax,0                    ; No.

set_modes2:
        mov     wFunction,ax            ; save function

        pop     bx                      ; restore coordinate
        jmp     AVioDrawText



cEnd    <nogen>
page


;/***************************************************************************
;*
;* FUNCTION NAME = InvertClippedRect 
;*
;* DESCRIPTION   = This routine does the display work for the InvertTextCursor 
;*                 routine.  It is driven by enumerate_clip_rects.             
;*                                                                             
;* INPUT         = SS:SI is the stack frame address for InvertTextCursor.                     
;*                 SS:BX is the address of a rectangle structure which defines                
;*                       a component of the region to be inverted.                            
;*                                                                             
;*                 Registers Destroyed:                                                       
;*                       AX, BX, CX, DX, DI, ES                                               
;*                 Registers Preserved:                                                       
;*                       BP, SI                                                               
;*                 Calls:
;*                        exclude, unexclude, InvertScreenRect
;*                                                                           
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

sEnd    VioSeg

sBegin  Code
        assumes cs,Code

cProc   InvertClippedRect,<PUBLIC,NEAR>
cBegin
        call    far ptr far_InvertClippedRect
cEnd

sEnd    Code


sBegin  VioSeg
        assumes cs,VioSeg

define_Invert_Cursor_Frame far_InvertClippedRect

cBegin  <nogen>

;/*
;**  First we save the stack frame for enumerate_clip_rects
;**  and set the frame pointer to reference the local environment
;**  of InvertTextCursor.
;*/

        push    bp
        mov     bp,si

        mov     fSomethingDrawn,1       ; to denote a non-null vis region

;/*
;**  Now we retrieve the screen coordinates of the rectangle to invert.
;**  For convenience we map the coordinates to top left origin.
;*/

        mov     di,wDestHeight
        mov     dx,di

        sub     di,ss:[bx].rcl_yBottom.lo
        sub     dx,ss:[bx].rcl_yTop.lo
        mov     cx,ss:[bx].rcl_xLeft.lo
        mov     si,ss:[bx].rcl_xRight.lo

;/*
;** If the rectangle is empty we do nothing.
;*/

        cmp     dx,di
        jge     null_invert_rect

        cmp     cx,si
        jge     null_invert_rect

;/*
;**  Otherwise we save the coordinates on the stack
;**  and exclude the mouse cursor.
;*/

        push    si
        push    cx
        push    dx
        push    di

        call    far_exclude


;/*
;**  Now we retrieve the coordinates and map them into
;**  a set of parameters for driving the InvertRect routine.
;*/

        pop     di      ; ... Bottom ...
        pop     bx      ; ... Top    ...
        pop     cx      ; ... Left   ...
        pop     si      ; ... Right  ...
        mov     es,seg_lpBitmapBits

        call    InvertScreenRect

        call    far_unexclude

null_invert_rect:

;/*
;**  Before we return to enumerate_clip_rects, we must restore SI and BP
;**  to the values they had on entry.
;*/

        mov     si,bp
        pop     bp

;/*
;**  enumerate_clip_rects requires a non-zero result to keep going.
;**  A zero result stops the enumeration.
;*/

        mov     ax,sp
        ret_far

cEnd    <nogen>
page


;/***************************************************************************
;*
;* FUNCTION NAME = AVioDrawText  
;*
;* DESCRIPTION   = Interprets pseudo code on the stack to draw Vio character
;*                 cell images with possible horizontal clipping, but no    
;*                 underscoring.                                            
;*
;*                 NB: This routine exits with a JMP next_attribute_cluster8
;*                 rather than a RET.
;*
;* INPUT         =     X -- Destination X coordinate
;*                    DI -- Destination Y coordinate                                           
;*                    DS -- The Segment selector for the Symbol Set we're using.               
;*                 SS:SP -- Pseudocode pointer.  This points to the next pseudo code           
;*                          syllable to process.
;*
;* OUTPUT        = SS:SP -- will move according to the pseudo code consumed by this           
;*                          function.                                                         
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


externA SCREEN_CBSCAN                   ; screen pitch in bytes

DrawText    proc
        assumes ds,FontSeg
        assume  es:EGAMem
        assumes ss,cb_variables

;/*
;** Entry point and initialization for AVIO character drawing                    ;
;*/

AVioDrawText    label   near
        std                            ; will do characters upwards

        mov     ax,0                   ; no mode set
        mov     wModeSet,ax            ; remember no modeset

        mov     wX,bx                  ; BX = destination X for this column
        mov     wY,di                  ; DI = destination Y for this column

;/*
;**  Get next value from stack into si                                            
;**  If SI is non-negative, it's a glyph index.  Negative values denote          
;**  control actions:  -1 => a signal to return control to InterpretAttributes.  
;**                    -2 => the end of a row of cell images has been reached.    
;**                          Move to the beginning of the next row.               
;/*

process_pseudo_code:

        pop     si                     ; Fetch glyph index

        sub     si,wFirstGlyph         ; Subtract the lowest defined glyph

        cmp     si,wGlyphSpan          ; Check against number of defined glyphs
        jb      have_glyph_index       ; Jump if in range

;/*
;** glyph index is out of range - use default if not control code
;*/

        add     si,wFirstGlyph         ; Add back in lowest defined glyph
        js      @f                     ; Unsigned value is non-control code

        mov     si,wDefaultGlyph       ; use default value
        jmp     have_glyph_index       ; and skip to process

;/*
;** Check for -1 value meaning end of string (attribute or return)
;*/

@@:
        inc     si                     ; -1 => an attribute or return addr.
        js      next_row               ; skip if not - assume new row code

        mov     bx,wX                  ; restore bx = dest X
        mov     di,wY                  ; restore di = dest Y

        jmp     next_attribute_cluster2 ; need to return to attribute processing

;/*
;** Still negative - assume -2=new line. Bump addresses and get next code
;*/

next_row:
        mov     ax,wDest3rdIncr        ; X decrement to start of row
        sub     wX,ax                  ; adjust x

        mov     ax,wCellHeight         ; get cell height
        add     wY,ax                  ; adjust y

        jmp     process_pseudo_code    ; and fetch next code

;/*
;** Have a valid glyph index to process
;*/

have_glyph_index:

;/*
;** First determine whether we are clear to draw 16 chars ahead without clipping ;
;*/

;/*
;** Calculate character block limits into ax,bx,cx,dx
;*/

        mov     ax,wX                  ; get X
        mov     bx,wCellWidth          ; get cell width
        imul    bx,bx,16               ; calculate end of block of 16 chars
        add     bx,ax                  ; add to start position
        dec     bx                     ; last pos

        mov     cx,wY                  ; get Y
        mov     dx,cx
        sub     dx,wCellHeight         ; add cell height
        inc     dx                     ; top row

;/*
;** Determine whether clipped totally out
;*/

        cmp     bx,wClipXmin           ; Clipped out on l ?
        jl      bclip_invis            ; go if yes

        cmp     ax,wClipXmax           ; Clipped out on r ?
        jge     bclip_invis            ; go if yes

        cmp     cx,wClipYmin           ; Clipped out at bottom ?
        jl      bclip_invis            ; go if yes

        cmp     dx,wClipYmax           ; Clipped out at top ?
        jge     bclip_invis            ; go if yes

;/*
;** Determine whether clipped totally in
;*/

        cmp     ax,wClipXmin           ; Clipped in on l ?
        jl      bclip_invis            ; go if yes

        cmp     bx,wClipXmax           ; Clipped in on r ?
        jge     bclip_invis            ; go if yes

        cmp     dx,wClipYmin           ; Clipped in at bottom ?
        jl      bclip_invis            ; go if yes

        cmp     cx,wClipYmax           ; Clipped in at top ?
        jge     bclip_invis            ; go if yes

;/*
;** We are totally visible for 16 chars ahead, call routine to get going fast    ;
;*/

        mov     ax,16                  ; maximum character count to draw
        jmp     draw_n_wide_noclip     ; else jump to n wide unclipped

;/*
;**  The clip window intersects some part of a 16 character string starting here  ;
;**  Determine whether this current character is clipped itself                   ;
;*/

bclip_invis:

;/*
;** Recalculate character box right edge
;*/

        mov     bx,wCellWidth          ; get cell width
        add     bx,ax                  ; add to start position
        dec     bx                     ; last pos

;/*
;** Determine whether this character is clipped totally out
;*/

        cmp     bx,wClipXmin           ; Clipped out on l ?
        jl      clip_out_j             ; go if yes

        cmp     ax,wClipXmax           ; Clipped out on r ?
        jge     clip_out_j             ; go if yes

        cmp     cx,wClipYmin           ; Clipped out at bottom ?
        jl      clip_out_j             ; go if yes

        cmp     dx,wClipYmax           ; Clipped out at top ?
        jge     clip_out_j             ; go if yes

        jmp     clip_not_out           ; else skip to do more tests
clip_out_j:
        jmp     clipped_out            ; skip to ignore character

;/*
;** Determine whether clipped totally in
;*/

clip_not_out:

        cmp     ax,wClipXmin           ; Clipped in on l ?
        jl      clip_not_in            ; go if yes

        cmp     bx,wClipXmax           ; Clipped in on r ?
        jge     clip_not_in            ; go if yes

        cmp     dx,wClipYmin           ; Clipped in at bottom ?
        jl      clip_not_in            ; go if yes

        cmp     cx,wClipYmax           ; Clipped in at top ?
        jge     clip_not_in            ; go if yes

;/*
;** We are totally visible for this char, call routine to draw it
;*/

        mov     ax,1                   ; maximum character count to draw
        jmp     draw_n_wide_noclip     ; else jump to n wide unclipped

;/*
;** We have a character that needs some clip attention - call the routine
;*/

clip_not_in:
        jmp     draw_n_wide_clip       ; jump to draw clipped

;/*
;** We have a character that is totally clipped out - update ptrs and continue
;*/

clipped_out:

;/*
;** Bump horizontal address by cell width
;*/

        mov     ax,wCellWidth          ; get cell width
        add     wX,ax                  ; Bump X value

;/*
;** test for horizontal clip (?) - move to next row if yes
;*/
 
        cmp     bHorizontalClipMask,0  ; Is this text clipped?
        jne     next_row_jjj           ; Yes - jump to force new row

;/*
;** Jump to fetch next psuedo code
;*/

        jmp     process_pseudo_code    ; else return to fetch next code

next_row_jjj:
        jmp     next_row               ; return to fetch next code

;/*
;** Draw any width character on arbitrary boundary clipped                       
;**                                                                              
;** Characters of any width any any boundary and opaque or transparent are       
;** drawn clipped by this code. Character drawing will continue until            
;** either the block count reaches zero, or a non-glyph index value is           
;** found on the stack.                                                          
;*/

draw_n_wide_clip:

;/*
;** Calculate Byte address of first character to be drawn
;*/

        mov     bx,wY                  ; get y
        mov     ax,SCREEN_CBSCAN       ; screen pitch in bytes
        mul     bx                     ; result to ax
        mov     bx,wX                  ; get x
        cmp     bx,-1
        jle     x_neg                  ; x negative
        shr     bx,3                   ; byte address
        add     ax,bx                  ; add to line address
        jmp     sv_byte_addr
x_neg:
        dec     ax
sv_byte_addr:
        mov     wByteAddr,ax           ; and save on stack

;/*
;** Calculate update mask and bit address
;*/

        mov     ax,00ffh
        mov     cx,wCellWidth          ; get cell width
        ror     ax,cl                  ; n bits to ah
        mov     al,0
        mov     cx,wX                  ; get x
        and     cx,0007h               ; bit address
        mov     wBitAddr,cx
        shr     ax,cl                  ; shift down to align
        mov     wMask,ax               ; store mask

;/*
;** Calculate font address for glpyh
;*/

        imul    si,si,6                ; glyph entries are 6 bytes
        mov     si,fsCharOffset[si].fe_dBits.lo ; DS:SI -> character data
        mov     bx,wCellHeight         ; Load bx with char height
        lea     si,[si+bx-1]           ; move si to end of definition

;/*
;** Indicate no modeset in hardware
;*/

        mov     ax,0                   ; no mode set
        mov     wModeSet,ax            ; remember no modeset

;/*
;** Enable all plames for update
;*/

        mov     dx,EGA_BASE+SEQ_ADDR   ; Sequncer regs
        mov     al,SEQ_MAP_MASK        ; Map mask register
        mov     ah,MM_ALL              ; Data (all on)
        out     dx,ax                  ; do i/o

;/*
;** Set graphics mode register to write mode 0
;*/

        mov     dx,EGA_BASE+GRAF_ADDR  ; graphics controller regs
        mov     al,GRAF_MODE           ; graphics mode register
        mov     ah,M_PROC_WRITE OR M_DATA_READ ; Data to be written
        out     dx,ax                  ; do i/o

;/*
;** Set data rotate register for no logic function
;*/

        mov     ah,DR_SET              ; no logic function
        mov     bx,wBitAddr            ; get bit address
        or      ah,bl                  ; combine
        mov     al,GRAF_DATA_ROT       ; rotate register
        out     dx,ax                  ; do i/o

;/*
;** Skip background box if transparent
;*/

        mov     ax,wFunction           ; get function
        cmp     ax,0                   ; transparent ?
        jne     cl_bg_box_do           ; go if not
        jmp     cl_bg_box_end          ; else skip
cl_bg_box_do:

;/*
;** Set all bits on in enable set/reset reg (select s/r reg)
;*/

        mov     dx,EGA_BASE+GRAF_ADDR  ; graphics controller regs
        mov     al,GRAF_ENAB_SR        ; graphics enable s/r reg
        mov     ah,0ffh                ; all ones
        out     dx,ax                  ; do i/o

;/*
;** Set/reset reg = background colour
;*/

        mov     ax,wColor              ; Get colours
        mov     al,GRAF_SET_RESET      ; set/reset reg
        out     dx,ax                  ; do i/o

;/*
;** Adjust hardware mask for x clipping
;*/

        mov     bx,wX                  ; get x coordinate
        and     bx,0fff8h              ; byte boundary x
        mov     dx,wMask               ; get mask
        mov     ax,07fffh              ; first bit mask
        mov     cx,16                  ; possible 16 bits
cl_mask_adjust:
        cmp     bx,wClipXmin           ; compare with ymin
        jl      cl_mask_adjust_1       ; go if outside
        cmp     bx,wClipXmax           ; compare with ymax
        jl      cl_mask_adjust_2       ; go if outside
cl_mask_adjust_1:
        and     dx,ax                  ; strip bit
cl_mask_adjust_2:
        ror     ax,1                   ; rotate mask
        inc     bx                     ; bump x
        loop    cl_mask_adjust         ; loop 16 bits
        mov     wMask,dx

;/*
;** Set bit mask = char mask first part
;*/

        mov     ax,wMask               ; get mask
        mov     dx,EGA_BASE+GRAF_ADDR  ; graphics controller regs
        mov     al,GRAF_BIT_MASK       ; graphics bit mask
        out     dx,ax                  ; do i/o

;/*
;** Loop For all slices to do background box first part
;*/

        mov     di,wByteAddr           ; get byte address
        mov     cx,wCellHeight         ; loop count = cell height
        mov     bx,wY                  ; get Y coordinate
cl_bg_box_loop_1:

        cmp     bx,wClipYmin           ; compare with ymin
        jl      @f                     ; go if outside
        cmp     bx,wClipYmax           ; compare with ymax
        jge     @f                     ; go if outside

        xchg    al,es:[di]             ; read/write VGA
@@:
        dec     bx                     ; y=y-1
        sub     di,SCREEN_CBSCAN       ; up one line

        loop    cl_bg_box_loop_1       ; loop

;/*
;** Set bit mask = char mask second part (unless no second run exists)
;*/

        mov     ax,wMask               ; get mask
        test    al,0ffh                ; any second mask ?
        jz      cl_bg_box_end          ; skip if not

        mov     ah,al                  ; mask to ah
        mov     al,GRAF_BIT_MASK       ; graphics bit mask
        out     dx,ax                  ; output to h/w

;/*
;** Loop For all slices to do background box second part
;*/

        mov     di,wByteAddr           ; get byte address
        inc     di                     ; bump address
        mov     cx,wCellHeight         ; loop count = cell height
        mov     bx,wY                  ; get Y coordinate
cl_bg_box_loop_2:

        cmp     bx,wClipYmin           ; compare with ymin
        jl      @f                     ; go if outside
        cmp     bx,wClipYmax           ; compare with ymax
        jge     @f                     ; go if outside

        xchg    al,es:[di]             ; read/write VGA
@@:
        dec     bx                     ; y=y-1
        sub     di,SCREEN_CBSCAN       ; up one line

        loop    cl_bg_box_loop_2       ; loop
cl_bg_box_end:

;/*
;** Set/reset reg = foreground colour
;*/

        mov     ax,wColor              ; Get colours
        mov     ah,al                  ; fg col to AL
        mov     dx,EGA_BASE+GRAF_ADDR  ; graphics controller regs
        mov     al,GRAF_SET_RESET      ; graphics set/reset reg
        out     dx,ax                  ; do i/o

;/*
;** Set graphics mode register to write mode 3
;*/

        mov     al,GRAF_MODE           ; graphics mode
        mov     ah,M_AND_WRITE OR M_DATA_READ ; write mode 3
        out     dx,ax                  ; do i/o

;/*
;** Set bit mask = char mask first part
;*/

        mov     ax,wMask               ; get mask
        mov     al,GRAF_BIT_MASK       ; graphics bit mask
        out     dx,ax                  ; do i/o

;/*
;** Do first 2 slices (including underscore)
;*/

        push    si                     ; save font pointer
        mov     di,wByteAddr           ; get byte address
        mov     bx,wY                  ; get Y coordinate

        cmp     bx,wClipYmin           ; compare with ymin
        jl      @f                     ; go if outside
        cmp     bx,wClipYmax           ; compare with ymax
        jge     @f                     ; go if outside

        mov     al,ds:[si]             ; read font
        or      al,bUnderscoreMask     ; or underscore

        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes

        xchg    al,es:[di]             ; read VGA data into latches
@@:
        dec     bx                     ; y=y-1
        sub     si,1                   ; up one line
        sub     di,SCREEN_CBSCAN        ; up one line

        cmp     bx,wClipYmin           ; compare with ymin
        jl      @f                     ; go if outside
        cmp     bx,wClipYmax           ; compare with ymax
        jge     @f                     ; go if outside

        mov     al,ds:[si]             ; read font

        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes

        xchg    al,es:[di]             ; read VGA data into latches
@@:
        dec     bx                     ; y=y-1
        sub     si,1                   ; up one line
        sub     di,SCREEN_CBSCAN       ; up one line

;/*
;** Loop For remaining slices to do foreground first part
;*/

        mov     cx,wCellHeight         ; loop count = cell height
        sub     cx,2                   ; already done 2
cl_fg_box_loop_1:

        cmp     bx,wClipYmin           ; compare with ymin
        jl      @f                     ; go if outside
        cmp     bx,wClipYmax           ; compare with ymax
        jge     @f                     ; go if outside

        mov     al,ds:[si]             ; read font

        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes

        xchg    al,es:[di]             ; read VGA data into latches
@@:
        dec     bx                     ; y=y-1
        sub     si,1                   ; up one line
        sub     di,SCREEN_CBSCAN        ; up one line

        loop    cl_fg_box_loop_1       ; loop
        pop     si                     ; rest font pointer

;/*
;** Set bit mask = char mask second part (unless no second run exists)
;*/

        mov     ax,wMask               ; get mask
        test    al,0ffh                ; any second mask ?
        jz      cl_fg_box_end          ; skip if not

        mov     ah,al                  ; data to ah
        mov     al,GRAF_BIT_MASK       ; graphics bit mask
        out     dx,ax                  ; output to h/w

;/*
;** Do first 2 slices (including underscore)
;*/

        mov     di,wByteAddr           ; get byte address
        inc     di                     ; bump dx
        mov     bx,wY                  ; get Y coordinate

        cmp     bx,wClipYmin           ; compare with ymin
        jl      @f                     ; go if outside
        cmp     bx,wClipYmax           ; compare with ymax
        jge     @f                     ; go if outside

        mov     al,ds:[si]             ; read font
        or      al,bUnderscoreMask     ; or underscore

        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes

        xchg    al,es:[di]             ; read VGA data into latches
@@:
        dec     bx                     ; y=y-1
        sub     si,1                   ; up one line
        sub     di,SCREEN_CBSCAN       ; up one line

        cmp     bx,wClipYmin           ; compare with ymin
        jl      @f                     ; go if outside
        cmp     bx,wClipYmax           ; compare with ymax
        jge     @f                     ; go if outside

        mov     al,ds:[si]             ; read font
        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes

        xchg    al,es:[di]             ; read VGA data into latches
@@:
        dec     bx                     ; y=y-1
        sub     si,1                   ; up one line
        sub     di,SCREEN_CBSCAN       ; up one line

;/*
;** Loop For remaining slices to do foreground second part
;*/

        mov     cx,wCellHeight         ; loop count = cell height
        sub     cx,2                   ; already done 2
cl_fg_box_loop_2:

        cmp     bx,wClipYmin           ; compare with ymin
        jl      @f                     ; go if outside
        cmp     bx,wClipYmax           ; compare with ymax
        jge     @f                     ; go if outside

        mov     al,ds:[si]             ; read font

        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes

        xchg    al,es:[di]             ; read/write vga
@@:
        dec     bx                     ; y=y-1
        sub     si,1                   ; up one line
        sub     di,SCREEN_CBSCAN       ; up one line

        loop    cl_fg_box_loop_2       ; loop

cl_fg_box_end:

;/*
;** Bump horizontal address by cell width
;*/

        mov     ax,wCellWidth          ; get cell width
        add     wX,ax                  ; Bump X value

;/*
;** test for horizontal clip (?) - move to next row if yes
;*/

        cmp     bHorizontalClipMask,0  ; Is this text clipped?
        jne     next_row_jj            ; Yes - jump to force new row

;/*
;** Jump to fetch next pseudo code
;*/

        jmp     process_pseudo_code    ; else jump to fetch next code

next_row_jj:
        jmp     next_row               ; return to fetch next code

;/*
;** Draw any width characters on arbitrary boundary unclipped                    
;**                                                                              
;** Characters of any width any any boundary and opaque or transparent and       
;** drawn totally unclipped by this code. Drawing will continue until            
;** either the block count reaches zero, or a non-glyph index value is           
;** found on the stack.                                                          
;*/

draw_n_wide_noclip:

;/*
;** Save character count
;*/

        mov     wCharCount,ax          ; save char count

;/*
;** jump to process first character
;*/

        jmp     draw_nw_glyph_ok       ; skip fetch code first time

;/*
;** Fetch glyph code - check for valid index value
;*/

draw_n_wide_noclip_next:
        pop     si                     ; get glyph index
        sub     si,wFirstGlyph         ; Subtract the lowest defined glyph
        cmp     si,wGlyphSpan          ; Check against number of defined glyphs
        jb      draw_nw_glyph_ok       ; Jump if in range

        add     si,wFirstGlyph         ; Add back in lowest defined glyph
        js      nw_not_default_glyph   ; Unsigned value is non-control code
        mov     si,wDefaultGlyph       ; use default value
        jmp     draw_nw_glyph_ok       ; Jump if in range

;/*
;** Control code - put pcode back on stack and return to process
;*/

nw_not_default_glyph:
        push    si                     ; return code to stack
        jmp     draw_nw_exit           ; and exit

;/*
;** Valid glyph index - Calculate address of font
;*/

draw_nw_glyph_ok:
        imul    si,si,6                ; glyph entries are 6 bytes
        mov     si,fsCharOffset[si].fe_dBits.lo ; DS:SI -> character data
        mov     wFontPtr0,si           ; save on stack
        mov     bx,wCellHeight         ; Load bx with char height
        lea     si,[si+bx-1]           ; move si to end of definition

;/*
;** determine whether the fast 5 wide code can be used
;*/

        cmp     word ptr wCellWidth,6  ; cell width = 6 ?
        je      maybe_56_special       ; go if yes

        cmp     word ptr wCellWidth,5  ; cell width = 5 ?
        jne     not_56_special         ; go if not

maybe_56_special:
        cmp     wCharCount,8           ; do we have 8 or more left ?
        jb      not_56_special         ; go if not

        mov     ax,wX                  ; get x
        and     ax,0007h               ; leave bit address and sign
        jne     not_56_special         ; go if not

        cmp     bHorizontalClipMask,0  ; Is this text clipped?
        jne     not_56_special         ; Yes - jump to force new row

        mov     bx,sp                  ; copy sp to bx
        test    ss:[bx],08000h         ; test next entry on stack (1)
        jnz     not_56_special         ; skip if control code

        test    ss:[bx+2],08000h       ; test next entry on stack (2)
        jnz     not_56_special         ; skip if control code

        test    ss:[bx+4],08000h       ; test next entry on stack (3)
        jnz     not_56_special         ; skip if control code

        test    ss:[bx+6],08000h       ; test next entry on stack (4)
        jnz     not_56_special         ; skip if control code

        test    ss:[bx+8],08000h       ; test next entry on stack (5)
        jnz     not_56_special         ; skip if control code

        test    ss:[bx+10],08000h      ; test next entry on stack (6)
        jnz     not_56_special         ; skip if control code

        test    ss:[bx+12],08000h      ; test next entry on stack (7)
        jnz     not_56_special         ; skip if control code

        mov     ax,wFunction           ; get function
        cmp     ax,0                   ; transparent ?
        je      not_56_special         ; go if yes

;/*
;** Conditions good to call fast 5 wide code - do it
;*/

        jmp     fast_5or6_wide          ; call fast 5 or 6 wide

;/*
;** Continue with normal draw - calculate update address in VGA
;*/

not_56_special:
        mov     bx,wY                  ; get y
        mov     ax,SCREEN_CBSCAN        ; screen pitch in bytes
        mul     bx                     ; result to ax
        mov     bx,wX                  ; get x
        shr     bx,3                   ; byte address
        add     ax,bx                  ; add to line address
        mov     wByteAddr,ax           ; and save on stack

;/*
;** Calculate update mask and bit address
;*/

        mov     ax,00ffh
        mov     cx,wCellWidth          ; get cell width
        ror     ax,cl                  ; n bits to ah
        mov     al,0
        mov     cx,wX                  ; get x
        and     cx,0007h               ; bit address
        mov     wBitAddr,cx
        shr     ax,cl                  ; shift down to align
        mov     wMask,ax               ; store mask

;/*
;** Indicate no modeset in hardware
;*/

        mov     ax,0                   ; no mode set
        mov     wModeSet,ax            ; remember no modeset

;/*
;** Enable all planes for update
;*/

        mov     dx,EGA_BASE+SEQ_ADDR   ; Sequencer regs
        mov     al,SEQ_MAP_MASK        ; Map mask register
        mov     ah,MM_ALL              ; Data (all on)
        out     dx,ax                  ; do i/o

;/*
;** Set graphics mode register to write mode 0
;*/

        mov     dx,EGA_BASE+GRAF_ADDR  ; graphics controller regs
        mov     al,GRAF_MODE           ; graphics mode
        mov     ah,M_PROC_WRITE OR M_DATA_READ ; write mode 0
        out     dx,ax                  ; do i/o

;/*
;** Set data rotate register for no logic function
;*/

        mov     ah,DR_SET              ; no logic function
        mov     bx,wBitAddr            ; get bit address
        or      ah,bl                  ; combine
        mov     al,GRAF_DATA_ROT       ; graphics rotate reg
        out     dx,ax                  ; do i/o

;/*
;** Skip background box if transparent
;*/

        mov     ax,wFunction           ; get function
        cmp     ax,0                   ; transparent ?
        je      bg_box_end             ; go if yes

;/*
;** Set all bits on in enable set/reset reg (select s/r reg)
;*/

        mov     al,GRAF_ENAB_SR        ; graphics enable s/r
        mov     ah,0ffh                ; all bits on
        out     dx,ax                  ; do i/o

;/*
;** Set/reset reg = background colour
;*/

        mov     ax,wColor              ; Get colours
        mov     al,GRAF_SET_RESET      ; graphics set reset reg
        out     dx,ax                  ; do i/o

;/*
;** Set bit mask = char mask first part
;*/

        mov     ax,wMask               ; get mask
        mov     al,GRAF_BIT_MASK       ; graphics bit mask
        out     dx,ax                  ; do i/o

;/*
;** Loop For all slices to do background box first part (2x unrolled loop)
;*/

        mov     di,wByteAddr           ; get byte address
        mov     cx,wCellHeight         ; loop count = cell height
        shr     cx,1                   ; divide cx by 2
bg_box_loop_1:

        xchg    al,es:[di]             ; read/write VGA
        sub     di,SCREEN_CBSCAN       ; up one line

        xchg    al,es:[di]             ; read/write VGA
        sub     di,SCREEN_CBSCAN       ; up one line

        loop    bg_box_loop_1          ; loop

;/*
;** Set bit mask = char mask second part (unless no second run exists)
;*/

        mov     ax,wMask               ; get mask
        test    al,0ffh                ; any second mask ?
        jz      bg_box_end             ; skip if not

        mov     ah,al                  ; mask to ah
        mov     al,GRAF_BIT_MASK       ; graphics bit mask
        out     dx,ax                  ; output to h/w

;/*
;** Loop For all slices to do background box second part
;*/

        mov     di,wByteAddr           ; get byte address
        inc     di                     ; bump address
        mov     cx,wCellHeight         ; loop count = cell height
        shr     cx,1                   ; divide cx by 2
bg_box_loop_2:

        xchg    al,es:[di]             ; read/write VGA
        sub     di,SCREEN_CBSCAN       ; up one line

        xchg    al,es:[di]             ; read/write VGA
        sub     di,SCREEN_CBSCAN       ; up one line

        loop    bg_box_loop_2          ; loop
bg_box_end:

;/*
;** Set/reset reg = foreground colour
;*/

        mov     ax,wColor              ; Get colours
        mov     ah,al                  ; fg to ah
        mov     al,GRAF_SET_RESET      ; graphics set reset reg
        out     dx,ax                  ; do i/o

;/*
;** Set graphics mode register to write mode 3
;*/

        mov     al,GRAF_MODE           ; graphics mode
        mov     ah,M_AND_WRITE OR M_DATA_READ ; write mode 3
        out     dx,ax                  ; do i/o

;/*
;** Set bit mask = char mask first part
;*/

        mov     ax,wMask               ; get mask
        mov     al,GRAF_BIT_MASK       ; graphics bit mask
        out     dx,ax                  ; do i/o

;/*
;** Do first 2 slices (including underscore)
;*/

        push    si                     ; save font pointer
        mov     di,wByteAddr           ; get byte address

        lodsb                          ; read font
        or      al,bUnderscoreMask     ; or underscore
        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes
        xchg    al,es:[di]             ; read and write vga
@@:
        sub     di,SCREEN_CBSCAN       ; up one line

        lodsb                          ; read font
        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes
        xchg    al,es:[di]             ; read and write VGA
@@:
        sub     di,SCREEN_CBSCAN       ; up one line

;/*
;** Loop For remaining slices to do foreground first part (2x unrolled)
;*/

        mov     cx,wCellHeight         ; loop count = cell height
        sub     cx,2                   ; already done 2
        shr     cx,1                   ; divide cx by 2
fg_box_loop_1:

        lodsb                          ; read font
        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes
        xchg    al,es:[di]             ; read and write VGA
@@:
        sub     di,SCREEN_CBSCAN       ; up one line

        lodsb                          ; read font
        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes
        xchg    al,es:[di]             ; read and write VGA
@@:
        sub     di,SCREEN_CBSCAN        ; up one line

        loop    fg_box_loop_1          ; loop
        pop     si                     ; rest font pointer

;/*
;** Set bit mask = char mask second part (unless no second run exists)
;*/

        mov     ax,wMask               ; get mask
        test    al,0ffh                ; any second mask ?
        jz      fg_box_end             ; skip if not

        mov     ah,al                  ; mask to ah
        mov     al,GRAF_BIT_MASK       ; graphics bit mask
        out     dx,ax                  ; output to h/w

;/*
;** Do first 2 slices (including underscore)
;*/

        mov     di,wByteAddr           ; get byte address
        inc     di                     ; bump di

        lodsb                          ; read font
        or      al,bUnderscoreMask     ; or underscore
        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes
        xchg    al,es:[di]             ; read and write VGA
@@:
        sub     di,SCREEN_CBSCAN        ; up one line

        lodsb                          ; read font
        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes
        xchg    al,es:[di]             ; read and write VGA
@@:
        sub     di,SCREEN_CBSCAN       ; up one line

;/*
;** Loop For remaining slices to do foreground second part (2x unrolled)
;*/

        mov     cx,wCellHeight         ; loop count = cell height
        sub     cx,2                   ; already done 2
        shr     cx,1                   ; divide cx by 2
fg_box_loop_2:

        lodsb                          ; read font
        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes
        xchg    al,es:[di]             ; read and write vga
@@:
        sub     di,SCREEN_CBSCAN        ; up one line

        lodsb                          ; read font
        test    al,0ffh                ; zero slice ?
        jz      @f                     ; skip if yes
        xchg    al,es:[di]             ; read and write VGA
@@:
        sub     di,SCREEN_CBSCAN       ; up one line
        loop    fg_box_loop_2          ; loop

fg_box_end:

;/*
;** Bump horizontal address by cell width
;*/

        mov     ax,wCellWidth          ; get cell width
        add     wX,ax                  ; Bump X value

;/*
;** test for horizontal clip (?) - move to next row if yes
;*/

        cmp     bHorizontalClipMask,0  ; Is this text clipped?
        jne     next_row_j               ; Yes - jump to force new row

;/*
;** Check character count, return if zero else decrement
;*/

        dec     word ptr wCharCount    ; dec and test character count
        jz      draw_nw_exit           ; zero  - exit

;/*
;** Jump to top of draw loop
;*/

        jmp     draw_n_wide_noclip_next ; Jump back to fetch next code

next_row_j:
        jmp     next_row               ; return to fetch next code

;/*
;** Exit n wide unclipped code
;*/

draw_nw_exit:
        jmp     process_pseudo_code    ; else return to fetch next code

;/*
;** Draw 8 x 5 wide characters in a group (very fast) without clipping           ;
;*/

fast_5or6_wide:

;/*
;** Fetch pseudo-code from stack and calculate font address for second char
;*/

        pop     si                     ; get glyph index
        sub     si,wFirstGlyph         ; Subtract the lowest defined glyph
        cmp     si,wGlyphSpan          ; Check against number of defined glyphs
        jb      @f                     ; skip if in range
        mov     si,wDefaultGlyph       ; use default value
@@:
        imul    si,si,6                ; glyph entries are 6 bytes
        mov     si,fsCharOffset[si].fe_dBits.lo ; DS:SI -> character data
        mov     wFontPtr1,si           ; store in stack frame

;/*
;** Fetch pseudo-code from stack and calculate font address for third char
;*/

        pop     si                     ; get glyph index
        sub     si,wFirstGlyph         ; Subtract the lowest defined glyph
        cmp     si,wGlyphSpan          ; Check against number of defined glyphs
        jb      @f                     ; skip if in range
        mov     si,wDefaultGlyph       ; use default value
@@:
        imul    si,si,6                ; glyph entries are 6 bytes
        mov     si,fsCharOffset[si].fe_dBits.lo ; DS:SI -> character data
        mov     wFontPtr2,si           ; store in stack frame

;/*
;** Fetch pseudo-code from stack and calculate font address for fourth char
;*/

        pop     si                     ; get glyph index
        sub     si,wFirstGlyph         ; Subtract the lowest defined glyph
        cmp     si,wGlyphSpan          ; Check against number of defined glyphs
        jb      @f                     ; skip if in range
        mov     si,wDefaultGlyph       ; use default value
@@:
        imul    si,si,6                ; glyph entries are 6 bytes
        mov     si,fsCharOffset[si].fe_dBits.lo ; DS:SI -> character data
        mov     wFontPtr3,si           ; store in stack frame

;/*
;** Fetch pseudo-code from stack and calculate font address for fifth char
;*/

        pop     si                     ; get glyph index
        sub     si,wFirstGlyph         ; Subtract the lowest defined glyph
        cmp     si,wGlyphSpan          ; Check against number of defined glyphs
        jb      @f                     ; skip if in range
        mov     si,wDefaultGlyph       ; use default value
@@:
        imul    si,si,6                ; glyph entries are 6 bytes
        mov     si,fsCharOffset[si].fe_dBits.lo ; DS:SI -> character data
        mov     wFontPtr4,si           ; store in stack frame

;/*
;** Fetch pseudo-code from stack and calculate font address for sixth char
;*/

        pop     si                     ; get glyph index
        sub     si,wFirstGlyph         ; Subtract the lowest defined glyph
        cmp     si,wGlyphSpan          ; Check against number of defined glyphs
        jb      @f                     ; skip if in range
        mov     si,wDefaultGlyph       ; use default value
@@:
        imul    si,si,6                ; glyph entries are 6 bytes
        mov     si,fsCharOffset[si].fe_dBits.lo ; DS:SI -> character data
        mov     wFontPtr5,si           ; store in stack frame

;/*
;** Fetch pseudo-code from stack and calculate font address for seventh char
;*/

        pop     si                     ; get glyph index
        sub     si,wFirstGlyph         ; Subtract the lowest defined glyph
        cmp     si,wGlyphSpan          ; Check against number of defined glyphs
        jb      @f                     ; skip if in range
        mov     si,wDefaultGlyph       ; use default value
@@:
        imul    si,si,6                ; glyph entries are 6 bytes
        mov     si,fsCharOffset[si].fe_dBits.lo ; DS:SI -> character data
        mov     wFontPtr6,si           ; store in stack frame

;/*
;** Fetch pseudo-code from stack and calculate font address for eighth char
;*/

        pop     si                     ; get glyph index
        sub     si,wFirstGlyph         ; Subtract the lowest defined glyph
        cmp     si,wGlyphSpan          ; Check against number of defined glyphs
        jb      @f                     ; skip if in range
        mov     si,wDefaultGlyph       ; use default value
@@:
        imul    si,si,6                ; glyph entries are 6 bytes
        mov     si,fsCharOffset[si].fe_dBits.lo ; DS:SI -> character data
        mov     wFontPtr7,si           ; store in stack frame

;/*
;** Skip setup if 5 wide hardware setup active
;*/

        cmp     word ptr wModeSet,5    ; 5 wide mode set ?
        je      @f                     ; go if yes
        mov     word ptr wModeSet,5    ; remember 5 wide modeset

;/*
;** Enable all planes for update in VGA sequencer
;*/

        mov     dx,EGA_BASE+SEQ_ADDR   ; Sequncer regs
        mov     al,SEQ_MAP_MASK        ; Map mask register
        mov     ah,MM_ALL              ; Data (all on)
        out     dx,ax                  ; do i/o

;/*
;** Set write mode in graphics controller
;*/

        mov     dx,EGA_BASE+GRAF_ADDR  ; graphics controller regs
        mov     al,GRAF_MODE           ; graphics mode register
        mov     ah,M_PROC_WRITE OR M_DATA_READ ; Data to be written
        out     dx,ax                  ; do i/o

;/*
;** Set data rotate=0 and no logic function
;*/

        mov     al,GRAF_DATA_ROT       ; Graphics data rotate register
        mov     ah,DR_SET              ; Set mode with no rotate
        out     dx,ax                  ; do i/o

;/*
;** Set set/reset reg = Background col
;*/

        mov     ax,wColor              ; get colour
        mov     al,GRAF_SET_RESET      ; Graphics set/reset register
        out     dx,ax                  ; do i/o

;/*
;** Enable s/r for all planes
;*/

        mov     al,GRAF_ENAB_SR        ; Graphics set/reset enable register
        mov     ah,MM_ALL              ; all on
        out     dx,ax                  ; do i/o

;/*
;** Enable all bits in bit mask
;*/

        mov     al,GRAF_BIT_MASK       ; Graphics bit mask
        mov     ah,0FFh                ; all on
        out     dx,ax                  ; do i/o

;/*
;** Write and read VGA to get bgcol into latches
;*/

        mov     tonys_bar_n_grill,ah   ; This gets our background color
        mov     ah,tonys_bar_n_grill   ; into the latches.

;/*
;** Change set/reset reg to all zeroes
;*/

        xor     ax,ax                  ; Clear ax
        mov     al,GRAF_SET_RESET      ; Graphics set/reset register
        out     dx,ax                  ; do i/o

;/*
;** Set enable s/r to difference between fg and bg cols
;*/

        mov     bx,wColor              ; get colors
        mov     ah,bh                  ; The Set/Reset enable register now
        xor     ah,bl                  ; flags where the foreground/background
        not     ah                     ; colors are the same.
        and     ah,0Fh                 ; leave lo nibble
        mov     al,GRAF_ENAB_SR        ; Graphics set/reset enable regsiter
        out     dx,ax                  ; do i/o

;/*
;** Set data rotate to XOR with zero rotate
;*/

        mov     al,GRAF_DATA_ROT       ; Color differences will be xor'd
        mov     ah,DR_XOR              ; with the latches.
        out     dx,ax                  ; do i/o

;/*
;** Continue with normal draw - calculate update address in VGA
;*/

@@:
        mov     bx,wY                  ; get y
        mov     ax,SCREEN_CBSCAN        ; screen pitch in bytes
        mul     bx                     ; result to ax
        mov     bx,wX                  ; get x
        shr     bx,3                   ; byte address
        add     ax,bx                  ; add to line address
        mov     wByteAddr,ax           ; and save on stack

;/*
;** Initialize loop to write all slices of 8 characters
;*/

        mov     bx,wCellHeight         ; Load bx with char height
        mov     di,wByteAddr           ; get byte address for drawing
        mov     dl,bUnderscoreMask     ; get underscore
        cld                            ; ensure count upwards

        cmp     word ptr wCellWidth,6  ; cell width = 6 ?
        je      charglp6               ; go 6 pel wide version

;/*
;** Fetch slice from characters 0 and 1
;*/

charglp5:
        mov     si,wFontPtr0           ; get font pointer 0
        mov     ah,[si+bx-1]           ; get slice into al
        mov     cl,5                   ; shift count
        rol     ax,cl                  ; shift data up
        mov     si,wFontPtr1           ; get font pointer 1
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,3                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 0 and move remaining bits down
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA
        mov     cl,2                   ; shift count
        rol     ax,cl                  ; get remaining 2 bits

;/*
;** Fetch slice from characters 2 and 3
;*/

        mov     si,wFontPtr2           ; get font pointer 2
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,5                   ; shift count
        rol     ax,cl                  ; shift data up
        mov     si,wFontPtr3           ; get font pointe r 3
        mov     ah,[si+bx-1]           ; get slice
        rol     ax,1                   ; shift data up

;/*
;** Store byte 1 and move remaining bits down
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA
        mov     cl,4                   ; shift count
        rol     ax,cl                  ; get remaining 4 bits

;/*
;** Fetch slice from character 4
;*/

        mov     si,wFontPtr4           ; get font pointer 4
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,4                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 2 and move remaining bit down
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA
        rol     ax,1                   ; get remaining bit

;/*
;** Fetch slice from characters 5 and 6
;*/

        mov     si,wFontPtr5           ; get font pointer 5
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,5                   ; shift count
        rol     ax,cl                  ; shift data up
        mov     si,wFontPtr6           ; get font pointer 6
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,2                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 3 and move remaining bits down
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA
        mov     cl,3                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Fetch slice from character 7
;*/

        mov     si,wFontPtr7           ; get font pointer 7
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,5                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 4
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA

;/*
;** End of main loop - bump pointers and test for end
;*/

        xor     dx,dx                  ; clear underscore mask
        sub     di,SCREEN_CBSCAN+5     ; decr to start of next line
        dec     bx                     ; decr character slice offset
        jnz     charglp5               ; and loop for all slices

;/*
;** Update all pointers before returing to get more to draw
;*/

        add     word ptr wX,8*5        ; bump X value for 8 characters
        std                            ; will do characters upwards
        jmp     process_pseudo_code    ; and return to fetch next code

charglp6:
        mov     si,wFontPtr0           ; get font pointer 0
        mov     ah,[si+bx-1]           ; get slice into al
        mov     cl,6                   ; shift count
        rol     ax,cl                  ; shift data up
        mov     si,wFontPtr1           ; get font pointer 1
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,2                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 0 and move remaining bits down
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA
        mov     cl,4                   ; shift count
        rol     ax,cl                  ; get remaining 4 bits

;/*
;** Fetch slice from characters 2
;*/

        mov     si,wFontPtr2           ; get font pointer 2
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,4                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 1 and move remaining bits down
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA
        mov     cl,2                   ; shift count
        rol     ax,cl                  ; get remaining 2 bits

;/*
;** Fetch slice from character 3
;*/

        mov     si,wFontPtr3           ; get font pointer 3
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,6                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 2
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA

;/*
;** Fetch slice from characters 4 and 5
;*/

        mov     si,wFontPtr4           ; get font pointer 4
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,6                   ; shift count
        rol     ax,cl                  ; shift data up
        mov     si,wFontPtr5           ; get font pointer 5
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,2                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 3 and move remaining bits down
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA
        mov     cl,4                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Fetch slice from character 6
;*/

        mov     si,wFontPtr6           ; get font pointer 6
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,4                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 4
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA
        mov     cl,2                   ; shift count
        rol     ax,cl                  ; get remaining 2 bits

;/*
;** Fetch slice from character 7
;*/

        mov     si,wFontPtr7           ; get font pointer 3
        mov     ah,[si+bx-1]           ; get slice
        mov     cl,6                   ; shift count
        rol     ax,cl                  ; shift data up

;/*
;** Store byte 5
;*/

        or      al,dl                  ; turn on underscore mask
        stosb                          ; store byte to VGA

;/*
;** End of main loop - bump pointers and test for end
;*/

        xor     dx,dx                  ; clear underscore mask
        sub     di,SCREEN_CBSCAN+6      ; decr to start of next line
        dec     bx                     ; decr character slice offset
        jnz     charglp6               ; and loop for all slices

;/*
;** Update all pointers before returing to get more to draw
;*/

        add     word ptr wX,8*6        ; bump X value for 8 characters
        std                            ; will do characters upwards
        jmp     process_pseudo_code    ; and return to fetch next code

DrawText    endp

page

;/***************************************************************************
;*
;* FUNCTION NAME = InvertScreenRect 
;*
;* DESCRIPTION   = This routine inverts a rectangle of pixels on the screen.
;*                 It's used to toggle the text cursor.                     
;*                                                                          
;*                 Registers Destroyed:                                                       
;*                       AX, DX                                                               
;*                 Registers Preserved:                                                       
;*                       None                                                                 
;*                                                                          
;* INPUT         = BX  =  Top of rectangle    
;*                 CX  =  Left of rectangle                                                   
;*                 DI  =  Bottom of rectangle                                                 
;*                 SI  =  Right of rectangle                                                  
;*                                                                          
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

InvertScreenRect    proc    near

wX1     equ     [bp+2]
wX2     equ     [bp+4]
wY1     equ     [bp+6]
wY2     equ     [bp+8]

;/*
;** Make BP temp frame on stack and save registers
;*/

        sub     sp,16                  ; create temp frame on stack
        push    bp                     ; save bp
        mov     bp,sp                  ; copy sp to bp
        pusha                          ; save all regs

;/*
;** Save parameters on stack
;*/

        mov     wX1,cx                 ; save left
        mov     wX2,si                 ; save right
        mov     wY1,bx                 ; save top
        mov     wY2,di                 ; save bottom

;/*
;** Setup VGA I/O ready for operation
;*/

        mov     dx,EGA_BASE+SEQ_ADDR   ; Sequencer regs
        mov     al,SEQ_MAP_MASK        ; Map mask register
        mov     ah,MM_ALL              ; Data (all on)
        out     dx,ax                  ; do i/o

        mov     dx,EGA_BASE+GRAF_ADDR  ; graphics controller regs
        mov     al,GRAF_MODE           ; Graphics mode
        mov     ah,M_PROC_WRITE OR M_DATA_READ ; set mode
        out     dx,ax                  ; do i/o

        mov     al,GRAF_SET_RESET      ; Graphics set reset reg
        mov     ah,MM_ALL              ; all bits
        out     dx,ax                  ; do i/o

        mov     al,GRAF_ENAB_SR        ; Graphics snable s/r reg
        mov     ah,0                   ; clear data
        out     dx,ax                  ; do i/o

        mov     al,GRAF_DATA_ROT       ; Graphics rotate and function
        mov     ah,DR_XOR              ; exclusive OR
        out     dx,ax                  ; do i/o

;/*
;** Calculate Byte address of box to be drawn
;*/

        mov     bx,wY1                 ; get y
        mov     ax,SCREEN_CBSCAN       ; screen pitch in bytes
        mul     bx                     ; retult to ax
        mov     bx,wX1                 ; get x
        shr     bx,3                   ; byte address
        add     ax,bx                  ; add to line address
        mov     di,ax                  ; and save in di

;/*
;** Calculate initial bit mask
;*/

        mov     ax,wX1                 ; get x1
        and     ax,0007h               ; strip all but bit offset
        mov     bx,08000h              ; mask for zero offset
mask_loop:
        cmp     ax,0                   ; in right pos ?
        jz      done_mask              ; go if yes
        shr     bh,1                   ; shift mask down
        dec     ax                     ; decrment ax
        jmp     mask_loop              ; and loop
done_mask:
        mov     dx,EGA_BASE+GRAF_ADDR  ; graphics controller address reg
        mov     al,GRAF_BIT_MASK       ; graphics bit mask
        out     dx,al                  ; select bit mask register only
        mov     dx,EGA_BASE+GRAF_ADDR+1  ; graphics controller data reg

;/*
;** loop to do all lines
;*/

        mov     cx,wY2                 ; get y2
        sub     cx,wY1                 ; y2-y1
inv_rect_line_loop:
        push    di                     ; save address
        push    bx                     ; save mask and ff

;/*
;** loop to do all pixels along line
;*/

        push    cx                     ; save outer loop count
        mov     cx,wX2                 ; get x2
        sub     cx,wX1                 ; x2-x1
        mov     bl,0                   ; clear bh
inv_rect_pixel_loop:

;/*
;** Accumulate pixel mask
;*/

        or      bl,bh                  ; or into mask

;/*
;** Shift mask and adjust address
;*/

        ror     bh,1                   ; rotate mask
        cmp     bh,080h                ; has it rolled over ?
        jne     not_new_byte           ; go if not

        mov     al,bl                  ; mask to ah
        out     dx,al                  ; do i/o to bit mask reg

        mov     bl,0                   ; clear mask
        mov     al,0ffh                ; data
        xchg    al,es:[di]             ; read/write VGA
        inc     di                     ; else bump address
not_new_byte:

;/*
;** End loop to do all pixels outputting mask if necessary
;*/

        loop    inv_rect_pixel_loop    ; and loop for all lines
        cmp     bl,0                   ; any remaining mask ?
        jz      no_bits_in_mask

        mov     al,bl                  ; mask to al
        out     dx,al                  ; do i/o to bit mask reg

        mov     bl,0                   ; clear mask
        mov     al,0ffh                ; data
        xchg    al,es:[di]             ; read/write VGA
no_bits_in_mask:
        pop     cx                     ; rest outer loop count

;/*
;** End loop to do all lines
;*/

        pop     bx                     ; rest mask
        pop     di                     ; rest address
        add     di,SCREEN_CBSCAN       ; next line
        loop    inv_rect_line_loop     ; and loop for all lines

;/*
;** Restore stack and return to caller
;*/

        popa                           ; rest all regs
        pop     bp                     ; rest bp
        add     sp,16                  ; lose temporary frame

        ret

InvertScreenRect    endp

endif;  VGA

        sEnd    VioSeg

        END
