Thursday, December 17, 2009

SLIP driver

;                       SLIP COMMUNICATION HANDLER
;                           Intel 386-based
;                      Receiver and Transmitter ISR
;                           * Thesis Project *
;                        M. Lutfi Shahab 13389011
;                   Department of Engineering Physics
;                       Institut Teknologi Bandung
;                           (c) Bandung, 1995
;                        First made:  March 1995
;                        Last upated: 4-Jan 1996
; 26/7/95       I found a bug on ISR. ISR should push flags to stack, but
;               in older version, this didn't!
;               I observed this bug affected keyboard flag checking
; 11 Nov 1995   In next version I plan to use extended USART controller,
;                                        like Intel 82510 and 16550
;               Detecting these chips done with writting appropriate setting
;                                        code to FCR (FIFO control register) and detects their presences
;                                        in extended fields of IIR (Interrupt Control Register).
;  8 Jan 1995   16 bit Cyclic Redundancy Check added.
PAGE 60, 120

MODEL large, C


DEBUG      EQU     0       ; debug


EXTRN       base           :WORD
EXTRN       queue          :tQUEUE
EXTRN       baudrate       :DWORD
EXTRN       int_no         :BYTE
EXTRN       thr            :WORD
EXTRN       rdr            :WORD         ; base
EXTRN       ier            :WORD         ; = base+1
EXTRN       iir            :WORD         ; = base+2
EXTRN       lcr            :WORD         ; = base+3
EXTRN       mcr            :WORD         ; = base+4
EXTRN       lsr            :WORD         ;
EXTRN       msr            :WORD         ; = base+6
EXTRN       scp            :WORD         ; scratch pad = base + 7
EXTRN       installed      :WORD         ;  initialization flag
EXTRN       imr            :WORD
EXTRN       rcv            :tCOMBUF

packet_sem       DW     0
xmit_time        DW     200    ;timeout
rcv_time         DW     200
timeout          DW     200
recv_pkt_ready   DW     0

        isr_msg                  DB      'Ini di dalam recv_isr', 10, 13, '$'
        isr_in_sema              db      'Semaphore > 0', 10,13,'$'


; This variable must be put in CODE SEGMENT
in_recv_isr      DW    0
; al = charactrer to print
      PUSH      ax bx cx dx ds es si di bp ; Turbo Assembler's macro
      mov       bh, 0
      mov       bl, 07h
      mov       ah, 0eh
      int       10H
      POP       bp di si es ds dx cx bx ax
print ENDP

; dx => offset string
print_msg PROC
      PUSH      ax bx cx dx ds es si di bp
      ASSUME    ds:@data
      mov       ax, @data
      mov       ds, ax
      mov       ah, 9h
      int       10H
      POP       bp di si es ds dx cx bx ax
print_msg ENDP

xmit  PROC    NEAR
xmit  ENDP

send_char   PROC NEAR
      PUSH      ds dx cx
      xchg      ah,al           ; put data char into ah
      push      ax
      ASSUME    ds:@data
      mov       ax,@data        ; resetting ds to DGROUP
      mov       ds, ax
      pop       ax
      xor       cx,cx           ; 64K retry counter

      mov       dx, [lsr]
      in        al, dx
      jmp       $+2             ; use time, prevent overdriving UART
      jmp       $+2
      test      al, LSR_THRE    ; Transmitter (THRE) ready?
      jnz       sendch2         ; nz = yes
      loop      sendch1
      stc                       ; carry set for failure
      jmp       short sendch3   ; timeout

      xchg      al, ah          ; now send it
      mov       dx, [thr]
      out       dx, al          ; send the byte
      jmp       $+2             ; use a little time
      clc                       ; status of success

      POP       cx dx ds
send_char       ENDP

; ------------------------------------------
; int _Send_SLIP_Packet(tSLIP_Pkt *pkt)
; ------------------------------------------
PUBLIC C _Send_SLIP_Packet
_Send_SLIP_Packet PROC C pkt:DWORD

;--------- Turbo Assembler automatically set stack frame
        PUSH      ds si
        sti                          ; enable interrupts
        lds       si, pkt

