; ==== ISUAL Flight Software -- Univ of California, Berkeley, SSL ====
; CMD.A -- ISUAL Command Processing
;   Obtains assembled commands from CMD_IN

; Includes
;   Command Processing Task
;   CMD_SEND_ECHO_PACKET
;   CMD_XGROUP - execute a grouped command
;   CMD_BEGIN_GROUP - begin loading a grouped command
;   CMD_END_GROUP - end loading a grouped command

;
; Revision History:
;  18-Dec-2002 - SPG - ISUAL Flight Software rev 7.3
;    First Official Release
;  31-Dec-2002
;    Removed debug.i
;  11-Jan-2003
;    added GBL_XGS_SP stack to make I_XGROUP recursive
;    added GBL_SOH_WAIT
;  14-Apr-2003
;    make END_GROUP offset zero-based
;  15-May-2003
;    length of 705 packet (TPA) changed from 70 to 62
;  29-May-2003
;    change CCSDS_SEND calls to CCSDS_SCIHDR
;   6-Aug-2003
;    fix double-echo of last cmd executed in a time-tagged group
; 18-Sep-2003 -- fix CMD_XGROUP to pop XG stack properly
; 19-Sep-2003 -- check Time-Tag List once a second
;   fix CMD_DTT - Time-Tag list dump
; 23-Sep-2003 - restored debug.i
; 29-Sep-2003 - now will properly detect full TT-list
;   minor cleanup in use of CMD_RESULT
; 21-June-2004 - reject all commands after sequence error
;   until next I_NOP
; **FSW rev 7.9***
; 17-May-2005 
;  - force CMD_SEQ to zero before storing in a group
;  - prevent I_XGROUP from executing during a group load
;  - skip sending TPA packets while in Standby mode
        include cmd.i
        include cmd_in.i
        include util.i
        include gbl.i
        include clock.i
        include ccsds.i
        include execute.i
        include task.i
        include ioports.i
        include debug.i

; Commands are received as bytes by interrupt service
; in CMD_IN.a  Command packets are assembled from a byte queue 
; by a state-machine.

; Commands are processed according to their packet ApID:
;  700 - TPA message -- these are captured as "bracketting pairs"
;         (may be echoed as telemetry packet ApID 705)
;  701 - MBR Reply message (Spacecraft signals OK to send PVCFs)
;  702 - Ground commands to be immediately executed.
;  703 - Time-Tagged Ground Commands
;  704 - Time Set; time of next PPS

; All ISUAL commands are defined in drawing 8564-X7

; Time-Tagged commands are all stored in RAM bank 03
TTBANK  equ 3 

; Additional commands may be stored in RAM or EEPROM
; as "command groups".  
; Format of each command group element:
;  byte 0 - FF=end of group; else=total command length + 1
;  byte 1 - first byte of command (opcode)
; Command groups are executed by:
;  /I_XGROUP BANK=X OFFSET=XX
;    which points to the first command in the group
  

; Command assembled from packet 
cmd_ApID   equ GBL_CMD    ; (low byte of packet ApID)
cmd_result equ GBL_CMD+1  ; 0=OK, else=error code
cmd_opcode equ GBL_CMD+2  ; operation code
cmd_seq    equ GBL_CMD+3  ; sequence number
cmd_args   equ GBL_CMD+4  ; arguments (up to 204 bytes)
; Note that "cmd_result" may be nonzero due
; to bad command checksum as well as execution errors

G   equ  GBL_CMD_WORKSPACE
soh_flag    equ G     ; ds 1  ; 1=SOH Task requests flush of command echoes
p2hdr       equ G+1   ; ds 2  ; pointer to secondary header
p_opcode    equ G+3   ; ds 2  ; pointer to opcode of command
cmd_length  equ G+5   ; ds 1  ; length - opcode thru last param
gflag       equ G+6   ; ds 1  ; 1=command group being loaded; 0=not
eptr        equ G+7   ; ds 2  ; pointer to TT list element
eptr_max    equ G+9   ; ds 2  ; pointer to TT el't with max_diff
time6       equ G+11  ; ds 6
load_offset equ G+17  ; ds 2
load_bank   equ G+19  ; ds 1

MAX_NECHOES equ 16  
ECHO_SIZE   equ 14
; ==Data portion of Echo Packet==
nechoes     equ G+20   ; ds 1
Echo_Array  equ G+21   ; ds ECHO_SIZE*MAX_NECHOES
; ======
pecho       equ G+255  ; ds 2    ; pointer to next slot in packet
sqecho      equ G+257  ; ds 2    ; Packet Source Seq Number
sqtpa       equ G+259  ; ds 2

Now         equ G+261  ; ds 6
Diff        equ G+267  ; ds 6
Max_Diff    equ G+273  ; ds 6
have_cmd_to_execute equ G+279 ; ds 1
;have_cmd_to_execute db  88h

