;-----------------------------------------------------------------------
; MODULE XTEXT
;
; Point functions all MODE X 256 Color resolutions
;
; Compile with Tasm.
; C callable.
;
;
; ****** XLIB - Mode X graphics library                ****************
; ******                                               ****************
; ****** Written By Themie Gouthas                     ****************
;
; egg@dstos3.dsto.gov.au
; teg@bart.dsto.gov.au
;-----------------------------------------------------------------------


include xlib.inc
include xtext.inc

.data

_FontDriverActive db 0


_CharHeight   db         0
_CharWidth    db         0
_FontPtr      dw  2 dup (0)
_FirstChar    db         0

_UserFontPtr   dw  2 dup (0)
_UserChHeight  db         0
_UserChWidth   db         0
_UserFirstCh   db         0


F8x8Ptr       dw  2 dup (0)
F8x14Ptr      dw  2 dup (0)

; This is a look up table for the mirror image of a byte eg
; a byte with the value 11001010 has a corresponding byte in the table
; 01010011. This is necessary as the VGA rom font bits are the reverse
; order of what we need for the Mode X. If you know a better-faster way
; TELL ME!

MirrorTable  label byte
	db   0,128, 64,192, 32,160, 96,224, 16,144, 80,208, 48,176,112,240
	db   8,136, 72,200, 40,168,104,232, 24,152, 88,216, 56,184,120,248
	db   4,132, 68,196, 36,164,100,228, 20,148, 84,212, 52,180,116,244
	db  12,140, 76,204, 44,172,108,236, 28,156, 92,220, 60,188,124,252
	db   2,130, 66,194, 34,162, 98,226, 18,146, 82,210, 50,178,114,242
	db  10,138, 74,202, 42,170,106,234, 26,154, 90,218, 58,186,122,250
	db   6,134, 70,198, 38,166,102,230, 22,150, 86,214, 54,182,118,246
	db  14,142, 78,206, 46,174,110,238, 30,158, 94,222, 62,190,126,254
	db   1,129, 65,193, 33,161, 97,225, 17,145, 81,209, 49,177,113,241
	db   9,137, 73,201, 41,169,105,233, 25,153, 89,217, 57,185,121,249
	db   5,133, 69,197, 37,165,101,229, 21,149, 85,213, 53,181,117,245
	db  13,141, 77,205, 45,173,109,237, 29,157, 93,221, 61,189,125,253
	db   3,131, 67,195, 35,163, 99,227, 19,147, 83,211, 51,179,115,243
	db  11,139, 75,203, 43,171,107,235, 27,155, 91,219, 59,187,123,251
	db   7,135, 71,199, 39,167,103,231, 23,151, 87,215, 55,183,119,247
	db  15,143, 79,207, 47,175,111,239, 31,159, 95,223, 63,191,127,255

MirrorTableOffs dw         ?
.code

;----------------------------------------------------------------------
; x_text_init    - Initializes the Mode X text driver and sets the
;                  default font (VGA ROM 8x8)
;
; C caller:
;
;  x_text_init()
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_text_init proc
  push bp

  mov  [_FontDriverActive],TRUE
  mov  ax,1130h                   ; AH = BIOS generator function
				  ; AL = BIOS get font pointer subfunction
  push ax                         ; Save Video interrupt function parameters
  mov  bh,3                       ; Select 8x8 VGA ROM font
  int  10h                        ; Call BIOS video interrupt
  mov  word ptr [F8x8Ptr],bp      ; Save 8x8 Font address in FontPtr table
  mov  word ptr [F8x8Ptr+2],es

  mov  word ptr [_FontPtr],bp     ; Default font = 8x8 ROM font
  mov  word ptr [_FontPtr+2],es

  pop  ax                         ; Recall Video interrupt function parameters
  mov  bh,2                       ; Select 8x14 VGA ROM font
  int  10h                        ; Call BIOS video interrupt
  mov  word ptr [F8x14Ptr],bp     ; Save 8x14 Font address in FontPtr table
  mov  word ptr [F8x14Ptr+2],es


  mov  al,8
  mov  [_CharHeight],al            ; Set the font character heights
  mov  [_CharWidth] ,al            ; Set the font character widths

  mov  dx,offset MirrorTable       ; Initialize mirror table offset
  mov  [MirrorTableOffs],dx

  pop  bp
  ret
_x_text_init endp


;----------------------------------------------------------------------
; x_set_font - Mode X Set current font for text drawing
;
; C caller:
;
;  x_set_font(int FontID)
;
; PARAMETERS  FontID    0 = VGA ROM 8x8
;                       1 = VGA ROM 8x14
;                       2 = User defined bitmapped font
;
;
; WARNING: A user font must be registered before setting FontID 2
;
; Written by Themie Gouthas
;----------------------------------------------------------------------

