TOOL_BOX  EQU 1
true      equ 1
false     equ 0
BLACK     EQU 0
BLUE      EQU 1
GREEN     EQU 2
CYAN      EQU 3
RED       EQU 4
MAGENTA   EQU 5
BROWN     EQU 6
GRAY      EQU 7
DARKGREY  EQU 8
HIBLUE    EQU 9
HIGREEN   EQU 10
HICYAN    EQU 11
HIRED     EQU 12
HIMAGENTA EQU 13
YELLOW    EQU 14
WHITE     EQU 15
BLINK     EQU 128
RO        EQU 0
WO        EQU 1
RW        EQU 2
COLORSCR  EQU 0b800h
MONOSCR   EQU 0b000h
ADLIBPORT EQU 0388h
ADLIBDATA EQU 0389h

include \nxdos\src\globchar.inc

; ===========================================================================
; === TOOLBOX CODE                                                        ===
; ===========================================================================

toolbox:
         jmp  short tstart
_compile db ' '
IF (TOOL_BOX)
         db 'Assembler Toolbox ',0ffh
         db ' Copyright (c) 95-2009 by Christopher Evans (teknopup@lycos.com)',0ffh
ENDIF
         db 0
tstart:

_initialize macro init_seg
        push ax
        mov  ax, init_seg
        mov  ds, ax           ;SET DS-REGISTER TO POINT TO LOCAL DATA SEGMENT
        mov  es, ax
        pop  ax
        endm

_toolbox macro
        pushf                 ;SAVE FLAGS
        _initialize TOOL_DATA
        endm

_rtoolbx macro 
        popf    ; RESTORE FLAGS & REGISTERS (ASSEMBLER GENERATES
                ; INSTRUCTIONS TO RESTORE REGISTERS - USES)
        endm

_cstods macro
        ; set our data segment to the kernel
        push cs
        pop  ax
        ;mov   ax, cs
        ;push  ds
        mov  ds, ax
        endm

_cstoes macro
        push cs
        pop  ax
        mov  es, ax   
        endm

_setstack macro stackaddr, stacktop
        cli
        mov  ax, stackaddr
        mov  bp, stacktop   
        mov  ss, ax
        mov  sp, bp
        sti
        endm

breakpoint macro
        int 3
        endm

_setflags macro bit
        pushf
        pop ax
        mov bx, 1
        shl bx, bit
        or  ax, bx
        push ax
        popf
        endm

_clearflags macro bit
        pushf
        pop ax
        mov bx, 0
        shl bx, bit
        or  ax, bx
        push ax
        popf
        endm

_isflag macro bit
        pushf
        pop ax
        mov bx, 1
        shl bx, bit
        and ax, bx
        ; and programmer just do an OR on ax to see it if it is NZ
        endm

pushregs macro
     push ax
     push bx
     push cx
     push dx
     push bp
     push si
     push di
     push ds
     push es
;       push  es
;       push  ds
;       push  si
;       push  di
;       push  ax
;       push  bx
;       push  cx
;       push  dx    
         endm

popregs macro
     pop  es
     pop  ds
     pop  di
     pop  si
     pop  bp
     pop  dx
     pop  cx
     pop  bx
     pop  ax
;       pop   dx
;       pop   cx
;       pop   bx
;       pop   ax
;       pop   di
;       pop   si
;       pop   ds
;       pop   es
        endm

copy macro csource, cdestination
     mov   cdestination, csource
     endm

cpy  macro csrc, cdest
     mov   cdest, csrc
     endm

move macro msource, mdestination
     mov   mdestination, msource
     mov   msource, 0
     endm

_mov macro msrc, mdest
     mov   mdest, msrc
     mov   msrc, 0
     endm

clearregs macro
          xor ax, ax
          xor bx, bx
          xor cx, cx
          xor dx, dx
          endm

clearax MACRO
             xor ax, ax
        ENDM
clearbx MACRO
             xor bx, bx
        ENDM
clearcx MACRO
             xor cx, cx
        ENDM
cleardx MACRO
             xor dx, dx
        ENDM

_swap macro
          push bx
          mov  bx, ax
          mov  ah, al
          mov  al, bh
          pop bx
      endm

_cprintf macro _www, _fff
         mov   ax, _www
         call  putnum
         _echo '.'
         mov   ax, _fff 
         call  putnum
         endm

_debug macro num                                  ; display variable
       push    ax
       _spaces 1
       mov     ax, num
       call    putnum
       _spaces 1
       mov     ax, num
       call    putoct
       _echo   'Q'
       _spaces 1
       mov    ax, num
       call   put_hex
       _echo   'H'
       _spaces 1
       mov     ax, num
       call    putbin
       _echo   'b'
       pop     ax
        endm

_debugs macro                                      ; displays last stack item
       pop    ax
       push   ax
       call    putnum
       _spaces 1
       call    putoct
       _echo   'Q'
       _spaces 1
       call   put_hex
       _echo   'H'
       _spaces 1
       call    putbin
       _echo   'b'
        endm

ReadCMOS macro addr
        mov al,addr     ; 'addr' ranges from 00h to 3Fh.
        out 70h,al      ;  wakes up the port.
        jmp $+2         ;  a delay loop..
        in al,71h       ;  reads CMOS.
         endm

WriteCMOS macro addr
        mov al,addr     ; 'addr' ranges from 00h to 3Fh.
        out 70h,al      ;  wakes up the port.
        jmp $+2         ;  a delay loop..
        out 71h, value  ;  Writes 'value' from 00h to FFh.
                        ;    note that 'addr' 10h to 20h are checksummed.
        endm

reached macro _rline_
             _echo 'L'
             _echo 'i' 
             _echo 'n'
             _echo 'e'
             _echo ':'
             _echo ' '
             mov  ax, _rline_
             call putnum
             _newline
        endm

assert macro _aline_, _avalue1_, _lteqgt_, _avalue2_
local @@assert1, @@assert_equal, @@assert_below, @@assert_greater, @@assert_fail, @@assert_exit, @@Assertd
            jmp  short @@assert1
@@Assertd   db ??Filename,' ',0
@@assert1:
            push ax
            push bx
            clc
            mov  ax, _avalue1_
            mov  bx, _avalue2_
            mov  cx, _lteqgt_   ; -1 0 1
            cmp  ax, bx
            jb   short @@assert_below
            jg   short @@assert_greater
@@assert_equal:
            cmp  cx, 0
            jne  short @@assert_fail
            jmp  short @@assert_exit
@@assert_below:
            cmp  cx, -1
            jne  short @@assert_fail
            jmp  short @@assert_exit
@@assert_greater:
            cmp  cx, 1
            jne  short @@assert_fail
            jmp  short @@assert_exit
@@assert_fail:
            _print @@Assertd, 1
            reached _aline_
            stc  ; abort()         
@@assert_exit:
            pop  bx
            pop  ax
       endm

max proc far
    cmp  ax, bx
    jge  short _nmax
         mov ax, bx
_nmax:
    ret
max endp

_max macro max1, max2
    mov  ax, max1
    mov  bx, max2
    call max
    endm

; ===========================================================================
_if macro num, num2, seg1, ofs1
; Pass num1, num2, and if they equal the ofs1 is jmp'ed to else nothing. 
; ===========================================================================
        mov   ax, num
        cmp   ax, num2
        je    far seg1:ofs1
    endm

; ===========================================================================
_cpuid   macro
; ===========================================================================
           db      0fh     ; Hardcoded opcode for CPUID instruction
           db      0a2h
        endm

;opnd32  macro op_code, op_erand
;             db      66h     ; Force 32-bit operand size
;             IFNB <op_code>
;                  db      op_code
;                  IFNB <op_erand>
;                       dd      op_erand; 32-bit immediate value
;                  ENDIF
;            ENDIF
;        endm

_wait macro
      push ax
      call readkey
      pop  ax
      endm

_multi macro multiplicand, multiplier
       push multiplicand
       push multiplier
       call multi
       endm

_divide macro dnum, divnum
        push dnum
        push divnum
        xor  bx, bx
        call divide
        endm

_add    macro const, const2
        push  bx
        mov   bx, const2
        mov   ax, const
        add   ax, bx
        pop   bx
        endm

_sub    macro const, const2
        push  bx
        mov   bx, const2
        mov   ax, const
        neg   bx                  ; Yeah, I read the book
        add   ax, bx              ; All computer can do is add. 
        pop   bx
        endm

_modulo macro mnum, modnum
; Output: AX=remainder (uses AX, BX, and DX in processing) 
        push mnum
        push modnum
        mov  bx, 1
        call divide
        endm

_justify macro jn, jm, jb, jt
         push jn  ; number
         push jm  ; magnitude
         push jb  ; base 
         push jt  ; type  
         call justify
         endm

_echo macro _char
      push ax
      mov  al, _char
      call teletype
      pop  ax
      endm

_spaces macro _count
        push cx
        mov  cx, _count
        call spaces
        pop  cx
        endm

_newline macro
         _echo CR
         _echo LF
         endm

_beep macro
      _echo BEL    ; BEL
      endm

_tty    macro
        call telety
        endm

_readkey  macro
          xor  ax, ax
          call readkey
          endm

keypressed macro
          mov  ax, 0100h
          call readkey
           endm

kbhit  macro
          mov  ax, 0100h
          call readkey
       endm

oport   macro p_addr, p_data
        mov  al, p_data
        mov  dx, p_addr
        out  dx, al
        endm

outport macro p_addr
     mov  dx, p_addr
     out  dx, al
     endm

oportw  macro p_addrw, p_dataw
        mov  ax, p_dataw
        mov  dx, p_addrw
        out  dx, ax
        endm

outportw macro p_addrw
     mov  dx, p_addrw
     out  dx, ax
     endm

inport macro i_addr
     mov  dx, i_addr
     in   al, dx
     endm

inportw macro i_addrw
     mov  dx, i_addrw
     in   ax, dx
     endm

osname    macro
          mov ax, _OS
          mov bx, osnamesize
          xor dx, dx
          mul bx
          lea si, n0_NXDOS
          add si, ax
          call showz
          endm

_print  macro _profs, _prmacros
        pushregs
        lea     si, _profs
        mov     dl, 1  
        mov     bh, _prmacros 
        xor     cx, cx
        call    cs:print
        popregs
        endm

_output  macro _opofs, _oplen, _opmacros
        pushregs
        lea     si, _opofs
        mov     dl, 3
        mov     cx, _oplen
        mov     bh, _opmacros 
        call    cs:print      
        popregs
        endm

_display  macro _dwofs, _dwmacros
        pushregs
        lea     si, _dwofs
        mov     dl, 2  
        mov     bh, _dwmacros 
        call    cs:print      
        popregs
        endm

_write macro  _wrofs, _wrmacros
        pushregs
        lea     si, _wrofs
        mov     dl, 0  
        mov     bh, _wrmacros 
        call    cs:print      
        popregs
       endm

_textcolor macro tc1, tc2
        mov  ax, tc1
        call textcolor
        mov  ax, tc2
        call textbackground
           endm
$textattr macro
        mov  al, textattr
        endm

_dispword  macro _ofs
    mov ah, 0
    mov ax, word ptr _ofs
    push ax
    call putnum
    _echo ' '
    pop ax
    call put_hex
    _echo 'H'
    _newline
    endm

_dispbyte  macro _ofs
    mov ah, 0
    mov al, byte ptr _ofs
    push ax
    call putnum
    _echo ' '
    pop ax
    call put_hex
    _echo 'H'
    _newline
    endm

; ===========================================================================
for  proc far
; input : cx=count, es:bx address of routine
; ===========================================================================
forloop:
          push bx
          push es
          retf

          dec  cx
     jnz  forloop
     ret
for  endp

; ===========================================================================
SafeDOS proc far
; 10/23/1996 12:25pm
; ===========================================================================
    mov  ah, 34h ; ah:=$34
    int  21h     ; int $21
    ; es:bx contains a pointer to the byte
    mov  ax, es
    mov  ds, ax
    mov  si, bx
    xor  ax, ax
    mov  al, byte ptr ds:[si]
    test al, 0   ; "save-flag", if nonzero its safe to popup
    jne  SafeDOS_Not
         clc
         jmp short SafeDOSX
SafeDOS_Not:
    stc
SafeDOSX:
    ret
SafeDOS endp

; ===========================================================================
multi proc far
; Input : ax= Multiple bx=multiplier
; Output: AX=whole DX=fractionary
; NOTE  : Doesn't preserve OF & CF of the flags register!!
; Duh, they named uses uses because the app is saying I am USING these regs.
; ===========================================================================           
                push bx
                push dx
;                mov   ax, _mult
;                mov   bx, _multiplier
                xor   dx, dx
                mul   bx
                pop dx
                pop bx
                ret
multi endp

; ===========================================================================
divide proc far
; Input: AX = dividend BX = divider ()
;        CX = swap whole & remainder for modulo operation
; Output: AX=quotient (uses AX, BX, and DX in processing) 
; ===========================================================================           
                push   bx
                push   cx
                push   dx
;                mov   ax, _dividend
;                mov   bx, _divider
                xor   dx, dx
                div   bx
                or    cx, cx
                jz    no_modulo_mode
                      mov   bx, dx
                      mov   dx, ax
                      mov   ax, bx
       ;               xchg  ax, dx
no_modulo_mode:
                pop   dx
                pop   cx
                pop   bx
                ret
divide endp

; ===========================================================================
div32 proc far
; dx:ax / cx = =cx remainder
; ===========================================================================
      mov  bx,dx               
      xchg bx,ax               
      xor  dx,dx               
      div  cx                 ; divide high order first
      xchg bx,ax               
      div  cx                 ; remainder will be in dx
      mov  cx,dx              ;  save remainder
      mov  dx,bx              ; full 32-bit answer
      ret
div32 endp

