COMMENT *

This file is used to generate DSP code for the utility board. It will time
     the exposure, operate the shutter, control the CCD temperature and 
     turn the analog power on. This is Rev. 3.00 software. 
Modified 1-12-97 for 10 MHz input clock frequency by adding 2 to elapsed
     exposure time rather than one. 
Power ON sequence written for Gen II power control board, Rev. 4A

-d DOWNLOAD 'HOST'	To generate code for downloading to DSP memory.
-d DOWNLOAD 'ROM'	To generate code for writing to the ROM.

	*
        PAGE    132	; Printronix page width - 132 columns

; Name it a section so it doesn't conflict with other application programs
	SECTION	UTILTEST

;  These are also defined in "utilboot.asm", so be sure they agree
APL_NUM	EQU	1	; Application number from 1 to 10
APL_ADR	EQU	$90	; Starting address of application program
BUF_STR	EQU	$80	; Starting address of buffers in X:
BUF_LEN	EQU	$20	; Length of buffers
SSI_BUF	EQU	BUF_STR		; Starting address of SCI buffer in X:
COM_BUF EQU     SSI_BUF+BUF_LEN	; Starting address of command buffer in X:
COM_TBL EQU     COM_BUF+BUF_LEN	; Starting address of command table in X:

;  Define some useful constants
APL_XY	EQU	$1EE0	; Starting address in EEPROM of X: and Y: values
DLY_MUX EQU     70      ; Number of DSP cycles to delay for MUX settling
DLY_AD  EQU     100     ; Number of DSP cycles to delay for A/D settling

; Assign addresses to port B data register
PBD     EQU     $FFE4   ; Port B Data Register
IPR     EQU     $FFFF   ; Interrupt Priority Register

;  Addresses of memory mapped components in Y: data memory space
;  Write addresses first
WR_DIG  EQU     $FFF0   ; was $FFFF  Write Digital output values D00-D15
WR_MUX  EQU     $FFF1   ; Select MUX connected to A/D input - one of 16
EN_DIG	EQU	$FFF2	; Enable digital outputs
WR_DAC3 EQU     $FFF7   ; Write to DAC#3 D00-D11
WR_DAC2 EQU     $FFF6   ; Write to DAC#2 D00-D11
WR_DAC1 EQU     $FFF5   ; Write to DAC#1 D00-D11
WR_DAC0 EQU     $FFF4   ; Write to DAC#0 D00-D11

;  Read addresses next
RD_DIG  EQU     $FFF0   ; Read Digital input values D00-D15
STR_ADC EQU     $FFF1   ; Start ADC conversion, ignore data
RD_ADC  EQU     $FFF2   ; Read A/D converter value D00-D11
WATCH   EQU     $FFF7   ; Watch dog timer - tell it that DSP is alive

;  Bit definitions of STATUS word
ST_SRVC	EQU     0       ; Set if ADC routine needs executing
ST_EX   EQU     1       ; Set if timed exposure is in progress
ST_SH   EQU     2       ; Set if shutter is open
ST_READ EQU     3	; Set if a readout needs to be initiated
STRT_EX	EQU	4	; Set to indicate start of exposure

; Bit definitions of software OPTIONS word
OPT_SH  EQU     0       ; Set to open and close shutter

;  Bit definitions of Port B = Host Processor Interface
PWR_EN1	EQU     0       ; Power enable bit ONE - Output
PWR_EN0	EQU     1       ; Power enable bit ZERO  - Output
PWRST	EQU     2       ; Reset power conditioner counter - Output
SHUTTER EQU     3       ; Control shutter - Output
IRQ_T   EQU     4       ; Request interrupt service from timing board - Output
SYS_RST EQU     5       ; Reset entire system - Output
WATCH_T EQU     8       ; Processed watchdog signal from timing board - Input

;**************************************************************************
;                                                                         *
;    Register assignments  						  *
;	 R1 - Address of SCI receiver contents				  *
;	 R2 - Address of processed SCI receiver contents		  *
;        R3 - Pointer to current top of command buffer                    *
;        R4 - Pointer to processed contents of command buffer		  *
;	 N4 - Address for internal jumps after receiving 'DON' replies	  *
;        R0, R5, R6, A, X0, X1 - For use by program only                  *
;	 R7 - For use by SCI ISR only					  *
;        Y0, Y1, and B - For use by timer ISR only. If any of these	  *
;		registers are needed elsewhere they must be saved and	  *
;	        restored in the TIMER ISR.                        	  *
;**************************************************************************