_x_set_font proc
  ARG FontID:word
  push bp
  mov  bp,sp

  xor  dx,dx             ; Clear DX - Mirror table offset (0 for non ROM fonts)
  mov  cx,FontID
  cmp  cx,2

  jne  @@not_userfont     ; Do we have a user font
  mov  ax,[_UserFontPtr]   ; Yes - Activate it
  mov  [_FontPtr],ax

  mov  ax,[_UserFontPtr+2]
  mov  [_FontPtr+2],ax

  mov  al,[_UserChHeight]
  mov  [_CharHeight],al   ; Set the font character heights

  mov  al,[_UserChWidth]
  mov  [_CharWidth],al    ; Set the font character heights

  mov  al,[_UserFirstCh]
  mov  [_FirstChar],al
  jmp  short @@done

@@not_userfont:              ; We have a ROM font

  mov  dx,offset MirrorTable
  mov  [_CharWidth],8        ; Set the font character widths
  mov  [_FirstChar],0        ; Character sets start at ascii 0
  cmp  cx,1                  ; Do we have an 8x14 ROM font
  jne  @@not_8x14font        ; No, we have 8x8 - jump

  mov  ax,[F8x14Ptr]         ; Yes Activate it
  mov  [_FontPtr],ax

  mov  ax,[F8x14Ptr+2]
  mov  [_FontPtr+2],ax

  mov  [_CharHeight],14    ; Set the font character heights
  jmp  short @@done

@@not_8x14font:
  mov  ax,[F8x8Ptr]        ; Activate the 8x8 ROM Font
  mov  [_FontPtr],ax

  mov  ax,[F8x8Ptr+2]
  mov  [_FontPtr+2],ax

  mov  [_CharHeight],8     ; Set the font character heights

@@done:
  mov  [MirrorTableOffs],dx

  pop  bp
  ret
_x_set_font endp


;----------------------------------------------------------------------
; x_register_userfont - Mode X register user font
;
; C caller:
;
;  x_register_userfont(void far *user_font)
;
;
; NOTES  registering a user font deregisters the previous user font
;        User fonts may be at most 8 pixels wide
;
;
; USER FONT STRUCTURE
;
;  Word:  ascii code of first char in font
;  Byte:  Height of chars in font
;  Byte:  Width of chars in font
;  n*h*Byte: the font data where n = number of chars and h = height
;	  of chars
;
; WARNING: The onus is on the program to ensure that all characters
;          drawn whilst this font is active, are within the range of
;          characters defined.
;
;
; UPDATE: Variable width fonts are now available (up to 8 pixels max)
;  If the Width byte in the font header is 0 then it is assumed that
;  the font is variable width. For variable width fonts each characters
;  data is followed by one byte representing the characters pixel width.
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_register_userfont proc
  ARG  FontToRegister:dword
  push bp
  mov  bp,sp
  push si

  mov  ax,word ptr [FontToRegister]
  mov  bx,word ptr [FontToRegister+2]
  add  ax,4
  mov  [_UserFontPtr],ax
  mov  [_UserFontPtr+2],bx

  push ds
  lds  si,[FontToRegister]
  lodsw
  mov  bx,ax
  lodsw
  pop  ds

  mov  [_UserChHeight],al
  mov  [_UserChWidth],ah
  mov  [_UserFirstCh],bl
  pop  si
  pop  bp
  ret
_x_register_userfont endp


_x_get_char_width  proc
  ARG  Chr:byte
  push bp
  mov  bp,sp

  xor  ah,ah
  mov  al,[_CharWidth]
  or   al,al
  jz   @@NotFixed
  pop  bp
  ret

@@NotFixed:
  push si
  mov  al,[_CharHeight]
  mov  bx,ax
  inc  al
  mov  dl,[Chr]                   ; User fonts may have incomplete charsets
  sub  dl,[_FirstChar]            ;  this compensates for fonts not starting at
				  ;  ascii value 0
  mul  dl                         ; Mult AX by character to draw giving offset
				  ; of first character byte in font table
  add  ax,bx
  les  si,dword ptr [_FontPtr]
  add  si,ax
  xor  ah,ah
  mov  al,es:[si]
  pop  si
  pop  bp
  ret
_x_get_char_width  endp