; ===========================================================================
Float_Add  proc far
; Make it compute Pi to the 200th fractionary... but no this one. 
; Mine, makers, makers will keepers. register overflow, and linux belched.
; haha! sourceforge just about a bunch of thieves 'n pirates.
; Input :    AX = W1
;            BX = F1
;            CX = W2
;            DX = F2
; Output: AX:DX = Whole.Fraction
; ===========================================================================
PRECISION EQU 1000 
ACCURACY  EQU PRECISION -1 ; 999
        add  ax, cx
        ; add  bx, 100
        add  bx, dx
        cmp  bx, ACCURACY
        jb   short F_No_CY
             inc  ax
             sub  bx, PRECISION
F_No_CY:
        mov  dx, bx
        ret
Float_Add  endp

_Float_Add macro W1, F1, W2, F2
        mov  ax, W1
        mov  bx, F1
        mov  cx, W2
        mov  dx, F2
        call Float_Add
        endm

; ===========================================================================
is_netware proc far
; Output: AX = 1 and CF=NZ = NetWare installed 
; ===========================================================================
          mov  ax, 7a00h     ; Netware installation check
          int  2fh
          or   al, al
          ; al = FF is installed and ES:DI ptr -> far entry point for
          ; routines otherwise accessed through INT 21h
          jnz  _chk_nw_faraddr ; double check
          jmp  _netw_x
_chk_nw_faraddr:               ; verify ES:DI address
          push di
          pop  ax
          or   ax, ax
          jnz  _netw
          push es
          pop  ax
          or   ax, ax
          jnz  _netw
_no_netw: clc                  ; no network
          jmp  _netw_x
_netw:    mov  ax, 1
          stc 
_netw_x:  ret
is_netware endp

; ===========================================================================
is_desqview proc far
; Output: AX = Desqview version
; ===========================================================================
          mov  ax, 2b01h
          mov  cx, 4445h
          mov  dx, 5351h
          int  21h
          cmp  al, 0ffh
          je   _no_desq
          mov  ax, bx
          ; swap ax
          jmp  _desq_x
_no_desq: xor  ax, ax
_desq_x:  ret
is_desqview endp

; ===========================================================================
is_win    proc
; Output: AX = Version of Windows running, else 0. 
; ===========================================================================
          mov  ax, 1600h
          int  2fh
          cmp  al, 0
          je   _not_win
          cmp  al, 80h
          je   _chk_win386std
          cmp  al, 1
          je   _win386_2x
          cmp  al, 0ffh
          je   _win386_2x
          xchg ax, dx
          jmp  _winchkx
_chk_win386std:
          mov  ax, 4680h
          int  2fh
          cmp  ax, 0
          jz   _win386_301
          jmp  _oldxms
_win386_2x:
          mov  dx, 1102h
          jmp  _winchkx
_win386_301:
          mov  dx, 0103h
          jmp  _winchkx
_win386_311:
          mov  dx, 1103h
          jmp  _winchkx
_oldxms:  mov  dx, 0ffffh
          jmp  _winchkx
_not_win: xor  dx, dx
_winchkx: xchg ax, dx
          ret
is_win    endp

; ===========================================================================
detect_os proc far
; ===========================================================================
          mov  ax, 3000h
          int  21h
          cmp  al, 0
          jne  _not_1x
          inc  ax
_not_1x:  mov  _OS_VER, ax
          cmp  al, 14h
          jb   _CHK_DOS
_IBM_OS2: ; div by 10 to get version
          mov  _OS_VER, ax
          mov  ax, 6
          jmp  _detos_x
_CHK_DOS:
          cmp  al, 5
          jnge _MSWIN  ; 4 =>
          mov  ax, 3306h
          int  21h
          mov  _OS_VER, bx
          mov  _OS_REV, dl
          mov  _OS_ROM, dh  ; ROM = bit 4 08h set HMA = bit 8 is set 10h
          cmp  cx, 0ffceh
          je   _NXDOS  ; v8 r6
_CHK_DDOS:
          ; DoubleDos - INSTALLATION CHECK  (INT 21h, E4h - DoubleDos)
          mov  ax, 0e400h
          int  21h
          cmp  al, 0
          je   _PCDOS  
          ; DoubleDos is active
          ; 1 = caller is running in the visible partition
          ; 2 = caller is running in the invisible partition
          mov  ax, 2
          jmp  _detos_x
_PCDOS:   mov  ax, 1
          jmp  _detos_x
_NXDOS:   mov  ax, 7
          jmp  _detos_x
_MSWIN:   call is_win
          cmp  ax, 0
          je   _dhknetw
          mov  _OS_VER, ax
          mov  ax, 5
          jmp  _detos_x
_dhknetw: call is_netware
          jnc  _dhkdesq         
          mov  _OS_VER, ax
          mov  ax, 3
          jmp  _detos_x
_dhkdesq: call is_desqview
          cmp  ax, 0
          je   _detnos  
          mov  _OS_VER, ax
          mov  ax, 4
          jmp  _detos_x
_detnos:  xor  ax, ax
_detos_x: mov  _OS, ax
          ret
detect_os endp

; ===========================================================================
timeslice proc far
; Give up remainder of clock cycle under supported operating systems.
; Supports DOS, DOUBLEDOS, Desqview, MS Windows, and IBM's OS/2
; Converted from timeslice() from sysid.pas, written by Chris Evans
; ===========================================================================
timeslice1:
          mov  ax, _OS
          cmp  ax, 0
          jne  chk_os
          ; initialize timslice value
          call detect_os
          jmp  short timeslice1
chk_os:
          cmp  ax, 1
          je   _DOSIDLE
          cmp  ax, 2
          je   _DOUBLEDOS
          cmp  ax, 3
          je   _NETWARE
          cmp  ax, 4
          je   _DESQVIEW
          cmp  ax, 5
          je   _WINDOWS
          cmp  ax, 6
          je   _OS2
          cmp  ax, 7
          je   _DOSIDLE
          jmp  _ts_none
_OS2:
          nop
          ; fall thorugh, since OS/2 is compatible
_WINDOWS:
          mov  ax, 1680h
          int  2fh
          jmp  _ts_none
_DESQVIEW:
          mov  ax, 1000h
          int  15h
          jmp  _ts_none
_NETWARE:
          mov  bx, 000ah
          int  7ah
          jmp  _ts_none
_DOUBLEDOS:
          mov  ax, 0ee01h
          int  21h
          jmp  _ts_none
_DOSIDLE: int  28h  ; dos idle
_ts_none: ret
timeslice endp

; ===========================================================================
readkey   proc far ; uses bx cx dx di si ds es
; Input : NONE
; Output: AX = BIOSSCANCODE/ASCII KEY CODE 
; ===========================================================================
          cmp  ax, 0100h
          jne  KEYINT
          mov  ah, 01h
          int  16h
          jz   no_keyp
keyp_tru: mov  al, 1
          jmp  no_xkeyp
no_keyp:  xor  ax, ax
          jmp  no_xkeyp
KEYINT:        
          xor  ax, ax
          int  16h
          ; key = 0 poll keyboard
no_xkeyp:
          pushf
          cmp  al, 0
          jne  short rk_x
          push ax
          call timeslice
          pop  ax 
rk_x:     popf
          ret
readkey   endp

;===================================================================
yesno    proc far 
;===================================================================
         push bx
         push cx
         push dx
         _readkey
         cmp   al, 'Y' 
         je    yes 
         cmp   al, 'y'
         je    yes 
         cmp   al, 13 
         je    yes 
no:      mov   ax, 0
         jmp   yesno_x
yes:     mov   ax, 1
yesno_x:
         pop   dx
         pop   cx
         pop   bx
         ret
yesno    endp

; ===========================================================================
pauseprompt proc far 
; Input: cs:si points to asciiz array (the prompt text)
; Output: al = key
; e.x. lea  si, pauseprompt
; ===========================================================================
      push dx             ; uh, i dont like underscore as it can mask
      _print _pp_data, 1  ; the cursor if left at that location for awhile.
      _readkey
      pop  dx
      ret
pauseprompt endp

_pause macro 
       call pauseprompt
       endm

; ===========================================================================
videoint   proc far 
; ===========================================================================
           ; Heard that some old EGA Bioses destroy BP (and maybe ES) on call.
           ; This macro prevents BP from being corrupted, which is problem w/
           ; interrupt functions that return information in BP.
           ; would be cool if it can detect if it is a old ega and save bp
           ; dynamicly.
           push    ds
           push    es
           push    bp
           int     10h
           pop     bp
           pop     es
           pop     ds
           ; call timeslice
           ret
;The problems encountered with other VESA modes are (yet) unknown to me, on
;the other side, there are many "incompatible" (==buggy) VESA drivers for
;every video card, so you maybe should try on these machines with some
;"respected" VESA driver like UNIVBE 5.1 (a driver where at least most of the
;bugs are known ;-) ). I think there should be no further problems with the
;text mode...
videoint   endp

scrnattr proc far
         mov  ah, 08h
         mov  bh, 0
         call videoint        
         ret
scrnattr endp

; ===========================================================================
teletype  proc far
; Input : AL = char arg char:byte:0
;         BL = foreground color 
; ===========================================================================
      pushregs
      mov  dl, byte ptr cs:[directvideo]
      or   dl, dl
      jz   short biosout          
           mov  byte ptr cs:[_buffer+0], al
           mov  byte ptr cs:[_buffer+1], 0           
           call cs:getxy
           mov  bl, dl
           mov  bh, bl           
           mov  ch, Normattr
           lea  si, _buffer
           call cs:fastwrite 
      jmp  short ttyx
biosout:
      mov  bl, TextAttr
;     mov  al, char
      cmp  al, 20h
      jb   ttymode
      mov  ah, 9
      mov  bh, 0
      mov  cx, 1
      jmp  short tty1
ttymode:
      mov  ah, 0eh
tty1: call videoint   
      cmp  ah, 9
      je   ttymode
ttyx:
      popregs
      ret
teletype  endp

; ===========================================================================
beep     proc far
; ===========================================================================
         mov  al, 07h 
         call teletype
         ret
beep     endp

; ===========================================================================
newline  proc far
; ===========================================================================
         mov  al, 0ah 
         call teletype
         mov  al, 0dh 
         call teletype
         ret
newline  endp

; ===========================================================================
spaces proc far
; ===========================================================================
        push ax
_sp0001:
        mov  al, 20h
        call teletype
        dec  cx
        jnz  _sp0001
        pop  ax

       ret 
spaces endp

showz proc far
         cld
_shz:    lodsb 
         call teletype
         or   al, al
         jnz  _shz
         ret
showz endp

strout proc far
strout_l:
       mov  al, byte ptr ds:[si]
       cmp  al, 0
       je   strout_x
       call teletype
       inc  si
       jmp  strout_l
strout_x:
       ret
strout endp

; ==============================================================
fastwrite proc far
; SI = ASCIIZ String
; CH = packed 8bit color 
; BL = column
; BH = row
; ============================================================== 
     ; (row * 80 + column) = offset into screen
     xor  ax, ax
     xor  dx, dx
     mov  al, bh ; row
     mov  cl, 80
     mul  cl
     xor  cx, cx
     mov  cl, bl
     add  ax, cx ; col
     xor  dx, dx
     add  ax, 2          
     ; 
     mov  di, ax 
     mov  ah, ch
     push word 0B800h     
     pop es              
top:
     lodsb               ; ds:si++                        
     or al, al           
     jz depart           
     stosw                                       
     jmp short top       
depart:
     ret
fastwrite endp

dwrite proc far
; SI = offset of string
; CX = length
;   lea si, [msg0]  
   mov ax, 0B800h
   mov es, ax
   mov di, (80 * 1 + 0) * 2
   xor cx, cx
   mov cx, 4
   cld
   rep movsb
;   jmp $
   ret
dwrite endp

; ===========================================================================
print proc far
; Input : ds:si pointer to asciiz, pascal, or dos string array
;         DL = Z if it is an pascal array (len at ofs 0), 1=asciiz, 2=dosstr
;         DL = 3 CX contains length
;         BL foreground color
;         BH <> 0 expand 0FFH in realtime into CR/LF (calls echo twice for each)
; Output: display one character at a time and CX is returned with character
;         count of processed (for verification purposes only).
; ===========================================================================
        push ax
        push bx
        push dx
        push si
        push di
        xor  dh, dh
pr_l:   mov  al, byte ptr ds:[si]
        or   dh, dh
        jnz  pr_do   ; we been initialized already, so continue
        or   dl, dl  ; configure behavior
        jz   pr_i_p
        cmp  dl, 3   ; direct control of len
        je   pr_cx_c
pr_i_z: xor  cx, cx
        cmp  dl, 2
        jl   pr_az
        mov  dh, 3   ; DOS terminated string
        jmp  pr_azx
pr_az:  mov  dh, 2   ; ASCII-Z string
pr_azx: inc  cl
        jmp  pr_do
pr_i_p: mov  cl, al  ; its a pascal string, MODE 1
        mov  dh, 1   
        inc  si
        jmp  pr_l    ; start over and get first character
        jmp  pr_do
pr_cx_c:             ; cx contains length counter
        mov  dh, 1
pr_do:
        inc  si
        cmp  dh, 1
        ja   pr_h_z   ; Exit if termination char is present.
        dec  cx       ; else process until cx hits zero.
        or   cx, cx
        jz   pr_x
        jmp  pr_m_x
pr_h_z: inc  cx
        cmp  dh, 3
        jne  pr_c4z
        cmp  al, 24h  ; hit a dos terminated str
        je   pr_x
        jmp  pr_m_x
pr_c4z: or   al, al   ; exit processing if its an ascii-z string 
        jz   pr_x
pr_m_x:
        or   bh, bh
        jz   pr_2
pr_m1:  cmp  al, 0ffh ; test   al, 0FFh  magic character; for 0ah 0dh; CR/LF
        jne  pr_m2
        mov  al, 0ah ; CR
        call teletype
        mov  al, 0dh ; LF
        call teletype
        jmp  pr_2
