; ==== ISUAL Flight Software -- Univ of California, Berkeley, SSL ====
; MMCB.A -- manage Mass Memory allocations
; 
; Revision History:
;  18-Dec-2002 - SPG - ISUAL Flight Software rev 7.3
;    First Official Release
;  12-Jan-2003
;    added GBL_RO_IDLE
;  14..25-Feb-2003 - SG
;    make sure Camera power is on before testing Day Sensor
;    Add MMCB_REARM function
;    eliminate NEV and IS_TRIG
;    New layout of GBL_MMCB_MISC
;    General Rewrite of Buffer-Switch ISR and Supply Task
;    Fix bugs in exposure-time list capture
;  13-Mar-2003
;    Fix "rar" bit bug in SEND16
;   6-Aug-2003
;    Ignore readout interrupts if not in science mode
;    Set current science mode in MMCB when appending to DCM Q
;  13-Aug-2003
;    fix introduced bug in SEND16
;  17-Aug-2003
;    store exposure pointer in mmcb_eq in any mode
;    fix bug in AP processing; was zeroing spf_nev instead of aph_nev
;    neutered HV protection check
; 21-Oct-2003 - remove debug 'I', 'S', 's', 'A' and '@'
;
; 11-Feb-2004 - restore ignore if not in science mode
;  (for camera only)
; 17-May-2005 (FSW 7.9)
; - remove some debug displays
        include mmcb.i
        include immcb.i
        include gbl.i
        include util.i
        include task.i
        include cdi.i
        include soh.i
        include debug.i
        include ioports.i
        include eeprom.i

G  equ GBL_MMCB_MISC  ; (100 bytes allocated in GBL.i)
first_ram_addr equ G ; ds 3  ; banked memory address of 1st MMCB

; which R/O register needs to be set
; (on a buffer-switch interrupt)
which_cam     equ G+3  ; ds 1  
which_spf     equ G+4  ; ds 1
which_sps     equ G+5  ; ds 1
which_aph     equ G+6  ; ds 1

; Camera
CAM_SUPPLY_Q  equ G+7  ; ds 3      ; MMCB pointer used by Supply Task
cam_RO_done   equ G+10 ; ds 1      ; incremented on each camera buffer switch
cam_efetch    equ G+11 ; ds 2      ; private fetch-pointer to the Event queue
cam_nev       equ G+13 ; ds 1      ; number of events since last readout
cam_trigtime  equ G+14 ; ds 6      ; time of trigger event

; Spectrophotometer
SPF_SUPPLY_Q  equ G+20 ; ds 3      ; MMCB pointer used by Supply Task
spf_RO_done   equ G+23 ; ds 1      ; incremented on each SPF buffer switch 
spf_efetch    equ G+24 ; ds 2      ; private fetch-pointer to the Event queue
spf_nev       equ G+26 ; ds 1      ; number of events since last readout
spf_trigtime  equ G+27 ; ds 6      ; time of trigger event

; Slow-rate spectrophotometer
SPS_SUPPLY_Q  equ G+33 ; ds 3      ; MMCB pointer used by Supply Task
sps_RO_done   equ G+36 ; ds 1      ; incremented on each SPS buffer switch 
sps_efetch    equ G+37 ; ds 2      ; private fetch-pointer to the Event queue
sps_nev       equ G+39 ; ds 1      ; number of events since last readout
sps_trigtime  equ G+40 ; ds 6      ; time of trigger event

; Array Photometer
APH_SUPPLY_Q  equ G+46 ; ds 3      ; MMCB pointer used by Supply Task
aph_RO_done   equ G+49 ; ds 1      ; incremented on each AP buffer switch 
aph_efetch    equ G+50 ; ds 2      ; private fetch-pointer to the Event queue
aph_nev       equ G+52 ; ds 1      ; number of events since last readout
aph_trigtime  equ G+53 ; ds 6      ; time of trigger event

mm            equ G+59 ; ds 4  ; last mmalloc'd
mmnext        equ G+64 ; ds 4  ; next to be mmalloc'd

ram           equ G+68 ; ds 3  ; banked RAM addr of last MMCB allocated
new_mmcb      equ ram
ramnext       equ G+71 ; ds 3  ; addr of next MMCB

event_time    equ G+74 ; ds 6
qtime         equ G+80 ; ds 6
etqp          equ G+86 ; ds 2  ; queue pointer for start of exposure-times

cam_fo        equ G+88 ; ds 1  ; 1=next readout is Follow-On

;****copied SEND16 and SEND26 from 7.6 ******
; HL - points to 32-bit MM address
; A  - CDI op code      
; Extract high 16 bits of address
; and use CDI to send it
SEND16:
        push psw        ; save op

; HL points to 4 bytes:
;  000000xx xxxxxxxx xxxxxx00 00000000
; Extract the 16 'x' bits
        mov a,M         ; 000000xx
        inx h
        mov d,M         ; xxxxxxxx
        inx h
        mov e,M         ; xxxxxx00
        xchg
; AHL = 000000xx xxxxxxxx xxxxxx00
; shift AHL right 2 bits
;  first shift
        rar             ; rotate right thru carry
        mov b,a         ; B=A
        mov a,h
        rar
        mov h,a
        mov a,l
        rar
        mov l,a
; BHL = -------x xxxxxxxx xxxxxxx0
;  second shift
        mov a,b         
        rar             ; rotate right thru carry
        mov a,h
        rar
        mov h,a
        mov a,l
        rar
        mov l,a
; HL = xxxxxxxx xxxxxxxx
        pop psw         ; restore op
        call CDI_AHL    ; send the CDI
        ret

; HL - points to 32-bit MM address
; A  - CDI op code for top 16 bits
; [A]+1 - CDI op code for bottom 10 bits
SEND26:
; Extract high 16 bits of address
; and use CDI to send it
        push psw        ; save op
        push h          ; save HL
        call SEND16     ; send top 16 bits
        pop h
; HL points to 4 bytes:
;  000000xx xxxxxxxx xxxxxxyy yyyyyyyy
        inx h
        inx h
        mov a,M         ; xxxxxxyy
        ani 03h         ; 000000yy
        inx h
        mov l,M         ; yyyyyyyy
        mov h,a         ; HL has the 10 bits
        pop psw         ; get op
        inr a           ; [A]+1
        call CDI_AHL    ; send the CDI
        ret

MMCB_INIT:

; Initialize private fetch-pointers to Event queue
; for each data type
        call mmcb_initevq

; mark MMCB parameters "undefined"
        mvi a,0EFh
        sta GBL_CAM_NWB

; MMCBs are allocated in DPU RAM banks 5, 6 and 7
; (Java program MMCBPARAMS calculates MMCB params so that
;  the MMCBs all fit within 3 banks)
        mvi a,5         ; bank=5
        sta first_ram_addr
        mvi a,0         ; offset=0
        sta first_ram_addr+1
        sta first_ram_addr+2

; no events
        mvi a,0
        sta cam_nev
        sta spf_nev
        sta sps_nev
        sta aph_nev
        sta cam_fo

; Start the supply task 
        lxi d,supply_task
        lxi h,GBL_SUPPLY_STACK+stack_size
        call task_new

