Skip to main content

Register Operations

This booklet covers the register opcodes 0x100x21, the register-literal arithmetic opcodes 0x2A0x2E, and the dedicated 0x30 7-segment encoder and 0x32 three-register operation: writing literals, copying, incrementing, port transfer, shifting, bitwise logic, register-register and register-literal arithmetic, random numbers, byte composing and swapping.

All registers R00~RFF hold 16-bit values. When a register index travels in a 16-bit whole operand, it is written as 00 followed by the register index — e.g. 001C means register R1C. Operand interpretation modes and the port system table are defined once in Brief.

0x10 Write Register (FZ)​

Write an immediate value into a register: R[P01] = P10P11.

000  10  001C  03E8
001 0F 0000 0000

Line 000 writes the value 03E8 (1000 in decimal) into R1C. The result can be checked in real time on the register pages of the secondary display.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register index
P10P110000~FFFF16-bit whole: immediate value

0x11 Register Copy (RFZ)​

Copy the value of one register into another: R[A] = R[B].

000  11  001D  001C
001 0F 0000 0000

Line 000 copies the value of R1C into R1D; R1C keeps its own value.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register A
P1000Don't care, ignored
P1100~FFSource register B

0x12 Increment / Decrement (ZJ)​

Increase or decrease a register by a 16-bit amount; each instruction also chooses whether overflow is allowed.

000  10  001C  FF00    R1C = FF00
001 12 001C 03E8 increase R1C by 03E8, overflow NOT allowed
002 0F 0000 0000

FF00 + 03E8 = 1 02E8, which exceeds the 16-bit range. With overflow not allowed the value is capped at FFFF; with overflow allowed the register simply wraps around to 02E8.

info
OperandsValue/RangeRemark
P0000Increase, overflow NOT allowed (capped at FFFF)
01Increase, overflow allowed (wraps around)
10Decrease, overflow NOT allowed (floored at 0000)
11Decrease, overflow allowed (wraps around)
P0100~FFRegister index
P10P110000~FFFF16-bit whole: amount to add or subtract

0x13 Send to Port (SS)​

Write the whole value of a register to a port (see the port system table):

  • Port1 receives the low 8 bits (8 digital IO, the LED network). Writing Port1 first restores the output direction and refreshes the hardware immediately, undoing an input switch made by 0x14 QS.
  • Port2 receives the low 4 bits (4 IO/PWM pins).
  • Port4~6 drive the 16 channels of PCA9685 module 0/1/2, one channel per bit.
  • Port0 / Port3 are read-only; a write is ignored.
000  10  0004  00FF    R04 = 00FF
001 13 0004 0001 Port1 = R04, all 8 LEDs on
002 0F 0000 0000
info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~7FSource register index
P1000Don't care, ignored
P1101Port1 — 8 digital IO (LED network)
02Port2 — 4 IO/PWM pins
04~06Port4~6 — PCA9685 modules 0/1/2, 16 channels each
00 / 03Read-only ports, write ignored

0x14 Fetch from Port (QS)​

Read a whole port into a register; each pin of the port becomes one bit of the value (pin 0 → bit 0):

  • Port0 — 7 onboard buttons (bit0~bit6, bit7 = 0).
  • Port1 — 8 digital IO. Reading Port1 first switches all 8 pins to input direction with pull-up (the LED network goes high-impedance and can be driven by an external device); a later 0x13 SS restores the output direction.
  • Port2 — 4 IO/PWM pins (bit0~bit3).
  • Port3 — 16 ADS1115 channels: bit n = 1 when the millivolt reading of channel n is above 2500.
  • Port4~6 are write-only and cannot be read.
000  14  0001  0000    R01 = Port0 (7 buttons)
001 0F 0000 0000
info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~7FTarget register index
P1000Don't care, ignored
P1100Port0 — 7 onboard buttons
01Port1 — 8 digital IO (switches to input direction first)
02Port2 — 4 IO/PWM pins
03Port3 — ADS1115 channels, above 2500 mV = 1

0x15 Bit Shift (XHYW)​

Shift the bits of a register left or right, circular or not, by 1~15 bit positions.