;-------- MODIFIED because CRC16 addition ($LTF$, 8/JAN/95)
        ;mov       cl, ds:[si].datalen
        ;xor       ch, ch
        ;cmp       cx, DATASIZE
        ;jle       LESS_OR_EQU
        ;mov       cx, DATASIZE        ; align to DATASIZE
        mov       cx, PKTSIZE

  ;---add       cx, 4               ; total packet length =
                                                                                                                                ; sizeof(pktlen)+sizeof(datatype)+sizeof(crc)
                                                                                                                                ; = 1 + 1 + 2 = 4 bytes
        mov       al, FR_END          ; Flush out any line garbage
      call      send_char
      jc        send_pkt_end        ; c = failure to send

        ; Copy input to output, escaping special characters
      cmp       al, FR_ESC          ;escape FR_ESC with FR_ESC and T_FR_ESC
      jne       send_pkt_2
      mov       al, FR_ESC
      call      send_char
      jc        send_pkt_end
      mov       al, T_FR_ESC
      jmp       short send_pkt_3

      cmp       al, FR_END          ;escape FR_END with FR_ESC and T_FR_END
      jne       send_pkt_3
      mov       al, FR_ESC
      call      send_char
      jc        send_pkt_end
      mov       al, T_FR_END

      call      send_char
      jc        send_pkt_end
      loop      send_pkt_1              ; do cx user characters
      mov       al, FR_END              ; terminate it with a FR_END
      call      send_char
      jc        send_pkt_end
      mov       ax, 1                   ; success
      jmp       short keluar

      xor       ax, ax                  ; error occured
      POP       si  ds
_Send_SLIP_Packet       ENDP

;  static void near asyrxint(void)
;  return:
;    -  packet_sem
;    -  recv_pkt_ready
asyrxint        PROC NEAR
      push      ds               ; save ds register
      ASSUME    ds:@data
      mov       ax, @data        ; ds points to DGROUP !
      mov       ds, ax
                mov       ax, WORD PTR [rcv.buf_end+2] ; just check !!
      xor       bx, bx
      cmp       WORD PTR [baudrate+2], 0
      jb        short asyrxint_a
      jbe       check_again
      jmp       short asyrxint_a

      cmp       WORD PTR [baudrate], 9600      ;below 9600 we're strictly
      jb        asyrxint_a                     ;interrupt driven
      mov       bx, rcv_time

                les       di, DWORD PTR [rcv.head]
      xor       bp, bp                  ; set flag to indicate 1st char processed
      mov       si, packet_sem          ; optimization
      mov       ah, LSR_DR

      xor       cx, cx                  ; initialize counter
      mov       dx, [lsr]

      in        al, dx                 ; check for data ready
      test      al, LSR_DR
      jnz       asyrxint_gotit         ; yes - break out of loop
      inc       cx                     ; no - increase loop counter
      cmp       cx, bx                 ; timeout?
      jae       asyrxint_exit          ; yes - leave
      jmp       asyrxint_in            ; no - keep looping

      mov       dx, [rdr]
      in        al, dx

; Process incoming data;
; If buffer is full, we have no choice but to drop the character
; segment of rcv.head and rcv.tail is same !!
; so just compare their offsets

      cmp      di, WORD PTR [rcv.tail]; is it buffer collision ?
      jne      asyrxint_ok             ; none - continue
      or       si, si                  ; maybe - if there are packets
      jnz      asyrxint_exit           ; yes exit

      cmp      di, WORD PTR [rcv.buf_end]  ; did we hit the end of the buffer?
      jne      asyrxint_3                  ; no.
      mov      di, WORD PTR [rcv.buf]      ; yes - wrap around.

      cmp      al,FR_END            ; might this be the end of a frame?
      jne      asyrxint_reset       ; no - reset flag and loop
      inc      si                   ; yes - indicate packet ready
      cmp      si, 1                ; determine if semaphore is <> 1
      jne      asyrxint_chk_flg     ; yes - recv_frame must be active
      inc      recv_pkt_ready       ; no - set flag to start recv_frame
      mov     al, 'O'
      call    print

      cmp      bp, 0                ; was this the first char?
      jne      asyrxint_1           ; no - exit handler

      inc      bp                   ; set 1st character flag
      jmp      asyrxint_again       ; get another character

      mov      WORD PTR [rcv.head], di     ; set offset pointed by rcv.head
      mov      [packet_sem], si

      pop      ds
asyrxint        ENDP

