Thursday, December 17, 2009

STARTUP.S

;����������������������������������������������������������������������������ͻ
;�                                                                            �
;�                SYSTEM STARTUP FOR 68HC16-BASED DATA-LOGGER                 �
;�                                                                            �
;�                    Copyright (c) 1995 by M. Lutfi                          �
;�                                                                            �
;����������������������������������������������������������������������������ͼ

    .PROCESSOR  M68HC16

    .external _main, __memory,__pdata
    .external ._main, .__pdata
    .external .__text__, .__data__, .__bss__
    .public    _exit, __stext

    .include "hc16regs.mac"
    .include "mcx16.mac"
    .include "lgrset.mac"

    .psect  _text
__stext:
;*********** MCU INIT ******************
; These procedures handled by MCX-16
; Rewritten for clarity only
;**********************************
;    ldab    #0Fh
;    tbek                    ; point EK to bank F for register access
;    ldab    #00H
;    tbxk                    ; point XK to bank 0
;    tbyk                    ; point YK to bank 0
;    tbzk                    ; point ZK to bank 0
;
;    ldaa    #007Fh          ; w=0, x=1, y=111111
;    staa    SYNCR           ; set system clock to 16.78 mhz
;
;    clr     SYPCR           ; turn cop (software watchdog) off,
;                            ; since cop is on after reset
;    ldab    #7FH
;    stab    SYNCR
;    clr     SYPCR
;
;****** init SRAM ******
;    ldab    #1
;    std     RAMBAH
;    clrd
;    std     RAMBAL
;    clr     RAMMCR          ;SRAM addr start from 0x10000 to 0x103FF

;****** init external RAM *******
    ldd     #00003          ; at reset, the csboot block size is 512k.
    std     CSBARBT         ; this line sets the block size to 64k since
                            ;  that is what physically comes with the evb16
    ldd     #00303h
    std     CSBAR0          ;set U1 ram base addr to 0x30000: bank 3, 64k
    std     CSBAR1          ;set U3 ram base addr to 0x30000: bank 3, 64k
    ldd     #05030h         ;no wait states
    std     CSOR0           ;set chip select 0, upper byte, write only
    ldd     #03030h
    std     CSOR1           ;set chip select 1, lower byte, write only
    ldd     #00303h
    std     CSBAR2          ;set chip select 2 to fire at base addr 0x30000
    ldd     #07830h
    std     CSOR2           ;set chip select 2, both bytes, read and write
    ldd     #03FFFh
    std     CSPAR0          ;set chip selects 0,1,2 to 16-bit ports

;�����������������������������������������������������������������������������͸
;�                          ADC INITIALIZATION                                 �
;�   Summary:                                                                  �
;�   The ADC module is mapped into 32 words of address space. Five words are   �
;�   control/status registers, one word is digital port data, and 24 words     �
;�   provide access to the results of ADC conversion (eight addresses for each �
;�   type of converted data). Two words are reserved for expansion.            �
;�   The ADC module base address is determined by the value of the MODMAP bit  �
;�   int the system integration module configuration register (SIMMCR).        �
;�   The base address is normaly $FFF700 in the MC68HC16Z1.                    �
;�       Internally, the ADC has both a differential data bus and a buffered   �
;�   IMB data bus. Registers not directly associated with AD conversion        �
;�   functions, such as the MCR, the MTR, and the PDR, reside on the bufferd   �
;�   bus, while conversion registers and result registers reside on the        �
;�   differential bus.                                                         �
;�                                                                             �
;�   Registers that must be set prior operation:                               �
;�     ADMCR:                                                                  �
;�       STOP = 0 (normal operation)                                           �
;�       FRZ  = 0                                                              �
;�       SUPV = 1 (supervisory mode)                                           �
;�     ADCTL0:                                                                 �
;�       PRS   = 1 (ADC clock = system clock/4 = 16.667 MHz/4)                 �
;�       STS   = 0 (4 A/D clock periods in the sample time)                    �
;�       RES10 = 1 (10-bit conversion)                                         �
;�     ADCTL1:                                                                 �
;�       SCAN  = 1 (continuous conversion)                                     �
;�       MULT  = 1 (sequential conversion of four or eight channels            �
;�                  selected by [CD:CA])                                       �
;�       S8CM  = 1 (eight-conversion sequence)                                 �
;�       CD    = 0 (measured data at each channel stored into his register)    �
;�                                                                             �
;�����������������������������������������������������������������������������;
    ldd     #0000h
    std     ADCMCR         ;turn on ADC
    ldd     #0001h
    std     ADCTL0         ;10-bit, set sample period


