;*****************************************************************************
; INTERFACE WhiteSmith to MCX-16
; ------------------------------
; Platform : Motorola 68HC16 running MCX16 Multitasking Executive
; Compiler : Whitesmith C
;
; (c) M. Lutfi Shahab (13389011)
; Instrumentation and Control Labs.
; Department of Engineering Physics
; Institut Teknologi Bandung (ITB)
;
; Created : 13 Apr 1995
; Updated : 5 May 1995
;
;Interface Spec.
;---------------
; In WSC:
; - The default memory model is the COMPACT model, in which all of
; the code and data must share the same 64K area.
; - Type SHORT INT are stored as two bytes, MSB first
; - Type LONG INTEGER are stored as four bytes, in descending order of
; significance.
; - Type FLOAT are represented in specific format adapted to the M68HC16
; processor, is a 4 bytes of data; the first two bytes contain a signed
; mantissa coding real values in the interval [-1.0,1.0]. The other 2
; bytes contain signed exponent. If the first word is zero, the entire
; number will be understood 0.0. The value of the number is the fraction
; multiplied by 2 raised to the exponent. The mantissa is then compatible
; with the internal DSP processor format.
; - Type DOUBLE are represented as for the proposed IEEE-754 floating point
; standard; 8 bytes stored in descending order of byte significance:
; * MSB bit is one of four negative numbers, and zero otherwise.
; * next 8 bits are the characteristic, biased such that the binary
; exponent of the number is the characteristic minus 1002.
; * the remaining bits are the fraction, starting with the weighted bit.
; - Arguments are moved onto the stack from right to left. The first
; argument is stored in the D register if its size is less than or equal
; to the size of an int, or in the register pair E, D if it is a long
; or float, and in the function doesn't return a struct or a double.
; - By default, character data is sign extended to short, and floats are
; extended to doubles.
; - The function is called via jsr _func or jbsr _func.
; - The arguments to the function are popped off the stack.
; - A data space address is move onto stack if a struct or double return
; area is required.
; - The return value is in D register (if length less than 2 bytes), or
; is in the registers E,D (the D register holds the low order bits).
; The addresses of double and struct data are added on stack at call time.
; - Each C function maintains its own stack frame, using IX as frame pointer.
; - You must save an restore the K register if want to call an assembly
; language routine that modifies the EK, XK, YK, or ZK registers.
; - BSR does these microcode sequences:
; 1. put PC to stack
; 2. SP = SP-2
; 3. put CCR to stack
; 4. SP = SP-2
; so SP (now) = SP (before bsr) + 4
;
; - XK, YK, ZK and SK can be modified by using the transfer index register
; to stack pointer and transfer stack pointer to index register instructions.
; When SP is transfered to (TSX, TSY, and TSZ) or from (TXS, TSY, TSZ) an
; index register, the corresponding address extension fields is also
; transfered. Before the extension field is transfered, it is incremented
; or decremented if bank overflow occured as a result of the instruction.
;
; NOTE
; > Be aware of invoking these routines. Make sure you have
; set extension register (K) to a proper value, otherwise the system
; will crash.
;*****************************************************************************/
.PROCESSOR M68HC16
.TITLE "Thesis Project : WSC -> MCX calling translation"
.INCLUDE "mcx16.mac"
.psect _text
.even
.LIST + .MACRO
;* below are calling codes to MCX-16 *
;-----------------------------------
; __mcx_nop
;
; cycles=14
; code = 4 bytes
;----------------------------------
.PUBLIC __mcx_nop
__mcx_nop:
MCX_CALL MCX_NOP_
rts
;------------------------------------------------------
;* Force a semaphore to PENDing state*
; Narrative:
; WSC:
; void _mcx_pend(int sema)
; sema passed to D (ACCB = lo(sema), ACCA = hi(sema))
;
; MCX:
; parameters:
; ACCA = code for _mcx_pend = 3
; ACCB = semaphore number
; return:
; none
;
; cycles = 14
; code = 6 bytes
;------------------------------------------------------
.even
.PUBLIC __mcx_pend
__mcx_pend:
MCX_CALL MCX_PEND_
rts
;--------------------------------------------------
; * Signal the occurence of an event.
; Narrative:
; WSC:
; void _mcx_signal(int sema)
; ACCD = sema
;
; MCX:
; parameters:
; ACCA = code for mcx_signal = 1
; ACCB = semaphore number
; return:
; none
;
; cycles = 14
; code = 6 bytes
;--------------------------------------------------
.even
.PUBLIC __mcx_signal
__mcx_signal:
MCX_CALL MCX_SIGNAL_ ;* Signal semaphore *
rts
;------------------------------------------------------
; * Make a task wait for the occurence of a specified
; event
; Narrative:
; WSC:
; int _mcx_wait(int sema)
; on call, sema in D register
;
; MCX:
; parameters:
; ACCA = code for _mcx_wait = 2
; ACCB = semaphore number
; return:
; ACCA, 0 = success, 1 = fail
;
; cycles = 16
; code = 8 bytes
;------------------------------------------------------
.even
.PUBLIC __mcx_wait
__mcx_wait:
MCX_CALL MCX_WAIT_ ;* Wait on event *
clrb
rts
;------------------------------------------------------
;* Signal completion of message processing*
; Narrative:
; WSC:
; void _mcx_msg_done(void *msg)
; msg passed to D
;
; MCX:
; parameters:
; ACCA = code for _mcx_msg_done = 18
; IX = message address
; return:
; none
;
; cycles = 28
; code = 12 bytes
;------------------------------------------------------
.even
.PUBLIC __mcx_msg_done
__mcx_msg_done:
pshm x ; save stack frame
xgdx ; IX = msg
MCX_CALL MCX_MSG_DONE_
pulm x ; restore old IX (stack frame)
rts
;------------------------------------------------------
;* Fetch message from a mailbox
; Narrative:
; WSC:
; void *_mcx_receive(int mbx)
; mbx passed to D
; return:
; D = address of the received message
;
; MCX:
; parameters:
; ACCA = code for _mcx_receive = 16
; ACCB = mailbox number
; return:
; IX = address of received message
;
; cycles = 28
; code = 12 bytes
;------------------------------------------------------
.even
.PUBLIC __mcx_receive
__mcx_receive:
pshm x ; save stack frame
MCX_CALL MCX_RECEIVE_
xgdx ; D contain address of msg
pulm x ; restore stack frame
rts
;------------------------------------------------------
;* Fetch message from a mailbox with wait directive
;
; Narrative:
; WSC:
; void *_mcx_receive_w(int mbx)
; mbx passed to D
; return:
; ACCD = address of the received message
;
; MCX:
; parameters:
; ACCA = code for _mcx_receive_w = 17
; ACCB = mailbox number
; return:
; IX = address of the received message
;
; cycles = 28
; code = 12 bytes
;------------------------------------------------------
.even
.PUBLIC __mcx_receive_w
__mcx_receive_w:
pshm x ; save stack frame
MCX_CALL MCX_RECEIVE_W_
xgdx ; D = address of received msg
pulm x ; restore stack frame
rts
;-------------------------------------------------------
;* send message MSG into target MAILBOX
; with SEMA semaphore
; Narrative:
; WSC:
; void _mcx_send_mbx(int mbx, void *msg, int sema)
; mbx in D
; msg in [SP+6]
; sema in [SP+8]
;
; MCX:
; parameters:
; ACCA = code for _mcx_send_mbx = 14
; ACCB = message semaphore number (sema)
; ACCE = mailbox number to receive message (mbx)
; IX = message address
; return:
; none
;
; cycles = 14
; code = 18 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_send_mbx
__mcx_send_mbx:
pshm x ; save old stack frame because IX used by MCX
tsz ; Z = SP + 2
xgde ; E = mbx
ldd 8,z ; D = sema
ldx 6,z ; IX = msg
mcx_call MCX_SENDMBX_
; accb = sema, acca = 14
pulm x
rts
;-------------------------------------------------------
;* send message MSG into target MAILBOX
; with SEMA semaphore and wait directive
; Narrative:
; WSC:
; void _mcx_send_mbx_w(int mbx, void *msg, int sema)
; mbx in D
; msg in [SP+6]
; sema in [SP+8]
;
; MCX:
; parameters:
; ACCA = code for _mcx_send_mbx_w = 15
; ACCB = message semaphore number
; ACCE = mailbox number to receive message
; IX = message address
; return:
; none
;
; cycles =
; code = 18 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_send_mbx_w
__mcx_send_mbx_w:
pshm x ; save old stack frame because IX used by MCX
tsz ; Z = SP + 2
xgde ; E = mbx
ldd 8,z ; D = sema
ldx 6,z ; IX = msg
MCX_CALL MCX_SENDMBX_W_
; accb = sema, acca = 15
pulm x
rts
;-------------------------------------------------------
;* Get the oldest entry from a FIFO queue
; Narrative:
; int _mcx_dequeue(int queue, void *ptr)
; queue in D
; ptr in [SP+6]
; return: ACCD
;
; parameters to mcx:
; ACCA = code for mcx_dequeue = 12
; ACCB = queue number
; IX = destination address of entry
; return:
; ACCA = 0, if queue was empty
; 1, if entry dequeued successfully other
;
; cycles =
; code = 16 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_dequeue
__mcx_dequeue:
pshm x
tsz ;
ldx 6,z ; IX = ptr
; ACCD hold queue number, so we just set ACCA
MCX_CALL MCX_DEQUEUE_
pulm x
clrb
rts
;-------------------------------------------------------
;* Get the oldest entry from a FIFO queue. If queue
; empty, wait until queue not empty
; Narrative:
; int _mcx_dequeue_w(int queue, void *ptr)
; queue in ACCD
; ptr in [SP+6]
;
; parameters to mcx:
; ACCA = code for mcx_dequeue_w = 13
; ACCB = queue number
; IX = destination address of entry
; return:
; ACCA = 0, if queue was empty
; = 1, if dequeue was successful
;
; cyles =
; code = 16 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_dequeue_w
__mcx_dequeue_w:
pshm x
tsz
ldx 6,z ; IX = ptr
; ACCD hold queue number, so we just set ACCA
MCX_CALL MCX_DEQUEUE_W_
pulm x
clrb ; ACCD = return value of mcx_dequeue_w
rts
;-------------------------------------------------------
;* Put an entry to a FIFO queue.
;
; Narrative:
; int _mcx_enqueue(int queue, void *ptr)
; queue in D
; ptr in [SP+6]
; return: ACCD
;
; parameters to mcx:
; ACCA = code for mcx_dequeue_w = 10
; ACCB = queue number
; IX = destination address of entry
; return:
; ACCA = 0, if queue was empty
; = 1, if dequeue was successful
;
; cycles =
; code = 16 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_enqueue
__mcx_enqueue:
pshm x
tsz ; Z = SP + 2
ldx 6,z ; IX = ptr
; ACCD hold queue number, so we just set ACCA
MCX_CALL MCX_ENQUEUE_
pulm x
clrb
rts
;-------------------------------------------------------
;* Put an entry to a FIFO queue and wait if empty.
; Narrative:
; int _mcx_enqueue_w(int queue, void *ptr)
; queue in ACCD
; ptr in [SP+6]
; return: ACCD
;
; parameters to mcx:
; ACCA = code for mcx_dequeue_w = 11
; ACCB = queue number
; IX = source address of entry
; return:
; ACCA = 0, if queue successful
; = 1, if empty
;
; cycles =
; code = 16 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_enqueue_w
__mcx_enqueue_w:
pshm x
tsz ; Z = SP + 2
ldx 6,z ; IX = ptr
; ACCD hold queue number, so we just set ACCA
MCX_CALL MCX_ENQUEUE_W_
pulm x
clrb
rts
;-------------------------------------------------------
;* Change the priority of a task *
; Narrative:
; void _mcx_change_pri(int task, priority)
; ACCD = task number
; [SP+6] = priority
;
; parameters:
; ACCA = code for _mcx_execute = 9
; ACCB = task number
; ACCE = task priority
;
; return:
; none
;
; cycles =
; code = 12 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_change_pri
__mcx_change_pri:
tsz ; Z = SP + 2
lde 6,z ; E = priority
; ACCD hold task address, so we just set ACCA
MCX_CALL MCX_CHANGE_PRI_
rts
;-------------------------------------------------------
;* Excecute a task *
; Narrative:
; void _mcx_execute(int tsk_num, priority, (void *)(task)(),
; void *stack_ptr, void *reg_init_ptr)
; tsk_num = ACCD
; reg_init_ptr = [SP +12]
; stack_ptr = [SP +10]
; task address = [SP + 8]
; priority = [SP + 6]
;
; parameters:
; ACCA = code for _mcx_execute = 4
; ACCB = task number
; ACCE = task priority
; IX = starting address of task
; IY = base address of task's stack
; IZ = address of register initialization packet
; (IZ=0 means no packet)
; return:
; none
;
; cycles =
; code = 22 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_execute
__mcx_execute:
pshm x
tsz ; Z = SP + 2
; ACCD = task number
lde 6,z ; ACCE = task priority
ldx 8,z ; IX = task
ldy 10,z ; IY = stack
ldz 12,z ; IZ = reg_init_pkt
MCX_CALL MCX_EXECUTE_
;* Execute a task *
pulm x
rts
;-------------------------------------------------------
;* Clears the suspend state of task
;
; Narrative:
; WSC:
; void _mcx_resume(int task_num)
; ACCD = task_num
;
; MCX:
; parameters:
; ACCA = code for _mcx_resume = 7
; ACCB = task number
;
; return:
; none
;
; cycles =
; code = 6 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_resume
__mcx_resume:
; ACCB = task number
MCX_CALL MCX_RESUME_
rts
;-------------------------------------------------------
;* Blocks the specified task with a SUSPEND condition
; Narrative:
; WSC:
; void _mcx_suspend(int task_num)
; ACCD = task_num
;
; MCX:
; parameters:
; ACCA = code for _mcx_suspend = 6
; ACCB = task number (0= SELF)
; return:
; none
;
; cycles =
; code = 6 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_suspend
__mcx_suspend:
; ACCB = task number
MCX_CALL MCX_SUSPEND_
rts
;-------------------------------------------------------
;* Terminate a task's operation
;
; Narrative:
; WSC:
; void _mcx_terminate(int task_num)
; ACCD = task_num
;
; MCX:
; parameters:
; ACCA = code for _mcx_resume = 5
; ACCB = task number
;
; return:
; none
;
; cycles =
; code = 6 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_terminate
__mcx_terminate:
; ACCB = task number
MCX_CALL MCX_TERMINATE_
rts
;-------------------------------------------------------
;* Blocks specified task with a DELAYED state for the
; period
; Narrative:
; WSC:
; void _mcx_delay(int task_num, int tick)
; ACCD = task_num
; [SP+6] = tick
;
; MCX:
; parameters:
; ACCA = code for _mcx_delay = 8
; ACCB = task number (0= SELF)
; ACCE = number of clock ticks to delay
; return:
; none
;
; cycles =
; code = 16 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_delay
__mcx_delay:
; ACCB = task number
tsz
lde 6,z ; ACCE = tick
MCX_CALL MCX_DELAY_
rts
;-------------------------------------------------------
;* Stop a timer whose handle is specified by ptr
; Narrative:
; WSC:
; void _mcx_kill_timer(void *tick_handler)
; ACCD = tick_handler
;
; MCX:
; parameters:
; ACCA = code for _mcx_kill_timer = 20
; ACCE = Handle of timer to be killed
; return:
; none
;
; cycles =
; code = 10 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_kill_timer
__mcx_kill_timer:
tsz
xgde ; ACCE = tick handler
MCX_CALL MCX_KILL_TIMER_
rts
;-------------------------------------------------------
;* Stop a active timer and reset its duration to a new
; value
; Narrative:
; WSC:
; void _mcx_reset_t(void *hndr, int tick)
; ACCD = hndlr
; [SP+6] = tick
;
; MCX:
; parameters:
; ACCA = code for _mcx_delay = 21
; ACCE = Handle of timer to be reset
; IX = duration of timer's initial period
; return:
; none
;
; cycles =
; code = 16 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_reset_t
__mcx_reset_t:
; ACCB = task number
pshm x
tsz
xgde ; ACCE = tick handler
ldx 6,z ; tick
MCX_CALL MCX_RESET_T_
pulm x
rts
;-------------------------------------------------------
;* Stop a active timer and reset its duration to a new
; value
; Narrative:
; WSC:
; void *_mcx_timer(int init_t, int recyle_t, int sema)
; ACCD = init_t
; [SP+6] = recyle_t
; [SP+8] = sema
; return: ACCD
;
; MCX:
; parameters:
; ACCA = code for _mcx_timer = 19
; ACCB = semaphore number
; ACCE = Duration of initial period
; IX = duration of recycle time
; return:
; ADDE = handle of timer if it was established
; = 0, if there were no timer blocks available
; in the pool of free timers.
;
; cycles =
; code = 20 bytes
;-------------------------------------------------------
.even
.PUBLIC __mcx_timer
__mcx_timer:
pshm x
tsz ; z = SP
xgde ; ACCE = init_t
ldd 8,z ; ACCD = sema = [SP+8]
ldx 6,z ; IX = recycle_t = [SP+6]
MCX_CALL MCX_TIMER_
pulm x
ted ; ACCD = handle of timer
rts
.END
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.
;
;-----------------------------------------------------------------------------
;IFDEF TASM
PAGE 60, 120
;ENDIF
MODEL large, C
MASM
DEBUG EQU 0 ; debug
INCLUDE PIC.ASH
INCLUDE SLIP.ASH
.DATA
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
IF DEBUG EQ 1
isr_msg DB 'Ini di dalam recv_isr', 10, 13, '$'
isr_in_sema db 'Semaphore > 0', 10,13,'$'
ENDIF
.CODE
; This variable must be put in CODE SEGMENT
in_recv_isr DW 0
IF DEBUG EQ 1
;-------------------------
; al = charactrer to print
;-------------------------
print PROC NEAR
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
RETN
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
RET
print_msg ENDP
ENDIF
xmit PROC NEAR
retn
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
sendch1:
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
sendch2:
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
sendch3:
POP cx dx ds
RETN
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
LESS_OR_EQU:
;---add cx, 4 ; total packet length =
; sizeof(pktlen)+sizeof(datatype)+sizeof(crc)
; = 1 + 1 + 2 = 4 bytes
cld
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
send_pkt_1:
lodsb
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
send_pkt_2:
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
send_pkt_3:
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
send_pkt_end:
xor ax, ax ; error occured
keluar:
POP si ds
RET
_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
check_again:
cmp WORD PTR [baudrate], 9600 ;below 9600 we're strictly
jb asyrxint_a ;interrupt driven
mov bx, rcv_time
asyrxint_a:
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
asyrxint_again:
xor cx, cx ; initialize counter
mov dx, [lsr]
asyrxint_in:
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
asyrxint_gotit:
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
asyrxint_ok:
stosb
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.
asyrxint_3:
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
IF DEBUG EQ 1
mov al, 'O'
call print
ENDIF
asyrxint_chk_flg:
cmp bp, 0 ; was this the first char?
jne asyrxint_1 ; no - exit handler
asyrxint_reset:
inc bp ; set 1st character flag
jmp asyrxint_again ; get another character
asyrxint_exit:
asyrxint_1:
mov WORD PTR [rcv.head], di ; set offset pointed by rcv.head
mov [packet_sem], si
exit:
pop ds
RETN
asyrxint ENDP
;--------------------
; registers used:
; ax, ds
;
;--------------------
recv PROC NEAR
recv_2:
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
recv_3:
recv_5:
; 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
recv_5_1:
jmp recv_2
;process IIR_RLS here
recv_1:
RETN
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
lodsb
pop ds
cmp si, WORD PTR [rcv.buf_end]
jne recv_char_1
mov si, WORD PTR [rcv.buf]
recv_char_1:
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_2:
RETN
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
ulang:
rep movsb
mov WORD PTR [queue.write], di ; points to next entry list
fail:
RETN
recv_copy ENDP
;---------------------------------------
; INTERRUPT SERVICE ROUTINE
; 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
;----------------------------------------
.CODE
PUBLIC C dummy_isr
dummy_isr PROC FAR
PUSH ax bx cx dx ds es si di bp
pushf
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
popf
POP bp di si es ds dx cx bx ax
iret
ENDP
PUBLIC C recv_isr
recv_isr PROC FAR
;jmp short start
;in_recv_isr DW 0
start:
cmp cs:[in_recv_isr], 0
je no_re_enter
iret
no_re_enter:
PUSH ax bx cx dx ds es si di bp
pushf
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
COMMENT *
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
;----------------------------------------------------------------
COMMENT *
; 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, <wjr@ftp.com>
; 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
recv_isr_4:
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
recv_isr_3:
;-------- 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
ready:
mov [recv_pkt_ready], 0 ; reset flag
sti
;===================== BEGIN DEPACKETIZE DATA HERE ======================
recv_frame:
cmp [packet_sem], 0 ; should we do this?
jne sema ; yes - process it
jmp next_sema ; no (packet_sem==0) => exit
sema:
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
;-------
recv_frame_full:
mov [queue.status], QFULL
next_pkt:
mov [recv_pkt_ready], 1
jmp recv_exit
not_full:
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
ENDIF
;--------
;---- find how long message in the packet is
recv_frame_1:
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
recv_frame_2:
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
trail_recv:
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
recv_frame_ok:
mov si, WORD PTR [rcv.tail] ;resetting pointer to rcv.tail.
;----mov ax, cx
;----stosb ;queue.write[0] = cx
mov dx, 0 ;count
recv_frame_4:
cmp di, WORD PTR [queue.last] ; is it reach the end of queue ?
jne recv_frame_ready
recv_too_big:
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
ENDIF
;jmp next_sema
jmp next_pkt
recv_frame_ready:
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
recv_frame_5:
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
recv_frame_6:
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
recv_frame_3:
mov WORD PTR [rcv.tail], si ;set to new location
recv_frame_end:
jcxz next_sema ; if no message, just skipped out
kecil:
add bp, Q_WIDTH
mov WORD PTR queue.write, bp ;next line of queue.
next_sema:
dec [packet_sem]
cmp [packet_sem], 0 ; are there more packets ready?
ja any_packet ; if (semaphore > 0)
jmp short recv_exit
any_packet:
jmp recv_frame ; yes - execute again
recv_exit:
; 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.
cli
mov al,[int_no] ; Now reenable device interrupts
call unmaskint
popf
POP bp di si es ds dx cx bx ax
sti
mov cs:in_recv_isr, 0 ; clear the re-entrancy flag
iret
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
mask_not_irq2:
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_1:
RETN
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
unmask_not_irq2:
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
RETN
unmaskint ENDP
ENDS
END
;============================ END OF PROGRAM ===============================
; 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.
;
;-----------------------------------------------------------------------------
;IFDEF TASM
PAGE 60, 120
;ENDIF
MODEL large, C
MASM
DEBUG EQU 0 ; debug
INCLUDE PIC.ASH
INCLUDE SLIP.ASH
.DATA
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
IF DEBUG EQ 1
isr_msg DB 'Ini di dalam recv_isr', 10, 13, '$'
isr_in_sema db 'Semaphore > 0', 10,13,'$'
ENDIF
.CODE
; This variable must be put in CODE SEGMENT
in_recv_isr DW 0
IF DEBUG EQ 1
;-------------------------
; al = charactrer to print
;-------------------------
print PROC NEAR
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
RETN
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
RET
print_msg ENDP
ENDIF
xmit PROC NEAR
retn
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
sendch1:
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
sendch2:
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
sendch3:
POP cx dx ds
RETN
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
LESS_OR_EQU:
;---add cx, 4 ; total packet length =
; sizeof(pktlen)+sizeof(datatype)+sizeof(crc)
; = 1 + 1 + 2 = 4 bytes
cld
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
send_pkt_1:
lodsb
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
send_pkt_2:
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
send_pkt_3:
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
send_pkt_end:
xor ax, ax ; error occured
keluar:
POP si ds
RET
_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
check_again:
cmp WORD PTR [baudrate], 9600 ;below 9600 we're strictly
jb asyrxint_a ;interrupt driven
mov bx, rcv_time
asyrxint_a:
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
asyrxint_again:
xor cx, cx ; initialize counter
mov dx, [lsr]
asyrxint_in:
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
asyrxint_gotit:
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
asyrxint_ok:
stosb
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.
asyrxint_3:
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
IF DEBUG EQ 1
mov al, 'O'
call print
ENDIF
asyrxint_chk_flg:
cmp bp, 0 ; was this the first char?
jne asyrxint_1 ; no - exit handler
asyrxint_reset:
inc bp ; set 1st character flag
jmp asyrxint_again ; get another character
asyrxint_exit:
asyrxint_1:
mov WORD PTR [rcv.head], di ; set offset pointed by rcv.head
mov [packet_sem], si
exit:
pop ds
RETN
asyrxint ENDP
;--------------------
; registers used:
; ax, ds
;
;--------------------
recv PROC NEAR
recv_2:
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
recv_3:
recv_5:
; 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
recv_5_1:
jmp recv_2
;process IIR_RLS here
recv_1:
RETN
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
lodsb
pop ds
cmp si, WORD PTR [rcv.buf_end]
jne recv_char_1
mov si, WORD PTR [rcv.buf]
recv_char_1:
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_2:
RETN
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
ulang:
rep movsb
mov WORD PTR [queue.write], di ; points to next entry list
fail:
RETN
recv_copy ENDP
;---------------------------------------
; INTERRUPT SERVICE ROUTINE
; 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
;----------------------------------------
.CODE
PUBLIC C dummy_isr
dummy_isr PROC FAR
PUSH ax bx cx dx ds es si di bp
pushf
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
popf
POP bp di si es ds dx cx bx ax
iret
ENDP
PUBLIC C recv_isr
recv_isr PROC FAR
;jmp short start
;in_recv_isr DW 0
start:
cmp cs:[in_recv_isr], 0
je no_re_enter
iret
no_re_enter:
PUSH ax bx cx dx ds es si di bp
pushf
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
COMMENT *
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
;----------------------------------------------------------------
COMMENT *
; 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, <wjr@ftp.com>
; 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
recv_isr_4:
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
recv_isr_3:
;-------- 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
ready:
mov [recv_pkt_ready], 0 ; reset flag
sti
;===================== BEGIN DEPACKETIZE DATA HERE ======================
recv_frame:
cmp [packet_sem], 0 ; should we do this?
jne sema ; yes - process it
jmp next_sema ; no (packet_sem==0) => exit
sema:
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
;-------
recv_frame_full:
mov [queue.status], QFULL
next_pkt:
mov [recv_pkt_ready], 1
jmp recv_exit
not_full:
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
ENDIF
;--------
;---- find how long message in the packet is
recv_frame_1:
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
recv_frame_2:
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
trail_recv:
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
recv_frame_ok:
mov si, WORD PTR [rcv.tail] ;resetting pointer to rcv.tail.
;----mov ax, cx
;----stosb ;queue.write[0] = cx
mov dx, 0 ;count
recv_frame_4:
cmp di, WORD PTR [queue.last] ; is it reach the end of queue ?
jne recv_frame_ready
recv_too_big:
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
ENDIF
;jmp next_sema
jmp next_pkt
recv_frame_ready:
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
recv_frame_5:
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
recv_frame_6:
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
recv_frame_3:
mov WORD PTR [rcv.tail], si ;set to new location
recv_frame_end:
jcxz next_sema ; if no message, just skipped out
kecil:
add bp, Q_WIDTH
mov WORD PTR queue.write, bp ;next line of queue.
next_sema:
dec [packet_sem]
cmp [packet_sem], 0 ; are there more packets ready?
ja any_packet ; if (semaphore > 0)
jmp short recv_exit
any_packet:
jmp recv_frame ; yes - execute again
recv_exit:
; 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.
cli
mov al,[int_no] ; Now reenable device interrupts
call unmaskint
popf
POP bp di si es ds dx cx bx ax
sti
mov cs:in_recv_isr, 0 ; clear the re-entrancy flag
iret
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
mask_not_irq2:
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_1:
RETN
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
unmask_not_irq2:
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
RETN
unmaskint ENDP
ENDS
END
;============================ END OF PROGRAM ===============================
Subscribe to:
Posts (Atom)