home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PROGRAMS / UTILS / NOVELL / PKTDRV7.ZIP / UBNIC.ASM < prev    next >
Encoding:
Assembly Source File  |  1990-07-27  |  15.8 KB  |  681 lines

  1. version    equ    0
  2.  
  3. ;History:678,1 0
  4.  
  5. ; Copyright, 1990, 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.  
  21.     include    defs.asm
  22.  
  23. ;some generic information about buffer lengths, etc
  24. XMITBUF_LEN    equ    7ffh        ;Transmit buffer length
  25. RPAGE_LEN    equ    128        ;receive page is 128 bytes
  26. NUM_RPAGES    equ    96        ;There are 96 receive pages
  27. NUM_XBUFS    equ    2        ;Number of xmit buffers
  28. NUM_XBUFS_MASK    equ    1
  29.  
  30. ub_seg    segment at 0
  31.  
  32.     org    10h
  33. eaddr_rom    db    6 dup(?)    ;Has serial # in 3 & 4th bytes
  34.  
  35.     org    2080h
  36. tsa_msb        db    ?    ;transmitter start address, high order 4
  37.                 ;bits on write, start send on read
  38. tsa_lsb        db    ?    ;Transmitter start address, least
  39.                 ;significant byte on write, clear PAV
  40.                 ;on read
  41.  
  42. ;Control / status register (CSR)
  43.  
  44. csr        db    ?    ;Control on write, status on read port
  45.  
  46. ;Command bits
  47. TxRdyIntEn    equ    080h        ;allow TxRdy interrupts
  48. PAVIntEn    equ    040h        ;Allow PAV to gen. intr.
  49. SFTInt        equ    020h        ;generate an interrupt reg.
  50. TimIntEn    equ    010h        ;allow Time interrupt req.
  51. ;status bits
  52. NTxRdyInt    equ    080h        ;Not req. TxRdy inter.
  53. NPAVInt        equ    040h        ;Not req. Pav intr.
  54. NSFTInt        equ    020h        ;Not req. SFTInt inter.
  55. NTimInt        equ    010h        ;Not req. timer inter.
  56. NRINT        equ    008h        ;Not req. recv inter.
  57. NTINT        equ    004h        ;Not req. xmit inter.
  58. TPOK        equ    002h        ;Packet okay of some type
  59. TXDONE        equ    001h        ;Transmit done
  60.  
  61. ;Page pointer register definitions
  62.  
  63. ffp        label    byte        ;ffp - first free page register
  64. epp        label    byte        ;epp - empty page pointer register
  65. pp        db    ?        ;ffp on write/epp on read
  66.  
  67.  
  68. ;ffp bits
  69. NIC_IE        equ    080h        ;NIC interrupts enable
  70.  
  71. ;epp bits
  72. PAV        equ    080h        ;Page available
  73. RPAGE_BITS    equ    07fh
  74.  
  75.  
  76.     org    2100h
  77. page_ram    db    NUM_RPAGES dup(?);Receiver buffer pages
  78.  
  79.     org    2180h
  80. tstatus        db    ?        ;Transmitter status 80
  81.  
  82. ;Transmit status bits - EDLC
  83. TX_READY    equ    080h        ;
  84. TX_BUSY        equ    040h        ;Carrier detect
  85. TX_TPR        equ    020h        ;Self received packet sent
  86. TX_SHORTED    equ    010h        ;Carrier lost
  87. TX_UF        equ    008h        ;Underflow
  88. TX_COL        equ    004h        ;Collision
  89. TX_C16        equ    002h        ;16 collisions
  90. TX_PE        equ    001h        ;Parity error
  91.  
  92. TXERRMASK    equ    TX_PE or TX_C16 or TX_COL or TX_UF or TX_SHORTED or TX_BUSY
  93.  
  94. tmask        db    ?        ;Transmitter mask 81
  95. rstatus        db    ?        ;Receiver status
  96. rmask        db    ?        ;Receiver mask 83
  97. tmode        db    ?        ;Transmitter mode 84
  98. rmode        db    ?        ;Receiver mode 85
  99. reset        db    ?        ;Reset line - high on bit 7, 86
  100. tdrlsb        db    ?        ;TDR lsb  87
  101. eaddr_ram    db    6 dup(?)    ;current Ethernet address.
  102.         db    ?        ;Reserved 8e
  103. tdrmsb        db    ?        ;TDR msb 8f
  104.  
  105.     org    4000h
  106. receive_buf    label    byte
  107.  
  108.     org    7000h
  109. transmit_buf_1    label    byte
  110.  
  111.     org    7800h
  112. transmit_buf_2    label    byte
  113.  
  114. ub_seg    ends
  115.  
  116. code    segment    word public
  117.     assume    cs:code, ds:code
  118.  
  119.     public    int_no
  120. int_no        db    3,0,0,0        ;must be four bytes long for get_number.
  121. base_addr    dw    0d000h,0
  122.  
  123.     public    driver_class, driver_type, driver_name, driver_function, parameter_list
  124. driver_class    db    1        ;from the packet spec
  125. driver_type    db    8        ;from the packet spec
  126. driver_name    db    'PC-NIC',0    ;name of the driver.
  127. driver_function    db    2
  128. parameter_list    label    byte
  129.     db    1    ;major rev of packet driver
  130.     db    9    ;minor rev of packet driver
  131.     db    14    ;length of parameter list
  132.     db    EADDR_LEN    ;length of MAC-layer address
  133.     dw    GIANT    ;MTU, including MAC headers
  134.     dw    MAX_MULTICAST * EADDR_LEN    ;buffer size of multicast addrs
  135.     dw    0    ;(# of back-to-back MTU rcvs) - 1
  136.     dw    0    ;(# of successive xmits) - 1
  137. int_num    dw    0    ;Interrupt # to hook for post-EOI
  138.             ;processing, 0 == none,
  139.  
  140.     public    rcv_modes
  141. rcv_modes    dw    7        ;number of receive modes in our table.
  142.         dw    0               ;There is no mode zero
  143.         dw    rcv_mode_1
  144.         dw    0        ;don't want to bother.
  145.         dw    rcv_mode_3
  146.         dw    0        ;haven't set up perfect filtering yet.
  147.         dw    rcv_mode_5
  148.         dw    rcv_mode_6
  149.  
  150. current_page    db    0        ;current receiver page
  151.  
  152. active_buffer    dw    transmit_buf_1
  153. inactive_buffer    dw    transmit_buf_2
  154.  
  155.     public    as_send_pkt
  156. ; The Asynchronous Transmit Packet routine.
  157. ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
  158. ;   interrupts possibly enabled.
  159. ; Exit with nc if ok, or else cy if error, dh set to error number.
  160. ;   es:di and interrupt enable flag preserved on exit.
  161. as_send_pkt:
  162.     ret
  163.  
  164.     public    drop_pkt
  165. ; Drop a packet from the queue.
  166. ; Enter with es:di -> iocb.
  167. drop_pkt:
  168.     assume    ds:nothing
  169.     ret
  170.  
  171.     public    xmit
  172. ; Process a transmit interrupt with the least possible latency to achieve
  173. ;   back-to-back packet transmissions.
  174. ; May only use ax and dx.
  175. xmit:
  176.     assume    ds:nothing
  177.     ret
  178.  
  179.  
  180.     public    send_pkt
  181. send_pkt:
  182. ;enter with es:di->upcall routine, (0:0) if no upcall is desired.
  183. ;  (only if the high-performance bit is set in driver_function)
  184. ;enter with ds:si -> packet, cx = packet length.
  185. ;if we're a high-performance driver, es:di -> upcall.
  186. ;exit with nc if ok, or else cy if error, dh set to error number.
  187.     assume    ds:nothing
  188.  
  189.     mov    es,base_addr
  190.     assume    es:ub_seg
  191.  
  192.     cmp    cx,GIANT        ; Is this packet too large?
  193.     ja    send_pkt_toobig
  194.  
  195.     cmp    cx,RUNT            ; minimum length for Ether
  196.     jnb    oklen
  197.     mov    cx,RUNT            ; make sure size at least RUNT
  198. oklen:
  199.  
  200.     mov    di,XMITBUF_LEN        ;compute where we're going to
  201.     sub    di,cx            ;  be putting the packet.
  202.  
  203. ;games above ensure that the last byte of data to go out is at the
  204. ;end of the ub xmit buffer, pos is really the offset from the
  205. ;start of that buffer
  206.  
  207.     mov    ax,active_buffer    ;swap the buffer pointers.
  208.     xchg    ax,inactive_buffer
  209.     mov    active_buffer,ax
  210.     add    di,ax
  211.     push    di
  212.     call    movemem
  213.     pop    di
  214.  
  215. ;Wait for previous packet to finish first
  216. ;could do this with interrupts, etc, but with only 2 xmit buffers
  217. ;doesn't seem worth the effort
  218.  
  219. send_pkt_1:
  220.     mov    al,csr
  221.     and    al,TPOK or TXDONE
  222.     cmp    al,TPOK or TXDONE
  223.     jne    send_pkt_1
  224.  
  225.     mov    al,tstatus        ;get xmiter status ...
  226.     mov    tstatus,0fh        ;... and clear status bits
  227.  
  228.     test    al,TXERRMASK        ;any errors?
  229.     je    send_pkt_2
  230.     call    count_out_err        ;yes, count them.
  231. send_pkt_2:
  232.  
  233. ;all done above, we can play with TSA now!
  234. ;give offset
  235.  
  236.     mov    ax,di
  237.     mov    tsa_lsb,al
  238.     mov    tsa_msb,ah
  239.  
  240.     cmp    al,tsa_msb        ;Start the send in action (SIDEEFFECT)
  241.  
  242.     clc
  243.     ret
  244. send_pkt_toobig:
  245.     mov    dh,NO_SPACE
  246.     stc
  247.     ret
  248.  
  249.  
  250.     include    movemem.asm
  251.  
  252.  
  253.     public    get_address
  254. get_address:
  255. ;get the address of the interface.
  256. ;enter with es:di -> place to get the address, cx = size of address buffer.
  257. ;exit with nc, cx = actual size of address, or cy if buffer not big enough.
  258.     assume    ds:code
  259.     cmp    cx,EADDR_LEN        ;make sure that we have enough room.
  260.     jb    get_address_2
  261.  
  262.     push    ds
  263.     mov    ds,base_addr
  264.     mov    si,offset eaddr_rom
  265.     mov    cx,EADDR_LEN
  266.     rep    movsb
  267.     pop    ds
  268.  
  269.     mov    cx,EADDR_LEN
  270.     clc
  271.     ret
  272. get_address_2:
  273.     stc
  274.     ret
  275.  
  276.  
  277.     public    set_address
  278. set_address:
  279. ;enter with ds:si -> Ethernet address, CX = length of address.
  280. ;exit with nc if okay, or cy, dh=error if any errors.
  281.     assume    ds:nothing
  282.     cmp    cx,EADDR_LEN        ;ensure that their address is okay.
  283.     je    set_address_4
  284.     mov    dh,BAD_ADDRESS
  285.     stc
  286.     jmp    short set_address_done
  287. set_address_4:
  288.  
  289.     mov    es,base_addr
  290.     mov    di,offset eaddr_ram
  291.     rep    movsb
  292. ;    call    initialize        ;initialize with our new address.
  293.  
  294. set_address_okay:
  295.     mov    cx,EADDR_LEN        ;return their address length.
  296.     clc
  297. set_address_done:
  298.     push    cs
  299.     pop    ds
  300.     assume    ds:code
  301.     ret
  302.  
  303.  
  304. ;    0 - accept no packets
  305. rcv_mode_1:
  306.     mov    es,base_addr
  307.     assume    es:ub_seg
  308.  
  309.     mov    rmode,0
  310.     ret
  311.  
  312. rcv_mode_3:
  313. ;    1 - accept node id packets, multicasts which match first three
  314. ;        bytes of node id, and broadcasts
  315.     mov    es,base_addr
  316.     assume    es:ub_seg
  317.  
  318.     mov    rmode,1
  319.     ret
  320.  
  321. ;    2 - accept node id packets, all multicasts and broadcasts
  322. rcv_mode_5:
  323.     mov    es,base_addr
  324.     assume    es:ub_seg
  325.  
  326.     mov    rmode,2
  327.     ret
  328.  
  329. ;    3 - take all, promiscous
  330. rcv_mode_6:
  331.     mov    es,base_addr
  332.     assume    es:ub_seg
  333.  
  334.     mov    rmode,3
  335.     ret
  336.  
  337.     public    set_multicast_list
  338. set_multicast_list:
  339. ;enter with ds:si ->list of multicast addresses, cx = number of addresses.
  340. ;return nc if we set all of them, or cy,dh=error if we didn't.
  341.     mov    dh,NO_MULTICAST
  342.     stc
  343.     ret
  344.  
  345.  
  346.     public    terminate
  347. terminate:
  348.     mov    es,base_addr
  349.     assume    es:ub_seg
  350.  
  351.     mov    ffp,0            ;set ffp to zero - disabling PAV
  352.     mov    csr,0            ;Turn off interrupt masks here and ...
  353.     mov    tmask,0            ;reset edlc interrupt masks
  354.     mov    rmask,0
  355.     mov    reset,80h        ;Turn on reset line
  356.  
  357.     ret
  358.  
  359.     public    reset_interface
  360. reset_interface:
  361. ;reset the interface.
  362.     assume    ds:code
  363.     ret
  364.  
  365.  
  366. ;called when we want to determine what to do with a received packet.
  367. ;enter with cx = packet length, es:di -> packet type.
  368.     extrn    recv_find: near
  369.  
  370. ;called after we have copied the packet into the buffer.
  371. ;enter with ds:si ->the packet, cx = length of the packet.
  372.     extrn    recv_copy: near
  373.  
  374.     extrn    count_in_err: near
  375.     extrn    count_out_err: near
  376.  
  377.  
  378.     public    recv
  379. recv:
  380. ;called from the recv isr.  All registers have been saved, and ds=cs.
  381. ;Upon exit, the interrupt will be acknowledged.
  382.     assume    ds:code
  383.  
  384.     mov    es,base_addr
  385.     assume    es:ub_seg
  386.  
  387. recv_3:
  388.     test    epp,PAV
  389.     jne    recv_4
  390.     jmp    recv_2            ;yes.
  391. recv_4:
  392.  
  393. ;checking against epp is a pain actually...
  394.     xor    cx,cx            ;start with zero length
  395.     mov    bl,current_page        ;get the first page.
  396.     xor    bh,bh
  397.     mov    dx,bx            ;remember the first page.
  398. count_pages_1:
  399.     mov    al,page_ram[bx]        ;Get page descriptor
  400.     and    al,RPAGE_BITS    ;Only page offset bits PLEASE
  401.     inc    al            ;Add 1 to offset to make length
  402.     xor    ah,ah
  403.     add    cx,ax            ;Add in current page length
  404.  
  405.     mov    al,page_ram[bx]        ;Get page descriptor
  406.     inc    bx            ;go to the next page
  407.     cmp    bx,NUM_RPAGES        ;wrap around if necessary.
  408.     jb    count_pages_2
  409.     xor    bx,bx
  410. count_pages_2:
  411.  
  412.     test    al,80h            ;End page?
  413.     je    count_pages_1        ;yes, go add this one's length.
  414.  
  415.     mov    current_page,bl
  416.  
  417.     mov    ax,dx            ;multiply dx by RPAGE_LEN.
  418.     xchg    ah,al
  419.     ror    ax,1
  420.     mov    di,ax
  421.     add    di,offset receive_buf    ;get the starting address.
  422.     add    di,EADDR_LEN+EADDR_LEN    ;skip the ethernet addreses and
  423.                     ;  point to the packet type.
  424.     push    bx
  425.     push    cx
  426.     push    dx
  427.     call    recv_find        ;look up our type.
  428.     assume    es:nothing
  429.     pop    dx
  430.     pop    cx
  431.     pop    bx
  432.  
  433.     mov    ax,es            ;is this pointer null?
  434.     or    ax,di
  435.     je    just_discard        ;yes - just free the frame.
  436.  
  437.     push    cx
  438.     push    di
  439.     push    es
  440.  
  441.     cmp    dx,bx            ;did we wrap around (first >last?)
  442.     jbe    no_wrap            ;no, just do one move.
  443.  
  444. ;we wrapped around the buffer, so we have to move in two halves.
  445. ;move the first part.
  446.     mov    ax,dx            ;multiply dx by RPAGE_LEN.
  447.     xchg    ah,al
  448.     ror    ax,1
  449.     mov    si,ax
  450.     add    si,offset receive_buf    ;get the starting address.
  451.  
  452.     mov    ax,NUM_RPAGES        ;Compute the number of pages to move.
  453.     sub    ax,dx
  454.     xchg    ah,al            ;convert to pages (*RPAGE_LEN).
  455.     ror    ax,1
  456.     cmp    cx,ax            ;are we moving less than that?
  457.     jbe    no_wrap            ;yes, just move it.
  458.     sub    cx,ax            ;reduce the total count by this count.
  459.     push    cx            ;preserve the second part's count
  460.     mov    cx,ax            ;get the first part's count.
  461.  
  462.     push    ds
  463.     mov    ds,base_addr
  464.     call    movemem
  465.     pop    ds
  466.  
  467.     pop    cx            ;get back the count
  468.  
  469.     mov    dx,bx
  470.  
  471. no_wrap:
  472.     mov    ax,dx            ;multiply dx by 128.
  473.     xchg    ah,al
  474.     ror    ax,1
  475.     mov    si,ax
  476.     add    si,offset receive_buf
  477.     mov    ds,base_addr
  478.     call    movemem
  479.  
  480.     pop    ds
  481.     pop    si
  482.     pop    cx
  483.     call    recv_copy
  484.     push    cs
  485.     pop    ds
  486.  
  487. just_discard:
  488.     mov    es,base_addr
  489.     assume    es:ub_seg
  490.     mov    al,epp            ;Any more packets left?
  491.     and    al,RPAGE_BITS
  492.     cmp    al,current_page
  493.     je    recv_5
  494.     jmp    recv_3
  495. recv_5:
  496.  
  497.     cmp    al,tsa_lsb        ;Clearing pav bit (SIDEEFFECT)
  498.  
  499. recv_2:
  500.  
  501. ;Ack the interrupt by turning bit on and off
  502.     mov    al,current_page        ;Set first free page pointer
  503.     mov    ffp,al
  504.     or    al,NIC_IE        ;set interrupt bit, was off
  505.     mov    ffp,al
  506. recv_1:
  507.     ret
  508.  
  509.  
  510.     public    recv_exiting
  511. recv_exiting:
  512. ;called from the recv isr after interrupts have been acknowledged.
  513. ;Only ds and ax have been saved.
  514.     assume    ds:nothing
  515.     ret
  516.  
  517.  
  518. ;any code after this will not be kept after initialization.
  519. end_resident    label    byte
  520.  
  521.  
  522.     public    usage_msg
  523. usage_msg    db    "usage: ubnic [-n] [-d] [-w] <packet_int_no> <int_no> <base_addr>",CR,LF,'$'
  524.  
  525.     public    copyright_msg
  526. copyright_msg    db    "Packet driver for the UB PC/NIC, version ",'0'+majver,".",'0'+version,CR,LF
  527.         db    '$'
  528.  
  529. int_no_name    db    "Interrupt number ",'$'
  530. base_addr_name    db    "Memory address ",'$'
  531. our_address    db    6 dup(?)    ;temporarily hold our address
  532.  
  533.     extrn    set_recv_isr: near
  534.  
  535. ;enter with si -> argument string, di -> wword to store.
  536. ;if there is no number, don't change the number.
  537.     extrn    get_number: near
  538.  
  539. ;enter with si -> argument string, di -> wword to print.
  540.     extrn    print_number: near
  541.  
  542.     public    parse_args
  543. parse_args:
  544. ;exit with nc if all went well, cy otherwise.
  545.     mov    di,offset int_no
  546.     call    get_number
  547.     mov    di,offset base_addr
  548.     call    get_number
  549.     clc
  550.     ret
  551.  
  552.  
  553.     public    etopen
  554. etopen:
  555.     cli
  556.  
  557.     cmp    base_addr,-1        ;did they ask for auto-configure?
  558.     jne    no_auto_config
  559.  
  560.     mov    bx,8000h        ;yes, they *could* address it there.
  561. auto_config_1:
  562.     mov    es,bx
  563.     assume    es:ub_seg
  564.     cmp    word ptr es:eaddr_rom[0],00h + 0ddh * 256
  565.     jne    auto_config_2
  566.     cmp    byte ptr es:eaddr_rom[2],01h
  567.     je    auto_config_3
  568. auto_config_2:
  569.     add    bx,800h            ;move up by 32K
  570.     jnc    auto_config_1        ;go if we didn't overflow.
  571.     sti
  572.     stc                ;say that we couldn't init.
  573.     ret
  574.  
  575. auto_config_3:
  576.     mov    base_addr,bx        ;remember where we found it.
  577.     mov    base_addr+2,0        ;and nuke the rest of the -1.
  578.  
  579. no_auto_config:
  580.     mov    es,base_addr
  581.     assume    es:ub_seg
  582.  
  583.     mov    reset,80h        ;Turn on reset line to tie down NIC
  584.     mov    rmode,0            ;no packets for now
  585.     mov    tmode,0            ;Turn on loopback
  586.  
  587.     mov    al,epp            ;start ffp at epp - eliminates bogus ....
  588.     and    al,07fh
  589.     mov    current_page,al        ;... packets if we've been started before
  590.     mov    ffp,al            ;Make sure interrupt enable is dead
  591.  
  592.     cmp    al,tsa_lsb        ;Clearing pav bit (SIDEEFFECT)
  593.  
  594.     mov    tstatus, 0fh        ;Clear xmit status bits
  595.     mov    tmask,0            ;No xmit intrs
  596.     mov    rmask,0            ;'Packet okay' only?
  597.     mov    rstatus,0ffh        ;Clear receiver status bits
  598.  
  599.     push    ds
  600.     pop    es
  601.     mov    di,offset our_address
  602.     mov    cx,EADDR_LEN
  603.     call    get_address
  604.     mov    si,offset our_address
  605.     mov    cx,EADDR_LEN
  606.     call    set_address
  607.  
  608. ;Since first packet will get sent no matter what we do, let's make it
  609. ;something everyone (include me) will ignore
  610. ;set length to zero
  611.  
  612.     mov    tsa_lsb,0ffh        ;Get last 8 bits
  613.     mov    tsa_msb,0fh        ;and top 4 bits
  614.  
  615.     mov    reset,0            ;Clear reset - expect packet to go
  616. ;Wait for packet to go!
  617. etopen_1:
  618.     mov    al,csr
  619.     and    al,TPOK or TXDONE
  620.     cmp    al,TPOK or TXDONE
  621.     jne    etopen_1
  622.  
  623.     mov    tstatus,0fh        ;clear xmiter status
  624.     mov    tmode,0ah        ;Clear loopbk, set parity enable
  625.  
  626.     mov    csr,0            ;make sure this interrupt was off first
  627.     mov    csr,PAVIntEn        ;Page frame available interrupt only
  628.     mov    al,current_page        ;Set first free page register ...
  629.     or    al,NIC_IE
  630.     mov    ffp,al            ;... and make sure interrupts enabled
  631.  
  632.     mov    al,epp            ;get epp again
  633.     test    al,PAV
  634.     je    etopen_2
  635.     cmp    al,tsa_lsb        ;Try clearing pav bit (SIDEEFFECT)
  636. etopen_2:
  637.  
  638. ;enable receiving now
  639. ;    1 - accept node id packets, multicasts which match first three
  640. ;        bytes of node id, and broadcasts
  641.     mov    rmode,1
  642.  
  643. ;
  644. ; Now hook in our interrupt
  645. ;
  646.     call    set_recv_isr
  647.  
  648.     sti
  649.  
  650.     mov    al, int_no        ; Get board's interrupt vector
  651.     add    al, 8
  652.     cmp    al, 8+8            ; Is it a slave 8259 interrupt?
  653.     jb    set_int_num        ; No.
  654.     add    al, 70h - 8 - 8        ; Map it to the real interrupt.
  655. set_int_num:
  656.     xor    ah, ah            ; Clear high byte
  657.     mov    int_num, ax        ; Set parameter_list int num.
  658.  
  659. ;if all is okay,
  660.     mov    dx,offset end_resident
  661.     clc
  662.     ret
  663. ;if we got an error,
  664.     stc
  665.     ret
  666.  
  667.     public    print_parameters
  668. print_parameters:
  669. ;echo our command-line parameters
  670.     mov    di,offset int_no
  671.     mov    dx,offset int_no_name
  672.     call    print_number
  673.     mov    di,offset base_addr
  674.     mov    dx,offset base_addr_name
  675.     call    print_number
  676.     ret
  677.  
  678. code    ends
  679.  
  680.     end
  681.