home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / os2 / os2ap / dump.asm next >
Encoding:
Assembly Source File  |  1991-11-15  |  25.5 KB  |  645 lines

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