temp6       equ G+280  ; ds 6
xbank       equ G+286  ; ds 1
xaddr       equ G+287  ; ds 2


CMD_INIT:
        push psw
        push h

        mvi a,0
        sta soh_flag    ; not time to write Echo packet
        sta gflag       ; group not being loaded

; Setup empty Echo packet data area
        lxi h,nechoes
        mvi M,0         ; packet is empty
        inx h
        shld pecho      ; pointer to 1st slot in packet
        
; clear Time-Tagged Command List
        mvi a,TTBANK    ; Time-Tagged Bank
        out BANK_SELECT_O ; set bank register
        lxi h,4000h     ; first byte in banked memory
        mvi M,0FFh      ; mark end of list

; zero packet primary header source-sequence counters
        lxi h,0
        shld sqga
        shld sqtt

; == rev 7.6 - initialise stack for xbank and xaddr
; allow one command group to call another
        lxi h,GBL_XGS+12        ; 4 3-byte stack elements
        shld GBL_XGS_SP
; ====

; start the Command Task
        lxi d,CMD_Task
        lxi h,GBL_CMD_PROC_STACK+stack_size
        call task_new

        pop h
        pop psw 
        ret

; ====Command Task====
CMD_Task:
        call task
; Try to get an assembled command from 
; the CMD_IN state-machine.
        call CMD_RECEIVE
        cpi 0
        jz cktt         ; no command received
; command received; stored in GBL_CMD
        lda CMD_ApID
; Look at ApID of packet
; to determine what to do with this command
        cpi 00h         ; 700 - TPA message     
        jz do_TPA
        cpi 01h         ; 701 - MBR Reply
        jz MBR_reply
        cpi 02h         ; 702 - Immediate Exec
        jz immed
        cpi 03h         ; 703 - Time-Tagged
        jz timetagged
        cpi 04h         ; 704 - Time of Next PPS
        jz timeset
; Invalid ApID; increment error-counter
        lxi h,GBL_SOH+49
        inr M
        mvi a,0FDh      ; FD - return code for invalid ApID
        sta CMD_Result
invech: call CMD_ECHO   ; echo bad command
        jmp cktt

; command was time-tagged; store in list
timetagged:
; Increment commands-received
        lxi h,GBL_SOH+37
        call INCBIG
; check ISUAL command sequence number
        call ckseq
        lda CMD_Result  ; any problems?
        cpi 0
        jnz invech      ; problems; echo as invalid
; store command in time-tagged list
        call CMD_STORE_TT
        lda CMD_result  ;
        cpi 3           ; 3=ERR_TTFULL (see EXECUTE.A)
        jnz cktt        ; no problem
; time-tagged list is full
        call CMD_ECHO
        jmp cktt

; Execute and echo an Immediate Command
immed:  
; Increment commands-received
        lxi h,GBL_SOH+37
        call INCBIG
; check ISUAL command sequence number
        call ckseq
        lda CMD_Result  ; any problems?
        cpi 0
        jnz invech      ; error; echo as invalid
        lda CMD_opcode
; ***7.9***
; (prevent I_XGROUP from executing during group-load)
        cpi 70h         ; I_END_GROUP
        jz xecute       ; always execute I_END_GROUP
        lda gflag       ; group load in progress?
        cpi 1
        jz load_command ; yes; don't execute command
; group load not in progress
        lda CMD_opcode
; ***
; I_XGROUP (while being received) gets echoed before execution
        cpi 72h         ; I_XGROUP
        jz echx
; same for I_ECHO_DISABLE
        cpi 08Eh
        jnz notx
echx:   call CMD_ECHO
        call CMD_EXEC
        jmp cktt
notx:
        cpi 70h         ; I_END_GROUP
        jz xecute       ; always execute this one
; is a command group is being loaded?
        lda gflag
        cpi 1
        jz load_command ; yes, don't execute
xecute:
        call CMD_EXEC   ; good command; execute
        call CMD_ECHO   ; echo the command
        
; Search time-tagged list and
; execute all commands which are ready
cktt:   call task
        lda GBL_TT_ABORT
        cpi 0   
        jz noab
; "I_SAFE" command was executed.  
; Stop executing Time-tagged commands, and
; purge the list
        mvi a,TTBANK    ; Time-Tagged Bank
        out BANK_SELECT_O ; set bank register
        lxi h,4000h     ; first byte in banked memory
        mvi M,0FFh      ; mark end of list
        mvi a,0
        sta GBL_TT_ABORT ; reset switch
; zero number of commands in list
        lxi h,0
        shld GBL_SOH+67  ; 16 bit counter
        jmp ck_echo