;�����������������������������������������������������������������������������͸
;�                                                                             �
;�                     SCI DRIVER INITIALIZATION                               �
;�                                                                             �
;� 1. Sets up the SCI and starts an infinite loop of receive transmit          �
;� 2. QSM configuration summary:                                               �
;�  * After reset, the QSM remains in an idle state, requiring initialization  �
;�    of several registers before any serial operations may begin execution.   �
;�  * The type of serial frame (8 or 9 bit) and the use of partiy must be      �
;�    determined by M. PE and PT.                                              �
;�  * For receive operation, WAKE, RWU, ILT, ILIE must be considered.          �
;�    The receiver must be enabled (RE) and, usually, RIE should be set.       �
;�  * For transmit operation, the transmitter must be enabled (TE) and,        �
;�    usually, TIE should be set. The use of wired-OR mode (WOMS) must also    �
;�    be decided. Once the transmitter is configured, data is not sent         �
;�    until TDRE and TC are cleared. To clear TDRE and TC, the SCSR read       �
;�    must be followed by a write to SCDR (either the lower byte or the        �
;�    entire word).                                                            �
;�  * QIVR should be programmed to one of the user-defined vectors ($40-$FF)   �
;�    during initialization of the QSM.                                        �
;�    After reset, QIVR determines which two vectors in the exception vector   �
;�    table are to be used for QSM interrupts. The QSPI and SCI submodules     �
;�    have separate interrupt vectors adjacent to each other.                  �
;�    Both submodules use the same interrupt vector which LSB:                 �
;�       1: interrupt generated by QSPI                                        �
;�       0: interrupt generated by QSCI                                        �
;�> Detail Configurations in QSM:                                              �
;�  * QMCR:                                                                    �
;�       STOP = 0 (Normal QSM clock operation)                                 �
;�       FRZ1 = 0 (Ignore the FREEZE signal on the IMB)                        �
;�       SUPV = 1 (supervisor access)                                          �
;�       IARB = $A (priority = 10; $F = highest priority, used by timer)       �
;�  * QILR:                                                                    �
;�       ILQSPI = 1 (lowest priority)                                          �
;�       ILSCI  = 7 (highest priority)                                         �
;�       QIVR   = set to SCI interrupt handler's address                       �
;�                                                                             �
;�����������������������������������������������������������������������������;
    orp    #INTS_OFF   ; Turn off interrupts while enabling SCI
    ldab   #0Fh
    tbek               ; EK = F (K=$Fxxx)

    ldd    #008Ah
    std    QMCR        ; Set IARB=10 for intermodule bus arbitration

    ldd    QILR        ; Get content of QSM Interrupt Levels Register
    anda   #0E8h       ; Clear out ILSCI field
    oraa   #SCIINTLV   ; Set SCI interrupt level in ILSCI field
    ldab   #SCIINTV    ; Load SCI Interrupt Vector #
    std    QILR        ; Update QILR

    ldd    #RIE+RE+TE  ; Receiver enabled with interrupts active,
                       ; Transmitter enabled without interrupts,
                       ; 1 Start Bit, 1 Stop Bit, 8 Data Bits
    std    SCCR1       ; Set up SCI operating conditions
    ldd    #BR9600     ; Set up 9600 baud rate
    std    SCCR0

;����������������������������������������������������������������������������͸
;�                           STACK ALLOCATION                                 �
;�                                                                            �
;�   I M P O R T A N T   S Y S T E M   C O N F I G U R A T I O N   N O T E:   �
;�                                                                            �
;�  MCX-16 Requires locations $10000 through $1002F for internal operations.  �
;�  Begin the allocation of MCX-16 System Tables at an address equal to or    �
;�  greater than $10030.                                                      �
;����������������������������������������������������������������������������;
    ldk     #.__pdata       ; select memory bank of _data
    tbxk
    tbyk
    ldk     #.__data__
    tbzk
    ldx     #__pdata        ; start of data descriptor
    ldy     0,x             ; start of data images
    aix     #2              ; next word