pr_m2:  cmp  al, 0fdh  ; duplicate char x number
        jne  pr_2
        inc  si        ; get count
        mov  al, byte ptr ds:[si]
        push cx
        xor  cx, cx
        mov  cl, al
        inc  si        ; get 3rd, the char
        mov  al, byte ptr ds:[si]
pr_d:
        call teletype
        loop pr_d
        pop  cx
        cmp  al, 9h    ; tabulate 
        jne  pr_2
        push cx        ; output tabs
        mov  cx, 8
pr_t:
        mov  al, 20h
        call teletype
        loop pr_t
        pop  cx
pr_2: 
        call teletype
        jmp  pr_l
pr_x:
        pop  di
        pop  si
        pop  dx
        pop  bx
        pop  ax
        ret
print   endp

bcd2num  proc far
;            17H
;            and 17 0f  = 07
;            and 17 F0  = 10
;            shr 10H    = 01
;            mul 1 * 10 = 10D
;            add 10D + 7 = 17D
;  
         push ax
         and  ax, 0fh
         mov  cx, ax  ; low half
         pop  ax
         and  ax, 0f0h
         shr  ax, 4
         xor  dx, dx
         mov  bx, 10
         mul  bx
         add  ax, cx
         ret
bcd2num  endp

bin2bcd proc far
      push cx
      mov  ch, ah
      aam  ; al = % 10   ah = al / 10
      mov  cl, 4
      shl  ah, cl
      or   ah, al
      mov  al, ch
      pop  cx
      ret
bin2bcd endp

bcd2bin proc far
      xchg al, ah
      push bx
      mov  bl, 0
b2b2:
      cmp  al, 10h
      jb   b2b3
      sub  al, 10h
      add  bl, 10
      jmp  short b2b2
b2b3:
      add  al, bl
      pop  bx
      ret
bcd2bin endp

; ===========================================================================
peek      proc far
; DL    = BYTE
; DX:SI = SEGMENT/OFFSET
; ===========================================================================
          push ds
          mov  ds, dx
          mov  al, byte ptr ds:[si]
          cmp  al, dl
          je   _ckgood
          mov  al, 0
          jmp  short _ckx
_ckgood:  mov  al, 1
_ckx:     pop  ds
          ret
peek      endp

; ===========================================================================
poke      proc far
; DL    = BYTE
; DX:DI = SEGMENT/OFFSET
; ===========================================================================
          push ds
          mov  ds, dx
          mov  byte ptr ds:[di], dl
          pop  ds
          ret
poke      endp

; ===========================================================================
lbabios proc far
; Check for int 13h extensions
; ===========================================================================
        xor  bp, bp   ; bp=0 LBA , 1=CHS
        mov  ah, 41h
        mov  bx, 55aah
        int  13h
        jc   NO_LBABIOS
        cmp  bx, 0aa55h
        jnz  NO_LBABIOS
        mov  ax, 1
        jmp  short LBAX
NO_LBABIOS:
        xor  ax, ax
LBAX:   ret
lbabios endp

; ===========================================================================
delay  proc far 
; Input : AX = Seconds = 0 take from CX
;         BX = Milliseconds 
;         DX = 1    = machine independant delay
;              0ffh = I/O Delay, count set in in CX 
;                CX = loop count the greater the # the longer the delay
;         CX = Seconds to wait for every 1/18th ticks
; ===========================================================================
    cmp dx, 0ffh
    je  short _idelay_chk
IO_delay:
        jmp  short $ + 2                  ; I/O Delay approx. equal to 3 nops
        loop IO_delay
        jmp  short _delay_ret
_idelay_chk:
    cmp dx, 0
    je  short _delay_ms_1
        ; The above routine does not depend on a CPU speed.
        push es
        mov  ax, 0040h
        push ax
        pop  es
        mov  si, 006Ch
        mov  dx, word ptr es:[si+2]
        mov  ax, word ptr es:[si]
        add  ax, word ptr [BX]
        adc  dx, word ptr [BX+2]
_idelay1:
        mov  bx, word ptr es:[si+2]
        cmp  word ptr es:[si+2],dx
        jl   _idelay1
        mov  cx, word ptr es:[si]
        cmp  word ptr es:[si],ax
        jl   _idelay1
        pop  es
_delay_ms_1:
    cmp bx, 0
    je  short _delay_ax
_delay_ms:
        mov  ax, 1000
        ; mov  bx, ms 
        mul  bx        ;                                  { ms := ms * 1000}
        mov  cx, dx    ;                            {good nice and slow :) }
        mov  dx, ax
        mov  ah, 86h   ;                                 { PC AT and above }
        int  15h
        jmp  short _delay_ret
_delay_ax:
    cmp ax, 0
    je  short _delay      
        ; mov  ax, sec
        mov  bx, 18
        call multi       ; 18 * secsdesired
        mov  cx, ax      ; delay() INT 1AH - skips specified ticks
        jmp  short _delay
_delay:     
    ; xor  ax, ax
    ; xor  bx, bx
    int  1ah
    ; add  bx, dx
    inc  bx
    cmp  cx, bx
    jle  _delay          ; loop while < interval
_delay_ret:
    ret
delay  endp

qdelay proc far
dts:
    push cx
; You can use INT 1Ah to get clock count
    mov  ah, 0
    int  1ah     ; cx - high word, dx - low word of ticks
; Therefore next code should wait for 1 sec:
    mov  ah, 0
    int  1ah
    mov  bx, dx
continue:
    mov  ah, 0
    int  1ah
    mov  ax, bx
    sub  ax, dx
    cmp  ax, 18  ; check if difference is 18 ticks ~ 1 sec
    jb   continue
    ;
    pop  cx
    loop dts
    ret
    xor  ax, ax
    xor  bx, bx
    xor  dx, dx
    mov  cx, 5
    call delay
qdelay endp

; ===========================================================================
ltoa proc far
; EAX = decimal
; ===========================================================================
     mov  cx, 0FFFFh               ; Ending flag
     push cx
     .386p
     mov  ecx, 10
PD1:
     xor  edx, edx
     div  ecx                     ; Divide by 10
     add  dl, 30h                  ; Convert to ASCII
     push dx                      ; Store remainder
     or   eax, eax                 ; Are we done?
     jnz  short PD1               ; No, so continue
PD2:
     pop  ax                      ; Character is now in DL
     cmp  ax, 0FFFFh               ; Is it the ending flag?
     je   short PD3               ; Yes, so continue
     call teletype
     jmp  short PD2               ; Keep doing it
     .286c
PD3: ret
ltoa endp

; ===========================================================================
putoct proc far
; ===========================================================================
          push ax
          push bx
          push cx
          push dx
          xor     dx, dx
          mov     cl, 12
          cmp     ax, 0
          jns     notsigned3
          push    ax
          mov     al, '-'
          call    teletype
          pop     ax
          ; mov     dh, 7   ; signed op   nevermind
          neg     ax    
notsigned3:
          mov     bx, ax
OctLoop:  
          shr     ax, cl
          and     ax, 0007h
          or      dl, dl
          jnz     innumber
          cmp     dh, al      ; check for sign mask
          je      leadzero    
innumber: mov     dl, 1
          add     al, 48
          call    teletype
leadzero:                   ; do not process number
          mov     ax, bx
          sub     cl, 3
          jge     OctLoop
          pop  dx
          pop  cx
          pop  bx
          pop  ax
          ret
putoct endp

; ===========================================================================
putnum proc far
; ===========================================================================
          push ax
          push bx
          push cx
          push dx
          xor  cx, cx
putnum1:
          xor  dx, dx
          mov  bx, 10
          div  bx
          mov  bx, ax
          mov  ax, dx
          add  al, 48
          push ax                            ; I'll have to use a lifo
          inc  cx
          mov  ax, bx    
          or   ax, ax
          mov  ax, bx
          jnz  putnum1
putnum2:  pop  ax
          call teletype
          dec  cx
          jnz  putnum2
          pop  dx
          pop  cx
          pop  bx
          pop  ax
          ret
putnum endp

; ===========================================================================
putbin proc far
; ===========================================================================
          push ax
          push bx
          push cx
          push dx
       mov  cx, 16
pb_1:  shl  ax, 1
       jnc  pb_z
       push ax
       mov  al, '1'
       call teletype
       pop  ax
       jmp   pb_c
pb_z:
       push ax
       mov  al, '0'
       call teletype
       pop  ax
pb_c:
       dec  cx
       jnz  pb_1
          pop  dx
          pop  cx
          pop  bx
          pop  ax
       ret
putbin endp

puthex8 proc far
;        AL = number
; Output:
;        AX = ascii HEX digits
       push bx
       push dx
       push ax
       xor  dx, dx
       and  al, 0f0h ; MSB
       shr  al, 1
       shr  al, 1
       shr  al, 1
       shr  al, 1
ph8:
       add  al, 48  ; -> ascii
       cmp  al, 3ah ; is nibble 0-9?
       jb   noletter
       add  al, 07h
noletter:
       call teletype
       cmp  dl, 1
       je   ph8x
       pop  ax
       and  al, 0fh ; LSB
       mov  dl, 1
       jmp  ph8
ph8x:
       pop  dx
       pop  bx
       ret
puthex8 endp

; ===========================================================================
put_hex proc far
; ===========================================================================
; 00000000-00000000 - F000
          push ax
          push bx
          push cx
          push dx
        mov  bx, 0f000H
        mov  cx, 12
        mov  dx, ax
nl:     mov  ax, dx   ;    mov  ax, num
        and  ax, bx
        shr  ax, cl
        cmp  ax, 9
        jbe  n1
        add  ax, 55
        jmp  d1
n1:     add  ax, 48
d1:     call teletype
        sub  cx, 4
        shr  bx, 4
        or   bx, bx
        jnz  nl
          pop  dx
          pop  cx
          pop  bx
          pop  ax
        ret
put_hex endp

; ===========================================================================
wrdec     proc far
; ===========================================================================
          push ax
          mov al, ah
          mov ah, 0
          call putnum
          _echo '.'
          pop  ax
          mov ah, 0
          call putnum
          ret
wrdec     endp

; ===========================================================================
setadlib proc far
; Input : DL = ADDRESS
;         BL = DATA
; Code found this t.pascal routine in the shareware echo... think it was
; original written by Mark Shadaram (mark.shadaram@oubbs.telecom.uoknor.edu)
; I converted this to assembler with major rewrites.
; ===========================================================================
      push dx
      push bx
      mov al, dl
      outport ADLIBPORT
      pop bx
      pop dx
      mov cx, 6
_adl1:                 ; Delay
      inport ADLIBPORT
      mov J, al
      loop _adl1
      push dx
      push bx
      mov al, bl
      outport ADLIBDATA
      pop bx
      pop dx
      mov cx, 35
_adl2:                 ; Delay
      inport ADLIBPORT
      mov J, al
      loop _adl2
          ret
setadlib endp

; ===========================================================================
detect_adlib proc far
; Output: AL = NZ = Adlib card found
; ===========================================================================
      mov  dl, 4h
      mov  bl, 60h
      call setadlib  ; step 1
      mov  dl, 4h
      mov  bl, 80h
      call setadlib  ; step 2
      xor  ax, ax
      xor  dx, dx
      xor  cx, cx
      mov  bx, 10
      call delay
      inport ADLIBPORT
      mov  J, al     ; step 3
      mov  dl, 2h
      mov  bl, 0ffh
      call setadlib  ; step 4
      mov  dl, 4h
      mov  bl, 21h
      call setadlib  ; step 5
      xor  ax, ax
      xor  dx, dx
      xor  cx, cx
      mov  bx, 10
      call delay
      inport ADLIBPORT
      mov  K, al     
      mov  dl, 4h
      mov  bl, 60h
      call setadlib  
      mov  dl, 4h
      mov  bl, 80h
      call setadlib  
      mov  dl, J
      and  dl, 0e0h
      mov  bl, K
      and  bl, 0e0h
      cmp  dl, 0
      jne  _no_adlib
      cmp  bl, 0c0h
      jne  _no_adlib
      mov  al, 1
      jmp  _detadlib
_no_adlib:
      xor  al, al
_detadlib:
      ret
detect_adlib endp
; NightShift / Wichita Falls, TX (817)855-1526 (1:3805/13)
; Never mind the bollocks here's TEROX BBS (1:120/324.0)

; ===========================================================================
qchipset   proc far
; ===========================================================================
_q_vtype:    
             mov  al, 0
         ;    mov  chipset, al
             ; Check for PCI VGA Adapter
             push es
             mov  ax, 0c000h
             mov  es, ax
             mov  al, byte ptr es:[0030h]
             mov  bl, byte ptr es:[0031h]
             mov  cl, byte ptr es:[0032h]
             pop  es
             cmp  al, 'P'
             jne  _qnpcivga
             call teletype
             mov  al, bl
             cmp  al, 'C'
             jne  _qnpcivga
             mov  al, cl
             cmp  al, 'I'
             jne  _qnpcivga
             mov  al, 1
             mov  chipset, al
_qnpcivga:
             ; Identify Paradise SVGA Chip
             push es
             mov  ax, 0c000h
             mov  es, ax
             mov  al, byte ptr es:[007dh]
             mov  bl, byte ptr es:[007eh]
             mov  cl, byte ptr es:[007fh]
             mov  dl, byte ptr es:[0080h]
             pop  es
             cmp  al, 'V'
             jne  _qcp1
             mov  al, bl
             cmp  al, 'G'
             jne  _qcp1
             mov  al, cl
             cmp  al, 'A'
             jne  _qcp1
             mov  al, dl
             cmp  al, '='
             jne  _qcp1
             mov  al, 2
             mov  chipset, al
             ; test for PVGA1A
             oportw 03ceh, 050fh ; Extensions ON
             oportw 03d4h, 8529h ; Unlock PR10-PR17
             oportw 03c4h, 4806h ; Unlock extended sequencer
             oport 03D4h, 02Bh
             inport 03D5h
             mov  L, al
             oport 03D5h, 0AAh
             inport 03D5h
             cmp  al, 0aah
             jne  _npga1a
             mov  al, 2
             mov  chipset, al
             oport 03D5h, L