tick0   db 0 
noab: 
; check time-tagged list no more often than
; once per second  
        lda tick0        ; last second 
        mov c,a
        lda GBL_SECONDS_CTR
        cmp c            ; compare with current
        jz ck_echo       ; not new second yet
        sta tick0        ; new second
        call CMD_GET_TT  ; get time-tagged command
        cpi 0
        jz ck_echo      ; did not find commands to execute

; Execute and echo a time-tagged command

; If time-tagged command was XGROUP, echo it first
        lda CMD_opcode
        cpi 72h         ; I_XGROUP
        jnz echx2
; ***6-Aug-03***
        call CMD_ECHO   ; echo first
        call CMD_EXEC   ; then execute
        jmp ittxi       
; Execute and echo a time-tagged command
echx2:  call CMD_EXEC   ; execute
        call CMD_ECHO   ; then echo
; 14-Jul-2004 - increment/decrement counters
ittxi:
; increment number of TT's executed
        lxi h,GBL_SOH+65
        call INCBIG
; decrement number of active Time-tagged elements
        lxi h,GBL_SOH+67 
        call DECBIG
        jmp cktt        ; repeat until all are done ...

; All ready time-tagged commands have been executed
ck_echo:
; If SOH task task has made a request,
; flush an Echo packet, even if packet only partially filled
        lda soh_flag
        cpi 0
        jz cmd_task             ; no request
        call SEND_ECHO_PACKET
        mvi a,0
        sta soh_flag            ; reset request flag
; continue command task loop ...
        jmp cmd_task

; Load command into RAM as part of a group
load_command:
        lda load_bank
        out BANK_SELECT_O
        lhld load_offset
        lda cmd_length
        inr a           ; +1 for length byte itself
        mov M,a         ; store length
        inx h
; copy the command itself
        dcr a
        mov c,a         ; length of command
        lxi d,CMD_Opcode ; DE - source; HL - destination
        xchg
        call copy_bytes
; **7.9**
; force CMD_SEQ to zero 
        mvi a,0
        sta CMD_SEQ
; save pointer, ready for next command loaded
        xchg
        shld load_offset
; echo the loaded command
        mvi a,4         ; (becomes 2) means Loaded group
        sta cmd_apid
        call CMD_ECHO  
        jmp cktt


CMD_BEGIN_GROUP:
; A  - bank
; HL - offset
        push psw
        push d
        push h
        sta load_bank
        lxi d,4000h     ; base of banked area
        dad d           ; add to offset
        shld load_offset
        mvi a,1
        sta gflag       ; 1=group is being loaded
        pop h
        pop d
        pop psw
        ret

CMD_END_GROUP:
        lda load_bank
        out BANK_SELECT_O   ; set load bank
        lhld load_offset
        mvi M,0FFh      ; mark end-of-group
; note this offset in SOH
        mov a,h
        ani 03Fh        ; effectively subtract 4000h
        sta GBL_SOH+172 ; IDH_G_OFFSET
        mov a,l
        sta GBL_SOH+173
        mvi a,0
        sta gflag       ; clear group-load flag
        ret

; =========
; Echo Generation


CMD_ECHO:
; Generate Echo for command in GBL_CMD
        push psw
        push d
        push h
; === rev 7.6
; hold off echo SOH finishing housekeeping output
ckka:   lda GBL_SOH_WAIT
        cpi 1
        jnz nota
        call task
        jmp ckka
nota:
; ===
; effect of I_ECHO_DISABLE and I_ECHO_ENABLE
        lda GBL_ECHO_DISABLE
        cpi 1
        jz eexit

; don't echo I_MEMDUMP
        lda cmd_opcode
        cpi 1Eh         ; I_MEMDUMP
        jz eexit
; don't echo I_PATCHx or I_DSP
        cpi 04Ah        ; I_PATCH1
        jz patchable
        cpi 04Bh        ; I_PATCH2
        jz patchable
        cpi 04Ch        ; I_PATCH4
        jz patchable
        cpi 074h        ; I_DSP
        jz patchable
        jmp gogo
patchable: jmp gogo
gogo:
; get current pointer to slot in echo packet
        lhld pecho      

        lda CMD_Opcode  
        mov M,a         ; store command opcode
        inx h

        lda cmd_seq
        mov M,a         ; and sequence number
        inx h

; convert ApID into 0=executed, 1=queued, 2=Group Load
        lda CMD_ApID    
        sui 2           ; 2->0, 3->1 (and 4->2, 5->3) 
        mov M,a         ; exec/queued code
        inx h

; HL now points to echo-time
        call CLOCK_READ ; time when echoed = Now
; change time to 10-byte format
        mov d,h
        mov e,l         ; DE=HL (conversion is in-place)
        call CLOCK_6_TO_10
        lxi d,10
        dad d           ; point past the time

        lda CMD_Result
        mov M,a         ; store execution result code
        inx h

        shld pecho      ; pointer to next echo