ibcl:
    ldab    0,x             ; flag
    beq     zbss            ; nul, next step
    bpl     idad            ; if segment
    aix     #2              ; move to it
    ldab    0,x             ; load it
    tbzk                    ; in data pointer extension
idad:
    ldz     1,x             ; data address
ircl:
    ldd     0,y             ; transfert by word
    std     0,z             ; size must be even
    aiy     #2              ; next word
    aiz     #2
    cpy     3,x             ; end of block ?
    blo     ircl            ; no, loop
    aix     #5              ; descriptor size
    bra     ibcl            ; next block
zbss:
    ldk     #.__bss__
    tbxk
    tbek
    tbyk
    tbzk
    tbsk
    ldx     #sbss       ; start of bss
    clrd                ; to be zeroed
    bra     mtest       ; start loop
bcl:
    std     0,x         ; clear memory
    aix     #2          ; next word
mtest:
    cpx     #__memory   ; end of memory ?
    blo     bcl         ; no, continue
;   aix     #4000H      ; you can set-up a 4K stack
;   txs                 ; above the BSS
    lds     #3FEh       ; or directly initialize ptr
    jsr     _main,#._main
                        ; call main routine of application
_exit:
    bra     _exit       ; loop here if return
;
    .end

MCXEVB.S

;   C STARTUP FOR 68HC16 UNDER MCX-16 REAL-TIME KERNEL
;   ==================================================
;   Copyright (c) 1995
;   Designed and programmed by Muhammad Lutfi
;   for Final Project (TA)
;   Thesis Advisors:
;     Dr. Ir. Farida Idealistina Muchtadi
;        and Ir. Sutanto Hadisupadmo, M.T.
;
;   Instrumentation and Controls Lab.
;   Engineering Physics Dept.
;   Institut Teknologi Bandung
;   Jl. Ganesha 10
;   Bandung 40132
;   Phone: (022) 2504424 ext 759
;   FACS:  (022)2504424
;
;
;
;   Platform : Microcontroller 68HC16Z1 on Evaluation Board (EVB)
;
;   Summary:
;   > Other initialization, except for QSM and ADC module, not necessarily
;     done
;   > Take care with heap an pre-initialized RAM initialization
;
    .include "lgrset.mac"

    .external _main, __memory,__pdata
    .external ._main, .__pdata
    .external .__text__, .__data__, .__bss__
    .public    _exit, __stext

.DEFINE STACK_SIZE = 1000H

    .psect _bss         ; stack section
sbss:

    .psect _text        ; program section

__stext:

;*********** MCU INIT ******************
; These procedures handled by MCX-16
; Rewritten for clarity only
; Still used in HARRAT version.
;***************************************
;
; ROM Start address: 0x00000 (program starts from 0x400 instead)
;
;                           BITS
; +--------------+--------------------------------+
; !              ! 2222 1111 1111 11              !
; ! ADDR PINS    ! 3210 9876 5432 1098 7654 3210  !
; +--------------+--------------------------------+----------+
; ! ADDR[23..0]  ! 0000 0000 0000 01?? ???? ????B != 0x00400 !
; ! ADDR[23..11] ! 0000 0000 0000 0xxx xxxx xxxxB != 0x00000 !
; +--------------+--------------------------------+----------+
;
; BLKSZ = 64 KB => address lines compared: ADDR[23..16]
;
; Frequency:
;      FQ_SYSTEM = FQ_REF*(4^(y+1)*(2^((2*W) + x)))
;      FQ_VCO    = F_SYSTEM^(2-X)
;
.DEFINE BLKSZ = 011B
.DEFINE ADDR  = 0000h
.SET CSBARBT_CFG = ((BLKSZ) | (ADDR << 3))
.DEFINE FQ_REF = 16780000   ; MHz
.DEFINE SW = 00B
.DEFINE SX = 01B
.DEFINE SY = 0111111B
.DEFINE
    SEDIV = (0<<7),
    SLIMP = (1<<4),
    SLOCK = (1<<3),
    RSTEN = (1<<2),
    STSIM = (1<<1),
    STEXT = 1

