;** code for Midi Cli commands **

; test if bottom byte of r0 is a char ('a'..'z' or 'A'..'Z')
; presumes top 3 bytes of r0 are clear
; return V_bit set if if true; clear if false;
IsChar ROUT
   CMP    r0, #"A"          ; 'A' < 'a'
   BICLTS pc, lr, #V_bit    ; => is false
   CMP    r0, #"z"
   BICGTS pc, lr, #V_bit    ; => is false
   CMP    r0, #"Z"
   ORRLES pc, lr, #V_bit    ; => is true
   CMP    r0, #"A"
   ORRGES pc, lr, #V_bit    ; => is true
   BICS   pc, lr, #V_bit    ; => is false

; convert character in r0 bottom byte to lower case
; presumes top 3 bytes are clear
; changes only upper-case characters
; affects only r0
ToLower ROUT
   CMP   r0, #"A"
   MOVLT pc, lr
   CMP   r0, #"Z"
   MOVGT pc, lr
   ADD   r0, r0, #"a"-"A" ; convert to lower case
   MOV   pc, lr

; case_insensitive comparison of 3 lsbytes of r0 and r1
; r1 chars are presumed lower case
; return Z set if equal
Compare ROUT
   STMFD sp!, {r0-r4, lr}
   MOV    r4, r0
   MOV    r3, #0
10 MOV    r0, r4, LSR r3      ; get the current byte
   AND    r0, r0, #&FF        ; mask out the char
   MOV    r2, r1, LSR r3      ; get char from match string
   AND    r2, r2, #&FF
   CMP    r2, #0              ; is this less than 3 chars, 0-terminated?
   BEQ    %FT30               ; exit loop with TRUE if match char is 0
   BL     IsChar              ; check that it is a character
   LDMVCFD sp!, {r0-r4, lr}
   BVC    Parse_Error
   BL     ToLower             ; convert to lower case if upper
   CMP    r0, r2              ; compare characters
   BNE    %FT30               ; exit with FALSE. char not equal
   ADD    r3, r3, #8
   CMP    r3, #24
   BLT    %BT10       ; loop for 3 bytes

30 LDMFD sp!, {r0-r4, lr} ; preserve z flag
   ORREQS pc, lr, #Z_bit
   BICS   pc, lr, #Z_bit

SkipSpaces ROUT  ; string pointed to by r0,
; returns with r0 points to after spaces + 1
; r3 is char pointed to by r0 - 1
   LDRB  r3, [r0], #1
   CMP   r3, #" "
   BEQ   SkipSpaces
   MOVS  pc, lr

;r0 is corrupted
MidiSound_Code ROUT
   STMFD  sp!, {r1-r4, lr}
   LDR    r12, [r12]  ; get wp
   MOV    r4, r1  ; number of parameters

   ADD    r1, r12, #ScratchBuffer
   MOV    r3, #0
   STR    r3, [r1]    ; clear buffer

   BL     SkipSpaces
   MOV    r2, r0  ; string pointer
   MOV    r0, r3  ; char after spaces

 ; clear and put 2 or 3 chars into ScratchBuffer
   BL     IsChar
   BVC    Parse_Error
   STRB   r0, [r1], #1
   LDRB   r0, [r2], #1
   BL     IsChar
   BVC    Parse_Error    ; there must be 2 chars at least
   STRB   r0, [r1], #1
   LDRB   r0, [r2], #1
   BL     IsChar
   STRVSB r0, [r1]

   CMP    r4, #1      ; number of parameters
   MOV    r4, #0     ; midi Port number
   BLE    %FT5

   MOV    r0, r2
   BL     SkipSpaces
; read Port number if there is one
   CMP    r3, #10   ; terminator?
   CMPNE  r3, #13
   CMPNE  r3, #0
   BEQ    %FT5
   CMP    r3, #"4"
   BGT    Parse_Error
   SUBS   r4, r3, #"1"  ; Port number (0..3)
   BMI    Parse_Error ; error if less than 1
; check there are no more chars
   LDRB   r3, [r0]
   CMP    r3, #10   ; terminator?
   CMPNE  r3, #13
   CMPNE  r3, #" "
   CMPNE  r3, #0
   BNE    Parse_Error
   