; increment number of echoes in packet
        lda nechoes
        inr a           
        sta nechoes

        cpi MAX_NECHOES ; echo packet is full?
        jnz eexit       ; no
; Echo packet is full,  send it
        call SEND_ECHO_PACKET
eexit: 
        pop h
        pop d
        pop psw
        ret

; request from SOH task to flush accumulated echoes
CMD_SEND_ECHO_PACKET:
        mvi a,1
        sta soh_flag
        ret

SEND_ECHO_PACKET:
        push psw
        push b
        push d
        push h
; don't send packet if empty.
        lda nechoes     ; any echoes in packet?
        cpi 0
        jz cexit        ; none; don't send packet

        mvi a,03h       ; ApID = 703
        lxi b,244       ; packet length
        lhld sqecho     ; this ApID's source sequence number
        xchg    
        lxi h,nechoes   ; point to data
        call CCSDS_HSKP ; generate CCSDS and send it out

; Reset Echo packet data area to empty
        mvi a,0
        sta nechoes     ; packet is empty
        lxi h,echo_array
        shld pecho      ; pointer to 1st slot
        lhld sqecho     ; increment source-sequence count
        inx h
        shld sqecho
cexit:  
        mvi a,0
        sta soh_flag
        pop h
        pop d
        pop b
        pop psw
        ret


CMD_RECEIVE:
; Try to get an assembled command from 
; the CMD_IN state-machine.
; returned:
;  A - 0=no command
;      1=command received; stored in GBL_CMD
        push b
        push d
        push h
        mvi a,0
        sta CMD_Result  ; assume result=0 (OK)
; Run the command input state-machine (see CMD_IN.A)
        call CMD_IN
; CMD_IN returns:
;  A  - 0=no command available, 1=command is available
;  B  - low byte of ApID
;  C  - length of data part of packet (incl. cksum and trailer)
;  HL - addr of assembled command (1st byte of 2ndary header) 
        cpi 0
        jz crxit       

; command was assembled from command byte queue
        shld p2hdr      ; save pointer to 2ndary header
        lxi d,10        
        dad d
        shld p_opcode   ; point to command opcode
        mov a,c         ; length of data
        sui 12          ; deduct for 2ndhdr, cksum and trailer
        sta cmd_length  ; length - op thru end of params

; copy Opcode and Params into GBL_CMD
        lxi d,CMD_opcode
        mov c,a         ; C=length
        call copy_bytes

; store lower byte of ApID (02-Immediate, 03-TimeTagged)
        mov a,b
        sta CMD_ApID

; increment commands-received 
; [unless command is 704 (time-set) or 700 (TPA) ]
        cpi 4           ; ApID=704 ?
        jz vcs
        cpi 0           ; ApID=700 ?
        jz vcs
; record the time at which command was received
        lxi h,GBL_SOH+39
        call CLOCK_READ
; as 10-byte time
        mov d,h
        mov e,l         ; convert in place
        call CLOCK_6_TO_10

; verify checksum
vcs:    lhld p2hdr
        lda cmd_length  
        adi 10          ; include 2ndary header
        mov c,a         ; byte count
        mvi a,0         ; clear checksum accumulator
vchk:   xra M
        inx h
        dcr c
        jnz vchk
        cmp M           ; calculated matches checksum in packet?
        jz crxok        ; yes
; Bad checksum
; increment error-counter
        lxi h,GBL_SOH+107
        inr M
        mvi a,0FEh      ; FE - return code for bad checksum
        sta CMD_Result
crxok:  mvi a,1         ; return=1; command was assembled
crxit:
        pop h
        pop d
        pop b
        ret

; Spacecraft sent a reply to the MBR message
MBR_reply:
        mvi a,1
        sta GBL_MBR_REPLY       ; set flag
; this will be picked up in the SC task (see "SC.a")
        jmp CMD_task


; Process a TPA Command Packet
do_TPA:
; == rev 7.6
; skip TPA echoes while SOH finishing housekeeping
        lda GBL_SOH_WAIT
        cpi 1
        jz CMD_Task     ; Nom/Red active
; ===
; ***7.9***
; skip sending TPA packets in standby mode
        lda GBL_SCIENCE_MODE
        cpi mode_Standby
        jz CMD_Task
; ***
        lxi h,GBL_SOH+31  ; count another TPA
        call INCBIG

; Send a copy of TPA as a telemetry packet
        mvi a,05h       ; ApID = 705
        lxi b,62        ; total packet length
        lhld sqtpa      ; this ApID's source sequence number
        xchg    
        lhld p2hdr      ; point to TPA time
        call CCSDS_HSKP ; generate CCSDS and send it out
        lhld sqtpa
        inx h
        shld sqtpa      ; increment source sequence