; Specify execution and load addresses.
	IF	@SCP("DOWNLOAD","HOST")
	ORG	P:APL_ADR,P:APL_ADR		; Download address
	ELSE
        ORG     P:APL_ADR,P:APL_NUM*$300+$1D00	; EEPROM generation
	ENDIF


; The TIMER addresses must be defined here and SERVICE must follow to match
;   up with the utilboot code
;	JMP	<SERVICE		; Millisecond timer interrupt
	RTS

;  The SERVICE address must be defined here to match up with the utilboot code,
;     and the TIMER routine must follow. They are both do-nothing returns. 
	RTS				; Just return from the subroutine call
TIMER	RTI				; RTI for now so downloading works

; Shutter support subroutines for the TIMER executive
;   Also support shutter connection to timing board for now.
OSHUT	BCLR    #SHUTTER,X:PBD  ; Clear Port B bit #3 to open shutter
        BCLR    #ST_SH,X:<STATUS ; Clear status bit to mean shutter open
        RTS

CSHUT	BSET    #SHUTTER,X:PBD  ; Set Port B bit #3 to close shutter
        BSET    #ST_SH,X:<STATUS ; Set status to mean shutter closed
        RTS

; These are called directly by command, so need to call subroutines in turn
OPEN	JSR	OSHUT		; Call open shutter subroutine
	JMP	<FINISH		; Send 'DON' reply
CLOSE	JSR	CSHUT		; Call close shutter subroutine
	JMP	<FINISH		; Send 'DON' reply

; Test D/A converter
TDA	MOVE	#CTDA,N4	; Set internal jump address	

CTDA	MOVEP	#$C000,X:IPR	; Disable timer interrupts
CTDA1	MOVEP	Y:WATCH,B	; Reset watchdog timer
	CLR 	B
	MOVE	X:<ONE,X0	; DAC increment value
	MOVE	#>$1000,Y0	; Cycle through all 12 bits of the DAC
	DO	Y0,D_TDA
	MOVE	#>$FFF4,R0
	REP	#4		; Update the four DACs in turn
	MOVE	B,Y:(R0)+	; Write to DAC
	ADD	X0,B		; B = B + 1
D_TDA
	MOVE	R1,A		; Keep executing TDA if a new command
	MOVE	R2,X0		;   has not come in
	CMP	X0,A
	JEQ	CTDA1		
	MOVEP	#$8007,X:IPR	; Re-enable timer interrrupts
	JMP	<START

; Test A/D converter
TAD	MOVE	#CTAD,N4	; Set internal jump address	

CTAD	MOVEP	#$C000,X:IPR	; Disable timer interrupts
CTAD1	MOVE	#>$3E8,B	; Starting DAC value = 1000
	MOVE	X:<ONE,X0	; Increment DAC value by one each cycle
	MOVE	#>$7D0,Y0	; So ending DAC value = $BB8 = 3000
	MOVE	#WR_DAC2,R0	; Select DAC2
	MOVE	#WR_DAC3,R5	; Select DAC3	
	MOVEP	#>$9,Y:WR_MUX	; Set MUX to select correct input to A/D
	DO	Y0,L_TAD1
	MOVE	B,Y:(R0)	; Write original value to DAC2
	REP	#DLY_MUX	; Delay for MUX to settle
	NOP
        MOVEP   Y:STR_ADC,X1    ; Start A/D conversion - dummy read
	REP	#DLY_AD		; Delay for A/D to settle
	NOP
        MOVEP   Y:RD_ADC,Y:(R5)	; Get the A/D value and write it to DAC3
	ADD	X0,B
	MOVEP	Y:WATCH,X1	; Reset watchdog timer
L_TAD1
	MOVE	R1,A		; Keep executing TAD if a new command
	MOVE	R2,X0		;   has not come in
	CMP	X0,A
	JEQ	CTAD1 
	MOVEP	#$8007,X:IPR	; Re-enable timer interrupts
	JMP	<START