_qcp1:       jmp  _vm_nparadise
_npga1a:     oport 03D5h, L
             ; Distinguish between WD90C00 and WD90C10
             oport 03D4h, 012h
             inport 03D5h
             mov   L, al
             and   al, 0bfh
             outport 03D5h
             inport 03D5h
             and   al, 40h
             or    al, al
             jz    _nwd90c00
             mov   al, 3
             mov   chipset, al      ;  WD90C00
             oport 03D5h, L
             jmp   _vm_nparadise
_nwd90c00:   ; Distinguish between WD90C10 and WD90C11
             oport 03D4h, 10h
             inport 03D5h
             mov   L, al
             and   al, 0fbh
             outport 03c5h
             inport 03D5h
             and   al, 04h
             jz    _nwd90c10
             mov   al, 4
             mov   chipset, al      ;  WD90C10
             oport 03D5h, L
             jmp   _vm_nparadise
_nwd90c10:   mov   L, al
             or    al, 04h
             inport 03D5h
             and   al, 04h
             jnz   _wd90c11
             mov   al, 4
             mov   chipset, al      ;  WD90C10
             oport 03D5h, L
             jmp   _vm_nparadise
_wd90c11:    ; We made it this far so it's a WD90C11
             mov   al, 5
             mov   chipset, al      ;  WD90C11
             oport 03D5h, L
             oportw 03ceh, 000fh ; Extensions OFF
             oportw 03d4h, 0029h ; Lock PR10-PR17
             oportw 03c4h, 0006h ; Lock extended sequencer
_vm_nparadise:
             ; Identifying the Video7 SVGA Chip
             oportw 03c4h, 0EA06h                      ; To enable extensions
             oport  03c4h, 0ch
             inport 03c5h
             mov    L, al
             oport  03c5h, 55h
             inport 03c5h
             mov    i, al
             oport  03c4h, 1fh
             inport 03c5h
             mov    j, al
             oport  03c4h, 0ch
             oport  03c5h, L
             oportw 03c4h, 0AE06h                     ; To disable extensions
             ; Check that register value is $55 Xor $EA }
             mov  bl, chipset
             mov  al, j
             cmp  al, 0bfh
             jne  _nVideo7
             mov   bl, 6
_nVideo7:    mov  chipset, bl
             mov  al, bl
             cmp  al, 6
             jb   _nV7
             ; Once you know that the video card has a video7 in it you
             ; can read the Chip Revision register to find out which chip it
             ; is:
             oport 03c4h, 8eh
             inport 03c5h
             cmp  al, 49h
             jg   _v7c2
             mov  bl, 6     ; 1024i
             jmp  _nV7
_v7c2:       cmp  al, 59h
             jg   _v7c3
             mov  bl, 7     ; V7VGAV5
             jmp  _nV7
_v7c3:       cmp  al, 7eh
             jg   _v7c4
             mov  bl, 8     ; V7VGAFWVRAM
             jmp  _nV7
_v7c4:       cmp  al, 0ffh
             jg   _nV7
             mov  bl, 9     ; V7VGA VEGA VGA
_nV7:
             ; Identify Tseng Labs Vid Card
             oport 03c0h, 6h
             inport 03c1h                                    ; Misc. register
             mov  L, al
             xor  al, 30h
             mov  i, al
             outport 03c1h
             inport 03c1h
             cmp  al, i
             jne  _nTseng
             oport 03c1h, L
             mov  bl, 10  ; ET3000
             ; Query if it is a ET4000, change the extended start address... 
             oport  03d4h, 33h
             inport 03d5h
             mov  L, al
             xor  al, 00001111b
             outport 03d5h
             inport  03d5h
             and  al, 00001111b
             cmp  al, 15
             jne  _Tseng3000
             mov  bl, 11
_Tseng3000:  jmp  _Tseng1
_nTseng:     oport 03c1h, L
_Tseng1:     mov  chipset, bl
             ; The Trident SVGA chips can be identified by attempting
             ; to change the Mode Control #1 register
             oport  03c4h, 0eh
             inport 03c5h
             mov   L, al
             oport  03c5h, 00h
             inport 03c5h
             and  al, 1
             or  al, al
             jz  _nTrident
             mov  bl, 12  ; Trident 8800
             oport  03c5h, L
             oport  03c4h, 0bh
             oport  03c5h, 00h
             inport 03c5h
             cmp  al, 3
             jl   _nTrident
             mov  bl, 13    ; Trident 8900
             ; Trident left in 64K newmode, not 128K paging
_nTrident:   oport  03c5h, L
             mov  chipset, bl
             ret
qchipset  endp

; ===========================================================================
showchipset  proc far
; ===========================================================================
             _print chipset_msg, 1
             xor  ax, ax
             xor  dx, dx
             mov  al, chipset
             mov  bx, CHIPSETSIZE
             mul  bx
             lea  si, NONVGA
             add  si, ax
             call showz
             ret
showchipset  endp

; ===========================================================================
videomode    proc far 
; Input   : AH = 0 set videomode w/ value in AL, else query videomode
; Returns : AL = current video mode
; Two routines for saving the origanial video-mode. do it at prog start.
; ===========================================================================
             or   ah, ah
             jnz  queryvidmode
             xor  ah, ah
             call videoint
             jmp  _videomode
queryvidmode:
             mov ax, 1130h
             xor bx, bx
             int 10h
             dec dl  ; screen rows 0040:0084
             mov _screenrows, dl
             ;   
             mov  ah, 0Fh
             call videoint
             mov  _OldMode, al
egavga:
             mov  ah, 12h           ; Detect EGA/VGA
             mov  bl, 10h
             call videoint
             mov  vid_features, ch
             mov  vid_switches, cl
             mov  vid_memsize, bl                        ; >4 CGA or MDA 
             mov  vid_default, bh
             ;
chkvesa:
             push ds
             push es
             mov  ax, ds
             mov  es, ax
             lea  di, VESASignature
             mov  ax, 4f00h
             call videoint          ; ah = 0 = vesa card found, buffer filled
             mov  VESAVIDEO, ah
             pop  es
             pop  ds
             ;
             call qchipset
             ;
             xor  ax, ax
             mov  al, _OldMode
             call videoint
_videomode:
             ; _gotoxy 99, 99, 0
             ret
videomode    endp

vesainfo proc far
         push ds

         push ds
         pop  es

           push ds
           mov ax, word ptr VESAOEMptr+2
           mov ds, ax
           mov si, word ptr VESAOEMptr
           call showz
           pop ds

           _output VESASignature, 5, 1
           _spaces 1
           _echo 'v'
           mov ax, word ptr VESAVersion ; di+4
           call wrdec
           _spaces 1
           mov ax, word ptr Videomemory
           mov bx, 64
           xor dx, dx
           imul bx
           call putnum
           _echo 'K'
           _echo 'B'
           _spaces 1
           mov ax, word ptr VESAVersion
           cmp al, 03h
           jb  _sv_noagp
           _print VIDEOACCEL, 1
_sv_noagp:
         pop  ds
         ret
vesainfo endp

; ===========================================================================
show_videohardware proc far
; ===========================================================================
           mov  al, VESAVIDEO
           or   al, al
           jnz  _svx
           call vesainfo
           _newline
_svx:
           call showchipset
           mov  al, vid_default
           or   al, al
           _echo 'C'
           _echo 'o'
           _echo 'l'
           _echo 'o'
           _echo 'r'
           jmp  _svhvm
           _echo 'M'
           _echo 'o'
           _echo 'n'
           _echo 'o'
_svhvm:    _echo ' '
           mov  al, vid_memsize
           cmp  al, 4
           jl   _svh2
           jmp  _svhcga
_svh3:     cmp  al, 3
           jne  _svh2
           mov  ax, 256
           jmp  _svh
_svh2:     cmp  al, 2
           jne  _svh1
           mov  ax, 192
           jmp  _svh
_svh1:     cmp  al, 1
           jne  _svh0
           mov  ax, 128
           jmp  _svh
_svh0:     mov  ax, 64           
_svh:      call putnum
           _echo 'K'
;          _echo '['
;          xor  ah, ah
;          mov  al, vid_features
;          call put_hex
;          _echo ':'
;          xor  ah, ah
;          mov  al, vid_switches
;          call put_hex
;          _echo 'b'
;          _echo ']'
           jmp  _svhx
_svhcga:   _echo ' ' 
           _echo 'C'
           _echo 'G'
           _echo 'A'
           _echo '/'
           _echo 'M'
           _echo 'D'
           _echo 'A'
_svhx:     _echo ' '
           _newline
           ret
show_videohardware endp

getxy proc far
; Output: DH/DL = X,Y
      mov  ah, 03h
      int  10h
      ret
getxy endp

; ===========================================================================
gotoxy proc far
; Input: DL = X coorident (Column)
;        DH = Y coorident (Row)
;        BH = Video Page
; ===========================================================================
       sub  dh, 1
       sub  dl, 1
       xor  bx, bx  ; mov  bh, vpage
       xor  ax, ax
       mov  ah, 02h
       call videoint
       ret
gotoxy endp

_gotoxy macro _dlx, _dhy, _bhp
       mov  bh, _bhp
       mov  dl, _dlx
       mov  dh, _dhy
       call gotoxy
        endm

; ===========================================================================
clearscreen proc far
; ===========================================================================
        pushregs ; store registers...

        MOV     AX, 40h
        MOV     ES, AX  ; for getting screen parameters.
        MOV     AH, 06h ; scroll up function id.
        MOV     AL, 0   ; scroll all lines!
        MOV     BH, TextAttr  ; attribute for new lines.
        MOV     CH, 0   ; upper row.
        MOV     CL, 0   ; upper col.
        MOV     DI, 84h ; rows on screen -1,
        MOV     DH, ES:[DI] ; lower row (byte).
        MOV     DI, 4Ah ; columns on screen,
        MOV     DL, ES:[DI]
        DEC     DL      ; lower col.
        call    videoint

        ; set cursor position to top
        ; of the screen:
        MOV     BH, 0   ; current page.
        MOV     DL, 0   ; col.
        MOV     DH, 0   ; row.
        MOV     AH, 02
        call    videoint

        popregs         ; re-store registers...

        ret
clearscreen endp

; ===========================================================================
clrscr  proc far
; ===========================================================================
        call clearscreen
        ret
clrscr  endp

; ===========================================================================
fastclrscr proc far
; ===========================================================================
     mov  ah, 0fh
     int  10h
     mov  ah, 0
     int  10h
     ret
fastclrscr endp

clreol proc far
    mov  cx, 80
_eol:
    _echo ' '
    loop _eol
    ret
clreol endp

; ===========================================================================
hidecursor proc far 
; ===========================================================================
           mov   ax, 0100h
           mov   cx, 2607h  ; 2000
           call  videoint
           ret
hidecursor endp

; ===========================================================================
showcursor proc far 
; ===========================================================================
           mov   ax, 0100h
           mov   cx, 0506h
           call  videoint
           ret
showcursor endp

TextColor proc far
; AL = color 
        push ax
	TEST	AL,0F0H
        JE      @@TC
	AND	AL,0FH
	OR	AL,80H
@@TC:   AND     TextAttr,70H
	OR	TextAttr,AL
        pop ax
        RET
TextColor endp

; Set text background (color modes)

TextBackground proc far
        push ax
        push cx
	AND	AL,7
	MOV	CL,4
	SHL	AL,CL
	AND	TextAttr,8FH
	OR	TextAttr,AL
        pop  cx
        pop  ax
        RET
TextBackground endp

LowVideo proc far
	AND	TextAttr,0F7H
        RET
LowVideo endp

HighVideo proc far
        OR      TextAttr,8  ; 00001000
        RET
HighVideo endp

NormVideo proc far
	MOV	AL,NormAttr
	MOV	TextAttr,AL
        RET
NormVideo endp

; ===========================================================================
memalloc proc far
; BX = Paragraphs
; ===========================================================================
       mov  ah, 48h
       ; mov  bx, paras
       int  21h
       jnc  allcok
       xor  ax, ax
allcok:
       ret  
memalloc endp

; ===========================================================================
memfree proc far
; ES = Segment
; ===========================================================================
       mov  ah, 49h
       ; mov  es, p
       int  21h
       ret
memfree endp

; ===========================================================================
memresize proc far
; ES = Segment of requested block
; BX = Size in paragraphs
; Out :
;      AX = error if CF=CY
;      BX = largest block availble if the request fails
; ===========================================================================
       mov  ah, 4ah
       int  21h
       ret
memresize endp

; ===========================================================================
memlargest proc far
; AX = Largest block free
; ===========================================================================
       mov  bx, -1
       call memalloc
       mov  ax, bx
       ret 
memlargest endp

; ===========================================================================
resize proc far 
; Input : BX = size 
; Output: AL = 8, if memory resize failed
; ===========================================================================
       mov  ax, bx
       xor  dx, dx
       mov  bx, 16
       div  bx
       add  ax, bx
       mov  bx, ax
       call memresize
       jnc  resize_ok
       ;  call putnum
       ;  _newline
       ;  call Readkey
       ; xor  ax, ax
       ; mov  al, 08h
resize_ok:
       ret
resize endp

; ===========================================================================
mcopy proc far
; Input : ES:SI = source
;         DS:DI = dest
;         CX    = size
; ===========================================================================
mv:
     mov  al, byte ptr es:[si]
     mov  byte ptr ds:[di], al
     inc  si
     inc  di
     loop mv
     ret
mcopy endp

; ===========================================================================
reboot  proc far 
; ===========================================================================
        jmp     short $ + 2
        xor     ax,ax      
        mov     ds,ax
        mov     ah, 40h
        mov     es,ax
        mov     word ptr es:0072,bx
        mov     ax, 0FFFFh
        mov     es,ax
        xor     si,si
        push    ax
        push    si
        ret