;----------------------------------------------------------------------
; x_char_put - Mode X Draw a text character at the specified location
;
;
; C caller:
;
;  x_char_put(char ch, int x, int y, unsigned ScrnOffs, unsigned Color)
;
; PARAMETERS  ch        char to draw
;             x,y       screen coords at which to draw ch
;             ScrnOffs  Starting offset of page on whih to draw
;	      Color     Color of the text
;
; NOTES:  Uses the current font settings. See SetFont, InitTextDriver,
;         RegisterUserFont
;
; WARNING: InitTextDriver must be called before using this function
;
;
; Written by Themie Gouthas
;----------------------------------------------------------------------
_x_char_put  proc
  ARG  Chr:byte,X:word,Y:word,ScrnOffs:word,Color:word
  LOCAL ScreenInc:word,Hold:word=LocalStk
  push bp
  mov  bp,sp
  sub  sp,LocalStk
  push si
  push di
  push ds

  cld
  mov  ax,[_ScrnLogicalByteWidth] ; AX = Virtual screen width
  mov  bx,ax                      ; copy Virt screen width and decrement
  sub  bx,3                       ; by the max number of bytes (whole or part)
				  ; that a character row may occupy on the screen
  mov  [ScreenInc],bx             ; Save it to the local stack var. SceenInc
  mul  [Y]                        ; Find the starting dest. screen address of
  mov  di,[X]                     ;  the character to draw
  mov  cx,di
  shr  di,2
  add  di,ax
  add  di,[ScrnOffs]              ; Dont forget to compensate for page

  mov  ax,SCREEN_SEG              ; ES:DI -> first screen dest. byte of char
  mov  es,ax

  and  cx,3                       ; CH = 0, CL = Plane of first pixel

  mov  bx,[MirrorTableOffs]       ; set BX to offset of mirror table for XLAT
  mov  al,[_CharHeight]           ; AL = Character height, AH = 0
  xor  ah,ah
  mov  ch,al                      ; CH = Character height

  cmp  [_CharWidth],0
  jne  @@NoWidthByte
  inc  al
@@NoWidthByte:

  mov  dl,[Chr]                   ; User fonts may have incomplete charsets
  sub  dl,[_FirstChar]            ;  this compensates for fonts not starting at
				  ;  ascii value 0
  mul  dl                         ; Mult AX by character to draw giving offset
				  ; of first character byte in font table

  lds  si,dword ptr [_FontPtr]    ; DS:SI -> beggining of required font
  add  si,ax                      ; DS:SI -> first byte of req. char

  mov  dx,SC_INDEX                ; Prepare for VGA out's

@@MainLoop:

  lodsb               ; load character byte into AL
  or   al,al
  jz   @@NoCharPixels ; Dont bother if no pixels to draw

  or   bx,bx          ; if BX=0 -> User font, so no need to mirror data
  jz   @@DontMirror
  push ds
  mov  dx,@data       ; Set DS to the Mirror lookup table's segment
  mov  ds,dx          ; - BX should already contain the offset addr of table
  xlatb               ; AL is now replaced by the corresponding table entry
  pop  ds             ; Restore previous data segment
  mov  dx,SC_INDEX    ; Restore DX

@@DontMirror:
  xor  ah,ah          ; shift the byte for the dest plane and save it
  shl  ax,cl
  mov  [Hold],ax

  mov  ah,al                 ; output high nibble of first byte of shifted char
  and  ah,0fh                ; 4 pixels at a time !
  jnz  @@p1                  ; if nibble has pixels, draw them
  inc  di                    ;  otherwise go to next nibble
  jmp  @@SecondNibble

@@p1:
  mov  al,MAP_MASK
  out  dx,ax
  mov  al,byte ptr [Color]
  stosb

@@SecondNibble:
			     ; output low nibble of first byte of shifted char
  mov  ax,[Hold]
  shl  ax,4
  and  ah,0fh
  jnz  @@p2
  inc  di
  jmp  @@ThirdNibble

@@p2:
  mov  al,MAP_MASK
  out  dx,ax
  mov  al,byte ptr [Color]
  stosb

@@ThirdNibble:
  mov  ax,[Hold]             ; output high nibble of last byte of shifted char
  and  ah,0fh
  jnz  @@p3
  inc  di
  jmp  short  @@NextCharRow

@@p3:
  mov  al,MAP_MASK           ; completing the drawing of one character row
  out  dx,ax
  mov  al,byte ptr [Color]
  stosb

@@NextCharRow:
  add  di,[ScreenInc]        ; Now move to the next screen row and do the same
  dec  ch                    ; any remaining character bytes
  jnz  @@MainLoop

@@done:
  pop  es
  mov  ah,0
  mov  al,es:[_CharWidth]     ; return the character width (for string fuctions
  or   al,al
  jnz  @@FixedSpacing         ;  using this character drawing function).
  lodsb
@@FixedSpacing:

  mov  bx,es
  mov  ds,bx

  pop  di
  pop  si
  mov  sp,bp
  pop  bp
  ret

@@NoCharPixels:
  add  di,3
  add  di,[ScreenInc]        ; Now move to the next screen row and do the same
  dec  ch                    ; any remaining character bytes
  jnz  @@MainLoop
  jmp  short @@done

_x_char_put endp


end