.SET SYNCR_CFG = ((SW<<7)|(SX<<6)|SY)

    ldab    #0Fh
    tbek                    ; point EK to bank F for register access
    ldab    #00H
    tbxk                    ; point XK to bank 0
    tbyk                    ; point YK to bank 0
    tbzk                    ; point ZK to bank 0
    ldd     #CSBARBT_CFG    ; at reset, the csboot block size is 512k, so
    std     CSBARBT         ; this line sets the block size to 64k since
                            ; that is what physically comes with the EVB16
                            ; Boot ROM starts from 0x400 to 0xFFFF
    ldab    #SYNCR_CFG
    stab    SYNCR           ; set system clock to 16.78 mhz

NOT_L:
    brclr   SYNCR+1,#SLOCK,NOT_L   ;wait until synthesizer lock bit is set

;
; SYPCR:
; �����������������������������������������������������������Ŀ
; � SWE = 0� SWP = 0� SWT = 11 � HME = 1 � BME = 0 � BMT = 00 �
; �������������������������������������������������������������
; SWE=0  : Watchdog disabled
; SWP=0  : Software watchdog clock not prescaled
; SWT[1:0]=11 : software watchdog timing
; HME=1  : enable halt monitor function
; BME=0  : disable bus monitor function for an internal to external bus cycle
; BMT=00 : 64 system clocks for bus monitor timing
;
.DEFINE
    SWE = 0,
    SWP = 0,
    SWT = 011B,
    HME=1,
    BME=0,
    BMT=0
.DEFINE SET_SYPCR = (BMT|(BME<<2)|(HME<<3)|(SWT<<4)|(SWP<<6)|(SWE<<7))

    ldab    #SET_SYPCR      ;#38H
    stab    SYPCR           ; turn cop (software watchdog) off,
                            ; since cop is on after reset

.IF TOROM
    ldd     CSORBT
    andd    #0E7FFh         ; R/W* = 00B, means that
    ord     #(01B<<11)      ; ROM is read only
    std     CSORBT
.ENDIF


;************* Stack Allocation *************
.IF USE_EXT_RAM
    ldk     #2
    tbsk                ; set SK to bank 2 for system stack
    lds     #3FEh        ; put SP at top of 1k internal SRAM (0x203FE)
.ELSE
    ldk     #1
    tbsk                ; set SK to bank 1 for system stack
    lds     #3FEh       ; directly initialize ptr to use 0x103FE (SRAM)
.ENDIF                  ; and below as system stack