; registers used:
;    ax, ds
recv    PROC NEAR
      push     ds
      mov      ax, @data
      mov      ds, ax
      mov      dx, [iir]
      pop      ds
      in       al, dx          ;any interrupts at all?
      test     al, IIR_IP
      jne      recv_1          ;no.
      and      al,IIR_ID
      cmp      al,IIR_RDA      ;Receiver interrupt
      jne      recv_3
      call     asyrxint
      jmp      recv_2

        ; process IIR_MSTAT here.
        ; If CTS and packet ready then
        ;               enable the      transmit buffer empty interrupt
        ; else
        ;         disable the transmit buffer empty interrupt
      cmp      al, IIR_MSTAT
      jne      recv_1
      push     ds
      mov      ax, @data
      mov      ds, ax
      mov      dx, [msr]
      pop      ds
      in       al, dx
      test     al, MSR_CTS         ; is CTS bit set
      jz       recv_5_1            ; no - disable xmit buffer empty int
      jmp      recv_2

      jmp      recv_2
        ;process IIR_RLS here
recv  ENDP

; recv_char
;    si = receive buffer = FP_OFF(rcv.tail)
;    bx = receive count
;    ds = data segment
; return:
;    al = next char
;    bx = pointer rcv.head
; zero flag:
;    nz (0) : character received
;    zr (1) : no more chars in this frame
recv_char       PROC NEAR
      push     ds                      ; we assume that data segment has been set
      mov      ax, WORD PTR [rcv.tail+2]
      mov      ds, ax
      pop      ds
      cmp      si, WORD PTR [rcv.buf_end]
      jne      recv_char_1
      mov      si, WORD PTR [rcv.buf]

      mov      bx, WORD PTR [rcv.head]
      cmp      si, bx                      ; if ((rcv.tail==rcv.head)|| //buffer collision
      je       recv_char_2                 ;       (_AL==FR_END))
      cmp      al, FR_END                  ; return (1);
                                           ; else return (0);
recv_char   ENDP

; recv_copy
; on entry:
;    ds:si = packet_buffer
;    cx = packet length
; do copy packet_to queue.write
recv_copy PROC NEAR
      les      di, DWORD PTR [queue.write]
      mov      ax, es
      or       ax, di
      jz       fail
      rep      movsb
      mov      WORD PTR [queue.write], di  ; points to next entry list
recv_copy ENDP

;   almost all registers used, so
;   we must save all of those.
;   use register variables as possible
;   because accesses to memory variable
;   consuming much more time

PUBLIC C dummy_isr
dummy_isr PROC FAR
      PUSH     ax bx cx dx ds es si di bp
      ASSUME   ds:@data
      mov      ax, @data
      mov      ds, ax
      mov      al, [int_no]               ;get hardware int #
      add      al, TMREOI                 ; make specific EOI dismissal
      out      BASE8259, al
      POP      bp di si es ds dx cx bx ax

PUBLIC  C recv_isr
recv_isr   PROC FAR
      ;jmp      short start
      ;in_recv_isr     DW    0

      cmp      cs:[in_recv_isr], 0
      je       no_re_enter

      PUSH     ax bx cx dx ds es si di bp
      mov      cs:in_recv_isr, 1
      ASSUME   ds:@data
      mov      ax, @data
      mov      ds, ax
      mov      al, int_no               ; Disable further specific device interrupt
      call     maskint

 I realize this re-entrancy trap is not perfect, you could be re-entered
 anytime before the above instruction. However since the stacks have not
 been swapped re-entrancy here will only appear to be a spurious interrupt.

;----------------------- CALL TRANSMITTER -----------------------
      call     xmit

