; slip.asm
;
; This is an 8086+ implementation of SLIP (RFC 1055)
;
; It may be assembled using Microsoft's MASM using the command line:
;   ml -Fl -c slip.asm
;
; or using Borland's TASM using the command line:
;   tasm -la -m2 -jLOCALS slip.asm
;
        .model small
        .stack 100h
        .data
SLIP_END        equ 0C0h
SLIP_ESC        equ 0DBh
SLIP_ESC_END    equ 0DCh
SLIP_ESC_ESC    equ 0DDh

; note that these are both sample macros and are very simple
; In both cases, DX is assumed to already be pointing to the
; appropriate I/O port and a character is always assumed to
; be ready.
SEND_CHAR macro char
        IFDIFI <char>, <al>
                mov al,char
        ENDIF
        out dx,al
endm

RECV_CHAR macro
        in al,dx
endm
        .code
;****************************************************************************
; send_packet
;
; sends the passed packet (which is in a memory buffer) to the output
; device by using the macro SEND_CHAR() which must be defined by the
; user.  A sample SEND_CHAR() is defined above.
;
; Entry:
;       DS:SI ==> raw packet to be sent
;       CX = length of raw packet to be sent
;       direction flag is assumed to be cleared (incrementing)
;
; Exit:
;
;
; Trashed:
;       none
;
;****************************************************************************
send_packet proc
        push cx
        push si
        SEND_CHAR SLIP_END      ; send an end char to flush any garbage
        jcxz @@bailout          ; if zero length packet, bail out now
@@nextchar:
        lodsb                   ; load next char
        cmp al,SLIP_END         ; Q: is it the special END char?
        jne @@check_esc         ;  N: check for ESC
        SEND_CHAR SLIP_ESC      ;  Y: send ESC + ESC_END instead
        mov al,SLIP_ESC_END     ;
        jmp @@ordinary          ;

@@check_esc:
        cmp al,SLIP_ESC         ; Q: is it the special ESC char?
        jne @@ordinary          ;  N: send ordinary char
        SEND_CHAR SLIP_ESC      ;  Y: send ESC + ESC_END instead
        mov al,SLIP_ESC_ESC     ;
        ; fall through to ordinary character

@@ordinary:
        SEND_CHAR al            ;

        loop @@nextchar         ; keep going until we've sent all chars
@@bailout:
        SEND_CHAR SLIP_END      ; send an end char to signal end of packet
        pop si
        pop cx
        ret
send_packet endp

;****************************************************************************
; recv_packet
;
; receives a packet using the macro RECV_CHAR() which must be defined by
; the user and places the received packet into the memory buffer pointed
; to by ES:DI.  The final length is returned in BX.
;
; Note that in the case of a buffer overrun, the portion of the packet
; that fit into the buffer is returned and BX and CX are equal.  There
; is no way to tell the difference between a packet that just exactly
; fit and one which was truncated due to buffer overrun, so it is
; important to assure that the buffer is big enough to ALWAYS contain
; at least one spare byte.
;
; A sample RECV_CHAR() is defined above.
;
; Entry:
;       ES:DI ==> packet buffer
;       CX = length of buffer
;       direction flag is assumed to be cleared (incrementing)
;
; Exit:
;       BX = length of packet received
;
; Trashed:
;       none
;
;****************************************************************************
recv_packet proc
        push cx
        push di
        xor bx,bx               ; zero received byte count
        jcxz @@bailout          ; if zero length packet, bail out now
@@nextchar:
        RECV_CHAR               ; fetch a character into al
        cmp al,SLIP_END         ; Q: is it the special END char?
        jne @@check_esc         ;  N: check for ESC
        or  bx,bx               ;  YQ: is it the beginning of packet?
        jz @@nextchar           ;   Y: keep looking
        jmp @@bailout           ;   N: end of packet, so return it

@@check_esc:
        cmp al,SLIP_ESC         ; Q: is it the special ESC char?
        jne @@ordinary          ;  N: it's an ordinary char
        RECV_CHAR               ;  Y: get another character
        cmp al,SLIP_ESC_END     ;  Q: is it ESC_END?
		jne @@check_esc_esc		;   N: check for ESC_ESC
        mov al,SLIP_END         ;   Y: convert to ordinary END char
        jmp @@ordinary          ;
@@check_esc_esc:
        cmp al,SLIP_ESC_ESC     ;  Q: is it ESC_ESC?
        mov al,SLIP_ESC         ;   Y: convert to ordinary ESC char
        ; protocol violation! fall through to ordinary character

@@ordinary:
        stosb                   ; store character in buffer
        inc bx                  ; got another char
        loop @@nextchar         ; keep going until we've sent all chars
@@bailout:
        pop di
        pop cx
        ret
recv_packet endp
        END