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