; Update a "bracketting pair" of TPA data blocks
; for use in annotating science products
; TPA1=TPA2
        lxi h,GBL_TPA2
        lxi d,GBL_TPA1
        mvi c,TPA_LENGTH
        call copy_bytes
; TPA2=new
        lhld p2hdr       ; point to TPA time
        lxi d,GBL_TPA2
        mvi c,TPA_LENGTH
        call COPY_BYTES 
        jmp CMD_Task

; Time-set -- pick off Time
; and set in clock registers, ready to be set
; on the next pulse-per-second tick.
timeset:
        lda GBL_TEST
        cpi 0           ; Timeset enabled
        jz CMD_Task     ;  no; ignore
; Setting time
; read the current time; store in Housekeeping
        lxi h,GBL_SOH+126   ; IDH_704_TIME
        call CLOCK_READ
; store "most recent time-set" in Housekeeping
        lhld p2hdr      ; point to time
        lxi d,GBL_SOH+116
        mvi c,10
        call COPY_BYTES
; convert 10-byte time to 6-byte
; and set ISUAL clock
        lhld p2hdr      ; 10-byte time
        xchg
        lhld p2hdr      ; HL=DE - convert in-place
        call CLOCK_10_TO_6 ; convert to 6-byte time
        call CLOCK_SET  ; set in registers
; convert IDH_704_TIME to 10-bytes
        lxi h,GBL_SOH+126
        lxi d,GBL_SOH+126
        call CLOCK_6_TO_10
; count a time-set statistic
        lxi h,GBL_SOH+100
        inr M
; reset time-not-set fault
        mvi a,0
        sta GBL_SOH+30
        sta GBL_TEST    ; disable time-sets
        jmp CMD_Task

; == Procedures for Time-Tagged List ==

; Bank "TTBANK" contains Time-Tagged Command List
; List Element Format:
TT_STATUS  equ 0  ; 0=empty element, 1=command stored
TT_LENGTH  equ 1  ; total element length (header + command)
TT_TIME    equ 2  ; time-tag (6 bytes, internal format)
TT_CMD     equ 8  ; command starts here
TT_HDR_LENGTH equ 8
; TT_STATUS 0FFh marks end of list

; share some global memory with I_EEPROM_TO_MM command
gblw     equ GBL_DSP_BUF
; pointers
pktptr        equ gblw    ; ds 2
ttlptr        equ gblw+2  ; ds 2
; data area for 719 packet
ttd_ncommands equ gblw+4    ; ds 1
ttd_pseq      equ gblw+5    ; ds 1
ttd_data      equ gblw+6    ; ds 19*12

; dump time-tagged list - generate a series of ApID=719 packets
CMD_DTT:
        mvi a,0
        sta ttd_pseq     ; dump packet sequence = 0
; Set Bank
        mvi a,TTBANK
        out BANK_SELECT_O
        lxi h,4000h     ; start of banked memory window
        shld ttlptr
; loop for each packet
ttd0:   lxi h,ttd_data  ; beginning of command list in pkt
        shld pktptr
        mvi a,0
        sta ttd_ncommands ; number of commands = 0
; loop for each packet element
; copy one command from List to Packet
ttd1:   lhld ttlptr
        mov a,M
        cpi 0FFh        ; FF marks end of list
        jz ttdend     
        cpi 0           ; element is empty?
        jz ttlnext        ; yes, skip it
; copy command into packet
; first, the time
        inx h
        inx h           ; point to time in list
        xchg
        lhld pktptr     ; pointer to time in pkt
        xchg            ; HL-List DE-Packet
        mvi c,6
        push d
        push d
        call copy_bytes
; store time in 10-byte Astrium format
        pop h
        pop d
        call clock_6_to_10
 
; follow time by op and seq
        lxi h,10
        dad d    
        push h
        lhld ttlptr
        lxi d,8
        dad d           ; HL points to op in list
        pop d           ; DE points to packet
        mvi c,2
        call copy_bytes ; copy op and seq

; advance packet pointer
        lhld pktptr
        lxi d,12        ; 10-byte-time, op, seq
        dad d
        shld pktptr
; increment number of Commands in packet
        lxi h,ttd_ncommands
        inr M

; advance TT List pointer
ttlnext:  lhld ttlptr
        inx h           ; step to length byte in header
        mov e,M         ; get element length into DE
        mvi d,0
        lhld ttlptr       ; element pointer
        dad d           ; add element length
        shld ttlptr       ; now pointing to next element

; test for full packet
        lda ttd_ncommands
        cpi 19          ; 19 commands max per packet
        jnz ttd1        ; continue
; generate the packet
        call send_ttd_packet
