A routine taking a pointer to a NUL-terminated string into R4 and setting the carry if the test is successful, or clearing it otherwise.
275 | PSHR R5
2A0 | @@read MVI@ R4, R0
338 061 | SUBI #'a', R0
20B 01B | BMI @@rtn
04C | SLL R0, 2
04C | SLL R0, 2
048 | SLL R0
3E0 | XOR@ R4, R0
2A1 | MVI@ R4, R1
33C 002 | SUBI #2, R4
001 | SDBD
2BD 0C6 048 | MVII #@@tbl, R5
368 | @@loop CMP@ R5, R0
204 00D | BEQ @@rtn
001 | @@next SDBD
37D 0CC 048 | CMPI #@@so, R5
22C 008 | BNEQ @@loop
368 | CMP@ R5, R0
22C 01B | BNEQ @@read
379 06C | CMPI #'l', R1
22C 01F | BNEQ @@read
2B7 | @@rtn PULR R7
00F | @@tbl DECLE $00F
245 | DECLE $245
1E9 | DECLE $1E9
0C1 | DECLE $0C1
101 | DECLE $101
229 | DECLE $229
22F | @@so DECLE $22F
How?
Each note made of the ASCII codes \$(c_0,c_1)\$ is encoded as a single DECLE with the following formula:
$$((c_0-97)\times 32) \operatorname{xor} c_1$$
The edge case "sol" is encoded as "so" and put at the end of the lookup table. There's an additional test for the "l".
Full commented test code
ROMW 10 ; use 10-bit ROM width
ORG $4800 ; map this program at $4800
;; ------------------------------------------------------------- ;;
;; main code ;;
;; ------------------------------------------------------------- ;;
main PROC
SDBD ; set up an interrupt service routine
MVII #isr, R0 ; to do some minimal STIC initialization
MVO R0, $100
SWAP R0
MVO R0, $101
EIS ; enable interrupts
SDBD ; R5 = pointer into the test case index
MVII #tc.tbl,R5
MVII #$200, R3 ; R3 = backtab pointer
MVII #14, R1 ; R1 = number of test cases
@@loop MVI@ R5, R4 ; R4 = pointer to next string
SDBD
ADDI #tc.00, R4
PSHR R5 ; save the test variables
PSHR R3
PSHR R1
CALL mmb ; invoke our routine
PULR R1 ; restore the test variables
PULR R3
PULR R5
MVII #$88, R0 ; R0 = '1'
BC @@draw
MVII #$80, R0 ; or '0' if the carry is not set
@@draw MVO@ R0, R3 ; draw this character
INCR R3 ; increment the backtab pointer
DECR R1 ; next test case
BNEQ @@loop
DECR R7 ; done: loop forever
ENDP
;; ------------------------------------------------------------- ;;
;; test cases ;;
;; ------------------------------------------------------------- ;;
tc PROC
@@tbl DECLE @@00 - @@00, @@01 - @@00, @@02 - @@00, @@03 - @@00
DECLE @@04 - @@00, @@05 - @@00, @@06 - @@00, @@07 - @@00
DECLE @@08 - @@00, @@09 - @@00, @@10 - @@00, @@11 - @@00
DECLE @@12 - @@00, @@13 - @@00
;; truthy
@@00 STRING "far", 0
@@01 STRING "solace", 0
@@02 STRING "fire", 0
@@03 STRING "summit", 0
@@04 STRING "doctor", 0
@@05 STRING "blast", 0
@@06 STRING "subsidiary", 0
@@07 STRING "obsolete", 0
;; falsy
@@08 STRING "boat", 0
@@09 STRING "shrimp", 0
@@10 STRING "biscuit", 0
@@11 STRING "bullet", 0
@@12 STRING "college", 0
@@13 STRING "also", 0
ENDP
;; ------------------------------------------------------------- ;;
;; ISR ;;
;; ------------------------------------------------------------- ;;
isr PROC
MVO R0, $0020 ; enable display
CLRR R0
MVO R0, $0030 ; no horizontal delay
MVO R0, $0031 ; no vertical delay
MVO R0, $0032 ; no border extension
MVII #$D, R0
MVO R0, $0028 ; light-blue background
MVO R0, $002C ; light-blue border
JR R5 ; return from ISR
ENDP
;; ------------------------------------------------------------- ;;
;; our routine ;;
;; ------------------------------------------------------------- ;;
mmb PROC
PSHR R5 ; save the return address on the stack
@@read MVI@ R4, R0 ; R0 = current character
SUBI #'a', R0 ; turn it into an index in [0..25]
BMI @@rtn ; if the result is negative, it means
; we've reached the end of the string:
; we return with the carry cleared by SUBI
SLL R0, 2 ; multiply R0 by 32
SLL R0, 2
SLL R0
XOR@ R4, R0 ; XOR it with the next character
MVI@ R4, R1 ; and load a 3rd character in R1
SUBI #2, R4 ; rewind the pointer by 2 characters
SDBD ; R5 = pointer into the lookup table
MVII #@@tbl, R5
@@loop CMP@ R5, R0 ; compare the lookup table entry with R0
BEQ @@rtn ; match? (if yes, the carry is set)
@@next SDBD ; if we haven't reached the end of the table,
CMPI #@@so, R5
BNEQ @@loop ; try again with the next entry
CMP@ R5, R0 ; last test with 'so'
BNEQ @@read ; abort if it doesn't match
CMPI #'l', R1 ; otherwise, make sure it's followed by a 'l'
BNEQ @@read ; abort if it doesn't match
; otherwise, the carry is set
@@rtn PULR R7 ; return
;; lookup table: 'do', 're', 'mi', 'fa', 'la', 'si', 'so'
@@tbl DECLE ('d' - 'a') * 32 XOR 'o'
DECLE ('r' - 'a') * 32 XOR 'e'
DECLE ('m' - 'a') * 32 XOR 'i'
DECLE ('f' - 'a') * 32 XOR 'a'
DECLE ('l' - 'a') * 32 XOR 'a'
DECLE ('s' - 'a') * 32 XOR 'i'
@@so DECLE ('s' - 'a') * 32 XOR 'o'
ENDP
Output
screenshot from jzIntv
1. A CP-1610 opcode is encoded with a 10-bit value (0x000 to 0x3FF), known as a 'DECLE'.