home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / OS2AP.ZIP / DUMP.ASM next >
Assembly Source File  |  1987-09-14  |  26KB  |  647 lines

  1.  DUMP.ASM
  2.  
  3.         name    dump
  4.         page    55,132
  5.         title   DUMP --- Display File Contents
  6.         .286c
  7. ;
  8. ; DUMP.ASM --- a OS/2 utility to display the contents of a 
  9. ; file on the standard output in hex and ASCII format.
  10. ;
  11. ; Copyright (C) 1987 Ray Duncan
  12. ;
  13. ; Usage:  C>DUMP path\filename.ext  [ >device ]
  14. ;
  15. ; This program has been intentionally complicated
  16. ; to demonstrate the use of multiple threads and semaphores
  17. ; in a MASM application.  For a roadmap to what is going
  18. ; on in this program, see its counterpart DUMP.C.
  19. ;
  20.  
  21. cr      equ     0dh             ; ASCII carriage return
  22. lf      equ     0ah             ; ASCII line feed
  23. blank   equ     20h             ; ASCII space code
  24. tab     equ     09h             ; ASCII tab code
  25.  
  26. recsize equ     16              ; size of input file records
  27. stksize equ     2048            ; size of stack for threads     
  28.  
  29. stdout  equ     1               ; handle of standard output device
  30. stderr  equ     2               ; handle of standard error device
  31.  
  32.         extrn   DOSOPEN:far     ; references to OS/2 services
  33.         extrn   DOSREAD:far
  34.         extrn   DOSWRITE:far
  35.         extrn   DOSCLOSE:far
  36.         extrn   DOSEXIT:far
  37.         extrn   DOSSEMCLEAR:far
  38.         extrn   DOSSEMSET:far
  39.         extrn   DOSSEMWAIT:far
  40.         extrn   DOSALLOCSEG:far
  41.         extrn   DOSCREATETHREAD:far
  42.         extrn   DOSSUSPENDTHREAD:far
  43.         extrn   DOSENTERCRITSEC:far
  44.         extrn   DOSEXITCRITSEC:far
  45.         extrn   DOSGETENV:far
  46.  
  47. DGROUP  group   _DATA
  48.  
  49. _DATA           segment word public 'DATA'
  50.  
  51. ExitSem         dd      0               ; storage for RAM semaphores
  52. Buf1FullSem     dd      0
  53. Buf1EmptySem    dd      0
  54. Buf2FullSem     dd      0
  55. Buf2EmptySem    dd      0
  56.  
  57. DisplayThrID    dw      0               ; Display thread ID
  58. DiskThrID       dw      0               ; Disk I/O thread ID
  59.  
  60. Buf1            db      recsize dup (0) ; disk I/O buffer #1
  61. Buf1Len         dw      0               ; length of buffer #1 data 
  62.  
  63. Buf2            db      recsize dup (0) ; disk I/O buffer #2    
  64. Buf2Len         dw      0               ; length of buffer #2 data 
  65.  
  66. fname           db      64 dup (0)      ; ASCIIZ name of input file
  67.  
  68. fhandle         dw      0               ; handle for input file
  69.  
  70. filptr          dw      0               ; relative address in file 
  71.  
  72. status          dw      0               ; receives status of DOSOPEN
  73.  
  74. selector        dw      0               ; receives segment selector 
  75.                                         ; from DOSALLOCSEG
  76.  
  77.                                         ; formatting area for output
  78. output          db      'nnnn',blank,blank
  79. outputa         db      16 dup ('nn',blank)
  80.                 db      blank
  81. outputb         db      16 dup (blank),cr,lf
  82. output_len      equ     $-output
  83.  
  84. heading         db      cr,lf           ; heading for each 128 bytes
  85.                 db      7 dup (blank)
  86.                 db      '0  1  2  3  4  5  6  7  '
  87.                 db      '8  9  A  B  C  D  E  F',cr,lf
  88. heading_len     equ     $-heading
  89.  
  90. msg1            db      cr,lf
  91.                 db      'dump: file not found'
  92.                 db      cr,lf
  93. msg1_len        equ     $-msg1
  94.  
  95. msg2            db      cr,lf
  96.                 db      'dump: missing file name'
  97.                 db      cr,lf
  98. msg2_len        equ     $-msg2
  99.  
  100. msg3            db      cr,lf
  101.                 db      'dump: memory allocation error'
  102.                 db      cr,lf
  103. msg3_len        equ     $-msg3   
  104.  
  105. msg4            db      cr,lf
  106.                 db      'dump: create thread failed'
  107.                 db      cr,lf
  108. msg4_len        equ     $-msg4  
  109.  
  110. _DATA           ends    
  111.  
  112.  
  113. _TEXT   segment word public 'CODE'
  114.  
  115.         assume  cs:_TEXT,ds:DGROUP
  116.  
  117. dump    proc    far                     ; entry point from OS/2
  118.  
  119.         call    argc                    ; is filename present?
  120.         cmp     ax,2
  121.         je      dump1                   ; yes, proceed 
  122.  
  123.         mov     dx,offset msg2          ; missing or illegal filespec,
  124.         mov     cx,msg2_len
  125.         jmp     dump9                   ; print error message and exit.
  126.  
  127. dump1:                                  ; copy filename to local buffer
  128.         mov     ax,1                    ; get ES:BX = filename
  129.         call    argv
  130.         mov     cx,ax                   ; set CX = length
  131.         mov     di,offset fname         ; DS:DI = local buffer
  132. dump15: mov     al,es:[bx]              ; copy it byte by byte
  133.         mov     [di],al
  134.         inc     bx
  135.         inc     di
  136.         loop    dump15  
  137.  
  138.         push    ds                      ; set ES = DS
  139.         pop     es
  140.  
  141. dump2:                                  ; now try to open file...
  142.         push    ds                      ; ASCIIZ file name
  143.         push    offset DGROUP:fname
  144.         push    ds                      ; receives handle
  145.         push    offset DGROUP:fhandle
  146.         push    ds                      ; receives handle
  147.         push    offset DGROUP:status
  148.         push    0                       ; file size (ignored)
  149.         push    0
  150.         push    0                       ; file attribute = normal
  151.         push    1                       ; OpenFlag = fail if doesn't exist
  152.         push    40h                     ; OpenMode = deny none,read only
  153.         push    0                       ; DWORD reserved
  154.         push    0
  155.         call    DOSOPEN                 ; transfer to OS/2
  156.         or      ax,ax                   ; test status
  157.         jz      dump3                   ; jump if open succeeded
  158.  
  159.         mov     dx,offset msg1          ; open of input file failed,
  160.         mov     cx,msg1_len
  161.         jmp     dump9                   ; print error msg and exit.
  162.  
  163. dump3:                                  ; initialize semaphores
  164.         push    ds
  165.         push    offset DGROUP:ExitSem
  166.         call    DOSSEMSET
  167.  
  168.         push    ds
  169.         push    offset DGROUP:Buf1FullSem
  170.         call    DOSSEMSET
  171.  
  172.         push    ds
  173.         push    offset DGROUP:Buf2FullSem
  174.         call    DOSSEMSET
  175.  
  176.                                         ; allocate Disk Thread stack
  177.         push    stksize                 ; size of stack
  178.         push    ds                      ; receives selector for
  179.         push    offset DGROUP:selector  ;   allocated block
  180.         push    0                       ; 0 = segment not shareable
  181.         call    DOSALLOCSEG             ; transfer to OS/2
  182.         or      ax,ax                   ; test status
  183.         jz      dump5                   ; jump if allocation succeeded
  184.  
  185. dump4:  mov     dx,offset DGROUP:msg3   ; display message
  186.         mov     cx,msg3_len             ; 'memory allocation error'
  187.         jmp     dump9                   ; and exit
  188.  
  189. dump5:                                  ; create Disk Thread
  190.         push    cs                      ; thread's entry point
  191.         push    offset _TEXT:DiskThread
  192.         push    ds                      ; receives thread ID
  193.         push    offset DGROUP:DiskThrID 
  194.         push    selector                ; thread's stack base
  195.         push    stksize
  196.         call    DOSCREATETHREAD         ; transfer to OS/2
  197.         or      ax,ax                   ; test status
  198.         jz      dump7                   ; jump if create succeeded
  199.  
  200. dump6:  mov     dx,offset DGROUP:msg4   ; create of thread failed,
  201.         mov     cx,msg4_len             ; display error message
  202.         jmp     dump9                   ; and exit
  203.  
  204. dump7:                                  ; allocate Display Thread stack
  205.         push    stksize                 ; size of stack
  206.         push    ds                      ; receives selector for
  207.         push    offset DGROUP:selector  ;   allocated block
  208.         push    0                       ; 0 = segment not shareable
  209.         call    DOSALLOCSEG             ; transfer to OS/2
  210.         or      ax,ax                   ; test status
  211.         jnz     dump4                   ; jump if allocation failed
  212.  
  213.                                         ; create Display Thread
  214.         push    cs                      ; thread's entry point
  215.         push    offset _TEXT:DisplayThread
  216.         push    ds                      ; receives thread ID
  217.         push    offset DGROUP:DisplayThrID      
  218.         push    selector                ; thread's stack base
  219.         push    stksize
  220.         call    DOSCREATETHREAD         ; transfer to OS/2
  221.         or      ax,ax                   ; test status
  222.         jnz     dump6                   ; jump if create failed
  223.  
  224.         push    ds                      ; now wait on exit semaphore
  225.         push    offset DGROUP:ExitSem   ; (it will be triggered
  226.         push    -1                      ; by routine DumpRec when
  227.         push    -1                      ; end of file is reached)
  228.         call    DOSSEMWAIT              ; transfer to OS/2
  229.  
  230.         push    DiskThrID               ; suspend Disk Thread
  231.         call    DOSSUSPENDTHREAD        ; transfer to OS/2
  232.  
  233.         push    DisplayThrID            ; suspend Display Thread
  234.         call    DOSSUSPENDTHREAD        ; transfer to OS/2
  235.  
  236.         push    fhandle                 ; close the input file
  237.         call    DOSCLOSE                ; transfer to OS/2
  238.  
  239.         push    1                       ; terminate all threads
  240.         push    0                       ; return code 0 for success
  241.         call    DOSEXIT                 ; final exit to OS/2
  242.  
  243. dump9:                                  ; print error message...
  244.         push    stderr                  ; standard error device handle
  245.         push    ds                      ; address of message
  246.         push    dx
  247.         push    cx                      ; length of message
  248.         push    ds                      ; receives bytes written
  249.         push    offset DGROUP:status
  250.         call    DOSWRITE                ; transfer to OS/2
  251.         
  252.         push    1                       ; terminate all threads
  253.         push    1                       ; return code <>0 for error
  254.         call    DOSEXIT                 ; final exit to OS/2
  255.         
  256. dump    endp
  257.  
  258.  
  259. DiskThread proc far                     ; this thread performs 
  260.                                         ; the file I/O, alternating
  261.                                         ; between the two buffers
  262.  
  263.                                         ; fill buffer #1
  264.         push    fhandle                 ; handle for input file
  265.         push    ds                      ; address of buffer #1
  266.         push    offset DGROUP:Buf1      
  267.         push    recsize                 ; record length requested
  268.         push    ds                      ; receives bytes read
  269.         push    offset DGROUP: Buf1Len
  270.         call    DOSREAD
  271.  
  272.                                         ; signal buffer 1 has data
  273.         mov     si,offset DGROUP:Buf1EmptySem
  274.         mov     di,offset DGROUP:Buf1FullSem
  275.         call    SemFlip
  276.         
  277.         push    ds                      ; wait until buffer 2 empty
  278.         push    offset DGROUP:Buf2EmptySem
  279.         push    -1
  280.         push    -1
  281.         call    DOSSEMWAIT
  282.  
  283.                                         ; fill buffer #2
  284.         push    fhandle                 ; handle for input file
  285.         push    ds                      ; address of buffer #1
  286.         push    offset DGROUP:Buf2
  287.         push    recsize                 ; record length requested
  288.         push    ds                      ; receives bytes read
  289.         push    offset DGROUP:Buf2Len
  290.         call    DOSREAD
  291.  
  292.                                         ; signal buffer 2 has data
  293.         mov     si,offset DGROUP:Buf2EmptySem
  294.         mov     di,offset DGROUP:Buf2FullSem
  295.         call    SemFlip
  296.         
  297.         push    ds                      ; wait until buffer 1 empty
  298.         push    offset DGROUP:Buf1EmptySem
  299.         push    -1
  300.         push    -1
  301.         call    DOSSEMWAIT
  302.  
  303.         jmp     DiskThread              ; do it again...
  304.  
  305. DiskThread endp
  306.  
  307.  
  308. DisplayThread proc far                  ; formats and displays disk
  309.                                         ; data, alternating between
  310.                                         ; the two disk buffers
  311.         
  312.         push    ds                      ; wait until buffer #1 full
  313.         push    offset DGROUP:Buf1FullSem
  314.         push    -1
  315.         push    -1
  316.         call    DOSSEMWAIT
  317.  
  318.         mov     si,offset DGROUP:Buf1   ; display buffer 1
  319.         mov     cx,Buf1Len
  320.         call    DumpRec
  321.  
  322.                                         ; signal buffer #1 is emptied
  323.         mov     si,offset DGROUP:Buf1FullSem
  324.         mov     di,offset DGROUP:Buf1EmptySem
  325.         call    SemFlip
  326.         
  327.         push    ds                      ; wait until buffer #2 full
  328.         push    offset DGROUP:Buf2FullSem
  329.         push    -1
  330.         push    -1
  331.         call    DOSSEMWAIT
  332.  
  333.         mov     si,offset DGROUP:Buf2   ; display buffer 2
  334.         mov     cx,Buf2Len
  335.         call    DumpRec
  336.  
  337.                                         ; signal buffer #2 is emptied
  338.         mov     si,offset DGROUP:Buf2FullSem
  339.         mov     di,offset DGROUP:Buf2EmptySem
  340.         call    SemFlip
  341.         
  342.         jmp     DisplayThread           ; do it again...        
  343.  
  344. DisplayThread endp
  345.  
  346.  
  347. SemFlip proc    near                    ; Flip status of two 
  348.                                         ; semaphores atomically
  349.  
  350.         call    DOSENTERCRITSEC         ; protect this code sequence
  351.  
  352.         push    ds                      ; set semaphore #1
  353.         push    si
  354.         call    DOSSEMSET
  355.  
  356.         push    ds                      ; clear semaphore #2
  357.         push    di
  358.         call    DOSSEMCLEAR
  359.  
  360.         call    DOSEXITCRITSEC          ; let other threads run again
  361.         ret
  362.  
  363. SemFlip endp
  364.  
  365.  
  366. DumpRec proc    near                    ; formats and displays 
  367.                                         ; contents of buffer
  368.                                         ; DS:SI = buffer, CX = length
  369.  
  370.         or      cx,cx                   ; anything to format?
  371.         jnz     DumpRec1                ; yes, continue
  372.         
  373.         push    ds                      ; no, clear exit semaphore
  374.         push    offset DGROUP:ExitSem   ; (releasing wait condition
  375.         call    DOSSEMCLEAR             ; for main thread)
  376.  
  377.         push    0                       ; and terminate this thread
  378.         push    0
  379.         call    DOSEXIT
  380.  
  381. DumpRec1:                               ; time for a heading?
  382.         test    filptr,07fh             ; if 128 byte boundary
  383.         jnz     DumpRec2                ; no,jump
  384.  
  385.         push    stdout                  ; standard output device handle
  386.         push    ds                      ; address of heading text
  387.         push    offset DGROUP:heading
  388.         push    heading_len             ; length of heading
  389.         push    ds                      ; receives bytes written
  390.         push    offset DGROUP:status
  391.         call    DOSWRITE        
  392.  
  393. DumpRec2:                               ; format record data...
  394.         push    cx                      ; save record length 
  395.  
  396.         mov     di,offset output        ; first clear output area
  397.         mov     cx,output_len-2
  398.         mov     al,blank
  399.         rep stosb
  400.  
  401.         mov     di,offset output        ; convert current file offset
  402.         mov     ax,filptr               ; to ASCII for output
  403.         call    w2hex
  404.  
  405.         pop     cx                      ; get back record length
  406.         mov     bx,0                    ; initialize record pointer
  407.  
  408. DumpRec3:                               ; fetch next byte from buffer
  409.         mov     al,[si+bx]
  410.                                         ; store ASCII version of character
  411.         mov     di,offset outputb       ; calculate output string address
  412.         mov     byte ptr [di+bx],'.'    ; if not alphanumeric
  413.         cmp     al,blank                ; just print a dot.     
  414.         jb      DumpRec4                ; jump, not alphanumeric.
  415.         cmp     al,7eh          
  416.         ja      DumpRec4                ; jump, not alphanumeric.
  417.         mov     [di+bx],al              ; else store ASCII character.
  418.  
  419. DumpRec4:                               ; now convert binary byte
  420.                                         ; to hex ASCII equivalent
  421.         mov     di,offset outputa       ; calc. position in output string
  422.         add     di,bx                   ; base addr + (offset*3)
  423.         add     di,bx
  424.         add     di,bx                   ; convert data in AL to hex
  425.         call    b2hex                   ; ASCII and store into output
  426.  
  427.         inc     bx                      ; bump data pointer and loop
  428.         loop    DumpRec3                ; until entire record converted
  429.  
  430.                                         ; now display formatted data
  431.         push    stdout                  ; standard output device handle
  432.         push    ds                      ; address of text
  433.         push    offset DGROUP:output                    
  434.         push    output_len              ; length of text
  435.         push    ds
  436.         push    offset DGROUP:status    ; receives bytes written
  437.         call    DOSWRITE
  438.  
  439.         add     word ptr filptr,recsize ; update file pointer
  440.  
  441.         ret                             ; return to caller
  442.  
  443. DumpRec endp
  444.  
  445.  
  446. argc    proc    near                    ; count command line arguments
  447.                                         ; returns count in AX
  448.  
  449.         enter   4,0                     ; make room for local variables
  450.                                         ; and give them names...
  451. envseg  equ     [bp-2]                  ; environment segment
  452. cmdoffs equ     [bp-4]                  ; command line offset   
  453.  
  454.         push    es                      ; save original ES,BX, and CX
  455.         push    bx
  456.         push    cx
  457.  
  458.         push    ss                      ; get selector for environment 
  459.         lea     ax,envseg               ; and offset of command line 
  460.         push    ax
  461.         push    ss
  462.         lea     ax,cmdoffs
  463.         push    ax
  464.         call    DOSGETENV               ; transfer to OS/2      
  465.         or      ax,ax                   ; check operation status
  466.         mov     ax,1                    ; force argc >= 1
  467.         jnz     argc3                   ; inexplicable failure
  468.  
  469.         mov     es,envseg               ; set ES:BX = command line
  470.         mov     bx,cmdoffs
  471.  
  472. argc0:  inc     bx                      ; ignore useless first field
  473.         cmp     byte ptr es:[bx],0      
  474.         jne     argc0
  475.  
  476. argc1:  mov     cx,-1                   ; set flag = outside argument
  477.  
  478. argc2:  inc     bx                      ; point to next character 
  479.         cmp     byte ptr es:[bx],0
  480.         je      argc3                   ; exit if null byte
  481.         cmp     byte ptr es:[bx],blank
  482.         je      argc1                   ; outside argument if ASCII blank
  483.         cmp     byte ptr es:[bx],tab    
  484.         je      argc1                   ; outside argument if ASCII tab
  485.  
  486.                                         ; otherwise not blank or tab,
  487.         jcxz    argc2                   ; jump if already inside argument
  488.  
  489.         inc     ax                      ; else found argument, count it
  490.         not     cx                      ; set flag = inside argument
  491.         jmp     argc2                   ; and look at next character
  492.  
  493. argc3:  pop     cx                      ; restore original BX, CX, ES
  494.         pop     bx
  495.         pop     es
  496.         leave                           ; discard local variables
  497.         ret                             ; return AX = argument count
  498.  
  499. argc    endp
  500.  
  501.  
  502. argv    proc    near                    ; get address and length
  503.                                         ; of command line arguments
  504.                                         ; call with AX = arg. no.
  505.                                         ; return ES:BX = address of
  506.                                         ; argument string, CX = length
  507.  
  508.         enter   4,0                     ; make room for local variables
  509.  
  510.         push    cx                      ; save original CX and DI 
  511.         push    di
  512.  
  513.         push    ax                      ; save argument number
  514.  
  515.         push    ss                      ; get selector for environment 
  516.         lea     ax,envseg               ; and offset of command line 
  517.         push    ax
  518.         push    ss
  519.         lea     ax,cmdoffs
  520.         push    ax
  521.         call    DOSGETENV               ; transfer to OS/2      
  522.         or      ax,ax                   ; test operation status
  523.         pop     ax                      ; restore argument number
  524.         jnz     argv7                   ; jump if DOSGETENV failed
  525.  
  526.         mov     es,envseg               ; set ES:BX = command line
  527.         mov     bx,cmdoffs
  528.  
  529.         or      ax,ax                   ; is requested argument=0?
  530.         jz      argv8                   ; yes, jump to get program name
  531.  
  532. argv0:  inc     bx                      ; scan off first field
  533.         cmp     byte ptr es:[bx],0      
  534.         jne     argv0
  535.  
  536.         xor     ah,ah                   ; initialize argument counter
  537.  
  538. argv1:  mov     cx,-1                   ; set flag = outside argument
  539.  
  540. argv2:  inc     bx                      ; point to next character 
  541.         cmp     byte ptr es:[bx],0
  542.         je      argv7                   ; exit if null byte
  543.         cmp     byte ptr es:[bx],blank
  544.         je      argv1                   ; outside argument if ASCII blank
  545.         cmp     byte ptr es:[bx],tab    
  546.         je      argv1                   ; outside argument if ASCII tab
  547.  
  548.                                         ; if not blank or tab...
  549.         jcxz    argv2                   ; jump if already inside argument
  550.  
  551.         inc     ah                      ; else count arguments found
  552.         cmp     ah,al                   ; is this the one we need?
  553.         je      argv4                   ; yes, go find its length
  554.         not     cx                      ; no, set flag = inside argument
  555.         jmp     argv2                   ; and look at next character
  556.  
  557. argv4:                                  ; found desired argument, now
  558.                                         ; determine its length...
  559.         mov     ax,bx                   ; save param. starting address 
  560.  
  561. argv5:  inc     bx                      ; point to next character
  562.         cmp     byte ptr es:[bx],0
  563.         je      argv6                   ; found end if null byte
  564.         cmp     byte ptr es:[bx],blank
  565.         je      argv6                   ; found end if ASCII blank
  566.         cmp     byte ptr es:[bx],tab    
  567.         jne     argv5                   ; found end if ASCII tab
  568.  
  569. argv6:  xchg    bx,ax                   ; set ES:BX = argument address
  570.         sub     ax,bx                   ; and AX = argument length
  571.         jmp     argvx                   ; return to caller
  572.  
  573. argv7:  xor     ax,ax                   ; set AX = 0, argument not found
  574.         jmp     argvx                   ; return to caller
  575.  
  576. argv8:                                  ; special handling for argv=0
  577.         xor     di,di                   ; find the program name by
  578.         xor     al,al                   ; first skipping over all the
  579.         mov     cx,-1                   ; environment variables...
  580.         cld
  581. argv9:  repne scasb                     ; scan for double null (can't use
  582.         scasb                           ; (SCASW since might be odd addr.)
  583.         jne     argv9                   ; loop if it was a single null
  584.         mov     bx,di                   ; save program name address
  585.         mov     cx,-1                   ; now find its length... 
  586.         repne scasb                     ; scan for another null byte
  587.         not     cx                      ; convert CX to length 
  588.         dec     cx
  589.         mov     ax,cx                   ; return length in AX
  590.  
  591. argvx:                                  ; common exit point
  592.         pop     di                      ; restore original CX and DI
  593.         pop     cx
  594.         leave                           ; discard stack frame
  595.         ret                             ; return to caller
  596.  
  597. argv    endp
  598.  
  599.  
  600. w2hex   proc    near                    ; convert word to hex ASCII
  601.                                         ; call with AX=binary value
  602.                                         ;           DI=addr to store string
  603.                                         ; returns AX, DI destroyed
  604.         push    ax
  605.         mov     al,ah
  606.         call    b2hex                   ; convert upper byte    
  607.         pop     ax
  608.         call    b2hex                   ; convert lower byte
  609.         ret                             ; back to caller
  610.  
  611. w2hex   endp
  612.  
  613.  
  614. b2hex   proc    near                    ; convert byte to hex ASCII
  615.                                         ; call with AL=binary value
  616.                                         ;           DI=addr to store string
  617.                                         ; returns   AX, DI destroyed
  618.  
  619.         push    cx                      ; save CX for later
  620.         sub     ah,ah                   ; clear upper byte
  621.         mov     cl,16
  622.         div     cl                      ; divide binary data by 16
  623.         call    ascii                   ; the quotient becomes the first
  624.         stosb                           ; ASCII character
  625.         mov     al,ah
  626.         call    ascii                   ; the remainder becomes the
  627.         stosb                           ; second ASCII character
  628.         pop     cx                      ; restore contents of CX
  629.         ret
  630.  
  631. b2hex   endp
  632.  
  633.  
  634. ascii   proc    near                    ; convert value 0-0FH in AL 
  635.                                         ; into a "hex ASCII" character
  636.         add     al,'0'                  
  637.         cmp     al,'9'
  638.         jle     ascii2                  ; jump if in range 0-9,
  639.         add     al,'A'-'9'-1            ; offset it to range A-F,
  640. ascii2: ret                             ; return ASCII char. in AL.
  641.  
  642. ascii   endp
  643.  
  644. _TEXT   ends
  645.         
  646.         end     dump
  647.