; Increment the sequence number of ApID=719 packets
        lxi h,ttd_pseq
        inr M
; go generate another packet
        jmp ttd0

; End of Time-tagged list
ttdend:
; if anything in packet, send it
        lda ttd_ncommands
        cpi 0
        rz 
        call send_ttd_packet
        ret

send_ttd_packet:
        mvi a,19h       ; ApID = 719
        lxi b,248       ; packet length
        lhld sqtt       ; this ApID's source sequence number
        xchg    
        lxi h,ttd_ncommands   ; point to data
        call CCSDS_HSKP ; generate CCSDS and send it out
        lhld sqtt       ; increment source-sequence count
        inx h
        shld sqtt
        ret
sqtt    ds 2


CMD_STORE_TT:
; Add command from GBL_CMD,
;  either appending to list,
;  or replacing an "empty" element.
; If command list is full, store 3 in CMD_Result and return.
        push b
        push d
        push h
        lxi h,GBL_SOH+63  ; number of time-tagged commands received
        call INCBIG
; Set Bank
        mvi a,TTBANK
        out BANK_SELECT_O
; First, convert Time from 10-byte to 6-byte
; to be compatible with the ISUAL time-of-day clock
        lhld p2hdr        ; secondary header (10-byte time)
        lxi d,time6       ; convert to 6-byte time
        call CLOCK_10_TO_6
; First, try to find an empty List Element
        lxi h,4000h     ; start of banked memory window
        shld eptr
findmt: mov a,M
        cpi 0FFh        ; FF marks end of list
        jz new_entry    ;  search went off the end
        cpi 0           ; element is empty?
        jnz nxte        ; no
; TT_STATUS 0 - entry is available, 
; but to be useable, it must also be exactly the
; same size (command length + TT header)
        lda cmd_length
        adi TT_HDR_LENGTH   ; add for TT element header
        inx h           ; step to length byte in header  
        cmp M           ; lengths match?
        jz gotit        ; yes
; length not the same
; continue searching for an empty element
nxte:   lhld eptr
        inx h           ; step to length byte in header
        mov e,M         ; get element length into DE
        mvi d,0
        lhld eptr       ; element pointer
        dad d           ; add element length
        shld eptr       ; now pointing to next element
        jmp findmt      ; keep looking

; Suitable empty List-element found
; Fill it out with the command
gotit:  lhld eptr       ; point to element
        mvi M,1         ; 1 = element is active
        inx h           ; move past the status
        inx h           ; skip element length
; copy in time-stamp (6-byte time)
        xchg            ; DE is destination
        lxi h,time6     ; 6-byte time derived from command
        mvi c,6
        call COPY_BYTES
; copy the command itself
        lhld p_opcode
        lda cmd_length ; from opcode to end of parameters
        mov c,a
        call COPY_BYTES ; (DE had been advanced by previous COPY_BYTES) 
; Increment number of active Time-tagged elements
istxit: lxi h,GBL_SOH+67 
        call INCBIG
stxit:  
        pop h
        pop d
        pop b
        ret

new_entry:
; Appending a new element to the list.
; First, see if there is room for it.
; ('eptr' now points to end-of-list marker - the FF)
        lda cmd_length 
        adi TT_HDR_LENGTH  ; plus TT el't header
        mov e,a
        mvi d,0         ; DE=new element length
        lhld eptr
        dad d           ; HL += element length
        inx h           ; allow for end-of-list marker byte
; HL now points to where the new end-of-list marker would go.
; Is this past the end of the banked memory area?
        mov a,h         
        ani 80h         ; past the end if HL >= 8000h
        jz room         ;  there's room
; Not room enough.
noroom:
        mvi a,1
        sta GBL_SOH+21  ; IDH_TTLIST_FAULT
; Result Code 3 means Time-Tagged list overflowed
        mvi a,3      
        sta CMD_Result  ; result_code
        jmp stxit    

; There's room.  Append the new element.
room:   lhld eptr       ; point to storage for new entry
        mvi M,1         ; TT_STATUS 1 = element is not empty
        inx h           ; point to length byte
        lda cmd_length
        adi TT_HDR_LENGTH  ; element length includes header
        mov M,a         ; store length in element
        inx h
; copy the time-stamp into the element
        xchg            ; DE points to time-stamp in element
        lxi h,time6     ; 6-byte time derived from Command
        mvi c,6
        call COPY_BYTES
; copy the command itself
        lhld p_opcode
        lda cmd_length  ; from opcode to end of parameters
        mov c,a
; (DE had been updated by COPY_BYTES)
        call COPY_BYTES 
; Mark new end-of-list
        xchg
        mvi M,0FFh
        jmp istxit      ; 14-Jul-04 (was jmp stxit)


