;---------------------------------------------------------------------------
; XMS.ASM - show XMS info.   Author: Adam Wheeler   adamwheeler@bix.com
;
; Contributed to the public domain.  1994
;
;---------------------------------------------------------------------------
;
; Downloaded from:
;               Professional Programmers' Pages
;               http://www.fys.ruu.nl/~faber
;
;    Check it out ! For more great sources, samples, docs & tips !!!

.model TINY

;extrn           DP_WORD_D:NEAR

.code
.startup
;get xms driver address
                MOV     AX, 04300H
                INT     2FH
                mov     dx, offset no_xms
                cmp     al, 80h
                jz      xms_driver_installed
                mov     dx, offset no_xms
                mov     ah, 9
                int     21h
                jmp     exit
;get XMS driver address
xms_driver_installed:
                mov     exitcod, 0
                MOV     AX,04310H
                INT     2FH
                mov     XMS_OFFSET, bx
                mov     XMS_SEGMENT, es
;get XMS version
                MOV     AH, 000H
                CALL    dword ptr XMS_ADDRESS
                call    show_xms_ver
                call    show_hma_status
                call    show_a20_status
                call    show_driver_entry
                call    show_xms_free
                call    free_req_xms
                mov     cx, 0ffffh
                mov     dx, offset xms_info_header
                mov     ah, 9
                int     21h
 next_xms_handle:
                mov     ah, 0eh
                mov     dx, cx
                CALL    dword ptr XMS_ADDRESS
                and     ax, ax
                jz      xms_handle_invalid
                call    show_handle_info
 xms_handle_invalid:
                loop    next_xms_handle
exit:
                ;.exit   exitcode
                mov     al,exitcod
                mov     ah,4Ch
                int     21h
;---------------------------------------------
; show handle info
show_handle_info    PROC    NEAR
                    mov     ax, cx
                    xchg    ah, al
                    call    ConvALHEX
                    mov     word ptr xms_info+1, ax
                    mov     ax, cx
                    call    ConvALHEX
                    mov     word ptr xms_info+3, ax
                    mov     al, bh
                    call    ConvALHEX
                    mov     word ptr xms_info+11, ax
                    push    dx
                    mov     dx, offset xms_info
                    mov     ah, 9
                    int     21h
                    pop     dx
                    mov     ax, dx
                    call    DP_WORD_D
                    mov     dx, offset kcrlf
                    mov     ah, 9
                    int     21h
                    ret
show_handle_info    ENDP

;---------------------------------------------
; show a20 status
show_a20_status PROC    NEAR
;check A20 line
                push    ax
                push    bx
                push    dx
                mov     ah, 7
                CALL    dword ptr XMS_ADDRESS
                and     al, al
                jnz     atison
                mov     word ptr xms_A+13, 'ff'
                mov     byte ptr xms_A+15, '.'
atison:
                mov     dx, offset xms_A
                mov     ah, 9
                int     21h
                pop     dx
                pop     bx
                pop     ax
                ret
show_a20_status ENDP


;---------------------------------------------
; show driver entry point
;
show_driver_entry   PROC    NEAR
                push    dx
                push    ax
                mov     al, byte ptr XMS_SEGMENT+2
                call    ConvALHEX
                mov     word ptr driver_addr+13, ax
                mov     al, byte ptr XMS_SEGMENT
                call    ConvALHEX
                mov     word ptr driver_addr+15, ax
                mov     al, byte ptr XMS_OFFSET+2
                call    ConvALHEX
                mov     word ptr driver_addr+18, ax
                mov     al, byte ptr XMS_OFFSET
                call    ConvALHEX
                mov     word ptr driver_addr+20, ax
                mov     ah, 9
                mov     dx, offset driver_addr
                int     21h
                pop     dx
                pop     ax
                ret
show_driver_entry   ENDP

;---------------------------------------------
; show free xms memory
;
show_xms_free   PROC    NEAR
;get free extended memsize
                push    ax
                push    bx
                push    dx
                mov     ah, 8
                CALL    dword ptr XMS_ADDRESS
                mov     ax, dx
                call    IntToAsc
                mov     xms_size+18, dh
                mov     xms_size+19, dl
                mov     xms_size+20, ah
                mov     xms_size+21, al
                mov     dx, offset xms_size
                mov     ah, 9
                int     21h
                pop     dx
                pop     bx
                pop     ax
                ret