; Set up "Buffer Switch" Interrupt Service
        mvi A,JMP_OPCODE
        sta RST_55          ; RST 5.5
        lxi h,BUFSW_ISR     ; address of Interrupt handler
        shld RST_55+1       ; set as JMP address
;
; enable interrupt-request
; setup the 8085
        rim             ; RST 5.5 is handled by the 8085
; bit 3 1=mask-set enable
; bit 0 0=enable RST 5.5
        ori 08h         ; mask set enable
; bits 4..7 off
        ani 0Eh         ; enable (unmask) 5.5
        sim

; == rev 7.6
; set readouts-idle
        mvi a,1
        sta GBL_RO_IDLE

        ret





; allocate Mass Memory for R/O buffers
; HL points to 4-byte number of words to allocate
; On return,
;   mm      MM address of allocated block
;   mmnext  MM address of next block to be allocated
mmalloc:
        push psw
        push b
        push d
        push h

; mm = mmnext
        push h          ; >>
        lxi h,mmnext
        lxi d,mm
        mvi c,4
       call copy_bytes
; mmnext += bytes allocated
        pop h           ; << pointer to 4-byte size 
        lxi d,3
        dad d           ; point to last byte of size
; 4-byte add of [HL] to mmnext
        lda mmnext+3
        add M             
        sta mmnext+3
        dcx h
;
        lda mmnext+2
        adc M           ; add with carry 
        sta mmnext+2
        dcx h
;
        lda mmnext+1
        adc M           ; add with carry 
        sta mmnext+1
        dcx h
;
        lda mmnext+0
        adc M           ; add with carry 
        sta mmnext+0
;
        pop h
        pop d
        pop b
        pop psw
        ret

; copy a 4-byte MM address from 'mm' to [DE]
copy_mm:
        mvi c,4
        lxi h,mm
        call copy_bytes
        ret



; allocate Banked RAM for MMCBs
; On return,
;   ram      points to allocated MMCB block
;   ramnext  points to next block to be allocated
ramalloc:
        push psw
        push b
        push d
        push h

; ram = ramnext
        lxi h,ramnext
        lxi d,ram
        mvi c,3
        call copy_bytes

; ramnext += size of an MMCB
; 3-byte add 
        lda ramnext+2
        adi MMCB_length  
        sta ramnext+2  
;
        mvi c,0
        lda ramnext+1
        adc c           ; add 0 with carry 
        sta ramnext+1
;
; step to next bank if offset goes past 4000h
        cpi 40h
        jnz samebank
        lda ramnext
        inr a
        sta ramnext
; wrap offset
        lda ramnext+1
        ani 3Fh
        sta ramnext+1
samebank:
        pop h
        pop d
        pop b
        pop psw
        ret

; copy a 3-byte MM address from 'ram' to [DE]
copy_ram:
        mvi c,3
        lxi h,ram
        call copy_bytes
        ret

; allocate an MMCB
mmcb_alloc:
        push psw
        push b
        push d
        push h

; allocate one MMCB
        call ramalloc            ; 'ram' points to new MMCB

; set status = no operation required
        mvi a,status_idle
        sta mmcb_status