; The following comment is wrong in that we now do a specific EOI command,
; and because we don't enable interrupts (even though we should).
; Chips & Technologies 8259 clone chip seems to be very broken.  If you
; send it a Non Specific EOI command, it clears all In Service Register
; bits instead of just the one with the highest priority (as the Intel
; chip does and clones should do).  This bug causes our interrupt
; routine to be reentered if:
;       1. we reenable processor interrupts;
;       2. we reenable device interrupts;
;       3. a timer or other higher priority device interrupt now comes in;
;       4. the new interrupting device uses a Non Specific EOI;
;       5. our device interrupts again.
; Because of this bug, we now completely mask our interrupts around
; the call    to "recv", the real device interrupt handler.  This allows us
; to send an EOI instruction to the 8259 early, before we actually
; reenable device interrupts.  Since the interrupt is masked, we
; are still guaranteed not to get another interrupt from our device
; until the interrupt handler returns.  This has another benefit:
; we now no longer prevent other devices from interrupting while our
; interrupt handler is running.  This is especially useful if we have
; other (multiple) packet drivers trying to do low-latency transmits.
; The following is from Bill Rust, <>
; this code dismisses the interrupt at the 8259. if the interrupt number
; is > 8 then it requires fondling two PICs instead of just one.

      mov      al, [int_no]              ;get hardware int #
      cmp      al, BASEVEC               ; see if its on secondary PIC
      jg       recv_isr_4
      add      al, TMREOI                ; make specific EOI dismissal
      out      BASE8259, al
      jmp      short recv_isr_3          ; all done

      add      al, 060H - BASEVEC        ; make specific EOI (# between 9 & 15).
      out      0a0H, al                  ; Secondary 8259 (PC/AT only)
      mov      al, 062H                  ; Acknowledge on primary 8259.
      out      BASE8259, al


;-------- save packet data to buffer
      call     recv

      cmp      [recv_pkt_ready], 1       ; is a packet ready?
      je       ready                     ; yes, start read
      jmp      recv_exit                 ; no - skip to end

      mov      [recv_pkt_ready],  0      ; reset flag

;===================== BEGIN DEPACKETIZE DATA HERE ======================
      cmp      [packet_sem], 0         ; should we do this?
      jne      sema                    ; yes - process it
      jmp      next_sema               ; no (packet_sem==0) => exit

      mov      bx, WORD PTR [queue.last]
      cmp      WORD PTR [queue.write], bx
      jne      not_full
 ; if queue.write reach queue.last, we have no choice thus just return
      mov      [queue.status], QFULL

      mov      [recv_pkt_ready], 1
      jmp      recv_exit

      mov      si, WORD PTR [rcv.tail]          ; get tail pointer
      xor      cx, cx                           ; count up the size here.

 ;---------- debug only, not needed int final linking
         IF DEBUG EQ 1
                  mov        bx, ds
                  mov        dx, WORD PTR [rcv.tail+2]
                  mov        ds, dx
                  mov        ax, ds:[si]
                  mov        ds, bx
                  mov        al, 'A'
                  call       print
  ;---- find how long message in the packet is
      call     recv_char                  ; get a char.
      je       recv_frame_2               ; go if no more chars (al==FR_END)
      cmp      al,FR_ESC                  ; an escape?
      je       recv_frame_1               ; yes - don't count this char.
      inc      cx                         ; no - count this one.
      jmp      recv_frame_1
 ;--- now cx contains message length

      jcxz     recv_frame_3                   ;no data in packet? yes - free the frame
      les      di, DWORD PTR [queue.write]
      mov      ax, es
      or       ax, di
      jnz      trail_recv
      xor      cx, cx                 ; if (queue.write==NULL)
      jmp      recv_frame_3           ; queue.write[0] = 0 //force packet length to zero
                                      ; cx reset to avoid putting a non-packet message
                                      ; into queue
      mov      bp, di                 ;remember first entry of queue line
      cmp      di, WORD PTR [queue.lastline]
      jg       recv_frame_full

; es:di -> packet buffer to be filled in
; cx = packet length
      cmp      cx, Q_WIDTH            ;if (cx > Q_WIDTH)
      jle      recv_frame_ok          ;
      mov      cx, Q_WIDTH            ;align packet to proper width, 1 byte for size
                                      ;so will truncate char beyond the bound
      mov      si, WORD PTR [rcv.tail]         ;resetting pointer to rcv.tail.
      ;----mov      ax, cx
      ;----stosb                               ;queue.write[0] = cx
      mov      dx, 0                           ;count

      cmp      di, WORD PTR [queue.last]     ; is it reach the end of queue ?
      jne      recv_frame_ready

      mov      [queue.status], QTOOBIG

      ;---  We don't need to store frame width anymore to simplify queue.
        ;---- The packet now contains data length to inform other task
      ;---- that frame has.
      ;----- (LUTFI, 16 Aug-95)

      ;-----mov      BYTE PTR es:[bp], Q_WIDTH     ;truncate packet
      IF DEBUG EQ 1
                mov     al, 'M'
                call print
      ;jmp     next_sema
      jmp      next_pkt

      call     recv_char                 ; if ((recv.tail==recv.buf_end)||(al==FR_END))
      je       recv_frame_6              ;     we're all done.
      cmp      al,FR_ESC                 ;an escape?
      jne      recv_frame_5              ;no - just store it.
      call     recv_char                 ;get the next character.
      je       recv_frame_6
      cmp      al,T_FR_ESC
      mov      al,FR_ESC                 ;assume T_FR_ESC
      je       recv_frame_5              ;yup, that's it - store FR_ESC
      mov      al,FR_END                 ;nope, store FR_END

      cmp      dx, cx
      jge      recv_frame_6              ; beyond queue width, so break loop
      stosb                              ;store the byte.
      inc      dx                        ;bytes stored
      jmp      recv_frame_4

      mov      WORD PTR [rcv.tail], si   ;we're skipped to the end
      mov      [queue.status], QOK       ;sign queue status to OK state
      jmp      recv_frame_end

      mov      WORD PTR [rcv.tail], si        ;set to new location

      jcxz     next_sema                     ; if no message, just skipped out

      add      bp, Q_WIDTH
      mov      WORD PTR queue.write, bp     ;next line of queue.

      dec      [packet_sem]
      cmp      [packet_sem], 0                 ; are there more packets ready?
      ja       any_packet                      ; if (semaphore > 0)
      jmp      short recv_exit

      jmp      recv_frame                      ; yes - execute again


;         DDP - This is a BIG mistake.  This routine SHOULD NOT enable interrupts.
;         doing so can cause interrupt recursion and blow your stack.
;         Processor interrupts SHOULD NOT be enabled after enabling device
;         interrupts until after the "IRET".  You will lose atleast 12 bytes
;         on the stack for each recursion.

      mov      al,[int_no]                 ; Now reenable device interrupts
      call     unmaskint
      POP      bp di si es ds dx cx bx ax
      mov      cs:in_recv_isr, 0           ; clear the re-entrancy flag
recv_isr        ENDP

; parameter:
;        al : IRQ number
; register used:
;        ax, cx, dx
maskint PROC NEAR
      or       al, al              ;are they using a hardware interrupt?
      je       maskint_1           ;no, don't mask off the timer!
      mov      dx, MASKPORT        ;assume the master 8259.
      cmp      al, BASEVEC         ;using the slave 8259 on an AT?
      jb       mask_not_irq2
      mov      dx, MASKSLAVEPORT   ;go disable it on slave 8259
      sub      al, BASEVEC

      mov      cl,al
      in       al,dx               ;disable them on the correct 8259.
      mov      ah,1                ;set the bit.
      shl      ah,cl
      or       al,ah

        ;500ns Stall required here, per INTEL documentation for eisa machines
      push     ax
        ;in       al, KBEOI     ; 1.5 - 3 uS should be plenty
        ;in       al, KBEOI
        in       al, KBEOI
        out      KBEOI,al
        pop      ax

        out      dx,al                ; mask IRQ

maskint  ENDP

; parameter:
;        al : IRQ number
; register used:
;        ax, cx, dx
unmaskint       PROC NEAR

      mov      dx, MASKPORT         ;assume the master 8259.
      mov      cl, al
        cmp      cl, BASEVEC          ;using the slave 8259 on an AT?
      jb       unmask_not_irq2      ;no
      in       al, dx               ;get master mask
      and      al, NOT (1 shl 2)    ; and clear slave cascade bit in mask
      out      dx, al               ;set new master mask (enable slave int)

        ;500ns Stall required here, per INTEL documentation for eisa machines
      push     ax
      in       al, KBEOI            ; 1.5 - 3 uS should be plenty
        ;********in       al, KBEOI
        ;********in       al, KBEOI
        out      KBEOI,al             ; refresh keyboard stroke (LUTFI, 7/10/95)
        pop      ax
        mov      dx, MASKSLAVEPORT    ;go enable interrupt on slave 8259
        sub      cl, BASEVEC

        in       al,dx                ;enable interrupts on the correct 8259.
        mov      ah,1                 ;clear the bit.
        shl      ah,cl
        not      ah
        and      al,ah

 ;    500ns Stall required here, per INTEL documentation for eisa machines
 ; --- not needed, caused a problem with kbhit(). (lutfi, april 1995)

        push          ax
        in          al, KBEOI            ; 1.5 - 3 uS should be plenty
        mov         ah, al               ; save keyboard stroke (LUTFI, 11/12/95)
        out         KBEOI, al            ; refresh keyboard stroke (LUTFI, 7/10/95)
        pop         ax

        out      dx,al                        ; un-mask IRQ

unmaskint      ENDP



;============================ END OF PROGRAM ===============================

No comments:

Post a Comment