; Test digital I/O
TDG	MOVEP	#1,Y:EN_DIG	; Enable digital outputs
	MOVE	#CTDG,N4	; Set internal jump address	

CTDG	MOVEP	#$C000,X:IPR	; Disable interrupts
CTDG1	MOVEP	#0,Y:WR_DIG	; Clear all outputs to make it look clean
	MOVEP	Y:WATCH,A	   ; Reset watchdog timer
	REP	#<$FA		; Delay just to make the waveforms
	CLR	A		;   look nicer
	MOVEP	#$FFFF,Y:WR_DIG	; Put a "start" bit to make scope
	REP	#5		;    trigerring easier
	NOP
	MOVEP	#$0000,Y:WR_DIG
	REP	#10
	NOP
	MOVEP	Y:RD_DIG,A1	; Read 16-bit digital number
	DO	#16,D_TDG
	MOVEP	A1,Y:WR_DIG	; Write 16-bit digital number
	LSR	A		; Shift right one bit
	JCS	SET_15		; Need to rotate 16 bits,
	BCLR	#15,A1		;  not 24
	JMP	CLR_15
SET_15	BSET	#15,A1
CLR_15	NOP
D_TDG
	MOVE	R1,A		; Check for input SCI commands
	MOVE	R2,X0
	CMP	X0,A
	JEQ	CTDG1		; Keep going if no commands
	MOVEP	#$8007,X:IPR	; Restore interrupts
	JMP	<START		; Go get the command

;  **************  BEGIN  COMMAND  PROCESSING  ***************
; Subroutine to turn analog power OFF
PWR_OFF_SUBROUTINE
	MOVE	X:<TIMING,A
	MOVE	A,X:(R3)+       ; Header from Utility to timing board
	MOVE	Y:<CSW,A
	MOVE	A,X:(R3)+       ; Clear all analog switches
	MOVE	#*+3,N4		; Set internal jump address after 'DON'
	JMP	<XMT_CHK	; Send the command to the timing board

; Instruct the power control board to turn the analog power switches OFF
	BSET	#9,X:PBDDR	; Make sure PWREN is an input
	IF	@SCP("POWER","R6")
	BSET	#LVEN,X:PBD	; LVEN = HVEN = 1 => Power reset
	BSET	#PWRST,X:PBD
	BSET	#HVEN,X:PBD
	ELSE			; Earlier Revision power control boards
	BSET	#PWRST,X:PBD	; Reset power control board
	REP	#30
	NOP
	BCLR	#PWRST,X:PBD
	ENDIF

	RTS			; Return from PWR_OFF_SUBROUTINE

; Power OFF command execution
PWR_OFF	MOVEP   #$2000,X:IPR    	; Disable TIMER interrupts
	JSR	<PWR_OFF_SUBROUTINE
	JMP	<PWR_END		; Reply 'DON'

; Start power-on cycle
PWR_ON	MOVEP   #$2000,X:IPR    ; Disable TIMER interrupts
	JSR	<PWR_OFF_SUBROUTINE ; Turn everything OFF

; The clocks must be not clocking during power ON because of the A/D converter	
	MOVE	X:<TIMING,A
	MOVE	A,X:(R3)+       ; Header from Utility to timing
	MOVE	Y:<STP,A
	MOVE	A,X:(R3)+       ; Stop the clocks during power on
	MOVE	#*+3,N4		; Set internal jump address after 'DON'
	JMP	<XMT_CHK	; Send the command to the timing board

; Wait for at least one cycle of serial and parallel clocks for the STP 
;   command to take effect
	MOVE	#30000,X0
	DO      X0,WT_PON1	; Wait 30 millisec or so for settling
        REP	#5
	MOVEP	Y:WATCH,X0	; Reset watchdog timer
WT_PON1

; Now turn ON the low voltages (+/- 6.5V, 16.5V) 
	IF	@SCP("POWER","R6")
	BCLR	#LVEN,X:PBD	; LVEN = Low => Turn on +/- 6.5V, +/- 16.5V
	BCLR	#PWRST,X:PBD
	ELSE
	BSET	#LVEN,X:PBD	; Make sure line is high to start with
	DO	#255,L_PON1	; The power conditioner board wants to
	BCHG    #LVEN,X:PBD	;   see 128 H --> L transitions
	NOP			; Backplane signal settling time delay
