24 KiB
Instructions
Instructions for the SM510, and the differences the SM5a introduces
Annotations
M- RAM valueBP- LCD control register BPBC- LCD Power. On when lowBA- Input pinBeta- Input pin1S- One second signal output by divider.Gammais set on the rising edge of that functionF1- 14th bit of the clock divider, 0 indexedF4- 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]
- MAME uses 2 cycle and 4 cycle instructions only. We've followed their lead, even though the non-
TMtwo 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:
- 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
- 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 |
|
ROT |
0x6B |
C <- Acc[0] <- Acc[1] <- ... <- C |
Rotates right |
RC |
0x66 |
C <- 0 |
Clears carry |
SC |
0x67 |
C <- 1 |
Sets carry |
Notes:
- I don't know why this is listed in the docs. This is what
ADX xdoes. 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:
- MAME does not check the clock divider value, but the current state of
Gamma. TODO: Is this correct? - 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 accompanyW' - Adds page setting functionality with the
Cs,Su,Slstack registers - Stack is initialized to initial PC - Adds
CBbank select for certain branches (TRandTRS) - Adds 4 bit
Routput - Removes stack
SandR - Removes LCD
Hand segmentsAandB. UsesOoutput pins based onWandW' - Removes 2 bit buzzer
Routput
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:
- MAME has some strange logic for the
m_rsubflag which indicates whether a call has occurred (TRS), and changes all of the branching behavior ofTRandTRSuntilRTN0is executed - 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
Routput - SM512 adds
Ciset of segment lines, alongside the existingAiandBi BSis two bits, withLdrivingBS[0], andXdrivingBS[1].BS[0]blinks segments (combined withH[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:
- Unclear what this register is at this point