CMD_GET_TT:
; Search TT List for next command to be executed
;   based on current time
; Result stored in GBL_CMD, after which
; the TT list element is marked "empty" (available for re-use)
; Return:
;  A - 0 = no command found
;      1 = command stored in GBL_CMD
        push b
        push d
        push h

; clear flag
        mvi a,0
        sta have_cmd_to_execute

; For each command, calculate Diff=Now-TimeTag
; If Diff<0, exec time has not yet come.
; If Diff>=0, find oldest command - with Maximum Diff

; set Max_Diff=0
        lxi h,Max_Diff
        mvi c,6
zmax:   mvi M,0
        inx h
        dcr c
        jnz zmax

; Get time of Now
        lxi h,Now
        call CLOCK_READ

; scan the Time-Tagged List
        mvi a,TTBANK
        out BANK_SELECT_O  ; set bank register     
        lxi h,4000h     ; start of banked memory
tloop:  shld eptr
 out WATCHDOG_PET_O ; ****
        mov a,M
        cpi 1           ; active entry?
        jz tryit        ; yes, go try it
        cpi 0FFh        ; end of list?
        jz done         ; yes
next_element:
        lhld eptr
; step to next element in list
        inx h           ; point to length field
        mov e,M         ; make length into 16 bits
        mvi d,0
        lhld eptr
        dad d           ; point HL to next element
        jmp tloop

; End of List.
; Did we find a command to execute?
done:   lda have_cmd_to_execute
        cpi 0
        jz dxit         ; (0 - cmd not found)
; Command was found, copy to GBL_CMD.
        mvi a,2         ; "executed"
        sta cmd_ApID
        lhld eptr_max   ; point to element that had max Diff
        inx h      
        mov a,M         ; length
        sui TT_HDR_LENGTH ; deduct TT header
        mov c,a         ; C = command length
        lhld eptr_max   ; point to element that had max Diff
        lxi d,TT_CMD
        dad d           ; HL now points to command
        lxi d,cmd_opcode ; DE points to GBL_CMD 
        call copy_bytes
; use last byte of time as command sequence number
        lhld eptr_max   ; point to element that had max Diff
        lxi d,TT_TIME+5 
        dad d
        mov a,M
        sta cmd_seq
; mark TTList element inactive
        lhld eptr_max
        mvi M,0         ; mark element empty

; 1=command was found
        mvi a,1

dxit:   pop h
        pop d
        pop b
        ret
        
; searching TT list; found Active TTLIST entry 
tryit:  
; Is time-tag Now or Earlier?
        lhld eptr
        lxi d,TT_TIME
        dad d           ; HL points to time in Element
; Difference the times: Diff=Now-Element
; If Diff<0 (tag is after Now), skip this element.
        push h
; Temp6=Now
        lxi h,Now
        lxi d,Temp6
        mvi c,6
        call copy_bytes
        pop h
        lxi d,5
        dad d           ; point to last byte of time-tag
; Diff = Temp6 = (Now - time-tag)
        call sub6       ; 6-byte subtract, condition returned
        jnz next_element  ; Diff<0
; Diff >= 0
        lxi h,Temp6     ; Diff=temp6
        lxi d,Diff
        mvi c,6
        call copy_bytes
; We want the element with maximum Diff.
;  Temp6 = Diff-Max_Diff
        lxi h,Diff
        lxi d,Temp6
        mvi c,6
        call copy_bytes
        lxi h,Max_Diff+5
        call sub6        ; Temp6=Diff-Max_Diff
        jnz next_element ; < 0 
; replace Max_Diff, and store the associated Element pointer
        lxi h,Diff
        lxi d,Max_Diff
        mvi c,6
        call copy_bytes
        lhld eptr
; keep element pointer associated with max diff
        shld eptr_max  
; note that at least 1 "time-ready" command was found
        mvi a,1
        sta have_cmd_to_execute
; continue examining TTList, looking for a bigger Max_Diff
        jmp next_element


CMD_EXEC:
; Execute command in GBL_CMD
        push psw
        push b
        push h

; All the code for Command Execution is in "EXECUTE.A"
; HL points to opcode 
; C=0 for immediate, 1 for time-tagged
        lxi h,CMD_Opcode   ; point to Opcode
        lda CMD_ApID
        mov c,a            ; 0=immediate exec, 1=TTag
        call EXECUTE    
; A - returned with result code
        sta CMD_Result
        pop h
        pop b
        pop psw
        ret


CMD_XGROUP:
; Execute a group of commands in banked memory
;  A - bank
;  HL - offset
        push psw
        push b
        push d
        push h
; push values of xbank and xaddr onto "xgroup stack"
        push h          ; >> preserve HL
        mov c,a         ; >> preserve A
        lhld GBL_XGS_SP ; xgroup stack pointer