reboot  endp

reset   proc far
        mov     al, 0FEh  ; $B0/$FE/$E6/$64
        out     64h, al   ; hardware reset..
;        db 'if we''re still running then the system is probably a PC/XT...'
        ret
reset   endp

; ===========================================================================
doserror proc far
; Input : AL = Errorcode
; ===========================================================================
         pushregs
         jnc  _noerror
         cmp  ax, 1fh  ; 00011111h    31
         jbe  _dekerr
         call putnum
         _echo ' '
         mov  ax, 0eh  ; force an unknown error
_dekerr: cmp  ax, 0
         je  _noerror
         dec  ax
         xor  dx, dx
         mov  bx, errorlen        
         mul  bx
         lea  bx, [err01]
         add  ax, bx
         mov  si, ax
         ; mov  dx, word ptr errors[si]
         ; mov  si, dx
         mov  dl, 3
         mov  cx, errorlen
         mov  bh, 1 
         call print      
         _newline
_noerror:
         popregs
         ret
doserror endp

; ===========================================================================
dosexit  proc far 
; ===========================================================================
         call    doserror
         mov     ah, 4ch           ; define AL for errorlevels..
         int     21h
         ret
dosexit  endp

; ===========================================================================
halt     proc far 
; ===========================================================================
         call    dosexit
         ret
halt     endp

_halt    macro errcode
         mov   al, errcode
         call  halt
         endm

; ===========================================================================
lockup   proc far
; ===========================================================================
@@hlt:   jmp  short @@hlt
         ret
lockup   endp

; ===========================================================================
lockpc   proc far
; AX = KEY
; BX = LOCKCODE      ; 1234H
; ===========================================================================
         mov  cx, ax ; Key
@@lck:
         mov  ax, cx
         sub  ax, bx ; Lock
         jnz  @@lck
         ret
lockpc   endp

; ===========================================================================
doswrite proc far 
; Input : DX, offset of 24h ($) terminated dos string
; ===========================================================================
         mov   ah, 09h
         int   21h
         ret
doswrite endp

_doswrite macro _doswofs
          mov  dx, offset _doswofs
          call doswrite
          endm

skip_spaces proc far
ss1:
     cmp  byte ptr [si], 20h
     jne  short ss2
     inc  si
     loop ss1
ss2: ret
skip_spaces endp

cparam macro cparams, cparamv, cparamg
       mov  bh, cparams
       mov  bl, cparamv
       mov  al, cparamg
       call scancmdline
       cmp  al, 1
       endm

; ===========================================================================
scancmdline proc far
; Input : AL = Which parameter you want
;         BL = character to match
;         BH = switch character slash-char to scan for as switch on command
;              line e.g."/?"
; Output: AL = 1 = True
;         BX = Parameter Count        
;         CX = length of commandline
;         DL = length of parameter retreived
;      ES:DI = location of buffer where parameter is found
; ===========================================================================
      push ds
      push si
      push cs
      pop  es
      mov  byte ptr cs:[scancmdg], al
      push cs
      pop  ax
      mov  ds, ax ; set ds to codeseg
      cld
      mov  si, 80h
      xor  bp, bp
      xor  dx, dx
      xor  cx, cx
      mov  cl, ds:[si] ; len
      inc  si
      cmp  cl, 0
      je   _scn 
      push cx
_scl: lodsb
      cmp  al, ' '
      jne  _sccs
           inc  cs:[scancmdc]
           xor  ax, ax
           mov  al, byte ptr cs:[scancmdc]
           cmp  al, byte ptr cs:[scancmdg]
           jne  _sccs
                xor  bp, bp
                lea  di, _buffer
_scngparam:
                mov  al, byte ptr ds:[si]
                inc  si
                cmp  al, ' '
                je   _scisns
                     mov  byte ptr es:[di], al
                     inc  di
                     inc  bp
                loop _scngparam
                push ax
                mov  ax, di
                mov  word ptr cs:[scancmdp], ax
                pop  ax
_sccs:
      cmp  al, bh
      jne  _scisns
      lodsb
      cmp  al, bl
      jne  _scisns
      mov  dl, 1
_scisns:
      loop _scl
      pop  cx
_scn:
      mov  al, dl
      mov  dx, bp
      lea  di, _buffer
      xor  bx, bx
      mov  bl, byte ptr cs:[scancmdc]
      pop  si
      pop  ds
      ret
; isswitch proc far
;      push  si                  
;_iss: lodsb                      ; Get a character
;      cmp   al, 0dh              ; exit if eol
;      je    _iss_x
;      cmp   al, dl               ; loop if not char
;      jne   _iss
;      cmp   byte ptr [si-2],'/'  ; loop if not /
;      jne   _iss
;      add   sp, 2                ; clear the stack
;      sub   si, 2                ; adjust si
;      stc                        ; set carry and exit
;      jmp   _iss_x2
;_iss_x:
;      pop   si
;      clc                        ; Clear carry and exit
;_iss_x2:
scancmdc db 0
scancmdg db 0
scancmdp dw 0
scancmdline endp

; ===========================================================================
multiplex proc far
; Input : AX = IDENT
; Output: AL = 1 = true 
; ===========================================================================
        ;  xor  ax, ax
        ;  mov  ax, ident        
          int  2fh                     ; dos multiplex
          cmp  al, 0ffh
          ; 00 = not installed okay
          ; 01 = not installed not okay  FF = installed
          je   mplx_fnd
          xor  ax, ax
          jmp  mplx_ext
mplx_fnd: mov  al, 1
mplx_ext: ret         
multiplex endp

; ===========================================================================
debugtrap proc far                                          
; ===========================================================================
; temporary set to code segment
          mov  dl, 1
Modify:   mov  byte ptr cs:m+1,0
m:        jmp  short no_dbg         ; overrides the jmp address, to continue.
          _print dbgtrace, 1
;         call doswrite 
;         mov  dx, offset dbgtrace         
;         lea  si, dbgmess  
;         mov  dx, offset dbgmess
          cli
          clearregs 
          _halt 255
no_dbg:   ret
debugtrap endp

getdos_errorlevel proc far
          mov  ah, 4dh
          int  21h
          ret
getdos_errorlevel endp

; ===========================================================================
dosexterror proc far
; Input : CF=1 after a dos file operation
; Output: AX = ErrorCode
;         BH = Error Class 
;         BL = Suggested Action
;         CL = location (where the error occured)
; ===========================================================================
             mov  ah, 59h  ; DOS Extended error
             int  21h
no_error:    ret 
dosexterror  endp

; ===========================================================================
odd     proc far 
; Input : AX = a 16BIT number
; Output: Sets the CF bit in the FLAGS register if it is odd.
; ===========================================================================
     push ax
     push dx
     ; xor   ax, ax
     ; mov   ax, number
     and   ax, 00000001b ; sweet
     or    ax, ax
     jz    oddp_even
     ; xor   dx, dx
     ; div   two
     ; cmp   dx, 0000h
     ; je    oddp_even
oddp_odd:
     stc           ; TRUE - set carry flag to indicate odd number
     jmp  short oddp_end
oddp_even:
     clc           ; FALSE - clear carry flag to indicate even number
oddp_end:
     pop  dx
     pop  ax
     ret
odd  endp

; ===========================================================================
is_even proc far
;        AX = word
; Output:
;        AX = true or false (1,0)
;
; 11/14/96 CE - Tests if number is even; return true if so...
;               Duuuuh! :) Even := odd(number); Or do it da other way.. and
;               div by 2? see if no reminder.
; 11/16/1996  - Interesting the solution some of us on the PASCAL_LESSONS
;               echo came up with an even function when in TP all you had to
;               do was NOT Odd().
; ===========================================================================
     test ax, 0
     je   rnoteven    ; test for posible div by zero
     xor  dx, dx
     mov  bl, 2
     div  bl
     test ah, 0       ; test for reminder..
     jne  rnoteven
     mov  ax, 1       ; true
     jmp  eiseven
rnoteven:
     mov  ax, 0       ; false
eiseven:
     ret
is_even endp

; ===========================================================================
seekspace proc far
; Input : DS:SI = string
; Output: CX    = Count
;         DS:SI = points to next space
; ===========================================================================
        xor cx, cx
_ssl:
        mov al, byte ptr [si]
        inc si
        inc cx
        cmp al, 0
        je  _ssx
        cmp al, 20h
        jne _ssl
_ssx:   ret
seekspace endp

; ===========================================================================
strcopy   proc far 
; Input:
;       DS:SI = string ofs asciiz
;       DS:DI = destination array
;       DL    = termination char
;       BX    = copy limit
; Output:
;       CX    = returns char count
; ===========================================================================
        xor  cx, cx
_copyz1:
        mov  al, byte ptr [si]
        inc  si
        inc  cx
        cmp  al, dl
        je   _copyz2
        mov  byte ptr [di], al
        inc  di
        push ax
        mov  al, 0
        mov  byte ptr [di], al     ; ASCIIZ terminated
        pop  ax
        cmp  cx, bx
        je   _copyz2
        or   al, al
        jnz  _copyz1
_copyz2:
        ret
strcopy endp

pos  proc far
; SI = asciiZ string
; BL = Character 
; returns: AX = offset of character, first occurance.
     xor  cx, cx
     xor  dx, dx
posloop:
     mov  al, byte ptr [si]
     cmp  al, bl                      
     jne  poscont
     mov  cx, dx
     jmp  short exitpos
poscont:
     inc  si
     inc  dx
     or   al, al
     jnz  posloop
exitpos:
     mov  ax, cx
     ret
pos  endp

atoi proc far
; si string
; cx count 
       xor  ax, ax
       xor  dx, dx
       inc  cx
       cld           ; increment
atoi1:
       dec cx
       jz  atoix
       lodsb
       call teletype
       or  al, al    ; AL = 0?
       jz  atoix      ; If So, Exit Loop
       push ax       ; Save AX
       xchg dx, ax   ; Swap DX and AX
       mov  bx, 10
       mul  bx       ; Multiply by 10
       xchg dx, ax   ; Move back to DX
       pop ax
       sub al, 30h        ; Convert to dec.
       sub ah, ah
       add dx, ax    ; Add the next digit in...
       jmp atoi1          ; Keep going till we hit the null....
atoix:
       mov  ax, dx
       ret
atoi   endp

; ===========================================================================
compare         proc far
; Input : DS:SI string number 1
;         DS:DI string number 2
;         CX    length of strings
;         DL  = custom termination, e.g. CR
;         DH  = custom termination, e.g. SPACE
; Output: BX  = NZ, if different
;         CX  = SI position
; ===========================================================================
                push dx
                push cx
                push ax
                mov bx, 0
_scans:
             ;   lea  si, input
             ;   lea  di, pwdata
                mov  ah, byte ptr [si]
                mov  al, byte ptr [di]
                cmp  ah, dl             ; if str1[i] = CUSTOM then done
                je   _comprd
                cmp  ah, dh             ; if str1[i] = CUSTOM then done
                je   _comprd
                cmp  al, dl             ; if str2[i] = CUSTOM then done
                je   _comprd
                cmp  al, dh             ; if str2[i] = CUSTOM then done
                je   _comprd
                cmp  ah, 0              ; if str1[i] = NUL then done
                je   _comprd
                cmp  al, 0              ; if str2[i] = NUL then done
                je   _comprd
                cmp  ah, al             ; if str1[i]<>str2[i] then different
                je   _strok
                mov  bx, 1
_strok:
                inc  si
                inc  di
                dec  cx
                jnz  _scans
_comprd:
                pop  ax
                pop  cx
                pop  dx
                ret
compare         endp

; ===========================================================================
getdata proc far
; Input : DS:DI = index of array
;         CX    = length of array
; Output: DS:DI = index of array
;         CX    = characters entered
; ===========================================================================
        push ax
        push bx
        push dx
        push di
        push si
        xor  bx, bx
_gd_i:
        call getxy
        mov  K, dl
        mov  J, dh
        call readkey
        cmp  al, 8
        jne  _no_takeback
        mov  ax, bx
        or   ax, ax
        jz   _write_forword
        mov  ax, 8
        call teletype
        mov  ax, 20h
        call teletype
        mov  ax, 8
        call teletype
        dec  di
        dec  bx
        mov  al, 0
        mov  byte ptr [di], al
        jmp  _gd_i
_write_forword:
        inc  J
        inc  K
        _gotoxy K, J, 0
        jmp  _gd_i
_no_takeback:
        call teletype
        cmp  al, 13
        je   _gd_x
        mov  byte ptr [di], al
        inc  di
        inc  bx
        xor  al, al
        mov  byte ptr [di], al
        loop _gd_i
_gd_x:  mov  cx, bx
        pop  si
        pop  di
        pop  dx
        pop  bx
        pop  ax
        ret
getdata endp

; ===========================================================================
getstring  PROC    far
; ===========================================================================
        PUSH    AX
        PUSH    CX
        PUSH    DI
        PUSH    DX
        MOV     CX, 0                   ; char counter.
        CMP     DX, 1                   ; buffer too small?
        JBE     empty_buffer            ;
        DEC     DX                      ; reserve space for last zero.
wait_for_key:
        MOV     AH, 0                   ; get pressed key.
        INT     16h
        CMP     AL, 13                  ; 'RETURN' pressed?
        JZ      _gsexit
        CMP     AL, 8                   ; 'BACKSPACE' pressed?
        JNE     add_to_buffer
        JCXZ    wait_for_key            ; nothing to remove!
        DEC     CX
        DEC     DI
        mov     al, 8h
        call    teletype
        mov     al, 20h
        call    teletype
        mov     al, 8h
        call    teletype
        JMP     wait_for_key