show_xms_free   ENDP

;------------------------------------------
; show_hma_status - call immediately after
; xms function 0
;
show_hma_status PROC    NEAR
                push    ax
                push    bx
                push    dx
                and     dx, dx
                mov     dx, offset hma_exist_y
                jnz     show_hma
                mov     dx, offset hma_exist_n
  show_hma:
                mov     ah, 9
                int     21h
;check for HMA available
                mov     ah, 1
                mov     dx, 0FFFFh
                CALL    dword ptr XMS_ADDRESS
                and     ax, ax
                mov     dx, offset hma_avail_n
                jz      show_havail
                mov     ah, 2
                CALL    dword ptr XMS_ADDRESS
                mov     dx, offset hma_avail_y
  show_havail:
                mov     ah, 9
                int     21h
                pop     dx
                pop     bx
                pop     ax
                ret
show_hma_status ENDP

;------------------------------------------
; show XMS version - call immediately after
; xms function 0
;
show_xms_ver    PROC    NEAR
                push    ax
                push    bx
                push    cx
                push    dx
                mov     dx, ax                  ;save xms version
                mov     ah, al
                mov     cl, 4
                shr     ah, cl
                and     ax, 0F0Fh
                add     ax, 3030h
                xchg    ah, al
                mov     word ptr xms_ver+10, ax
                mov     al, dh
                mov     ah, al
                mov     cl, 4
                shr     ah, cl
                and     ax, 0F0Fh
                add     ax, 3030h
                xchg    ah, al
                mov     word ptr xms_ver+7, ax
                mov     ax, bx                  ;get driver version
                mov     dx, ax
                mov     ah, al
                mov     cl, 4
                shr     ah, cl
                and     ax, 0F0Fh
                add     ax, 3030h
                xchg    ah, al
                mov     word ptr xms_ver+24, ax
                mov     al, dh
                mov     ah, al
                mov     cl, 4
                shr     ah, cl
                and     ax, 0F0Fh
                add     ax, 3030h
                xchg    ah, al
                mov     word ptr xms_ver+21, ax
                mov     dx, offset xms_ver
                mov     ah, 9
                int     21h
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                ret
show_xms_ver    ENDP


;------------------------------------------
; Procedure to release extended memory blocks.
;
free_req_xms    PROC    NEAR
                push    cs
                pop     es
                mov     bx, 80h
                xor     cx, cx
                mov     cl, cs:[bx]
                jcxz    exit_free_req_xms
                cld
                mov     di, bx
                mov     al, '/'
                repne   scasb
                jcxz    exit_free_req_xms
                mov     si, di
                lodsb
                or      al, 20h
                cmp     al, 'f'
                jnz     exit_free_req_xms
;/f found on command line...  get next 4 chrs and free that handle...
                lodsw
                call    ConvAXBin
                mov     dh, al
                lodsw
                call    ConvAXBin
                mov     dl, al
                sub     bl, bl
                mov     ax, 0A00h
                call    dword ptr XMS_ADDRESS
exit_free_req_xms:
                ret
free_req_xms    ENDP


;--------------------------------------------------------------------
;converts HEX number in AX to binary number in AL
; AL contains the high 4 bits, AH contains the low.
ConvAXBin       PROC    NEAR
                push    cx
                call    ConvALBin       ;convert al to binary
                mov     cl,4            ;
                shl     al,cl           ;move lower 4 bits to upper 4 bits
                xchg    ah,al           ;put ah in al
                call    ConvALBin       ;convert to binary
                or      al,ah           ;combine ah and al to make binary value
                pop     cx
                ret
ConvAXBin       ENDP

;--------------------------------------------------------------------
;converts HEX character in AL (1-9, a-f) to binary 0-15
ConvALBin       PROC    NEAR
                or      al, 20h         ;convert to lower case
                cmp     al, 40h         ;check for letter or number
                jb      notlet          ;if not letter convert
                sub     al,27h          ;make 'a' be after '9'
        notlet:
                sub     al,30h          ;convert ascii to binary
                and     al,0fh          ;make sure it's 0-15
                ret
ConvALBin       ENDP