; push xbank
        dcx h
        lda xbank
        mov M,a
; push xaddr
        dcx h
        lda xaddr
        mov M,a
; push xaddr+1
        dcx h
        lda xaddr+1
        mov M,a          
        shld GBL_XGS_SP
        mov a,c         ; << restore A
        pop h           ; << restore HL
; ====
        sta xbank
        lxi d,4000h     ; base of banked memory
        dad d           ; HL = exec address
        shld xaddr
xagain: call task
        lda xbank
        out BANK_SELECT_O       ; set bank
; build command in GBL_CMD
        mvi a,5         ; (becomes 3) means "Group Exec"
        sta CMD_ApID
        mvi a,0
        sta CMD_RESULT  ; assume OK
        sta GBL_SOH+171 ; IDH_GAB_RES=0
; Is the end of the sequence?
        lhld xaddr
        mov a,M         ; get length
        cpi 0FFh        
        jz xxit         ; the end
        dcr a           ; deduct for length byte itself
        mov c,a         ; command length
; copy command itself
        inx h
        lxi d,CMD_Opcode
        call copy_bytes
; (this advances HL to next command's length byte)
        shld xaddr
; Execute and echo command
        call CMD_EXEC
        call CMD_ECHO
; If error return, abort execution of group
        lda CMD_Result
        cpi 0
        jz xagain       ; no error, continue with group

; == group abort ==
        call group_abort  ; send Group Abort packet
; Then skip out of group-execution

xxit:  
        lhld GBL_XGS_SP         ; stack pointer
; pop xaddr+1
        mov a,M
        sta xaddr+1
        inx h
; pop xaddr
        mov a,M
        sta xaddr
        inx h
; pop xbank
        mov a,M
        sta xbank
        inx h 
        out BANK_SELECT_O       ; set bank
        shld GBL_XGS_SP

        pop h
        pop d
        pop b
        pop psw
        ret



; send a "Group Abort" packet
ga_data ds 2
sqga    ds 2
group_abort:
; A - return code of offending command
        push psw
        push b
        push d
        push h
; store SOH info
        sta GBL_SOH+171 ; IDH_GAB_RES  
        lda CMD_OPCODE
        sta GBL_SOH+170 ; IDH_GAB_OP
        lda CMD_SEQ
        sta GBL_SOH+169 ; IDH_GAB_SEQ
        sta ga_data+1
        mvi a,1
        sta ga_data     ; Alert code=1
        mvi a,1Fh       ; ApID = 71F
        lxi b,20        ; packet length
        lhld sqga       ; this ApID's source sequence number
        xchg    
        lxi h,ga_data   ; point to data
        call CCSDS_HSKP ; generate CCSDS and send it out
; increment source seq number
        lhld sqga
        inx h
        shld sqga
        pop h
        pop d
        pop b
        pop psw 
        ret


; do a 6-byte subtract
; Result in temp6
sub6:           ; HL - points to low byte of subtrahend

        lda temp6+5
        sub M             
        sta temp6+5
        dcx h

        lda temp6+4
        sbb M           ; subtract with borrow
        sta temp6+4
        dcx h

        lda temp6+3
        sbb M           ; subtract with borrow
        sta temp6+3
        dcx h

        lda temp6+2
        sbb M           ; subtract with borrow
        sta temp6+2
        dcx h

        lda temp6+1
        sbb M           ; subtract with borrow
        sta temp6+1
        dcx h

        lda temp6+0
        sbb M           ; subtract with borrow
        sta temp6+0

; test for negative
        lda temp6
        ani 80h

        ret

; Check the ISUAL sequence number on the command
expected_seq db 55h
sqerrflg     db 0
ckseq:  lxi h,expected_seq
        lda cmd_opcode
; reject all commands after a sequence error
; but always execute I_STANDBY and I_SAFE
        cpi 03Eh        ; I_STANDBY?
        jz as_expected
        cpi 06h         ; I_SAFE
        jz as_expected
        cpi 20h         ; I_NOP ?
        jnz notnop
; I_NOP resets the sequence number and error flag
        mvi a,0
        sta sqerrflg    ; reset flag
        lda cmd_seq     ; get seq from command
        mov M,a         ; store
        jmp as_expected

notnop:
; not NOP, STANDBY or SAFE
        lda sqerrflg    ; previous seq err?
        cpi 1
        jz sqerr        ; yes, reject this command too
; is seq on command as expected?
        lda cmd_seq
        cmp M
        jz as_expected
; sequence number not as expected
        mvi a,1
        sta sqerrflg    ; set error flag
; store an error code
sqerr:  mvi a,10        ; error code
        sta cmd_result
        ret
; sequence number as expected
; increment to be ready for next command
as_expected:
        inr M
        ret

