;****************************************************************************
;* Copyright 1990 to 1994 MBARI                                             *
;****************************************************************************
;* Summary  : Assembly language routines for MBARI Microcontroller          *
;* Filename : micro.s                                                       *
;* Author   : Andrew Pearce                                                 *
;* Project  : Tiburon ROV                                                   *
;* Version  : 1.0                                                           *
;* Created  : 04/26/91                                                      *
;* Modified : 08/18/94                                                      *
;* Archived :                                                               *
;
;****************************************************************************
;* Modification History:                                                    *
;* $Header: /usr/tiburon/.cvsroot/micro/lib/micro.s,v 1.1.1.1 1997/05/02 17:16:00 pean Exp $
;* $Log: micro.s,v $
;* Revision 1.1.1.1  1997/05/02 17:16:00  pean
;* Initial release of the microcontroller software after Tiburon
;* Moolpool Dive to test IView, Lapboxes, modified Power can
;* GF/5V using bus capacitance mode.
;*
;Revision 1.1  92/05/14  09:21:10  09:21:10  pean (Andrew Pearce 408-647-3746)
;Initial revision
;
;*
;****************************************************************************

PUBLIC  read_sysclock           ; Returns the value of the system ticker
PUBLIC  reset_processor         ; resets the processor to power up state
PUBLIC  ram_test_result         ; Result of External Ram Test
PUBLIC  mem_size                ; Make ?MEMORY_SIZE available to C
PUBLIC  mem_start               ; Make MEM_START available to C
PUBLIC  REG0                    ; Temporary Scratch Register 0
PUBLIC  REG1                    ; Temporary Scratch Register 1
PUBLIC  REG2                    ; Temporary Scratch Register 2
PUBLIC  REG3                    ; Temporary Scratch Register 3
PUBLIC  REG4                    ; Temporary Scratch Register 4
;
PUBLIC  SYSCLOCK                ; System Clock (Ticker)
PUBLIC  IOS1_COPY               ; Copy of IOS1 Register
;
EXTRN   MEMORY:WORD             ; Base of free memory segment
EXTRN   ?MEMORY_SIZE:WORD       ; Size of free memory segment
EXTRN   receiveByte:ENTRY       ; Serial Port Receive Byte C hook
EXTRN   toggleLED:ENTRY         ; Toggle Front Panel LED hook
EXTRN   initTimerChain:ENTRY    ; Timer Chain Initialization hook
EXTRN   timerEvent:ENTRY        ; Timer chain event hook
EXTRN   mainLoop:ENTRY          ; The brain dead Intel Assembler treates MAIN
                                ; as a reserved keyword so I cannot call the
                                ; C main procedure MAIN!

;******************** 80C196KB Internal Register Map. ************************
; There are 256 registers mapped at addresses 0000H to 0100H. Registers from
; address 0000H to 0019H are pre-defined special purpose registers (SFR) as
; shown below. Registers from 0020H to 0100H are user definable.
;*****************************************************************************
;
; Special Function Register Map:
;
;
R0              EQU 00H:WORD    ;R/W ZERO REGISTER
AD_COMMAND      EQU 02H:BYTE    ;  W A/D Command Register
AD_RESULT       EQU 02H:WORD    ;R   A/D Result Register
HSI_MODE        EQU 03H:BYTE    ;  W High Speed Output Mode Register
HSO_TIME        EQU 04H:WORD    ;  W High Speed Output Time Register
HSI_TIME        EQU 04H:WORD    ;R   High Speed Input Time Register
HSO_COMMAND     EQU 06H:BYTE    ;  W High Speed Output Command Register
HSI_STATUS      EQU 06H:BYTE    ;R   High Speed Input Status Reguster
SBUF            EQU 07H:BYTE    ;R/W Serial Buffer Read = Rx, Write = Tx
INT_MASK        EQU 08H:BYTE    ;R/W Interrupt Mask Register 0
INT_PEND        EQU 09H:BYTE    ;R/W Interrupt Pending Register 1
WATCHDOG        EQU 0AH:BYTE    ;  W Watchdog Timer Register
TIMER1          EQU 0AH:WORD    ;R   Timer 1
IOC2            EQU 0BH:BYTE    ;  W Input/Output Control Register 2
TIMER2          EQU 0CH:WORD    ;R/W Timer 2
IOPORT0         EQU 0EH:BYTE    ;R   Input/Output Port 0
BAUD_RATE       EQU 0EH:BYTE    ;  W Baud Rate Control Register
IOPORT1         EQU 0FH:BYTE    ;R/W Input/Output Port 1
IOPORT2         EQU 10H:BYTE    ;R/W Input/Output Port 2
SP_CON          EQU 11H:BYTE    ;  W Serial Port Control Register
SP_STAT         EQU 11H:BYTE    ;R   Serial Port Status Register
INT_PEND1       EQU 12H:BYTE    ;R/W Interrupt Pending Register 1
INT_MASK1       EQU 13H:BYTE    ;R/W Interrupt Mask Register 1
WSR             EQU 14H:BYTE    ;R   Window Select Register
IOC0            EQU 15H:BYTE    ;  W Input/Output Control Register 0
IOS0            EQU 15H:BYTE    ;R   Input/Output Status Register 0
IOC1            EQU 16H:BYTE    ;  W Input/Output Control Register 1
IOS1            EQU 16H:BYTE    ;R   Input/Output Status Register 1
PWM_CONTROL     EQU 17H:BYTE    ;  W Pulse Width Modulator Control Register
IOS2            EQU 17H:BYTE    ;R   Input/Output Status Register 2
SP              EQU 18H:WORD    ;R/W Stack Pointer
SPARE           EQU 1AH:WORD    ;    Leave some room by SP Reg