;--------------------------------------
IntToAsc        PROC    NEAR
                cwd                             ; Zero DX register
                mov     cx, 100                 ; Divide AX by 100, yields
                div     cx                      ;   AX=quotient, DX=remainder
                aam                             ; Make digits unpacked BCD
                or      ax, '00'                ; Convert to ASCII
                xchg    ax, dx                  ; Do same thing for DX
                aam
                or      ax, '00'
                ret                             ; Return DX:AX = ASCII number
IntToAsc ENDP

;----------------------------------------------------------------------------
;ConvALHEX - converts binary number in AL to ASCII HEX number in AX
;
ConvALHEX   PROC    NEAR
        push    cx
        mov ah, al          ;save al
        call    ConvHEX         ;convert al to hex
        xchg    ah, al          ;swap digits
        mov cl, 4           ;4 bit shift
        shr al, cl          ;move high 4 bits to low 4
        call    ConvHEX         ;convert AL to hex
        pop cx
        ret
ConvALHEX   ENDP

;----------------------------------------------------------------------------
;ConvHEX - converts low 4 bits in AL to their ASCII HEX equivalent in AL.
;
ConvHEX PROC    NEAR
        and al, 0Fh         ;only low bits of AL
        cmp al, 9           ;greater than 9?
        ja  HEXltr          ;yes - A-F
        add al, 30h         ;convert to ASCII
        jmp doneHEX         ;digit was 0-9
HEXltr:
        add al, 37h         ;convert to letter
doneHEX:
        ret             ;return to caller
ConvHEX ENDP

;------------------------------------------------------------
; DP_WORD - call with ax = unsigned word to print in decimal
DP_WORD_D       PROC
                push    ax
                push    bx
                push    cx
                push    dx
                xor     cx, cx          ;zero cx
                and     ax, ax
                jnz     dpwdgetval
                mov     ax, 0030h       ;put '0',0 on stack
                inc     cx              ;inc char counter
                jmp     short dpwdprint ;print it
  dpwdgetval:
                mov     bx, 10          ;divisor (base)
        nextdigit:
                mov     dx, ax          ;save ax
                aam
                xor     ah, ah          ;zero ah
                cmp     ax, 9           ;check for above 9
                jbe     saveones        ;save if below or eq
                sub     al, 10          ;else subtract 10
    saveones:
                sub     dx, ax          ;subtract ones from dx (num)
                add     al, 30h         ;convert to decimal
                push    ax              ;put on stack
                inc     cx              ;increment digit counter
                and     dx, dx          ;check for zero
                jz      dpwdprint       ;print if done
                mov     ax, dx          ;get working num
                xor     dx, dx
                div     bx              ;divide by 10
                jmp     short nextdigit ;repeat til done
dpwdprint:
                cld                     ;count down
                mov     ah, 2           ;print char in dl
    nextdpwdchar:
                pop     dx              ;get char
                int     21h
                loop    nextdpwdchar    ;loop til done
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                ret
DP_WORD_D       ENDP

;-----------------------------------------------------------------------
;                          ----- DATA -----                            ;
;-----------------------------------------------------------------------
.data
no_xms          db      13,10,'XMS driver not found...',13,10,'$'

                db      13,10,'XMS information.  Author:adamwheelet@bix.com',13,10
                db      '                 WARNING: XMS /Fnnnn attempts to free handle nnnn.',13,10
xms_ver         db      13,10,'XMS v  .   driver v  .  ',13,10,'$'
driver_addr     db      'Entry point: xxxx:xxxx',13,10,'$'
hma_exist_y     db      'HMA exists.',13,10,'$'
hma_exist_n     db      'HMA does not exist.',13,10,'$'
hma_avail_y     db      'HMA is available.',13,10,'$'
hma_avail_n     db      'HMA is in use.',13,10,'$'
xms_A           db      'A20 line is on. ',13,10,'$'
xms_size        db      'Free XMS memory =     k',13,10,'$'

xms_info_header db      13,10,' Handle | Locks |  Size  ',13,10
                db            '--------+-------+--------',13,10,'$'
xms_info        db            ' xxxxh  |  xxh  |  $'
kcrlf           db      'k',13,10,'$'

XMS_ADDRESS     EQU     $
XMS_OFFSET      dw      0
XMS_SEGMENT     dw      0

exitcod         db      1

end