add_to_buffer:
        CMP     CX, DX          ; buffer is full?
        JAE     wait_for_key    ; if so wait for 'BACKSPACE' or 'RETURN'...
        MOV     [DI], AL
        INC     DI
        INC     CX
        ; print the key:
        MOV     AH, 0Eh
        INT     10h
        JMP     wait_for_key
        ;============================
_gsexit:  ; terminate by null:
        MOV     byte ptr ds:[DI], 0
empty_buffer:
        POP     DX
        POP     DI
        POP     CX
        POP     AX
        ret
getstring ENDP

; ===========================================================================
readnum proc far ; uses bx cx dx di si ds es
; SIGNED READNUM! The ultimate ball of perfect energy.
; Input : BX = BASE
; Output: AX = number
; ===========================================================================
; 2-3-2003  - Created for CIS35A
; 3-30-2005 - revised for assembly in generic assembler syntax
; ===========================================================================
        push bx            ; stack ptrs ss:bp+i
        xor  ax, ax        ; initialize vars
        xor  dx, dx
        mov  BASE, bx      ; bx sets BASE in our dataseg
        mov  NUM, ax       
        mov  SB, ax        
        mov  RANGE, 0ffffh ; start out as unsigned
_rwhile:                   ; .WHILE  (NUM <= 65535) && (al != 13)
        mov  ax, NUM   
        cmp  ax, RANGE
        jbe  _rrepeat      
        jmp  _rpltf
_rrepeat:
        call readkey
        mov  CHAR, al
        cmp  al, 1bh
        jbe  _rplt3
        cmp  al, '-' ; .IF (CHAR == '-') && (NUM == 0)
        je   _rplt3
        jmp  _rnsf
_rplt3: jmp  _relse
_rpltf: jmp  _rend
_rnsf:  call teletype    ; Yes, Good to see it, even if wrong.
        ; a 97 61h = 01100001
        ; A 65 41h = 01000001
        ; B 66 42h = 01000010
        ; 0 48 30h = 00110000
        ;   10 0ah = 00001010
        ;   11 0bh = 00001011
        cmp  al, 'a'                            ; .IF     (al >= 'a')
        jl   _not_alpha_else
        and  al, 11011111b                      ; lowercase
_not_alpha_else:
        cmp  al, 'A'                            ; .IF     (al >= 'A')
        jnae _not_alpha
        xor  al, 01000000b                      ; strip off ALPHA bit
        add  al, 9
        jmp  _saven
_not_alpha:
        cmp  al, 48         ;  .IF     (al >= 48) && (al < 58)
        jb   _relse
        cmp  al, 58
        jg   _relse
        sub  al, 48   ; get numerical
_saven: mov  K, al
        xor  ax, ax
        cmp  ax, BASE ; .IF (ax < BASE)
        jae  _rplt4   ; illegal base entered, dont calculate into number
_rcalc:
        mov  ax, NUM
        mov  bx, BASE
        mul  bx
        jo   _rovrf
        xor  bx, bx
        mov  bl, K
        add  ax, bx
        jnc  _norovrf  
_rovrf: _print  _overflow_msg, 1             ; blank out vars and go through
        xor  ax, ax
        mov  K, al
_norovrf:
        mov  NUM, ax
        jmp  _rrepeat
_rplt4: jmp  _rbinp
_relse:
        clc
        mov  al, CHAR
        cmp  al, 1bh   ; .IF (CHAR == 01bh)
        jne  _rbss
        xor  ax, ax
        mov  NUM, ax    ; abort and return w/ zero
        mov  al, 13
        stc
        jmp  _rccr
_rbss:
        cmp  al, '-'
        jne  _rbse
        mov  ax, 1
        mov  sb, ax
        mov  RANGE, 8000h ; -32768 (32767+1)
        _echo '-'
        jmp  _rendw
_rbse:
        cmp  al, 8
        jne  _rccr
        mov  ax, NUM
        cmp  ax, 0
        je   _rendw
        ; DUH, wasnt throughly checking all conditions for edit function to
        ; be active.
        xor  dx, dx
        mov  ax, NUM
        mov  bx, BASE
        div  bx
        jns  _rns    ; not signed 
        xor  ax, ax  ; blank out vars and go through
_rns:   mov  NUM, ax
        mov  al, 08
        call teletype
        mov  al, 20h
        call teletype
        mov  al, 08
        call teletype
        jmp  _rendw
_rccr:  cmp  al, 13   
        je   _rend
        mov  ax, NUM
        cmp  ax, 0    ; .IF (al != 13) && (NUM == 0) ; ILLEGAL -- beep user.
        jne  _rendw   ; cntrl chars are exception
_rbinp: ; _print _badinput, 1
        call beep
        ; .WHILE  (CHAR != 13) (AX <= RANGE) &&
_rendw: jmp  _rwhile
_rend:
        mov  ax, NUM
        mov  bx, SB
        or   bx, bx
        jz  _rno1s
        not  ax
        inc  ax
_rno1s:
        pop  bx
        ret
readnum endp

; ===========================================================================
bitcount      proc   far 
; Input : BX = Bits
; ===========================================================================
              push bx
              push cx
;              mov    bx, bits
              xor    ax, ax
              mov    cx, 15
countbits:    shr    bx, 1       ; ror ax, 1
              jnc    zerobit
              inc    ax 
zerobit:      loop   countbits
              pop cx
              pop bx
              ret
bitcount      endp

; ===========================================================================
parity   proc far
; AX = pnum BX = pattern
; ===========================================================================
              push bx
;              mov    ax, pnum
              and    ax, bx ; ptrn
              ; push   ax
              call   bitcount
              ; push   ax
              call   odd
              jc     setbit
              xor    ax, ax
              jmp    pbitdone
setbit:       mov    ax, 1
pbitdone:     pop bx  
              ret
parity   endp

; ===========================================================================
digits proc far
; Input: AX = NUM BX = BASE
; returns count in CX register
; ===========================================================================
        push bx
        push cx
        push dx
;       mov  ax, NUM
;       mov  bx, BASE
       xor  cx, cx
dig_1: 
       xor  dx, dx
       div  bx
       inc  cx
       or   ax, ax
       jnz  dig_1
       mov  ax, cx
       pop dx
       pop cx
       pop bx
       ret
digits endp

; ===========================================================================
justify proc far jnum:word, jmag:word, jbase:word, jtype:word 
; Input : number and type of justification passed on stack
;         jtype = 0 flush right/left, 1 centered
; Output: writes space padding on screen
; ===========================================================================
        push ax
        push bx
        push cx
        push dx
        mov  ax, jnum
        mov  bx, 0
        cmp  ax, 0  ; CMP works in signed subtraction operation, as why extra spaces with
        jns  j_1    ; numbers > 32767.
        neg  ax
        inc  bx
j_1:
       ; push ax  
       ; push jbase
        call digits ; Cool, yeah just count the base positions and from there
        add  ax, bx ; figure out how many spaces are needed.
        mov  bx, ax
        mov  ax, jmag
        sub  ax, bx
        mov  cx, ax
j_2:    mov  al, 20h
        call teletype    
        loop j_2         
        pop  dx
        pop  cx
        pop  bx          ; cant call macro from macro??   Think I solved 
        pop  ax          ; this once in 2001 but I FORGOT and all my 
        ret              ; knowledge is stored in my big toe.
; There was once a farmer that lived on planet very similiar to ours, and he
; had a big toe. It was very big and used it to plow fields with his toenail.
; One day he had an toe ache and laid down his plow and sat holding his big
; toe and he remembered all the knowledge he had forgotten. Uh, I forget what
; happened next...
justify endp

; ===========================================================================
ZEROPAD proc far
; Input : CL = count padding length. (zplth)
; Zeropad the display number to Nth decimal places
; ===========================================================================
           push    ax
           push    bx
           cmp     ax, 9
           jg      zp_cool
           xor     cx, cx
           ; mov     cl, zpnth
           dec     cl       ; Nth-1 tiered zero 
zplooptop: mov     ax, 0
           mov     bh, 0
           call    putnum
           loop    zplooptop
zp_cool:   pop     bx
           pop     ax
           ret
ZEROPAD ENDP

; ===========================================================================
monthday     proc far
; Input: bx = leapyear dx = month
; convenience, instead of through that array again
; ===========================================================================
             mov ax, 31               ; assume months 1,3,5,7,8,10 & 12
          ;   mov bx, lpy
          ;   mov dx, mnth
             cmp dx, 4
             je  subday
             cmp dx, 6
             je  subday
             cmp dx, 9
             je  subday
             cmp dx, 11
             je  subday
             cmp dx, 2
             jne tx
             sub ax, 3    ; 31 - 3 = 28
             cmp bx, 0
             jz  tx
             jmp tx
addday:      add ax, 1
             jmp tx
subday:      sub ax, 1
tx:          ret
monthday      endp

; I wrote these in math notebook, havnt tested them yet. - summer 2002

sqr macro sqr_x1, sqr_x2
    mov  ax, sqr_x1
    mov  bx, sqr_x2
    xor  dx, dx
    mul  bx
    endm

sine macro sine_x

     endm

cosine macro cosine_x

     endm

tan macro tan_x

     endm

; ===========================================================================
sqrt proc far
; ===========================================================================
        mov  cx, 2
        mov  bx, ax
sqrt1:  mov  ax, bx
        xor  dx, dx
        div  cx
        or   dx, dx
        jnz  sqrt1     ; not perfect square
        cmp  ax, bx
        jne  sqrt1
        ret
sqrt endp

; ===========================================================================
pythgorean proc
; uses bx cx dx, x2:word, x1:word, y2:word, y1:word
; Calc distance betwwen two points
; ===========================================================================
        push cx
        push dx
     ;   mov ax, x2      ; d = SQRT[(x2-x1)^2 + (y2-y1)^2)
     ;   mov bx, x1
        sub ax, bx
        mul ax  ; 2
        pop  dx
        pop  cx
        push ax
     ;   mov ax, cx ; y2
     ;   mov bx, dx ; y1
        sub ax, bx
        mul ax
        pop bx
        add ax, bx
        call sqrt
        ret
pythgorean endp

; ===========================================================================
dbgarr proc far
; ===========================================================================
       
       mov  bp, ax  ; ofs address
       mov  si, 0   ; size
       mov  cx, bx
       mov  dx, 0
dbga1:
       xor  ax, ax
       mov  al, byte ptr [bp+si]
       push ax
       push 5
       push 10
       push 0
       call putnum
       inc  si
       inc  dx
       cmp  dx, 8
       jl   nocrlf
       mov  al, CR
       call teletype
       mov  al, LF
       call teletype
       xor  dx, dx
nocrlf:
       loop dbga1
       
       ret
dbgarr endp

; ===========================================================================
chpath proc far
; Switch to new directory, but save current path first
; inputs:  es:bx - points to new path
; outputs: old_path has origional path
;	    carry set if dos error
; ===========================================================================
	push	ds
        mov     ah, 19h
	int	21h		;get current drive# in -al-
        add     al,'A'
        mov     old_drive, al
        ; save current path
	mov	ax,cs
	mov	ds,ax
	mov	si,offset old_sep+1
	mov	ah,47h
	mov	dl,0
	int	21h		;get current path -> ds:si
        ; check if drive present
	cmp	byte ptr es:[bx+1],':'
	je	cp_drv_fnd		;jmp if drive found
	mov	si,bx
        cmp     byte ptr es:[bx],'\'
	je	cp_path_fnd		;jmp if path without drive found
        clc     ;do not return error, this is probably the debugger RUN.COM
	jmp	change_path_exit	;exit if drive not present
        ; switch to new drive at es:bx
cp_drv_fnd:
	mov	dl,es:[bx]	;get drive letter
	sub	dl,'A'
	mov	ah,0eh
	int	21h		;switch to new drive
        ; switch to new path, but first extract info. from es:bx+1
	mov	si,bx
	add	si,2			;skip past drive
cp_path_fnd:
	mov	di,offset new_path
        cmp     byte ptr es:[si],'\'
	je	mp_loop			;jmp if directory present
	mov	word ptr ds:[di],004ch
	jmp	do_path_change
mp_loop:
	mov	al,byte ptr es:[si]
	mov	byte ptr ds:[di],al
	inc	si
	inc	di
	cmp	al,0
	jne	mp_loop
        ; now scan back and remove executable file name
mp_bak: dec	di
        cmp     byte ptr [di],'\'
	jne	mp_bak
	mov	byte ptr [di],0
do_path_change:
	mov	ah,3bh
	mov	dx,offset new_path
	int	21h
change_path_exit:
	pop	ds
       ret
chpath endp

; ===========================================================================
rspath proc far
; switch to directory saved by CHANGE_PATH
; inputs: none
; 
; output: carry set if dos error
; ===========================================================================
        mov     dl,old_path
	cmp	dl,0
	je	rp_path_switch		;jmp if drive not present
	sub	dl,'A'
	mov	ah,0eh
	int	21h			;switch drives
        ; switch paths
rp_path_switch:
	push	ds
	mov	ax,cs
	mov	ds,ax
	cmp	old_sep,0
	je	rp_exit			;jmp if no path avail
	mov	dx,offset old_sep
	mov	ah,3bh
	int	21h
rp_exit:
	pop	ds
       ret
rspath endp

_fopen macro fname, newfile, attrib, accessmode
         mov  al, accessmode
         mov  bx, newfile
         mov  cx, attrib
         lea  dx, fname
         call fopen
         endm

; ===========================================================================
fopen proc far
; Input : ds:dx offset of filename, asciiz.
;         bx = 1 to create the file.
;         cx = attributes, see dos reference. (0=normal)
; Output: ax = NZ is filehandle (save this), else error code in DL.
; ===========================================================================
;      xor  al, al
;      xor  cx, cx
      mov  ah, 3dh    ; Open it
      cmp  bx, 1
      jne  doit      
      mov  ah, 03ch   ; Create it - 60d
doit: mov  bx, 0      
      int  21h
      jnc  ofpt           
