home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / hamradio / ax253008.zip / AX25.ASM next >
Assembly Source File  |  1992-09-02  |  50KB  |  2,077 lines

  1. ;AX25 packet driver for RS232 port by Pawel Jalocha
  2. ;version of 30th August 1992
  3.  
  4. ;The purpose of this piece of software is to provide interface
  5. ;between hardware (BAYCOM-like modem) and software (NOS).
  6.  
  7. ;Driver's calling convention conforms to "FTP packet driver specification"
  8.  
  9. ;To make ax25.com from ax25.asm execute two commands:
  10. ;    tasm ax25
  11. ;    tlink /t ax25
  12.  
  13. ;Free license for this software is herein granted for all radio _amateurs_
  14. ;Commercial usage in whole or part is prohibited.
  15.  
  16. ;Questions may be addressed to:
  17. ;    email:    jalocha@chopin.ifj.edu.pl
  18. ;    or    jalocha@vxcern.cern.ch
  19. ;    packet:    SR9VRC@SP9ZDN.POL.EU (untested)
  20.  
  21. ;==============================================================
  22.  
  23. CL_AX25 equ 9
  24.  
  25. code    segment    word public
  26.     assume    cs:code, ds:code
  27.  
  28.     org 2ch
  29. phd_environ    dw ?
  30.  
  31.     org 100h
  32. start:  jmp install_driver
  33.  
  34.         even
  35. ;primary parameters
  36. packet_int_no    db 60h    ;software interrupt
  37. com_irq        db 4    ;COM port IRQ
  38. com_base    dw 3f8h    ;COM port base
  39. bit_rate    dw 1200    ;bps
  40. tx_head        dw 480    ;transmition header length in bits
  41. tx_tail        dw 24    ;transmition tail length in bits
  42. slot_time    db 120    ;slot time for carrier sensing in bits
  43. persistance    db 64    ;p-persistence
  44. carrier_sense    db 2    ;carrier sensing
  45.             ;0 - don't care - just transmit (full duplex)
  46.             ;1 - sense DCD line
  47.             ;2 - sense data transitions (BAYCOM-like)
  48.             ;3 - deliver from data analysis
  49.  
  50. ;secondary parameters computed from primary ones
  51. irq_mask    db 0      ;IRQ mask for 8259
  52. cl_bit_len    dw 0      ;bit len in system clock (8253/4) ticks
  53. cl_bit_len_2    dw 0    ;half bit len in clock ticks
  54. bd_slot_time    dw 0    ;slot time in baud generator ticks / 8
  55. bd_bit_len    db 0    ;bit len in baud generator ticks / 8
  56.  
  57. driver_name    db 'AX25 driver for BAYCOM-like modem',0
  58.         even
  59. old_packet_int    dw 0,0
  60. receive_upcall    dw 0,0
  61.  
  62. ;==============================================================
  63. ;Service routine for software interrupt to control the driver
  64.  
  65. ;  Packet Driver Error numbers
  66. NO_ERROR    equ    0    ;no error at all.
  67. BAD_HANDLE    equ    1    ;invalid handle number
  68. NO_CLASS    equ    2    ;no interfaces of specified class found
  69. NO_TYPE        equ    3    ;no interfaces of specified type found
  70. NO_NUMBER    equ    4    ;no interfaces of specified number found
  71. BAD_TYPE    equ    5    ;bad packet type specified
  72. NO_MULTICAST    equ    6    ;this interface does not support multicast
  73. CANT_TERMINATE    equ    7    ;this packet driver cannot terminate
  74. BAD_MODE    equ    8    ;an invalid receiver mode was specified
  75. NO_SPACE    equ    9    ;operation failed because of insufficient space
  76. TYPE_INUSE    equ    10    ;the type had previously been accessed, and not released.
  77. BAD_COMMAND    equ    11    ;the command was out of range, or not implemented
  78. CANT_SEND    equ    12    ;the packet couldn't be sent (usually hardware error)
  79. CANT_SET    equ    13    ;hardware address couldn't be changed (more than 1 handle open)
  80. BAD_ADDRESS    equ    14    ;hardware address has bad length or format
  81. CANT_RESET    equ    15    ;Couldn't reset interface (more than 1 handle open).
  82. BAD_IOCB    equ    16    ;an invalid iocb was specified
  83.  
  84. regs_w    struc                ; stack offsets of incoming regs
  85. _ES    dw    ?
  86. _DS    dw    ?
  87. _BP    dw    ?
  88. _DI    dw    ?
  89. _SI    dw    ?
  90. _DX    dw    ?
  91. _CX    dw    ?
  92. _BX    dw    ?
  93. _AX    dw    ?
  94. _IP    dw    ?
  95. _CS    dw    ?
  96. _F    dw    ?            ; flags, Carry flag is bit 0
  97. regs_w    ends
  98.  
  99. CY    equ    0001h
  100. EI    equ    0200h
  101.  
  102. regs_b    struc                ; stack offsets of incoming regs
  103.     dw    ?            ; es, ds, bp, di, si are 16 bits
  104.     dw    ?
  105.     dw    ?
  106.     dw    ?
  107.     dw    ?
  108. _DL    db    ?
  109. _DH    db    ?
  110. _CL    db    ?
  111. _CH    db    ?
  112. _BL    db    ?
  113. _BH    db    ?
  114. _AL    db    ?
  115. _AH    db    ?
  116. regs_b    ends
  117.  
  118. DRVR_ISR:            ;service interrupt vector points here
  119.     jmp exec_command
  120.     db 'PKT DRVR',0
  121.  
  122. exec_command:       ;packet driver command executor
  123.     sti         ;don't lock interrupts
  124.     push ax        ;save registers on stack
  125.     push bx
  126.     push cx
  127.     push dx
  128.     push si
  129.     push di
  130.     push bp
  131.     push ds
  132.     push es
  133.     mov  bp,sp        ;bp=sp so we can address pushed registers
  134.     and _F[bp],not CY       ;Clear carry on exit
  135.     mov bx,cs        ;make ds=cs
  136.     mov ds,bx
  137.     mov bl,ah        ;execute command given by ah
  138.     mov bh,0
  139.     cmp bx,26
  140.         jnc f_above_25
  141.     add bx,bx
  142.     call [functions+bx]
  143. DRVR_ISR_return:
  144.     mov _DH[bp],dh        ;pass dh now to dh on exit
  145.     sbb ax,ax
  146.     and ax,CY
  147.     or _F[bp],ax        ;pass carry now to carry on exit
  148.     pop es
  149.     pop ds
  150.     pop bp
  151.     pop di
  152.     pop si
  153.     pop dx
  154.     pop cx
  155.     pop bx
  156.     pop ax
  157.         iret
  158.  
  159. f_above_25:
  160.     call f_not_implemented
  161.     jmp DRVR_ISR_return
  162.  
  163.     even
  164. functions    label    word
  165.     dw    f_not_implemented    ;0
  166.     dw    f_driver_info        ;1
  167.     dw    f_access_type        ;2
  168.     dw    f_release_type        ;3
  169.     dw    f_send_pkt        ;4
  170.     dw    f_terminate        ;5
  171.     dw    f_get_address        ;6
  172.     dw    f_reset_interface    ;7
  173.     dw    f_stop            ;8
  174.     dw    f_not_implemented    ;9
  175.     dw    f_get_parameters    ;10
  176.     dw    f_not_implemented    ;11
  177.     dw    f_as_send_pkt        ;12
  178.     dw    f_drop_pkt        ;13
  179.     dw    f_not_implemented    ;14
  180.     dw    f_not_implemented    ;15
  181.     dw    f_not_implemented    ;16
  182.     dw    f_not_implemented    ;17
  183.     dw    f_not_implemented    ;18
  184.     dw    f_not_implemented    ;19
  185.     dw    f_set_rcv_mode        ;20
  186.     dw    f_get_rcv_mode        ;21
  187.     dw    f_set_multicast_list    ;22
  188.     dw    f_get_multicast_list    ;23
  189.     dw    f_get_statistics    ;24
  190.     dw    f_set_address        ;25
  191.  
  192. f_not_implemented:        ;non-implemented functions jump here
  193.     stc                     ;set carry to indicate an error
  194.     mov dh,BAD_COMMAND      ;error code = BAD_COMMAND
  195.         ret
  196.  
  197. f_driver_info:
  198.     mov _CH[bp],CL_AX25        ;driver class
  199.     mov _AL[bp],1            ;basic flag
  200.     mov _DX[bp],0            ;driver type
  201.     mov _CL[bp],0            ;driver number
  202.     mov _BX[bp],0            ;driver version
  203.     mov _DS[bp],ds            ;driver name pointer
  204.     mov _SI[bp],offset driver_name
  205.     mov dh,NO_ERROR
  206.     clc
  207.         ret
  208.  
  209. f_access_type:
  210.     mov bx,_BX[bp]
  211.     cmp al,CL_AX25        ;our class ?
  212.     jnz wrong_class
  213.     cmp bx,0FFFFh        ;generic type ?
  214.     jz type_OK
  215.     cmp bx,0        ;our type ?
  216.     jnz wrong_type
  217. type_OK:
  218.     cmp dl,0        ;generic num ?
  219.     jnz wrong_num
  220.     mov ax,receive_upcall    ;check if handle busy
  221.     or ax,receive_upcall+2
  222.     jnz busy_handle
  223.     mov receive_upcall,di    ;store receiver upcall
  224.     mov ax,es
  225.     mov receive_upcall+2,ax
  226.     mov _AX[bp],0        ;return handle=0
  227.     clc
  228.     mov dh,NO_ERROR
  229.     ret
  230.  
  231. wrong_class:
  232.     stc
  233.     mov dh,NO_CLASS
  234.     ret
  235. wrong_type:
  236.     stc
  237.     mov dh,NO_TYPE
  238.     ret
  239. wrong_num:
  240.     stc
  241.     mov dh,NO_NUMBER
  242.     ret
  243. busy_handle:
  244.     stc
  245.     mov dh,TYPE_INUSE
  246.     ret
  247.  
  248. f_stop:    jmp clear_upcall
  249.  
  250. f_release_type:
  251.     cmp _BX[bp],0        ;handle=0 ?
  252.     jnz wrong_handle
  253.     mov ax,receive_upcall  ;is receiver upcall defined ?
  254.     or ax,receive_upcall+2
  255.     jz wrong_handle        ;jump if not
  256. clear_upcall:
  257.     xor ax,ax        ;clear receiver upcall
  258.     mov receive_upcall,ax
  259.     mov receive_upcall+2,ax
  260.     clc
  261.     mov dh,NO_ERROR
  262.     ret
  263.  
  264. wrong_handle:
  265.     stc
  266.     mov dh,BAD_HANDLE
  267.     ret
  268.  
  269. f_send_pkt:        ;_DS:si=data, cx=length
  270.     mov es,_DS[bp]    ;es:si=packet address, cx=packet length
  271.  
  272.     mov bx,cx    ;save packet length
  273.     mov ah,7Eh    ;starting HDLC flag
  274.     call AddTxByteDirect
  275.     jc PacketTooBig
  276.     mov dx,0FFFFh        ;initialize CRC
  277. AddNextByte:
  278.       mov ah,es:[si]
  279.       inc si
  280.       call CRCpass          ;pass through CRC
  281.       call AddTxByteStuffing
  282.       jc PacketTooBig
  283.     loop AddNextByte
  284.     not dx            ;complete CRC computation by inverting all bits
  285.     mov ax,dx         ;append CRC
  286.     xchg al,ah        ;lower byte first
  287.     call AddTxByteStuffing
  288.     jc PacketTooBig
  289.     xchg al,ah        ;higher byte now
  290.     call AddTxByteStuffing
  291.     jc PacketTooBig
  292.     mov ah,7Eh        ;add ending HDLC flag
  293.     call AddTxByteDirect
  294.     jc PacketTooBig
  295.     call TxFlush8bit
  296.     jz PacketTooBig
  297.  
  298.     call ValidateTxBlock    ;make the packet we just put into buffer
  299.                 ;valid for transmition
  300.  
  301.     mov ax,bx        ;increment bytes_out counter
  302.     mov bx,offset bytes_out ;note that we still had the packet length in bx
  303.     call inc_dword_bx_by_ax
  304.  
  305.     mov bx,offset packets_out    ;increment packet_out counter
  306.     call inc_dword_bx
  307.  
  308.     clc
  309.     mov dh,NO_ERROR
  310.     ret
  311.  
  312. PacketTooBig:
  313.     call TxFlush8bit
  314.     call CancelTxBlock        ;cancel the block we were writing
  315.                     ;into Tx buffer
  316.     mov bx,offset errors_out    ;increment errors_out counter
  317.     call inc_dword_bx
  318.  
  319.     stc
  320.     mov dh,CANT_SEND
  321.     ret
  322.  
  323. ;Note that packets_out/bytes_out counts data sent by application
  324. ;that is _not_ the data transmitted from the buffer.
  325.  
  326. f_terminate:
  327.     call deinstall_driver
  328.     clc
  329.     mov dh,NO_ERROR
  330.     ret
  331.  
  332. f_get_address:      jmp f_not_implemented
  333. f_reset_interface:      jmp f_not_implemented
  334. f_get_parameters:    jmp f_not_implemented
  335. f_as_send_pkt:          jmp f_not_implemented
  336. f_drop_pkt:        jmp f_not_implemented
  337. f_set_rcv_mode:        jmp f_not_implemented
  338. f_get_rcv_mode:         jmp f_not_implemented
  339. f_set_multicast_list:    jmp f_not_implemented
  340. f_get_multicast_list:    jmp f_not_implemented
  341.  
  342. f_get_statistics:
  343.     mov _DS[bp],ds
  344.     mov _SI[bp],offset statistics_list
  345.     clc
  346.     mov dh,NO_ERROR
  347.     ret
  348.  
  349. f_set_address:        jmp f_not_implemented
  350.  
  351.         even
  352. statistics_list    label    dword    ;statistics structure
  353. packets_in    dw    0,0    ;as in packet driver specification
  354. packets_out    dw    0,0
  355. bytes_in    dw    0,0
  356. bytes_out    dw    0,0
  357. errors_in    dw    0,0
  358. errors_out    dw    0,0
  359. packets_dropped    dw    0,0
  360.                 ;extended statistics
  361. ;PTT_pushes    dw    0,0
  362. ;HDLC_flags    dw    0,0
  363. ;frame_aborts    dw    0,0
  364. ;short_packets    dw    0,0    ;count valid but too short packets
  365.  
  366. ;Simple routines to increment double words
  367.  
  368. inc_dword_bx_by_ax:        ;increments dword ds:[bx] by ax
  369.     add word ptr [bx],ax
  370.     inc bx
  371.     inc bx
  372.     adc word ptr [bx],0
  373.     dec bx
  374.     dec bx
  375.     ret
  376.  
  377. inc_dword_bx:        ;increments dword ds:[bx] by 1
  378.     add word ptr [bx],1
  379.     inc bx
  380.     inc bx
  381.     adc word ptr [bx],0
  382.     dec bx
  383.     dec bx
  384.     ret
  385.  
  386. ;==============================================================
  387.         even
  388. OldTimerISR    dw 0,0
  389. TimerISRActive    db 0
  390.  
  391. Initialize_Timer:
  392.     pushf
  393.     push ax
  394.     push dx
  395.     cli
  396.  
  397.     push es
  398.     push bx
  399.     mov al,8        ;save Timer interrupt vector
  400.     mov ah,35h
  401.     int 21h
  402.     mov OldTimerISR,bx
  403.     mov ax,es
  404.     mov OldTimerISR+2,ax
  405.     pop bx
  406.     pop es
  407.  
  408.     mov al,8        ;set new vector
  409.     mov ah,25h
  410.     mov dx,offset Timer_ISR
  411.     int 21h
  412.  
  413.     pop dx
  414.     pop ax
  415.     popf
  416.     ret
  417.  
  418. Restore_Timer:
  419.     pushf
  420.     push ax
  421.     push dx
  422.     cli
  423.  
  424.     push ds
  425.     mov al,8        ;restore interrupt vector
  426.     mov ah,25h
  427.     lds dx,dword ptr OldTimerISR
  428.     int 21h
  429.     pop ds
  430.  
  431.     pop dx
  432.     pop ax
  433.     popf
  434.     ret
  435.  
  436. Timer_ISR:
  437.     ;sti            ;shall we enable interrupts already here ?
  438.     pushf            ;first call the old routine
  439.     call dword ptr cs:[OldTimerISR]
  440.     push ax
  441.     push bx
  442.     push cx
  443.     push dx
  444.     push ds
  445.     mov ax,cs        ;make ds=cs
  446.     mov ds,ax
  447.     mov al,TimerISRActive    ;check for possible recursive call
  448.     and al,al
  449.     jnz TimerISR_already_Active    ;jump if it is so
  450.     mov al,0FFh              ;mark Timer ISR as really active
  451.     mov TimerISRActive,al
  452.     sti            ;enable interrupts so Tx and Rx can run
  453.                 ;while we process data collected by Rx
  454. ReadNextRxPeriod:
  455.       call ReadRxPeriod       ;read next Rx period into ax
  456.       jnc TimerISR_end        ;jump if buffer empty
  457.       call ProcessPeriod      ;here we process the period
  458.       jmp ReadNextRxPeriod    ;loop
  459. TimerISR_end:
  460.     xor al,al
  461.     mov TimerISRActive,al
  462. TimerISR_already_active:
  463.     pop ds
  464.     pop dx
  465.     pop cx
  466.     pop bx
  467.     pop ax
  468.     iret
  469.  
  470. MaxFrameLen    equ 2050        ;maximum frame length in bytes
  471.         even
  472. RxFrameData    db  MaxFrameLen dup(0)    ;frame buffer
  473. RxFrameLen    dw  0            ;actual frame length (counts bytes)
  474. RxBitCount     db  0            ;count single data bits
  475. RxFrameValid    db  0                   ;non-zero if frame is valid
  476. SamplePhase    dw  0                   ;time to next sampling point
  477. SampleLevel    dw  0
  478. RxByteReg    db  0
  479. RxStuffing    db  0
  480. RxShiftReg    db  0
  481.  
  482. ProcessPeriod:           ;on input ax=period
  483.     mov cx,SampleLevel    ;bit 0,cl=level, bit 0,ch=previous level
  484.     xor cl,1        ;flip level
  485.     mov dx,SamplePhase    ;dx=time to next sample
  486. MoveSampling:
  487.       cmp ax,dx        ;compare SamplePhase with period
  488.       jc PeriodLower    ;jump if Period lower
  489.       sub ax,dx        ;subtract SamplePhase from period
  490.       xor ch,cl        ;xor level with previous level
  491.       call NewRxBit        ;analyze bit in bit 0,ch
  492.       mov ch,cl        ;previous level = level
  493.       mov dx,cl_bit_len    ;load SamplePhase with bit length
  494.       jmp MoveSampling    ;loop
  495. PeriodLower:
  496.     sub dx,ax        ;subtract period form SamplePhase
  497.     mov SampleLevel,cx      ;save SampleLevel
  498.                 ;rather primitive DPLL
  499.     mov ax,cl_bit_len_2    ;load half bit period
  500.     sub ax,dx               ;subtract SamplePhase
  501.                                 ;now: dx=SamplePhase, ax=phase error
  502.         mov cl,2                ;divide the error by 4
  503.         sar ax,cl
  504.     add dx,ax        ;add correction to SamplePhase
  505.     mov SamplePhase,dx    ;save SamplePhase
  506.     ret
  507.  
  508. NewRxBit:        ;bit 0,ch = _inverted_ data bit to append to the frame
  509.             ;ax,cx,dx must not be modified
  510.     push ax
  511.     push cx
  512.     push dx
  513.     mov al,RxShiftReg       ;load shift reg.
  514.     shl al,1                ;append data bit (which is still inverted)
  515.     or al,ch
  516.     mov RxShiftReg,al    ;save shift reg.
  517.     cmp al,81h        ;check for HDLC flag (01111110 pattern)
  518.     jz RxFoundFlag        ;jump if so
  519.     test al,7Fh        ;check for invalid frame (7 1s in a row)
  520.     jz RxFrameInvalid    ;jump if so
  521.  
  522.     mov al,RxFrameValid    ;is frame still valid ?
  523.     and al,al
  524.     jz NewRxBit_ret        ;jump if so
  525.     xor ch,1        ;invert bit to append (it was inverted at entry)
  526.     mov al,RxByteReg    ;load byte reg.
  527.     mov ah,RxBitCount
  528.     and ch,1        ;zero to append ?
  529.     jz AppendZeroBit
  530.     ror ch,1        ;carry=data bit (must be 1 here...)
  531.     rcr al,1        ;append data bit to byte buffer
  532.     inc ah            ;increase bit counter
  533.     inc RxStuffing        ;increase stuffing flag
  534.     jmp CheckBitCount
  535. AppendZeroBit:
  536.     xor cl,cl
  537.     mov ch,RxStuffing    ;check for stuffing
  538.     cmp ch,5
  539.     mov RxStuffing,cl    ;clear stuffing flag
  540.     jz NewRxBit_ret        ;avoid adding zero bit
  541.       shr al,1        ;append zero bit
  542.       inc ah
  543. CheckBitCount:
  544.     mov RxByteReg,al    ;save byte reg.
  545.     mov RxBitCount,ah    ;save bit counter
  546.     test ah,07h             ;check for byte boundary
  547.     jnz NewRxBit_ret
  548.       mov bx,RxFrameLen     ;load frame length
  549.       cmp bx,MaxFrameLen    ;check frame size
  550.       jnc RxFrameInvalid    ;jump if frame would exceed max. length
  551.       mov [RxFrameData+bx],al
  552.       inc bx
  553.       mov RxFrameLen,bx    ;save new frame length
  554. NewRxBit_ret:
  555.     pop dx
  556.     pop cx
  557.     pop ax
  558.     ret
  559. RxFrameInvalid:
  560.     xor al,al
  561.     mov RxFrameValid,al
  562.     jmp NewRxBit_ret
  563. RxFoundFlag:
  564.     mov al,RxFrameValid    ;frame valid ?
  565.     and al,al
  566.     jz PrepareNewFrame    ;jump if not valid
  567.     mov al,RxBitCount    ;check bit count
  568.     inc al
  569.     test al,07h        ;check if multiply of 8
  570.     jnz PrepareNewFrame    ;jump if not
  571.     mov cx,RxFrameLen    ;check frame length
  572.     cmp cx,17
  573.     jc PrepareNewFrame    ;jump if length less then 17 bytes
  574.     mov dx,0FFFFh        ;initialize CRC
  575.     mov bx,offset RxFrameData      ;pass all frame bytes through CRC except last two
  576.     sub cx,2        ;decrease length by 2
  577. PassNextRxbyte:
  578.       mov ah,[bx]        ;load next byte
  579.       inc bx
  580.       call CRCpass        ;pass it through CRC
  581.     loop PassNextRxByte
  582.     not dx            ;negate CRC
  583.     cmp dl,[bx]        ;check lower CRC byte
  584.     jnz BadCRCFrame        ;jump if bad
  585.     inc bx
  586.     cmp dh,[bx]        ;check high CRC byte
  587.     jnz BadCRCFrame        ;jump if bad
  588.  
  589. ;Frame is OK !!! - do the upcall to the application layer.
  590.     call DoUpCall
  591.  
  592. PrepareNewFrame:
  593.     xor ax,ax        ;null frame lemgth
  594.     mov RxFrameLen,ax
  595.     mov RxBitCount,al    ;null bit count
  596.     mov RxStuffing,al    ;initialize bit stuffing
  597.     mov al,0FFh
  598.     mov RxFrameValid,al    ;mark frame as valid
  599.     jmp NewRxBit_ret
  600.  
  601. BadCRCFrame:            ;shall we really count bad CRC packets
  602.                 ;as "errors_in" ?
  603.  
  604.     mov bx,offset errors_in    ;increment "errors_in"
  605.     call inc_dword_bx
  606.  
  607.     jmp PrepareNewFrame    ;abort current frame and make ready for new one
  608.  
  609. DoUpCall:               ;input: RxFrameData contains a valid packet (CRC is OK)
  610.             ;       RxFrameLen contains its length
  611.     push ds
  612.     push es
  613.     push si
  614.     push di
  615.  
  616.     mov bx,offset packets_in    ;increment input packet counter
  617.     call inc_dword_bx
  618.  
  619.     mov cx,RxFrameLen       ;load packet length
  620.     sub cx,2        ;exclude CRC
  621.  
  622.     mov ax,cx        ;increment input bytes counter by packet length
  623.     mov bx,offset bytes_in
  624.     call inc_dword_bx_by_ax
  625.  
  626.     mov ax,receive_upcall    ;is there a valid upcall address ?
  627.     or ax,receive_upcall+2
  628.     jz drop_packet        ;jump if there is not
  629.  
  630.     mov ax,0        ;flag=0 - first upcall
  631.     mov bx,0        ;handle = 0
  632.     mov di,0         ;set es:di = NULL
  633.     mov es,di
  634.     call dword ptr [receive_upcall]    ;first upcall
  635.     mov ax,es        ;check if application returned
  636.     or ax,di                ;valid buffer pointer
  637.     jz drop_packet        ;jump if not
  638. ;    jz DoUpCall_ret
  639.  
  640.     mov si,di        ;make si=di before we alter di (for second upcall)
  641.     mov bx,offset RxFrameData ;copy the packet to es:di
  642.     mov cx,RxFrameLen    ;packet length (exclude CRC)
  643.     sub cx,2
  644. CopyLoop: mov al,[bx]        ;loop over packet bytes
  645.       inc bx        ;would movsb do the job ?
  646.       mov es:[di],al
  647.       inc di
  648.       loop CopyLoop
  649.     mov cx,RxFrameLen    ;again packet len for second call
  650.     sub cx,2        ;and without CRC
  651.     mov ax,es        ;make ds=es (ds not same as cs now !)
  652.     mov ds,ax
  653.     mov bx,0             ;handle 0
  654.     mov ax,1                ;flag=1 - second upcall
  655.     call dword ptr cs:[receive_upcall]    ;second upcall
  656. ;we have to use cs: addressing in above call because we modified ds
  657.  
  658. DoUpCall_ret:
  659.     pop di
  660.     pop si
  661.     pop es
  662.     pop ds
  663.     ret
  664.  
  665. drop_packet:
  666.     mov bx,offset packets_dropped    ;increment dropped packet counter
  667.     call inc_dword_bx
  668.     jmp DoUpCall_ret
  669.  
  670. ;Note that packets_dropped counts packets refused by the application
  671. ;on the first upcall
  672.  
  673. ;==============================================================
  674.         even
  675. save_IER    db 0
  676. save_LCR    db 0
  677. save_MCR    db 0
  678. save_DLL    db 0
  679. save_DLM    db 0
  680. save_irq_en    db 0FFh    ;save irq enabled in 8259
  681. save_ISR    dw 0,0    ;saved interrupt vector
  682.  
  683. initialize_COM:
  684.     push ax
  685.     push dx
  686.     pushf            ;save CPU interrupt flag
  687.     cli            ;disable interrupts
  688.  
  689.     push es
  690.     push bx
  691.     mov al,com_irq        ;save COM interrupt vector
  692.     add al,8
  693.     mov ah,35h
  694.     int 21h
  695.     mov save_ISR,bx
  696.     mov ax,es
  697.     mov save_ISR+2,ax
  698.     pop bx
  699.     pop es
  700.  
  701.     mov al,com_irq        ;set new vector
  702.     add al,8
  703.     mov ah,25h
  704.     mov dx,offset COM_ISR
  705.     int 21h
  706.  
  707.     mov ah,irq_mask
  708.     not ah
  709.     in al,21h        ;read 8259 mask
  710.     or al,ah        ;extract com irq mask
  711.     mov save_irq_en,al    ;save it
  712.     in al,21h        ;enable com irq in 8259
  713.     and al,ah        ;by clearing the right bit.
  714.     out 21h,al
  715.  
  716.                 ;save COM registers
  717.     mov dx,com_base         ;dx=com_base
  718.     inc dx            ;dx=IER
  719.     in al,dx                ;save IER
  720.     mov save_IER,al
  721.     xor al,al        ;disable all COM interrupts
  722.     out dx,al
  723.     add dx,2                ;dx=LCR
  724.     in al,dx                ;save LCR
  725.     mov save_LCR,al
  726.     inc dx            ;dx=MCR
  727.     in al,dx                ;save MCR
  728.     mov save_MCR,al
  729.     dec dx            ;dx=LCR
  730.     mov al,81h
  731.     out dx,al        ;enable divisor read/write
  732.     sub dx,3        ;dx=com_base=DLL
  733.     in al,dx            ;read DLL
  734.     mov save_DLL,al        ;save it
  735.     inc dx            ;dx=DLM
  736.     in al,dx        ;read DLM
  737.     mov save_DLM,al        ;save it
  738.     dec dx            ;dx=com_base=DLL
  739.     mov ax,bd_slot_time    ;set rate divisor
  740.                 ;to slot_time*bd_bit_len
  741.     out dx,ax
  742.     add dx,3        ;dx=LCR
  743.     mov al,01h        ;set 6 data/1 stop/no parity format
  744.     out dx,al
  745.     inc dx                  ;dx=MCR
  746.     mov al,09h        ;set DTR high, RTS low.
  747.     out dx,al               ;and OUT2 high
  748.     sub dx,3        ;dx=IER
  749.     mov al,0Ah
  750.     out dx,al        ;enable TxEmpty and modem status interrupt
  751.     add dx,4                ;dx=LSR
  752.     in al,dx        ;read LSR
  753.     inc dx            ;to clear possible line status int.
  754.     in al,dx        ;read MSR to clear possible modem status int
  755.     sub dx,6        ;dx=com_base again
  756.     in al,dx        ;read Rx buffer
  757.     in al,dx            ;to clear any possible pending Rx int.
  758.     mov al,000000b        ;load Tx with 000000 char
  759.     out dx,al
  760.     out dx,al
  761.  
  762.     popf            ;restore interrupt flag
  763.     pop dx
  764.     pop ax
  765.     ret
  766.  
  767. restore_COM:
  768.     pushf
  769.     push ax
  770.     push dx
  771.     cli
  772.  
  773.     push ds
  774.     mov al,com_irq        ;restore interrupt vector
  775.     add al,8
  776.     mov ah,25h
  777.     lds dx,dword ptr save_ISR
  778.     int 21h
  779.     pop ds
  780.  
  781.     in al,21h        ;restore int. enable in 8259
  782.     or al,irq_mask
  783.     and al,save_irq_en
  784.     out 21h,al
  785.  
  786.     mov dx,com_base
  787.     in al,dx        ;read Rx to clear a possible int.
  788.     in al,dx
  789.     inc dx            ;dx=IER
  790.     mov al,save_IER         ;restore IER
  791.     out dx,al
  792.     add dx,2                 ;dx=LCR
  793.     mov al,save_LCR         ;restore LCR
  794.     out dx,al
  795.     inc dx            ;dx=MCR
  796.     mov al,save_MCR         ;restore MCR
  797.     out dx,al
  798.     inc dx            ;dx=LSR
  799.     in al,dx         ;read LSR to clear possible int.
  800.     inc dx                  ;dx=MSR
  801.     in al,dx                ;read MSR to clear possible int.
  802.     sub dx,3         ;dx=LCR
  803.     in al,dx                ;enable rate divisor access
  804.     or al,80h
  805.     out dx,al
  806.     sub dx,3        ;dx=com_base=DLL
  807.     mov al,save_DLL         ;restore rate divisor
  808.     out dx,al
  809.     inc dx                  ;dx=DLM
  810.     mov al,save_DLM
  811.     out dx,al
  812.     add dx,2        ;dx=LCR
  813.     mov al,save_LCR         ;restore LCR again
  814.     out dx,al
  815.  
  816.     pop dx
  817.     pop ax
  818.     popf
  819.     ret
  820.  
  821.         even
  822. TxCountDown    dw 0    ;Down counter to measure Tx bits.
  823. TxState        db 0    ;0 = idle
  824.             ;1 = sending head
  825.             ;2 = sending usefull data
  826.             ;3 = sending tail
  827.  
  828. COM_ISR:
  829.     push ax        ;save most often used registers on stack
  830.     push bx
  831.     push cx
  832.     push dx
  833.     push ds
  834.  
  835.     mov ax,cs
  836.     mov ds,ax
  837.     mov dx,com_base
  838.     add dx,2        ;dx=IIR
  839.  
  840.     in al,dx        ;load IIR
  841.     test al,000000001b    ;COM interrupt pending ?
  842.     jnz No_COM_Service    ;jump if not
  843.  
  844.     dec dx            ;dx=IER
  845.     xor al,al
  846.     out dx,al        ;disable all COM interrupts
  847.     inc dx
  848.                 ;now check for possible interrupts sources
  849.     call serv_modem_state    ;dx=IIR and may not be changed
  850.     call serv_tx_empty
  851.  
  852.     mov al,20h        ;tell the interrupt controler
  853.     out 20h,al        ;that interrupt service is done.
  854.  
  855.     dec dx            ;dx=IER
  856.     mov al,0Ah
  857.     out dx,al        ;enable TxEmpty and Modem Status interrupts
  858.     inc dx
  859.  
  860. End_ISR:
  861.     pop ds            ;restore saved registers
  862.     pop dx
  863.     pop cx
  864.     pop bx
  865.     pop ax
  866.     iret
  867.  
  868. No_COM_service:
  869.     mov al,20h
  870.     out dx,al
  871.     jmp End_ISR
  872.  
  873.     even
  874. prev_timer_count dw 0
  875.  
  876. serv_modem_state:        ;dx=IIR
  877.     add dx,4        ;dx=MSR
  878.     in al,dx        ;read MSR
  879.     sub dx,4        ;dx=IIR
  880.     test al,00000001b    ;did CTS change state ?
  881.     jz serv_modem_state_ret    ;jump if not
  882.     xor al,al        ;read system timer count
  883.     out 43h,al
  884.     in al,40h
  885.     xchg al,ah
  886.     in al,40h
  887.     xchg al,ah        ;Timer value in ax now
  888.     mov bx,ax        ;subtract previous count (Timer counts _down_ !)
  889.     xchg ax,prev_timer_count
  890.     sub ax,bx        ;so now ax contains the period elapsed
  891.     shr ax,1
  892.     call StoreRxPeriod    ;may not change dx
  893.     call UpdateDataStat
  894. serv_modem_state_ret:
  895.     ret            ;dx=IIR
  896.  
  897. serv_tx_empty:        ;dx=IIR
  898.     add dx,3    ;dx=LSR
  899.     in al,dx
  900.     sub dx,3    ;dx=IIR
  901.     test al,00100000b ;THRE ?
  902.     jz serv_tx_empty_ret
  903.     mov bx,2    ;for faster add/sub dx,2
  904.     sub dx,bx     ;dx=TxData
  905.     xor al,al    ;load Tx with 0000000 char
  906.     out dx,al
  907.     add dx,bx    ;dx=IIR
  908.     call [word ptr ServTx]    ;bx=2, dx must _not_ be modified
  909. serv_tx_empty_ret:
  910.     ret
  911.  
  912.     even
  913. ServTx    dw ServTxIdle
  914.  
  915. ServTxIdle:            ;here a decision about pushing PTT should be taken
  916.     call Randomize        ;but first update RandomByte
  917.     call TxBufferEmpty    ;any data to transmit ?
  918.     jz ClearStat        ;jump if not
  919.     call TxPTTDecision    ;ask PTT decision circuit for permision
  920.     jnc ClearStat        ;jump if no permition
  921.                 ;decision positive - enter transmit mode
  922.     add dx,2        ;dx=MCR
  923.     in al,dx
  924.     or al,2            ;set RTS high
  925.     out dx,al        ;thus activate PTT
  926.     dec dx            ;dx=LCR
  927.     mov bl,bd_bit_len    ;bx=bit length in baud divisor units
  928.     xor bh,bh
  929.     call SetTxBaudDiv_bx    ;set baud generator to 1 bit len.
  930.     dec dx            ;dx=IIR
  931.     mov ax,tx_head        ;initialize count down
  932.     mov TxCountDown,ax    ;with Tx head len
  933.     mov ServTx,offset ServTxHead    ;Transmitter state is "head"
  934.     ret
  935. ClearStat:
  936.     call ClearDataStat
  937.     ret
  938.  
  939.     even
  940. Tx8bit     dw 0
  941. NextTxBit  db 0
  942.  
  943. ServTxHead:
  944.     add dx,bx        ;dx=MCR
  945.     in al,dx        ;flip DTR
  946.     xor al,1
  947.     out dx,al
  948.     sub dx,bx        ;dx=IIR
  949.     dec TxCountDown
  950.     jnz ServTxHead_ret
  951.       mov ServTx,offset ServTxData    ;enter data phase
  952.       jmp ReadNext8bit
  953. ServTxHead_ret:
  954.     ret
  955.  
  956. ServTxData:
  957.     add dx,bx        ;dx=MCR
  958.     in al,dx        ;flip DTR if next bit is 1
  959.     xor al,NextTxBit
  960.     out dx,al
  961.     sub dx,bx        ;dx=IIR
  962.     mov ax,Tx8bit        ;save bit counter and 8bit buffer
  963.     dec ah            ;decrement bit counter
  964.     jz ReadNext8bit        ;jump if zero
  965.     ror al,1        ;rotate 8bit buffer
  966.     mov Tx8Bit,ax        ;save it
  967.     and al,1        ;extract lowest bit
  968.     mov NextTxBit,al    ;save it
  969.     ret
  970. ReadNext8bit:
  971.     call ReadTx8bit        ;Read next 8 bits
  972.     jnc TxStartTail        ;jump if buffer empty
  973.     mov ah,8        ;bit counter = 8
  974.     not al            ;invert data bits
  975.     mov Tx8Bit,ax        ;save bit counter and 8bit buffer
  976.     and al,1        ;leave lowest bit only
  977.     mov NextTxBit,al
  978.     ret
  979. TxStartTail:
  980.     mov ax,tx_tail        ;load count down with tx tail length
  981.     mov TxCountDown,ax
  982.     mov ServTx,offset ServTxTail
  983.     ret
  984.  
  985. ServTxTail:            ;bx=2 at entry
  986.     add dx,bx        ;dx=MCR
  987.     in al,dx        ;flip DTR
  988.     xor al,1
  989.     out dx,al
  990.     sub dx,bx        ;dx=IIR
  991.     dec TxCountDown
  992.     jnz ServTxTail_ret
  993.       add dx,bx        ;dx=MCR
  994.       in al,dx
  995.       or al,1        ;set DTR high
  996.       and al,0FDh        ;and RTS low
  997.       out dx,al
  998.       dec dx        ;dx=LCR
  999.       mov bx,bd_slot_time    ;set TxEmpty interrupt rate to slot time
  1000.       call SetTxBaudDiv_bx
  1001.       dec dx                ;dx=IIR
  1002.       mov ServTx,offset ServTxIdle
  1003.       call ClearDataStat    ;clear statistics for DCD
  1004. ServTxTail_ret:
  1005.     ret
  1006.  
  1007. ;SetTxBaudDiv:        ;input: al=period in bits, dx=LCR
  1008. ;    mov ah,bd_bit_len
  1009. ;    mul ah
  1010. ;    mov bx,ax    ;save new baud
  1011.  
  1012. SetTxBaudDiv_bx:    ;input: bx=divisor value, dx=LCR
  1013.     mov al,81h    ;enable divisor access
  1014.     out dx,al
  1015.     sub dx,3    ;dx=DLL
  1016.     mov ax,bx    ;write in new baud rate
  1017.     out dx,ax
  1018.     add dx,3    ;dx=LCR again
  1019.     mov al,01h    ;disable divisor access
  1020.     out dx,al
  1021.     ret        ;output: dx=LCR, ax,bx modified
  1022.  
  1023. RandomWord dw 079BDh            ;random number for p-persistance algorithm
  1024.  
  1025. Randomize:            ;make new random number
  1026.     mov cx,dx        ;save dx
  1027.     xor al,al           ;read system timer count
  1028.     out 43h,al
  1029.     in al,40h        ;low byte
  1030.     xchg al,ah
  1031.     in al,40h               ;high byte
  1032.     xchg al,ah              ;timer value in ax now
  1033.     add ax,RandomWord       ;add previous random word
  1034.     mov bx,65521            ;multiply by 65521
  1035.     mul bx                  ;dx:ax = ax * bx
  1036.     xor ax,dx
  1037.     mov RandomWord,ax       ;save random word
  1038.     mov dx,cx        ;restore dx
  1039.     ret                     ;ax,bx,cx modified
  1040.  
  1041. TxPTTDecision:            ;this routine decides whether to push PTT now
  1042.                 ;input: dx=IIR
  1043.     mov al,carrier_sense
  1044.     and al,al         ;Full duplex ?
  1045.     jz PushPTT          ;jump if so
  1046.     call sense_carrier    ;Is somebody else transmiting ?
  1047.     jc DontPushPTT        ;jump if so
  1048.                 ;now comes the p-persistance...
  1049.         mov ax,RandomWord       ;ax=pseudo-random word
  1050.         xor ah,al               ;ah=pseudo-random byte
  1051.         mov al,persistance      ;al=persistance
  1052.         cmp ah,al               ;carry when ah<persistance
  1053.     ret
  1054.  
  1055. PushPTT:
  1056.     stc    ;yes, push PTT
  1057.     ret
  1058.  
  1059. DontPushPTT:
  1060.     clc
  1061.     ret
  1062.  
  1063. sense_carrier:        ;input: al=carrier mode, dx=MCR
  1064.             ;output: carry=carrier state
  1065.  
  1066.     cmp al,1        ;sense DCD line ?
  1067.     jz sense_DCD
  1068.     cmp al,2        ;sense data transition
  1069.     jz sense_DataTrans
  1070.     cmp al,3        ;be more clever ?
  1071.     jz sense_data
  1072.     clc            ;otherwise just say carrier=false
  1073.     ret
  1074.  
  1075. sense_DCD:
  1076.     add dx,4    ;dx=MSR
  1077.     in al,dx    ;read MSR
  1078.     sub dx,4    ;dx=IIR
  1079.     rcl al,1    ;carry=DCD state
  1080.     ret
  1081.  
  1082. sense_DataTrans:
  1083.     xor ax,ax        ;at least one data transition
  1084.     cmp ax,DataTransCount    ;since previous time slot ?
  1085.     ret            ;carry=1 if so
  1086.  
  1087. sense_data:
  1088.     mov bx,DataTransCount
  1089.     cmp bx,2        ;more than 1 transitions counted ?
  1090.     jc few_trans        ;jump if not
  1091.     mov cx,dx        ;save dx
  1092.     mov ax,PeriodDevSum    ;compute sum/count that is the average
  1093.     mov dx,PeriodDevSum+2
  1094.     div bx            ;ax=periodDevSum div DataTransCount
  1095.     mov bx,cl_bit_len_2
  1096.     shr bx,1
  1097.     shr bx,1        ;bx = 1/8 of the bit period
  1098.     cmp ax,bx              ;is the average bigger than 1/8 of the bit period ?
  1099.     mov dx,cx        ;restore dx
  1100.     ret            ;carry=0 (no carrier) if so
  1101. few_trans:
  1102.     clc        ;say carrier=false if there were only few transitions
  1103.     ret
  1104.  
  1105.         even
  1106. DataTransCount    dw 0        ;count input signal transitions
  1107. PeriodDevSum    dw 0,0        ;sums period deviations from round bit lenghts
  1108.  
  1109. ClearDataStat:            ;clear statistics for DCD
  1110.     xor ax,ax
  1111.     mov DataTransCount,ax
  1112.     mov PeriodDevSum,ax
  1113.     mov PeriodDevSum+2,ax
  1114.     ret
  1115. UpdateDataStat:            ;input: ax=period
  1116.     inc DataTransCount    ;increment data transition counter
  1117.     mov bl,carrier_sense    ;check carrier mode
  1118.     cmp bl,3        ;execute the rest only if carrier mode is 3
  1119.     jnz UpdateDataStat_ret
  1120.     push dx
  1121.     xor dx,dx
  1122.     add ax,cl_bit_len_2    ;period+=cl_bit_len/2
  1123.     mov bx,cl_bit_len
  1124.     div bx            ;dx=period div cl_bit_len
  1125.     shr bx,1        ;bx=cl_bit_len/2
  1126.     sub dx,bx        ;dx-=cl_bit_len/2
  1127.     jnc UpdateDevSum    ;if result negative
  1128.       neg dx        ;then negate dx
  1129. UpdateDevSum:
  1130.     xor ax,ax        ;add dx to PeriodDevSum
  1131.     add PeriodDevSum,dx    ;PeriodDevSum sums deviation of periods
  1132.     adc PeriodDevSum+2,ax    ;from multiple bit lengths
  1133.     pop dx
  1134. UpdateDataStat_ret:
  1135.     ret        ;ax,bx modified
  1136. ;==============================================================
  1137.  
  1138.     even
  1139. TxBufferLen equ 4095         ;Tx buffer length in bytes - must be 2^n-1
  1140. TxBuffer db TxBufferLen+1 dup(0) ;Tx buffer storage
  1141.                  ;this buffer is filled by send_pkt routine
  1142.                  ;and flushed by TxEmpty service routine
  1143.                  ;when tx is in data phase
  1144.                  ;buffer pointers
  1145. TxReadPtr  dw 0        ;points to next byte to read
  1146. TxWritePtr dw 0        ;points to successor of the last valid byte
  1147. TxBlockPtr dw 0        ;Temporary pointer while writing in data block
  1148.             ;this is to ensure that only complete blocks
  1149.             ;will be transmitted
  1150.  
  1151. WriteTx8bit:        ;input: al=byte to write
  1152.     push bx
  1153.     mov bx,TxBlockPtr       ;load block end pointer
  1154.     mov [TxBuffer+bx],al    ;store byte
  1155.     inc bx                  ;increase pointer
  1156.     and bx,TxBufferLen    ;and flip it around
  1157.     cmp bx,TxReadPtr        ;same as read ptr ?
  1158.     jz WriteTx_ret        ;jump if so
  1159.     mov TxBlockPtr,bx
  1160. WriteTx_ret:
  1161.     pop bx
  1162.     ret         ;on exit: Z set = buffer full
  1163.             ;registers unchanged
  1164.  
  1165. ValidateTxBlock:
  1166.     push bx
  1167.     mov bx,TxBlockPtr    ;make write pointer same as block pointer
  1168.     mov TxWritePtr,bx
  1169.     pop bx
  1170.     ret        ;registers unchanged
  1171.  
  1172. CancelTxBlock:
  1173.     push bx
  1174.     mov bx,TxWritePtr    ;make block pointer same as write pointer
  1175.     mov TxBlockPtr,bx
  1176.     pop bx
  1177.     ret        ;registers unchanged
  1178.  
  1179. ReadTx8bit:
  1180.     mov bx,TxReadPtr    ;load read pointer
  1181.     cmp bx,TxWritePtr    ;same as write pointer ?
  1182.     jz ReadTx_ret        ;jump if so
  1183.       mov al,[TxBuffer+bx]    ;read data into AL
  1184.       inc bx                ;increment the read pointer
  1185.       and bx,TxBufferLen    ;and flip it around
  1186.       mov TxReadPtr,bx    ;save it
  1187.       stc            ;set carry
  1188. ReadTx_ret:
  1189.     ret    ;if carry is 0 => buffer was empty
  1190.         ;bx is modified but this does not matter really
  1191.  
  1192.     even
  1193. Tx8bitBuffer dw 8000h
  1194.  
  1195. AddTxBit:            ;input: bh=8bit reg., bl=1s counter
  1196.     inc bl            ;increment 1s counter
  1197.     jc AddTxBit_1
  1198.       mov bl,0        ;clear counter when 0 bit
  1199. AddTxBit_1:
  1200.     rcr bh,1        ;shift the bit into 8bit reg.
  1201.     jnc AddTxBit_ret
  1202.       mov al,bh        ;write reg. into buffer
  1203.       call WriteTx8bit
  1204.       jz AddTxBit_err
  1205.       mov bh,80h
  1206. AddTxBit_ret:
  1207.     clc            ;clear carry => no problems
  1208.     ret
  1209. AddTxBit_err:
  1210.     stc              ;set carry => buffer overflow
  1211.     ret
  1212.  
  1213. TxFlush8bit:
  1214.     push ax
  1215.     mov ax,Tx8bitBuffer
  1216.     mov al,ah
  1217.     and al,al
  1218.     jz TxFlush_ret
  1219.     clc
  1220. TxFlush_l:
  1221.     rcr al,1
  1222.     jnc TxFlush_l
  1223.     call WriteTx8bit
  1224. TxFlush_ret:
  1225.     mov ax,8000h
  1226.     mov Tx8bitBuffer,ax
  1227.     pop ax
  1228.     ret        ;output: Z=1 means tx buffer overflow
  1229.  
  1230. AddTxByteDirect:    ;input: ah=byte to add
  1231.     push ax
  1232.     push bx
  1233.     push cx
  1234.     mov bx,Tx8bitBuffer
  1235.     mov cx,8
  1236. AddNextBit:
  1237.       ror ah,1
  1238.       call AddTxBit
  1239.       jc AddTxByte_ret
  1240.     loop AddNextBit
  1241. AddTxByte_ret:
  1242.     mov Tx8bitBuffer,bx
  1243.     pop cx
  1244.     pop bx
  1245.     pop ax
  1246.     ret        ;output: carry=1 if buffer overflow
  1247.  
  1248. AddTxByteStuffing:        ;input: ah=byte to append _with_ bit stuffing
  1249.     push ax
  1250.     push bx
  1251.     push cx
  1252.     mov bx,Tx8bitBuffer
  1253.     mov cx,8
  1254. AddNextBitS:            ;loop over bits
  1255.       ror ah,1        ;copy next bit to carry flag
  1256.       call AddTxBit
  1257.       jc AddTxByteS_ret
  1258.       cmp bl,5
  1259.       jc AddTxByteS_l
  1260.         clc            ;if more than 5 1s in a row
  1261.         call AddTxBit    ;append an extra 0 bit
  1262.         jc AddTxByteS_ret    ;jump if buffer overflow
  1263. AddTxByteS_l:
  1264.     loop AddNextBitS
  1265.     clc
  1266. AddTxByteS_ret:
  1267.     mov Tx8bitBuffer,bx
  1268.     pop cx
  1269.     pop bx
  1270.     pop ax
  1271.     ret            ;output: carry=1 means buffer overflow
  1272.  
  1273.  
  1274. TxBufferEmpty:
  1275.     mov bx,TxReadPtr
  1276.     cmp bx,TxWritePtr
  1277.     ret     ;Z=1 means buffer is empty
  1278.         ;bx is modified
  1279.  
  1280.     even                ;align buffer to word boudary
  1281. RxBufferLen equ 1023            ;in words, must be 2^n-1
  1282. RxBuffer   dw RxBufferLen+1 dup(0)    ;Rx buffer storing periods between CTS transition
  1283.                     ;this buffer is filled by CTS transition
  1284.                     ;interrupt routine and flushed
  1285.                     ;by system timer service routine
  1286. RxReadPtr  dw 0                ;read pointer
  1287. RxWritePtr dw 0                ;write pointer
  1288.  
  1289. StoreRxPeriod:            ;must not modify dx,cx
  1290.     mov bx,RxWritePtr    ;load store pointer
  1291.     mov [RxBuffer+bx],ax    ;store the period
  1292.     add bx,2        ;increment the pointer
  1293.     and bx,2*RxBufferLen    ;turn it around if needed
  1294.     mov RxWritePtr,bx    ;save it
  1295.     cmp bx,RxReadPtr        ;same as ReadPtr ?
  1296.     jnz RxStore_ret        ;jump if not
  1297.       mov bx,RxReadPtr
  1298.       add bx,2        ;increment the read pointer
  1299.       and bx,2*RxBufferLen    ;and turn it around
  1300.       mov RxReadPtr,bx
  1301. RxStore_ret:
  1302.     ret
  1303. ;the above routine discards the oldest period when the buffer overflows
  1304.  
  1305. ReadRxPeriod:            ;modifies only ax
  1306.     push bx
  1307.     mov bx,RxReadPtr    ;load read pointer
  1308.     cmp bx,RxWritePtr    ;same as write pointer ?
  1309.     jz RxRead_ret        ;jump if so
  1310.       mov ax,[RxBuffer+bx]    ;read the period
  1311.       add bx,2              ;increase the pointer
  1312.       and bx,2*RxBufferLen  ;turn it around
  1313.       mov RxReadPtr,bx      ;save it
  1314.       stc            ;set carry => data is in ax
  1315. RxRead_ret:
  1316.     pop bx
  1317.     ret            ;if carry is 0 => then buffer was empty
  1318.                 ;otherwise ax = period
  1319.  
  1320. ;==============================================================
  1321. ;CRC computation table and routine
  1322.  
  1323.     even
  1324. CRCtable dw        0,  4489,  8978, 12955, 17956, 22445, 25910, 29887
  1325.      dw    35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735
  1326.      dw     4225,   264, 13203,  8730, 22181, 18220, 30135, 25662
  1327.      dw    40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510
  1328.      dw     8450, 12427,   528,  5017, 26406, 30383, 17460, 21949
  1329.      dw    44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797
  1330.      dw    12675,  8202,  4753,   792, 30631, 26158, 21685, 17724
  1331.      dw    48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572
  1332.      dw    16900, 21389, 24854, 28831,  1056,  5545, 10034, 14011
  1333.      dw    52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859
  1334.      dw    21125, 17164, 29079, 24606,  5281,  1320, 14259,  9786
  1335.      dw    57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634
  1336.      dw    25350, 29327, 16404, 20893,  9506, 13483,  1584,  6073
  1337.      dw    61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921
  1338.      dw    29575, 25102, 20629, 16668, 13731,  9258,  5809,  1848
  1339.      dw    65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696
  1340.      dw    33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623
  1341.      dw     2112,  6601, 11090, 15067, 20068, 24557, 28022, 31999
  1342.      dw    38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398
  1343.      dw     6337,  2376, 15315, 10842, 24293, 20332, 32247, 27774
  1344.      dw    42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685
  1345.      dw    10562, 14539,  2640,  7129, 28518, 32495, 19572, 24061
  1346.      dw    46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460
  1347.      dw    14787, 10314,  6865,  2904, 32743, 28270, 23797, 19836
  1348.      dw    50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747
  1349.      dw    19012, 23501, 26966, 30943,  3168,  7657, 12146, 16123
  1350.      dw    54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522
  1351.      dw    23237, 19276, 31191, 26718,  7393,  3432, 16371, 11898
  1352.      dw    59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809
  1353.      dw    27462, 31439, 18516, 23005, 11618, 15595,  3696,  8185
  1354.      dw    63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584
  1355.      dw    31687, 27214, 22741, 18780, 15843, 11370,  7921,  3960
  1356.  
  1357. CRCpass:        ;input: dx=partial CRC
  1358.     push bx         ;       ah=char to process
  1359.     push ax
  1360.     xor dl,ah
  1361.     mov bl,dl
  1362.     xor bh,bh
  1363.     add bx,bx
  1364.     mov dl,dh
  1365.     xor dh,dh
  1366.     xor dx,[CRCtable+bx]
  1367.     pop ax
  1368.     pop bx
  1369.     ret
  1370.  
  1371. ;CRC in DX must be initialized with 0FFFFh
  1372. ;and inverted after passing through all characters
  1373. ;==============================================================
  1374. ;here is the installation and de-installation code
  1375.  
  1376. deinstall_driver:
  1377.     mov al,packet_int_no    ;disconnect DRVR_ISR
  1378.     mov ah,25h
  1379.     push ds
  1380.     lds dx,dword ptr old_packet_int
  1381.     int 21h
  1382.     pop ds
  1383.  
  1384.     call restore_COM    ;restore COM port state
  1385.                 ;and interrupt vector(s)
  1386.     call Restore_Timer    ;restore Timer interrupt
  1387.  
  1388.     push cs            ;free memory
  1389.     pop es
  1390.     mov ah,49h
  1391.     int 21h
  1392.     ret
  1393.  
  1394. end_resident:    ;all code after this point will not stay resident
  1395.         ;after the installation is done.
  1396.  
  1397. Int_is_busy:        ;there is already a packet driver installed
  1398.             ;at specified software interrupt
  1399.     call Print_following_string
  1400.     db 'There is already a packet driver at interrupt 0x',0
  1401.     mov dl,packet_int_no
  1402.     call Print_DL_hex
  1403. DoNotInstall:
  1404.     call Print_following_string
  1405.     db 13,10,'AX25 driver has _not_ been installed',13,10,0
  1406.     mov ax,4C00h            ;terminate but don't stay resident
  1407.     int 21h
  1408.     ret
  1409.  
  1410. BadUsage:
  1411.     call Print_following_string
  1412.     db 13,10,0
  1413.     mov bx,offset Msg_usage
  1414.     call Print_BX_string
  1415.     jmp DoNotInstall
  1416.  
  1417. install_driver:
  1418.  
  1419.     call Print_following_string
  1420.     db 'AX25 packet driver for RS232 port by Pawel Jalocha',13,10
  1421.     db 'Version of 30th August 1992',13,10
  1422.     db 'Free licence is granted for radio _amateurs_ only',13,10,13,10,0
  1423.  
  1424.     call ReadOptions
  1425.     jnc DoPrintParam
  1426.     jmp BadUsage
  1427. DoPrintParam:
  1428.     call PrintParameters
  1429.     call CheckParameters
  1430.     jnc DoComputeSec
  1431.     jmp BadUsage
  1432. DoComputeSec:
  1433.     call ComputeSecondaryPar
  1434.  
  1435.     push es
  1436.  
  1437.     mov al,packet_int_no    ;save int vector
  1438.     mov ah,35h
  1439.     int 21h
  1440.     mov old_packet_int,bx
  1441.     mov ax,es
  1442.     mov old_packet_int+2,ax
  1443.  
  1444.     add bx,3        ;check if there is already a packet driver
  1445.     mov di,bx
  1446.     mov si,offset DRVR_ISR +3
  1447.     mov cx,9
  1448. cmp_char: mov al,[si]
  1449.       cmp al,es:[di]
  1450.       jne Int_is_free
  1451.     loop cmp_char
  1452.     jmp Int_is_busy
  1453. Int_is_free:
  1454.  
  1455.     mov al,packet_int_no    ;put a new one in place
  1456.     mov ah,25h
  1457.     mov dx,offset DRVR_ISR
  1458.     int 21h            ;ds must be equal to cs here
  1459.  
  1460.     mov es,phd_environ    ;release our environment
  1461.     mov ah,49h
  1462.     int 21h
  1463.  
  1464.     pop es
  1465.  
  1466. ;initialize COM port and interrupt vector(s)
  1467.     call initialize_COM
  1468. ;initialize Timer routine
  1469.     call Initialize_Timer
  1470.  
  1471.     call Print_following_string
  1472.     db 'AX25 driver is now installed and initialized',13,10,0
  1473.  
  1474. ;make the code resident
  1475.     mov ax,3100h
  1476.     mov dx,offset end_resident + 0Fh
  1477.     mov cl,4
  1478.     shr dx,cl
  1479.     int 21h
  1480.     ret
  1481.  
  1482. Msg_usage:
  1483.     db 'ax25 options: (default values in [])',13,10
  1484.     db '-?  prints this help message',13,10
  1485.     db '-i<int_no>(hex) software interrupt number [60]',13,10
  1486.     db '-I<irq>(hex)    COM IRQ number 2..7 [4]',13,10
  1487.     db '-B<base>(hex)   COM base address 0..3ff [3f8]',13,10
  1488.     db '-b<bit rate>(dec) [1200]',13,10
  1489.     db '-c<carrier mode> possible values:',13,10
  1490.     db '                   f = full duplex',13,10
  1491.     db '                   c = sense DCD modem line',13,10
  1492.     db '                   t = sense data transitions [default]',13,10
  1493.     db '                   d = deliver carrier signal from data analysis',13,10
  1494.     db '-s<slot time>(dec) slot time in data bits [120]',13,10
  1495.     db '-p<peristance>(dec) persistance/255 [64]',13,10
  1496.     db '-h<tx head>(dec)  Transmitter head in data bit units [480]',13,10
  1497.     db '-t<tx tail>(dec)  transmitter tail in data bit units [24]',13,10
  1498.     db 0
  1499.  
  1500. ;==============================================================
  1501. ;Here are some routine for printing numbers and strings
  1502.  
  1503. print_following_string: ;prints string following the call - modifies bx !
  1504.     pop bx        ;pop return address from the stack
  1505.     push ax        ;so we know where the char. string is
  1506.     push dx
  1507. print_next_char:
  1508.       mov dl,cs:[bx]    ;load next character
  1509.       inc bx
  1510.       and dl,dl             ;NULL char ?
  1511.       jz string_end         ;exit this loop if so
  1512.       mov ah,2              ;otherwise print it
  1513.       int 21h
  1514.     jmp print_next_char
  1515. string_end:
  1516.     pop dx
  1517.     pop ax
  1518.     push bx            ;push new return address on stack
  1519.     ret
  1520. print_BX_string:    ;prints string addressed by ds:bx
  1521.     push ax        ;the string must be terminated by NULL char
  1522.     push bx
  1523.     push dx
  1524. print_next_BX_char:        ;loop over characters
  1525.       mov dl,[bx]       ;read next character
  1526.       inc bx
  1527.       and dl,dl         ;NULL char ?
  1528.       jz BX_string_end      ;jump if so
  1529.       mov ah,2              ;otherwise print it
  1530.       int 21h
  1531.     jmp print_next_BX_char
  1532. BX_string_end:
  1533.     pop dx
  1534.     pop bx
  1535.     pop ax
  1536.     ret
  1537.  
  1538. print_DL_hex:        ;prints in hex byte stored in DL
  1539.     push ax
  1540.     push cx
  1541.     mov cl,4
  1542.     jmp print_low_byte
  1543. print_DX_hex:        ;prints in hex word stored in DX
  1544.     push ax
  1545.     push cx
  1546.     mov cl,4
  1547.     mov al,dh
  1548.     rol al,cl
  1549.     call print_hex_digit
  1550.     rol al,cl
  1551.     call print_hex_digit
  1552. print_low_byte:
  1553.     mov al,dl
  1554.     rol al,cl
  1555.     call print_hex_digit
  1556.     rol al,cl
  1557.     call print_hex_digit
  1558.     pop cx
  1559.     pop ax
  1560.     ret
  1561.  
  1562. print_hex_digit:    ;prints hex digit stored in AL
  1563.     push ax
  1564.     push dx
  1565.     and al,0Fh
  1566.     cmp al,10
  1567.     jc add_0
  1568.     add al,'a'-'0'-10
  1569. add_0:    add al,'0'
  1570.     mov dl,al
  1571.     mov ah,2
  1572.     int 21h
  1573.     pop dx
  1574.     pop ax
  1575.     ret
  1576.  
  1577. print_DX_dec:        ;print in dec word stored in DX
  1578.     push ax
  1579.     push bx
  1580.     push cx
  1581.     push dx
  1582.     mov bx,10
  1583.     xor cx,cx
  1584.     mov ax,dx
  1585. calc_next_dig:
  1586.     xor dx,dx
  1587.     div bx
  1588.     push dx
  1589.     inc cx
  1590.     and ax,ax
  1591.     jnz calc_next_dig
  1592. print_next_dig:
  1593.     pop ax
  1594.     call print_dec_digit
  1595.     loop print_next_dig
  1596.     pop dx
  1597.     pop cx
  1598.     pop bx
  1599.     pop ax
  1600.     ret
  1601. print_dec_digit:    ;prints in dec digit stored in AL
  1602.     push ax
  1603.     push dx
  1604.     add al,48
  1605.     mov dl,al
  1606.     mov ah,2
  1607.     int 21h
  1608.     pop dx
  1609.     pop ax
  1610.     ret
  1611.  
  1612. ;==============================================================
  1613. ;Routines to interprete input
  1614.  
  1615. ReadOptions:
  1616.     push ax
  1617.     push bx
  1618.     push cx
  1619.     push dx
  1620.     mov bx,81h    ;load offset to command line arguments
  1621. NextOption:
  1622.     call SkipBlanks
  1623.     cmp al,13    ;carriage return ?
  1624.     jz ReadOptions_ret
  1625.     cmp al,'-'
  1626.     jz InterpreteOption
  1627. PrintOptionUsage:
  1628. ;    mov bx,offset Msg_usage
  1629. ;    call Print_BX_string
  1630. ReadOptions_err:
  1631.     stc
  1632. ReadOptions_ret:
  1633.     pop dx
  1634.     pop cx
  1635.     pop bx
  1636.     pop ax
  1637.     ret        ;carry=1 => results are _not_ valid
  1638.  
  1639. InterpreteOption:
  1640.     inc bx
  1641.     mov al,[bx]
  1642.     inc bx
  1643.     cmp al,'?'
  1644.     jz PrintOptionUsage
  1645.     cmp al,'B'
  1646.     jz Opt_base
  1647.     cmp al,'I'
  1648.     jz Opt_irq
  1649.     cmp al,'i'
  1650.     jz Opt_interrupt
  1651.     cmp al,'b'
  1652.     jz Opt_baud
  1653.     cmp al,'c'
  1654.     jz Opt_carrier
  1655.     cmp al,'s'
  1656.     jz Opt_slot
  1657.     cmp al,'p'
  1658.     jz Opt_persistance
  1659.     cmp al,'h'
  1660.     jz Opt_head
  1661.     cmp al,'t'
  1662.     jz Opt_tail
  1663.     call UnknownOption
  1664.     jmp ReadOptions_err
  1665.  
  1666. Opt_base:
  1667.     call ReadHexNumber
  1668.     mov com_base,dx
  1669.     jmp NextOption
  1670. Opt_irq:
  1671.     call ReadHexNumber
  1672.     mov com_irq,dl
  1673.     jmp NextOption
  1674. Opt_interrupt:
  1675.     call ReadHexNumber
  1676.     mov packet_int_no,dl
  1677.     jmp NextOption
  1678. Opt_baud:
  1679.     call ReadDecNumber
  1680.     mov bit_rate,dx
  1681.     jmp NextOption
  1682. Opt_slot:
  1683.     call ReadDecNumber
  1684.     mov slot_time,dl
  1685.     jmp NextOption
  1686. Opt_persistance:
  1687.     call ReadDecNumber
  1688.     mov persistance,dl
  1689.     jmp NextOption
  1690. Opt_head:
  1691.     call ReadDecNumber
  1692.     mov tx_head,dx
  1693.     jmp NextOption
  1694. Opt_tail:
  1695.     call ReadDecNumber
  1696.     mov tx_tail,dx
  1697.     jmp NextOption
  1698. Opt_carrier:
  1699.     mov al,[bx]
  1700.     cmp al,'f'
  1701.     jz Carr_0
  1702.     cmp al,'c'
  1703.     jz Carr_1
  1704.     cmp al,'t'
  1705.     jz Carr_2
  1706.     cmp al,'d'
  1707.     jz Carr_3
  1708.     call UnknownCarrierOpt
  1709.     jmp ReadOptions_err
  1710. Opt_carr_end:
  1711.     inc bx
  1712.     mov Carrier_sense,al
  1713.     jmp NextOption
  1714.  
  1715. Carr_0: mov al,0
  1716.     jmp Opt_carr_end
  1717. Carr_1: mov al,1
  1718.     jmp Opt_carr_end
  1719. Carr_2: mov al,2
  1720.     jmp Opt_carr_end
  1721. Carr_3: mov al,3
  1722.     jmp Opt_carr_end
  1723.  
  1724. UnknownCarrierOpt:
  1725.     push bx
  1726.     call Print_following_string
  1727.     db 'Unknown option: -c',0
  1728.     mov dl,al
  1729.     mov ah,2
  1730.     int 21h
  1731.     call Print_following_string
  1732.     db 13,10,0
  1733.     pop bx
  1734.     ret
  1735.  
  1736. UnknownOption:
  1737.     push bx
  1738.     call Print_following_string
  1739.     db 'Unknown option: -',0
  1740.     mov dl,al
  1741.     mov ah,2
  1742.     int 21h
  1743.     call Print_following_string
  1744.     db 13,10,0
  1745.     pop bx
  1746.     ret
  1747.  
  1748. SkipBlanks:        ;mov to first non-SPACE or TAB char.
  1749. SkipThisChar:
  1750.     mov al,[bx]
  1751.     inc bx
  1752.     cmp al,' '    ;space ?
  1753.     jz SkipThisChar
  1754.     cmp al,9        ;TAB ?
  1755.     jz SkipThisChar
  1756.     dec bx
  1757.     ret        ;ds:bx=address of non-blank character
  1758.             ;al=this character
  1759.  
  1760. Param_OK db 0
  1761.  
  1762. CheckParameters:
  1763.     push bx
  1764.     push ax
  1765.     mov al,1
  1766.     mov Param_OK,al
  1767.  
  1768. ;    mov al,packet_int_no
  1769. ;    cmp al,60h
  1770. ;    jnc int_no_OK
  1771. ;      call Print_following_string
  1772. ;      db 'Packet interrupt number is below 0x60',13,10,0
  1773. ;      xor al,al
  1774. ;      mov Param_OK,al
  1775. ;int_no_OK:
  1776.  
  1777.     mov ax,bit_rate
  1778.     cmp ax,300
  1779.     jnc baud_upp
  1780.       call Print_following_string
  1781.       db 'Bauds below 300 bps not supported',13,10,0
  1782.       xor al,al
  1783.       mov Param_OK,al
  1784. baud_upp:
  1785.     cmp ax,14400+1
  1786.     jc baud_OK
  1787.       call Print_following_string
  1788.       db 'Bauds above 14400 bps not supported',13,10,0
  1789.       xor al,al
  1790.       mov Param_OK,al
  1791. baud_OK:
  1792.     mov ax,tx_head
  1793.     cmp ax,8
  1794.     jnc tx_head_OK
  1795.       call Print_following_string
  1796.       db 'Tx head should be at least 8 bits',13,10,0
  1797.       xor al,al
  1798.       mov Param_OK,al
  1799. tx_head_OK:
  1800.     mov ax,tx_tail
  1801.     cmp ax,8
  1802.     jnc tx_tail_OK
  1803.       call Print_following_string
  1804.       db 'Tx tail should be at least 8 bits',13,10,0
  1805.       xor al,al
  1806.       mov Param_OK,al
  1807. tx_tail_OK:
  1808.     mov al,slot_time
  1809.     cmp al,8
  1810.     jnc slot_time_OK
  1811.       call Print_following_string
  1812.       db 'Slot time should be at least 8 bits',13,10,0
  1813.       xor al,al
  1814.       mov Param_OK,al
  1815. slot_time_OK:
  1816.     mov ax,com_base
  1817.     cmp ax,400h
  1818.     jc com_base_OK
  1819.       call Print_following_string
  1820.       db 'COM base address should be in the range 0..3ff',13,10,0
  1821.       xor al,al
  1822.       mov Param_OK,al
  1823. com_base_OK:
  1824.     mov al,com_irq
  1825.     cmp al,2
  1826.     jnc com_irq_upp
  1827. wrong_irq:
  1828.       call Print_following_string
  1829.       db 'COM irq should be between 2 and 7',13,10,0
  1830.       xor al,al
  1831.       mov Param_OK,al
  1832.       jmp com_irq_OK
  1833. com_irq_upp:
  1834.       cmp al,8
  1835.       jnc wrong_irq
  1836. com_irq_OK:
  1837.  
  1838.     mov al,Param_OK
  1839.     shr al,1
  1840.     cmc
  1841.     jnc CheckParam_ret
  1842.     call Print_following_string
  1843.     db 'ax25 driver can not accept these parameters',13,10,0
  1844.     stc
  1845. CheckParam_ret:
  1846.     pop bx
  1847.     pop ax
  1848.     ret
  1849.  
  1850. ComputeSecondaryPar:
  1851.     push ax
  1852.     push bx
  1853.     push cx
  1854.     push dx
  1855.  
  1856.     mov al,1
  1857.     mov cl,com_irq
  1858.     shl al,cl
  1859.     mov irq_mask,al
  1860.  
  1861.     mov ax,14400        ;compute baud rate for the transmitter
  1862.     xor dx,dx
  1863.     mov cx,bit_rate
  1864.     mov bx,cx
  1865.     shr bx,1
  1866.     add ax,bx
  1867.     adc dx,0
  1868.     div cx
  1869.     mov bd_bit_len,al
  1870.  
  1871.     mov ax,14400        ;correct bit rate to a round value
  1872.     xor dx,dx
  1873.     mov cl,bd_bit_len
  1874.     xor ch,ch
  1875.     div cx
  1876.     xchg ax,bit_rate
  1877.     cmp ax,bit_rate
  1878.     jz Compute_cl_bit_len
  1879.       call Print_following_string
  1880.       db 'Bit rate adjusted to ',0
  1881.       mov dx,bit_rate
  1882.       call Print_DX_dec
  1883.       call Print_following_string
  1884.       db ' bps',13,10,0
  1885.  
  1886. Compute_cl_bit_len:
  1887.     mov ax,13532
  1888.     mov dx,18
  1889.     mov cx,bit_rate
  1890.     mov bx,cx
  1891.     shr bx,1
  1892.     add ax,bx
  1893.     adc dx,0
  1894.     div cx
  1895.     mov cl_bit_len,ax
  1896.     shr ax,1
  1897.     adc ax,0
  1898.     mov cl_bit_len_2,ax
  1899.  
  1900.     mov ah,bd_bit_len
  1901.     mov al,slot_time
  1902.     mul ah
  1903.     mov bd_slot_time,ax
  1904.  
  1905.     pop dx
  1906.     pop cx
  1907.     pop bx
  1908.     pop ax
  1909.     ret
  1910.  
  1911. PrintParameters:
  1912.     push ax
  1913.     push bx
  1914.     push cx
  1915.     push dx
  1916.     call print_following_string
  1917.     db 'Actual ax25 driver parameters:',13,10,0
  1918.     call Print_following_string
  1919.     db 'Service interrupt 0x',0
  1920.     mov dl,packet_int_no
  1921.     call Print_DL_hex
  1922.     call Print_following_string
  1923.     db 13,10,'COM I/O base 0x',0
  1924.     mov dx,com_base
  1925.     call Print_DX_hex
  1926.     call Print_following_string
  1927.     db '  IRQ 0x',0
  1928.     mov dl,com_irq
  1929.     call Print_DL_hex
  1930.     call Print_following_string
  1931.     db 13,10,'Bit rate ',0
  1932.     mov dx,bit_rate
  1933.     mov cx,dx            ;keep data rate in cx
  1934.     call Print_DX_dec
  1935.     call Print_following_string
  1936.     db ' bps',13,10,'Tx head ',0
  1937.     mov dx,tx_head
  1938.     call Print_DX_dec
  1939.     call Print_following_string
  1940.     db ' bits (',0
  1941.     mov ax,1000    ;compute tx_head in ms units
  1942.     mul dx
  1943.     div cx        ;ax=tx_head*1000/bit_rate
  1944.     mov dx,ax
  1945.     call print_DX_dec
  1946.     call Print_following_string
  1947.     db 'ms)   Tx tail ',0
  1948.     mov dx,tx_tail
  1949.     call Print_DX_dec
  1950.     call Print_following_string
  1951.     db ' bits (',0
  1952.     mov ax,1000    ;compute tx_tail in ms units
  1953.     mul dx
  1954.     div cx        ;ax=tx_tail*1000/bit_rate
  1955.     mov dx,ax
  1956.     call Print_DX_dec
  1957.     call Print_following_string
  1958.     db 'ms)',13,10,'Slot time ',0
  1959.     mov dl,slot_time
  1960.     xor dh,dh
  1961.     call Print_DX_dec
  1962.     call Print_following_string
  1963.     db ' bits (',0
  1964.     mov ax,1000    ;compute slot_time in ms units
  1965.     mul dx
  1966.     div cx        ;ax=slot_time*1000/bit_rate
  1967.     mov dx,ax
  1968.     call Print_DX_dec
  1969.     call Print_following_string
  1970.     db 'ms)   p-persistance ',0
  1971.     mov dl,persistance
  1972.     xor dh,dh
  1973.     call Print_DX_dec
  1974.     call Print_following_string
  1975.     db '/255',13,10,'Carrier mode: ',0
  1976.     mov bl,carrier_sense
  1977.     xor bh,bh
  1978.     add bx,bx
  1979.     mov bx,[msg_carrier_modes+bx]
  1980.     call Print_BX_string
  1981.     call Print_following_string
  1982.     db 13,10,0
  1983.     pop dx
  1984.     pop cx
  1985.     pop bx
  1986.     pop ax
  1987.     ret
  1988.  
  1989.     even
  1990. msg_carrier_modes   label word
  1991.     dw offset Msg_full_duplex
  1992.     dw offset Msg_sense_DCD
  1993.     dw offset Msg_sense_trans
  1994.     dw offset Msg_sense_DPLL
  1995.  
  1996. Msg_full_duplex    db 'do not sense carrier => full duplex',13,10,0
  1997. Msg_sense_DCD    db 'sense DCD modem line',13,10,0
  1998. Msg_sense_trans    db 'sense data transitions',13,10,0
  1999. Msg_sense_DPLL    db 'deliver carrier from data analysis',13,10,0
  2000.  
  2001. ReadDecDigit:        ;ds:bx points to a character
  2002.     mov al,[bx]
  2003.     cmp al,'0'
  2004.     jc ReadDecDigit_ret    ;jump if below '0'
  2005.       cmp al,'9'+1
  2006.       cmc
  2007.       jc ReadDecDigit_Ret   ;jump if above '9'
  2008.         sub al,'0'
  2009. ReadDecDigit_ret:
  2010.     ret        ;carry=1 if not a dec digit => then al=the character
  2011.             ;otherwise al=digit value
  2012.  
  2013. ReadDecNumber:        ;ds:bx points to the first character
  2014.     push cx
  2015.     mov dx,0
  2016. ReadNextDecDigit:
  2017.     call ReadDecDigit
  2018.     jc ReadDecNumber_ret
  2019.       inc bx
  2020.       xor ah,ah
  2021.       add dx,dx    ;multiply dx by 2
  2022.       mov cx,dx     ;multiply dx by 5
  2023.       add dx,dx
  2024.       add dx,dx
  2025.       add dx,cx
  2026.       add dx,ax     ;add the digit just read
  2027.     jmp ReadNextDecDigit
  2028. ReadDecNumber_ret:
  2029.     pop cx
  2030.     ret        ;dx=the number,
  2031.             ;ds:bx *char where the interpretation stopped
  2032.             ;al=this character, ah possibly modified
  2033.  
  2034. ;the routine below accepts only _lowercase_ letters as hex numbers
  2035. ReadHexDigit:        ;ds:bx = digit pointer
  2036.     mov al,[bx]
  2037.     cmp al,'0'
  2038.     jc ReadHexDigit_ret    ;jump if below '0'
  2039.       cmp al,'9'+1
  2040.       cmc
  2041.       jc ReadHexDigit_lett   ;jump if above '9'
  2042.         sub al,'0'
  2043.         jmp ReadHexDigit_ret
  2044. ReadHexDigit_lett:
  2045.     cmp al,'a'
  2046.     jc ReadHexDigit_ret      ;jump if below 'a'
  2047.       cmp al,'f'+1
  2048.       cmc
  2049.       jc ReadHexDigit_ret     ;jump if above 'f'
  2050.         sub al,'a'-10
  2051. ReadHexDigit_ret:
  2052.     ret        ;carry=1 => not a hex digit, al=char.
  2053.             ;carry=0 => hex digit indeed, al=value
  2054.  
  2055. ReadHexNumber:
  2056.     push cx
  2057.     mov cl,4
  2058.     mov dx,0
  2059. ReadNextHexDigit:
  2060.     call ReadHexDigit
  2061.     jc ReadHexNumber_ret
  2062.       inc bx
  2063.       shl dx,cl    ;multiply dx by 16
  2064.       or dl,al    ;add the digit just read
  2065.     jmp ReadNextHexDigit
  2066. ReadHexNumber_ret:
  2067.     pop cx
  2068.     ret        ;dx=the number,
  2069.             ;ds:bx *char where the interpretation stopped
  2070.             ;al=this character
  2071.  
  2072. ;==============================================================
  2073.  
  2074. code    ends
  2075.  
  2076.     end start
  2077.