home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / mskermit.tar.gz / mskermit.tar / msnpdi.asm < prev    next >
Assembly Source File  |  1998-05-28  |  66KB  |  2,021 lines

  1.     NAME    MSNPDI
  2. ; File MSNPDI.ASM
  3. ; Packet Driver and ODI interface 
  4. ;
  5. ;    Copyright (C) 1982, 1997, 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    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 60h..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.     inc    _kdebug            ; turn on to get overhead
  484.     cli                ; compute timing overhead
  485.     call    rstart
  486.     call    rstop
  487.     mov    ax,startrtic
  488.     sub    ax,stoprtic
  489.     jns    pdinit8
  490.     neg    ax
  491. pdinit8:mov    overhead,ax        ; overhead, ticks
  492.     in    al,40h        ; 8254 timer channel T0, read LSB
  493.     xchg    ah,al
  494.     in    al,40h        ; read MSB
  495.     xchg    al,ah        ; order properly
  496.     mov    bx,ax
  497.     mov    ax,1        ; number of milliseconds to delay
  498.     call    pcwait        ; delay AX milliseconds
  499.     mov    ax,1
  500.     call    pcwait
  501.     mov    ax,1
  502.     call    pcwait
  503.     mov    ax,1
  504.     call    pcwait
  505.     in    al,40h        ; 8254 timer channel T0, read LSB
  506.     xchg    ah,al
  507.     in    al,40h        ; read MSB
  508.     xchg    al,ah        ; order properly
  509.     sti
  510.     dec    _kdebug        ; restore value
  511.     sub    ax,bx
  512.     jns    pdinit9
  513.     neg    ax
  514. pdinit9:add    ax,500        ; round up to 4+millisec
  515.     xor    dx,dx
  516.     mov    cx,1000        ; micro to millisec
  517.     div    cx
  518.     shr    ax,1        ; divide by four timings
  519.     shr    ax,1        ; get 1 or 2 for 1 millisec
  520.     and    al,3        ; keep only two lower bits
  521.     mov    t0count,al    ; remember for msnpdi.asm
  522.  
  523.  
  524.     mov    ax,[bp+4+0]        ; get offset of pktbuf
  525.     mov    pktbufoff,ax        ; save locally
  526.     cmp    _kpdint,'DO'        ; special indicator to use ODI?
  527.     je    short pdinit2        ; e = yes, do ODI
  528.     mov    cx,60h            ; interrupt range
  529.     cmp    _kpdint,0        ; user value given for PD Int?
  530.     je    pdinit1            ; e = no
  531.     mov    cx,_kpdint        ; use it
  532.     mov    _kpdint,0        ; assume no user value
  533. pdinit1:mov    ah,getintv        ; get interrupt vector to es:bx
  534.     mov    al,cl            ; vector number
  535.     int    dos
  536.     mov    si,offset dgroup:pdsignature ; look for signature
  537.     push    cx
  538.     mov    cx,pdslen        ; length of string
  539.     mov    di,bx
  540.     add    di,3            ; sig starts 3 bytes from entry point
  541.     cld
  542.     repe    cmpsb            ; compare bytes
  543.     pop    cx
  544.     je    pdinit3            ; e = found a match
  545.     cmp    _kpdint,0        ; user value given?
  546.     jne    pdinit2            ; ne = yes, so fail
  547.     inc    cx
  548.     cmp    cx,80h            ; at end of range?
  549.     jna    pdinit1            ; na = not yet, try another int
  550.                     ; no Packet Driver found or use ODI
  551. pdinit2:call    odichk            ; see if ODI is available now
  552.     jnc    pdinit6            ; nc = yes
  553.     cmp    _kpdint,'DO'        ; special indicator to use ODI?
  554.     jne    pdinit5            ; ne = no, have tried Packet Drivers
  555.     mov    _kpdint,0        ; setup for Packet Driver test
  556.     mov    cx,60h            ; scan from this interrupt
  557.     jmp    pdinit1
  558. pdinit6:mov    useodi,1        ; say using ODI
  559.     mov    _kpdint,'DO'        ; signal ODI via PD interrupt variable
  560.     mov    if_class,1        ; say Ethernet_II for internal work
  561.     mov    di,[bp+4+2]        ; get offset of user's buffer
  562.     mov    cx,eaddr_len        ; length of address provided
  563.     mov    si,offset DGROUP:SLIPmac ; get fake Ethernet address
  564.     cld                ;  in case code wants it early
  565.     push    ds
  566.     pop    es
  567.     push    di
  568.     rep    movsb            ; copy to user buffer
  569.     mov    cx,ecbr_qty
  570.     mov    di,offset DGROUP:ecbr_busy ; clear receive ecb busy flags
  571.     xor    al,al
  572.     rep    stosb
  573.     mov    ecbx_busy,al        ; and transmitter busy lock
  574.     pop    di
  575.     clc
  576. pdinit5:jmp    pdret            ; exit (carry set is failure)
  577.                      ; Packet Driver details
  578. pdinit3:mov    byte ptr PKTDRVI+1,cl    ; force in new PD interrupt, code mod
  579.     mov    _kpdint,cx        ; remember interrupt number
  580.                     ; find Ethernet address
  581.     mov    ah,pdgetinfo        ; get Packet Driver information
  582.     mov    al,0ffh
  583.     xor    bx,bx            ; optional handle
  584.     push    ds            ; this call changes ds and si
  585.     push    si
  586.     call    pktdrvr            ; call the Packet Driver
  587.     pop    si
  588.     pop    ds
  589.     jc    pdret            ; c = failure
  590.     mov    if_type,dx        ; save details for access calls
  591.     mov    if_class,ch
  592.     mov    if_num,cl
  593.     mov    if_func,al
  594.     mov    if_version,bx
  595.     mov    ah,pd_access        ; access packets
  596.     mov    al,ch            ; Ethernet class
  597.     mov    bx,dx            ; type
  598.     mov    dl,cl            ; interface number
  599.     mov    cx,iptypelen        ; type length for iptype
  600.     mov    si,offset dgroup:iptype    ; address of TYPE
  601.     mov    di,cs
  602.     mov    es,di
  603.     mov    di,offset pdrcvr    ; ES:DI is our Packet Driver receiver
  604.     call    pktdrvr
  605.     jc    pdret            ; c = failure
  606.     mov    bx,ax            ; put returned handle in BX
  607.     mov    _arp_hardware,0001h    ; Type 1 hardware, Ethernet
  608.     mov    _MAC_len,6        ; 6 bytes of MAC level address
  609.     mov    ax,DGROUP        ; our data segment
  610.     mov    es,ax            ; segment of Ethernet address buffer
  611.     mov    di,[bp+4+2]        ; get offset of user's buffer
  612.     mov    cx,eaddr_len        ; length of address wanted
  613.     cmp    if_class,IF_SLIP    ; interface class of SLIP?
  614.     jne    pdinit4            ; ne = no
  615.     mov    si,offset DGROUP:SLIPmac ; get fake Ethernet address
  616.     cld
  617.     push    di            ; save in case PD actually uses it
  618.     rep    movsb            ; copy to user buffer
  619.     pop    di
  620.     mov    _arp_hardware,0        ; no hardware type
  621.     mov    _MAC_len,0        ; no MAC level address
  622.     mov    _mss,1006-44        ; set SLIP max frame size too
  623. pdinit4:mov    ah,pd_get_address    ; get the Ethernet address
  624.     push    bx            ; save handle
  625.     call    pktdrvr            ; get Ethernet address to es:di buf
  626.     pop    bx
  627.     pushf                ; save carry flag
  628.     jnc    pdinit7
  629.     cmp    if_class,IF_SLIP    ; interface class of SLIP?
  630.     jne    pdinit7            ; ne = no
  631.     popf
  632.     clc                ; forgive error of SLIP8250 v11.x
  633.     pushf
  634. pdinit7:mov    ah,pd_release        ; release this Type, bx has handle
  635.     call    pktdrvr
  636.     popf                ; recover carry flag
  637. pdret:    mov    ax,1            ; return C status, 1 for success
  638.     jnc    pdret1
  639.     xor    ax,ax            ; 0 for failure
  640. pdret1:    pop    ds            ; success
  641.     pop    di
  642.     pop    si
  643.     pop    es
  644.     mov    sp,bp            ; restore stack
  645.     pop    bp            ; recover bp reg
  646.     ret
  647. _pdinit    endp
  648.  
  649. ; int pdinfo(& int version, & int class, & int pdtype, & int number,
  650. ;  & int functionality)
  651. ; Get Packet Driver pedigree
  652.     public    _pdinfo
  653. _pdinfo    proc    near
  654.     push    bp
  655.     mov    bp,sp
  656.     push    di
  657.     push    cx
  658.     push    bx
  659.     push    ax            ; save al for later use
  660.     mov    di,[bp+4+0]
  661.     mov    bx,if_version
  662.     mov    [di],bx            ; return version
  663.     mov    al,if_class        ; class
  664.     xor    ah,ah
  665.     mov    di,[bp+4+2]
  666.     mov    [di],ax            ; return as an int
  667.     mov    di,[bp+4+4]
  668.     mov    dx,if_type
  669.     mov    [di],dx            ; type
  670.     mov    di,[bp+4+6]
  671.     xor    ch,ch
  672.     mov    cl,if_num
  673.     mov    [di],cx            ; interface number, as an int
  674.     pop    ax            ; recover al
  675.     mov    di,[bp+4+8]
  676.     xor    ah,ah
  677.     mov    al,if_func
  678.     mov    [di],ax            ; functionality, as an int
  679.     mov    ax,1            ; C style exit status, 1 = success
  680.     pop    bx
  681.     pop    cx
  682.     pop    di
  683.     pop    bp
  684.     ret
  685. _pdinfo    endp
  686.  
  687. ; int pdclose(int handle)
  688. ; Close a Packet Driver or ODI handle.
  689. ; Returns (in AX) 1 if successful, else 0.
  690.     public    _pdclose
  691. _pdclose    proc    near
  692.     push    bp
  693.     mov    bp,sp
  694.     push    bx
  695.     mov    bx,[bp+4+0]        ; handle
  696.     cmp    useodi,0        ; using ODI?
  697.     je    pdclos2            ; e = no
  698.     mov    ax,bx            ; get handle
  699.     call    odiunbind        ; unbind from LSL and MLID
  700.     jmp    short pdclos3
  701.  
  702. pdclos2:mov    ah,pd_release        ; release_type
  703.     call    pktdrvr
  704.  
  705. pdclos3:mov    ax,1            ; assume success
  706.     jnc    pdclos1            ; nc = success
  707.     xor    ax,ax            ; 0 for failure
  708. pdclos1:pop    bx
  709.     pop    bp
  710.     ret
  711. _pdclose    endp
  712.  
  713. ; int pdaccess(char *type, int typelen, int *handle)
  714. ; Register access for packet TYPE with the Packet Driver or ODI
  715. ; Provides a handle for the TYPE.
  716. ; Returns 1 for success, 0 for failure.
  717.     public    _pdaccess
  718. _pdaccess proc    near
  719.     push    bp
  720.     mov    bp,sp
  721.     push    es
  722.     push    si
  723.     push    di
  724.     push    ds
  725.     push    es
  726.     mov    ax,dgroup        ; set up data segment addressibility
  727.     mov    ds,ax
  728.     mov    al,if_class        ; interface class (frame)
  729.     mov    bx,if_type        ; interface type (vendor)
  730.     mov    dl,if_num        ; interface number (board number)
  731.     xor    dh,dh
  732.     mov    si,[bp+4+0]        ; get offset of packet TYPE buffer
  733.     mov    cx,[bp+4+2]        ; typelen (length of buf contents)
  734.     cmp    useodi,0        ; using ODI?
  735.     je    pdacc8            ; e = no
  736.     mov    ax,[si]            ; provide TYPE
  737.     call    odibind            ; Bind to a virtual board
  738.     jc    pdacc1            ; c = fail, error code in AX
  739.     jmp    short pdacc9        ; store handle returned in AX
  740.  
  741. pdacc8:    cmp    if_class,IF_SLIP    ; SLIP?
  742.     jne    pdacc3            ; ne = no
  743.     xor    cx,cx            ; TYPE len = 0 means accept all types
  744. pdacc3:    mov    di,cs            ; ES:DI is our Packet Driver receiver
  745.     mov    es,di
  746.     mov    di,offset pdrcvr    ; local receiver
  747.     mov    ah,pd_access        ; set access
  748.     call    pktdrvr
  749.     jc    pdacc1            ; c = failure
  750. pdacc9:    mov    si,[bp+4+4]        ; offset of handle
  751.     mov    [si],ax            ; return the handle
  752. pdacc1:    mov    ax,1            ; C level status, 1 = success
  753.     jnc    pdacc2            ; nc = success
  754.     xor    ax,ax            ; 0 = failure
  755. pdacc2:    pop    es
  756.     pop    ds
  757.     pop    di
  758.     pop    si
  759.     pop    es
  760.     pop    bp
  761.     ret
  762. _pdaccess endp
  763.  
  764. ; int pkt_send(char *buffer, int length)
  765. ; returns 1 on success, 0 on failure
  766. ; Send a packet.
  767.     public    _pkt_send
  768. _pkt_send    proc    near
  769.     push    bp
  770.     mov    bp,sp
  771.     push    es
  772.     push    ds
  773.     push    si
  774.     push    di            ; don't trust lower levels on regs
  775.     push    cx
  776.     push    dx
  777.     mov    ax,DGROUP        ; segment of outgoing buffer
  778.     mov    ds,ax            ;  will be DS:SI for Packet Driver
  779.     cmp    useodi,0        ; using ODI?
  780.     je    pktsen5            ; e =no, use PD
  781.     call    _odi_busy        ; is transmitter busy?
  782.     or    ax,ax            ; returned response
  783.     jz    pktsen4            ; z = not busy
  784.     stc                ; fail
  785.     jmp    short pktsen1
  786.  
  787. pktsen4:mov    si,[bp+4+0]        ; buffer's offset (seg is dgroup)
  788.     mov    cx,[bp+4+2]        ; buffer's length
  789.     call    odixmt            ; do LSL transmit with DS:SI and CX
  790.     sti                ; interrupts on (odixmt turns off)
  791.     jc    pktsen1            ; c = internal failure to send
  792.     or    ax,ax            ; LSL status
  793.     jz    pktsen1            ; z = success
  794.     stc                ; set carry for failure
  795.     jmp    short pktsen1        ; done (AX non-zero if error)
  796. ; Note that checking for transmission errors is not readily done with async
  797. ; sending, so we cross our fingers and hope for the best.
  798.                     ;
  799.                     ; Packet Driver sending
  800. pktsen5:mov    si,[bp+4+0]        ; buffer's offset (seg is dgroup)
  801.     mov    cx,[bp+4+2]        ; buffer's length
  802.     mov    ah,pd_send        ; send packet (buffer = ds:si)
  803.     call    tstart            ; start transmit timer
  804.     call    pktdrvr            ; invoke Packet Driver
  805.     call    tstop            ; stop transmit timer
  806.                     ; common exit
  807. pktsen1:mov    ax,1            ; return C level success (1)
  808.     jnc    pktsen2            ; nc = success
  809.     xor    ax,ax            ; else C level failure (0)
  810. pktsen2:pop    dx
  811.     pop    cx
  812.     pop    di
  813.     pop    si
  814.     pop    ds
  815.     pop    es
  816.          pop    bp
  817.     ret
  818. _pkt_send endp
  819.  
  820. ; int odi_busy(void)
  821. ; Return non-zero if ODI is unable to accept new transmission at this time.
  822.     public _odi_busy
  823. _odi_busy proc near
  824.     xor    ax,ax            ; prepare not-busy/false (0) response
  825.     cmp    useodi,0        ; using ODI?
  826.     je    odi_bsx            ; e = no, using Packet Driver
  827.     mov    cx,3            ; loop counter
  828. odi_bs1:cmp    ecbx_busy,0        ; is ODI transmit done yet?
  829.     je    odi_bs2            ; e = yes, else we can't touch it yet
  830.     push    bx
  831.     push    cx
  832.     push    bp            ; playing safe
  833.     push    es
  834.     mov    bx,PROTSUP_RELINQUISH_CONTROL
  835.     call    lslsupport        ; give time to LSL and MLID
  836.     pop    es
  837.     pop    bp
  838.     pop    cx
  839.     pop    bx
  840.     loop    odi_bs1            ; retry til ready or exhausted
  841. odi_bs2:mov    al,ecbx_busy        ; return 0 for not-busy
  842.     xor    ah,ah
  843. odi_bsx:ret
  844. _odi_busy endp
  845.  
  846. ; Our Packet Driver receiver, far called only by the Packet Driver and our
  847. ; local ODI code (odircvr and odircmp).
  848. ; Packet buffer linked list -
  849. ;    each link is    db flag        ; 1 = free, 2 = in use, 4 = allocated
  850. ;                    ;  but not in use yet, 0 = end of buf,
  851. ;                    ;  8 = read but not freed.
  852. ;            db _pktwnum    ; sequential number of pkt written
  853. ;            dw count    ; length of data field
  854. ;            db count dup (?) ; the allocated data field
  855. ;    The head of the chain has a link like all others.
  856. ;    The end of the chain has a link with flag == 0 and count = -BUFISZE
  857. ;    to point to the beginning of the buffer (circular).
  858. ;    Packet buffer garbage collection is done after a buffer has been
  859. ;    transferred to us, and does so by relinking adjacent free blocks.
  860. ; _pktbuf_wrote is used to remember the link where the last write occurred
  861. ; and should be initialized to the tail link to point the next write to
  862. ; the beginning of the buffer.
  863. ; The Packet Driver and our ODI routines call this first with AX = 0 to 
  864. ; obtain a buffer pointer in ES:DI from us (0:0 if we refuse the pkt) with 
  865. ; CX = packet size, and again later with AX = 1 to post completion.
  866.  
  867. pdrcvr    proc    far            ; Packet Driver receiver
  868.     or    ax,ax            ; kind of request (0, 1)
  869.     jz    pdrcvr1            ; z = first, get-a-buffer
  870.     ; Second upcall, packet has xfered, DS:SI set by caller to buffer
  871.      push    ds
  872.     push    si
  873.     push    cx
  874.     push    bx
  875.     push    ax            ; assume DS:SI is one of our buffers
  876.     mov    ax,DGROUP
  877.     mov    ds,ax            ; set ds to our data segment
  878. call rstop
  879.     or    si,si            ; is it legal (from first upcall)?
  880.     jz    pdrcvr11        ; z = no, ignore this call
  881.     sub    si,linksize        ; backup to link info
  882.     mov    cx,100            ; number of trials to find space
  883.     cmp    byte ptr [si].flag,4    ; is this buffer allocated (4)?
  884.     jne    pdrcvr8            ; ne = no, do cleanups and quit
  885.     mov    byte ptr [si].flag,2    ; flag = 2 for buffer is now ready
  886.     mov    si,pktbufoff         ; start of packet buffer
  887.                     ; join contiguous free links
  888. pdrcvr8:mov    al,[si].flag        ; flags byte
  889.     cmp    al,1            ; is link free?
  890.     jne    pdrcvr10        ; ne = no, look for a free link
  891. pdrcvr9:mov    bx,[si].count        ; count (length) of this link
  892.     mov    al,[bx+si+linksize].flag; flag of following link
  893.     cmp    al,1             ; is next link free?
  894.     jne    pdrcvr10        ; ne = no, look for free link
  895.     mov    ax,[bx+si+linksize].count ; count taken from next link
  896.     add    ax,linksize        ;  plus the next link's info field
  897.     add    [si].count,ax        ; add it to this count (merge links)
  898.     loop    pdrcvr9            ; re-examine this new longer link
  899.     jmp    short pdrcvr11        ; too many trials, abandon effort
  900.  
  901. pdrcvr10:or    al,al            ; end of list?
  902.     jz    pdrcvr11        ; z = yes
  903.     add    si,[si].count        ; look at next link (add count)
  904.     add    si,linksize        ; and link info
  905.     loop    pdrcvr8            ; keep looking
  906. pdrcvr11:pop    ax
  907.     pop    bx
  908.     pop    cx
  909.     pop    si
  910.     pop    ds
  911.     ret
  912.  
  913. pdrcvr1:push    ds            ; First upcall, provide buffer ptr
  914.     push    dx            ; return buffer in ES:SI
  915.     mov    di,dgroup        ; get local addressibility
  916.     mov    ds,di
  917.     mov    es,di            ; packet buffer is in same group
  918.     cmp    useodi,0        ; using Packet Driver?
  919.     jne    pdrcvr1a        ; ne = no, ODI, has separate rstart
  920.     call    rstart            ; start receive timer
  921. pdrcvr1a:mov    di,_pktbuf_wrote    ; where last write occurred
  922.     or    di,di            ; NULL?
  923.     jz    pdrcvr4            ; z = yes, write nothing
  924.     mov    dl,100            ; retry counter, breaks endless loops
  925.     cmp    [di].flag,1        ; is this link free?
  926.     je    pdrcvr5            ; e = yes, use it
  927.  
  928. pdrcvr2:add    di,[di].count        ; point at next link (add count and
  929.     add    di,linksize        ;  link overhead)
  930.     dec    dl            ; loop breaker count down
  931.     jz    pdrcvr4            ; z = in an endless loop, exit
  932.     cmp    [di].flag,1        ; is this link free (1)?
  933.     je    pdrcvr5            ; e = yes, setup storage
  934.     cmp    di,_pktbuf_wrote     ; have we come full circle?
  935.     jne    pdrcvr2            ; ne = no, keep looking
  936. pdrcvr4:pop    dx
  937.     pop    ds            ; failure or buffer not available (0)
  938.     xor    ax,ax            ; return what we received in ax
  939.     xor    di,di            ; return ES:DI as null to reject
  940.     mov    es,di
  941.     ret
  942.                     ; this link is free
  943. pdrcvr5:add    cx,2            ; defense for 8/16 bit xfr mistakes
  944.     mov    ax,[di].count        ; length of available data space
  945.     cmp    ax,cx            ; cx is incoming size, enough space?
  946.     jl    pdrcvr2            ; l = no, go to next link
  947.     mov    [di].flag,4        ; mark link flag as being alloc'd (4)
  948.     mov    dh,_pktwnum         ; write pkt sequencer number
  949.     mov    [di].bufnum,dh        ; store in buffer, permits out of
  950.     inc    _pktwnum        ;  temporal order deliveries
  951.     mov    _pktbuf_wrote,di    ; remember where we wrote last
  952.     sub    ax,cx            ; allocated minus incoming packet
  953.     cmp    ax,60+linksize        ; enough for new link and miminal pkt?
  954.     jl    pdrcvr6            ; l = not enough for next pkt
  955.     mov    [di].count,cx        ; update space really used
  956.     push    di            ; save this link pointer
  957.     add    di,linksize        ; plus current link info
  958.     add    di,cx            ; plus space used = new link point
  959.     sub    ax,linksize        ; available minus new link info
  960.     mov    [di].flag,1        ; mark new link as free (1)
  961.     mov    [di].count,ax        ; size of new free data area
  962.     pop    di            ; return to current link
  963. pdrcvr6:add    di,linksize        ; point at data portion
  964. pdrcvr7:xor    ax,ax            ; return what we received in ax
  965.     pop    dx            ; CX is size of requested buffer
  966.     pop    ds            ; ES:DI is the pkt buffer address
  967.     ret
  968. pdrcvr    endp
  969.  
  970. ; Check for Windows enhanced mode. Return carry set if true, else carry clear.
  971. chkwin proc    near
  972.     push    es
  973.     mov    ah,getintv        ; check for valid Int 2Fh handler
  974.     mov    al,2fh            ; vector 2fh
  975.     int    dos            ; to es:bx
  976.     mov    ax,es
  977.     pop    es
  978.     or    ax,bx            ; check if vector exists
  979.     jnz    chkwin2            ; nz = yes
  980.     stc
  981.     ret
  982.  
  983. chkwin2:mov    ax,1683h        ; Windows 3, get current virt machine
  984.     int    2fh
  985.     cmp    ax,1683h        ; virtual machine, if any
  986.     je    chkwin3            ; e = no Windows, ok to proceed
  987.     stc
  988.     ret
  989. chkwin3:clc                ; not Windows enhanced mode
  990.     ret
  991. chkwin endp
  992.  
  993. ; Begin Novell ODI support routines
  994. ; Note that while we use Ethernet_II (6 dest, 6 source, 2 TYPE bytes) to/from
  995. ; internal consumers the frame format to/from ODI is in the hands of ODI.
  996. ; Hopefully this will permit TCP/IP operation over all supported frame types.
  997. ; ARP/RARP packets are sized to the frame in use.
  998. ;
  999. ; Check for LSL presence, and if present then get entry points.
  1000. ; Returns carry set if failure, else carry clear.
  1001. ; This procedure is closely modeled upon the Novell example.
  1002. odichk    proc    near
  1003.     cmp    useodi,0        ; already inited?
  1004.     je    odichk0            ; e = no
  1005.     clc
  1006.     ret
  1007. odichk0:call    chkwin            ; check for Windows enhanced mode
  1008.     jnc    odichk5            ; nc = not, continue
  1009.     ret                ; return failure
  1010. odichk5:push    es
  1011.     mov    ah,getintv        ; get LSL via multiplexer interrupt
  1012.     mov    al,2fh            ; vector 2fh
  1013.     int    dos            ; to es:bx
  1014.     mov    ax,es
  1015.     or    ax,bx            ; check if vector exists
  1016.     jnz    odichk1            ; nz = yes
  1017.     pop    es
  1018.     stc
  1019.     ret
  1020.  
  1021. odichk1:mov    ax,0c000h        ; look at multiplexer slots c0 et seq
  1022.     push    si
  1023.     push    di
  1024. odichk2:push    ax
  1025.     int    2fh
  1026.     cmp    al,0ffh            ; is slot in use?
  1027.     pop    ax
  1028.     je    odichk4            ; e = yes, check for LSL being there
  1029. odichk3:inc    ah            ; next slot
  1030.     or    ah,ah            ; wrapped?
  1031.     jnz    odichk2            ; nz = no, keep looking
  1032.     pop    di
  1033.     pop    si
  1034.     pop    es
  1035.     stc                ; not found, fail
  1036.     ret
  1037.  
  1038. odichk4:mov    di,si            ; es:si should point to "LINKSUP$"
  1039.     mov    si,offset DGROUP:lslsig    ; expected signature
  1040.     mov    cx,lslsiglen        ; length
  1041.     cld
  1042.     repe    cmpsb            ; check for signature
  1043.     jne    odichk3            ; ne = no match, try next Int 2fh slot
  1044.     mov    word ptr lslinit,bx    ; found entry, save init entry point
  1045.     mov    ax,es            ;  returned in es:bx
  1046.     mov    word ptr lslinit+2,ax
  1047.     mov    ax,ds
  1048.     mov    es,ax            ; get LSL main support/service addrs
  1049.     mov    si,offset DGROUP:lslsupport ; address of LSL entry point array
  1050.     mov    bx,2            ; request support/service entry points
  1051.         ; fills in far addresses of lslsupport and lslservice routines
  1052.     call    lslinit            ; call LSL initialization routine
  1053.     pop    di
  1054.     pop    si
  1055.     pop    es
  1056.     clc                ; success
  1057.     ret
  1058. odichk    endp
  1059.  
  1060. ; Bind a protocol TYPE to an ODI virtual board.
  1061. ; Enter with TYPE (big endian/network order) in AX.
  1062. ; Packet reception begins immediately upon a successful bind.
  1063. ; Uses NET.CFG if information is available.
  1064. ; Obtain StackID (our ident to the LSL), ProtID (ident of LSL's decoder),
  1065. ; and boardnumber (the logical board), then bind to start reception. Do for
  1066. ; one of our protocols.
  1067. ; Returns PD handle (TYPE) in AX and carry clear upon success, else carry set.
  1068. odibind proc    near
  1069.     push    ax
  1070.     push    bx
  1071.     push    si
  1072.     push    di
  1073.     push    es
  1074.     mov    bx,DGROUP
  1075.     mov    es,bx
  1076.     cmp    ax,ip_type            ; IP, 0x0008h?
  1077.     jne    odibind1            ; ne = no
  1078.     mov    ax,offset DGROUP:ip_string    ; put IP string in request
  1079.     mov    bx,offset ip_rcvr        ; set address of receiver esr
  1080.     mov    di,offset DGROUP:ip_stackid    ; set address of stackid struc
  1081.     jmp    short odibind3
  1082.  
  1083. odibind1:cmp    ax,arp_type            ; ARP, 0x0608?
  1084.     jne    odibind2            ; ne = no
  1085.     mov    ax,offset DGROUP:arp_string
  1086.     mov    bx,offset arp_rcvr
  1087.     mov    di,offset DGROUP:arp_stackid
  1088.     jmp    short odibind3
  1089.  
  1090. odibind2:cmp    ax,rarp_type            ; RARP, 0x3580?
  1091.     je    odibind2a            ; e = yes
  1092.     jmp    odibindx            ; ne = no, fail
  1093. odibind2a:mov    ax,offset DGROUP:rarp_string
  1094.     mov    bx,offset rarp_rcvr
  1095.     mov    di,offset DGROUP:rarp_stackid
  1096.  
  1097. odibind3:mov    word ptr registerstk.StackNamePtr,ax ; insert ptr to string
  1098.     mov    word ptr registerstk.StackReceiveHandler,bx  ; setup esr addr
  1099.  
  1100. ; Note: to use Prescan or Default registrations delete StackNamePtr & StackID.
  1101. ; StackID is not used with these latter methods, and their reception begins
  1102. ; at registration rather than at bind (so this area would be redesigned).
  1103.     mov    bx,PROTSUP_REGISTER_STACK ; register the protocol by name
  1104.     mov    si,offset DGROUP:registerstk ; registration form pointer
  1105.     push    di            ; save ptr to xxx_stackid storage
  1106.     call    lslsupport        ; call LSL with the address in es:si
  1107.     pop    di
  1108.     jz    odibind3a        ; z = success
  1109.     jmp    odibindx        ; nz = failure
  1110. odibind3a:mov    [di].pstack,bx        ; save returned StackID (LSL's handle
  1111.                     ;  for our protocol stack)
  1112.     cmp    readnetcfg,0        ; have read NET.CFG for BIND info?
  1113.     jne    odibind4        ; ne = yes
  1114.     mov    useboard,-1        ; clear board-to-use word
  1115.     call    getbind            ; find Kermit's bind board in NET.CFG
  1116.     inc    readnetcfg        ; say have read the file
  1117.     cmp    word ptr bdname,256*'#'+2 ; is board name #<digit>?
  1118.     jne    odibind4        ; ne = no, assume regular driver name
  1119.     mov    al,bdname+2        ; get ascii digit
  1120.     sub    al,'1'            ; remove ascii bias (external=1 based)
  1121.     xor    ah,ah            ;  but we are zero based internally
  1122.     cmp    al,8            ; arbitrary limit of 8 boards
  1123.     ja    odibind4        ; a = out of range, ignore value
  1124.     mov    useboard,ax        ; and make this the board number
  1125.     mov    bdname,0        ; and don't use bdname as a name
  1126. odibind4:mov    [di].pboard,0        ; assume board zero to start loop
  1127.     mov    ax,useboard        ; board to be used, if any
  1128.     or    ax,ax            ; boards 0 and up are legal
  1129.     jl    odibind5        ; l = no board found yet, search
  1130.     mov    [di].pboard,ax        ; specify board, get ProtID
  1131.  
  1132. odibind5:mov    bx,PROTSUP_GET_MLID_CTL_ENTRY    ; get MLID control entry
  1133.     mov    ax,[di].pboard        ; for this board
  1134.     push    di
  1135.     call    lslsupport        ; call LSL for the address to es:si
  1136.     pop    di
  1137.     mov    word ptr mlidcont,si
  1138.     mov    word ptr mlidcont+2,es    ; MLID control routine
  1139.     jz    odibind7        ; z=success, have a board to work with
  1140.     cmp    ax,LSLERR_NO_MORE_ITEMS ; out of items?
  1141.     je    odibind5a        ; e = yes, no more boards
  1142.       cmp    ax,LSLERR_ITEM_NOT_PRESENT ; other boards may exist?
  1143.     je    odibind7        ; e = yes
  1144. odibind5a:jmp    odibindx        ; fail
  1145.  
  1146. odibind7:mov    bx,PROTSUP_GET_PID_PROTNUM_MLIDNUM ; get ProtID from StackID
  1147.     mov    ax,[di].pstack        ; StackID
  1148.     mov    cx,[di].pboard        ;  and assumed board number
  1149.     mov    si,dgroup
  1150.     mov    es,si            ; set es:di to the ProtID buffer
  1151.     lea    si,[di].pprotid        ;  in our storage slot per protocol
  1152.     push    di
  1153.     call    lslsupport        ; ask LSL for the ProtID string
  1154.     pop    di            ;  to that 6-byte buffer
  1155.     jz    odibind9        ; z = success, found a recognizer
  1156.     cmp    useboard,0        ; has a board been pre-identified?
  1157.     jge    odibind5a        ; ge = yes, so the matchup failed
  1158.     inc    [di].pboard        ; next board
  1159.     jmp    short odibind5        ; keep looking for a board
  1160.  
  1161. odibind9:mov    bx,GET_MLID_CONFIGURATION ; get MLID config ptr to es:si
  1162.     mov    ax,[di].pboard
  1163.     call    mlidcont        ; call MLID control routine
  1164.     jnz    odibindx        ; nz = failure
  1165.     cmp    bdname,0        ; was a board name bound via BIND?
  1166.     je    odibin10        ; e = no, don't check on it
  1167.     push    es            ; save pointer to MLID config table
  1168.     push    di
  1169.     push    si
  1170.     les    di,es:[si].MCardShortName ; get short name of this board
  1171.     lea    si,bdname        ; desired board name string
  1172.     mov    cl,bdname        ; length of desired board name
  1173.     inc    cl            ; include length byte
  1174.     xor    ch,ch
  1175.     cld
  1176.     repe    cmpsb            ; compare  len,string  for both
  1177.     pop    si
  1178.     pop    di
  1179.     pop    es
  1180.     je    odibin10        ; e = found desired board
  1181.     inc    [di].pboard        ; try next board
  1182.     jmp    short odibind5        ; keep looking for the desired board
  1183.  
  1184. odibin10:mov    ax,[di].pboard        ; get current board number
  1185.     mov    useboard,ax        ; remember for next protocol
  1186.     mov    ax,es:[si].MWorstDataSize ; max header, leaving this size
  1187.     sub    ax,20+20        ; minus IP and TCP headers
  1188.     mov    _mss,ax            ; set new operational value
  1189.     mov    bx,es:[si].MFrameID    ; frame ident, for get_hwd
  1190.     call    get_hwd            ; get hardware specifics
  1191.     push    es
  1192.     push    si            ; save config pointer
  1193.     lea    si,es:[si].MNodeAddress    ; point to address in config struct
  1194.     push    ds            ; save ds
  1195.     push    di
  1196.     mov    di,offset DGROUP:_eth_addr; where our MAC address is stored
  1197.     mov    ax,ds
  1198.     mov    cx,es
  1199.     mov    es,ax
  1200.     mov    ds,cx
  1201.     cld
  1202.     mov    cx,6            ; MAC address length, bytes, fixed
  1203.     rep    movsb            ; copy MAC address to global array
  1204.     pop    di
  1205.     pop    ds
  1206.     pop    si            ; recover configuration table pointer
  1207.     pop    es
  1208.     mov    tells_len,0        ; presume no lookahead data length
  1209.     test    es:[si].MFlags,Len_Info    ; capas bit for length provided (new)
  1210.     jz    odibin12        ; z = does not provide
  1211.     inc    tells_len        ; say provides length
  1212.  
  1213. odibin12:mov    bx,PROTSUP_BIND_STACK_TO_MLID    ; Bind stack to MLID
  1214.     mov    ax,[di].pstack        ; StackID
  1215.     mov    cx,[di].pboard        ; board number
  1216.     call    lslsupport        ; bind our protocol stack to board
  1217.     jnz    odibindx        ; nz = failure
  1218.     pop    es            ; received packets can interrupt now
  1219.     pop    di
  1220.     pop    si
  1221.     pop    bx
  1222.     pop    ax
  1223.     clc
  1224.     ret
  1225. odibindx:pop    es
  1226.     pop    di
  1227.     pop    si
  1228.     pop    bx
  1229.     pop    ax
  1230.     stc                ; say failure
  1231.     ret
  1232. odibind endp
  1233.  
  1234. ; Worker for odibind. Find NET.CFG, extract name of board driver from pair of
  1235. ; lines reading as below (Protocol must be in column 1, bind must be indented)
  1236. ; Protocol Kermit            Kermit's main section header
  1237. ;   bind  <board_driver_name>        indented, without the <> signs
  1238. ;or
  1239. ; Protocol Kermit
  1240. ;   bind #<digit>            selects DOS driver load order (from 1)
  1241. ;
  1242. ; Examples -
  1243. ; Protocol Kermit
  1244. ;   bind exos
  1245. ;or
  1246. ; Protocol Kermit
  1247. ;   bind #2
  1248. ;            and elsewhere there is the board driver section:
  1249. ; Link Driver exos
  1250. ;
  1251. ; If found put the board driver name in array bdname, as length byte, string,
  1252. ; then a null. If not found make length byte bdname be zero. We treat NET.CFG
  1253. ; as case insensitive.
  1254. ; Unless we use the special Kermit section then LSL will assign to us the
  1255. ; first board loaded by DOS supporting the frame kind of our protocol.
  1256. ; Link Driver section line "Protocol name type frame"  simply associates a
  1257. ; frame kind with the name and type, but not with a board. L.D. section line
  1258. ; frame <frame kind> attaches that frame kind to the board, if it fits.
  1259. ; Kermit uses "name" in the above line to pinpoint a protocol, not a board.
  1260. ; Add keyword MYIP to obtain a dynamically assigned IP value from Telebit's
  1261. ; ODI PPP driver. The user must say "Set TCP Address Telebit-PPP" for this
  1262. ; to have effect.
  1263. getbind    proc    near
  1264.     mov    bdname,0        ; clear board name length
  1265.     mov    _tempip,0        ; clear dynamic IP string count
  1266.     push    ds
  1267.     mov    bx,GENSERV_GET_NETCFG_PATH ; get fully formed NET.CFG name
  1268.     call    lslservice        ;  from LSL general services to ds:dx
  1269.     jz    getbin1            ; z = success
  1270.     pop    ds            ; fail
  1271.     ret
  1272. getbin1:mov    ah,fopen        ; open file NET.CFG
  1273.     mov    al,40h            ;  for reading, deny none
  1274.     int    dos            ; returns file handle in ax
  1275.     pop    ds
  1276.     mov    temp,ax            ; save handle for getbyte
  1277.     jnc    getbin2            ; nc = success
  1278.     ret                ; carry set for failure
  1279.  
  1280. getbin2:mov    bx,1            ; subscript, at start of a line
  1281.  
  1282. getbin3:call    getbyte            ; read a byte, uppercased
  1283.     jnc    getbin4            ; nc = success
  1284.     ret                ; c = end of file
  1285. getbin4:cmp    protword[bx],al        ; compare to "PROTOCOL"
  1286.     jne    getbin5            ; ne = failure, scan for end of line
  1287.     inc    bx
  1288.     cmp    bl,protword        ; length, matched all bytes?
  1289.     jbe    getbin3            ; be = no, match more
  1290.     jmp    short getbin6        ; ae = yes, next phrase
  1291.     ret                ; fail out 
  1292.  
  1293. getbin5:cmp    al,LF            ; end of a line?
  1294.     je    getbin2            ; e = yes, scan for PROTOCOL again
  1295.     call    getbyte
  1296.     jnc    getbin5            ; keep consuming line material
  1297.     ret                ; fail out at end of file
  1298.                     ; Short Name following "PROTOCOL"
  1299. getbin6:call    getbyte            ; get separator char, discard
  1300.     jnc    getbin7
  1301.     ret                ; c = eof
  1302. getbin7:call    getbyte            ; read short name of protocol
  1303.     jnc    getbin7a        ; nc = text
  1304.     ret                ; return on eof
  1305. getbin7a:cmp    al,' '            ; white space?
  1306.     jbe    getbin7            ; be = yes, stay in this state
  1307.     mov    bx,1            ; subscript
  1308. getbin8:cmp    psname[bx],al        ; compare to our protocol short name
  1309.     jne    getbin5            ; ne = failure, scan for end of line
  1310.     cmp    bl,psname        ; matched all bytes?
  1311.     jae    getbin9            ; ae = yes, next phrase
  1312.     inc    bx
  1313.     call    getbyte            ; get next byte to match
  1314.     jnc    getbin8            ; nc = not eof yet
  1315.     ret
  1316.  
  1317. getbin9:call    getbyte            ; go to next line, enforce whitespace
  1318.     jc    getbin20        ; c = eof
  1319.     cmp    al,LF            ; end of a line?
  1320.     jne    getbin9            ; ne = no, scan for end of line
  1321.     call    getbyte            ; look for whitespace
  1322.     jc    getbin20        ; c = eof
  1323.     cmp    al,'#'            ; comment line?
  1324.     je    getbin9            ; e = yes, get next line
  1325.     cmp    al,';'            ; comment line?
  1326.     je    getbin9            ; e = yes, get next line
  1327.     cmp    al,' '            ; required whitespace?
  1328.     ja    getbin5            ; a = no, start over
  1329.  
  1330. getbin10:call    getbyte            ; look for keyword "BIND"
  1331.     jc    getbin20
  1332.     cmp    al,' '            ; white space?
  1333.     jbe    getbin10        ; be = yes, stay in this state
  1334.     mov    bx,1            ; subscript
  1335.     cmp    al,'M'            ; M for MYIP?
  1336.     je    getbin30        ; e = yes
  1337.     cmp    al,'B'            ; B for BIND?
  1338.     jne    getbin9            ; ne = no, next line
  1339.  
  1340. getbin12:cmp    bdname,0        ; have bind name yet?
  1341.     je    getbin13        ; e = no
  1342.     jmp    getbin9            ; else get next line
  1343. getbin13:cmp    bindword[bx],al        ; compare to "BIND"
  1344.     jne    getbin9
  1345.     cmp    bl,bindword        ; matched all bytes?
  1346.     jae    getbin14        ; ae = yes, next phrase
  1347.     call    getbyte
  1348.     jc    getbin20        ; c = eof
  1349.     inc    bx
  1350.     jmp    short getbin13        ; keep reading
  1351.  
  1352. getbin14:call    getbyte            ; skip white space before board name
  1353.     jc    getbin20            
  1354.     cmp    al,' '            ; white space?
  1355.     jbe    getbin14        ; be = yes, skip it
  1356.  
  1357. getbin15:mov    bl,bdname        ; board name, length byte, starts at 0
  1358.     xor    bh,bh
  1359.     inc    bl
  1360.     mov    bdname,bl        ; update length of board driver name
  1361.     xor    ah,ah            ; get a null
  1362.     mov    word ptr bdname[bx],ax    ; store as board short name,null
  1363.     cmp    bx,15            ; legal limit on short name?
  1364.     jbe    getbin16        ; be = ok
  1365.     mov    bdname,ah        ; illegal, clear board name length
  1366.     jmp    getbin9            ; get next line
  1367. getbin16:call    getbyte
  1368.     jc    getbin20        ; reached eof, is ok
  1369.            cmp    al,' '            ; usable text?
  1370.     ja    getbin15        ; a = yes, else stop storing name
  1371.     jmp    getbin9            ; get next line
  1372.  
  1373. getbin20:ret
  1374.     
  1375. getbin30:cmp    _tempip,0        ; have IP word already?
  1376.     je    getbin31        ; e = no
  1377.     jmp    getbin9            ; get new line
  1378.  
  1379. getbin31:cmp    myipword[bx],al        ; compare to "MYIP"
  1380.     jne    getbin9            ; ne = failure, start over
  1381.     cmp    bl,myipword        ; matched all bytes?
  1382.     jb    getbin32        ; b = no
  1383.     jmp    short getbin33
  1384.  
  1385. getbin32:call    getbyte
  1386.     jc    getbin20        ; c = eof
  1387.     inc    bx
  1388.     jmp    getbin31        ; keep reading
  1389.  
  1390. getbin33:call    getbyte            ; skip white space before IP address
  1391.     jc    getbin20            
  1392.     cmp    al,' '            ; white space?
  1393.     jbe    getbin33        ; be = yes, skip it
  1394.  
  1395. getbin34:mov    bl,_tempip        ; our IP, length byte, starts at 0
  1396.     xor    bh,bh
  1397.     inc    bl
  1398.     mov    _tempip,bl        ; update length of IP string
  1399.     xor    ah,ah            ; get a null
  1400.     mov    word ptr _tempip[bx],ax    ; store as IP, null
  1401.     cmp    bx,15            ; legal limit on IP?
  1402.     jbe    getbin35        ; be = ok
  1403.     mov    _tempip+1,ah        ; illegal, clear IP
  1404.     jmp    getbin9            ; and quit
  1405. getbin35:call    getbyte
  1406.     jc    getbin36        ; reached eof, is ok
  1407.            cmp    al,' '            ; usable text?
  1408.     ja    getbin34        ; a = yes, else stop storing name
  1409.     jmp    getbin9            ; get next line
  1410. getbin36:ret    
  1411. getbind    endp
  1412.  
  1413. ; Worker for getbind. Delivers one byte per call from NET.CFG, upper cased.
  1414. ; Returns carry set and NET.CFG file closed at end of file.
  1415. ; Temp has NET.CFG file handle, tempb is our one byte buffer for disk i/o.
  1416. getbyte    proc    near
  1417.     mov    dx,offset tempb        ; ds:dx points to start of buffer
  1418.     mov    ah,fread        ; read from file to buffer
  1419.     mov    cx,1            ; this many bytes
  1420.     push    bx
  1421.     mov    bx,temp            ; get file handle
  1422.     int    dos
  1423.     pop    bx
  1424.     jc    getbyt2            ; c = failure
  1425.     cmp    ax,1            ; got the single byte?
  1426.     jb    getbyt2            ; b = no, failure
  1427.     mov    al,tempb        ; return read byte
  1428.     cmp    al,'z'            ; in lower case range?
  1429.     ja    getbyt1            ; a = no
  1430.     cmp    al,'a'            ; in lower case range?
  1431.     jb    getbyt1            ; b = no
  1432.     and    al,not 20h        ; lower to upper case
  1433. getbyt1:clc                ; carry clear for success
  1434.     ret                ; return char in AL
  1435. getbyt2:push    bx
  1436.     mov    bx,temp            ; file handle
  1437.     mov    ah,fclose        ; close the file
  1438.     int    dos
  1439.     pop    bx
  1440.     stc                ; say EOF or other failure
  1441.      ret
  1442. getbyte    endp
  1443.  
  1444. ; Worker for odibind.
  1445. ; Enter with BX holding Novell frame type from the MLID configuration table. 
  1446. ; Set _arp_hardware and _MAC_len and return BX holding _MAC_len value, else 
  1447. ; if frame is not supported return BX = 0. These two values are needed by the
  1448. ; ARP functions. This list searching method is to accomodate the ever 
  1449. ; expanding quantity of frame types appearing with ODI; we deal with those we
  1450. ; understand (sic).
  1451. get_hwd    proc    near
  1452.     push    es
  1453.     push    di
  1454.     mov    ax,DGROUP
  1455.     mov    es,ax
  1456.     mov    al,bl            ; get frame value (MLID config)
  1457.     xor    bx,bx            ; prepare no-match return value
  1458.     mov    di,offset frame_type    ; list to search
  1459.     mov    cx,num_frames        ; number of elements in the list
  1460.     cld
  1461.     repne    scasb            ; byte search
  1462.     jne    get_hwd1        ; ne = no match, fail
  1463.     sub    di,offset frame_type+1    ; make di be an index along the list
  1464.     mov    al,hardware_type[di]    ; ARP/RARP hardware type ident
  1465.     xor    ah,ah            ; return in local (host) order
  1466.     mov    _arp_hardware,ax    ; hardware type for ARP/RARP pkts
  1467.     mov    bl,frame_adlen[di]    ; array of MAC lengths for frame types
  1468.     xor    bh,bh
  1469.     mov    _MAC_len,bx        ; save MAC address length (1..6 bytes)
  1470.     pop    di
  1471.     pop    es
  1472.     ret
  1473. get_hwd1:mov    _arp_hardware,bx    ; hardware type 0 for ARP/RARP pkts
  1474.     mov    _MAC_len,bx        ; save MAC address length (0 bytes)
  1475.     pop    di
  1476.     pop    es
  1477.     ret                ; return _MAC_len in BX
  1478. get_hwd    endp
  1479.  
  1480. ; Unbind a protocol TYPE from an ODI virtual board
  1481. ; Enter with protocol TYPE (net order) in AX, return carry set if failure.
  1482. ; The TYPE is used as our handle to the application.
  1483. ; Prescan and Default methods call lslsupport with the board number in AX 
  1484. ; rather than StackID and use matching PROTSUP_DEREGISTER_* code.
  1485. odiunbind proc    near
  1486.     cmp    ax,ip_type        ; IP, 0x0008h?
  1487.     jne    odiunb1            ; ne = no
  1488.     mov    ax,ip_stackid.pstack    ; StackID
  1489.     jmp    short odiunb3
  1490.  
  1491. odiunb1:cmp    ax,arp_type        ; ARP, 0x0608?
  1492.     jne    odiunb2            ; ne = no
  1493.     mov    ax,arp_stackid.pstack
  1494.     jmp    short odiunb3
  1495.  
  1496. odiunb2:cmp    ax,rarp_type        ; RARP, 0x3580?
  1497.     jne    odiunb4            ; ne = no
  1498.     mov    ax,rarp_stackid.pstack
  1499.  
  1500. odiunb3:mov    bx,PROTSUP_DEREGISTER_STACK ; deregister stack (StackID in AX)
  1501.     call    lslsupport        ; stops reception now
  1502.     jnz    odiunb4            ; nz = failure
  1503.     clc                ; success
  1504.     ret
  1505. odiunb4:stc                ; failure
  1506.     ret
  1507. odiunbind endp
  1508.  
  1509. ; ODI receive interrupt handler, for use only by the LSL.
  1510. ; Called with DS:DI pointing to lookahead structure, interrupts are off.
  1511. ; Returns ES:SI pointing at ECB, AX = 0 if we want pkt, AX = 8001h if decline.
  1512. ; There are three of these, one each for IP, ARP, and RARP. All have the
  1513. ; same calling convention and all jump to odircv to do the real work. 
  1514. ; The length of the arriving packet is available if the MLID supports the
  1515. ; new (mid-May 1992) capability, our "tells_len"; otherwise we make an
  1516. ; intelligent guess based on the protocol header. These entry points can be 
  1517. ; called multiple times before receive-completion, and likely will be, so we
  1518. ; use several ecb's to accept requests.
  1519. ip_rcvr    proc    far
  1520.     push    bx
  1521.     push    cx
  1522.     push    di
  1523.     push    ds
  1524.     mov    cx,ds            ; DS:DI from LSL
  1525.     mov    es,cx            ; use ES for LSL items
  1526.     mov    ax,DGROUP        ; set DS to our data segment
  1527.     mov    ds,ax
  1528.     call    rstart            ; start timer
  1529.     push    es
  1530.     push    si
  1531.     cmp    tells_len,0        ; have data length available?
  1532.     je    ip_rec1            ; e = no
  1533.     les    si,es:[di].DataLookAheadDataSize ; ptr to what it says
  1534.     mov    cx,word ptr es:[si]    ; get length of data field
  1535.     add    cx,4            ; for overzealous board transfers
  1536.     pop    si
  1537.     pop    es
  1538.     jmp    far ptr odircv
  1539.  
  1540. ip_rec1:les    si,es:[di].LookAheadPtr    ; point at data lookahead ptr
  1541.     mov    cx,word ptr es:[si+2]    ; IP pkt header, length word
  1542.     cmp    byte ptr es:[si],45h    ; validate IP pkt kind (ver/hlen)?
  1543.     pop    si
  1544.     pop    es
  1545.     jne    ip_rcvr1        ; ne = invalid, decline
  1546.     xchg    ch,cl            ; net to local order
  1547.     add    cx,14+2            ; our MAC level addressing + 2 safety
  1548. ip_rec2:mov    ax,ip_stackid.pstack    ; StackID for ecb structure
  1549.     mov    rcvtype,ip_type        ; store protocol TYPE int
  1550.     jmp    odircv
  1551.  
  1552. ip_rcvr1:add    pstats.PIgnoredRxPackets,1 ; update ODI statistics counter
  1553.     adc    pstats.PIgnoredRxPackets+2,0
  1554.     mov    ax,LSLERR_OUT_OF_RESOURCES ; decline the packet
  1555.     or    ax,ax            ; set Z flag to match AX
  1556.     pop    ds
  1557.     pop    di
  1558.     pop    cx
  1559.     pop    bx
  1560.     ret
  1561. ip_rcvr    endp
  1562.  
  1563. ; RARP protocol receive service routine, similar to ip_rcvr.
  1564. rarp_rcvr proc    far
  1565.     push    bx
  1566.     push    cx
  1567.     push    di
  1568.     push    ds
  1569.     mov    cx,ds            ; DS:SI from LSL
  1570.     mov    es,cx
  1571.     mov    ax,DGROUP        ; set DS to our data segment
  1572.     mov    ds,ax
  1573.     mov    ax,rarp_stackid.pstack    ; StackID for ecb structure
  1574.     mov    rcvtype,rarp_type    ; store protocol TYPE int
  1575.     jmp    short arp_common    ; do ARP/RARP common code
  1576. rarp_rcvr endp
  1577.  
  1578. ; ARP protocol receive service routine, similar to ip_rcvr.
  1579. arp_rcvr proc    far
  1580.     push    bx
  1581.     push    cx
  1582.     push    di
  1583.     push    ds
  1584.     mov    cx,ds            ; DS:SI from LSL
  1585.     mov    es,cx
  1586.     mov    ax,DGROUP        ; set DS to our data segment
  1587.     mov    ds,ax
  1588.     mov    ax,arp_stackid.pstack    ; StackID for ecb structure
  1589.     mov    rcvtype,arp_type    ; store protocol TYPE int
  1590.  
  1591. arp_common:                ; common code for ARP/RARP
  1592.     push    es
  1593.     push    si
  1594.     cmp    tells_len,0        ; have data length available?
  1595.     je    arp_com1        ; e = no
  1596.     les    si,es:[di].DataLookAheadDataSize ; ptr to what it says
  1597.     mov    cx,word ptr es:[si]    ; get length of data field
  1598.     add    cx,4            ; for overzealous board transfers
  1599.     pop    si
  1600.     pop    es
  1601.     jmp    short odircv
  1602.  
  1603. arp_com1:les    si,es:[di].LookAheadPtr    ; point at lookahead ptr for Data
  1604.     mov    cx,word ptr es:[si+4]    ; ARP/RARP pkt header, length bytes
  1605.     add    cl,ch            ; add HA and IP address lengths
  1606.     xor    ch,ch
  1607.     add    cl,cl            ; for host and target
  1608.     adc    ch,0
  1609.     add    cx,8            ; plus ARP/RARP main header
  1610.     cmp    word ptr es:[si+2],ip_type ; ARP/RARP Protocol type of IP?
  1611.     pop    si
  1612.     pop    es
  1613.     jne    ip_rcvr1        ; ne = invalid, decline
  1614.     ; fall through to odircv
  1615. arp_rcvr endp
  1616.  
  1617. ; General worker for ip_rcvr, arp_rcvr, rarp_rcvr. These are invoked by the
  1618. ; LSL when their kind of packet arrives. This module creates the ECB and
  1619. ; dispatches it to the LSL. Operating at LSL interrupt level.
  1620. ; ES:DI is ptr to ODI Lookahead structure, DS is our data seg (DGROUP).
  1621. ; AX is stackid for invoked protocol kind, CX is (guessed) pkt length overall.
  1622. ; Rcvtype is the current invoked protocol TYPE (0008h is IP etc).
  1623. ; When done store in the ecb's protocolws array:
  1624. ;    dw    protocol TYPE (0008h for IP etc)
  1625. ;    dw    subscript of this ecbr item (for use by odircmp)
  1626. ;    dw    <unused>,<unused>
  1627. ; Return ES:SI as pointer to a free ecb and AX = 0 to accept pkt, else
  1628. ; return AX = 8001h to decline pkt (and to ignore ES:SI). Set Z flag to
  1629. ; match AX value.
  1630. odircv    proc    far
  1631.     add    pstats.PtotalRxPackets,1 ; update ODI statistics counter
  1632.     adc    pstats.PtotalRxPackets+2,0
  1633.     push    ax
  1634.     push    cx
  1635.     mov    cx,ecbr_qty        ; number of receive ecb's
  1636.     xor    bx,bx            ; find a free ecb for this packet
  1637.     mov    ax,offset DGROUP:ecbr    ; start of receive ecbs
  1638. odircv8:cmp    ecbr_busy[bx],0        ; is this ECB free?
  1639.     jne    odircv9            ; ne = no, try next
  1640.     mov    ecbr_num,bx        ; remember index for end of proc
  1641.     mov    bx,ax            ; offset of free ecb
  1642.     pop    cx
  1643.     pop    ax
  1644.     jmp    short odircv2        ; use ds:[bx] for address of ecb
  1645. odircv9:inc    bx            ; next byte in busy array
  1646.     add    ax,size ecbstruct    ; size of an ecb
  1647.     loop    odircv8
  1648.     pop    cx            ; failed to find a free ecbr
  1649.     pop    ax
  1650.  
  1651. odircv1:add    pstats.PIgnoredRxPackets,1 ; update ODI statistics counter
  1652.     adc    pstats.PIgnoredRxPackets+2,0
  1653.     mov    ax,LSLERR_OUT_OF_RESOURCES ; decline the packet
  1654.     or    ax,ax            ; set Z flag for ODI
  1655.     pop    ds
  1656.     pop    di
  1657.     pop    cx
  1658.     pop    bx
  1659.     ret
  1660.                     ; ds:[bx] is ptr to a free ecbr
  1661. odircv2:mov    [bx].stackid,ax        ; StackID from odircv entry points
  1662.     mov    ax,es:[di].LBoardNum    ; boardnum from ProtocolID lookahead
  1663.     mov    [bx].boardnum,ax    ; store in ecbr
  1664.     mov    ax,rcvtype        ; get TYPE from odircv entry points
  1665.     mov    word ptr [bx].protocolws,ax    ; save TYPE for odircmp
  1666.     mov    ax,ecbr_num        ; ecbr index
  1667.     mov    word ptr [bx].protocolws+2,ax    ; save index for odircmp
  1668.     cmp    cx,46            ; min packet for Ethernet + 4 spare
  1669.     jae    odircv3            ; ae = no padding needed here
  1670.     mov    cx,46            ; padded min pkt plus 4 spare bytes
  1671. odircv3:push    bx            ; get a buffer of length CX bytes
  1672.     xor    ax,ax            ; set AX = 0 for PD "get buf" call
  1673.     call    pdrcvr            ; use PD buffer allocator code
  1674.     pop    bx            ; ES:DI = buffer pointer, CX = length
  1675.     mov    ax,es
  1676.     or    ax,di            ; check for refused pkt (es:di = NULL)
  1677.     jz    odircv1            ; z = pkt refused (no buffer space)
  1678.     add    di,6+6+2        ; skip our MAC header for ecb use
  1679.     sub    cx,6+6+2        ; less same length for MLID
  1680.     mov    [bx].frag1addr,di    ; offset of buffer which MLID sees
  1681.     mov    ax,es
  1682.     mov    [bx].frag1addr+2,ax    ; seg of buffer
  1683.     mov    [bx].datalen,cx        ; length of buffer for MLID/LSL use
  1684.     mov    [bx].frag1len,cx    ; ditto
  1685.     mov    ax,DGROUP        ; segment of our ecb's
  1686.     mov    es,ax
  1687.     mov    si,bx            ; return ES:SI pointing to ECB
  1688.     mov    bx,ecbr_num        ; get ecbr index
  1689.     mov    ecbr_busy[bx],1        ; mark this ecbr as busy
  1690.     pop    ds
  1691.     pop    di
  1692.     pop    cx
  1693.     pop    bx
  1694.     xor    ax,ax            ; return AX = 0 to accept
  1695.     ret
  1696. odircv    endp
  1697.  
  1698. ; ODI receive-complete call-back routine for use only by the LSL.
  1699. ; Enter with ES:SI pointing at ECB, interrupts are off.
  1700. ; Returns nothing.
  1701. ; There is no guarantee that this routine will be called in the sequence
  1702. ; which packets arrived, so we carry the bookkeeping in the delivered ECB:
  1703. ; TYPE is for Ethernet_II struct results, ecbr_busy is don't-touch interlock.
  1704. ; Note that we have to construct our own "destination" MAC address.
  1705. ; es:[si].status is 0 (success), 8006h (buffer overrun), 8007h (canceled).
  1706. ; StackID field is from LSL, and it's 0ffffh if using Prescan, and undefined
  1707. ; if using Default. The manual says LSL, but not MLID, calls are ok in here.
  1708. odircmp    proc    far
  1709.     push    ds
  1710.     push    ax
  1711.     push    bx
  1712.     push    cx
  1713.     mov    ax,DGROUP
  1714.     mov    ds,ax            ; set ds to our data segment
  1715.     cmp    es:[si].frag1addr+2,0    ; segment of pkt being confirmed
  1716.     je    odircmp6        ; e = illegal, ignore this call
  1717.     cmp    es:[si].status,0     ; check ECB status for failure
  1718.     je    odircmp1        ; e = success
  1719.     mov    es:[si].protocolws,0    ; write TYPE of 0 to permit queueing
  1720. odircmp1:push    di            ; put dest,src,TYPE into pkt buffer
  1721.     push    es
  1722.     push    si            ; save ecbr's si
  1723.     mov    cl,byte ptr es:[si].driverws ; kind of destination, from LSL
  1724.     mov    di,es:[si].frag1addr    ; start of our pkt buffer + 6+6+2
  1725.     sub    di,6+6+2        ; back to start, for our MAC header
  1726.     push    ds            ; WATCH this, presumes ES == DS!
  1727.     pop    es            ; set ES to DS (where pkt buffer is)
  1728.     mov    si,offset DGROUP:_eth_addr ; our hardware address
  1729.     cmp    cl,ECB_BROADCAST     ; a broadcast?
  1730.     jne    odircmp2        ; ne = no, use our address
  1731.     mov    si,offset DGROUP:bcast    ; fill with all 1's
  1732. odircmp2:mov    cx,3            ; 6 byte addresses to our application
  1733.     cld
  1734.      rep    movsw            ; store source address in pkt buffer
  1735.     pop    si            ; recover ecb si
  1736.     push    si            ; save it again
  1737.     mov    ax,es:[si].protocolws     ; get TYPE, from odircv
  1738.     lea    si,es:[si].immaddr    ; offset to MAC address of sender
  1739.     mov    cx,3            ; three words worth, garbage and all
  1740.     rep    movsw            ; copy to packet buffer
  1741.     stosw                ; and write TYPE to packet buffer
  1742.     pop    si
  1743.     pop    es
  1744.     pop    di
  1745.     mov    cx,es:[si].datalen    ; length of data field from ecb
  1746.     add    cx,6+6+2        ; plus space for dest,src,TYPE
  1747.     push    es            ; save ecb's es:si
  1748.     push    si
  1749.     mov    si,es:[si].frag1addr    ; offset of pkt being confirmed
  1750.     sub    si,6+6+2        ; adj to beginning, for our MAC header
  1751.     mov    ax,1            ; set AX = 1 for buffer done call
  1752.     call    pdrcvr            ; do post processing of buffer
  1753.     pop    si
  1754.     pop    es
  1755.     xor    ax,ax
  1756.     mov    es:[si].frag1addr+2,ax    ; clear pkt buffer pointer (seg)
  1757.     mov    es:[si].protocolws,ax     ; clear packet TYPE
  1758.     mov    bx,es:[si].protocolws+2    ; point to ecb index
  1759.     mov    ecbr_busy[bx],al    ; say this ecb is free now
  1760. odircmp6:
  1761.     call    rstop            ; stop receive timer
  1762.     pop    cx
  1763.     pop    bx
  1764.     pop    ax
  1765.     pop    ds
  1766.     ret
  1767. odircmp    endp
  1768.  
  1769. ; ODI transmission routine
  1770. ; Enter with ds:si pointing at full Ethernet_II packet, cx = length (bytes)
  1771. ; Once sent the ecb belongs to ODI until the xmt-complete routine is called.
  1772. odixmt    proc    near
  1773.     push    si
  1774.     push    di
  1775.     push    es
  1776.     mov    ax,ds
  1777.     mov    es,ax
  1778.     mov    ax,cx            ; overall Ethernet_II length
  1779.     sub    ax,6+6+2        ; omit our MAC header
  1780.     jc    odixmt6            ; c = failure, abandon
  1781.     mov    ecbx.datalen,ax        ; setup ECB overall data
  1782.     mov    ecbx.frag1len,ax    ; and fragment length
  1783.  
  1784.     mov    cx,3            ; three words of dest MAC address
  1785.     mov    di,offset DGROUP:ecbx.immaddr    ; destination address in ecb
  1786.     rep    movsw            ; copy destination MAC address
  1787.     add    si,6            ; skip source Ethernet address
  1788.     lodsw                ; get protocol TYPE, move it to AX
  1789.     mov    ecbx.frag1addr,si    ; offset of packet data
  1790.     mov    si,ds
  1791.     mov    ecbx.frag1addr+2,si    ; segment of packet data
  1792.  
  1793. ; Note: Prescan and Default methods use Raw Send: put 0ffffh in StackID
  1794. ; and include the full frame header in the data field. Check MLID 
  1795. ; configuration word ModeFlags, MRawSendBit, for Raw Send capability.
  1796.  
  1797.     cmp    ax,ip_type        ; IP, 0x0008h?
  1798.     jne    odixmt1            ; ne = no
  1799.     mov    si,offset DGROUP:ip_stackid
  1800.     jmp    short odixmt3
  1801.  
  1802. odixmt1:cmp    ax,arp_type        ; ARP, 0x0608?
  1803.     jne    odixmt2            ; ne = no
  1804.     mov    si,offset DGROUP:arp_stackid
  1805.     jmp    short odixmt3
  1806.  
  1807. odixmt2:cmp    ax,rarp_type        ; RARP, 0x3580?
  1808.     jne    odixmt6            ; ne = error, do not send
  1809.     mov    si,offset DGROUP:rarp_stackid
  1810.  
  1811. odixmt3:mov    cx,5            ; stackid, protid, boardnum
  1812.     mov    di,offset DGROUP:ecbx.stackid    ; get stack ident area
  1813.     rep    movsw            ; copy to ecbx
  1814.  
  1815.     mov    ecbx_busy,1        ; set ECBx busy flag to busy state
  1816.     add    pstats.PtotalTxPackets,1 ; update ODI statistics counter
  1817.     adc    pstats.PtotalTxPackets+2,0
  1818.  
  1819.     mov    si,offset DGROUP:ecbx    ; set es:si to ECB
  1820.     mov    bx,PROTSUP_SEND_PACKET    ; send it
  1821.     call    tstart            ; start transmit timer
  1822.     call    lslsupport        ; call LSL with ecbx ptr in es:si
  1823.     call    tstop            ; stop transmit timer
  1824.     clc                ; success so far, ints are still off
  1825. odixmt6:pop    es
  1826.     pop    di
  1827.     pop    si
  1828.     ret
  1829. odixmt    endp
  1830.  
  1831. ; ODI transmission-complete processor, for use only by the LSL.
  1832. ; Returns nothing. Unlocks ECB busy flag.
  1833. odixcmp    proc    far
  1834.     push    ds
  1835.     push    ax
  1836.     mov    ax,DGROUP        ; set addressibility
  1837.     mov    ds,ax
  1838.     mov    ecbx_busy,0        ; set ECB busy flag to not-busy
  1839.     pop    ax
  1840.     pop    ds
  1841.     ret
  1842. odixcmp    endp
  1843.  
  1844. ; ODI Protocol (that's us) Control routine, required, called from outside.
  1845. ; In principle we should have one of these for each protocol (IP, ARP, RARP)
  1846. ; by putting a different StackControlHandler address in registerstk, but I 
  1847. ; doubt that anyone is that interested in such detailed counts.
  1848. ; Return AX clear, Z flag set, and ES:SI as table pointer if success, else
  1849. ; return AX with error code and Z flag clear.
  1850. pcontrol proc    far
  1851.     cmp    bx,GET_STACK_CONFIGURATION ; get stack configuration?
  1852.     jne    pcont1            ; ne = no
  1853.     mov    si,DGROUP
  1854.     mov    es,si            ; es:si points to configuration table
  1855.     mov    si,offset DGROUP:pconfig; the table
  1856.     xor    ax,ax            ; set Z flag
  1857.     ret
  1858. pcont1:    cmp    bx,GET_STACK_STATISTICS    ; get stack statistics?
  1859.     jne    pcont2            ; ne = no
  1860.     mov    si,DGROUP
  1861.     mov    es,si            ; es:si points to statistics table
  1862.     mov    si,offset DGROUP:pstats    ; the table
  1863.     xor    ax,ax            ; set Z flag
  1864.     ret
  1865. pcont2:    mov    ax,LSLERR_OUT_OF_RESOURCES ; other functions, report error
  1866.     or    ax,ax            ; clear Z flag
  1867.     ret
  1868. pcontrol endp
  1869.  
  1870. ; timer support routines
  1871. rstart    proc    near
  1872.     pushf
  1873.     cmp    _kdebug,0
  1874.     je    rstart1
  1875.     push    ax
  1876.     in    al,40h        ; 8254 timer channel T0, read LSB
  1877.     xchg    ah,al
  1878.     in    al,40h        ; read MSB
  1879.     xchg    al,ah        ; order properly
  1880.     mov    startrtic,ax
  1881.     pop    ax
  1882. rstart1:popf
  1883.     ret
  1884. rstart    endp
  1885.  
  1886. rstop    proc    near
  1887.     pushf
  1888.     cmp    _kdebug,0
  1889.     je    rstop1
  1890.     push    ax
  1891.     in    al,40h        ; 8254 timer channel T0, read LSB
  1892.     xchg    ah,al
  1893.     in    al,40h        ; read MSB
  1894.     xchg    al,ah        ; order properly
  1895.     mov    stoprtic,ax
  1896.     pop    ax
  1897. rstop1:    popf
  1898.     ret
  1899. rstop    endp
  1900.  
  1901. tstart    proc    near
  1902.     pushf
  1903.     cmp    _kdebug,0
  1904.     je    tstart1
  1905.     push    ax
  1906.     in    al,40h        ; 8254 timer channel T0, read LSB
  1907.     xchg    ah,al
  1908.     in    al,40h        ; read MSB
  1909.     xchg    al,ah        ; order properly
  1910.     mov    startttic,ax
  1911.     pop    ax
  1912. tstart1:popf
  1913.     ret
  1914. tstart    endp
  1915.  
  1916. tstop    proc    near
  1917.     pushf
  1918.     cmp    _kdebug,0
  1919.     je    tstop1
  1920.     push     ax
  1921.     in    al,40h        ; 8254 timer channel T0, read LSB
  1922.     xchg    ah,al
  1923.     in    al,40h        ; read MSB
  1924.     xchg    al,ah        ; order properly
  1925.     sti                ; for odixmt
  1926.     mov    stopttic,ax
  1927.     call    showtime        ; display timing results
  1928.     pop    ax
  1929. tstop1:    popf
  1930.     ret
  1931. tstop    endp
  1932.  
  1933. showtime proc    near
  1934.     push    es
  1935.     push    di
  1936.     push    bx
  1937.     mov    bx,data
  1938.     mov    es,bx
  1939.     mov    al,es:crt_cols            ; screen columns
  1940.     mul    es:crt_lins            ; screen lines
  1941.     mov    bx,ax
  1942.     add    bx,40                ; column 40
  1943.     shl    bx,1
  1944.     mov    di,es:tv_sego            ; current screen offset
  1945.     add    bx,di
  1946.     mov    di,es:tv_segs            ; current screen segment
  1947.     mov    es,di
  1948.     mov    ax,startrtic
  1949.     sub    ax,stoprtic
  1950.     jns    showti1
  1951.     neg    ax
  1952. showti1:sub    ax,overhead
  1953.     mov    di,bx
  1954.     call    decout
  1955.     mov    byte ptr es:[di],'R'
  1956.  
  1957.     mov    ax,startttic
  1958.     sub    ax,stopttic
  1959.     jns    showti2
  1960.     neg    ax
  1961. showti2:sub    ax,overhead
  1962.     mov    di,bx
  1963.     add    di,5*2
  1964.     call    decout
  1965.     mov    byte ptr es:[di],'T'
  1966.     pop    bx
  1967.     pop    di
  1968.     pop    es
  1969.     ret
  1970. showtime endp
  1971.  
  1972. decout    proc    near        ; display decimal number in ax
  1973.     push    cx
  1974.     push    dx
  1975.     mov    byte ptr es:[di],' '    ; clear screen of old info
  1976.     mov    byte ptr es:[di+2],' '
  1977.     mov    byte ptr es:[di+4],' '
  1978.     mov    byte ptr es:[di+6],' '
  1979.     mov    byte ptr es:[di+8],' '
  1980.     test    ax,8000h        ; negative?
  1981.     jz    decout1            ; z = no
  1982.     neg    ax
  1983.     mov    byte ptr es:[di],'-'    ; minus sign
  1984.     inc    di
  1985.     inc    di
  1986. decout1:mov    cl,t0count        ; timer chip T0 count factor
  1987.     mov    dx,2000
  1988.     shr    dx,cl        ; t0count compensation (by 1 or 2 shifts)
  1989.     mul    dx
  1990.     mov    cx,1193
  1991.     div    cx
  1992.     mov    cx,10            ; set the numeric base
  1993.     call    mvalout            ; convert and output value
  1994.     pop    dx
  1995.     pop    cx
  1996.     ret
  1997. decout    endp
  1998.  
  1999. mvalout    proc    near        ; output number in ax using base in cx
  2000.                 ; corrupts ax and dx
  2001.     xor    dx,dx        ; clear high word of numerator
  2002.     div    cx        ; (ax / cx), remainder = dx, quotient = ax
  2003.     push    dx        ; save remainder for outputting later
  2004.     or    ax,ax        ; any quotient left?
  2005.     jz    mvalout1    ; z = no
  2006.     call    mvalout        ; yes, recurse
  2007. mvalout1:pop    dx        ; get remainder
  2008.     add    dl,'0'        ; make digit printable
  2009.     cmp    dl,'9'        ; above 9?
  2010.     jbe    mvalout2    ; be = no
  2011.     add    dl,'A'-1-'9'    ; use 'A'--'F' for values above 9
  2012. mvalout2:mov    es:[di],dl
  2013.     inc    di
  2014.     inc    di
  2015.     ret
  2016. mvalout    endp
  2017. _TEXT    ends
  2018.         end
  2019.  
  2020.