Files
GameAndWatch_MiSTer/docs/instructions.md
2023-07-30 12:17:27 -07:00

24 KiB

Instructions

Instructions for the SM510, and the differences the SM5a introduces

Annotations

  • M - RAM value
  • BP - LCD control register BP
  • BC - LCD Power. On when low
  • BA - Input pin
  • Beta - Input pin
  • 1S - One second signal output by divider. Gamma is set on the rising edge of that function
  • F1 - 14th bit of the clock divider, 0 indexed
  • F4 - 11th bit of the clock divider, 0 indexed - TODO: MAME uses the 10th bit

Timing

Docs list oscillator at 32.768kHz and typical 61us instruction timing, which implies two cycles. Assuming two byte instructions, and particularly TM to IDX is multiple cycles. Skipping an instruction takes two cycles [1]

  1. MAME uses 2 cycle and 4 cycle instructions only. We've followed their lead, even though the non-TM two byte instructions can be done in 3

1. RAM Address Instructions

Mnemonic Opcode Operation Description
LB x 0x4X Bl[3:2] <- {2{x[3] | x[2]}}, Bl[1:0] <- x[3:2], Bm[1:0] <- x[1:0] Set lower 2 bits of Bm to the lower 2 of immed. Set lower 2 bits of Bl to upper 2 of immed. Set upper 2 bits of Bl to the upper 2 of immed ORed. [1]
LBL xy (2 byte) 0x5F 0xXX Bm <- x[6:4], Bl <- x[3:0] Set Bm to high 3 bits of immed. Set Bl to low 4 bits of immed
SBM 0x02 Bm[2] <- 1 for only the next step Sets the high bit of Bm high for the next cycle only. It will return to its previous value after that cycle
EXBLA 0x0B Acc <-> Bl Swap Acc and Bl
INCB 0x64 Skip next if Bl == 0xF. Bl <- Bl + 1 Increment Bl. If original Bl was 0xF, skip next instruction
DECB 0x6C Skip next if Bl == 0. Bl <- Bl - 1 Decrement Bl. If original Bl was 0x0, skip next instruction

Notes:

  1. Docs use a plus in a circle symbol for the OR, but elsewhere uses it to indicate XOR. MAME and other implementations use OR, so this is probably a docs bug

2. ROM Address Instructions