L_PON1
	ENDIF
	JSR	<PWR_DLY	; Delay for a little while
	MOVEP   #2,Y:WR_MUX     ; Select +15V MUX input
	MOVE	#65000,X0
	DO      X0,WT_PON2	; Wait 20 millisec or so for settling
	REP	#5
	MOVEP	Y:WATCH,X0	; Reset watchdog timer
WT_PON2
        MOVEP   Y:STR_ADC,X0    ; Start A/D conversion - dummy read
        DO	#DLY_AD,L_PON2	; Wait for the A/D to settle
        CLR     A  X:<CFFF,X0	; This saves some space
L_PON2
        MOVEP   Y:RD_ADC,A1     ; Get the A/D value
        AND     X0,A  Y:<T_P15,X0 ; A/D is only valid to 12 bits

; Test that the voltage is in the range abs(initial - target) < margin
        SUB     X0,A  A1,Y:<I_P15
        ABS     A  Y:<K_P15,X0
        SUB     X0,A
        JGT     <PERR           ; Take corrective action

TST_M15 MOVEP   #3,Y:WR_MUX     ; Select -15v MUX input
        DO	#DLY_MUX,L_PON3	; Wait for the MUX to settle
        NOP
L_PON3
        MOVEP   Y:STR_ADC,X0    ; Start A/D conversion - dummy read
        DO	#DLY_AD,L_PON4	; Wait for the A/D to settle
        CLR     A  X:<CFFF,X0	; Clear A, so put it in DO loop
L_PON4
        MOVEP   Y:RD_ADC,A1     ; Get the A/D value
        AND     X0,A  Y:<T_M15,X0 ; A/D is only valid to 12 bits

; Test that the voltage is in the range abs(initial - target) < margin
        SUB     X0,A  A1,Y:<I_M15
        ABS     A  Y:<K_M15,X0
        SUB     X0,A
        JGT     <PERR

; Now turn on the high voltage HV (nominally +36 volts)
	IF	@SCP("POWER","R6")
HV_ON	BCLR	#HVEN,X:PBD	; HVEN = Low => Turn on +36V
	ELSE
HV_ON	BSET	#HVEN,X:PBD	; Make sure line is high to start with
	DO	#255,L_PON5	; The power conditioner board wants to
	BCHG    #HVEN,X:PBD	;   see 128 H --> L transitions
L_PON5
	ENDIF

	JSR	<PWR_DLY	; Delay for a little while	
	MOVEP   #1,Y:WR_MUX     ; Select high voltage MUX input
	MOVE	#65000,X0
	DO      X0,WT_HV	; Wait a few millisec for settling
	NOP
WT_HV
	MOVEP   Y:STR_ADC,X0    ; Start A/D conversion - dummy read
	DO	#DLY_AD,L_PON6	; Wait for the A/D to settle
	CLR     A  X:<CFFF,X0	; Clear A, so put it in DO loop
L_PON6
	MOVEP   Y:RD_ADC,A1     ; Get the A/D value
	AND     X0,A  Y:<T_HV,X0 ; A/D is only valid to 12 bits

; Test that the voltage is in the range abs(initial - target) < margin
	SUB     X0,A  A1,Y:<I_HV
	ABS     A  Y:<K_HV,X0
	SUB     X0,A
	JGT     <PERR           ; Take corrective action

; Start up the clocks and DC biases from the timing board
	MOVE	X:<TIMING,A
	MOVE	A,X:(R3)+       ; Header from Utility to timing
	MOVE	Y:<IDL,A
	MOVE	A,X:(R3)+       ; Start up the clock drivers
	MOVE	X:<TIMING,A
	MOVE	A,X:(R3)+       ; Header from Utility to timing
	MOVE	Y:<SBV,A
	MOVE	A,X:(R3)+       ; Set bias voltages

; Reply with a DONE message to the host computer
PWR_END	MOVE    X:<HOST,A
	MOVE    A,X:(R3)+       ; Header to host
	MOVE    X:<DON,A
	MOVE    A,X:(R3)+       ; Power is now ON
	MOVEP   #$2007,X:IPR    ; Enable TIMER interrupts
	JMP     <XMT_CHK	; Go transmit reply