000  10   0007  8009    R07 = 8009
001 15 0007 0002 shift R07 right by 2 bits
002 0F 0000 0000

Suppose R07 = 8009 = 1000 0000 0000 1001. A plain right shift by 2 drops the two lowest bits and gives 0010 0000 0000 0010 = 2002; in circular mode the two shifted-out bits re-enter from the left and give 0110 0000 0000 0010 = 6002.

info
OperandsValue/RangeRemark
P0000Shift right
01Shift right, circular
10Shift left
11Shift left, circular
P0100~FFRegister index
P1000Don't care, ignored
P1100~0FNumber of bits to shift (values above 0F are not executed)

0x16 Bit Inversion (QF)​

Invert every bit of a register.

000  10  0007  8009    R07 = 8009
001 16 0007 0000 invert all bits of R07
002 0F 0000 0000

8009 = 1000 0000 0000 1001, inverted bit by bit → 0111 1111 1111 0110 = 7FF6, stored back into R07.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFRegister index
P10P110000Don't care, ignored

0x17 Bitwise AND (WY)​

Bitwise AND of two registers, result stored in the first one: R[dst] &= R[src].

000  10  0007  8009    R07 = 8009
001 10 0009 FF00 R09 = FF00
002 17 0007 0009 R07 = R07 AND R09
003 0F 0000 0000

1000 0000 0000 1001 AND 1111 1111 0000 0000 = 1000 0000 0000 0000 — R07 becomes 8000, R09 keeps its value.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register (result)
P1000Don't care, ignored
P1100~FFSource register

0x18 Bitwise OR (WH)​

Bitwise OR of two registers, result stored in the first one: R[dst] |= R[src]. Operand format is identical to 0x17.

000  10  0007  8009    R07 = 8009
001 10 0009 FF00 R09 = FF00
002 18 0007 0009 R07 = R07 OR R09
003 0F 0000 0000

1000 0000 0000 1001 OR 1111 1111 0000 0000 = 1111 1111 0000 1001 — R07 becomes FF09.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register (result)
P1000Don't care, ignored
P1100~FFSource register

0x19 Bitwise XOR (WYH)​

Bitwise XOR of two registers, result stored in the first one: R[dst] ^= R[src]. Operand format is identical to 0x17.

000  10   0007  8009    R07 = 8009
001 10 0009 FF00 R09 = FF00
002 19 0007 0009 R07 = R07 XOR R09
003 0F 0000 0000

1000 0000 0000 1001 XOR 1111 1111 0000 0000 = 0111 1111 0000 1001 — R07 becomes 7F09.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register (result)
P1000Don't care, ignored
P1100~FFSource register

0x1A Register Addition (RJIA)​

Add two registers, result stored in the first one: R[dst] += R[src].

000  10    0008  1234    R08 = 1234
001 10 0006 0020 R06 = 0020
002 1A 0008 0006 R08 = R08 + R06
003 0F 0000 0000

1234 + 0020 = 1254 — R08 becomes 1254. The result is the low 16 bits of the true sum (a carry beyond 16 bits is lost).

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register (result)
P1000Don't care, ignored
P1100~FFSource register

0x1B Register Subtraction (RJIAN)​

Subtract two registers, result stored in the first one: R[dst] −= R[src]. Operand format is identical to 0x1A.

000  10     0008  1234    R08 = 1234
001 10 0006 0020 R06 = 0020
002 1B 0008 0006 R08 = R08 - R06
003 0F 0000 0000

1234 − 0020 = 1214 — R08 becomes 1214.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register (result)
P1000Don't care, ignored
P1100~FFSource register

0x1C Register Multiplication (RCHENG)​

Multiply two registers, result stored in the first one: R[dst] ×= R[src]. Operand format is identical to 0x1A.

000  10     0008  1234    R08 = 1234
001 10 0006 0020 R06 = 0020
002 1C 0008 0006 R08 = R08 * R06
003 0F 0000 0000

1234 × 0020 = 2 4680 — the true product needs 17+ bits, so only the low 16 bits are kept and R08 becomes 4680.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register (result)
P1000Don't care, ignored
P1100~FFSource register