PLMREG          EQU 1CH:WORD    ; Temporary Registers Used by the PLM96 Compiler
PLMREG1         EQU 1EH:WORD
PLMREG2         EQU 20H:WORD
PLMREG3         EQU 22H:WORD
;
;*****************************************************************************
; User defined register map:
;
        RSEG                    ; Select Register Segment
;
REG0:           dsw 1           ; Temporary Scratch Register 0
REG1:           dsw 1           ; Temporary Scratch Register 1
REG2:           dsw 1           ; Temporary Scratch Register 2
REG3:           dsw 1           ; Temporary Scratch Register 3
REG4:           dsw 1           ; Temporary Scratch Register 4
;
STACK_IMASK:    dsw 1           ; Interrupt mask register from stack
;
RAM_TEST_RESULT:dsw 1           ; Ram Test Result
SYSCLOCK:       dsw 1           ; System Clock (Ticker)
IOS1_COPY:      dsb 1           ; Copy of IOS1 Register
TEMP_STACK:     dsw 4           ; Small stack for RAM test
;
;*****************************************************************************
;
; The following are masks which can be ANDed and ORed with the INT_MASK
; register to disable or enable respective interrupts.
;
T1_OVFLW_MASK   EQU 00000001B:BYTE      ; Timer 1 Overflow    - bit 0
AD_DONE_MASK    EQU 00000010B:BYTE      ; A/D Conversion Done - bit 1
HSI_DATA_MASK   EQU 00000100B:BYTE      ; HSI Data Available  - bit 2
HSO_INT_MASK    EQU 00001000B:BYTE      ; High Speed Output   - bit 3
HSI_0_INT_MASK  EQU 00010000B:BYTE      ; HSI.0 Pin           - bit 4
SW_TIMER_MASK   EQU 00100000B:BYTE      ; Software Timer      - bit 5
SER_PORT_MASK   EQU 01000000B:BYTE      ; Serial Port         - bit 6
EXTINT_MASK     EQU 10000000B:BYTE      ; External Interrupt  - bit 7
;
; The following are masks which can be ANDed and ORed with the INT_MASK1
; register to disable or enable respective interrupts.
;
TI_INT_MASK     EQU 00000001B:BYTE      ; Transmit Data       - bit 0
RI_INT_MASK     EQU 00000010B:BYTE      ; Receive Data        - bit 1
HSI4_INT_MASK   EQU 00000100B:BYTE      ; 4th HSI FIFO Entry  - bit 2
T2CAPTURE_MASK  EQU 00001000B:BYTE      ; Timer 2 Capture     - bit 3
T2OVERFLOW_MASK EQU 00010000B:BYTE      ; Timer 2 Overflow    - bit 4
EXTINT_MASK1    EQU 00100000B:BYTE      ; External Interrupt  - bit 5
FIFO_FULL_MASK  EQU 01000000B:BYTE      ; HSI FIFO Full       - bit 6
;                                       ; NMI Mask - not used - bit 7
;*****************************************************************************
; push_cregs - macro to save the C context for Interrupt Service Routines
;
push_cregs  MACRO
        PUSH    PLMREG+0
        PUSH    PLMREG+2
        PUSH    PLMREG+4
        PUSH    PLMREG+6
        PUSH    PLMREG+8
        ENDM