Mnemonic Opcode Operation Description
ATPL 0x03 Pl[3:0] <- Acc Load PC low bits with Acc
RTN0 0x6E {Pu, Pm, Pl} <- {Su, Sm, Sl} <- {Ru, Rm, Rl} Pop stack. Move S into PC, and R into S
RTN1 0x6F {Pu, Pm, Pl} <- {Su, Sm, Sl} <- {Ru, Rm, Rl} Pop stack. Move S into PC, and R into S. Skip next instruction
TL xyz (2 byte) 0x70-7A X 0x00-FE Y {Pu, Pm, Pl} <- {y[7:6], x[3:0], y[5:0]} Long jump. Load PC with immediates as shown
TML xyz (2 byte) 0x7C-7F X 0x00-FE Y R <- S <- PC + 1, Pu <- y[7:6], Pm <- {2'b0, x[1:0]}, Pl <- y[5:0] Long call. Push PC + 2 into stack registers. Load PC with immediates as shown [1]
TM x (psuedo 2 byte) 0xC0-FE R <- S <- PC + 1, {Pu, Pm, Pl} <- {2'b0, 4'b0, x[5:0]} Jumps to IDX table, and executes (see IDX below). Push PC + 1 into stack registers. Jump to zero page
IDX yz 0x00-FE {Pu, Pm, Pl} <- {y[7:6], 4'h4, x[5:0]}, Not a real opcode. Always preceeded by TM. Loads immediate into PC
T xy 0x80-BF Pl <- x[5:0] Short jump, within page. Set Pl to immediate

Note

  1. This pushes PC + 2, because PC + 1 is part of the contents of the instruction

3. Data Transfer Instructions

Mnemonic Opcode Operation Description
EXC x 0x10-13 Acc <-> M, Bm[1:0] <- Bm[1:0] ^ x[1:0] Swap Acc and RAM value. XOR Bm with immed
BDC 0x6D BC <- C Set LCD power. Display is on if C is low
EXCI x 0x14-17 Acc <-> M, Bm[1:0] <- Bm[1:0] ^ x[1:0]. Skip next instr if Bl = 0xF. Bl <- Bl + 1 Swap Acc and RAM value. XOR Bm with immed. Increment Bl. If original Bl was 0xF, skip next instruction. Combines EXC and INCB
EXCD x 0x1C-1F Acc <-> M, Bm[1:0] <- Bm[1:0] ^ x[1:0]. Skip next instr if Bl = 0x0. Bl <- Bl - 1 Swap Acc and RAM value. XOR Bm with immed. Decrement Bl. If original Bl was 0x0, skip next instruction. Combines EXC and DECB
LDA x 0x18-1B Acc <- M, Bm[1:0] <- Bm[1:0] ^ x[1:0] Load Acc with RAM value. XOR Bm with immed
LAX x 0x20-2F Acc <- x[3:0]. Skip next instr if also LAX Load Acc with immed. If following instruction is LAX, skip it. Continue skipping instruction until all of the LAX are passed
WR 0x62 W[7] <- W[6] <- ... <- W[0] <- 0 Shift 0 into W
WS 0x63 W[7] <- W[6] <- ... <- W[0] <- 1 Shift 1 into W

4. I/O Instructions

Mnemonic Opcode Operation Description
KTA 0x6A Acc <- K Reads K input bits into Acc
ATBP 0x01 BP <- Acc[0] Set LCD BP reg to Acc
ATL 0x59 L <- Acc Set Segment output L to Acc
ATFC 0x60 Y <- Acc Set Segment output Y to Acc
ATR 0x61 R <- Acc[1:0] Set R buzzer control value to the bottom two bits of Acc

5. Arithmetic Instructions

Mnemonic Opcode Operation Description
ADD 0x08 Acc <- Acc + RAM Add RAM to Acc
ADD11 0x09 Acc <- Acc + RAM + C, Set carry, skip next instr if new carry Add RAM to Acc with carry. Skip next instr if carry
ADX x 0x3X Acc <- Acc + x. Skip next instr if add carried Add 4 bit immediate to Acc. Skip next instr if add carried except if immediate is 'd10 (exception is a bug)
COMA 0x0A Acc <- ~Acc NOT Acc
DC 0x3A Acc <- Acc + (1010)^2 This command doesn't seem to exist anywhere. No idea what it's for [1]
ROT 0x6B C <- Acc[0] <- Acc[1] <- ... <- C Rotates right
RC 0x66 C <- 0 Clears carry
SC 0x67 C <- 1 Sets carry

Notes:

  1. I don't know why this is listed in the docs. This is what ADX x does. However it has a bug at 0xA, which is what this exception shows

6. Test Instructions

Mnemonic Opcode Operation Description
TB 0x51 Skip next instr if Beta = 1
TC 0x52 Skip next instr if C = 0
TAM 0x53 Skip next instr if Acc = M
TMI x 0x54-57 Skip next instr if M[x[1:0]] = 1
TAO 0x5A Skip next instr if Acc = 0
TABL 0x5B Skip next instr if Acc = Bl
TIS 0x58 Skip next instr if 1S = 0, Gamma <- 0 Check one second clock divider signal and zero Gamma [1]
TAL 0x5E Skip next instr if BA = 1
TF1 0x68 Skip next instr if F1 = 1 Clock divider value (14th bit)
TF4 0x69 Skip next instr if F4 = 1 Clock divider value (11th bit) [2]

Note:

  1. MAME does not check the clock divider value, but the current state of Gamma. TODO: Is this correct?
  2. MAME says this should be the 10th bit, but if F1 is 14, then F4 should be 11

7. Bit Manipulation Instructions

Mnemonic Opcode Operation Description
RM x 0x04-07 M[x[1:0]] <- 0 Zero bit of RAM indexed by immediate
SM x 0x0C-0F M[x[1:0]] <- 1 Set bit of RAM indexed by immediate

8. Special Instructions

Mnemonic Opcode Operation
SKIP 0x00 Do nothing
CEND 0x5D Stop clock
IDIV 0x65 DIV <- 0. Reset clock divider

Variant: SM5a

Hardware

  • Adds W' shift registers, which drive output
  • Adds m' flag register to accompany W'
  • Adds page setting functionality with the Cs, Su, Sl stack registers - Stack is initialized to initial PC
  • Adds CB bank select for certain branches (TR and TRS)
  • Adds 4 bit R output
  • Removes stack S and R
  • Removes LCD H and segments A and B. Uses O output pins based on W and W'
  • Removes 2 bit buzzer R output

Data Bus Layout

Memory is arranged as 5 sets of 13 nibbles (5 x 13 x 4bits). This is exposed as:

0x00 - 0x0C: Chunk 0
0x10 - 0x1C: Chunk 1
0x20 - 0x2C: Chunk 2
0x30 - 0x3C: Chunk 3
0x40 - 0x4C: Chunk 4

Values outside of this range (in the form 0xXD-F where X <= 4) are wrapped back to 0xXC. Values >= 0x50 are wrapped into the 0x40 block, such that 0x67 maps to 0x47

Instructions

Mnemonic Opcode Operation Replaces
SBM 0x02 Set high bit of Bm high SBM, just different definition
LB 0x4X Set Bm to low 2 immed. Set Bl to high two immed ORed with 8 LB, just different definition
SSR 0x7X Set stack S Pm (page) to immed. Sets E flag for next opcode TL and TML, long jump and long call
TR 0x80-0xBF Long or short jump. Uses set page value to determine whether long/short [1] T short jump
TRS 0xC0-0xFF Call subroutine. Sets Pl to immed, pushes stack. Uses stored page/bank if E flag is set from SSR prev instruction TM jump to IDX table
ATR 0x01 Same as normal: Set R buzzer control value to the bottom two bits of Acc ATBP set BP
ATBP 0x03 Same as normal: Set LCD BP reg to Acc ATPL set PC low bits
TAL 0x50 Skip next instr if BA is set None
PTW 0x59 Copy last two values from W' to W ATL set L segment output
TW 0x5C Copy W' to W None
DTW 0x5D Shift PLA value into W'. See [2] CEND stop clock
COMCN 0x60 XOR (complement) LCD CN flag ATFC set Y segment output
PDTW 0x61 Shift last two nibbles only, moving one PLA value into W' [2] ATR set buzzer
WR 0x62 Shift Acc (with 0 high bit) into W' WR, just different definition
WS 0x63 Shift Acc (with 1 high bit) into W' WS, just different definition
INCB 0x64 Increment Bl. If Bl was 8, skip next inst INCB, just different definition
IDIV 0x65 Reset clock divider, keeping the low 6 bits bits IDIV, just different definition
RMF 0x68 Clear m' and Acc TF1 skip if divider
SMF 0x69 Set m' TF4 skip if divider
RBM 0x6B Clear Bm high bit ROT rotate right
COMCB 0x6D XOR (complement) CB BCD set LCD power
CEND (2 byte) 0x5E 0x00 Stop clock TAL
DTA (2 byte) 0x5E 0x04 Copy high 4 bits of clock divider to Acc TAL

Note:

  1. MAME has some strange logic for the m_rsub flag which indicates whether a call has occurred (TRS), and changes all of the branching behavior of TR and TRS until RTN0 is executed
  2. PLA values from MAME: 0xe, 0x0, 0xc, 0x8, 0x2, 0xa, 0xe, 0x2, 0xe, 0xa, 0x0, 0x0, 0x2, 0xa, 0x2, 0x2, 0xb, 0x9, 0x7, 0xf, 0xd, 0xe, 0xe, 0xb, 0xf, 0xf, 0x4, 0x0, 0xd, 0xe, 0x4, 0x0

Variant: SM511/SM512

SM512 addresses significiantly more segments (200 vs 136), but has a smaller main RAM to compensate (corresponding to the same total amount of RAM, 96 x 4 + 32 x 4 bits).

Hardware

  • Grows ROM size to 4032 x 8 bits, as opposed to the 2772 x 8 for SM510
  • Adds Melody ROM and generator, which directly drives the buzzer R output
  • SM512 adds Ci set of segment lines, alongside the existing Ai and Bi
  • BS is two bits, with L driving BS[0], and X driving BS[1]. BS[0] blinks segments (combined with H[0]-H[3]) with a period of 1s
  • While input clock remains at 32.768kHz, the system clock is selectable as 8.192kHz or 16.384kHz. The initial clock is 8.192kHz

Melody

ROM consists of 256 x 6 bits which consists of music notes, pauses, and stops. 12 tones are available across two octaves (generated by alternating the number of low and high clock cycles), and can be generated for 125ms or 62.5ms.

Instructions

Mnemonic Opcode Operation Replaces
TL xyz (2 byte) 0x70-7F X 0x00-FF Y {Pu, Pm, Pl} <- {y[7:6], x[3:0], y[5:0]} TL, just expands the opcode to fill the entire space
TML xyz (2 byte) 0x68-6B X 0x00-FF Y Long call. Push PC + 2 into stack registers. R <- S <- PC + 1, Pu <- y[7:6], Pm <- {2'b0, x[1:0]}, Pl <- y[5:0] Same as TML, just moved. Replaces TF1 and TF4
TM x (psuedo 2 byte) 0xC0-FF R <- S <- PC + 1, {Pu, Pm, Pl} <- {2'b0, 4'b0, x[5:0]} TM, just expands the opcode to fill the entire space
IDX yz 0x00-FF {Pu, Pm, Pl} <- {y[7:6], 4'h4, x[5:0]}, IDX, just expands the opcode to fill the entire space
BDC (2 byte) 0x60 0x34 Set BC to C. Set LCD power. Display is on if C is low ATFC
KTA 0x50 Reads K input bits into Acc None
ATBP (2 byte) 0x60 0x35 Set LCD BP reg to Acc ATFC
ATX 0x5C Set Segment output X to Acc None
ATFC (2 byte) 0x60 0x33 Set Segment output Y to Acc ATFC, converted into two byte
ROT 0x00 Rotates Acc right with carry SKIP
PRE x (2 byte) 0x61 0x00-FF Set melody ROM pointer position ATR
SME (2 byte) 0x60 0x31 Enable melody ATFC
RME (2 byte) 0x60 0x30 Disable melody ATFC
TMEL (2 byte) 0x60 0x32 Skip if MES == 1, set MES to 0 [1] ATFC

Note:

  1. Unclear what this register is at this point