errorhandler:
      xor  dx, dx
      mov  dl, al   ; error code in dl  ax=0 the file failed to open.
      mov  ax, 0
ofpt: ret
fopen endp

_fclose macro handle
        mov  bx, handle    
        call fclose
        endm

; ===========================================================================
fclose proc far
; Input : bx = handle
; ===========================================================================
     xor  al, al
     mov  ah, 3eh
     int  21h
     ret
fclose endp

_fileexist macro fname
         lea  dx, fname
         call fileexist
         endm

; ===========================================================================
fflush proc far
; flushes the DOS output buffer for specified handle
; inputs:    BX = file handle
;            flushing the buffer guards against data loss in case power
;            outages.
; output:    if CF = 0, no error; function successful
;            if CF = 1, AX = DOS error code
; ===========================================================================
       push bx
       mov  ah, 45h               ;duplicate file handle
       int  21h
       jb   FF_EXIT
       mov  bx, ax
       mov  ah, 3Eh               ;close file
       int  21h
FF_EXIT:
       pop  bx
       ret
fflush endp

; ===========================================================================
ferase proc far
; DS:DX addr of asciiz filename
; ===========================================================================
       mov  ah, 41h
       int  21h
       ret
ferase endp

; ===========================================================================
fileexist proc far
; Input : ds:dx offset of filename, asciiz.
; Output: carry flag set it does exist.
; ===========================================================================
          mov  bx, 0
          call fopen
          cmp  ax, 0
          jz   notexist
exist:
          mov  bx, ax
          call fclose
          stc
          jmp  feexit
notexist: clc         
feexit:   ret
;  push    bx
;  mov     ax,3d00h             ;open file at ds:dx
;  INT     21h
;  JB      FEX_EXIT
;  MOV     BX,AX
;  MOV     AH,3Eh               ;close file
;  INT     21h
;FEX_EXIT:
fileexist endp

_fread  macro handle, bufferofs, bufsize
        mov  bx, handle    
        lea  dx, bufferofs  
        mov  cx, bufsize
        call readfile
        endm

_fget   macro handle
        mov  bx, handle    
        lea  dx, _buffer
        mov  cx, 1
        call readfile
        endm

; ===========================================================================
readfile proc far                                     ; Reads a block into memory
; Input : bx = handle
;         dx = offset of buffer
;         cx = size of buffer
; Output: ax = NZ if eof  or dl = errorcode
; ===========================================================================
         mov  ah, 3fh
         int  21h
         jnc  _rf_ok
         mov  dl, al
         mov  ax, 0
_rf_ok:  ret
readfile endp

; ===========================================================================
freadln proc far
; Input : AX    = Handle
;         DS:DI = Buffer
; Output: DS:DI = Buffer start
;         CX    = Buffer length
; ===========================================================================
        push di
        mov  W, ax
        xor  cx, cx
_frl:
        xor  ax, ax
        mov  byte ptr [di], al
        push cx
        mov  bx, W
        mov  dx, di
        mov  cx, 1
        call readfile
        pop  cx
        mov  byte ptr [di], al
        inc  di
        inc  cx
        cmp  al, 0ah
        jne  _frl
        pop  di
        ret
freadln endp

_freadln macro handle
         mov  ax, handle
         call freadln
         endm

_fwrite macro handle, bufferofs, bufsize
        mov  bx, handle    
        lea  dx, bufferofs  
        mov  cx, bufsize
        call fwrite
        endm

_fput macro handle
        mov  bx, handle    
        lea  dx, _buffer
        mov  cx, 1
        call fwrite
        endm

; ===========================================================================
fwrite proc far                                  ; Writes a block from memory
; Input : bx = handle
;         dx = offset of buffer
;         cx = size of buffer
; Output: ax = NZ if diskfull  or dl = errorcode
; ===========================================================================
         mov  ah, 40h
         int  21h
         jnc  _wf_ok
         mov  dl, al
         mov  ax, 0
_wf_ok:  ret
fwrite endp

; ===========================================================================
fwriteln proc far
; Input : AX    = Handle
;         DS:SI = Buffer
; Output: DS:SI = Buffer start
;         CX    = Buffer length
; ===========================================================================
        push si
        mov  W, ax
        xor  cx, cx
_wrl:
        push cx
        mov  bx, W
        mov  dx, si
        mov  cx, 1
        call fwrite
        pop  cx
        mov  al, byte ptr [si]
        inc  si
        inc  cx
        cmp  al, 0ah
        jne  _wrl
        pop  si
        ret
fwriteln endp

; ===========================================================================
fsqueeze proc far
; ===========================================================================
         mov  ax, 3eh
         mov  bx, 3   ; AUX file handle
         int  21h
         inc  bx      ; PRN file handle
         int  21h
         ret
fsqueeze endp

; ===========================================================================
filecopy proc far
; Input : DS:[SI] = address of source filename, ASCIIZ
;         ES:[DI] = address of destination filename, ASCIIZ
; Output: if CF = 0, no problem
;         if CF = 1, AX = DOS error code (AX = -1 if insufficient memory)
; ===========================================================================
          push  si
          push  di
          push  ds
          push  es
          mov  dx, si
          mov  bx, 0
          mov  cx, 0
          call fopen
          cmp  ax, 0
          je   fc1
          mov  fc_handle, ax

          mov  dx, di
          mov  bx, 1
          mov  cx, 0
          call fopen
          cmp  ax, 0
          je   fc2
          mov  fc_handle2, ax

          mov  bx, fc_handle
          mov  ax, 5700h
          int  21h                     ;get input file date
          jc   fc2
          mov  fcdate1,cx
          mov  fcdate2,dx

          ; allocate some dos memory
       ;   mov  ah, 48h
       ;   mov  bx, 256  ; 4096/16 blocks
       ;   int  21h
       ;   jc   fc_memerr
          ; AX = segment:0
       ;   push ax
       ;   call put_hex
       ;   _newline
       ;   pop  es
       ;   mov  ds, ax
           push ds
           pop  es
           mov dx, offset _buffer
          ;
          ; ES and DS not point to our buffer
          ;
          jmp  short fc_loop
fc1:      jmp  fc_error1
fc2:      jmp  fc_error2
fc3:      jmp  fc_error3
          ; bx=file handle,  cx=read buffer size  ds:dx = buffer pointer
          nop
fc_loop:
         mov     bx, fc_handle            ;get file handle
         mov     ah, 3fh
         mov     cx, 255
         int     21h                     ;read file
         jc      fc_error3               ;  jmp if read error
         cmp     ax, 0                    ;check if any data read
         je      fc_done                 ;  jmp if all done
         mov     cx, ax
         mov     ah, 40h
         mov     bx, fc_handle2           ;get file handle
         int     21h                     ;write data
         jc      fc_error3               ;  jmp if error
         cmp     ax, cx
         jne     fc_error3
         cmp     cx, 255 ; 4096
         je      fc_loop                 ;loop till done

fc_done:  ; file has been copied, set date of new file
          mov  bx, fc_handle2           ;get output file handle
          mov  cx, fcdate1
          mov  dx, fcdate2
          mov  ax, 5701h                ;set file date
          int  21h                     

          mov  bx, fc_handle
          call fclose
          mov  bx, fc_handle
          call fclose

         ; ; release memory if dynamic
         ; mov  ah, 49h
         ; ; es:0 already set 
         ; int  21h

          clc
          xor  ax, ax
          jmp  fc_exit
fc_memerr:
          stc
          mov  ax, 8
          jmp  fc_exit
fc_error3:
          stc
          mov  ax, 3
          jmp  fc_exit
fc_error2:
          stc
          mov  ax, 2
          jmp  fc_exit
fc_error1:
          stc
          mov  ax, 1
fc_exit:
         pop  es
         pop  ds
         pop  di
         pop  si
         ret
filecopy endp

; ===========================================================================
filesize  proc far
; Find an open file's size
; inputs:    BX = file handle
; output:    if CF = 0, DX:AX = file size (low word in AX)
;            if CF = 1, AX = DOS error code
; ===========================================================================
        mov     AX,4201h             ;set ds:ax to file posn (save)
        xor     CX,CX
        mov     DX,CX
        int     21h
        jb      FSIZE_1
        mov     DI,DX
        mov     SI,AX
        mov     AX,4202h             ;seek to end of file, size -> dx:ax
        xor     CX,CX
        mov     DX,CX
        int     21h
        push    DX
        push    AX
        mov     AX,4200h              ;restore orig. file posn
        mov     CX,DI
        mov     DX,SI
        int     21h
        pop     AX                   ;put file size in dx:ax
        pop     DX
FSIZE_1:
          ret
filesize  endp

; ===========================================================================
sizefile  proc far
; Find a closed file's size
; inputs:    DS:DX = pointer to file name asciiz string
; output:    if CF = 0, DX:AX = file size (low word in AX)
;            if CF = 1, AX = DOS error code
; ===========================================================================
        mov  ax,3d00h
        int  21h                     ;open file
        jc   fs_err2                 ;jmp if error
        mov  bx,ax                   ;get file handle
        call filesize
	pushf
	push	ax
        mov  ah,3eh
        int  21h                     ;close file
        pop  ax
	popf
fs_err2:
          ret
sizefile  endp

; ===========================================================================
filecount proc far
; Counts the number of files matching an ASCIIZ filespec string.
; inputs:    DS:[DX] pointing to filespec string
;            CX = file attributes
; output:    AX = number of files matching the filespec string
; notes:     The filespec string may include the '*' and '?' wildcards.
; ===========================================================================
   push    bp
   cld
   xor     BP,BP
   mov     AH,4Eh               ;find first file
FC_01:
   int     21h
   jb      FC_02
   inc     BP
   mov     AH,4Fh               ;find next file
   jmp     FC_01
FC_02:
   mov     AX,BP
   pop     BP
          ret
filecount endp

; ===========================================================================
spawn proc far
; Input : DX = Commandline 
;         BX = wEnvSeg block
; Output: AX = ErrorCode if CF=CY
; ===========================================================================
      mov  ax,4b00h
      ; lea  dx, commandline
      ; lea  bx, wEnvSeg
      int  21h
      ret
spawn endp

rombasic proc far
           int 18h
           ret
rombasic endp

bootstrap proc far
           int 19h
           ret
bootstrap endp

set_IVT   macro
;    AL = VECTOR 
; ES:SI = 0:VECT
          xor  bx, bx
          mov  es, bx
          mov  bx, 4
          xor  dx, dx
          mul  bx
          mov  ah, 0
          mov  si, ax
          ; es:[si]   ; OFS   ; vector offset  = (AL * 4)
          ; es:[si+2] ; SEG   ; vector segment = (AL * 4)+2 
          endm

; ===========================================================================
getvector proc far
; input : ax = vector
; output: es:bx address of vector
; ===========================================================================
         push dx
         push ax
         mov bx, 4
         xor dx, dx
         mul bx
         xor dx, dx
         mov es, dx
         mov si, ax
         cli
         mov bx, word ptr es:[si]
         mov ax, word ptr es:[si+2]
         mov es, ax
         sti
         pop ax
         pop dx
         ret
getvector endp

; ===========================================================================
setvector proc far
; input : ax = vector
;         ds:cx address of vector
; output: none
; ===========================================================================
         mov  bx, 4
         xor  dx, dx
         mul  bx
         xor  dx, dx
         mov  es, dx
         mov  si, ax
         cli
         mov  ax, ds
         mov  word ptr es:[si], cx
         mov  word ptr es:[si+2], ax ; *4+2
         sti
         mov  es, ax
         ret
setvector endp

debugdump proc far
; ES:SI = buffer
;    CX = Size
          _newline
          mov  ax, cx
          mov  bx, 16
          xor  dx, dx
          div  bx
          mov  cx, ax
debug_dump:
          push cx
          mov  ax, es
          call put_hex
          _echo ':'
          mov  ax, si
          call put_hex
          _echo ' '
          _echo ' '
          mov  bx, 0
          push si
hexdisp:
          push bx
          xor  ax, ax
          mov  al, byte ptr es:[si]
          call puthex8
          _echo ' '
          inc  si
          pop  bx
          inc  bx
          cmp  bx, 16
          jb   hexdisp  
          pop  si

          mov  bx, 0
chrdisp:
          push bx
          xor  ax, ax
          mov  al, byte ptr es:[si]
          cmp  al, 32
          jg   disp
          mov  al, '.'
disp:
          call teletype
          inc  si
          pop  bx
          inc  bx
          cmp  bx, 16
          jb   chrdisp  
          _newline

          pop  cx
          loop debug_dump
          ret
debugdump endp

_Debugd macro _Ddata, _Dsize
     push cs
     pop  es
     mov  si, offset _Ddata
     mov  cx, _Dsize
     call debugdump
     endm

; ===========================================================================
debugregisters proc far
; ===========================================================================
        pushregs
        ; ------------------
        _print _AX, 1
        call put_hex
        _echo 'H'
        _echo ' '
        ; ------------------
        _print _BX, 1
        push bx
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        ; ------------------
        _print _CX, 1
        push cx
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        ; ------------------
        _print _DX, 1
        push dx
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        _newline
        _print _CS, 1
        push cs
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        ; ------------------
        _print _DS, 1
        push ds
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        ; ------------------
        _print _ES, 1
        push es
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        ; ------------------
        _print _SS, 1
        push ss
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        ; ------------------
        _print _SP, 1
        push sp
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        ; ------------------
        _print _SI, 1
        push si
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        ; ------------------
        _print _DI, 1
        push di
        pop ax
        call put_hex
        _echo 'H'
        _echo ' '
        _print _BP, 1
        push bp
        pop  ax
        call put_hex
        _echo 'H'
        popregs
        ret
debugregisters endp

; ===========================================================================
fillarray  proc far
; DI = array
; CX = len
; BL = char to fill with 
; ===========================================================================
           xor ax, ax
_farry:
           mov  al, bl
           mov  byte ptr ds:[di], al
           inc  di
           loop _farry
           ret