;
;*****************************************************************************
; pop_cregs - macro to restore the C context for Interrupt Service Routines
;
pop_cregs   MACRO
        POP     PLMREG+8
        POP     PLMREG+6
        POP     PLMREG+4
        POP     PLMREG+2
        POP     PLMREG+0
        ENDM
;
;*****************************************************************************
; power up reset vector - software start location
;
reset   EQU 2080H               ; Power up reset vector location
        CSEG AT reset           ; Place code at RESET location

        LD  REG0, #10000        ; 25 Millisecond delay

startup_delay:
        DEC REG0                ; to allow everything
        JNE startup_delay       ; to power up

        LD SP,#TEMP_STACK       ; Load initial stack pointer for RAM test

        SCALL external_ram_test ; Test external RAM
        SCALL external_ram_clear; Clear external RAM

        LD SP,#STACK            ; Load real stack pointer
                                ; Initialize System Ticker & Watchdog timer
        SCALL initialize_sysclock
        SCALL led_sw_timer      ; Initialize LED Flasher Software Timer 2

        EI                      ; Enable Interrupts

        CALL mainLoop           ; start C main procedure
                                ; mainLoop did an exit

        SCALL reset_processor   ; Restart the processor
                                ; We never get here
here:   sjmp here               ; repeat for ever

;*****************************************************************************
;
; external_ram_test: Writes a sequence of alternating 0's and 1's to
; external RAM and reads back the result. The compliment is written
; and read also. This will destroy any data in RAM so care should be
; taken not to walk on the STACK or other data structures. On exit
; RAM_TEST_RESULT will contain PASSED (0) or the address of the bad
; RAM location if FAILED.
;
;*****************************************************************************
;
RAM_TEST_WORD   EQU  5555H          ; Alternating 0's and 1's

external_ram_test:
        LD  REG1,#RAM_BASE_ADDR     ; Base Address of RAM segment

        LD  REG0,#RAM_TEST_WORD     ; Fetch RAM Test Word
        LD  REG2,#NOT RAM_TEST_WORD ; And the compilment

test_next_word:
        ST  REG0,[REG1]             ; Write test value into RAM
        CMP REG0,[REG1]             ; Read back and compare
        JNE ram_test_failed         ; Not the same so failure

        ST  REG2,[REG1]             ; Write test value into RAM
        CMP REG2,[REG1]+            ; Read back and compare
        JNE ram_test_failed         ; Not the same so failed
                                    ; Have we tested all the RAM
        CMP REG1,#RAM_BASE_ADDR+RAM_SIZE
        JLT test_next_word          ; If not, then test next word

ram_test_passed:                    ; RAM passed read/write test
        CLR RAM_TEST_RESULT         ; Test passed so result = 0
        RET

ram_test_failed:
        LD RAM_TEST_RESULT,REG1     ; RAM failed read/write test
        RET                         ; result is the address
                                    ; of the bad RAM location


external_ram_clear:
        LD  REG1,#RAM_BASE_ADDR     ; Base Address of RAM segment

clear_next_word:
        ST  R0,[REG1]+              ; Write test value into RAM
                                    ; Have we cleared all the RAM
        CMP REG1,#RAM_BASE_ADDR+RAM_SIZE
        JLT clear_next_word          ; If not, then clear next word
        RET

;*****************************************************************************
;
; initialize_sysclock: Resets the system ticker (SYSCLK) and initializes
; Software Timer 0 to provide approximately a 10 millisecond resolution
; ticker. User code should use the SYSCLOCK register for timing. Software
; Timer 0 uses Timer 1 which is clocked at the crystal frequency divided
; by 16. At 10 MHz this would be a clock rate of 1.6 microseconds. The
; time taken to service the interrupt and re-load HSI_TIMER is 138 state
; times. At 10 MHz this is 138 * 0.0000002 = 0.0000276. To achieve a 10
; Millisecond ticker, (0.010-0.0000276) / 0.0000016 = 6232 counts.
;
;*****************************************************************************

SYSCLK_RATE EQU 6232                ; See discussion above

initialize_sysclock:
                                    ; Disable S/W Timer Interrupt
        ANDB INT_MASK,#NOT SW_TIMER_MASK
        CLR  SYSCLOCK               ; Reset System Ticker to zero
        CLR  IOS1_COPY              ; Clear IOS1 Copy Register

IF USE_TIMER_CHAIN
        LCALL initTimerChain        ; Initialize timer chain
ENDIF

        SCALL sysclock_sw_timer     ; Load Software Timer 0
        ORB INT_MASK,#SW_TIMER_MASK ; Enable S/W Timer Interrupt
        RET