; Or, return with an error message
PERR	JSR	<PWR_OFF_SUBROUTINE 	; Turn power OFF if there's an error
	MOVE    X:<HOST,A
	MOVE    A,X:(R3)+       ; Header to host
	MOVE    X:<ERR,A
        MOVE    A,X:(R3)+	; Power is ON
	MOVEP   #$2007,X:IPR    ; Enable TIMER interrupts
	JMP     <XMT_CHK	; Go transmit reply

; Delay between power control board instructions
PWR_DLY	DO	#4000,L_DLY
	NOP			
L_DLY
	RTS

; Start an exposure by first issuing a 'CLR' to the timing board
START_EX
	MOVE	X:<TIMING,A
	MOVE	A,X:(R3)+       ; Header from Utility to timing
	MOVE	Y:<CLR,A
	MOVE	A,X:(R3)+       ; Clear the CCD
	MOVE	#DONECLR,N4	; Set internal jump address after 'DON'
	JMP	<XMT_CHK	; Transmit these

; Come to here after timing board has signaled that clearing is done
DONECLR	BSET	#STRT_EX,X:<STATUS
	BSET    #ST_EX,X:<STATUS ; Exposure is in progress
	MOVE	X:<HOST,A
	MOVE	A,X:(R3)+
	MOVE	X:<DON,A
	MOVE	A,X:(R3)+
	JMP     <XMT_CHK	; Issue a 'DON' - exposure has begun

PAUSE   BCLR    #ST_EX,X:<STATUS ; Take out of exposing mode
        JSSET   #OPT_SH,X:<OPTIONS,CSHUT ; Close shutter if needed
        JMP     <FINISH		; Issue 'DON' and get next command

RESUME	BSET    #ST_EX,X:<STATUS ; Put in exposing mode
	JSSET   #OPT_SH,X:<OPTIONS,OSHUT ; Open shutter if needed
        JMP     <FINISH		; Issue 'DON' and get next command

ABORT	BCLR    #ST_EX,X:<STATUS ; Take out of exposing mode
	MOVE    X:<TIMING,A
	MOVE    A,X:(R3)+       ; Header from Utility to timing
	MOVE    Y:<IDL,A
	MOVE    A,X:(R3)+       ; Put timing board in IDLE mode
	JSR     <CSHUT          ; To be sure
	JMP     <FINISH		; Issue 'DON' and get next command

; A 'DON' reply has been received in response to a command issued by
;    the Utility board. Read the X:STATUS bits in responding to it.

; Test if an internal program jump is needed after receiving a 'DON' reply
PR_DONE	MOVE	N4,R0		; Get internal jump address
	MOVE	#<START,N4	; Set internal jump address to default
	JMP	(R0)		; Jump to the internal jump address

; Check for program overflow - its hard to overflow since this application
;   can be very large indeed
	IF	@CVS(N,*)>APL_XY
        WARN    'Application P: overflow!'	; Make sure next application
	ENDIF					;  will not be overwritten

; Command table resident in X: data memory
;  The last part of the command table is not defined for "bootrom"
;     because it contains application-specific commands

	IF	@SCP("DOWNLOAD","HOST")
	ORG	X:COM_TBL,X:COM_TBL
	ELSE			; Memory offsets for generating EEPROMs
        ORG     P:COM_TBL,P:APL_NUM*$300+$1D00+($200-APL_ADR)
	ENDIF
	DC	'PON',PWR_ON	; Power ON
	DC      'POF',PWR_OFF	; Power OFF
	DC	'TAD',TAD	; Test A/D
	DC	'TDA',TDA	; Test D/A
	DC	'TDG',TDG	; Test Digital I/O
	DC	'SEX',START_EX	; Start exposure
	DC	'PEX',PAUSE	; Pause exposure
	DC	'REX',RESUME	; Resume exposure
	DC	'AEX',ABORT	; Abort exposure
	DC	'OSH',OPEN	; Open shutter
	DC	'CSH',CLOSE	; Close shutter
	DC      'DON',PR_DONE	; Process DON reply
	DC	0,START,0,START,0,START,0,START

; Y: parameter table definitions, containing no "bootrom" definitions
	IF	@SCP("DOWNLOAD","HOST")
	ORG	Y:0,Y:0		; Download address
	ELSE
        ORG     Y:0,P:		; EEPROM address continues from P: above
	ENDIF