; translate "in", "off" or "out"
5  LDR   r0, [r12, #ScratchBuffer]     ; 4 bytes of command tail is all I should need

; decode parameter. Should be "off" "in" or "out"

   MOV   r1, #"o"
   ADD   r1, r1, #"f":SHL:8
   ADD   r1, r1, #"f":SHL:16
   BL    Compare
   MOVEQ r0, #0
   BEQ   %FT10
  
   MOV   r1, #"i"
   ADD   r1, r1, #"n":SHL:8
   BL    Compare
   MOVEQ r0, #1
   ADDEQ r0, r0, r4, LSL#1     ; combine Port number
   BEQ   %FT10

   MOV   r1, #"o"
   ADD   r1, r1, #"u":SHL:8
   ADD   r1, r1, #"t":SHL:16
   BL    Compare
   BNE   Parse_Error
   MOV   r0, #2
   ADD   r0, r0, r4, LSL#1     ; combine Port number
10 MOV   r1, #0
   SWI   XMIDI_SoundEnable
   BVS   Parse_Error
   LDMFD sp!, {r1-r4, pc}^

; r0 is corrupted
MidiChannel_Code
   STMFD sp!, {r1-r4, lr}
   MOV   r1, r0
   MOV   r2, #16    ; restrict range to 0..16
   MOV   r0, #10 :OR: (2_101 :SHL: 29) ; flags
   SWI   XOS_ReadUnsigned
   BVS   Parse_Error
   CMP   r2, #0
   BEQ   Parse_Error  ; must be between 1 and 16
   MOV   r1, r2   ; set basic channel, leave M unchanged
   MOV   r0, #0   ; do not change mode
   SWI   XMIDI_SetMode
   LDMFD sp!, {r1-r4, pc}

; r0 is corrupted
MidiMode_Code
   STMFD sp!, {r1-r4, lr}
   MOV   r1, r0
   MOV   r2, #4    ; restrict range to 0..4
   MOV   r0, #10 :OR: (2_101 :SHL: 29) ; flags
   SWI   XOS_ReadUnsigned
   BVS   Parse_Error
   CMP   r2, #0
   BEQ   Parse_Error  ; must be between 1 and 4
   MOV   r1, #0   ; leave basic channel, N, and M, unchanged
   MOV   r0, r2   ; change mode
   SWI   XMIDI_SetMode
   LDMFD sp!, {r1-r4, pc}

; r0 is corrupted
MidiStart_Code ROUT
   STMFD sp!, {r1-r4, lr}
   MOVS  r1, r1
   BEQ   %FT10  ; if no parameters then presume timing mode already set
   MOV   r1, r0
   MOV   r2, #&FF
   ORR   r2, r2, #&FF00    ; restrict range to 0..FFFF
   MOV   r0, #10 :OR: (2_101 :SHL: 29) ; flags
   SWI   XOS_ReadUnsigned
   BVS   Parse_Error
   CMP   r2, #0
   BEQ   Parse_Error  ; must be minimum of 1
   MOV   r0, r2   ; set tc transmission rate
   SWI   XMIDI_FastClock
10 SWI   XMIDI_TxStart
   LDMFD sp!, {r1-r4, pc}

MidiContinue_Code ROUT
   STMFD sp!, {lr}
   SWI   XMIDI_TxContinue
   LDMFD sp!, {pc}

MidiStop_Code
   STMFD sp!, {lr}
   SWI   XMIDI_TxStop
   LDMFD sp!, {pc}

;r0 is corrupted
MidiTouch_Code ROUT
   STMFD sp!, {r1-r4, lr}
   LDR   r12, [r12]  ; get wp
   ADD   r1, r12, #ScratchBuffer  ; buffer address
   MOV   r2, #4 + 3:SHL:29        ; buffer size, space terminates, and ignore '|'
   SWI   XOS_GSTrans
   BVS   Parse_Error
   CMP   r2, #4
   BGE   Parse_Error
   LDR   r0, [r1]                 ; 4 bytes of command tail is all I should need

; decode parameter. Should be "off" "on"

   MOV     r1, #"o"
   ADD     r1, r1, #"f":SHL:8
   ADD     r1, r1, #"f":SHL:16
   BL      Compare
   BNE     %FT10
   TEQP    pc, #I_bit:OR:3         ; disable irq for consistent mode state
   LDR     r0, [r12, #ModeFlagBits]
   ORR     r0, r0, #TouchSenseOff
   STR     r0, [r12, #ModeFlagBits]
   LDMFD   sp!, {r1-r4, pc}^
10 MOV     r1, #"o"
   ADD     r1, r1, #"n":SHL:8
   BL      Compare
   BNE     Parse_Error
   TEQP    pc, #I_bit:OR:3         ; disable irq for consistent mode state
   LDR     r0, [r12, #ModeFlagBits]
   BIC     r0, r0, #TouchSenseOff
   STR     r0, [r12, #ModeFlagBits]
   LDMFD   sp!, {r1-r4, pc}^

Parse_Error ROUT
   ADR   r0, ErrorBlock_MIDI_BadParameter
ReturnError   ; for * commands
   LDMFD sp!, {r1-r4, lr}
   ORRS  pc, lr, #V_bit

   MakeErrorBlock  MIDI_BadParameter

  END