;*****************************************************************************
;
; sysclock_sw_timer: Re-loads software timer 0 with a command to generate
; a 10 millisecond delay. This code is called at startup and from the
; software timer interrupt service routine.
;
sysclock_sw_timer:
        JBS IOS0, 7, sysclock_sw_timer
        LDB HSO_COMMAND,#18H        ; Initialize Software Timer 0
                                    ; Set up for 10 Msec Resolution
        ADD HSO_TIME,TIMER1,#SYSCLK_RATE
        RET

;*****************************************************************************
;
; read_sysclock - reads the current value of the system ticker and returns
; the value to C.
;
read_sysclock:
        LD PLMREG,SYSCLOCK     ; Return SYSCLOCK to C
        RET
;
;****************************************************************************
; reset_processor - reset the processor and pulse the reset line
;
reset_processor:
        RST                         ; Reset processor
        RET                         ; We never get here, but just to be tidy
;
;****************************************************************************
;
; led_sw_timer: Re-loads software timer 1 with a command to generate
; a 100 millisecond delay. This code is called at startup and from the
; software timer interrupt service routine.
;
LED_CLK_RATE EQU 62320              ; 10 * 6232 (Rate for 10 Msec ticker)

led_sw_timer:
        JBS IOS0, 7, led_sw_timer
        LDB HSO_COMMAND,#19H        ; Initialize Software Timer 1
                                    ; Set up for 100 Msec Resolution
        ADD HSO_TIME,TIMER1,#LED_CLK_RATE
        RET

;****************************************************************************
; Stub for Interrupt Service Routines

isr_dummy:
        RET

;****************************************************************************

SW_TIMER0       EQU 0               ; Mask to test for SWT 0
SW_TIMER1       EQU 1               ; Mask to test for SWT 1
SW_TIMER2       EQU 2               ; Mask to test for SWT 2
SW_TIMER3       EQU 3               ; Mask to test for SWT 3
SW_TIMER_ALL    EQU 0FH             ; All Software timers

software_timer_isr:
        PUSHA                       ; Save PSW and INT_MASK1
        PUSH STACK_IMASK            ; Save register used for function

        EI                          ; Re-enable '196 Global Interrupts
        LDB STACK_IMASK, 3[SP]      ; Fetch saved INT_MASK1 from stack

                                    ; Re-enable Serial Port Interrupts
        ANDB INT_MASK1, STACK_IMASK, #RI_INT_MASK + TI_INT_MASK

        push_cregs                  ; Save C Context

IF USE_WATCHDOG_TIMER               ; Enable Watchdog timer the 1st time
        LDB WATCHDOG,#01EH          ; and reset the Watchdog Timer
        LDB WATCHDOG,#0E1H
ENDIF
        ORB IOS1_COPY,IOS1          ; Read IO Status Register 1
        JBC IOS1_COPY,SW_TIMER0,swt1; Test for Software Timer 0

swt0:                               ; Software Timer 0 Code
        INC SYSCLOCK                ; Increment System Ticker
        SCALL sysclock_sw_timer     ; Re-Load Software Timer 0

IF USE_TIMER_CHAIN
        LCALL timerEvent            ; Call timer chain event handler
ENDIF

swt1:                               ; Test for Software Timer 1
        JBC IOS1_COPY,SW_TIMER1,swt2; Software Timer 1 Code

        LCALL toggleLED             ; Change LED State
        SCALL led_sw_timer          ; Re-load Software Timer 2

swt2:                               ; Test for Software Timer 2
        JBC IOS1_COPY,SW_TIMER2,swt3; Software Timer 2 Code
        LCALL sw_timer_2_hook

swt3:                               ; Test for Software Timer 3
        JBC IOS1_COPY,SW_TIMER3,stexit
        LCALL sw_timer_3_hook

                                    ; Software Timer 3 Code
stexit:                             ; Clear SW Timer Flags
        ANDB IOS1_COPY,#NOT SW_TIMER_ALL

        pop_cregs                   ; Restore C Context
        POP STACK_IMASK             ; Restore temp register
        POPA                        ; Restore PSW and INT_MASK1
        RET

;****************************************************************************
;
receiver_isr:
        PUSHA
        push_cregs                  ; Save C environment registers
        LCALL receiveByte
        pop_cregs                   ; Restore C environment registers
        POPA
        RET

;****************************************************************************

MEM_SIZE:   DCW ?MEMORY_SIZE
MEM_START:  DCW MEMORY

;****************************************************************************
;
prog_pulse_width    EQU 2014H   ; Address of PPW in ROM

            CSEG    at prog_pulse_width
            DCB     150

;****************************************************************************

        END