DIG_IN  DC      0       ; Values of 16 digital input lines
DIG_OUT DC      0       ; Values of 16 digital output lines
DAC0    DC      0       ; Table of four DAC values to be output
DAC1    DC      1000               
DAC2    DC      2000            
DAC3    DC      3000            
NUM_AD  DC      16      ; Number of inputs to A/D converter
AD_IN   DC      0,0,0,0,0,0,0,0
        DC      0,0,0,0,0,0,0,0 ; Table of 16 A/D values
EL_TIM_MSECONDS  DC      0       ; Number of milliseconds elapsed
TGT_TIM DC      6000    ; Number of milliseconds desired in exposure
U_CCDT  DC      $C20    ; Upper range of CCD temperature control loop
L_CCDT  DC      $C50    ; Lower range of CCD temperature control loop
K_CCDT  DC      85      ; Constant of proportionality for CCDT control
A_CCDT  EQU     AD_IN+5 ; Address of CCD temperature in A/D table
T_CCDT	DC	$0FFF	; Target CCD T for small increment algorithmn
T_COEFF	DC	$010000	; Coefficient for difference in temperatures
DAC0_LS	DC	0	; Least significant part of heater voltage

; Define power supply turn-on variables
	IF	@SCP("POWER","R6")
T_HV	DC      $240    ; Target HV supply voltage for Rev 6 pwr contl board
	ELSE
T_HV	DC      $4D0    ; Target HV supply voltage for Rev 2 or 3 boards
	ENDIF
K_HV	DC      $080    ; Tolerance of HV supply voltage
T_P15   DC      $5C0    ; Target +15 volts supply voltage
K_P15   DC      $080     ; Tolerance of +15 volts supply voltage
T_M15   DC      $A40    ; Target -15 volts supply voltage
K_M15   DC      $080     ; Tolerance of -15 volts supply voltage
I_HV	DC      0       ; Initial value of HV
I_P15   DC      0       ; Initial value of +15 volts
I_M15   DC      0       ; Initial value of -15 volts

; Define some command names
CLR     DC      'CLR'   ; Clear CCD
RDC     DC      'RDC'   ; Readout CCD
ABR     DC      'ABR'   ; Abort readout
OSH     DC      'OSH'   ; Open shutter connected to timing board
CSH     DC      'CSH'   ; Close shutter connected to timing board
POK     DC      'POK'   ; Message to host - power in OK
PER     DC      'PER'   ; Message to host - ERROR in power up sequence
SBV	DC	'SBV'	; Message to timing - set bias voltages
IDL	DC	'IDL'	; Message to timing - put camera in idle mode
STP	DC	'STP'	; Message to timing - Stop idling
CSW	DC	'CSW'	; Message to timing - clear switches

; Miscellaneous
CC00	DC	$C00	; Maximum heater voltage so the board doesn't burn up
SV_A1	DC	0	; Save register A1 during analog processing
SV_SR	DC	0	; Save status register during timer processing
EL_TIM_FRACTION DC 0	; Fraction of a millisecond of elapsed exposure time
INCR	DC	$CCCCCC	; Exposure time increment = 0.8 milliseconds
SH_DEL	DC	10	; Shutter closing time
TEMP	DC	0	; Temporary storage location for X:PBD word

; During the downloading of this application program the one millisecond 
;   timer interrupts are enabled, so the utility board will attempt to execute 
;   the partially downloaded TIMER routine, and crash. A workaround is to 
;   put a RTI as the first instruction of TIMER so it doesn't execute, then 
;   write the correct instruction only after all the rest of the application 
;   program has been downloaded. Here it is - 

; Specify execution and load addresses.
	IF	@SCP("DOWNLOAD","HOST")
	ORG	P:APL_ADR,P:APL_ADR		; Download address
	ELSE
        ORG     P:APL_ADR,P:APL_NUM*$300+$1D00	; EEPROM generation
	ENDIF
	JMP	<SERVICE			; Millisecond timer interrupt
	MOVEC	SR,Y:<SV_SR 			; Save Status Register

	ENDSEC		; End of SECTION UTILAPPL

; End of program
        END