0x1D Register Division (RCHU)​

Divide two registers, the integer quotient stored in the first one: R[dst] ÷= R[src]. Operand format is identical to 0x1A.

caution

The division is between integers — only the quotient is kept, the remainder is dropped (use 0x1E RQY for it). If the source register is 0000, a divide-by-zero exception is flagged.

000  10    0008  1234    R08 = 1234
001 10 0006 0020 R06 = 0020
002 1D 0008 0006 R08 = R08 / R06 (quotient)
003 0F 0000 0000

1234 ÷ 0020 = 0091 (4660 ÷ 32 = 145, remainder dropped) — R08 becomes 0091.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register (result)
P1000Don't care, ignored
P1100~FFSource register (divisor, must not be 0000)

0x1E Register Remainder (RQY)​

Divide two registers and keep only the remainder, stored in the first one: R[dst] %= R[src]. Operand format is identical to 0x1A.

caution

If the source register is 0000, a divide-by-zero exception is flagged.

000  10    0008  1234    R08 = 1234
001 10 0006 0020 R06 = 0020
002 1E 0008 0006 R08 = R08 % R06
003 0F 0000 0000

1234 % 0020 = 0014 (4660 mod 32 = 20) — R08 becomes 0014.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFTarget register (result)
P1000Don't care, ignored
P1100~FFSource register (divisor, must not be 0000)

0x1F Random Number (SJS)​

Generate a random number and store it in the target register: R[reg] = rand() (low 16 bits).

000  1F  0005  0000    R05 = random value
001 0F 0000 0000
info
OperandsValue/RangeRemark
P00P010000~007F16-bit whole: target register (write as 00, register index)
P10P110000Don't care, ignored

0x20 Compose Word (GDCZ)​

Compose one 16-bit word from the low bytes of two registers and store it in the target register: the low byte of the target register becomes the high 8 bits, the low byte of the source register becomes the low 8 bits.

000  10    0005  2450    R05 = 2450
001 10 0006 FF00 R06 = FF00
002 20 0005 0006 R05 = low(R05) : low(R06)
003 0F 0000 0000

The low byte of R05 (50) becomes the high byte of the result and the low byte of R06 (00) becomes the low byte — R05 becomes 5000.

note

The dual 16-bit-whole operand format of 0x20/0x21 comes from firmware code analysis; on-device verification is recommended.

info
OperandsValue/RangeRemark
P00P010000~007F16-bit whole: target register (write as 00, register index)
P10P110000~007F16-bit whole: source register (write as 00, register index)

0x21 Byte Swap (GDJH)​

Swap the high and low bytes of the target register.

000  10    0005  1234    R05 = 1234
001 21 0005 0000 R05 = 3412
002 0F 0000 0000
note

The dual 16-bit-whole operand format of 0x20/0x21 comes from firmware code analysis; on-device verification is recommended.

info
OperandsValue/RangeRemark
P00P010000~007F16-bit whole: target register (write as 00, register index)
P10P110000Don't care, ignored

0x2A Add Literal (JIA)​

Add a literal value to a register: R[reg] += P10P11.

000  10  0007  8009    R07 = 8009
001 2A 0007 03E8 R07 += 03E8
002 0F 0000 0000

8009 + 03E8 = 83F1 — R07 becomes 83F1.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFRegister index
P10P110000~FFFF16-bit whole: literal value

0x2B Subtract Literal (JIAN)​

Subtract a literal value from a register: R[reg] −= P10P11. Operand format is identical to 0x2A.

000  10   0007  83F1    R07 = 83F1
001 2B 0007 03E8 R07 -= 03E8
002 0F 0000 0000

83F1 − 03E8 = 8009 — R07 becomes 8009.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFRegister index
P10P110000~FFFF16-bit whole: literal value

0x2C Multiply Literal (CHENG)​

Multiply a register by a literal value: R[reg] ×= P10P11. Operand format is identical to 0x2A.

000  10    0007  03E8    R07 = 03E8 (1000 in decimal)
001 2C 0007 0003 R07 *= 0003
002 0F 0000 0000