;�����������������������������������������������������������������������������Ŀ
;�                          ADC INITIALIZATION                                 �
;�   Summary:                                                                  �
;�   The ADC module is mapped into 32 words of address space. Five words are   �
;�   control/status registers, one word is digital port data, and 24 words     �
;�   provide access to the results of ADC conversion (eight addresses for each �
;�   type of converted data). Two words are reserved for expansion.            �
;�   The ADC module base address is determined by the value of the MODMAP bit  �
;�   int the system integration module configuration register (SIMMCR).        �
;�   The base address is normaly $FFF700 in the MC68HC16Z1.                    �
;�       Internally, the ADC has both a differential data bus and a buffered   �
;�   IMB data bus. Registers not directly associated with AD conversion        �
;�   functions, such as the MCR, the MTR, and the PDR, reside on the bufferd   �
;�   bus, while conversion registers and result registers reside on the        �
;�   differential bus.                                                         �
;�                                                                             �
;�   Registers that must be set prior operation:                               �
;�     ADMCR:                                                                  �
;�       STOP = 0 (normal operation)                                           �
;�       FRZ  = 0                                                              �
;�       SUPV = 1 (supervisory mode)                                           �
;�     ADCTL0:                                                                 �
;�       PRS   = 00011B (ADC clock = system clock/8 = 2.1 MHz (Max ADC freq)   �
;�       STS   = 00 (4 A/D clock periods in the sample time)                   �
;�       RES10 = 01 (10-bit conversion)                                        �
;�     ADCTL1:                                                                 �
;�       SCAN  = 1 (continuous conversion)                                     �
;�       MULT  = 1 (sequential conversion of four or eight channels            �
;�                  selected by [CD:CA])                                       �
;�       S8CM  = 1 (eight-conversion sequence)                                 �
;�       CDCA  = 0xxx (measured data at each channel stored into his register) �
;�                                                                             �
;�������������������������������������������������������������������������������
.DEFINE MY_PRS   = 11B,
        MY_STS   = 00B,
        MY_RES10 = 1
.DEFINE MY_CDCA  = 1000B,
        MY_S8CM  = 1,
        MY_MULT  = 1,
        MY_SCAN  = 1

    ldab    #0Fh
    tbek                ; EK = F (K=$Fxxx)
    ADCMCR_SET 0, 0, 0
    ADCTL0_SET MY_PRS, MY_STS, MY_RES10
    ADCTL1_SET MY_CDCA, MY_S8CM, MY_MULT, MY_SCAN


;�����������������������������������������������������������������������������Ŀ
;�                                                                             �
;�                     SCI DRIVER INITIALIZATION                               �
;�                                                                             �
;� 1. Sets up the SCI and starts an infinite loop of receive transmit          �
;� 2. QSM configuration summary:                                               �
;�  * After reset, the QSM remains in an idle state, requiring initialization  �
;�    of several registers before any serial operations may begin execution.   �
;�  * The type of serial frame (8 or 9 bit) and the use of partiy must be      �
;�    determined by M. PE and PT.                                              �
;�  * For receive operation, WAKE, RWU, ILT, ILIE must be considered.          �
;�    The receiver must be enabled (RE) and, usually, RIE should be set.       �
;�  * For transmit operation, the transmitter must be enabled (TE) and,        �
;�    usually, TIE should be set. The use of wired-OR mode (WOMS) must also    �
;�    be decided. Once the transmitter is configured, data is not sent         �
;�    until TDRE and TC are cleared. To clear TDRE and TC, the SCSR read       �
;�    must be followed by a write to SCDR (either the lower byte or the        �
;�    entire word).                                                            �
;�  * QIVR should be programmed to one of the user-defined vectors ($40-$FF)   �
;�    during initialization of the QSM.                                        �
;�    After reset, QIVR determines which two vectors in the exception vector   �
;�    table are to be used for QSM interrupts. The QSPI and SCI submodules     �
;�    have separate interrupt vectors adjacent to each other.                  �
;�    Both submodules use the same interrupt vector which LSB:                 �
;�       1: interrupt generated by QSPI                                        �
;�       0: interrupt generated by QSCI                                        �
;�> Detail Configurations in QSM:                                              �
;�  * QMCR:                                                                    �
;�       STOP = 0 (Normal QSM clock operation)                                 �
;�       FRZ1 = 1 (Halt the QSM on a transfer boundary)                        �
;�       FRZ0 = 0                                                              �
;�       SUPV = 1 (supervisor access)                                          �
;�       IARB = $A (priority = A; $F = highest priority, used by timer)        �
;�  * QILR:                                                                    �
;�       ILQSPI = 0 (disabled)                                                 �
;�       ILSCI  = 7 (highest priority)                                         �
;�       QIVR   = set to SCI interrupt handler's address                       �
;�                                                                             �
;�������������������������������������������������������������������������������
    disable             ; Turn off interrupts while enabling SCI

    ldd    #(0080h+QSMIARB)
    std    QMCR         ; Set IARB=10 for intermodule bus arbitration

    ldd    #MY_QILR     ; our QILR configuration
    std    QILR         ;

    ldd    #BR9600      ; Set up 9600 baud rate
    std    SCCR0
    ldd    #RIE+RE+TE   ; Receiver enabled with interrupts active,
                        ; Transmitter enabled without interrupts,
                        ; 1 Start Bit, 1 Stop Bit, 8 Data Bits
    std    SCCR1        ; Set up SCI operating conditions

rdclr:
    ldd    SCSR         ; Get current status of SCI
    bitb   #RDRF        ; Read  receive data ready (RDR) until RDR reset
    beq    scirdy       ; ZF=1 (RDRF=0), so no RDR is empty
    ldab   SCDR         ; Read input data (a mechanism to reset RDRF)
    bra    rdclr        ; Loop until clear

scirdy:
    enable              ; Turn interrupts back on



;*************** BEGIN **************
    ldk     #.__bss__   ; load extended address of stack
    tbek
    tbxk
    tbyk
    tbzk
    ldx     #sbss       ; start of bss
    clrd                ; to be zeroed
    bra     mtest       ; start loop
bcl:
    std     0,x         ; clear memory
    aix     #2          ; next word
mtest:
    cpx     #__memory   ; end of memory ?
    blo     bcl         ; no, continue
    jsr     _main       ; call application
_exit:
    bgnd                ; loop here if return
1$:
    bra    1$
;
.end

MCXCALLS.S

;*****************************************************************************
;                      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

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 ===============================