home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msk316src.zip / MSNPDI.ASM < prev    next >
Assembly Source File  |  1999-04-24  |  66KB  |  2,022 lines

  1.     NAME    MSNPDI
  2. ; File MSNPDI.ASM
  3. ; Packet Driver and ODI interface 
  4. ;
  5. ;    Copyright (C) 1982, 1999, Trustees of Columbia University in the 
  6. ;    City of New York.  The MS-DOS Kermit software may not be, in whole 
  7. ;    or in part, licensed or sold for profit as a software product itself,
  8. ;    nor may it be included in or distributed with commercial products
  9. ;    or otherwise distributed by commercial concerns to their clients 
  10. ;    or customers without written permission of the Office of Kermit 
  11. ;    Development and Distribution, Columbia University.  This copyright 
  12. ;    notice must not be removed, altered, or obscured.
  13. ;
  14. ; Written by Joe R. Doupnik, Utah State University, Logan Utah 84322
  15. ;  jrd@cc.usu.edu, jrd@usu.Bitnet.
  16. ;
  17. ; Packet Driver reference:
  18. ; "PC/TCP Version 1.09 Packet Driver Specification", FTP Software, Inc.,
  19. ;  September-14-1989.
  20. ;
  21. ; ODI references:
  22. ; "Open Data-Link Interface Developer's Guide for DOS Network Layer Protocol 
  23. ;  Stacks", Novell Inc, document number 100-001218-001 (1992, but no printed 
  24. ;  date).
  25. ; "Open Data-Link Interface Developer's Guide for NetWare v3.1x Server 
  26. ;  Driver Protocol Stacks", Novell Inc, document number 100-001196-00, v1.0,
  27. ;  19 Sept 1991.
  28. ; "Open Data-Link Interface LAN Driver Developer's Guide for DOS", Novell Inc,
  29. ;  document number 107-000010-001, Revision i, 13 Nov 1990.
  30. ;
  31. ; C language interface presumes the Small memory model and Microsoft C.
  32. ; Assembler is MS MASM v6.
  33. ;
  34. ; These procedures interface between ODI or a Packet Driver and the main
  35. ; protocol stack to both send and receive packets. The upper levels provide
  36. ; and receive packets framed as Ethernet_II (Blue Book/DIX), and receive
  37. ; ARP information particular to the physical frames involved. Conversion of
  38. ; this internal form to the actual framing method on the wire is done by ODI.
  39. ; The receive buffer is external to this routine. Received packets are linked
  40. ; into the buffer on an order of arrival basis (and sized to fit). High level
  41. ; reception has to poll the receive buffer queue. Transmitted packets are 
  42. ; operated with a single external buffer, and sending blocks until the lower 
  43. ; level driver material is ready for us. Initialization, status, attachment, 
  44. ; and disengagment procedures are here.
  45. ; External int kpdint is used by pdinit() to select between Packet Driver
  46. ; and ODI interfaces. Call pdinit() to initialize the system, then call
  47. ; pdaccess() to register each desired frame TYPE, call pdclose() to release
  48. ; each frame TYPE.
  49. ; Packet senders call pkt_send() directly.
  50. ; Packet receivers examine the external packet buffer for packets.
  51. ; ARP information is returned in external ints arp_hardware (hardware ARP
  52. ; type code) and MAC_len (length of MAC address, in bytes). See table below
  53. ; for the possible pairs.
  54. ; Packet Driver usage is limited to Ethernet_II/DIX and SLIP. ODI usage is
  55. ; at least Ethernets, Token Ring, Arcnet, and others untested here.
  56. ; Edit history
  57. ; 12 Jan 1995 version 3.14
  58. ; Last edit
  59. ; 23 Dec 1995
  60. INCLUDE MSSDEF.H
  61. getintv        equ    35h        ; DOS get interrupt vector to es:bx
  62. dos        equ    21h
  63. lf        equ    0ah
  64. fopen        equ    3dh        ; DOS file operations
  65. fclose        equ    3eh
  66. fread        equ    3fh
  67.  
  68. pdgetinfo    equ    1        ; Packet Driver functions
  69. pd_access     equ    2
  70. pd_release    equ    3
  71. pd_send        equ    4
  72. pd_get_address    equ    6
  73. eaddr_len    equ    6        ; length of an Ethernet address
  74. IF_EII        equ    1        ; Ethernet II interface type
  75. IF_SLIP        equ    6        ; SLIP interface type, RFC1055
  76.  
  77. link    struc                ; receiver buffer link structure
  78.     flag    db    0        ; buffer use flag
  79.     bufnum    db    0        ; buffer write sequence number
  80.     count    dw    0        ; count of bytes to follow
  81. link    ends
  82. linksize equ    4            ; bytes in link structure
  83.  
  84. ; Novell ODI material, based on LSL v1.2.
  85. ; Most of these header structures were taken from Novell file ODI.INC,
  86. ; and parts have been tailored for Kermit. Information on the "new" item in
  87. ; the lookahead structure is compliments of Novell, private correspondence.
  88.  
  89. ;  Event Control Block Structure
  90. ECBstruct    struc
  91.     nextlink    dd    0        ; leave intact
  92.     prevlink    dd    0        ; leave intact
  93.     status        dw    0        ; general status
  94.     esr        dd    0        ; addr of event service routine
  95.     stackid        dw    0        ; protocol stack ident
  96.     protid        db    6 dup (0)    ; sending only
  97.     boardnum    dw    0        ; sending, MILD board number
  98.     immaddr        db    6 dup (0)    ; MAC destination address
  99.     driverws    db    4 dup (0)    ; their work space
  100.     protocolws    dw    4 dup (0)    ; our work space
  101.     datalen        dw    0        ; total length of sent buffer
  102.     fragcount    dw    1        ; number of buffer pieces
  103.     frag1addr    dw    0,0        ; seg:offset of first buffer
  104.     frag1len    dw    0        ; length of first buf frag
  105. ECBstruct    ends                ; 26 words
  106.  
  107. ;  Look Ahead Structure
  108. LookAheadStruc    struc
  109.     LMediaHeaderPtr    dd    0        ; pointer to MAC header
  110.     LookAheadPtr    dd    0        ; pointer to pkt Data
  111.     LookAheadLen    dw    0        ; length of pkt Data field
  112.     LProtID        db    6 dup (0)    ; protocol ident
  113.     LBoardNum    dw    -1        ; logical board of rcv'd pkt
  114.     DataLookAheadDataSize dd 0    ; new field, exists if bit 7 of the 
  115.             ; Driver's Configuration Table Mode Flags is set.
  116. LookAheadStruc    ends
  117.  
  118. ;  Rx Destination Address Type (First byte of ECB.DriverWS)
  119. ECB_DIRECT        equ    00h        ;Physical destination address
  120. ECB_MULTICAST        equ    01h        ;Multicast destination address
  121. ECB_BROADCAST        equ    03h        ;Broadcast destination address
  122.  
  123. ;  System Error Code Definitions
  124. LSLERR_OUT_OF_RESOURCES    equ    8001h
  125. LSLERR_BAD_PARAMETER    equ    8002h
  126. LSLERR_NO_MORE_ITEMS    equ    8003h
  127. LSLERR_ITEM_NOT_PRESENT    equ    8004h
  128. LSLERR_FAIL        equ    8005h
  129. LSLERR_RX_OVERFLOW    equ    8006h
  130. LSLERR_CANCELLED    equ    8007h
  131. LSLERR_BAD_COMMAND    equ    8008h
  132. LSLERR_DUPLICATE_ENTRY    equ    8009h
  133. LSLERR_NO_SUCH_HANDLER    equ    800ah
  134. LSLERR_NO_SUCH_DRIVER    equ    800bh
  135.  
  136. ;  LSL MLID Services Function Codes
  137. MLIDSUP_GET_ECB                   equ    0
  138. MLIDSUP_RETURN_ECB             equ    1
  139. MLIDSUP_DEFRAG_ECB           equ    2
  140. MLIDSUP_SCHEDULE_AES_EVENT     equ    3
  141. MLIDSUP_CANCEL_AES_EVENT       equ    4
  142. MLIDSUP_GET_INTERVAL_MARKER    equ    5
  143. MLIDSUP_DEREGISTER_MLID           equ    6  
  144. MLIDSUP_HOLD_RECV_EVENT           equ    7  
  145. MLIDSUP_START_CRITICAL_SECTION    equ    8  
  146. MLIDSUP_END_CRITICAL_SECTION       equ    9  
  147. MLIDSUP_CRITICAL_SECTION_STATUS    equ    10 
  148. MLIDSUP_SERVICE_EVENTS              equ    11
  149. MLIDSUP_SEND_COMPLETE              equ    14 
  150. MLIDSUP_ADD_PID            equ    15
  151. MLIDSUP_GET_STACK_ECB        equ    16
  152.  
  153. ;  LSL Protocol Stack Services Function Codes
  154. PROTSUP_GET_ECB                equ    0
  155. PROTSUP_RETURN_ECB            equ    1
  156. PROTSUP_SCHEDULE_AES_EVENT         equ    3
  157. PROTSUP_CANCEL_EVENT            equ    4
  158. PROTSUP_GET_INTERVAL_MARK        equ    5
  159. PROTSUP_REGISTER_STACK            equ    6  
  160. PROTSUP_DEREGISTER_STACK        equ    7  
  161. PROTSUP_REGISTER_DEFAULT_STACK        equ    8  
  162. PROTSUP_DEREGISTER_DEFAULT_STACK    equ    9  
  163. PROTSUP_REGISTER_PRESCAN_STACK        equ    10 
  164. PROTSUP_DEREGISTER_PRESCAN_STACK    equ    11 
  165. PROTSUP_SEND_PACKET            equ    12 
  166. PROTSUP_GET_PROTNUM_FROM_NAME        equ    16 
  167. PROTSUP_GET_PID_PROTNUM_MLIDNUM        equ    17 
  168. PROTSUP_GET_MLID_CTL_ENTRY        equ    18 
  169. PROTSUP_GET_PROTO_CTL_ENTRY        equ    19 
  170. PROTSUP_GET_LSL_STATS            equ    20 
  171. PROTSUP_BIND_STACK_TO_MLID        equ    21 
  172. PROTSUP_UNBIND_STACK_FROM_MLID        equ    22 
  173. PROTSUP_ADD_PID                equ    23 
  174. PROTSUP_RELINQUISH_CONTROL        equ    24 
  175. PROTSUP_GET_LSL_CONFIG            equ    25
  176.  
  177. ;  LSL General Services Function Codes
  178. GENSERV_ALLOC_MEMORY        equ    0
  179. GENSERV_FREE_MEMORY        equ    1
  180. GENSERV_REALLOC_MEMORY        equ    2
  181. GENSERV_MEMORY_STATISTICS    equ    3
  182. GENSERV_ADD_MEMORY_TO_POOL    equ    4
  183. GENSERV_ADD_GENERAL_SERVICE    equ    5
  184. GENSERV_REMOVE_GENERAL_SERVICE    equ    6
  185. GENSERV_GET_NETCFG_PATH        equ    7
  186.  
  187. ;  LSL Configuration Table
  188. LSLConfigurationStructure    struc
  189.     LConfigTableMajorVer    db    1
  190.     LConfigTableMinorVer    db    0
  191.     LNumLSLRxBuffers    dd    0
  192.     LRxBufferSize        dd    0    ;Buffer size NOT including ECB struc size
  193.     LMajorVersion        db    0
  194.     LMinorVersion        db    0
  195.     LConfigTableReserved    db    16 dup (0)
  196. LSLConfigurationStructure    ends
  197.  
  198. ;  MLID Control Commands
  199. GET_MLID_CONFIGURATION        equ    0
  200. GET_MLID_STATISTICS        equ    1
  201. ADD_MULTICAST_ADDRESS        equ    2
  202. DELETE_MULTICAST_ADDRESS    equ    3
  203. MLID_SHUTDOWN            equ    5
  204. MLID_RESET            equ    6
  205. CREATE_CONNECTION        equ    7
  206. REMOVE_CONNECTION        equ    8
  207. SET_LOOK_AHEAD_SIZE        equ    9
  208. DRIVER_POLL            equ    12
  209.  
  210. ;  MLID Configuration Table Structure
  211. MLIDConfigurationStructure    struc
  212.     MSignature        db    'HardwareDriverMLID',8 dup (' ')
  213.     MConfigTableMajorVer    db    1
  214.     MConfigTableMinorVer    db    11
  215.     MNodeAddress        db    6 dup (?)
  216.     MModeFlags        dw    ?
  217.     MBoardNumber        dw    ?
  218.     MBoardInstance        dw    ?
  219.     MMaxPacketSize        dw    ?
  220.     MBestDataSize        dw    ?
  221.     MWorstDataSize        dw    ?
  222.     MCardLongName        dd    ?
  223.     MCardShortName        dd    ?        ; visible board name
  224.     MFrameString        dd    ?
  225.     MReserved0        dw    0        ;Must be set to 0
  226.     MFrameID        dw    ?
  227.     MTransportTime        dw    ?
  228.     MRouteHandler        dd    ?        ;Only for Token-Ring
  229.     MLookAheadSize        dw    ?
  230.     MLineSpeed        dw    ?        ;In Mbps or Kbps
  231.     MReserved1        db    8 dup (0)    ;Must be set to 0
  232.     MMLIDMajorVer        db    ?
  233.     MMLIDMinorVer        db    ?
  234.     MFlags            dw    ?
  235.     MSendRetries        dw    ?
  236.     MLink            dd    ?
  237.     MSharingFlags        dw    ?
  238.     MSlot            dw    ?
  239.     MIOAddress1        dw    ?
  240.     MIORange1        dw    ?
  241.     MIOAddress2        dw     ?
  242.     MIORange2        dw    ?
  243.     MMemoryAddress1        dd    ?
  244.     MMemorySize1        dw    ?
  245.     MMemoryAddress2        dd    ?
  246.     MMemorySize2        dw    ?
  247.     MIntLine1        db    ?
  248.     MIntLine2        db    ?
  249.     MDMALine1        db    ?
  250.     MDMALine2        db    ?
  251. MLIDConfigurationStructure    ends
  252.  
  253. ;  MLID Config Table 'MFlags' bit definitions.
  254. EISA    equ    01h            ;EISA Bus
  255. ISA     equ    02h            ;PC/AT Bus
  256. MCA     equ    04h            ;PS/2 MCA Bus
  257. Len_Info equ    40h            ; pkt data length in lookahead info
  258.  
  259. ;  MLID Config Table 'MModeFlags' bit definitions (no promiscuous mode).
  260. MRealDriverBit        equ    0001h
  261. MUsesDMABit        equ    0002h
  262. MGuaranteedDeliveryBit    equ    0004h        ;100% reliable on transmits
  263. MMulticastBit        equ    0008h
  264. MNeedsPollingBit    equ    0020h
  265. MRawSendBit        equ    0040h
  266.  
  267. ; Registered Stack structure, used during registration only
  268. StackInfoStruc    struc
  269.     StackNamePtr        dd    ip_string    ; ptr to short name
  270.     StackReceiveHandler    dd    ip_rcvr     ; rcv routine
  271.     StackControlHandler    dd    pcontrol    ; control routine
  272. StackInfoStruc    ends
  273.  
  274. ;  Protocol Control Commands
  275. GET_STACK_CONFIGURATION        equ    0
  276. GET_STACK_STATISTICS        equ    1
  277. BIND_TO_MLID            equ    2
  278. UNBIND_FROM_MLID        equ    3
  279. INFORM_MLID_DEREGISTERED    equ    4
  280.  
  281. ;  Protocol Configuration Table
  282. ProtocolConfigStructure    struc
  283.     PConfigTableMajorVer    db    1
  284.     PConfigTableMinorVer    db    0
  285.     PProtocolLongName    dd    plname
  286.     PProtocolShortName    dd    psname        ; "KERMIT"
  287.     PProtocolMajorVer    db    3         ; MSK v3.15
  288.     PProtocolMinorVer    db    15
  289.     PConfigTableReserved    db    16 dup (0)
  290. ProtocolConfigStructure    ends
  291.  
  292. ;  Protocol Statistics Table
  293. ProtocolStatStructure    struc
  294.     PStatTableMajorVer    db    1
  295.     PStatTableMinorVer    db    0
  296.     PNumGenericCounters    dw    3        ; just those below
  297.     PValidCounterMask    dd    111b        ; bitfield, 3 valids
  298.     PTotalTxPackets        dw    2 dup (0)
  299.     PTotalRxPackets        dw    2 dup (0)
  300.     PIgnoredRxPackets    dw    2 dup (0)
  301.     PNumCustomCounters    dw    0        ; none
  302. ProtocolStatStructure    ends
  303.  
  304. pinfo    struc                ; per protocol local data for ecb
  305.     pstack    dw    0        ; StackID
  306.     pprotid    db    6 dup (0)    ; ProtID
  307.     pboard    dw    0        ; boardnum
  308. pinfo    ends
  309.  
  310. _TEXT    SEGMENT  WORD PUBLIC 'CODE'
  311. _TEXT    ENDS
  312. _DATA    SEGMENT  WORD PUBLIC 'DATA'
  313. _DATA    ENDS
  314. CONST    SEGMENT  WORD PUBLIC 'CONST'
  315. CONST    ENDS
  316. _BSS    SEGMENT  WORD PUBLIC 'BSS'
  317. _BSS    ENDS
  318. DGROUP    GROUP    CONST, _BSS, _DATA
  319.     ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP, ES:NOTHING
  320.  
  321. _DATA      SEGMENT
  322.     extrn    _pktbuf_wrote:word, _pktwnum:byte, _kpdint:word
  323.     extrn    _eth_addr:byte, _arp_hardware:word, _MAC_len:word
  324.     extrn    _mss:word, _tempip:byte, _kdebug:byte
  325.  
  326. pdsignature    db    'PKT DRVR'    ; signature of a Packet Driver
  327. pdslen        equ    $-pdsignature
  328. if_type        dw    0        ; interface type
  329. if_class    db    0        ; interface class
  330. if_num        db    0        ; interface number
  331. if_func        db    0        ; interface functionality
  332. if_version    dw    0        ; interface version
  333. iptype        db    8,0        ; IP packet type
  334. iptypelen    equ    $-iptype    ; length of type field for iptype
  335. pktbufoff    dw    0        ; offset of packet buffer
  336. SLIPmac        dw    0,0,2        ; fake SLIP dest Ethernet address
  337.                     ; ODI material
  338. useodi        db    0        ; non-zero if using ODI for transport
  339. lslsig        db    'LINKSUP$'    ; LSL presence signature
  340. lslsiglen    equ    $-lslsig
  341. lslinit        dd    0        ; LSL init entry point
  342.                     ; LSL entry structure, do not separate
  343. lslsupport    dd    0        ; LSL protocol support API entry point
  344. lslservice    dd    0        ; LSL general services API entry point
  345.  
  346. mlidcont     dd    0        ; MLID Control entry point
  347.  
  348. ecbr_qty    equ    6;;4        ; number of receive ECB's to allocate
  349. maketab    MACRO                ; macro to make receiver ecbs
  350. cnt = 0
  351.     rept ecbr_qty - 1
  352.     ecbstruct <,,,odircmp>
  353. cnt = cnt + 1
  354.     endm
  355. ENDM
  356. ecbr        ecbstruct <,,,odircmp>    ; first receiver ECB
  357.         maketab            ; make table of the other ecbr's
  358. ecbx        ecbstruct <,,,odixcmp>    ; one ECB for transmission
  359. ecbr_busy    db     ecbr_qty dup (0) ; our ecbr locks
  360. ecbx_busy    db    0        ; non-zero if ECBx owned by ODI
  361. ecbr_num    dw    0        ; temp to hold index of ecbr/ecbr_busy
  362. rcvtype        dw    0        ; temp, holds protocol TYPE for rcv
  363. pconfig     ProtocolConfigStructure <>    ; as the name says
  364. pstats        ProtocolStatStructure <>    ; protocol statistics
  365. registerstk     StackInfoStruc <>    ; bound stack setup structure
  366. plname        db 13,'MS-DOS Kermit',0    ; cnt, protocol stack long name, null
  367. protword     db 8,'PROTOCOL',0    ; four NET.CFG keywords for Kermit
  368. psname        db 6,'KERMIT',0        ; cnt, protocol stack short name, null
  369. bindword     db 4,'BIND',0        ; board to which to bind
  370. myipword    db 4,'MYIP',0        ; local IP from Telebit PPP driver
  371. ip_type        equ    0008h        ; Protocol TYPEs, big endian/net order
  372. arp_type    equ    0608h
  373. rarp_type    equ    3580h
  374. ip_string     db    2,'IP',0    ; strings to match in NET.CFG file
  375. arp_string    db    3,'ARP',0    ;  to select pkt TYPEs
  376. rarp_string    db    4,'RARP',0    ;  RARP is optional
  377. ip_stackid    pinfo    <>        ; StackID, Protid, boardnum 
  378. arp_stackid    pinfo    <>        ;  for each protocol
  379. rarp_stackid    pinfo    <>
  380. bcast        db    6 dup (0ffh)    ; Broadcast address, for reception
  381. readnetcfg    db    0        ; non-zero if have read NET.CFG
  382. useboard    dw    -1        ; board to be used, -1 = not inited
  383. bdname        db    0,16 dup (0)    ; length, 15 text, null, bound board
  384. tells_len    db    0        ; if MLID tells pkt len for lookahead
  385. tempb        db    0
  386. temp        dw    0
  387. ; parallel lists of NetWare ODI frame types, address lengths, ARP idents
  388. frame_type    db    2,3,4,5,6,7, 9,10,11,14,15,16,23,27,28
  389. num_frames    equ    ($ - frame_type)
  390. frame_adlen    db    6,6,6,6,6,6, 1,6, 6, 1, 6, 6, 6, 0, 0
  391. hardware_type    db    1,6,6,6,6,12,4,6, 6, 7, 6, 6, 0, 0, 0
  392.  
  393. startttic dw    0        ; my transmit timer storage spot
  394. startrtic dw    0        ; my receive timer storage spot
  395. stoprtic dw    0
  396. stopttic dw    0
  397. tickind     db    0        ; 0 for receive, 1 for transmit
  398. overhead dw    0        ; timer measurement overhead
  399. t0count    db    0
  400. _DATA      ENDS
  401.  
  402. ; ODI Frame types, frame strings, length (bytes) of a MAC level address:
  403. ; type    frame string        MAC_len   hardware    comments
  404. ; 0    VIRTUAL_LAN        0    0    no MAC header used
  405. ; 1    LOCALTALK        6    11    Apple (Ether/Tokentalk is 802)
  406. ; 2    ETHERNET_II        6    1    Blue Book
  407. ; 3    ETHERNET_802.2        6    6    802.3 with 802.2 wrap
  408. ; 4    TOKEN-RING        6    4    802.5 with 802.2 wrap
  409. ; 5    ETHERNET_802.3        6    6    802.3 "raw", old Novell
  410. ; 6    802.4            6    6    Token Bus
  411. ; 7    NOVELL_PCN2          6    12    Novell's IBM PCnet2
  412. ; 8    GNET              6    4    Gateway, assumed TRN-like
  413. ; 9    PRONET-10        1     4    Proteon TRN-like
  414. ; 10    ETHERNET_SNAP        6    1    802.3 with 802.2+SNAP
  415. ; 11    TOKEN-RING_SNAP        6    6    802.5 with 802.2+SNAP
  416. ; 12    LANPAC_II        6    ?    Racore
  417. ; 13    ISDN            6    ?    telco
  418. ; 14    NOVELL_RX-NET        1    7    Arcnet-like
  419. ; 15    IBM_PCN2_802.2        6    12    IBM PCnet2 with 802.2
  420. ; 16    IBM_PCN2_SNAP        6    12    IBM PCnet2,802.2+SNAP
  421. ; 17    OMNINET/4        ?    ?    Corvus
  422. ; 18    3270_COAXA        ?    ?    Harris
  423. ; 19    IP            ?    ?    tunneled
  424. ; 20    FDDI_802.2        6    ?
  425. ; 21    IVDLAN_802.9        6    ?    Commtex
  426. ; 22    DATACO_OSI        ?    ?    Dataco
  427. ; 23    FDDI_SNAP        6    6    802.7, with 802.2+SNAP
  428. ; 27    SLIP            0    0    SLIP, IP over serial link
  429. ; 28    PPP            0    0    PPP, IP over PPP serial link
  430. ;
  431. ;      ARP hardware field, from RFC 1060
  432. ;     Type   Description                 
  433. ;     ----   -----------                
  434. ;    1    Ethernet (10Mb)
  435. ;    2    Experimental Ethernet (3Mb)
  436. ;    3    Amateur Radio AX.25
  437. ;    4    Proteon ProNET Token Ring
  438. ;    5    Chaos
  439. ;       6    IEEE 802 Networks
  440. ;       7    ARCNET
  441. ;       8    Hyperchannel
  442. ;       9    Lanstar
  443. ;      10    Autonet Short Address
  444. ;      11    LocalTalk
  445. ;      12    LocalNet (IBM PCNet or SYTEK LocalNET)
  446.  
  447. data    segment
  448.     extrn    tv_segs:word, tv_sego:word, crt_lins:byte
  449.     extrn    crt_cols:byte
  450. data    ends
  451.  
  452. code    segment
  453.     extrn    pcwait:far
  454. code    ends
  455.  
  456. _TEXT    segment
  457.  
  458. pktdrvr    proc    near            ; Packet Driver interrupt invokation
  459. PKTDRVI:int    60h        ; Interrupt number, modified by startup code
  460.     ret
  461. pktdrvr    endp
  462.  
  463. ; pdinit(ðeraddress)
  464. ; Initialize Packet Driver or ODI for use by this program. Stores Ethernet
  465. ; address (or MAC address). _kpdint is 0 to scan first for a Packet Driver
  466. ; interrupt and fall back to search for ODI, or is a number 40h..7fh to
  467. ; target only that PD interrupt, or is 'DO' to target only ODI. If a PD is
  468. ; used then _kpdint is modified to be the found interrupt value.
  469. ; A 6 byte MAC level address is returned for convenience, even for SLIP. 
  470. ; Returns 1 for success, 0 for failure.
  471.  
  472.     public    _pdinit
  473. _pdinit    proc    near
  474.     push    bp
  475.     mov    bp,sp
  476.     push    es
  477.     push    si
  478.     push    di
  479.     push    ds
  480.     mov    ax,DGROUP
  481.     mov    ds,ax
  482.  
  483.     mov    al,_kdebug
  484.     push    ax
  485.     or    _kdebug,2        ; turn on timing option
  486.     cli                ; compute timing overhead
  487.     call    rstart
  488.     call    rstop
  489.     mov    ax,startrtic
  490.     sub    ax,stoprtic
  491.     jns    pdinit8
  492.     neg    ax
  493. pdinit8:mov    overhead,ax        ; overhead, ticks
  494.     in    al,40h        ; 8254 timer channel T0, read LSB
  495.     xchg    ah,al
  496.     in    al,40h        ; read MSB
  497.     xchg    al,ah        ; order properly
  498.     mov    bx,ax
  499.     mov    ax,1        ; number of milliseconds to delay
  500.     call    pcwait        ; delay AX milliseconds
  501.     mov    ax,1
  502.     call    pcwait
  503.     mov    ax,1
  504.     call    pcwait
  505.     mov    ax,1
  506.     call    pcwait
  507.     in    al,40h        ; 8254 timer channel T0, read LSB
  508.     xchg    ah,al
  509.     in    al,40h        ; read MSB
  510.     xchg    al,ah        ; order properly
  511.     sti
  512.     sub    ax,bx
  513.     jns    pdinit9
  514.     neg    ax
  515. pdinit9:add    ax,500        ; round up to 4+millisec
  516.     xor    dx,dx
  517.     mov    cx,1000        ; micro to millisec
  518.     div    cx
  519.     shr    ax,1        ; divide by four timings
  520.     shr    ax,1        ; get 1 or 2 for 1 millisec
  521.     and    al,3        ; keep only two lower bits
  522.     mov    t0count,al    ; remember for msnpdi.asm
  523.     pop    ax            ; recover debug setting
  524.     mov    _kdebug,al
  525.     mov    ax,[bp+4+0]        ; get offset of pktbuf
  526.     mov    pktbufoff,ax        ; save locally
  527.     cmp    _kpdint,'DO'        ; special indicator to use ODI?
  528.     je    short pdinit2        ; e = yes, do ODI
  529.     mov    cx,40h            ; interrupt range
  530.     cmp    _kpdint,0        ; user value given for PD Int?
  531.     je    pdinit1            ; e = no
  532.     mov    cx,_kpdint        ; use it
  533.     mov    _kpdint,0        ; assume no user value
  534. pdinit1:mov    ah,getintv        ; get interrupt vector to es:bx
  535.     mov    al,cl            ; vector number
  536.     int    dos
  537.     mov    si,offset dgroup:pdsignature ; look for signature
  538.     push    cx
  539.     mov    cx,pdslen        ; length of string
  540.     mov    di,bx
  541.     add    di,3            ; sig starts 3 bytes from entry point
  542.     cld
  543.     repe    cmpsb            ; compare bytes
  544.     pop    cx
  545.     je    pdinit3            ; e = found a match
  546.     cmp    _kpdint,0        ; user value given?
  547.     jne    pdinit2            ; ne = yes, so fail
  548.     inc    cx
  549.     cmp    cx,80h            ; at end of range?
  550.     jna    pdinit1            ; na = not yet, try another int
  551.                     ; no Packet Driver found or use ODI
  552. pdinit2:call    odichk            ; see if ODI is available now
  553.     jnc    pdinit6            ; nc = yes
  554.     cmp    _kpdint,'DO'        ; special indicator to use ODI?
  555.     jne    pdinit5            ; ne = no, have tried Packet Drivers
  556.     mov    _kpdint,0        ; setup for Packet Driver test
  557.     mov    cx,40h            ; scan from this interrupt
  558.     jmp    pdinit1
  559. pdinit6:mov    useodi,1        ; say using ODI
  560.     mov    _kpdint,'DO'        ; signal ODI via PD interrupt variable
  561.     mov    if_class,1        ; say Ethernet_II for internal work
  562.     mov    di,[bp+4+2]        ; get offset of user's buffer
  563.     mov    cx,eaddr_len        ; length of address provided
  564.     mov    si,offset DGROUP:SLIPmac ; get fake Ethernet address
  565.     cld                ;  in case code wants it early
  566.     push    ds
  567.     pop    es
  568.     push    di
  569.     rep    movsb            ; copy to user buffer
  570.     mov    cx,ecbr_qty
  571.     mov    di,offset DGROUP:ecbr_busy ; clear receive ecb busy flags
  572.     xor    al,al
  573.     rep    stosb
  574.     mov    ecbx_busy,al        ; and transmitter busy lock
  575.     pop    di
  576.     clc
  577. pdinit5:jmp    pdret            ; exit (carry set is failure)
  578.                      ; Packet Driver details
  579. pdinit3:mov    byte ptr PKTDRVI+1,cl    ; force in new PD interrupt, code mod
  580.     mov    _kpdint,cx        ; remember interrupt number
  581.                     ; find Ethernet address
  582.     mov    ah,pdgetinfo        ; get Packet Driver information
  583.     mov    al,0ffh
  584.     xor    bx,bx            ; optional handle
  585.     push    ds            ; this call changes ds and si
  586.     push    si
  587.     call    pktdrvr            ; call the Packet Driver
  588.     pop    si
  589.     pop    ds
  590.     jc    pdret            ; c = failure
  591.     mov    if_type,dx        ; save details for access calls
  592.     mov    if_class,ch
  593.     mov    if_num,cl
  594.     mov    if_func,al
  595.     mov    if_version,bx
  596.     mov    ah,pd_access        ; access packets
  597.     mov    al,ch            ; Ethernet class
  598.     mov    bx,dx            ; type
  599.     mov    dl,cl            ; interface number
  600.     mov    cx,iptypelen        ; type length for iptype
  601.     mov    si,offset dgroup:iptype    ; address of TYPE
  602.     mov    di,cs
  603.     mov    es,di
  604.     mov    di,offset pdrcvr    ; ES:DI is our Packet Driver receiver
  605.     call    pktdrvr
  606.     jc    pdret            ; c = failure
  607.     mov    bx,ax            ; put returned handle in BX
  608.     mov    _arp_hardware,0001h    ; Type 1 hardware, Ethernet
  609.     mov    _MAC_len,6        ; 6 bytes of MAC level address
  610.     mov    ax,DGROUP        ; our data segment
  611.     mov    es,ax            ; segment of Ethernet address buffer
  612.     mov    di,[bp+4+2]        ; get offset of user's buffer
  613.     mov    cx,eaddr_len        ; length of address wanted
  614.     cmp    if_class,IF_SLIP    ; interface class of SLIP?
  615.     jne    pdinit4            ; ne = no
  616.     mov    si,offset DGROUP:SLIPmac ; get fake Ethernet address
  617.     cld
  618.     push    di            ; save in case PD actually uses it
  619.     rep    movsb            ; copy to user buffer
  620.     pop    di
  621.     mov    _arp_hardware,0        ; no hardware type
  622.     mov    _MAC_len,0        ; no MAC level address
  623.     mov    _mss,1006-44        ; set SLIP max frame size too
  624. pdinit4:mov    ah,pd_get_address    ; get the Ethernet address
  625.     push    bx            ; save handle
  626.     call    pktdrvr            ; get Ethernet address to es:di buf
  627.     pop    bx
  628.     pushf                ; save carry flag
  629.     jnc    pdinit7
  630.     cmp    if_class,IF_SLIP    ; interface class of SLIP?
  631.     jne    pdinit7            ; ne = no
  632.     popf
  633.     clc                ; forgive error of SLIP8250 v11.x
  634.     pushf
  635. pdinit7:mov    ah,pd_release        ; release this Type, bx has handle
  636.     call    pktdrvr
  637.     popf                ; recover carry flag
  638. pdret:    mov    ax,1            ; return C status, 1 for success
  639.     jnc    pdret1
  640.     xor    ax,ax            ; 0 for failure
  641. pdret1:    pop    ds            ; success
  642.     pop    di
  643.     pop    si
  644.     pop    es
  645.     mov    sp,bp            ; restore stack
  646.     pop    bp            ; recover bp reg
  647.     ret
  648. _pdinit    endp
  649.  
  650. ; int pdinfo(& int version, & int class, & int pdtype, & int number,
  651. ;  & int functionality)
  652. ; Get Packet Driver pedigree
  653.     public    _pdinfo
  654. _pdinfo    proc    near
  655.     push    bp
  656.     mov    bp,sp
  657.     push    di
  658.     push    cx
  659.     push    bx
  660.     push    ax            ; save al for later use
  661.     mov    di,[bp+4+0]
  662.     mov    bx,if_version
  663.     mov    [di],bx            ; return version
  664.     mov    al,if_class        ; class
  665.     xor    ah,ah
  666.     mov    di,[bp+4+2]
  667.     mov    [di],ax            ; return as an int
  668.     mov    di,[bp+4+4]
  669.     mov    dx,if_type
  670.     mov    [di],dx            ; type
  671.     mov    di,[bp+4+6]
  672.     xor    ch,ch
  673.     mov    cl,if_num
  674.     mov    [di],cx            ; interface number, as an int
  675.     pop    ax            ; recover al
  676.     mov    di,[bp+4+8]
  677.     xor    ah,ah
  678.     mov    al,if_func
  679.     mov    [di],ax            ; functionality, as an int
  680.     mov    ax,1            ; C style exit status, 1 = success
  681.     pop    bx
  682.     pop    cx
  683.     pop    di
  684.     pop    bp
  685.     ret
  686. _pdinfo    endp
  687.  
  688. ; int pdclose(int handle)
  689. ; Close a Packet Driver or ODI handle.
  690. ; Returns (in AX) 1 if successful, else 0.
  691.     public    _pdclose
  692. _pdclose    proc    near
  693.     push    bp
  694.     mov    bp,sp
  695.     push    bx
  696.     mov    bx,[bp+4+0]        ; handle
  697.     cmp    useodi,0        ; using ODI?
  698.     je    pdclos2            ; e = no
  699.     mov    ax,bx            ; get handle
  700.     call    odiunbind        ; unbind from LSL and MLID
  701.     jmp    short pdclos3
  702.  
  703. pdclos2:mov    ah,pd_release        ; release_type
  704.     call    pktdrvr
  705.  
  706. pdclos3:mov    ax,1            ; assume success
  707.     jnc    pdclos1            ; nc = success
  708.     xor    ax,ax            ; 0 for failure
  709. pdclos1:pop    bx
  710.     pop    bp
  711.     ret
  712. _pdclose    endp
  713.  
  714. ; int pdaccess(char *type, int typelen, int *handle)
  715. ; Register access for packet TYPE with the Packet Driver or ODI
  716. ; Provides a handle for the TYPE.
  717. ; Returns 1 for success, 0 for failure.
  718.     public    _pdaccess
  719. _pdaccess proc    near
  720.     push    bp
  721.     mov    bp,sp
  722.     push    es
  723.     push    si
  724.     push    di
  725.     push    ds
  726.     push    es
  727.     mov    ax,dgroup        ; set up data segment addressibility
  728.     mov    ds,ax
  729.     mov    al,if_class        ; interface class (frame)
  730.     mov    bx,if_type        ; interface type (vendor)
  731.     mov    dl,if_num        ; interface number (board number)
  732.     xor    dh,dh
  733.     mov    si,[bp+4+0]        ; get offset of packet TYPE buffer
  734.     mov    cx,[bp+4+2]        ; typelen (length of buf contents)
  735.     cmp    useodi,0        ; using ODI?
  736.     je    pdacc8            ; e = no
  737.     mov    ax,[si]            ; provide TYPE
  738.     call    odibind            ; Bind to a virtual board
  739.     jc    pdacc1            ; c = fail, error code in AX
  740.     jmp    short pdacc9        ; store handle returned in AX
  741.  
  742. pdacc8:    cmp    if_class,IF_SLIP    ; SLIP?
  743.     jne    pdacc3            ; ne = no
  744.     xor    cx,cx            ; TYPE len = 0 means accept all types
  745. pdacc3:    mov    di,cs            ; ES:DI is our Packet Driver receiver
  746.     mov    es,di
  747.     mov    di,offset pdrcvr    ; local receiver
  748.     mov    ah,pd_access        ; set access
  749.     call    pktdrvr
  750.     jc    pdacc1            ; c = failure
  751. pdacc9:    mov    si,[bp+4+4]        ; offset of handle
  752.     mov    [si],ax            ; return the handle
  753. pdacc1:    mov    ax,1            ; C level status, 1 = success
  754.     jnc    pdacc2            ; nc = success
  755.     xor    ax,ax            ; 0 = failure
  756. pdacc2:    pop    es
  757.     pop    ds
  758.     pop    di
  759.     pop    si
  760.     pop    es
  761.     pop    bp
  762.     ret
  763. _pdaccess endp
  764.  
  765. ; int pkt_send(char *buffer, int length)
  766. ; returns 1 on success, 0 on failure
  767. ; Send a packet.
  768.     public    _pkt_send
  769. _pkt_send    proc    near
  770.     push    bp
  771.     mov    bp,sp
  772.     push    es
  773.     push    ds
  774.     push    si
  775.     push    di            ; don't trust lower levels on regs
  776.     push    cx
  777.     push    dx
  778.     mov    ax,DGROUP        ; segment of outgoing buffer
  779.     mov    ds,ax            ;  will be DS:SI for Packet Driver
  780.     cmp    useodi,0        ; using ODI?
  781.     je    pktsen5            ; e =no, use PD
  782.     call    _odi_busy        ; is transmitter busy?
  783.     or    ax,ax            ; returned response
  784.     jz    pktsen4            ; z = not busy
  785.     stc                ; fail
  786.     jmp    short pktsen1
  787.  
  788. pktsen4:mov    si,[bp+4+0]        ; buffer's offset (seg is dgroup)
  789.     mov    cx,[bp+4+2]        ; buffer's length
  790.     call    odixmt            ; do LSL transmit with DS:SI and CX
  791.     sti                ; interrupts on (odixmt turns off)
  792.     jc    pktsen1            ; c = internal failure to send
  793.     or    ax,ax            ; LSL status
  794.     jz    pktsen1            ; z = success
  795.     stc                ; set carry for failure
  796.     jmp    short pktsen1        ; done (AX non-zero if error)
  797. ; Note that checking for transmission errors is not readily done with async
  798. ; sending, so we cross our fingers and hope for the best.
  799.                     ;
  800.                     ; Packet Driver sending
  801. pktsen5:mov    si,[bp+4+0]        ; buffer's offset (seg is dgroup)
  802.     mov    cx,[bp+4+2]        ; buffer's length
  803.     mov    ah,pd_send        ; send packet (buffer = ds:si)
  804.     call    tstart            ; start transmit timer
  805.     call    pktdrvr            ; invoke Packet Driver
  806.     call    tstop            ; stop transmit timer
  807.                     ; common exit
  808. pktsen1:mov    ax,1            ; return C level success (1)
  809.     jnc    pktsen2            ; nc = success
  810.     xor    ax,ax            ; else C level failure (0)
  811. pktsen2:pop    dx
  812.     pop    cx
  813.     pop    di
  814.     pop    si
  815.     pop    ds
  816.     pop    es
  817.          pop    bp
  818.     ret
  819. _pkt_send endp
  820.  
  821. ; int odi_busy(void)
  822. ; Return non-zero if ODI is unable to accept new transmission at this time.
  823.     public _odi_busy
  824. _odi_busy proc near
  825.     xor    ax,ax            ; prepare not-busy/false (0) response
  826.     cmp    useodi,0        ; using ODI?
  827.     je    odi_bsx            ; e = no, using Packet Driver
  828.     mov    cx,3            ; loop counter
  829. odi_bs1:cmp    ecbx_busy,0        ; is ODI transmit done yet?
  830.     je    odi_bs2            ; e = yes, else we can't touch it yet
  831.     push    bx
  832.     push    cx
  833.     push    bp            ; playing safe
  834.     push    es
  835.     mov    bx,PROTSUP_RELINQUISH_CONTROL
  836.     call    lslsupport        ; give time to LSL and MLID
  837.     pop    es
  838.     pop    bp
  839.     pop    cx
  840.     pop    bx
  841.     loop    odi_bs1            ; retry til ready or exhausted
  842. odi_bs2:mov    al,ecbx_busy        ; return 0 for not-busy
  843.     xor    ah,ah
  844. odi_bsx:ret
  845. _odi_busy endp
  846.  
  847. ; Our Packet Driver receiver, far called only by the Packet Driver and our
  848. ; local ODI code (odircvr and odircmp).
  849. ; Packet buffer linked list -
  850. ;    each link is    db flag        ; 1 = free, 2 = in use, 4 = allocated
  851. ;                    ;  but not in use yet, 0 = end of buf,
  852. ;                    ;  8 = read but not freed.
  853. ;            db _pktwnum    ; sequential number of pkt written
  854. ;            dw count    ; length of data field
  855. ;            db count dup (?) ; the allocated data field
  856. ;    The head of the chain has a link like all others.
  857. ;    The end of the chain has a link with flag == 0 and count = -BUFISZE
  858. ;    to point to the beginning of the buffer (circular).
  859. ;    Packet buffer garbage collection is done after a buffer has been
  860. ;    transferred to us, and does so by relinking adjacent free blocks.
  861. ; _pktbuf_wrote is used to remember the link where the last write occurred
  862. ; and should be initialized to the tail link to point the next write to
  863. ; the beginning of the buffer.
  864. ; The Packet Driver and our ODI routines call this first with AX = 0 to 
  865. ; obtain a buffer pointer in ES:DI from us (0:0 if we refuse the pkt) with 
  866. ; CX = packet size, and again later with AX = 1 to post completion.
  867.  
  868. pdrcvr    proc    far            ; Packet Driver receiver
  869.     or    ax,ax            ; kind of request (0, 1)
  870.     jz    pdrcvr1            ; z = first, get-a-buffer
  871.     ; Second upcall, packet has xfered, DS:SI set by caller to buffer
  872.      push    ds
  873.     push    si
  874.     push    cx
  875.     push    bx
  876.     push    ax            ; assume DS:SI is one of our buffers
  877.     mov    ax,DGROUP
  878.     mov    ds,ax            ; set ds to our data segment
  879.     call    rstop            ; stop receive timer
  880.     or    si,si            ; is it legal (from first upcall)?
  881.     jz    pdrcvr11        ; z = no, ignore this call
  882.     sub    si,linksize        ; backup to link info
  883.     mov    cx,100            ; number of trials to find space
  884.     cmp    byte ptr [si].flag,4    ; is this buffer allocated (4)?
  885.     jne    pdrcvr8            ; ne = no, do cleanups and quit
  886.     mov    byte ptr [si].flag,2    ; flag = 2 for buffer is now ready
  887.     mov    si,pktbufoff         ; start of packet buffer
  888.                     ; join contiguous free links
  889. pdrcvr8:mov    al,[si].flag        ; flags byte
  890.     cmp    al,1            ; is link free?
  891.     jne    pdrcvr10        ; ne = no, look for a free link
  892. pdrcvr9:mov    bx,[si].count        ; count (length) of this link
  893.     mov    al,[bx+si+linksize].flag; flag of following link
  894.     cmp    al,1             ; is next link free?
  895.     jne    pdrcvr10        ; ne = no, look for free link
  896.     mov    ax,[bx+si+linksize].count ; count taken from next link
  897.     add    ax,linksize        ;  plus the next link's info field
  898.     add    [si].count,ax        ; add it to this count (merge links)
  899.     loop    pdrcvr9            ; re-examine this new longer link
  900.     jmp    short pdrcvr11        ; too many trials, abandon effort
  901.  
  902. pdrcvr10:or    al,al            ; end of list?
  903.     jz    pdrcvr11        ; z = yes
  904.     add    si,[si].count        ; look at next link (add count)
  905.     add    si,linksize        ; and link info
  906.     loop    pdrcvr8            ; keep looking
  907. pdrcvr11:pop    ax
  908.     pop    bx
  909.     pop    cx
  910.     pop    si
  911.     pop    ds
  912.     ret
  913.  
  914. pdrcvr1:push    ds            ; First upcall, provide buffer ptr
  915.     push    dx            ; return buffer in ES:SI
  916.     mov    di,dgroup        ; get local addressibility
  917.     mov    ds,di
  918.     mov    es,di            ; packet buffer is in same group
  919.     cmp    useodi,0        ; using Packet Driver?
  920.     jne    pdrcvr1a        ; ne = no, ODI, has separate rstart
  921.     call    rstart            ; start receive timer
  922. pdrcvr1a:mov    di,_pktbuf_wrote    ; where last write occurred
  923.     or    di,di            ; NULL?
  924.     jz    pdrcvr4            ; z = yes, write nothing
  925.     mov    dl,100            ; retry counter, breaks endless loops
  926.     cmp    [di].flag,1        ; is this link free?
  927.     je    pdrcvr5            ; e = yes, use it
  928.  
  929. pdrcvr2:add    di,[di].count        ; point at next link (add count and
  930.     add    di,linksize        ;  link overhead)
  931.     dec    dl            ; loop breaker count down
  932.     jz    pdrcvr4            ; z = in an endless loop, exit
  933.     cmp    [di].flag,1        ; is this link free (1)?
  934.     je    pdrcvr5            ; e = yes, setup storage
  935.     cmp    di,_pktbuf_wrote     ; have we come full circle?
  936.     jne    pdrcvr2            ; ne = no, keep looking
  937. pdrcvr4:pop    dx
  938.     pop    ds            ; failure or buffer not available (0)
  939.     xor    ax,ax            ; return what we received in ax
  940.     xor    di,di            ; return ES:DI as null to reject
  941.     mov    es,di
  942.     ret
  943.                     ; this link is free
  944. pdrcvr5:add    cx,2            ; defense for 8/16 bit xfr mistakes
  945.     mov    ax,[di].count        ; length of available data space
  946.     cmp    ax,cx            ; cx is incoming size, enough space?
  947.     jl    pdrcvr2            ; l = no, go to next link
  948.     mov    [di].flag,4        ; mark link flag as being alloc'd (4)
  949.     mov    dh,_pktwnum         ; write pkt sequencer number
  950.     mov    [di].bufnum,dh        ; store in buffer, permits out of
  951.     inc    _pktwnum        ;  temporal order deliveries
  952.     mov    _pktbuf_wrote,di    ; remember where we wrote last
  953.     sub    ax,cx            ; allocated minus incoming packet
  954.     cmp    ax,60+linksize        ; enough for new link and miminal pkt?
  955.     jl    pdrcvr6            ; l = not enough for next pkt
  956.     mov    [di].count,cx        ; update space really used
  957.     push    di            ; save this link pointer
  958.     add    di,linksize        ; plus current link info
  959.     add    di,cx            ; plus space used = new link point
  960.     sub    ax,linksize        ; available minus new link info
  961.     mov    [di].flag,1        ; mark new link as free (1)
  962.     mov    [di].count,ax        ; size of new free data area
  963.     pop    di            ; return to current link
  964. pdrcvr6:add    di,linksize        ; point at data portion
  965. pdrcvr7:xor    ax,ax            ; return what we received in ax
  966.     pop    dx            ; CX is size of requested buffer
  967.     pop    ds            ; ES:DI is the pkt buffer address
  968.     ret
  969. pdrcvr    endp
  970.  
  971. ; Check for Windows enhanced mode. Return carry set if true, else carry clear.
  972. chkwin proc    near
  973.     push    es
  974.     mov    ah,getintv        ; check for valid Int 2Fh handler
  975.     mov    al,2fh            ; vector 2fh
  976.     int    dos            ; to es:bx
  977.     mov    ax,es
  978.     pop    es
  979.     or    ax,bx            ; check if vector exists
  980.     jnz    chkwin2            ; nz = yes
  981.     stc
  982.     ret
  983.  
  984. chkwin2:mov    ax,1683h        ; Windows 3, get current virt machine
  985.     int    2fh
  986.     cmp    ax,1683h        ; virtual machine, if any
  987.     je    chkwin3            ; e = no Windows, ok to proceed
  988.     stc
  989.     ret
  990. chkwin3:clc                ; not Windows enhanced mode
  991.     ret
  992. chkwin endp
  993.  
  994. ; Begin Novell ODI support routines
  995. ; Note that while we use Ethernet_II (6 dest, 6 source, 2 TYPE bytes) to/from
  996. ; internal consumers the frame format to/from ODI is in the hands of ODI.
  997. ; Hopefully this will permit TCP/IP operation over all supported frame types.
  998. ; ARP/RARP packets are sized to the frame in use.
  999. ;
  1000. ; Check for LSL presence, and if present then get entry points.
  1001. ; Returns carry set if failure, else carry clear.
  1002. ; This procedure is closely modeled upon the Novell example.
  1003. odichk    proc    near
  1004.     cmp    useodi,0        ; already inited?
  1005.     je    odichk0            ; e = no
  1006.     clc
  1007.     ret
  1008. odichk0:call    chkwin            ; check for Windows enhanced mode
  1009.     jnc    odichk5            ; nc = not, continue
  1010.     ret                ; return failure
  1011. odichk5:push    es
  1012.     mov    ah,getintv        ; get LSL via multiplexer interrupt
  1013.     mov    al,2fh            ; vector 2fh
  1014.     int    dos            ; to es:bx
  1015.     mov    ax,es
  1016.     or    ax,bx            ; check if vector exists
  1017.     jnz    odichk1            ; nz = yes
  1018.     pop    es
  1019.     stc
  1020.     ret
  1021.  
  1022. odichk1:mov    ax,0c000h        ; look at multiplexer slots c0 et seq
  1023.     push    si
  1024.     push    di
  1025. odichk2:push    ax
  1026.     int    2fh
  1027.     cmp    al,0ffh            ; is slot in use?
  1028.     pop    ax
  1029.     je    odichk4            ; e = yes, check for LSL being there
  1030. odichk3:inc    ah            ; next slot
  1031.     or    ah,ah            ; wrapped?
  1032.     jnz    odichk2            ; nz = no, keep looking
  1033.     pop    di
  1034.     pop    si
  1035.     pop    es
  1036.     stc                ; not found, fail
  1037.     ret
  1038.  
  1039. odichk4:mov    di,si            ; es:si should point to "LINKSUP$"
  1040.     mov    si,offset DGROUP:lslsig    ; expected signature
  1041.     mov    cx,lslsiglen        ; length
  1042.     cld
  1043.     repe    cmpsb            ; check for signature
  1044.     jne    odichk3            ; ne = no match, try next Int 2fh slot
  1045.     mov    word ptr lslinit,bx    ; found entry, save init entry point
  1046.     mov    ax,es            ;  returned in es:bx
  1047.     mov    word ptr lslinit+2,ax
  1048.     mov    ax,ds
  1049.     mov    es,ax            ; get LSL main support/service addrs
  1050.     mov    si,offset DGROUP:lslsupport ; address of LSL entry point array
  1051.     mov    bx,2            ; request support/service entry points
  1052.         ; fills in far addresses of lslsupport and lslservice routines
  1053.     call    lslinit            ; call LSL initialization routine
  1054.     pop    di
  1055.     pop    si
  1056.     pop    es
  1057.     clc                ; success
  1058.     ret
  1059. odichk    endp
  1060.  
  1061. ; Bind a protocol TYPE to an ODI virtual board.
  1062. ; Enter with TYPE (big endian/network order) in AX.
  1063. ; Packet reception begins immediately upon a successful bind.
  1064. ; Uses NET.CFG if information is available.
  1065. ; Obtain StackID (our ident to the LSL), ProtID (ident of LSL's decoder),
  1066. ; and boardnumber (the logical board), then bind to start reception. Do for
  1067. ; one of our protocols.
  1068. ; Returns PD handle (TYPE) in AX and carry clear upon success, else carry set.
  1069. odibind proc    near
  1070.     push    ax
  1071.     push    bx
  1072.     push    si
  1073.     push    di
  1074.     push    es
  1075.     mov    bx,DGROUP
  1076.     mov    es,bx
  1077.     cmp    ax,ip_type            ; IP, 0x0008h?
  1078.     jne    odibind1            ; ne = no
  1079.     mov    ax,offset DGROUP:ip_string    ; put IP string in request
  1080.     mov    bx,offset ip_rcvr        ; set address of receiver esr
  1081.     mov    di,offset DGROUP:ip_stackid    ; set address of stackid struc
  1082.     jmp    short odibind3
  1083.  
  1084. odibind1:cmp    ax,arp_type            ; ARP, 0x0608?
  1085.     jne    odibind2            ; ne = no
  1086.     mov    ax,offset DGROUP:arp_string
  1087.     mov    bx,offset arp_rcvr
  1088.     mov    di,offset DGROUP:arp_stackid
  1089.     jmp    short odibind3
  1090.  
  1091. odibind2:cmp    ax,rarp_type            ; RARP, 0x3580?
  1092.     je    odibind2a            ; e = yes
  1093.     jmp    odibindx            ; ne = no, fail
  1094. odibind2a:mov    ax,offset DGROUP:rarp_string
  1095.     mov    bx,offset rarp_rcvr
  1096.     mov    di,offset DGROUP:rarp_stackid
  1097.  
  1098. odibind3:mov    word ptr registerstk.StackNamePtr,ax ; insert ptr to string
  1099.     mov    word ptr registerstk.StackReceiveHandler,bx  ; setup esr addr
  1100.  
  1101. ; Note: to use Prescan or Default registrations delete StackNamePtr & StackID.
  1102. ; StackID is not used with these latter methods, and their reception begins
  1103. ; at registration rather than at bind (so this area would be redesigned).
  1104.     mov    bx,PROTSUP_REGISTER_STACK ; register the protocol by name
  1105.     mov    si,offset DGROUP:registerstk ; registration form pointer
  1106.     push    di            ; save ptr to xxx_stackid storage
  1107.     call    lslsupport        ; call LSL with the address in es:si
  1108.     pop    di
  1109.     jz    odibind3a        ; z = success
  1110.     jmp    odibindx        ; nz = failure
  1111. odibind3a:mov    [di].pstack,bx        ; save returned StackID (LSL's handle
  1112.                     ;  for our protocol stack)
  1113.     cmp    readnetcfg,0        ; have read NET.CFG for BIND info?
  1114.     jne    odibind4        ; ne = yes
  1115.     mov    useboard,-1        ; clear board-to-use word
  1116.     call    getbind            ; find Kermit's bind board in NET.CFG
  1117.     inc    readnetcfg        ; say have read the file
  1118.     cmp    word ptr bdname,256*'#'+2 ; is board name #<digit>?
  1119.     jne    odibind4        ; ne = no, assume regular driver name
  1120.     mov    al,bdname+2        ; get ascii digit
  1121.     sub    al,'1'            ; remove ascii bias (external=1 based)
  1122.     xor    ah,ah            ;  but we are zero based internally
  1123.     cmp    al,8            ; arbitrary limit of 8 boards
  1124.     ja    odibind4        ; a = out of range, ignore value
  1125.     mov    useboard,ax        ; and make this the board number
  1126.     mov    bdname,0        ; and don't use bdname as a name
  1127. odibind4:mov    [di].pboard,0        ; assume board zero to start loop
  1128.     mov    ax,useboard        ; board to be used, if any
  1129.     or    ax,ax            ; boards 0 and up are legal
  1130.     jl    odibind5        ; l = no board found yet, search
  1131.     mov    [di].pboard,ax        ; specify board, get ProtID
  1132.  
  1133. odibind5:mov    bx,PROTSUP_GET_MLID_CTL_ENTRY    ; get MLID control entry
  1134.     mov    ax,[di].pboard        ; for this board
  1135.     push    di
  1136.     call    lslsupport        ; call LSL for the address to es:si
  1137.     pop    di
  1138.     mov    word ptr mlidcont,si
  1139.     mov    word ptr mlidcont+2,es    ; MLID control routine
  1140.     jz    odibind7        ; z=success, have a board to work with
  1141.     cmp    ax,LSLERR_NO_MORE_ITEMS ; out of items?
  1142.     je    odibind5a        ; e = yes, no more boards
  1143.       cmp    ax,LSLERR_ITEM_NOT_PRESENT ; other boards may exist?
  1144.     je    odibind7        ; e = yes
  1145. odibind5a:jmp    odibindx        ; fail
  1146.  
  1147. odibind7:mov    bx,PROTSUP_GET_PID_PROTNUM_MLIDNUM ; get ProtID from StackID
  1148.     mov    ax,[di].pstack        ; StackID
  1149.     mov    cx,[di].pboard        ;  and assumed board number
  1150.     mov    si,dgroup
  1151.     mov    es,si            ; set es:di to the ProtID buffer
  1152.     lea    si,[di].pprotid        ;  in our storage slot per protocol
  1153.     push    di
  1154.     call    lslsupport        ; ask LSL for the ProtID string
  1155.     pop    di            ;  to that 6-byte buffer
  1156.     jz    odibind9        ; z = success, found a recognizer
  1157.     cmp    useboard,0        ; has a board been pre-identified?
  1158.     jge    odibind5a        ; ge = yes, so the matchup failed
  1159.     inc    [di].pboard        ; next board
  1160.     jmp    short odibind5        ; keep looking for a board
  1161.  
  1162. odibind9:mov    bx,GET_MLID_CONFIGURATION ; get MLID config ptr to es:si
  1163.     mov    ax,[di].pboard
  1164.     call    mlidcont        ; call MLID control routine
  1165.     jnz    odibindx        ; nz = failure
  1166.     cmp    bdname,0        ; was a board name bound via BIND?
  1167.     je    odibin10        ; e = no, don't check on it
  1168.     push    es            ; save pointer to MLID config table
  1169.     push    di
  1170.     push    si
  1171.     les    di,es:[si].MCardShortName ; get short name of this board
  1172.     lea    si,bdname        ; desired board name string
  1173.     mov    cl,bdname        ; length of desired board name
  1174.     inc    cl            ; include length byte
  1175.     xor    ch,ch
  1176.     cld
  1177.     repe    cmpsb            ; compare  len,string  for both
  1178.     pop    si
  1179.     pop    di
  1180.     pop    es
  1181.     je    odibin10        ; e = found desired board
  1182.     inc    [di].pboard        ; try next board
  1183.     jmp    short odibind5        ; keep looking for the desired board
  1184.  
  1185. odibin10:mov    ax,[di].pboard        ; get current board number
  1186.     mov    useboard,ax        ; remember for next protocol
  1187.     mov    ax,es:[si].MWorstDataSize ; max header, leaving this size
  1188.     sub    ax,20+20        ; minus IP and TCP headers
  1189.     mov    _mss,ax            ; set new operational value
  1190.     mov    bx,es:[si].MFrameID    ; frame ident, for get_hwd
  1191.     call    get_hwd            ; get hardware specifics
  1192.     push    es
  1193.     push    si            ; save config pointer
  1194.     lea    si,es:[si].MNodeAddress    ; point to address in config struct
  1195.     push    ds            ; save ds
  1196.     push    di
  1197.     mov    di,offset DGROUP:_eth_addr; where our MAC address is stored
  1198.     mov    ax,ds
  1199.     mov    cx,es
  1200.     mov    es,ax
  1201.     mov    ds,cx
  1202.     cld
  1203.     mov    cx,6            ; MAC address length, bytes, fixed
  1204.     rep    movsb            ; copy MAC address to global array
  1205.     pop    di
  1206.     pop    ds
  1207.     pop    si            ; recover configuration table pointer
  1208.     pop    es
  1209.     mov    tells_len,0        ; presume no lookahead data length
  1210.     test    es:[si].MFlags,Len_Info    ; capas bit for length provided (new)
  1211.     jz    odibin12        ; z = does not provide
  1212.     inc    tells_len        ; say provides length
  1213.  
  1214. odibin12:mov    bx,PROTSUP_BIND_STACK_TO_MLID    ; Bind stack to MLID
  1215.     mov    ax,[di].pstack        ; StackID
  1216.     mov    cx,[di].pboard        ; board number
  1217.     call    lslsupport        ; bind our protocol stack to board
  1218.     jnz    odibindx        ; nz = failure
  1219.     pop    es            ; received packets can interrupt now
  1220.     pop    di
  1221.     pop    si
  1222.     pop    bx
  1223.     pop    ax
  1224.     clc
  1225.     ret
  1226. odibindx:pop    es
  1227.     pop    di
  1228.     pop    si
  1229.     pop    bx
  1230.     pop    ax
  1231.     stc                ; say failure
  1232.     ret
  1233. odibind endp
  1234.  
  1235. ; Worker for odibind. Find NET.CFG, extract name of board driver from pair of
  1236. ; lines reading as below (Protocol must be in column 1, bind must be indented)
  1237. ; Protocol Kermit            Kermit's main section header
  1238. ;   bind  <board_driver_name>        indented, without the <> signs
  1239. ;or
  1240. ; Protocol Kermit
  1241. ;   bind #<digit>            selects DOS driver load order (from 1)
  1242. ;
  1243. ; Examples -
  1244. ; Protocol Kermit
  1245. ;   bind exos
  1246. ;or
  1247. ; Protocol Kermit
  1248. ;   bind #2
  1249. ;            and elsewhere there is the board driver section:
  1250. ; Link Driver exos
  1251. ;
  1252. ; If found put the board driver name in array bdname, as length byte, string,
  1253. ; then a null. If not found make length byte bdname be zero. We treat NET.CFG
  1254. ; as case insensitive.
  1255. ; Unless we use the special Kermit section then LSL will assign to us the
  1256. ; first board loaded by DOS supporting the frame kind of our protocol.
  1257. ; Link Driver section line "Protocol name type frame"  simply associates a
  1258. ; frame kind with the name and type, but not with a board. L.D. section line
  1259. ; frame <frame kind> attaches that frame kind to the board, if it fits.
  1260. ; Kermit uses "name" in the above line to pinpoint a protocol, not a board.
  1261. ; Add keyword MYIP to obtain a dynamically assigned IP value from Telebit's
  1262. ; ODI PPP driver. The user must say "Set TCP Address Telebit-PPP" for this
  1263. ; to have effect.
  1264. getbind    proc    near
  1265.     mov    bdname,0        ; clear board name length
  1266.     mov    _tempip,0        ; clear dynamic IP string count
  1267.     push    ds
  1268.     mov    bx,GENSERV_GET_NETCFG_PATH ; get fully formed NET.CFG name
  1269.     call    lslservice        ;  from LSL general services to ds:dx
  1270.     jz    getbin1            ; z = success
  1271.     pop    ds            ; fail
  1272.     ret
  1273. getbin1:mov    ah,fopen        ; open file NET.CFG
  1274.     mov    al,40h            ;  for reading, deny none
  1275.     int    dos            ; returns file handle in ax
  1276.     pop    ds
  1277.     mov    temp,ax            ; save handle for getbyte
  1278.     jnc    getbin2            ; nc = success
  1279.     ret                ; carry set for failure
  1280.  
  1281. getbin2:mov    bx,1            ; subscript, at start of a line
  1282.  
  1283. getbin3:call    getbyte            ; read a byte, uppercased
  1284.     jnc    getbin4            ; nc = success
  1285.     ret                ; c = end of file
  1286. getbin4:cmp    protword[bx],al        ; compare to "PROTOCOL"
  1287.     jne    getbin5            ; ne = failure, scan for end of line
  1288.     inc    bx
  1289.     cmp    bl,protword        ; length, matched all bytes?
  1290.     jbe    getbin3            ; be = no, match more
  1291.     jmp    short getbin6        ; ae = yes, next phrase
  1292.     ret                ; fail out 
  1293.  
  1294. getbin5:cmp    al,LF            ; end of a line?
  1295.     je    getbin2            ; e = yes, scan for PROTOCOL again
  1296.     call    getbyte
  1297.     jnc    getbin5            ; keep consuming line material
  1298.     ret                ; fail out at end of file
  1299.                     ; Short Name following "PROTOCOL"
  1300. getbin6:call    getbyte            ; get separator char, discard
  1301.     jnc    getbin7
  1302.     ret                ; c = eof
  1303. getbin7:call    getbyte            ; read short name of protocol
  1304.     jnc    getbin7a        ; nc = text
  1305.     ret                ; return on eof
  1306. getbin7a:cmp    al,' '            ; white space?
  1307.     jbe    getbin7            ; be = yes, stay in this state
  1308.     mov    bx,1            ; subscript
  1309. getbin8:cmp    psname[bx],al        ; compare to our protocol short name
  1310.     jne    getbin5            ; ne = failure, scan for end of line
  1311.     cmp    bl,psname        ; matched all bytes?
  1312.     jae    getbin9            ; ae = yes, next phrase
  1313.     inc    bx
  1314.     call    getbyte            ; get next byte to match
  1315.     jnc    getbin8            ; nc = not eof yet
  1316.     ret
  1317.  
  1318. getbin9:call    getbyte            ; go to next line, enforce whitespace
  1319.     jc    getbin20        ; c = eof
  1320.     cmp    al,LF            ; end of a line?
  1321.     jne    getbin9            ; ne = no, scan for end of line
  1322.     call    getbyte            ; look for whitespace
  1323.     jc    getbin20        ; c = eof
  1324.     cmp    al,'#'            ; comment line?
  1325.     je    getbin9            ; e = yes, get next line
  1326.     cmp    al,';'            ; comment line?
  1327.     je    getbin9            ; e = yes, get next line
  1328.     cmp    al,' '            ; required whitespace?
  1329.     ja    getbin5            ; a = no, start over
  1330.  
  1331. getbin10:call    getbyte            ; look for keyword "BIND"
  1332.     jc    getbin20
  1333.     cmp    al,' '            ; white space?
  1334.     jbe    getbin10        ; be = yes, stay in this state
  1335.     mov    bx,1            ; subscript
  1336.     cmp    al,'M'            ; M for MYIP?
  1337.     je    getbin30        ; e = yes
  1338.     cmp    al,'B'            ; B for BIND?
  1339.     jne    getbin9            ; ne = no, next line
  1340.  
  1341. getbin12:cmp    bdname,0        ; have bind name yet?
  1342.     je    getbin13        ; e = no
  1343.     jmp    getbin9            ; else get next line
  1344. getbin13:cmp    bindword[bx],al        ; compare to "BIND"
  1345.     jne    getbin9
  1346.     cmp    bl,bindword        ; matched all bytes?
  1347.     jae    getbin14        ; ae = yes, next phrase
  1348.     call    getbyte
  1349.     jc    getbin20        ; c = eof
  1350.     inc    bx
  1351.     jmp    short getbin13        ; keep reading
  1352.  
  1353. getbin14:call    getbyte            ; skip white space before board name
  1354.     jc    getbin20            
  1355.     cmp    al,' '            ; white space?
  1356.     jbe    getbin14        ; be = yes, skip it
  1357.  
  1358. getbin15:mov    bl,bdname        ; board name, length byte, starts at 0
  1359.     xor    bh,bh
  1360.     inc    bl
  1361.     mov    bdname,bl        ; update length of board driver name
  1362.     xor    ah,ah            ; get a null
  1363.     mov    word ptr bdname[bx],ax    ; store as board short name,null
  1364.     cmp    bx,15            ; legal limit on short name?
  1365.     jbe    getbin16        ; be = ok
  1366.     mov    bdname,ah        ; illegal, clear board name length
  1367.     jmp    getbin9            ; get next line
  1368. getbin16:call    getbyte
  1369.     jc    getbin20        ; reached eof, is ok
  1370.            cmp    al,' '            ; usable text?
  1371.     ja    getbin15        ; a = yes, else stop storing name
  1372.     jmp    getbin9            ; get next line
  1373.  
  1374. getbin20:ret
  1375.     
  1376. getbin30:cmp    _tempip,0        ; have IP word already?
  1377.     je    getbin31        ; e = no
  1378.     jmp    getbin9            ; get new line
  1379.  
  1380. getbin31:cmp    myipword[bx],al        ; compare to "MYIP"
  1381.     jne    getbin9            ; ne = failure, start over
  1382.     cmp    bl,myipword        ; matched all bytes?
  1383.     jb    getbin32        ; b = no
  1384.     jmp    short getbin33
  1385.  
  1386. getbin32:call    getbyte
  1387.     jc    getbin20        ; c = eof
  1388.     inc    bx
  1389.     jmp    getbin31        ; keep reading
  1390.  
  1391. getbin33:call    getbyte            ; skip white space before IP address
  1392.     jc    getbin20            
  1393.     cmp    al,' '            ; white space?
  1394.     jbe    getbin33        ; be = yes, skip it
  1395.  
  1396. getbin34:mov    bl,_tempip        ; our IP, length byte, starts at 0
  1397.     xor    bh,bh
  1398.     inc    bl
  1399.     mov    _tempip,bl        ; update length of IP string
  1400.     xor    ah,ah            ; get a null
  1401.     mov    word ptr _tempip[bx],ax    ; store as IP, null
  1402.     cmp    bx,15            ; legal limit on IP?
  1403.     jbe    getbin35        ; be = ok
  1404.     mov    _tempip+1,ah        ; illegal, clear IP
  1405.     jmp    getbin9            ; and quit
  1406. getbin35:call    getbyte
  1407.     jc    getbin36        ; reached eof, is ok
  1408.            cmp    al,' '            ; usable text?
  1409.     ja    getbin34        ; a = yes, else stop storing name
  1410.     jmp    getbin9            ; get next line
  1411. getbin36:ret    
  1412. getbind    endp
  1413.  
  1414. ; Worker for getbind. Delivers one byte per call from NET.CFG, upper cased.
  1415. ; Returns carry set and NET.CFG file closed at end of file.
  1416. ; Temp has NET.CFG file handle, tempb is our one byte buffer for disk i/o.
  1417. getbyte    proc    near
  1418.     mov    dx,offset tempb        ; ds:dx points to start of buffer
  1419.     mov    ah,fread        ; read from file to buffer
  1420.     mov    cx,1            ; this many bytes
  1421.     push    bx
  1422.     mov    bx,temp            ; get file handle
  1423.     int    dos
  1424.     pop    bx
  1425.     jc    getbyt2            ; c = failure
  1426.     cmp    ax,1            ; got the single byte?
  1427.     jb    getbyt2            ; b = no, failure
  1428.     mov    al,tempb        ; return read byte
  1429.     cmp    al,'z'            ; in lower case range?
  1430.     ja    getbyt1            ; a = no
  1431.     cmp    al,'a'            ; in lower case range?
  1432.     jb    getbyt1            ; b = no
  1433.     and    al,not 20h        ; lower to upper case
  1434. getbyt1:clc                ; carry clear for success
  1435.     ret                ; return char in AL
  1436. getbyt2:push    bx
  1437.     mov    bx,temp            ; file handle
  1438.     mov    ah,fclose        ; close the file
  1439.     int    dos
  1440.     pop    bx
  1441.     stc                ; say EOF or other failure
  1442.      ret
  1443. getbyte    endp
  1444.  
  1445. ; Worker for odibind.
  1446. ; Enter with BX holding Novell frame type from the MLID configuration table. 
  1447. ; Set _arp_hardware and _MAC_len and return BX holding _MAC_len value, else 
  1448. ; if frame is not supported return BX = 0. These two values are needed by the
  1449. ; ARP functions. This list searching method is to accomodate the ever 
  1450. ; expanding quantity of frame types appearing with ODI; we deal with those we
  1451. ; understand (sic).
  1452. get_hwd    proc    near
  1453.     push    es
  1454.     push    di
  1455.     mov    ax,DGROUP
  1456.     mov    es,ax
  1457.     mov    al,bl            ; get frame value (MLID config)
  1458.     xor    bx,bx            ; prepare no-match return value
  1459.     mov    di,offset frame_type    ; list to search
  1460.     mov    cx,num_frames        ; number of elements in the list
  1461.     cld
  1462.     repne    scasb            ; byte search
  1463.     jne    get_hwd1        ; ne = no match, fail
  1464.     sub    di,offset frame_type+1    ; make di be an index along the list
  1465.     mov    al,hardware_type[di]    ; ARP/RARP hardware type ident
  1466.     xor    ah,ah            ; return in local (host) order
  1467.     mov    _arp_hardware,ax    ; hardware type for ARP/RARP pkts
  1468.     mov    bl,frame_adlen[di]    ; array of MAC lengths for frame types
  1469.     xor    bh,bh
  1470.     mov    _MAC_len,bx        ; save MAC address length (1..6 bytes)
  1471.     pop    di
  1472.     pop    es
  1473.     ret
  1474. get_hwd1:mov    _arp_hardware,bx    ; hardware type 0 for ARP/RARP pkts
  1475.     mov    _MAC_len,bx        ; save MAC address length (0 bytes)
  1476.     pop    di
  1477.     pop    es
  1478.     ret                ; return _MAC_len in BX
  1479. get_hwd    endp
  1480.  
  1481. ; Unbind a protocol TYPE from an ODI virtual board
  1482. ; Enter with protocol TYPE (net order) in AX, return carry set if failure.
  1483. ; The TYPE is used as our handle to the application.
  1484. ; Prescan and Default methods call lslsupport with the board number in AX 
  1485. ; rather than StackID and use matching PROTSUP_DEREGISTER_* code.
  1486. odiunbind proc    near
  1487.     cmp    ax,ip_type        ; IP, 0x0008h?
  1488.     jne    odiunb1            ; ne = no
  1489.     mov    ax,ip_stackid.pstack    ; StackID
  1490.     jmp    short odiunb3
  1491.  
  1492. odiunb1:cmp    ax,arp_type        ; ARP, 0x0608?
  1493.     jne    odiunb2            ; ne = no
  1494.     mov    ax,arp_stackid.pstack
  1495.     jmp    short odiunb3
  1496.  
  1497. odiunb2:cmp    ax,rarp_type        ; RARP, 0x3580?
  1498.     jne    odiunb4            ; ne = no
  1499.     mov    ax,rarp_stackid.pstack
  1500.  
  1501. odiunb3:mov    bx,PROTSUP_DEREGISTER_STACK ; deregister stack (StackID in AX)
  1502.     call    lslsupport        ; stops reception now
  1503.     jnz    odiunb4            ; nz = failure
  1504.     clc                ; success
  1505.     ret
  1506. odiunb4:stc                ; failure
  1507.     ret
  1508. odiunbind endp
  1509.  
  1510. ; ODI receive interrupt handler, for use only by the LSL.
  1511. ; Called with DS:DI pointing to lookahead structure, interrupts are off.
  1512. ; Returns ES:SI pointing at ECB, AX = 0 if we want pkt, AX = 8001h if decline.
  1513. ; There are three of these, one each for IP, ARP, and RARP. All have the
  1514. ; same calling convention and all jump to odircv to do the real work. 
  1515. ; The length of the arriving packet is available if the MLID supports the
  1516. ; new (mid-May 1992) capability, our "tells_len"; otherwise we make an
  1517. ; intelligent guess based on the protocol header. These entry points can be 
  1518. ; called multiple times before receive-completion, and likely will be, so we
  1519. ; use several ecb's to accept requests.
  1520. ip_rcvr    proc    far
  1521.     push    bx
  1522.     push    cx
  1523.     push    di
  1524.     push    ds
  1525.     mov    cx,ds            ; DS:DI from LSL
  1526.     mov    es,cx            ; use ES for LSL items
  1527.     mov    ax,DGROUP        ; set DS to our data segment
  1528.     mov    ds,ax
  1529.     call    rstart            ; start timer
  1530.     push    es
  1531.     push    si
  1532.     cmp    tells_len,0        ; have data length available?
  1533.     je    ip_rec1            ; e = no
  1534.     les    si,es:[di].DataLookAheadDataSize ; ptr to what it says
  1535.     mov    cx,word ptr es:[si]    ; get length of data field
  1536.     add    cx,4            ; for overzealous board transfers
  1537.     pop    si
  1538.     pop    es
  1539.     jmp    far ptr odircv
  1540.  
  1541. ip_rec1:les    si,es:[di].LookAheadPtr    ; point at data lookahead ptr
  1542.     mov    cx,word ptr es:[si+2]    ; IP pkt header, length word
  1543.     cmp    byte ptr es:[si],45h    ; validate IP pkt kind (ver/hlen)?
  1544.     pop    si
  1545.     pop    es
  1546.     jne    ip_rcvr1        ; ne = invalid, decline
  1547.     xchg    ch,cl            ; net to local order
  1548.     add    cx,14+2            ; our MAC level addressing + 2 safety
  1549. ip_rec2:mov    ax,ip_stackid.pstack    ; StackID for ecb structure
  1550.     mov    rcvtype,ip_type        ; store protocol TYPE int
  1551.     jmp    odircv
  1552.  
  1553. ip_rcvr1:add    pstats.PIgnoredRxPackets,1 ; update ODI statistics counter
  1554.     adc    pstats.PIgnoredRxPackets+2,0
  1555.     mov    ax,LSLERR_OUT_OF_RESOURCES ; decline the packet
  1556.     or    ax,ax            ; set Z flag to match AX
  1557.     pop    ds
  1558.     pop    di
  1559.     pop    cx
  1560.     pop    bx
  1561.     ret
  1562. ip_rcvr    endp
  1563.  
  1564. ; RARP protocol receive service routine, similar to ip_rcvr.
  1565. rarp_rcvr proc    far
  1566.     push    bx
  1567.     push    cx
  1568.     push    di
  1569.     push    ds
  1570.     mov    cx,ds            ; DS:SI from LSL
  1571.     mov    es,cx
  1572.     mov    ax,DGROUP        ; set DS to our data segment
  1573.     mov    ds,ax
  1574.     mov    ax,rarp_stackid.pstack    ; StackID for ecb structure
  1575.     mov    rcvtype,rarp_type    ; store protocol TYPE int
  1576.     jmp    short arp_common    ; do ARP/RARP common code
  1577. rarp_rcvr endp
  1578.  
  1579. ; ARP protocol receive service routine, similar to ip_rcvr.
  1580. arp_rcvr proc    far
  1581.     push    bx
  1582.     push    cx
  1583.     push    di
  1584.     push    ds
  1585.     mov    cx,ds            ; DS:SI from LSL
  1586.     mov    es,cx
  1587.     mov    ax,DGROUP        ; set DS to our data segment
  1588.     mov    ds,ax
  1589.     mov    ax,arp_stackid.pstack    ; StackID for ecb structure
  1590.     mov    rcvtype,arp_type    ; store protocol TYPE int
  1591.  
  1592. arp_common:                ; common code for ARP/RARP
  1593.     push    es
  1594.     push    si
  1595.     cmp    tells_len,0        ; have data length available?
  1596.     je    arp_com1        ; e = no
  1597.     les    si,es:[di].DataLookAheadDataSize ; ptr to what it says
  1598.     mov    cx,word ptr es:[si]    ; get length of data field
  1599.     add    cx,4            ; for overzealous board transfers
  1600.     pop    si
  1601.     pop    es
  1602.     jmp    short odircv
  1603.  
  1604. arp_com1:les    si,es:[di].LookAheadPtr    ; point at lookahead ptr for Data
  1605.     mov    cx,word ptr es:[si+4]    ; ARP/RARP pkt header, length bytes
  1606.     add    cl,ch            ; add HA and IP address lengths
  1607.     xor    ch,ch
  1608.     add    cl,cl            ; for host and target
  1609.     adc    ch,0
  1610.     add    cx,8            ; plus ARP/RARP main header
  1611.     cmp    word ptr es:[si+2],ip_type ; ARP/RARP Protocol type of IP?
  1612.     pop    si
  1613.     pop    es
  1614.     jne    ip_rcvr1        ; ne = invalid, decline
  1615.     ; fall through to odircv
  1616. arp_rcvr endp
  1617.  
  1618. ; General worker for ip_rcvr, arp_rcvr, rarp_rcvr. These are invoked by the
  1619. ; LSL when their kind of packet arrives. This module creates the ECB and
  1620. ; dispatches it to the LSL. Operating at LSL interrupt level.
  1621. ; ES:DI is ptr to ODI Lookahead structure, DS is our data seg (DGROUP).
  1622. ; AX is stackid for invoked protocol kind, CX is (guessed) pkt length overall.
  1623. ; Rcvtype is the current invoked protocol TYPE (0008h is IP etc).
  1624. ; When done store in the ecb's protocolws array:
  1625. ;    dw    protocol TYPE (0008h for IP etc)
  1626. ;    dw    subscript of this ecbr item (for use by odircmp)
  1627. ;    dw    <unused>,<unused>
  1628. ; Return ES:SI as pointer to a free ecb and AX = 0 to accept pkt, else
  1629. ; return AX = 8001h to decline pkt (and to ignore ES:SI). Set Z flag to
  1630. ; match AX value.
  1631. odircv    proc    far
  1632.     add    pstats.PtotalRxPackets,1 ; update ODI statistics counter
  1633.     adc    pstats.PtotalRxPackets+2,0
  1634.     push    ax
  1635.     push    cx
  1636.     mov    cx,ecbr_qty        ; number of receive ecb's
  1637.     xor    bx,bx            ; find a free ecb for this packet
  1638.     mov    ax,offset DGROUP:ecbr    ; start of receive ecbs
  1639. odircv8:cmp    ecbr_busy[bx],0        ; is this ECB free?
  1640.     jne    odircv9            ; ne = no, try next
  1641.     mov    ecbr_num,bx        ; remember index for end of proc
  1642.     mov    bx,ax            ; offset of free ecb
  1643.     pop    cx
  1644.     pop    ax
  1645.     jmp    short odircv2        ; use ds:[bx] for address of ecb
  1646. odircv9:inc    bx            ; next byte in busy array
  1647.     add    ax,size ecbstruct    ; size of an ecb
  1648.     loop    odircv8
  1649.     pop    cx            ; failed to find a free ecbr
  1650.     pop    ax
  1651.  
  1652. odircv1:add    pstats.PIgnoredRxPackets,1 ; update ODI statistics counter
  1653.     adc    pstats.PIgnoredRxPackets+2,0
  1654.     mov    ax,LSLERR_OUT_OF_RESOURCES ; decline the packet
  1655.     or    ax,ax            ; set Z flag for ODI
  1656.     pop    ds
  1657.     pop    di
  1658.     pop    cx
  1659.     pop    bx
  1660.     ret
  1661.                     ; ds:[bx] is ptr to a free ecbr
  1662. odircv2:mov    [bx].stackid,ax        ; StackID from odircv entry points
  1663.     mov    ax,es:[di].LBoardNum    ; boardnum from ProtocolID lookahead
  1664.     mov    [bx].boardnum,ax    ; store in ecbr
  1665.     mov    ax,rcvtype        ; get TYPE from odircv entry points
  1666.     mov    word ptr [bx].protocolws,ax    ; save TYPE for odircmp
  1667.     mov    ax,ecbr_num        ; ecbr index
  1668.     mov    word ptr [bx].protocolws+2,ax    ; save index for odircmp
  1669.     cmp    cx,46            ; min packet for Ethernet + 4 spare
  1670.     jae    odircv3            ; ae = no padding needed here
  1671.     mov    cx,46            ; padded min pkt plus 4 spare bytes
  1672. odircv3:push    bx            ; get a buffer of length CX bytes
  1673.     xor    ax,ax            ; set AX = 0 for PD "get buf" call
  1674.     call    pdrcvr            ; use PD buffer allocator code
  1675.     pop    bx            ; ES:DI = buffer pointer, CX = length
  1676.     mov    ax,es
  1677.     or    ax,di            ; check for refused pkt (es:di = NULL)
  1678.     jz    odircv1            ; z = pkt refused (no buffer space)
  1679.     add    di,6+6+2        ; skip our MAC header for ecb use
  1680.     sub    cx,6+6+2        ; less same length for MLID
  1681.     mov    [bx].frag1addr,di    ; offset of buffer which MLID sees
  1682.     mov    ax,es
  1683.     mov    [bx].frag1addr+2,ax    ; seg of buffer
  1684.     mov    [bx].datalen,cx        ; length of buffer for MLID/LSL use
  1685.     mov    [bx].frag1len,cx    ; ditto
  1686.     mov    ax,DGROUP        ; segment of our ecb's
  1687.     mov    es,ax
  1688.     mov    si,bx            ; return ES:SI pointing to ECB
  1689.     mov    bx,ecbr_num        ; get ecbr index
  1690.     mov    ecbr_busy[bx],1        ; mark this ecbr as busy
  1691.     pop    ds
  1692.     pop    di
  1693.     pop    cx
  1694.     pop    bx
  1695.     xor    ax,ax            ; return AX = 0 to accept
  1696.     ret
  1697. odircv    endp
  1698.  
  1699. ; ODI receive-complete call-back routine for use only by the LSL.
  1700. ; Enter with ES:SI pointing at ECB, interrupts are off.
  1701. ; Returns nothing.
  1702. ; There is no guarantee that this routine will be called in the sequence
  1703. ; which packets arrived, so we carry the bookkeeping in the delivered ECB:
  1704. ; TYPE is for Ethernet_II struct results, ecbr_busy is don't-touch interlock.
  1705. ; Note that we have to construct our own "destination" MAC address.
  1706. ; es:[si].status is 0 (success), 8006h (buffer overrun), 8007h (canceled).
  1707. ; StackID field is from LSL, and it's 0ffffh if using Prescan, and undefined
  1708. ; if using Default. The manual says LSL, but not MLID, calls are ok in here.
  1709. odircmp    proc    far
  1710.     push    ds
  1711.     push    ax
  1712.     push    bx
  1713.     push    cx
  1714.     mov    ax,DGROUP
  1715.     mov    ds,ax            ; set ds to our data segment
  1716.     cmp    es:[si].frag1addr+2,0    ; segment of pkt being confirmed
  1717.     je    odircmp6        ; e = illegal, ignore this call
  1718.     cmp    es:[si].status,0     ; check ECB status for failure
  1719.     je    odircmp1        ; e = success
  1720.     mov    es:[si].protocolws,0    ; write TYPE of 0 to permit queueing
  1721. odircmp1:push    di            ; put dest,src,TYPE into pkt buffer
  1722.     push    es
  1723.     push    si            ; save ecbr's si
  1724.     mov    cl,byte ptr es:[si].driverws ; kind of destination, from LSL
  1725.     mov    di,es:[si].frag1addr    ; start of our pkt buffer + 6+6+2
  1726.     sub    di,6+6+2        ; back to start, for our MAC header
  1727.     push    ds            ; WATCH this, presumes ES == DS!
  1728.     pop    es            ; set ES to DS (where pkt buffer is)
  1729.     mov    si,offset DGROUP:_eth_addr ; our hardware address
  1730.     cmp    cl,ECB_BROADCAST     ; a broadcast?
  1731.     jne    odircmp2        ; ne = no, use our address
  1732.     mov    si,offset DGROUP:bcast    ; fill with all 1's
  1733. odircmp2:mov    cx,3            ; 6 byte addresses to our application
  1734.     cld
  1735.      rep    movsw            ; store source address in pkt buffer
  1736.     pop    si            ; recover ecb si
  1737.     push    si            ; save it again
  1738.     mov    ax,es:[si].protocolws     ; get TYPE, from odircv
  1739.     lea    si,es:[si].immaddr    ; offset to MAC address of sender
  1740.     mov    cx,3            ; three words worth, garbage and all
  1741.     rep    movsw            ; copy to packet buffer
  1742.     stosw                ; and write TYPE to packet buffer
  1743.     pop    si
  1744.     pop    es
  1745.     pop    di
  1746.     mov    cx,es:[si].datalen    ; length of data field from ecb
  1747.     add    cx,6+6+2        ; plus space for dest,src,TYPE
  1748.     push    es            ; save ecb's es:si
  1749.     push    si
  1750.     mov    si,es:[si].frag1addr    ; offset of pkt being confirmed
  1751.     sub    si,6+6+2        ; adj to beginning, for our MAC header
  1752.     mov    ax,1            ; set AX = 1 for buffer done call
  1753.     call    pdrcvr            ; do post processing of buffer
  1754.     pop    si
  1755.     pop    es
  1756.     xor    ax,ax
  1757.     mov    es:[si].frag1addr+2,ax    ; clear pkt buffer pointer (seg)
  1758.     mov    es:[si].protocolws,ax     ; clear packet TYPE
  1759.     mov    bx,es:[si].protocolws+2    ; point to ecb index
  1760.     mov    ecbr_busy[bx],al    ; say this ecb is free now
  1761. odircmp6:
  1762.     call    rstop            ; stop receive timer
  1763.     pop    cx
  1764.     pop    bx
  1765.     pop    ax
  1766.     pop    ds
  1767.     ret
  1768. odircmp    endp
  1769.  
  1770. ; ODI transmission routine
  1771. ; Enter with ds:si pointing at full Ethernet_II packet, cx = length (bytes)
  1772. ; Once sent the ecb belongs to ODI until the xmt-complete routine is called.
  1773. odixmt    proc    near
  1774.     push    si
  1775.     push    di
  1776.     push    es
  1777.     mov    ax,ds
  1778.     mov    es,ax
  1779.     mov    ax,cx            ; overall Ethernet_II length
  1780.     sub    ax,6+6+2        ; omit our MAC header
  1781.     jc    odixmt6            ; c = failure, abandon
  1782.     mov    ecbx.datalen,ax        ; setup ECB overall data
  1783.     mov    ecbx.frag1len,ax    ; and fragment length
  1784.  
  1785.     mov    cx,3            ; three words of dest MAC address
  1786.     mov    di,offset DGROUP:ecbx.immaddr    ; destination address in ecb
  1787.     rep    movsw            ; copy destination MAC address
  1788.     add    si,6            ; skip source Ethernet address
  1789.     lodsw                ; get protocol TYPE, move it to AX
  1790.     mov    ecbx.frag1addr,si    ; offset of packet data
  1791.     mov    si,ds
  1792.     mov    ecbx.frag1addr+2,si    ; segment of packet data
  1793.  
  1794. ; Note: Prescan and Default methods use Raw Send: put 0ffffh in StackID
  1795. ; and include the full frame header in the data field. Check MLID 
  1796. ; configuration word ModeFlags, MRawSendBit, for Raw Send capability.
  1797.  
  1798.     cmp    ax,ip_type        ; IP, 0x0008h?
  1799.     jne    odixmt1            ; ne = no
  1800.     mov    si,offset DGROUP:ip_stackid
  1801.     jmp    short odixmt3
  1802.  
  1803. odixmt1:cmp    ax,arp_type        ; ARP, 0x0608?
  1804.     jne    odixmt2            ; ne = no
  1805.     mov    si,offset DGROUP:arp_stackid
  1806.     jmp    short odixmt3
  1807.  
  1808. odixmt2:cmp    ax,rarp_type        ; RARP, 0x3580?
  1809.     jne    odixmt6            ; ne = error, do not send
  1810.     mov    si,offset DGROUP:rarp_stackid
  1811.  
  1812. odixmt3:mov    cx,5            ; stackid, protid, boardnum
  1813.     mov    di,offset DGROUP:ecbx.stackid    ; get stack ident area
  1814.     rep    movsw            ; copy to ecbx
  1815.  
  1816.     mov    ecbx_busy,1        ; set ECBx busy flag to busy state
  1817.     add    pstats.PtotalTxPackets,1 ; update ODI statistics counter
  1818.     adc    pstats.PtotalTxPackets+2,0
  1819.  
  1820.     mov    si,offset DGROUP:ecbx    ; set es:si to ECB
  1821.     mov    bx,PROTSUP_SEND_PACKET    ; send it
  1822.     call    tstart            ; start transmit timer
  1823.     call    lslsupport        ; call LSL with ecbx ptr in es:si
  1824.     call    tstop            ; stop transmit timer
  1825.     clc                ; success so far, ints are still off
  1826. odixmt6:pop    es
  1827.     pop    di
  1828.     pop    si
  1829.     ret
  1830. odixmt    endp
  1831.  
  1832. ; ODI transmission-complete processor, for use only by the LSL.
  1833. ; Returns nothing. Unlocks ECB busy flag.
  1834. odixcmp    proc    far
  1835.     push    ds
  1836.     push    ax
  1837.     mov    ax,DGROUP        ; set addressibility
  1838.     mov    ds,ax
  1839.     mov    ecbx_busy,0        ; set ECB busy flag to not-busy
  1840.     pop    ax
  1841.     pop    ds
  1842.     ret
  1843. odixcmp    endp
  1844.  
  1845. ; ODI Protocol (that's us) Control routine, required, called from outside.
  1846. ; In principle we should have one of these for each protocol (IP, ARP, RARP)
  1847. ; by putting a different StackControlHandler address in registerstk, but I 
  1848. ; doubt that anyone is that interested in such detailed counts.
  1849. ; Return AX clear, Z flag set, and ES:SI as table pointer if success, else
  1850. ; return AX with error code and Z flag clear.
  1851. pcontrol proc    far
  1852.     cmp    bx,GET_STACK_CONFIGURATION ; get stack configuration?
  1853.     jne    pcont1            ; ne = no
  1854.     mov    si,DGROUP
  1855.     mov    es,si            ; es:si points to configuration table
  1856.     mov    si,offset DGROUP:pconfig; the table
  1857.     xor    ax,ax            ; set Z flag
  1858.     ret
  1859. pcont1:    cmp    bx,GET_STACK_STATISTICS    ; get stack statistics?
  1860.     jne    pcont2            ; ne = no
  1861.     mov    si,DGROUP
  1862.     mov    es,si            ; es:si points to statistics table
  1863.     mov    si,offset DGROUP:pstats    ; the table
  1864.     xor    ax,ax            ; set Z flag
  1865.     ret
  1866. pcont2:    mov    ax,LSLERR_OUT_OF_RESOURCES ; other functions, report error
  1867.     or    ax,ax            ; clear Z flag
  1868.     ret
  1869. pcontrol endp
  1870.  
  1871. ; timer support routines
  1872. rstart    proc    near
  1873.     pushf
  1874.     test    _kdebug,2        ; DEBUG_TIMING?
  1875.     jz    rstart1
  1876.     push    ax
  1877.     in    al,40h        ; 8254 timer channel T0, read LSB
  1878.     xchg    ah,al
  1879.     in    al,40h        ; read MSB
  1880.     xchg    al,ah        ; order properly
  1881.     mov    startrtic,ax
  1882.     pop    ax
  1883. rstart1:popf
  1884.     ret
  1885. rstart    endp
  1886.  
  1887. rstop    proc    near
  1888.     pushf
  1889.     test    _kdebug,2        ; DEBUG_TIMING?
  1890.     jz    rstop1
  1891.     push    ax
  1892.     in    al,40h        ; 8254 timer channel T0, read LSB
  1893.     xchg    ah,al
  1894.     in    al,40h        ; read MSB
  1895.     xchg    al,ah        ; order properly
  1896.     mov    stoprtic,ax
  1897.     pop    ax
  1898. rstop1:    popf
  1899.     ret
  1900. rstop    endp
  1901.  
  1902. tstart    proc    near
  1903.     pushf
  1904.     test    _kdebug,2        ; DEBUG_TIMING?
  1905.     jz    tstart1
  1906.     push    ax
  1907.     in    al,40h        ; 8254 timer channel T0, read LSB
  1908.     xchg    ah,al
  1909.     in    al,40h        ; read MSB
  1910.     xchg    al,ah        ; order properly
  1911.     mov    startttic,ax
  1912.     pop    ax
  1913. tstart1:popf
  1914.     ret
  1915. tstart    endp
  1916.  
  1917. tstop    proc    near
  1918.     pushf
  1919.     test    _kdebug,2        ; DEBUG_TIMING?
  1920.     jz    tstop1
  1921.     push     ax
  1922.     in    al,40h        ; 8254 timer channel T0, read LSB
  1923.     xchg    ah,al
  1924.     in    al,40h        ; read MSB
  1925.     xchg    al,ah        ; order properly
  1926.     sti            ; for odixmt
  1927.     mov    stopttic,ax
  1928.     call    showtime    ; display timing results (microseconds)
  1929.     pop    ax
  1930. tstop1:    popf
  1931.     ret
  1932. tstop    endp
  1933.  
  1934. showtime proc    near
  1935.     push    es
  1936.     push    di
  1937.     push    bx
  1938.     mov    bx,data
  1939.     mov    es,bx
  1940.     mov    al,es:crt_cols            ; screen columns
  1941.     mul    es:crt_lins            ; screen lines
  1942.     mov    bx,ax
  1943.     add    bx,40                ; column 40
  1944.     shl    bx,1
  1945.     mov    di,es:tv_sego            ; current screen offset
  1946.     add    bx,di
  1947.     mov    di,es:tv_segs            ; current screen segment
  1948.     mov    es,di
  1949.     mov    ax,startrtic
  1950.     sub    ax,stoprtic
  1951.     jns    showti1
  1952.     neg    ax
  1953. showti1:sub    ax,overhead
  1954.     mov    di,bx
  1955.     call    decout
  1956.     mov    byte ptr es:[di],'R'
  1957.  
  1958.     mov    ax,startttic
  1959.     sub    ax,stopttic
  1960.     jns    showti2
  1961.     neg    ax
  1962. showti2:sub    ax,overhead
  1963.     mov    di,bx
  1964.     add    di,5*2
  1965.     call    decout
  1966.     mov    byte ptr es:[di],'T'
  1967.     pop    bx
  1968.     pop    di
  1969.     pop    es
  1970.     ret
  1971. showtime endp
  1972.  
  1973. decout    proc    near        ; display decimal number in ax
  1974.     push    cx
  1975.     push    dx
  1976.     mov    byte ptr es:[di],' '    ; clear screen of old info
  1977.     mov    byte ptr es:[di+2],' '
  1978.     mov    byte ptr es:[di+4],' '
  1979.     mov    byte ptr es:[di+6],' '
  1980.     mov    byte ptr es:[di+8],' '
  1981.     test    ax,8000h        ; negative?
  1982.     jz    decout1            ; z = no
  1983.     neg    ax
  1984.     mov    byte ptr es:[di],'-'    ; minus sign
  1985.     inc    di
  1986.     inc    di
  1987. decout1:mov    cl,t0count        ; timer chip T0 count factor
  1988.     mov    dx,2000
  1989.     shr    dx,cl        ; t0count compensation (by 1 or 2 shifts)
  1990.     mul    dx
  1991.     mov    cx,1193
  1992.     div    cx
  1993.     mov    cx,10            ; set the numeric base
  1994.     call    mvalout            ; convert and output value
  1995.     pop    dx
  1996.     pop    cx
  1997.     ret
  1998. decout    endp
  1999.  
  2000. mvalout    proc    near        ; output number in ax using base in cx
  2001.                 ; corrupts ax and dx
  2002.     xor    dx,dx        ; clear high word of numerator
  2003.     div    cx        ; (ax / cx), remainder = dx, quotient = ax
  2004.     push    dx        ; save remainder for outputting later
  2005.     or    ax,ax        ; any quotient left?
  2006.     jz    mvalout1    ; z = no
  2007.     call    mvalout        ; yes, recurse
  2008. mvalout1:pop    dx        ; get remainder
  2009.     add    dl,'0'        ; make digit printable
  2010.     cmp    dl,'9'        ; above 9?
  2011.     jbe    mvalout2    ; be = no
  2012.     add    dl,'A'-1-'9'    ; use 'A'--'F' for values above 9
  2013. mvalout2:mov    es:[di],dl
  2014.     inc    di
  2015.     inc    di
  2016.     ret
  2017. mvalout    endp
  2018. _TEXT    ends
  2019.         end
  2020.  
  2021.