fillarray  endp

; ===========================================================================
parameters proc far
; input : SI = offset of string, terminated by null
; output: CX = parameters (words)
; ===========================================================================
           push ax
           push si
           xor  cx, cx
           mov  al, byte ptr ds:[si]
           or   al, al   ; is it empty?
           jz   _pes
           inc  cx       ; account for first word
_pl1:      lodsb
           cmp  al, 20h  ; ' '
           jne  _pdc
           inc  cx
_pdc:      or   al, al
           jnz  _pl1
_pes:      pop  si
           pop  ax
           ret
parameters endp

parameter proc far
; SI = string
; DI = sub string destination
; BX = parameter 
; CX = parameter count
; DX = 0 
     xor  dx, dx
     mov  W, dx
paramloop:
     mov  al, byte ptr[si]
     cmp  dx, bx
     jb   paramnostore
     cmp  dx, bx
     ja   paramnostore
     mov  byte ptr[di], al
     inc  di
     push ax
     mov  al, 0
     mov  byte ptr[di], al
     pop  ax
     cmp  al, ' '
     je   paramdone
paramnostore:
     cmp  al, ' '
     jne  paramnotnextword
     inc  DX
paramnotnextword:
     inc  si
     or  al, al
     jnz  paramloop
paramdone:
     ret
parameter endp

; ===========================================================================
romannum proc far
; ===========================================================================
_rn:
         mov  si, 6
_rnM:
         cmp  ax, romanv[si]
         jl   _rnD
         _echo romann[si]
         sub  ax, romanv[si]
_rnD:    dec  si
         mov  cx, si
         cmp  cx, 0
         jg   _rnM
         cmp  ax, 0
         jg   _rn
         ret
romannum endp

; ===========================================================================
alpha proc far
; AL = char
; Output: CF=CY=FALSE
;         CF=NCY=TRUE
; Is the value in AL a capitalized alpha char?
; ===========================================================================
      cmp  al, 'A'
      jb   short alphax
      cmp  al, 'Z'
      ja   short alphax
      clc
      jmp  short alphax1
alphax:
      stc
alphax1:
      ret
alpha endp

; ===========================================================================
upcase   proc far
; AL = character to upcase 
; ===========================================================================
         cmp al, 'a'
         jb  _no_upcase
         cmp al, 'z'
         ja  _no_upcase
         ; 61h -> 41h
         ; 0110-0001  0100-0001
         xor al, 20h
         ; sub  al,32
_no_upcase:
         ret
upcase   endp

; ===========================================================================
dncase   proc far
; AL = character to upcase 
; ===========================================================================
         cmp al, 'A'
         jl  _no_dncase
         cmp al, 'Z'
         ja  _no_dncase
         xor al, 20h
_no_dncase:
         ret
dncase   endp

; ===========================================================================
upstr proc far
; Input: DS:SI = ASCIIZ
;        BL    = Special termination char
;        CX    = NZ upcase first CX characters
; ===========================================================================
_upsi:
      cmp  cx, 0ffh
      je   _upcx
      dec  cx
      cmp  cx, 0
      je   _upsx
_upcx:
      mov  al, byte ptr [si]
      cmp  al, bl
      je   _upsx
      call upcase
      mov  byte ptr [si], al
      inc  si
      or   al, al
      jnz  _upsi
_upsx:
      ret
upstr endp

; ===========================================================================
random proc far
; Input : AX = low end (0..65535)
;         BX = high end
;         CX = 0 pick from clock else use 16bit seed value
; Output: AX = Random number
; ===========================================================================
ADDEND     equ 13849
       push ax
       push bx
       cmp  cx, 0
       jne  userseedval
       MOV  AH, 0 
       INT  1AH  
       xchg AX, DX    ; generate a number
       mov  cx, ax
userseedval:
       mov  seed, cx
       pop  bx
       pop  ax
       ;
       mov  LOWER, ax
       mov  UPPER, bx
       ; X = Seed * multiplier mod 65536
       ; seed = (x + addend) mod 65536
       mov  ax, seed
       mul  MULTIPLIER
       add  ax, ADDEND
       mov  seed, ax
       ; range = upper - lower + 1
       mov  cx, upper
       sub  cx, lower
       inc  cx
       mul  cx                 ;RANDOM = (SEED*RANGE)/65536
       add  dx, lower            ;                    + LOWER
       mov  ax, dx 
       ret
; remainder = ((wSeed * 106) + 1283)+(cf=1+1)/6075
; RandomNum = (remainder * MaxRange) / 6075
random endp

; ===========================================================================
cpuid proc far
; ===========================================================================
_get_cpuid:
         .386p
         xor  eax, eax
         _cpuid               ; TASM 3.2 dont have cpuid pentium instructions
         push ebx
         shl  ebx, 16
         mov  word ptr VendorSign, bx
         pop  ebx
         mov  word ptr VendorSign+2, bx
         push edx
         shl  edx, 16
         mov  word ptr VendorSign+4, dx
         pop  edx
         mov  word ptr VendorSign+6, dx
         push ecx
         shl  ecx, 16
         mov  word ptr VendorSign+8, cx
         pop  ecx
         mov  word ptr VendorSign+10, cx
         mov  byte ptr VendorSign+12, al ; eax ; 00
         .286c
_skip_cpuid:
         ret
cpuid endp

; ===========================================================================
checkndp proc far
; ===========================================================================
         xor  ax, ax
         mov  W, ax
         fninit   ; init ndp...
         fnstcw W
         cmp  byte ptr [W+1], 3
         jne  ckndp_no
         mov  ax, 1
ckndp_no:
         ret
checkndp endp

; ===========================================================================
checkcpu proc far
; A small CPU check to determine if we run on a 386
; see http://www.ddj.com/articles/1996/9611/9611n/9611n.htm for details
; CF=CY=NOT A 386
; CF=NCY=386+
; AX = 0 = 8086, 1 = 186, 2 = 286, 3 = 386+ etc... 
; ===========================================================================
         pushf
         stc
         xor  bx, bx
         push sp    ; If SP pushed and its it origanial valud then its a 286+
         pop  ax
         cmp  ax, sp
         jz   _chkcpu16
         mov  ax, 7000h           ; if bits 12,13,14 are still set
         push ax                  ; after pushing/poping to/from
         popf                     ; the flags register then we have
         pushf                    ; a 386+
         pop  ax                  
         and  ax, 7000h           
         cmp  ax, 7000h           
         mov  ax, 2               
         je   short _chkcpu32     ; it's a 386+
_chkcpu8:
         dec  ax               ; mov  ax, 1 ; its a 80186
         ; 80186 - 8086 class, confirm
         mov  cx, 0121h        ; If CH can be shifted by 21h, then its a 8086
         shl  ch, cl        
         jnz  _chkcpux   ; 80186+ limits shift counts.
         dec  ax               ; 8086!
         jmp  _chkcpux
_chkcpu16:
         mov  ax, 2  ; 80286
         cmp  bl, 1
         je   _chkcpux
         mov  bl, 1
         lea  dx, _chkcpu16
         mov word ptr [bx + 24], dx      ; Install an exception handler
         mov [bx + 26], bx
         .386p
         cwde                            ; and try a 32 bit instruction
         .286p
_chkcpu32:
         .386p
         mov  ebx, esp
         and  esp, 0fffffffcH
         pushfd
         pop eax
         mov  ecx, eax
         xor  eax, 40000h
         push eax
         popfd
         pushfd
         pop eax
         and  eax, 40000h
         and  ecx, 40000h
         push ecx
         popfd
         cmp  ecx, eax
         je   _ccpuis386
         .286p
_ccpuis386:
         call checkndp
         or   ax, ax
         jnz  _ccpuis486
         mov  ax, 3      ; 80386+
         jmp  short chkcpuid
_ccpuis486:
         mov  ax, 4      ; 32bit with NDP -- assume 486
chkcpuid:
         push ax
         call cpuid
         pop  ax
         clc
         xor bx, bx
         xor cx, cx
         xor dx, dx
_chkcpux:
         popf
         ret
checkcpu endp

SOUND proc far  
; AX = frequency
; CX = duration
              push           ax
              push           cx
              push           ds
              pushf
              push           dx
              or             ax,ax
              jz             SDelay
              mov            cx,ax
              mov            dx,0012h
              mov            ax,34DCh
              div            cx             ; calculate sound value
              mov            cx,ax
              mov            al,0B6h
              out            43h,al         ; timer: square wave on channel 2
              mov            al,cl
              out            42h,al         ; send LSB of sound frequency
              mov            al,ch
              out            42h,al         ; send MSB of sound frequency
              in             al,61h
              or             al,3           ; speaker on & driven by timer
              out            61h,al

SDelay:        pop            cx
              mov            dx,cx
              jcxz           SDone
              sti                           ; allow interrupts
              xor            ax,ax
              mov            ds,ax
SWaitLoop:     mov            al,ds:[046Ch]  ; PC heartbeat (system time data)
SWait1:        cmp            al,ds:[046Ch]  ; has the time changed?
              je             SWait1          ;   no, go try again
              loop           SWaitLoop       ;   loop if more ticks required

SDone:         in             al,61h
              and            al,0FCh        ; speaker off
              out            61h,al
              popf
              pop            ds
              pop            cx
              pop            ax
              ret
SOUND endp                     ; make a sound of specific freq/duration

encrypt proc far
; AL = byte to crypt
; BL = key
        dec  al
        not  al  
        rol  al, 1
        rol  al, 1
        xor  al, bl     ; KEY = say 0ceh  or something 
        ret
encrypt endp

decrypt proc far	 ; http://www.simonshepherd.supanet.com/tea.htm
        xor  al, bl
        ror  al, 1
        ror  al, 1
        not  al
        inc  al
        ret
decrypt endp

; ===========================================================================
Slow_TurboPC proc far
; ===========================================================================
     mov  al, 0eah        ; value for deturbo mode
     out  64h, al        ; write to command port
     ret
Slow_TurboPC endp

; ===========================================================================
Fast_TurboPC proc far
; ===========================================================================
     mov  al, 0e5h        ; value for turbo mode
     out  64h, al        ; write to command port
     ret
Fast_TurboPC endp

; ===========================================================================
Is_HPDV5 proc far
; CF = CY = TRUE Hewlett-Packard 
; ===========================================================================
     ; f000:36d7 "Hewlett-Packard"
     push es
     push ds
     push cs
     pop  ds
     mov  cx, 16
     mov  si, 36d7h 
     mov  di, offset HewlettPackard_m
     mov  ax, 0f000h
     mov  dx, ax
     mov  es, ax
hpchk:
     push dx
     push cx
     push si
     push di
     mov  bl, byte ptr es:[si]
     mov  al, bl
     call teletype
     mov  al, byte ptr ds:[di]
     call teletype
     pop  di
     pop  si
     pop  cx
     pop  dx
     cmp  al, bl
     jne  not_HP
     inc  di
     inc  si
     loop hpchk
is_HP:
     stc
     jmp  _hpckx
     ret
not_HP:
     clc
_hpckx:
     pop  ds
     pop  es
     ret
HewlettPackard_m db 'Hewlett-Packard',0
Is_HPDV5 endp

__toolbox_api:
_toolbox_api dw max, for, SafeDOS, multi, divide, div32, Float_Add, is_netware, is_desqview
            dw is_win, detect_os, timeslice, readkey, yesno, pauseprompt, videoint, scrnattr
            dw teletype, beep, newline, spaces, showz, strout, fastwrite, dwrite, print
	    dw bcd2num, bin2bcd, bcd2bin, peek, poke, lbabios, delay, qdelay, ltoa
            dw putoct, putnum, putbin, puthex8, put_hex, wrdec, setadlib, detect_adlib
            dw qchipset, showchipset, videomode, vesainfo, show_videohardware, getxy
            dw gotoxy, clearscreen, clrscr, fastclrscr, clreol, hidecursor, showcursor
            dw TextColor, TextBackground, LowVideo, HighVideo, NormVideo, memalloc, memfree
	    dw memresize, memlargest, resize, mcopy, reboot, reset, doserror, dosexit, halt
            dw lockup, lockpc, doswrite, skip_spaces, scancmdline, multiplex, debugtrap
            dw dosexterror, odd, is_even, seekspace, strcopy, pos, atoi, compare, getdata
            dw getstring, readnum, bitcount, parity, digits, justify, ZEROPAD, monthday
	    dw sqrt, pythgorean, dbgarr, chpath, rspath, fopen, fclose, fflush, ferase
            dw fileexist, readfile, freadln, fwrite, fwriteln, fsqueeze, filecopy, filesize
            dw sizefile, filecount, spawn, rombasic, bootstrap, getvector, setvector, debugdump
            dw debugregisters, fillarray, parameters, parameter, romannum, alpha, upcase, dncase
            dw upstr, random, cpuid, checkndp, checkcpu, SOUND, encrypt, decrypt, Slow_TurboPC
            dw Fast_TurboPC, Is_HPDV5
TOOLBOX_LEN EQU ($-__toolbox_api)

; ===========================================================================
toolbox_api proc far
; Toolbox API - indexed access 
; AX = Function Number
; ===========================================================================
        mov  si, ax
        mov  ax, TOOLBOX_LEN
        mov  bx, 2
        div  bx
        cmp  ax, TOOLBOX_LEN            
        ja   short tbxbad 
           mov  ax, cs:_toolbox_api[si]            
           mov  cs:[W], ax            
           pusha
           ; call proc 
           ; call cs:_toolbox_api[si]
           db 0e8h
           ; dw cs
           dw cs:[W]
           popa
           jmp  short tbx_okay
tbxbad: stc
        jmp  short tbxapi_x
tbx_okay:
        clc
tbxapi_x:
        ret
toolbox_api endp

IF (TOOL_BOX)
       db 'Portability is for people who cannot write new programs. -Linus Torvalds'
ENDIF

TOOLBOX_SIZE EQU $ - toolbox