1000 × 3 = 3000 = 0BB8 — R07 becomes 0BB8.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFRegister index
P10P110000~FFFF16-bit whole: literal value

0x2D Divide by Literal (CHU)​

Divide a register by a literal value, keeping the integer quotient: R[reg] ÷= P10P11. Operand format is identical to 0x2A.

caution

If the literal divisor is 0000, a divide-by-zero exception is flagged.

000  10   0007  0BB8    R07 = 0BB8 (3000 in decimal)
001 2D 0007 0003 R07 /= 0003
002 0F 0000 0000

3000 ÷ 3 = 1000 = 03E8 — R07 becomes 03E8.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFRegister index
P10P110000~FFFF16-bit whole: literal value (divisor, must not be 0000)

0x2E Modulo Literal (QY)​

Compute the remainder of a register divided by a literal value: R[reg] %= P10P11. Operand format is identical to 0x2A.

caution

If the literal divisor is 0000, a divide-by-zero exception is flagged.

000  10  0007  03E9    R07 = 03E9 (1001 in decimal)
001 2E 0007 0003 R07 %= 0003
002 0F 0000 0000

1001 mod 3 = 2 = 0002 — R07 becomes 0002.

info
OperandsValue/RangeRemark
P0000Don't care, ignored
P0100~FFRegister index
P10P110000~FFFF16-bit whole: literal value (divisor, must not be 0000)

0x30 7-Segment Encode (BMQD)​

Convert a value into two 7-segment segment codes, ready for the onboard 4-digit display driven by 0x44 SSE.

The value of the source register is split into two digits and each digit is looked up in the segment code table; the two codes are packed into the target register:

  • High 8 bits = segment code of the tens / high digit.
  • Low 8 bits = segment code of the ones / low digit.
  • Segment bit order: bit7 = a … bit0 = dp.

Two split modes are selected by P11: decimal (00) splits the value 099 into tens and ones; hexadecimal (01) splits the low byte of the source register into a high nibble and a low nibble (00FF).

With P10 = 01 the whole 16-bit result is inverted — use this on the common-anode display where a segment lights when its line is LOW; P10 = 00 outputs the codes directly (common-cathode convention).

000  10    0001  003C    R01 = 60
001 30 0102 0100 R02 = segments of 60, inverted (common anode)
002 0F 0000 0000

Line 001 splits R01 = 60 into 6 and 0, looks up the segment codes (0xBE for 6, 0xFC for 0) and inverts them for the common-anode hardware — R02 = 4103: high byte = tens segments, low byte = ones segments. A full scanning example built on this opcode is shown in 0x44 SSE.

info
OperandsValue/RangeRemark
P0000~FFSource register number (its low 8 bits are split)
P0100~FFTarget register number
P1000Direct output (common-cathode convention)
01Invert the whole 16-bit result (common-anode display)
P1100Decimal split: value 0~99 → tens + ones
01Hex split: low byte → high nibble + low nibble

0x32 Three-Register Operation (TJYS)​

Perform one operation on two source registers: R[dst] = R[s0] op R[s1], where the operation is selected by an operation code.

info
OperandsValue/RangeRemark
P0000~FFTarget register (result)
P0100~0DOperation code, see table below
P1000~FFSource register 0 (left operand)
P1100~FFSource register 1 (right operand)

Operation code table:

CodeOperationCodeOperation
00Bitwise AND08Write (R[s0] = R[s1])
01Bitwise OR09Shift
02Bitwise XOR0ACopy
03Addition0BCompose word from low bytes
04Subtraction0CByte swap of R[s0]
05Multiplication0DRemainder
06Division
07Bitwise NOT (invert R[s0])
caution

Operation codes 07, 08, 0A, 0B and 0C write their result into source register 0 (P10), not into the target register — for these codes the P00 operand has no effect. For division (06) and remainder (0D) make sure source register 1 is not zero.

000  10    0001  0005    R01 = 5
001 10 0002 0003 R02 = 3
002 32 0303 0102 R03 = R01 + R02
003 0F 0000 0000

Line 002 uses operation code 03 (addition) on source registers R01 and R02 and stores the result in the target register — R03 = 5 + 3 = 8.