home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / pksnd102.zip / TRACE.ASM < prev    next >
Assembly Source File  |  1997-01-06  |  20KB  |  971 lines

  1. version    equ    0
  2.  
  3.     include    defs.asm
  4.  
  5. ;  Copyright, 1988, 1989, Russell Nelson
  6.  
  7. ;   This program is free software; you can redistribute it and/or modify
  8. ;   it under the terms of the GNU General Public License as published by
  9. ;   the Free Software Foundation, version 1.
  10. ;
  11. ;   This program is distributed in the hope that it will be useful,
  12. ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. ;   GNU General Public License for more details.
  15. ;
  16. ;   You should have received a copy of the GNU General Public License
  17. ;   along with this program; if not, write to the Free Software
  18. ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. code    segment word public
  21.     assume    cs:code, ds:code
  22.  
  23.     org    2ch
  24. phd_env    label    word
  25.  
  26.     org    5ch
  27. phd_fcb1    label    byte
  28.  
  29.     org    6ch
  30. phd_fcb2    label    byte
  31.  
  32.     org    80h
  33. phd_dioa    label    byte
  34.  
  35.     org    100h
  36. start:
  37.     jmp    start_1
  38.  
  39. stack    label    byte
  40.  
  41. comment /
  42.  
  43. Plan:
  44.  
  45. Keep a circular queue of events.  The size of the queue is a settable
  46. parameter.  Discard events that fall off the end.  Remember how many events
  47. were discarded.  Remember when the events occurred.
  48.  
  49. Type of events to remember:
  50.     calls to the packet driver
  51.     upcalls to the receiver handler (both kinds)
  52.  
  53. /
  54.  
  55. ; In addition to the function numbers specified in the FTP Software
  56. ; packet driver spec, the following pseudo-functions are defined:
  57.  
  58. EVENT_RECEIVE    equ    255        ;receiver upcall.
  59.  
  60. event_struc    struc
  61. event_length    dw    ?        ;length of this event.
  62. event_function    db    ?        ;the event function number.
  63. event_time    dw    ?,?        ;timer tick at the time of the call.
  64. event_error    db    ?        ;set to value of dh after the SWI.
  65. event_struc    ends
  66.  
  67. di_struc    struc
  68.         db    (size event_struc) dup (?)
  69. di_version    dw    ?
  70. di_class    db    ?
  71. di_type        dw    ?
  72. di_number    db    ?
  73. di_basic    db    ?
  74. di_struc    ends
  75.  
  76. at_struc    struc
  77.         db    (size event_struc) dup (?)
  78. at_if_class    db    ?
  79. at_if_type    dw    ?
  80. at_if_number    db    ?
  81. at_typelen    dw    ?
  82. at_handle    dw    ?
  83. at_struc    ends
  84.  
  85. handle_struc    struc
  86.         db    (size event_struc) dup (?)
  87. event_handle    dw    ?
  88. handle_struc    ends
  89.  
  90. ga_struc    struc
  91.         db    (size event_struc) dup (?)
  92. ga_handle    dw    ?
  93. ga_length    dw    ?
  94. ga_struc    ends
  95.  
  96. srm_struc    struc
  97.         db    (size event_struc) dup (?)
  98. srm_mode    dw    ?
  99. srm_handle    dw    ?
  100. srm_struc    ends
  101.  
  102. queue_length    dw    10000,?        ;length of the queue.
  103. queue_tail    dw    ?        ;points after the last item in the queue.
  104. queue_ptr    dw    ?        ;points to the new item in the queue.
  105. queue_head    dw    ?        ;points to the first item in the queue.
  106. queue_end    dw    ?        ;points to the end of the queue, but
  107.                     ;there is room for one more event after
  108.                     ;this one.
  109.  
  110. packet_int_no    db    ?,0,0,0
  111.  
  112. parm    dw    0
  113. parm2    dw    their_dioa,?
  114. parm3    dw    phd_fcb1,?
  115. parm4    dw    phd_fcb2,?
  116.  
  117. comspec_env_str    db    "COMSPEC="
  118. comspec_env_len    equ    $-comspec_env_str
  119.  
  120. program        db    64 dup(?)
  121. their_dioa    db    0,0dh,128-2 dup(?)
  122.  
  123. saved_ax    label    word
  124. saved_al    db    ?
  125. saved_ah    db    ?
  126. saved_bx    dw    ?
  127. saved_ds    dw    ?
  128. saved_f        dw    ?
  129.  
  130. functions    label    word
  131.     dw    f_driver_info        ;function 1
  132.     dw    f_access_type
  133.     dw    f_release_type
  134.     dw    f_send_pkt
  135.     dw    f_terminate
  136.     dw    f_get_address
  137.     dw    f_reset_interface    ;function 7
  138.     dw    f_set_rcv_mode        ;function 20
  139.     dw    f_get_rcv_mode
  140.     dw    f_set_multicast_list
  141.     dw    f_get_multicast_list
  142.     dw    f_get_statistics
  143.     dw    f_set_address        ;function 25
  144.  
  145. their_isr    dd    ?
  146.  
  147. our_isr:
  148.     jmp    our_isr_0        ;the required signature.
  149. signature    db    'PKT DRVR',0
  150. signature_len    equ    $-signature
  151.  
  152. our_isr_0:
  153.     assume    ds:nothing
  154.     mov    saved_ds,ds
  155.     mov    saved_bx,bx
  156.     mov    saved_ax,ax
  157.     cld
  158.  
  159.     mov    bx,sp
  160.     mov    bx,ss:[bx+4]        ;get the original flags.
  161.     mov    saved_f,bx
  162.  
  163.  
  164.     mov    bx,cs            ;set up ds.
  165.     mov    ds,bx
  166.     assume    ds:code
  167.  
  168. ;the following code runs with ax, bx, ds, and flags saved in save_*.
  169. ;otherwise, all the registers are the same as those we were called with.
  170.  
  171.     mov    bl,ah            ;jump to the correct function.
  172.     mov    bh,0
  173.     cmp    bx,7            ;highest function is 7.
  174.     jbe    our_isr_3
  175.     cmp    bx,20
  176.     jb    our_isr_bad
  177.     cmp    bx,25
  178.     ja    our_isr_bad
  179.     sub    bx,20-7-1        ;map 20 right after 7.
  180. our_isr_3:
  181.     add    bx,bx            ;*2
  182.     jmp    functions-2[bx]        ;table starts at 1.
  183.  
  184. our_isr_bad:
  185.     call    do_their_isr
  186.     jmp    our_isr_done
  187.  
  188. f_driver_info:
  189.     mov    bx,(size di_struc)
  190.     call    queue_advance
  191.     call    do_their_isr
  192.     jc    f_driver_info_1
  193.  
  194.     mov    ax,saved_bx
  195.     mov    [bx].di_version,ax
  196.     mov    [bx].di_class,ch
  197.     mov    [bx].di_type,dx
  198.     mov    [bx].di_number,cl
  199.     mov    al,saved_al
  200.     mov    [bx].di_basic,al
  201. ;we ignore the name -- too much work.
  202. f_driver_info_1:
  203.     jmp    our_isr_done
  204.  
  205. f_get_statistics:
  206. ;strictly speaking, we should remember the statistics, but I'm not going to now.
  207. f_terminate:
  208. f_reset_interface:
  209. f_release_type:
  210. f_get_rcv_mode:
  211.     mov    bx,(size handle_struc)
  212.     call    queue_advance
  213.     mov    ax,saved_bx
  214.     mov    [bx].event_handle,ax
  215.     call    do_their_isr
  216.     jmp    our_isr_done
  217.  
  218. f_access_type:
  219.     mov    bx,(size at_struc)
  220.     call    queue_advance
  221.     mov    al,saved_al
  222.     mov    [bx].at_if_class,al
  223.     mov    ax,saved_bx
  224.     mov    [bx].at_if_type,ax
  225.     mov    [bx].at_if_number,dl
  226.     mov    [bx].at_typelen,cx
  227.     mov    their_recv.segm,es
  228.     mov    their_recv.offs,di
  229.     mov    ax,cs            ;and stick our receiver in.
  230.     mov    es,ax
  231.     mov    di,offset our_recv
  232.     call    do_their_isr
  233.     jc    f_access_type_1
  234.     mov    ax,saved_ax
  235.     mov    [bx].at_handle,ax
  236. f_access_type_1:
  237.     les    di,their_recv        ;now restore ds and si.
  238.     jmp    our_isr_done
  239.  
  240.  
  241. f_send_pkt:
  242.     mov    bx,(size event_struc)
  243.     add    bx,cx
  244.     call    queue_advance
  245. ;make a copy of their packet.
  246.     push    cx
  247.     push    si
  248.     push    di
  249.     push    ds
  250.     push    es
  251.     lea    di,[bx] + (size event_struc)
  252.     mov    ax,cs
  253.     mov    es,ax
  254.     mov    ds,saved_ds
  255.     rep    movsb
  256.     pop    es
  257.     pop    ds
  258.     pop    di
  259.     pop    si
  260.     pop    cx
  261.     call    do_their_isr
  262.     jmp    our_isr_done
  263.  
  264. f_get_address:
  265.     mov    bx,(size ga_struc)
  266.     add    bx,cx
  267.     call    queue_advance
  268.     mov    ax,saved_bx        ;save their handle
  269.     mov    [bx].ga_handle,ax
  270.     call    do_their_isr
  271.     jc    f_get_address_1
  272. ;make a copy of their address.
  273.     mov    [bx].ga_length,cx    ;we need to save this because it
  274.                     ;might be less than the total allocated.
  275.     push    cx
  276.     push    si
  277.     push    di
  278.     push    ds
  279.     push    es
  280.     mov    si,di            ;get es:di into ds:si
  281.     mov    ax,es
  282.     mov    ds,ax
  283.     lea    di,[bx] + (size ga_struc)    ;get our pointer into es:di.
  284.     mov    ax,cs
  285.     mov    es,ax
  286.     rep    movsb
  287.     pop    es
  288.     pop    ds
  289.     pop    di
  290.     pop    si
  291.     pop    cx
  292. f_get_address_1:
  293.     jmp    our_isr_done
  294.  
  295.  
  296. f_set_rcv_mode:
  297.     mov    bx,(size srm_struc)
  298.     add    bx,2
  299.     call    queue_advance
  300.     mov    ax,saved_bx        ;save their handle.
  301.     mov    [bx].srm_handle,ax
  302.     mov    [bx].srm_mode,cx    ;save their mode.
  303.     call    do_their_isr
  304.     jmp    our_isr_done
  305.  
  306. f_set_multicast_list:
  307. f_get_multicast_list:
  308. f_set_address:
  309.     mov    bx,(size event_struc)
  310.     call    queue_advance
  311.     call    do_their_isr
  312.  
  313. our_isr_done:
  314.     push    saved_f            ;restore their flags, see [2]
  315.     popf
  316.  
  317.     mov    ax,saved_ax
  318.     mov    bx,saved_bx
  319.     mov    ds,saved_ds        ;restore the two registers we destroyed.
  320.     assume    ds:nothing
  321.  
  322. foobar    proc    far            ;masm 4.0 is really stupid.
  323.     ret    2            ;return, popping their old flags, [2].
  324. foobar    endp
  325.     assume    ds:code
  326.  
  327.  
  328. ;do_their_isr executes their isr with the original registers.
  329. ;called with all their registers except f, ds, and bx.
  330. ;exits with all their registers except ds, ax, and bx.  bx is queue_ptr
  331. do_their_isr:
  332.  
  333. ;setup their context.
  334.     mov    ax,saved_f        ;restore their flags, see [1]
  335.     and    ax,not 200h        ;clear the interrupt flag, as required
  336.     push    ax            ;  when faking an interrupt.
  337.     mov    ax,saved_ax
  338.     mov    bx,saved_bx
  339.     mov    ds,saved_ds        ;restore the two registers we destroyed.
  340.     assume    ds:nothing
  341.  
  342. ;    [1] we pushed the flags earlier.
  343.     call    their_isr        ;now fake their interrupt.
  344.  
  345. ;save their context.
  346.     mov    saved_ax,ax        ;save the new registers.
  347.     mov    saved_bx,bx
  348.     mov    saved_ds,ds
  349.     mov    ax,cs            ;set up a pointer to the next event.
  350.     mov    ds,ax
  351.     assume    ds:code
  352.     pushf                ;save the new flags.
  353.     pop    ax
  354.     and    saved_f,200h        ;merge the interrupt flag in saved_f
  355.     or    saved_f,ax        ;  with the new flags.
  356.  
  357. ;remember whether it succeeded or not.
  358.     mov    bx,queue_ptr
  359.     mov    [bx].event_error,NO_ERROR    ;assume that all was okay.
  360.     jnc    our_isr_1
  361.     mov    [bx].event_error,dh    ;it wasn't.
  362. our_isr_1:
  363.     ret
  364.  
  365.  
  366. their_recv    dd    ?
  367.  
  368. our_recv:
  369.     assume    ds:nothing
  370.     mov    saved_ds,ds
  371.     mov    saved_bx,bx
  372.     mov    saved_ax,ax
  373.     cld
  374.  
  375.     mov    bx,cs            ;set up ds.
  376.     mov    ds,bx
  377.     assume    ds:code
  378.  
  379.     or    ax,ax            ;first call or second?
  380.     je    our_recv_first
  381.  
  382.     mov    bx,(size handle_struc)
  383.     add    bx,cx
  384.     call    queue_advance
  385.     mov    [bx].event_function,EVENT_RECEIVE    ;not a real function.
  386.     mov    [bx].event_error,0        ;no errors possible.
  387.     mov    ax,saved_bx
  388.     mov    [bx].event_handle,ax    ;remember which handle it was.
  389.  
  390.     push    cx
  391.     push    si
  392.     push    di
  393.     push    ds
  394.     push    es
  395.     lea    di,[bx] + (size handle_struc)
  396.     mov    ax,cs
  397.     mov    es,ax
  398.     mov    ds,saved_ds
  399.     rep    movsb
  400.     pop    es
  401.     pop    ds
  402.     pop    di
  403.     pop    si
  404.     pop    cx
  405.  
  406.     jmp    short our_recv_done
  407. our_recv_first:
  408. ;ignore the first upcall.
  409. our_recv_done:
  410.     mov    ax,saved_ax
  411.     mov    bx,saved_bx
  412.     mov    ds,saved_ds        ;restore the two registers we destroyed.
  413.     assume    ds:nothing
  414.  
  415.     jmp    their_recv
  416.     assume    ds:code
  417.  
  418.  
  419. queue_advance:
  420. ;enter with bx = number of bytes that we require in the queue.
  421. ;exit with bx,queue_ptr set to a pointer to our entry.
  422. ;preserve everything but ds, bx, and the flags.
  423. queue_advance_3:
  424.     mov    ax,queue_tail
  425.     add    ax,bx
  426.     cmp    ax,queue_head        ;if we don't overlap the head, we're
  427.     jbe    queue_advance_1        ;  okay.
  428.  
  429.     xchg    bx,queue_head        ;get queue_head and save bx.
  430.     add    bx,[bx].event_length
  431.     cmp    bx,queue_end        ;see if we hit the end.
  432.     xchg    queue_head,bx        ;store queue_head and restore bx.
  433.     jb    queue_advance_3        ;if we're less than the end, continue.
  434. ;we have to wrap here.
  435.     push    bx
  436.     mov    bx,queue_tail
  437.     mov    ax,queue_end        ;make an event length that's too large.
  438.     sub    ax,offset queue_begin
  439.     mov    [bx].event_length,ax
  440.     mov    bx,offset queue_begin    ;and restart from the beginning.
  441.     mov    queue_tail,bx        ;ensure that we nuke some more.
  442.     mov    queue_head,bx
  443.     pop    bx
  444.     jmp    queue_advance_3
  445. queue_advance_1:
  446.     xchg    ax,queue_tail        ;update the tail and get this ptr.
  447.     mov    queue_ptr,ax        ;save this pointer.
  448.     xchg    ax,bx
  449.     mov    [bx].event_length,ax    ;store the length of this entry here.
  450.  
  451.     mov    ah,saved_ah        ;store their function value.
  452.     mov    [bx].event_function,ah
  453.  
  454. ;remember when it happened.
  455.     push    dx
  456.     push    ds
  457.     mov    ax,40h
  458.     mov    ds,ax
  459.     mov    ax,ds:6ch        ;get the timer tick count.
  460.     mov    dx,ds:6eh
  461.     pop    ds
  462.     mov    [bx].event_time+0,ax
  463.     mov    [bx].event_time+2,dx
  464.     pop    dx
  465.  
  466.     ret
  467.  
  468.  
  469. copyleft_msg    label    byte
  470.  db "Packet driver tracer version ",majver+'0','.',version+'0'," copyright 1988-89, Russell Nelson.",CR,LF
  471.  db "This program is free software; see the file COPYING for details.",CR,LF
  472.  db "NO WARRANTY; see the file COPYING for details.",CR,LF
  473. crlf_msg    db    CR,LF,'$'
  474.  
  475. packet_int_no_name    db    "Packet interrupt number ",'$'
  476. buffer_size_name    db    "Buffer size ",'$'
  477.  
  478. before_exec_msg    db    "Now run your network software and type 'exit' when finished",CR,LF,'$'
  479. run_dump_msg    db    "Now run 'dump' to interpret 'trace.out'",CR,LF,'$'
  480.  
  481. disk_full_msg    db    "Disk Full!",'$'
  482.  
  483. already_msg    db    CR,LF,"There is no packet driver at ",'$'
  484. packet_int_msg    db    CR,LF
  485.         db    "Error: <packet_int_no> should be in the range 0x60 to 0x80"
  486.         db    '$'
  487.  
  488. usage_msg    db    "usage: trace packet_int_no <buffer_size>",'$'
  489.  
  490. queue_error_msg    db    "Error: <buffer_size> should be larger than 2000 and less than 64000",'$'
  491.  
  492. trace_out    db    "TRACE.OUT",0    ;filename that we write the dump to.
  493.  
  494. HT    equ    09h
  495. CR    equ    0dh
  496. LF    equ    0ah
  497.  
  498. usage_error:
  499.     mov    dx,offset usage_msg
  500. error:
  501.     mov    ah,9
  502.     int    21h
  503.     int    20h
  504.  
  505. already_error:
  506.     mov    dx,offset already_msg
  507.     mov    di,offset packet_int_no
  508.     call    print_number
  509.     int    20h
  510.  
  511. start_1:
  512.     mov    sp,offset stack
  513.  
  514.     mov    dx,offset copyleft_msg
  515.     mov    ah,9
  516.     int    21h
  517.  
  518.     mov    si,offset phd_dioa+1
  519.     cmp    byte ptr [si],CR    ;end of line?
  520.     je    usage_error
  521.  
  522.     mov    di,offset packet_int_no    ;parse the packet interrupt number
  523.     mov    bx,offset packet_int_no_name
  524.     call    get_number        ;  for them.
  525.  
  526.     mov    di,offset queue_length    ;parse the packet interrupt number
  527.     mov    bx,offset buffer_size_name
  528.     call    get_number        ;  for them.
  529.  
  530.     cmp    byte ptr [si],CR    ;end of line?
  531.     jne    usage_error
  532.  
  533.     cmp    queue_length+2,0
  534.     jne    start_3
  535.     cmp    queue_length,2000
  536.     ja    start_2
  537. start_3:
  538.     mov    dx,offset queue_error_msg
  539.     jmp    error
  540. start_2:
  541.  
  542. ;initialize the queue
  543.     mov    bx,offset queue_begin
  544.     mov    queue_tail,bx
  545.     add    bx,queue_length
  546.     mov    queue_end,bx        ;initialize the head of the queue.
  547.     mov    [bx].event_length,1    ;anything >0 will ensure that we're >end.
  548.     mov    queue_head,bx
  549.  
  550. ;do some error checking.
  551.     mov    dx,offset packet_int_msg;make sure that the packet interrupt
  552.     cmp    packet_int_no,60h    ;  number is in range.
  553.     jb    error
  554.     cmp    packet_int_no,80h
  555.     ja    error
  556.  
  557.     mov    ah,35h            ;get their packet interrupt.
  558.     mov    al,packet_int_no
  559.     int    21h
  560.  
  561.     lea    di,3[bx]        ;see if there is already a signature
  562.     mov    si,offset signature    ;  there.
  563.     mov    cx,signature_len
  564.     repe    cmpsb
  565.     je    start_4            ;yes, so we can trace it.
  566.     jmp    already_error        ;no, give them an error.
  567. start_4:
  568.  
  569.     mov    ah,35h            ;remember their packet interrupt.
  570.     mov    al,packet_int_no
  571.     int    21h
  572.     mov    their_isr.offs,bx
  573.     mov    their_isr.segm,es
  574.  
  575.     mov    ah,25h            ;install our packet interrupt
  576.     mov    dx,offset our_isr
  577.     int    21h
  578.  
  579.     mov    dx,offset before_exec_msg
  580.     mov    ah,9
  581.     int    21h
  582.  
  583. ;
  584. ; Now free the memory we don't need.
  585. ;
  586.     mov    bx,queue_end
  587.     add    bx,size event_struc    ;leave room for one more.
  588.     add    bx,0fh            ;round up to next highest paragraph.
  589.     mov    cl,4
  590.     shr    bx,cl
  591.     push    cs
  592.     pop    es
  593.     mov    ah,4ah
  594.     int    21h
  595.  
  596. ; Now we execute command.com
  597.  
  598.     mov    ax,cs
  599.     mov    word ptr parm2+2,ax
  600.     mov    word ptr parm3+2,ax
  601.     mov    word ptr parm4+2,ax
  602.  
  603.     mov    si,offset their_dioa+1    ;re-parse the two fcbs.
  604.     mov    di,offset phd_fcb1
  605.     push    ds
  606.     pop    es
  607.     mov    ax,2901h
  608.     int    21h
  609.  
  610.     mov    di,offset phd_fcb2
  611.     mov    ax,2901h
  612.     int    21h
  613.  
  614.     mov    si,offset comspec_env_str    ;see if this is the one.
  615.     mov    cx,comspec_env_len
  616.     mov    di,offset program
  617.     call    getenv
  618.  
  619.     mov    ah,4bh
  620.     mov    bx,offset parm
  621.     mov    dx,offset program
  622.     mov    al,0
  623.     int    21h
  624.  
  625.     mov    bx,cs            ;restore our segment registers.
  626.     mov    ds,bx
  627.     mov    es,bx
  628.     mov    ss,bx
  629.     mov    sp,offset stack
  630.  
  631. ; Give up our packet interception.
  632.  
  633.     mov    al,packet_int_no    ;release our_isr.
  634.     mov    ah,25h
  635.     push    ds
  636.     lds    dx,their_isr
  637.     int    21h
  638.     pop    ds
  639.  
  640. ; Now we write our captured information out to disk.
  641.  
  642.     mov    dx,offset trace_out    ;create "trace.out".
  643.     mov    ah,3ch
  644.     mov    cx,0
  645.     int    21h
  646.  
  647.     mov    bx,ax
  648.  
  649.     mov    si,queue_head
  650. write_out:
  651.     mov    dx,si
  652.     mov    cx,[si].event_length    ;write this event out.
  653.     add    si,cx            ;is this the end of the queue?
  654.     cmp    si,queue_end        ;
  655.     ja    write_out_1
  656.  
  657.     mov    ah,40h
  658.     int    21h
  659.     cmp    ax,cx
  660.     jne    write_out_full
  661.  
  662.     jmp    write_out
  663.  
  664. write_out_1:
  665.     mov    si,offset queue_begin    ;yes.
  666. write_out_2:
  667.     cmp    si,queue_tail        ;quit when we hit the tail.
  668.     jae    write_out_3
  669.  
  670.     mov    dx,si            ;set dx for the file write below.
  671.     mov    cx,[si].event_length    ;write this event out.
  672.     add    si,cx
  673.  
  674.     mov    ah,40h
  675.     int    21h
  676.     cmp    ax,cx
  677.     jne    write_out_full
  678.  
  679.     jmp    write_out_2
  680. write_out_3:
  681.     mov    ah,3eh            ;close the file.
  682.     int    21h
  683.  
  684.     mov    dx,offset run_dump_msg
  685.     mov    ah,9
  686.     int    21h
  687.  
  688.     int    20h
  689.  
  690. write_out_full:
  691.     mov    ah,9
  692.     mov    dx,offset disk_full_msg
  693.     int    21h
  694.     int    20h
  695.  
  696.  
  697. get_number:
  698.     mov    bp,10            ;we default to 10.
  699.     jmp    short get_number_0
  700.  
  701. get_hex:
  702.     mov    bp,16
  703. ;get a hex number, skipping leading blanks.
  704. ;enter with si->string of digits,
  705. ;    bx -> dollar terminated name of number,
  706. ;    di -> dword to store the number in.  [di] is not modified if no
  707. ;        digits are given, so it acts as the default.
  708. ;return cy if there are no digits at all.
  709. ;return nc, bx:cx = number, and store bx:cx at [di].
  710. get_number_0:
  711.     push    bx            ;remember the name of this number.
  712.     call    skip_blanks
  713.     call    get_digit        ;is there really a number here?
  714.     jc    get_number_3
  715.     or    al,al            ;Does the number begin with zero?
  716.     jne    get_number_4        ;no.
  717.     mov    bp,8            ;yes - they want octal.
  718. get_number_4:
  719.  
  720.     xor    cx,cx            ;get a hex number.
  721.     xor    bx,bx
  722. get_number_1:
  723.     lodsb
  724.     cmp    al,'x'            ;did they really want hex?
  725.     je    get_number_5        ;yes.
  726.     cmp    al,'X'            ;did they really want hex?
  727.     je    get_number_5        ;yes.
  728.     call    get_digit        ;convert a character into an int.
  729.     jc    get_number_2        ;not a digit (neither hex nor dec).
  730.     xor    ah,ah
  731.     cmp    ax,bp            ;larger than our base?
  732.     jae    get_number_2        ;yes.
  733.  
  734.     push    ax            ;save the new digit.
  735.  
  736.     mov    ax,bp            ;multiply the low word by ten.
  737.     mul    cx
  738.     mov    cx,ax            ;keep the low word.
  739.     push    dx            ;save the high word for later.
  740.     mov    ax,bp
  741.     mul    bx
  742.     mov    bx,ax            ;we keep only the low word (which is our high word)
  743.     pop    dx
  744.     add    bx,ax            ;add the high result from earlier.
  745.  
  746.     pop    ax            ;get the new digit back.
  747.     add    cx,ax            ;add the new digit in.
  748.     adc    bx,0
  749.     jmp    get_number_1
  750. get_number_5:
  751.     mov    bp,16            ;change the base to hex.
  752.     jmp    get_number_1
  753. get_number_2:
  754.     dec    si
  755.     mov    [di],cx            ;store the parsed number.
  756.     mov    [di+2],bx
  757.     clc
  758.     jmp    short get_number_6
  759. get_number_3:
  760.     stc
  761. get_number_6:
  762.     pop    dx            ;get the name of the number back.
  763.  
  764.     pushf                ;save some stuff.
  765.     push    bx
  766.     push    cx
  767.     push    si
  768.     push    di
  769.     call    print_number
  770.     pop    di
  771.     pop    si
  772.     pop    cx
  773.     pop    bx
  774.     popf
  775.     ret
  776.  
  777.  
  778. print_number:
  779. ;enter with dx -> dollar terminated name of number, di ->dword.
  780. ;exit with the number printed and the cursor advanced to the next line.
  781.     mov    ah,9            ;print the name of the number.
  782.     int    21h
  783.     mov    al,'0'
  784.     call    chrout
  785.     mov    al,'x'
  786.     call    chrout
  787.     mov    ax,[di]            ;print the number in hex.
  788.     mov    dx,[di+2]
  789.     call    hexout
  790.     mov    al,' '
  791.     call    chrout
  792.     mov    al,'('
  793.     call    chrout
  794.     mov    ax,[di]            ;print the number in decimal.
  795.     mov    dx,[di+2]
  796.     call    decout
  797.     mov    al,')'
  798.     call    chrout
  799.     mov    al,CR
  800.     call    chrout
  801.     mov    al,LF
  802.     call    chrout
  803.     ret
  804.  
  805.  
  806. skip_blanks:
  807.     lodsb                ;skip blanks.
  808.     cmp    al,' '
  809.     je    skip_blanks
  810.     cmp    al,HT
  811.     je    skip_blanks
  812.     dec    si
  813.     ret
  814.  
  815.  
  816. get_digit:
  817. ;enter with al = character
  818. ;return nc, al=digit, or cy if not a digit.
  819.     cmp    al,'0'            ;decimal digit?
  820.     jb    get_digit_1        ;no.
  821.     cmp    al,'9'            ;. .?
  822.     ja    get_digit_2        ;no.
  823.     sub    al,'0'
  824.     clc
  825.     ret
  826. get_digit_2:
  827.     or    al,20h
  828.     cmp    al,'a'            ;hex digit?
  829.     jb    get_digit_1
  830.     cmp    al,'f'            ;hex digit?
  831.     ja    get_digit_1
  832.     sub    al,'a'-10
  833.     clc
  834.     ret
  835. get_digit_1:
  836.     stc
  837.     ret
  838.  
  839.  
  840. hexout:
  841.     mov    cl,'0'            ;prepare to eliminate leading zeroes.
  842.     xchg    ax,dx            ;just output 32 bits in hex.
  843.     call    wordout            ;output dx.
  844.     xchg    ax,dx
  845.     jmp    wordout            ;output ax.
  846.  
  847. decout:
  848.     mov    si,ax            ;get the number where we want it.
  849.     mov    di,dx
  850.  
  851.     xor    ax,ax            ;start with all zeroes in al,bx,bp
  852.     mov    bx,ax
  853.     mov    bp,ax
  854.  
  855.     mov    cx,32            ;32 bits in two 16 bit registers.
  856. decout_1:
  857.     shl    si,1
  858.     rcl    di,1
  859.     xchg    bp,ax
  860.     call    addbit
  861.     xchg    bp,ax
  862.     xchg    bx,ax
  863.     call    addbit
  864.     xchg    bx,ax
  865.     adc    al,al
  866.     daa
  867.     loop    decout_1
  868.  
  869.     mov    cl,'0'            ;prepare to eliminate leading zeroes.
  870.     call    byteout            ;output the first two.
  871.     mov    ax,bx            ;output the next four
  872.     call    wordout            ;output the next four
  873.     mov    ax,bp
  874. wordout:
  875.     push    ax
  876.     mov    al,ah
  877.     call    byteout
  878.     pop    ax
  879. byteout:
  880.     mov    ah,al
  881.     shr    al,1
  882.     shr    al,1
  883.     shr    al,1
  884.     shr    al,1
  885.     call    digout
  886.     mov    al,ah
  887. digout:
  888.     and    al,0fh
  889.     add    al,90h    ;binary digit to ascii hex digit.
  890.     daa
  891.     adc    al,40h
  892.     daa
  893.     cmp    al,cl            ;leading zero?
  894.     je    return
  895.     mov    cl,-1            ;no more leading zeros.
  896. chrout:
  897.     push    ax            ;print the char in al.
  898.     xchg    al,dl
  899.     mov    ah,2
  900.     int    21h
  901.     xchg    al,dl
  902.     pop    ax
  903. return:
  904.     ret
  905.  
  906.  
  907. addbit:    adc    al,al
  908.     daa
  909.     xchg    al,ah
  910.     adc    al,al
  911.     daa
  912.     xchg    al,ah
  913.     ret
  914.  
  915.  
  916. getenv:
  917. ;enter with ds:si -> environment string to look for, cx = length of string,
  918. ;  ds:di -> place to put the string's value.
  919.     push    es
  920.     push    di            ;remember where we're supposed to put it.
  921.  
  922.     mov    es,phd_env        ;search the environment.
  923.     xor    di,di
  924.  
  925. getenv_2:
  926.     cmp    byte ptr es:[di],0    ;see if we're at the end.
  927.     je    getenv_0
  928.  
  929.     push    cx
  930.     push    si
  931.     push    di
  932.     repe    cmpsb
  933.     pop    di
  934.     pop    si
  935.     je    getenv_3
  936.     mov    cx,-1            ;skip to the next null.
  937.     xor    al,al
  938.     repne    scasb
  939.     pop    cx
  940.     jmp    getenv_2
  941. getenv_3:
  942. ;copy the environment string to current_dir.
  943.     pop    cx
  944.     add    di,cx            ;go to the end of the string.
  945.     pop    si            ;pushed as di, -> place to put the string.
  946. getenv_4:
  947.     mov    al,es:[di]
  948.     mov    [si],al
  949.     inc    di
  950.     inc    si
  951.     or    al,al
  952.     jne    getenv_4
  953.     dec    si            ;point si to the null again.
  954.     pop    es
  955.     clc
  956.     ret
  957. getenv_0:
  958.     add    sp,2
  959.     pop    es
  960.     stc
  961.     ret
  962.  
  963.  
  964. end_code    label    byte
  965.  
  966. queue_begin    label    byte
  967.  
  968. code    ends
  969.  
  970.     end    start
  971.