; Set Science mode
;***        lda GBL_SCIENCE_MODE  
;***        sta mmcb_mode
; *** (this is done at "append" time (readout)

; allocate readout buffer in Mass Memory
        lxi h,mmcb_OI_n
        call mmalloc
; set pointer to camera readout area
        lxi d,mmcb_OI           ; mmcb_OI=mm
        call copy_mm
; (size of readout area aready set)
; set link to next MMCB = ramnext
        lxi h,ramnext
        lxi d,mmcb_link
        mvi c,3
        call copy_bytes
; Write current MMCB to banked memory
        lxi d,ram          ; 3-byte banked memory addr
        lxi h,mmcb_status  ; source MMCB
        call mmcb_store
        pop h
        pop d
        pop b
        pop psw
        ret


alloc_queue:
; Having allocated the first MMCB of a queue,
; allocate the remaining MMCBs
; HL = number of elements in queue

        dcx h                   ; deduct for the one just done
csq1:   call mmcb_alloc         ; alloc an MMCB
        dcx h                   ; deduct 1 from the MMCB count
        mov a,h
        ora l                   ; stop at zero
        jnz csq1
; set link field in last MMCB to Null
        mvi a,0FFh
        sta mmcb_link
; re-write the last MMCB
        lxi d,ram          ; 3-byte banked memory addr
        lxi h,mmcb_status  ; source MMCB
        call mmcb_store
        ret

MMCB_FETCH:
; DE - points to banked memory address
;      3-bytes: bank(1), offset(2)
; HL - where data is to be stored in DPU memory
        push psw
        push b
        push d
        push h
; save current Bank
        in BANK_SELECT_I
        push psw

        push h          ; --save destination
; set bank for source 
        ldax d 
        out BANK_SELECT_O
; get offset within 16K
; = low 2 bytes of 3-byte address
        inx d
        ldax d
        mov h,a
        inx d
        ldax d
        mov l,a         ; HL = offset
; Banked Memory window starts at 4000h
        lxi d,4000h     
        dad d
        pop d           ; --retrieve destination
; HL - source (in 16K Bank window)
; DE - destination
        mvi c,mmcb_length
; C - byte count

; Copy bytes
fl1:    mov a,M         ; fetch from source
        stax d          ; store in destination
        inx h           ; increment pointers
        inx d
; If source offset went past end of 16K window,
; the bank has to be incremented
        mov a,h
        cpi 80h         ; 8000
        jnz nbankf
        in BANK_SELECT_I
        inr a
        out BANK_SELECT_O ; set new bank
; wrap source pointer back to beginning
; of banked window
        lxi h,4000h
nbankf:
        dcr c           ; decrement count
        jnz fl1         ; until C=zero

fxit:
; restore original Bank
        pop psw
        out BANK_SELECT_O

        pop h
        pop d
        pop b
        pop psw
        ret

mmcb_store:
; DE - points to 3-byte MMCB address in banked memory
; HL - points to source MMCB
        push psw
        push b
        push d
        push h

; save current Bank
        in BANK_SELECT_I
        push psw

        push h          ; --save source pointer
; set bank for destination 
        ldax d          ; get first of 3 bytes
        out BANK_SELECT_O

; get offset within 16K
; HL = low 2 bytes of 3-byte address
        inx d
        ldax d          ; 2nd of 3 bytes
        mov h,a
        inx d
        ldax d          ; 3rd of 3 bytes
        mov l,a         ; HL = offset
; Banked Memory window starts at 4000h
        lxi d,4000h     
        dad d           ; HL is now offset within bank window

        pop d           ; --retrieve source pointer
; DE - source
; HL - destination (in 16K bank window)
        mvi c,mmcb_length
; C - byte count

; Copy bytes
sl1:    ldax d          ; fetch from source
        mov M,a         ; store in destination
        inx h           ; increment pointers
        inx d

; If destination offset went past end of 16K window,
; the bank has to be incremented
        mov a,h
        cpi 80h         ; 8000
        jnz same_bank
        in BANK_SELECT_I
        inr a
        out BANK_SELECT_O  ; set new bank
; wrap source pointer back to beginning
; of banked window
        lxi h,4000h
same_bank:
        dcr c           ; decrement count
        jnz sl1         ; until C=zero

; restore original Bank
        pop psw
        out BANK_SELECT_O

        pop h
        pop d
        pop b
        pop psw
        ret

; =================================================================
; Allocate the chain of MMCBs 
; and define the data buffer part of Mass Memory 
; (the part outside the spacecraft buffers)
MMCB_FORMAT:
        push psw
        push b
        push d
        push h

; are MMCB parameters defined (patched-in) ?
        lda GBL_CAM_NWB
        cpi 0EFh        ; (1st byte of actual value won't ever be EF)
        jz mmfxit       ; no

; allocate first MMCB in banked RAM
        lxi h,first_ram_addr
        lxi d,ramnext
        mvi c,3
        call copy_bytes
; first R/O buffer in Mass Memory
;  mmnext=GBL_RO_BASE
        lxi h,GBL_RO_BASE
        lxi d,mmnext
        mvi c,4
        call copy_bytes

; Create the Camera supply queue
; Allocate the first Camera MMCB
        mvi a,dtype_Camera      ; data type = camera
        sta mmcb_dtype
; store buffer allocation   in mmcb_OI_n
; store actual readout size in mmcb_owords
        lxi h,GBL_CAM_NWB ; point to 4-byte buffer size
        lxi d,mmcb_OI_n         ; allocation 
        mvi c,4
        call copy_bytes
        lxi h,GBL_CAM_NWRO      ; actual readout
        lxi d,mmcb_owords
        mvi c,4
        call copy_bytes
        call mmcb_alloc
; set pointer to head of queue
; CAM_SUPPLY_Q = new_mmcb       (used by Buffer Switch ISR)
        lxi h,new_mmcb
        lxi d,GBL_CAM_SUPPLY_Q
        mvi c,3
        call copy_bytes
; private pointer to same queue (used by supply task)
        lxi h,new_mmcb
        lxi d,CAM_SUPPLY_Q
        mvi c,3
        call copy_bytes
; allocate the rest of the queue
        lda GBL_N_CAM_MMCBS    ; number of MMCBs to allocate
        mov h,a                 ; (stored big-endian)
        lda GBL_N_CAM_MMCBS+1
        mov l,a
        call alloc_queue
; set pointer to end of queue
        lxi h,new_mmcb
        lxi d,GBL_CAM_SUPPLY_QTAIL
        mvi c,3
        call copy_bytes

        call task               ; give the other tasks a chance

; Create the Spectrophotometer supply queue
; set pointer to head of queue
; Allocate the first SPF MMCB
        mvi a,dtype_SPF         ; data type = SPF
        sta mmcb_dtype
; store buffer allocation   in mmcb_OI_n
; store actual readout size in mmcb_owords
        lxi h,GBL_SPF_NWB ; point to 4-byte buffer size
        lxi d,mmcb_OI_n
        mvi c,4
        call copy_bytes
        lxi h,GBL_SPF_NWRO
        lxi d,mmcb_owords
        mvi c,4
        call copy_bytes
        call mmcb_alloc
; SPF_SUPPLY_Q= new_mmcb      (used by Buffer Switch ISR)
        lxi h,new_mmcb
        lxi d,GBL_SPF_SUPPLY_Q
        mvi c,3
        call copy_bytes
; private pointer to same queue (used by supply task)
        lxi h,new_mmcb
        lxi d,SPF_SUPPLY_Q
        mvi c,3
        call copy_bytes
; allocate the rest of the queue
        lda GBL_N_SPF_MMCBS    ; number of MMCBs to allocate
        mov h,a                 ; (stored big-endian)
        lda GBL_N_SPF_MMCBS+1
        mov l,a
        call alloc_queue
; set pointer to end of queue
        lxi h,new_mmcb
        lxi d,GBL_SPF_SUPPLY_QTAIL
        mvi c,3
        call copy_bytes

        call task               ; give the other tasks a chance

; Create the Array Photometer supply queue
; Allocate the first AP MMCB
        mvi a,dtype_APH
        sta mmcb_dtype
        lxi h,GBL_APH_NWB ; point to 4-byte buffer size
        lxi d,mmcb_OI_n
        mvi c,4
        call copy_bytes
        lxi h,GBL_APH_NWRO
        lxi d,mmcb_owords
        mvi c,4
        call copy_bytes
        call mmcb_alloc
; APH_SUPPLY_Q= new_mmcb
        lxi h,new_mmcb
        lxi d,GBL_APH_SUPPLY_Q
        mvi c,3
        call copy_bytes
; private pointer to same queue (used by supply task)
        lxi h,new_mmcb
        lxi d,APH_SUPPLY_Q
        mvi c,3
        call copy_bytes
; allocate the rest of the queue
        lda GBL_N_APH_MMCBS    ; number of MMCBs to allocate
        mov h,a                 ; (stored big-endian)
        lda GBL_N_APH_MMCBS+1
        mov l,a
        call alloc_queue
; set pointer to end of queue
        lxi h,new_mmcb
        lxi d,GBL_APH_SUPPLY_QTAIL
        mvi c,3
        call copy_bytes

        call task               ; give the other tasks a chance

; Create the Slow-rate Spectrophotometer supply queue
; set pointer to head of queue
; Allocate the first SPS MMCB
        mvi a,dtype_SPS
        sta mmcb_dtype
        lxi h,GBL_SPS_NWB ; point to 4-byte buffer size
        lxi d,mmcb_OI_n
        mvi c,4
        call copy_bytes
        lxi h,GBL_SPS_NWRO
        lxi d,mmcb_owords
        mvi c,4
        call copy_bytes
        call mmcb_alloc
; SPS_SUPPLY_Q= new mmcb
        lxi h,new_mmcb
        lxi d,GBL_SPS_SUPPLY_Q
        mvi c,3
        call copy_bytes
; private pointer to same queue (used by supply task)
        lxi h,new_mmcb
        lxi d,SPS_SUPPLY_Q
        mvi c,3
        call copy_bytes
; allocate the rest of the queue
        lda GBL_N_SPS_MMCBS    ; number of MMCBs to allocate
        mov h,a                 ; (stored big-endian)
        lda GBL_N_SPS_MMCBS+1
        mov l,a
        call alloc_queue
; set pointer to end of queue
        lxi h,new_mmcb
        lxi d,GBL_SPS_SUPPLY_QTAIL
        mvi c,3
        call copy_bytes
; **7.9**
; lxi h,mmnext
; mvi c,4
; call debug_dump

        call task               ; give the other tasks a chance

; set all instrument readout sizes (subroutine is in EEPROM.a)
        in BANK_SELECT_I
        push psw                ; preserve Bank
        mvi a,EE_BANK
        out BANK_SELECT_O       ; select EEPROM bank
        call ee_set_rosizes

        pop psw
        out BANK_SELECT_O


        
; Create the DCM Queue -- data ready for compression
; (queue is empty)
        mvi a,0FFh
        sta GBL_DCM_QHEAD
        sta GBL_DCM_QTAIL

; Create the TM Queue -- compressed data ready for Telemetry
; (queue is empty)
        sta GBL_TM_QHEAD
        sta GBL_TM_QTAIL

        call task

; Set initial primary and alternate R/O addresses
; ==camera==
        mvi a,0
        sta cam_RO_done ;  flag: no camera R/O completed
        lxi d,GBL_CAM_SUPPLY_Q 
        lxi h,mmcb_status
        call MMCB_fetch
; Set primary hardware address
        lxi h,mmcb_OI           ; high 2 bytes of addr
        mvi a,0A0h              ; "A" buffer
        call SEND16             ; send top 16 bits of MM address
; (low 10 bits of address are 0)
; (First R/O completion will be for primary hardware address)
        mvi a,0         ; 0=1st
        sta which_cam
; link to the next queue element
        lxi h,mmcb_link
        lxi d,GBL_CAM_SUPPLY_Q
        mvi c,3
        call copy_bytes
; fetch it
        lxi d,GBL_CAM_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_fetch
; set alternate hardware address
        lxi h,mmcb_OI           ; high 2 bytes of addr
        mvi a,0A1h              ; "B" buffer
        call SEND16             ; send top 16 bits of MM address
; (low 10 bits of address are 0)
; link to next queue element
        lxi h,mmcb_link
        lxi d,GBL_CAM_SUPPLY_Q
        mvi c,3
        call copy_bytes

        call task

; ==Spectrophotometer=
        mvi a,0
        sta spf_RO_done ; flag: R/O not completed
        lxi d,GBL_SPF_SUPPLY_Q 
        lxi h,mmcb_status
        call MMCB_fetch
; set primary hardware address
        lxi h,mmcb_OI   ; hi 16 bits of address
        mvi a,0AAh      ; "A" buffer
        call SEND16     ; send top 16 bits of MM address
; (low 10 bits of address are 0)
; (First R/O completion will be for primary hardware address)
        mvi a,0         ; 0=1st
        sta which_spf
; link to the next queue element
        lxi h,mmcb_link
        lxi d,GBL_SPF_SUPPLY_Q
        mvi c,3
        call copy_bytes
; fetch it
        lxi d,GBL_SPF_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_fetch
; set alternate hardware address
        lxi h,mmcb_OI   ; MM address
        mvi a,0ABh      ; "B" buffer
        call SEND16     ; send high 16 bits of address
; link to next queue element
        lxi h,mmcb_link
        lxi d,GBL_SPF_SUPPLY_Q
        mvi c,3
        call copy_bytes
        call task

; ==Slow-rate Spectrophotometer=
        mvi a,0
        sta sps_RO_done ; flag: R/O not completed
        lxi d,GBL_SPS_SUPPLY_Q 
        lxi h,mmcb_status
        call MMCB_fetch
; set primary hardware address
        lxi h,mmcb_OI   ; MM address
        mvi a,0AEh      ; "A" buffer
        call SEND26     ; send 26-bit address 16+10
; (First R/O completion will be for primary hardware address)
        mvi a,0         ; 0=1st
        sta which_sps
; link to the next queue element
        lxi h,mmcb_link
        lxi d,GBL_SPS_SUPPLY_Q
        mvi c,3
        call copy_bytes
; fetch it
        lxi d,GBL_SPS_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_fetch
; set alternate hardware address
        lxi h,mmcb_OI   ; MM address
        mvi a,0B0h      ; "B" buffer
        call SEND26     ; send 26-bit address 16+10
; link to next queue element
        lxi h,mmcb_link
        lxi d,GBL_SPS_SUPPLY_Q
        mvi c,3
        call copy_bytes
        call task

; =Array Photometer=
        mvi a,0
        sta aph_RO_done ; flag: R/O not completed
        lxi d,GBL_APH_SUPPLY_Q 
        lxi h,mmcb_status
        call MMCB_fetch
; set primary hardware address
        lxi h,mmcb_OI   ; MM address
        mvi a,0A6h      ; "A" buffer
        call SEND16     ; send high 16 bits of address
; (First R/O completion will be for primary hardware address)
        mvi a,0         ; 0=1st
        sta which_aph
; link to the next queue element
        lxi h,mmcb_link
        lxi d,GBL_APH_SUPPLY_Q
        mvi c,3
        call copy_bytes
; fetch it
        lxi d,GBL_APH_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_fetch
; set alternate hardware address
        lxi h,mmcb_OI   ; MM address
        mvi a,0A7h      ; "B" buffer
        call SEND16     ; send high 16 bits of address
; link to next queue element
        lxi h,mmcb_link
        lxi d,GBL_APH_SUPPLY_Q
        mvi c,3
        call copy_bytes

        mvi a,1
        sta GBL_MMCB_Status ; formatted 

mmfxit:
        pop h
        pop d
        pop b
        pop psw
        ret


; Append a "readout" MMCB to the DCM queue, for compression
append_to_DCM_queue:
; fetch the MMCB
        lxi d,ram            ; 3-byte banked memory addr
        lxi h,mmcb_status
        call mmcb_fetch
; set new status
        mvi a,status_Readout
        sta mmcb_status
; note the current Science Mode
        lda GBL_SCIENCE_MODE
        sta mmcb_mode
; make the link null
; (originally, it pointed to a supply queue MMCB)
        mvi a,0FFh
        sta mmcb_link

; Write MMCB back into banked memory
        lxi d,ram          ; 3-byte banked memory addr
        lxi h,mmcb_status  ; source MMCB
        call mmcb_store

;
; fetch Tail of DCM queue
        lda GBL_DCM_QTAIL
        cpi 0FFh                ; null?
        jz newtail              ; yes, new tail
        lxi d,GBL_DCM_QTAIL
        lxi h,mmcb_status
        call mmcb_fetch
; set link
        lxi h,ram
        lxi d,mmcb_link
        mvi c,3
        call copy_bytes
; re-write the MMCB
        lxi d,GBL_DCM_QTAIL ; 3-byte banked memory addr
        lxi h,mmcb_status   ; source MMCB
        call mmcb_store
; new DCM queue tail
newtail:
        lxi h,ram
        lxi d,GBL_DCM_QTAIL
        mvi c,3
        call copy_bytes
; If Head is null, make it same as Tail
        lda GBL_DCM_QHEAD
        cpi 0FFh
        jnz headok
        lxi h,GBL_DCM_QTAIL
        lxi d,GBL_DCM_QHEAD
        mvi c,3
        call copy_bytes
headok:
; Increment count of MMCBs placed on DCM queue
; (i.e. needing compression)
        lxi h,GBL_SOH+27        ; IDH_N_MC
        call INCBIG
        ret



; ============================================
; ==Supply Task==
; Task to handle Buffer Switches
;
; Read the MMCB Supply Queues and obtain
; readout addresses for the instruments.
;
; Initially, for each queue, 
; two MMCBs were read to obtain readout pointers.  

supply_task:
        call task

        lda GBL_MMCB_Status
        cpi 1               ; MMCBs should be allocated
        jnz supply_task     ; wait until they are 

; If in a science mode, check the Day Sensors
        lda GBL_SCIENCE_MODE
        cpi MODE_DAYSIDE
        jz proc_data
        cpi MODE_STANDBY
        jz proc_data
; In a Science Mode
; Camera power must be on
        in PWR_STS_LO_I  ; services 0..7
        ani 10h         ; test PS4
        jz proc_data    ; Cam is off

; If Day-Sensors see daylight, switch to dayside mode
        in DAY_LAT_STS_I
        ani 80h         ; day sensed?
        jz still_night
; Some sensor saw daylight (or HV protect tripped)
;***  Set "Force day"
;***        in DAY_SENS_STS_I
;***        ori 80h         ; b7=1 - Simulate Daylight
; don't enable day sensor interrupts
; zero the TBD bits
;***        ani 0B0h        ; b6,b3,b2,b1,b0=0 
;***        out DAY_SENS_CTL_O
; Disable Triggers
        lxi h,ctd       ; execute I_TRIGGER_DISABLE
        public EXECUTE
        call EXECUTE
; Disable data-taking 
        lxi h,cst       ; execute I_STOP
        call EXECUTE
; Set "Dayside" mode -- no data taking,
;  but continue processing data into packets and PVCFs.
        mvi a,MODE_DAYSIDE
        sta GBL_SCIENCE_MODE
; Now disable the HV Protection and look at the day-sensor state.
; If day was not sensed, then we had an HV Protection trip.  
;***        in SCIF_STS_I
;***        ori 008h        ; b3=1 disables
;***        out SCIF_CTL_O
;***        call wait_1ms
;***        in DAY_LAT_STS_I
;***        ani 80h         ; "day sensed" ?
;***        jnz proc_data   ; yes, it was truly Day
; Tell the ground that the HV Prot tripped
;***        lxi h,GBL_SOH+69
;***        inr M           ; increment a counter
        jmp proc_data
CTD     db 07Ch ; I_TRIGGER_DISABLE
CST     db 069h ; I_STOP
still_night:

proc_data:
; Set "readouts idle" if no instrument has a readout ready
        lda cam_RO_done
        cpi 0
        jnz notidle
        lda spf_RO_Done
        cpi 0
        jnz notidle
        lda sps_RO_done
        cpi 0
        jnz notidle
        lda aph_RO_done
        cpi 0
        jnz notidle
; nothing happening 
        mvi a,1
        sta GBL_RO_IDLE         ; set "readouts idle"
        jmp supply_task

; at least one readout is ready
notidle: mvi a,0
        sta GBL_RO_IDLE

; ==Camera==
ckcam:  lda cam_RO_done     ; any R/O done?
        cpi 0        
        jz ckspf        ; no, skip the camera

; Camera readout completed (buffer-switch)

; Fetch current MMCB into local storage
        lxi d,CAM_SUPPLY_Q
        ldax d
        cpi 0FFh        ; null pointer?
        jnz fchcam      ; no
; **7.9**
;        call debug_asc
;        asc "e:CAMsq$"
        jmp ckspf
fchcam:
        lxi h,mmcb_status
        call MMCB_fetch
rdcamq:
; read Event queue (with private pointer)
        lxi d,qtime           ; where to store time
        lhld cam_efetch       ; CAM private pointer
        call SOH_FROMEVQ
; Returned:
;  A  - 0=queue empty, 1=element delivered
;  C  - code (0=trigger-event,  2=exposure, 
;        30,31,32,33=BufferSwitch from CAM,SPF,SPS,APH)
;  HL - updated queue fetch-pointer value
        cpi 0         ; queue empty?
        jnz camstp    ; no
; event queue really shouldn't go empty, because there should
; be one readout element for every count in CAM_RO_done
; **7.9*
;        call debug_asc
;        asc "e:EOFceq$"
        jmp ckspf

; store the updated queue pointer
camstp: shld cam_efetch

        mov a,c         ; look at code
        cpi 0           ; trigger event?
        jnz ck30        ; no
; Trigger Event
; If Nev=0, capture trigger time (first of a chain)
;***        lda cam_fo
;***        cpi 0
;****        jnz nocap
        lda cam_nev
        cpi 0
        jnz nocap
; capture trigger time
        lxi h,qtime
        lxi d,event_time
        mvi c,6
        call copy_bytes
; also capture queue pointer
; this is the start of the exposure time list
        lhld cam_efetch
        shld etqp
nocap:
; Increment Nev
        lda cam_nev
        inr a
        sta cam_nev
        jmp rdcamq

; If code==30 - Camera Buffer Switch
ck30:   cpi 30
        jnz rdcamq      ; read more from Event Queue
; Camera Buffer switch
; If Nev=0 or cam_fo=1, process readout as follow-on
        lda cam_nev
        cpi 0
        jz cnev0       ; (need to set cam_fo=0 first)
        lda cam_fo     
        cpi 1
        jz camfo       ; process as follow-on
; triggered readout
        mvi a,0         ; not follow-on
        sta mmcb_fo
camcom:
; If Nev>1 set FO=1
        lda cam_nev
        sui 2
        ani 80h         ; negative if >=2
        jnz nofo
; (Force to zero for now ...)
        mvi a,0
        sta cam_fo      ; FO=1
nofo:   mvi a,0
; zero Nev - number of events between readouts
        sta cam_nev     
; Capture buffer switch time
        lxi h,qtime  ; time in event queue el't
        lxi d,mmcb_t2   ; buffer switch time in MMCB
        mvi c,6
        call copy_bytes
; store trigger event time in MMCB
        lxi h,event_time
        lxi d,mmcb_te
        mvi c,6
        call copy_bytes
; For Sprite Camera, store the event queue pointer associated
;   with the trigger event, into mmcb_eq.
; mmcb_eq pointer will be used to capture a list of exposure times
;   for sprite images.
;***        lda mmcb_mode
;***        cpi MODE_AUR
;****        jz pass_cam_MMCB_to_DCM
; Camera Sprite mode
; mmcb_eq = Event Queue pointer
        lhld etqp
        shld mmcb_eq
        jmp pass_cam_MMCB_to_DCM
cnev0:  mvi a,0
        sta cam_fo

; follow-on readout
camfo:  mvi a,1
        sta mmcb_fo
; otherwise, same as triggered readout
        jmp camcom

pass_cam_MMCB_to_DCM:
; move current MMCB from supply queue to DCM queue.

; save ram address of current queue element
        lxi h,CAM_SUPPLY_Q
        lxi d,ram
        mvi c,3
        call copy_bytes
; copy MMCB back to banked memory
        lxi d,CAM_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_store
; step (private) queue pointer to next MMCB
        lxi h,mmcb_link         ; follow link
        lxi d,CAM_SUPPLY_Q
        mvi c,3
        call copy_bytes
; append the original MMCB (addr in 'ram')
; to the DCM queue
        call append_to_DCM_queue
        call task

        lxi h,cam_RO_done ; decrement readout count
        dcr M
        jmp ckcam

; ==Spectrophotometer (Fast rate)==
ckspf:  
        lda spf_RO_done     ; any R/O done?
        cpi 0        
        jz cksps        ; no, skip it

; SPF readout completed (buffer switch)

; fetch current MMCB into local storage
        lxi d,SPF_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_fetch

rdspfq:
; read Event queue (with private pointer)
        lxi d,qtime           ; where to store time
        lhld spf_efetch
        call SOH_FROMEVQ
; Returned:
;  A  - 0=queue empty, 1=element delivered
;  C  - code (0=trigger-event,  2=exposure, 
;        30,31,32,33=BufferSwitch from CAM,SPF,SPS,APH)
;  HL - updated queue fetch-pointer value
        cpi 0
        jnz spfstp 
; queue really shouldn't go empty, because there should
; be one readout element for every count in SPF_RO_done
; **7.9**
;        call debug_asc
;        asc "e:SPF ROq$"
        jmp cksps

; store the updated queue pointer
spfstp: shld spf_efetch

        mov a,c         ; look at code
        cpi 0           ; trigger event?
        jnz ck31        ; no
; count a trigger Event
        lda spf_nev
        inr a
        sta spf_nev
; if spf_nev==1  capture trigger time
        cpi 1
        jnz rdspfq
        lxi h,qtime
        lxi d,event_time
        mvi c,6
        call copy_bytes
        jmp rdspfq

; If code==31  (SPF buffer switch)   
ck31:    cpi 31 
        jnz rdspfq
; SPF buffer switch
; If spf_nev==0, process readout as follow-on
        lda spf_nev
        cpi 0
        jz spffo       ; process as follow-on
; triggered readout
        mvi a,0         ; not follow-on
        sta mmcb_fo
spfcom: mvi a,0
        sta spf_nev     ; number of events = 0
; Capture buffer switch time
        lxi h,qtime  ; time in event queue el't
        lxi d,mmcb_t2   ; buffer switch time in MMCB
        mvi c,6
        call copy_bytes
; store event time in MMCB
        lxi h,event_time
        lxi d,mmcb_te
        mvi c,6
        call copy_bytes
        jmp pass_spf_MMCB_to_DCM
  
; follow-on readout
spffo:  mvi a,1
        sta mmcb_fo
; otherwise, same as triggered readout
        jmp spfcom

pass_spf_mmcb_to_DCM:
; Move current MMCB from supply queue to DCM queue.

; save ram address of current queue element
        lxi h,SPF_SUPPLY_Q
        lxi d,ram
        mvi c,3
        call copy_bytes
; store MMCB back to banked memory
        lxi d,SPF_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_store
; step (private) queue pointer to next MMCB
; follow chain to next MMCB
        lxi h,mmcb_link
        lxi d,SPF_SUPPLY_Q
        mvi c,3
        call copy_bytes
; append the original MMCB (addr in 'ram')
; to the DCM queue
        call append_to_DCM_queue
        call task

        lxi h,spf_RO_done  ; decrement readout count
        dcr M
        jmp ckspf

; ==Slow-rate Spectrophotometer==
cksps:  
        lda sps_RO_done     ; any R/O done?
        cpi 0        
        jz ckaph        ; no, skip the slow Spectrophotometer

; SPS readout completed (buffer switch)

; fetch current MMCB into local memory
        lxi d,SPS_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_fetch

rdspsq:
; Read Event Queue (with private pointer)
        lxi d,qtime           ; where to store time
        lhld sps_efetch
        call SOH_FROMEVQ
; Returned:
;  A  - 0=queue empty, 1=element delivered
;  C  - code (0=trigger-event,  2=exposure, 
;        30,31,32,33=BufferSwitch from CAM,SPF,SPS,APH)
;  HL - updated queue fetch-pointer value
        cpi 0
        jnz spsstp 
; queue really shouldn't go empty, because there should
; be one readout element for every count in SPS_RO_done
; **7.9**
;        call debug_asc
;        asc "e:SPS ROq$"
        jmp ckaph

; store the updated queue pointer
spsstp: shld sps_efetch

        mov a,c         ; look at code
        cpi 0
        jnz ck32
; count a trigger Event
        lda sps_nev
        inr a
        sta sps_nev

; if sps_nev==1, capture trigger time
        cpi 1
        jnz rdspsq
        lxi h,qtime
        lxi d,event_time
        mvi c,6
        call copy_bytes
        jmp rdspsq

; If code==32  (SPS buffer switch)   
ck32:   cpi 32
        jnz rdspsq
; SPS buffer switch
;   for SPS, follow-on is meaningless
        mvi a,0
        sta mmcb_fo     
        sta sps_nev     ; number of events = 0
; Capture buffer switch time
        lxi h,qtime  ; time in event queue el't
        lxi d,mmcb_t2   ; buffer switch time in MMCB
        mvi c,6
        call copy_bytes
; even for SPS, store event time in MMCB
        lxi h,event_time
        lxi d,mmcb_te
        mvi c,6
        call copy_bytes

; pass SPS MMCB to DCM
; save ram address of current queue element
        lxi h,SPS_SUPPLY_Q
        lxi d,ram
        mvi c,3
        call copy_bytes
; update MMCB into banked memory
        lxi d,SPS_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_store
; step (private) queue pointer to next MMCB
; follow chain to next MMCB
        lxi d,SPS_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_fetch
        lxi h,mmcb_link
        lxi d,SPS_SUPPLY_Q
        mvi c,3
        call copy_bytes
; append the original MMCB (addr in 'ram')
; to the DCM queue
        call append_to_DCM_queue
        call task

        lxi h,sps_RO_done  ; decrement readout count
        dcr M
        jmp cksps

; ==Array Photometer==
ckaph:  
        lda aph_RO_done     ; any AP R/O done?
        cpi 0        
        jz supply_task      ; no, cycle task

; AP readout completed (buffer switch)

; fetch current MMCB into local memory
        lxi d,APH_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_fetch

rdaphq:
; read Event queue (with private pointer)
        lxi d,qtime           ; where to store time
        lhld aph_efetch
        call SOH_FROMEVQ
; Returned:
;  A  - 0=queue empty, 1=element delivered
;  C  - code (0=trigger-event,  2=exposure, 
;        30,31,32,33=BufferSwitch from CAM,SPF,SPS,APH)
;  HL - updated queue fetch-pointer value
        cpi 0
        jnz aphstp 
; queue really shouldn't go empty, because there should
; be one readout element for every count in APH_RO_done
; **7.9**
;        call debug_asc
;        asc "e:APH ROq$"
        jmp supply_task


; this stores the updated queue pointer
aphstp: shld aph_efetch

        mov a,c         ; look at code
        cpi 0           ; trigger event?
        jnz ck33        ; no
; count a trigger Event
        lda aph_nev
        inr a
        sta aph_nev
; if aph_nev==1  capture trigger time
        cpi 1
        jnz rdaphq
        lxi h,qtime
        lxi d,event_time
        mvi c,6
        call copy_bytes
        jmp rdaphq

; If code==33   APH buffer switch
ck33:   cpi 33
        jnz rdaphq
; APH buffer switch
; If aph_nev==0, process readout as follow-on
        lda aph_nev
        cpi 0
        jz aphfo       ; treat as follow-on
; triggered readout
        mvi a,0
        sta mmcb_fo
aphcom: mvi a,0
;*** (was spf_nev)
        sta aph_nev     ; number of events = 0
; Capture buffer switch time
        lxi h,qtime  ; time in event queue el't
        lxi d,mmcb_t2   ; buffer switch time in MMCB
        mvi c,6
        call copy_bytes
; store event time in MMCB
        lxi h,event_time
        lxi d,mmcb_te
        mvi c,6
        call copy_bytes
        jmp pass_aph_MMCB_to_DCM

; follow-on readout
aphfo:  mvi a,1
        sta mmcb_fo
; otherwise, same as triggered readout
        jmp aphcom

pass_aph_MMCB_to_DCM:
; save ram address of current queue element
        lxi h,APH_SUPPLY_Q
        lxi d,ram
        mvi c,3
        call copy_bytes
; copy MMCB back to banked memory
        lxi d,APH_SUPPLY_Q
        lxi h,mmcb_status
        call MMCB_STORE
; step (private) queue pointer to next MMCB
        lxi h,mmcb_link
        lxi d,APH_SUPPLY_Q
        mvi c,3
        call copy_bytes
; append the original MMCB (addr in 'ram')
; to the DCM queue
        call append_to_DCM_queue
        call task

        lxi h,aph_RO_done  ; decrement readout count
        dcr M
        jmp ckaph

whichb  ds 2 

;***** ignore if not in science mode
ign:   lda GBL_SCIENCE_MODE
       cpi MODE_DAYSIDE
      jz showatt 
      cpi MODE_STANDBY
      jz showatt
; increment buffer-switch counter in housekeeping
      lxi h,GBL_SOH+33
      inr M
      ret
showatt: pop psw ; drop return from "call ign" 
      ret  ; return will be from "cz cam_switched" etc.

; ===Interrupt Service for Readout Buffer Switch=== 
BUFSW_ISR:
        push psw
        push b
        push d
        push h

; clear RST 5.5 IRQ latch
        in MM_IM_DCM_STS_I
        ori 01h
        out MM_IM_DCM_CTL_O ; (latch is self-restoring)

; disable RST 5.5 
; and allow interrupts again
        rim 
; bit 3 1=mask-set enable
; bit 0 1=disable RST 5.5
        ori 08h         ; mask set enable
; bits 4..7 off
        ani 0Fh
        ori 01h         ; b1 1=disable 5.5
        sim
        ei              ; allow interrupts

; On a Buffer Switch interrupt, 
; this ISR reads the flags, and does the following:
;   (1) Determine which instrument switched.
;   (2) Read another MMCB from the supply queue  
;   (3) If queue ran dry, stop the instrument
;   (4) Set new buffer address in hardware
;   (5) Put "now" as buffer-switch Time in Event Queue, 
;       with a code to identify the instrument
;   (6) Increment the readout-counter for this instrument

; find out which instrument did a buffer switch
        mvi a,MM_BUF_SW      ; "readback" opcode
        lxi h,whichb
        call CDI_readback
        lda whichb+1
        ani 02h
        cnz tm_done
        lda whichb+1
        ani 1   ; Camera
        cnz cam_switched
        lda whichb+1
        ani 08h
        jz nospf
        call spf_switched
        call mmcb_rearm
nospf: 
        lda whichb+1
        ani 04h
        cnz aph_switched
        lda whichb+1
        ani 10h
        cnz sps_switched

dism:
        di              ; interrupts off again
; enable RST 5.5 
        rim 
; bit 3 1=mask-set enable
        ori 08h
; bit 0 0=enable RST 5.5
        ani 0FEh         ; b0 0=enable
        sim
        pop h
        pop d
        pop b
        pop psw
        ei              ; allow interrupts
        ret

; Camera switched
cam_switched:
      lda GBL_SOH+11
      ani 08h         ; b3=1 -- camera active
      rz              ; no
;***** ignore if not in science mode
      call ign

; increment buffer-switch counter in housekeeping
        lxi h,GBL_SOH+33
        inr M

; increment readout-done counter
        lxi h,cam_RO_done
        inr M

; Put "now" as buffer-switch Time in Event Queue, code=30
        mvi a,30
  di
        call SOH_TOEVQ
  ei

; Read another MMCB from Camera Supply Queue
        lda GBL_CAM_SUPPLY_Q
        cpi 0FFh        ; head pointer is Null?
        jz camstop      ; yes, stop the camera
        lxi d,GBL_CAM_SUPPLY_Q
        lxi h,immcb_status
        call MMCB_fetch

; change queue pointer to next element
; follow chain to next MMCB in cam supply queue
; (link --> queue head pointer)
        lxi h,immcb_link
        lxi d,GBL_CAM_SUPPLY_Q
        mvi c,3
        call copy_bytes
; Which buffer was completed?
        lda which_cam   
        cpi 1
        jz cam_alt      ; the alternate buffer
; primary buffer completed
; Set new buffer address into hardware
; Set primary hardware address
        lxi h,immcb_OI          ; high 2 bytes of addr
        mvi a,0A0h              ; "A" buffer
        call SEND16             ; send top 16 bits of MM address
; (low 10 bits of address are 0)
        mvi a,1 ; expect alternate buffer next time
        sta which_cam
        ret      
cam_alt:
; alternate cam readout completed
; Set new buffer address into hardware
        lxi h,immcb_OI           ; high 2 bytes of addr
        mvi a,0A1h              ; "B" buffer
        call SEND16             ; send top 16 bits of MM address
; (low 10 bits of address are 0)
        mvi a,0 ; expect primary next time
        sta which_cam
        ret

camstop:
; Camera Supply queue ran dry.
; Stop the camera
        mvi a,MM_IM_ENBL
        lxi h,GBL_zero
        call CDI_SEND
; disable camera data processing
        lda GBL_SOH+11
        ani 0F7h                ; turn off bit 3
        sta GBL_SOH+11
; Log a camera supply queue fault
        lxi h,GBL_SOH+165
        inr M
        mvi a,1
        sta GBL_SOH+12  ; at least one fault happened
; **7.9**
; call debug_asc
; asc "CamSt$"
        ret


; Spectrophotometer switched
spf_switched:
;        mvi a,'S'
;        call debug_putbyte
        lda GBL_SOH+11   ; SPF active?
        ani 04h
        rz               ; no
;***** ignore if not in science mode
;       call ign

; put "now" as buffer-switch time in Event Queue (code=31)
        mvi a,31
  di
        call SOH_TOEVQ
  ei
; increment readout-done counter
        lxi h,spf_RO_done
        inr M
; SPF R/O completed
; Read another MMCB from SPF Supply Queue
        lda GBL_SPF_SUPPLY_Q
        cpi 0FFh        ; head pointer is Null?
        jz spfstop      ; yes, stop the SPF
        lxi d,GBL_SPF_SUPPLY_Q
        lxi h,immcb_status
        call MMCB_fetch
; follow chain to next MMCB in SPF supply queue
; (link --> queue head pointer)
        lxi h,immcb_link
        lxi d,GBL_SPF_SUPPLY_Q
        mvi c,3
        call copy_bytes
; Which buffer was completed?
        lda which_spf
        cpi 1
        jz spf_alt      ; alternate SPF buffer
; Set new buffer address into hardware
; set primary hardware address
        lxi h,immcb_OI   ; hi 16 bits of address
        mvi a,0AAh      ; "A" buffer
        call SEND16     ; send top 16 bits of MM address
; (low 10 bits of address are 0)
        mvi a,1 ; expect alternate buffer next time
        sta which_spf
        ret
spf_alt:
; alternate SPF buffer completed
; Set new buffer address into hardware
        lxi h,immcb_OI   ; MM address
        mvi a,0ABh      ; "B" buffer
        call SEND16     ; send high 16 bits of address
        mvi a,0 ; expect primary next time
        sta which_spf
        ret
spfstop:
; SPF Supply queue ran dry.
; Stop SPF
        lda GBL_SOH+11
        ani 0FBh          ; b2=0
        sta GBL_SOH+11
; Log a SPF supply queue fault
        lxi h,GBL_SOH+166
        inr M
        mvi a,1
        sta GBL_SOH+12  ; at least one fault happened
        ret


; Slow-rate Spectrophotometer switched
sps_switched:
;        mvi a,'s'
;        call debug_putbyte
        lda GBL_SOH+11  ; SPS active
        ani 02h
        rz              ; no
;***** ignore if not in science mode
;       call ign
 
; put "now" as buffer-switch time in Event Queue, code=32
        mvi a,32
  di
        call SOH_TOEVQ
  ei
; increment readout-done counter
        lxi h,sps_RO_done
        inr M
; SPS R/O completed
; Read another MMCB from SPS Supply Queue
        lda GBL_SPS_SUPPLY_Q
        cpi 0FFh        ; head pointer is Null?
        jz spsstop      ; yes, stop the SPS 
        lxi d,GBL_SPS_SUPPLY_Q
        lxi h,immcb_status
        call MMCB_fetch
; follow chain to next MMCB in supply queue
; (link --> queue head pointer)
        lxi h,immcb_link
        lxi d,GBL_SPS_SUPPLY_Q
        mvi c,3
        call copy_bytes
; Which buffer was completed?
        lda which_sps
        cpi 1
        jz sps_alt      ; alternate SPS buffer
; Set new buffer address into hardware
; set primary hardware address
        lxi h,immcb_OI  ; 32 bits MM address 
        mvi a,0AEh      ; "A" buffer
        call SEND26     ; send 26 bits of MM address
        mvi a,1 ; expect alternate buffer next time
        sta which_sps
        ret
sps_alt:
; alternate SPS buffer completed
; Set new buffer address into hardware
        lxi h,immcb_OI   ; 32 bits MM address
        mvi a,0B0h      ; "B" buffer
        call SEND26     ; send 26 bits of address
        mvi a,0 ; expect primary next time
        sta which_sps
        ret
spsstop:
; SPS Supply queue ran dry.
; Stop SPS
        lda GBL_SOH+11
        ani 0FDh              ; b1=0
        sta GBL_SOH+11
; Log a SPS supply queue fault
        lxi h,GBL_SOH+167
        inr M
        mvi a,1
        sta GBL_SOH+12  ; at least one fault happened
        ret

; Array Photometer switched
aph_switched:
;        mvi a,'A'
;        call debug_putbyte
        lda GBL_SOH+11   ; APH active?
        ani 01h
        rz               ; no
;***** ignore if not in science mode
;       call ign

; Put "now" as buffer-switch time in Event Queue, code=33
        mvi a,33
  di
        call SOH_TOEVQ
  ei
; increment readout-done counter
        lxi h,aph_RO_done
        inr M
; APH R/O completed
; Read another MMCB from AP Supply Queue
        lda GBL_APH_SUPPLY_Q
        cpi 0FFh        ; head pointer is Null?
        jz aphstop      ; yes, stop the APH
        lxi d,GBL_APH_SUPPLY_Q
        lxi h,immcb_status
        call MMCB_fetch
; follow chain to next MMCB in cam supply queue
; (link --> queue head pointer)
        lxi h,immcb_link
        lxi d,GBL_APH_SUPPLY_Q
        mvi c,3
        call copy_bytes
; Which buffer was completed?
        lda which_aph
        cpi 1
        jz aph_alt      ; alternate APH buffer
; Set new buffer address into hardware
; set primary hardware address
        lxi h,immcb_OI   ; hi 16 bits of address
        mvi a,0A6h      ; "A" buffer
        call SEND16     ; send top 16 bits of MM address
; (low 10 bits of address are 0)
        mvi a,1 ; expect alternate buffer next time
        sta which_aph
        ret
aph_alt:
; alternate APH buffer completed
; Set new buffer address into hardware
        lxi h,immcb_OI   ; MM address
        mvi a,0A7h      ; "B" buffer
        call SEND16     ; send high 16 bits of address
        mvi a,0 ; expect primary next time
        sta which_aph
        ret
aphstop:
; APH Supply queue ran dry.
; Stop APH
        lda GBL_SOH+11
        ani 0FEh          ; b0=0
        sta GBL_SOH+11
; Log a APH supply queue fault
        lxi h,GBL_SOH+168
        inr M
        mvi a,1
        sta GBL_SOH+12  ; at least one fault happened
        ret

; Science Telemetry done 
tlm_done ds 1
tm_done:
        mvi a,1
        sta tlm_done
        ret

; use TLM channel to send one PVCF to spacecraft
; HL - points to 32-bit MM address of PVCF
; returns A: 0=timeout, 1=OK
MMCB_XMT_PVCF:
        push b
        mvi a,0         ; clear "TLM done flag"
        sta tlm_done
        mvi a,0A4h      ; A4=set Top 16 bits (A5=bottom 10)
        call SEND26     ; set 26-bits of MM address
; Start transmission
        mvi a,MM_TLM_START  ; (CDI data not used)
        call CDI_SEND
; wait for TLM done (set by buffer-switch ISR)
        lda GBL_SECONDS_CTR
        mov c,a
wtd:    call task
        lda GBL_SECONDS_CTR
        sub c        
        cpi 5           ; seconds
        jp timeout
        lda tlm_done
        cpi 1
        jnz wtd
        mvi a,1         ; success
xxit:   pop b
        ret
timeout:
        mvi a,0
        jmp xxit

; re-arm triggers 
; (called from timer interrupt service)
MMCB_REARM:
        push psw
        push h
        lda GBL_IDC_CTRL
; in this 16-bit word (high bit is b15)
; b13=1 arms AP; b9=1 arms MM/IM
        ori 02h
        sta foo
; is AP enabled?
        lda GBL_SOH+11
        ani 01h
        jz readpu    ; AP not enabled
; yes, set b13=1
        lda foo
        ori 20h
        sta foo
readpu:
; b5=1 arms DPU trigger
        lda GBL_IDC_CTRL+1
        ori 20h
        sta FOO+1
; send Re-arm
        lxi h,FOO
        mvi a,IDC_CTRL
        call CDI_SEND
; now more triggers can happen
        pop h
        pop psw
        ret
foo     ds 2

mmcb_initevq:
; Initialize private fetch-pointers to Event queue
; for each data type
        lhld GBL_EVENT_QUEUE_STORE_PTR
        shld cam_efetch
        shld spf_efetch
        shld sps_efetch
        shld aph_efetch
        ret


