====== Sources of information ====== Instruction set documentation archived from "unSP Programmer's Guide" PDF from the unSP IDE installation - {{ :unsp_programmer_s_guide.pdf |local copy}}. A good overview can also be found in {{ ::unsp_isa-1.2.pdf | this document}}. ====== CPU ====== The V-Smile processor is a Sunplus µ'nSP implementing version **1.1** of the ISA (source: this is how the SPG200 projects in µ'nSP IDE are configured). ===== Registers ===== ^ID^Name^Function ^ |0 |SP |Stack Pointer | |1 |R1 |General Purpose| |2 |R2 |General Purpose| |3 |R3 |General Purpose| |4 |R4 |General Purpose| |5 |BP |Base Pointer | |6 |SR |Status Register| |7 |PC |Program Counter| R3 and R4 can be used to form a 32-bit register pair for some instructions, this pair is noted MR. R3 stores the lower half and R4 stores the upper half. There is also a 4-bit shift buffer (SB) register used by the shift and rotate addressing modes. There are separate SB registers for normal, FIQ and interrupt mode, which gets automatically switched. The BP register has a special addressing mode with a 6-bit immediate offset. It is usually (for example by unsp IDE C compiler) used as a frame pointer, to access a function local variables efficiently. But it can also be used as a normal register if needed. SP is generally used as a stack pointer but can be used for other things occasionally. When using the PUSH and POP instructions, the stack grows downwards (PUSH decrements SP) and the stack pointer points to the first empty space below the stack. As a result, the typical organization of the stack in VTech code is: ^Address^ Contents ^ Notes ^ | NNNF | Parameter 0 | Pushed by caller | | NNNE | Parameter 1 | | | NNND | PC (from CALL) | Saved by CALL | | NNNC | SR (from CALL) | Saved by CALL | | NNNB | R4 | Saved by PUSH in function prelude | | NNNA | R3 | (and more registers as needed) | | NNN9 | Local variable | Space allocated by decrementing SP | | NNN8 | Local variable | <- BP points here | | NNN7 | Empty | <- SP points here | The SP can move when calling other functions, interrupts, ... or when saving registers inside the function code. But BP doesn't move and can be used to access both local variables and function parameters. The parameters are usually removed by the caller (by incrementing SP). It would also be possible to use a POP including SR and PC to return from the function while clearing parameters, but this requires loading them into registers, which is not always desirable since registers are rather used for return values. ==== Status Register ==== The status register contains various status flags, as well as the current code segment (CS) and data segment (DS) base addresses. ^Bits ^Name^Description ^Notes ^ |0-5 |CS |Code Segment |Auto-incremented when PC wraps around | |6 |C |Carry Flag |Set if a carry occurred | |7 |S |Sign Flag |Set if the result is negative (two's complement)| |8 |Z |Zero Flag |Set when the result is zero | |9 |N |Negative Flag|Set when the MSB of the result is 1 | |10-15|DS |Data Segment |Auto incremented with pre/post inc/dec addressing mode as needed | ====== Address Space ====== The memory map of the µ'nSP is split into 64K sized pages. The entire 4M address space of the CPU is divided into 64 pages (0x00-0x3F). The current page can be selected via the segment registers, CS (for instruction fetch) and DS (for data operations). When code execution reaches the end of the current page, the CS register is auto-incremented by the hardware. ====== Interrupts ====== The interrupt vector table is at the end of the first memory page. It contains addresses of the handlers for each interrupt, including the software interrupt (BREAK instruction) and CPU reset. Since the addresses are only 16bit, all handlers must be in the first memory page. ^Vector^Name ^ |0xFFF5|BREAK| |0xFFF6|FIQ | |0xFFF7|RESET| |0xFFF8|IRQ0 | |0xFFF9|IRQ1 | |0xFFFA|IRQ2 | |0xFFFB|IRQ3 | |0xFFFC|IRQ4 | |0xFFFD|IRQ5 | |0xFFFE|IRQ6 | |0xFFFF|IRQ7 | See http://vtech.pulkomandy.tk/doku.php?id=memory_map&s%5B%5D=irq#interrupts for details about the interruptions usage. ====== Instruction Set ====== These tables detail the instruction both in the format used by the official µ'nSP toolchain as well as the mnemonic form used by the free for non-commercial use vasm. NOTE: Unless otherwise specified, instructions that operate on memory ignore DS and only operate on page 0 (0x0000-0xFFFF). Instruction varients that allow a "D:" prefix generate a full 22-bit address via ''%%(DS << 16 | addr)%%'' ===== Data Transfer ===== ==== Load ==== ^Instruction ^naken_asm form ^Notes ^Flags Affected^ |%%Rd = Value %%|%%ld rd, value %%|6 and 16-bit variants available | NZ | |%%Rd = [BP + offset] %%|%%ld rd, [BP + offset] %%|Offset is limited to 6 bits | ::: | |%%Rd = [addr] %%|%%ld rd, [addr] %%|6 and 16-bit variants available | ::: | |%%Rd = Rs %%|%%ld rd, rs %%| | ::: | |%%Rd = {D:}[Rs] %%|%%ld rd, {D:}[rs] %%|Optional data-segment qualifier (D:)| ::: | |%%Rd = {D:}[++Rs] %%|%%ld rd, {D:}[++rs] %%| ::: | ::: | |%%Rd = {D:}[Rs--] %%|%%ld rd, {D:}[rs--] %%| ::: | ::: | |%%Rd = {D:}[Rs++] %%|%%ld rd, {D:}[rs++] %%| ::: | ::: | ==== Store ==== ^Instruction ^naken_asm form ^Notes ^Flags Affected^ |%%[BP + offset] = Rd %%|%%st rd, [BP + offset] %%|Offset is limited to 6 bits | | |%%[addr] = Rd %%|%%st rd, [addr] %%| | | |%%{D:}[Rs] = Rd %%|%%st rd, {D:}[rs] %%|Optional data-segment qualifier (D:)| | |%%{D:}[++Rs] = Rd %%|%%st rd, {D:}[++rs] %%| ::: | | |%%{D:}[Rs--] = Rd %%|%%st rd, {D:}[rs--] %%| ::: | | |%%{D:}[Rs++] = Rd %%|%%st rd, {D:}[rs++] %%| ::: | | ==== Push/Pop ==== ^Instruction ^naken_asm form ^Notes ^Flags Affected^ |%%PUSH Rx, Ry to [Rs] %%|%%push rx-ry, [rs] %%|rx-ry signifies a range of registers to push| | |%%PUSH Rx to [Rs] %%|%%push rx, [rs] %%|Push a single register | | |%%POP Rx, Ry from [Rs] %%|%%pop rx-ry, [rs] %%|rx-ry signifies a range of registers to pop | | |%%POP Rx from [Rs] %%|%%pop rx, [rs] %%|Pop a single register | | The stack grows downwards (towards lower addresses). Push does post-decrement so SP points to the unused entry at the top of the stack. **Values before pushing:** ^Address ^Content ^ |FFFF |XXXX ^ |FFFE |--- ^ |FFFD |--- ^ SP = FFFE ''PUSH BP, [SP]'' **Values after pushing:** ^Address ^Content ^ |FFFF |XXXX ^ |FFFE |**BP** ^ |FFFD |--- ^ SP = FFFD ===== ALU operations ===== All these operations have similar syntax and support the same addressing modes ^Mnemonic ^naken_asm form ^Description ^Flags affected ^ |%%Rd += Rs %%|%%ADD Rd, Rs %%|Add | NZSC | |%%Rd += Rs,carry %%|%%ADC Rd, Rs %%|Add with carry | ::: | |%%Rd -= Rs %%|%%SUB Rd, Rs %%|Subtract | ::: | |%%Rd -= Rs,carry %%|%%SBC Rd, Rs %%|Subtract with carry | ::: | |%%CMP Rd, Rs %%|%%CMP Rd, Rs %%|Compare (same effect on flags as SUB) | ::: | |%%Rd = -Rs %%|%%NEG Rd, Rs %%|Negate | NZ | |%%Rd ^= Rs %%|%%XOR Rd, Rs %%|Exclusive OR | ::: | |%%Rd |= Rs %%|%%OR Rd, Rs %%|Bitwise OR | ::: | |%%Rd &= Rs %%|%%AND Rd, Rs %%|Bitwise AND | ::: | |%%Test Rd, Rs %%|%%TEST Rd, Rs %%|Same effect on flags as bitwise AND | ::: | ==== Addressing modes ==== For all ALU operations, the following addressing modes are available: ^Syntax ^naken_asm form ^Description ^ |%% R1 += R2 %%|%% ADD R1, R2 %%|Register | |%% R1 += R2 ASR 1 %%|%% ADD R1, R2 ASR 1 %%|Register with arithmetic right shift up to 4 bits | |%% R1 += R2 LSL 1 %%|%% ADD R1, R2 LSL 1 %%|Register with logical left shift up to 4 bits | |%% R1 += R2 LSR 1 %%|%% ADD R1, R2 LSR 1 %%|Register with logical left shift up to 4 bits | |%% R1 += R2 ROL 1 %%|%% ADD R1, R2 ROL 1 %%|Register with 20-bit left rotation through SB up to 4 bits | |%% R1 += R2 ROR 1 %%|%% ADD R1, R2 ROR 1 %%|Register with 20-bit right rotation through SB up to 4 bits | |%% R1 += 23 %%|%% ADD R1, #23 %%|6-bit immediate value (Rd cannot be PC) | |%% R1 = R2 + 1234 %%|%% ADD R1, R2, #1234 %%|3-operand 16-bit immediate | |%% R1 += [12] %%|%% ADD R1, [12] %%|Direct (get value at 6-bit address 12) | |%% R1 = R2 + [1234] %%|%% ADD R1, R2, [1234] %%|Direct 3-operand (get values at R2 and 16-bit address 1234) | |%% [1234] = R1 + R2 %%|%% ADD [1234], R1, R2 %%|Direct-store 3-operand | |%% R1 += [R2] %%|%% ADD R1, [R2] %%|Indirect (register used as pointer) | |%% R1 += [R2--] %%|%% ADD R1, [R2--] %%| ... with post-decrement | |%% R1 += [R2++] %%|%% ADD R1, [R2++] %%| ... with post-increment | |%% R1 += [++R2] %%|%% ADD R1, [++R2] %%| ... with pre-increment | |%% R1 += D:[R2] %%|%% ADD R1, D:[R2] %%| ... in data segment (address is (DS << 16) %%|%% R2)) | |%% R1 += D:[R2--] %%|%% ADD R1, D:[R2--] %%| ... with post-decrement in data segment | |%% R1 += D:[R2++] %%|%% ADD R1, D:[R2++] %%| ... with post-increment in data segment | |%% R1 += D:[++R2] %%|%% ADD R1, D:[++R2] %%| ... with pre-increment in data segment | |%% R1 += [BP+12] %%|%% ADD R1, [BP+12] %%|6-bit displacement from BP (Rd cannot be PC) | It is possible to combine indirect with data segment and increments and decrements in the same instruction. The data segment will then be automatically updated in case the increment or decrement wraps. The shift operations use the shift buffer (see "shift operations" section below). In particular the ROR and ROL operations do a 20-bit rotation resulting in bits from the shift buffer moving into the destination register. === Bit shift addressing modes === Given a starting register like this: ^ ^Bits 15-0^ ^Rs|Rs15-Rs0 | And the SB bits: ^ ^Bits 3-0^ ^SB|SB3-SB0 | The result of the shift operations are (for a shift by 3 bits): == ASR == Arithmetic shift right (signed divide by two) ^ ^Bits 15-13^Bits 12-0^ ^Rs|Rs15 |Rs15-Rs3 | ^ ^Bits 3-1^Bit 0^ ^SB|Rs2-Rs0 |SB3 | == LSL == Logical shift left (multiply by two) ^ ^Bits 15-3^Bits 2-0^ ^Rs|Rs12-Rs0 |0 | ^ ^Bit 3^Bit 2-0 ^ ^SB|SB0 |Rs15-Rs13| == LSR == Logical shift right (unsigned divide by two) ^ ^Bits 15-13^Bits 12-0^ ^Rs|0 |Rs15-Rs3 | ^ ^Bits 3-1^Bit 0^ ^SB|Rs2-Rs0 |SB3 | == ROL == Rotate left through shift buffer ^ ^Bits 15-3^Bits 2-0^ ^Rs|Rs12-Rs0 |SB3-SB1 | ^ ^Bit 3^Bit 2-0 ^ ^SB|SB0 |Rs15-Rs13| == ROR == Rotate right through shift buffer ^ ^Bits 15-13^Bits 12-0^ ^Rs|SB2-SB0 |Rs15-Rs3 | ^ ^Bits 3-1^Bit 0^ ^SB|Rs2-Rs0 |SB3 | ===== Multiplication and division ===== ==== Multiplication ==== Multiplication result is stored in R3 and R4 (the register pair is called MR) ^Instruction ^naken_asm form ^Notes ^Flags Affected^ |%%MR = Rd x Rs %%|%%MUL.SS Rd, Rs %%|Signed multiplication | | |%%MR = Rd x Rs,us %%|%%MUL.US Rd, Rs %%|Rd is unsigned, Rs is signed | ::: | |%%MR = [Rd] x [Rs],n %%|%%MAC [Rd], [Rs], N %%|Multiply-accumulate two sets of N signed values pointed by Rd and Rs | | |%%MR = [Rd] x [Rs],us,n %%|%%MAC.US [Rd], [Rs], N %%|Multiply-accumulate two sets of N values pointed by Rd (unsigned values) and Rs (signed values) | ::: | Multiply-accumulate operations do the computation with 32-bit precision with the result is stored in MR. The Rd and Rs pointers will automatically be incremented by N. The values of the array pointed to by the Rd array will each be moved one index forward when the FIR_MOV setting is enabled, see the FIR_MOV section for details. ==== FIR_MOV ==== ''FIR_MOV ON'' ''FIR_MOV OFF'' Affects the FIR setting used by the MULS instructions. When enabled, MULS instructions will modify the array pointed to by the Rd register after finishing the calculation by moving each value one index forward with the last value discarded and the first value left in place. This can be used for automatically advancing the time step when implementing FIR filters, with the Rd array storing the input signal and the Rs array storing the weights. ===== Program Flow ===== ==== Conditional jumps ==== Syntax: JMP label The target address is stored as a 6 bit displacement and a separate bit indicating forward or backward jump. So this can only jump back and forward 63 addresses. ^Instruction ^Notes ^Flags Affected^ |JB,JCC,JNAE label |Jump if C=0 | | |JAE,JNB,JCS label |Jump if C=1 | | |JGE,JNL,JSC label |Jump if S=0 | | |JL,JNGE,JSS label |Jump if S=1 | | |JNE,JNZ label |Jump if Z=0 | | |JE,JZ label |Jump if Z=1 | | |JPL label |Jump if N=0 | | |JMI label |Jump if N=1 | | |JBE,JNA label |Jump if Z=1 or C=0 | | |JA,JNBE label |Jump if Z=0 and C=1 | | |JLE,JNG label |Jump if Z=1 or S=1 | | |JG,JNLE label |Jump if Z=0 and S=0 | | |JVC label |Jump if N=S | | |JVS label |Jump if N != S | | |JMP label |Jump always | | xasm supports the syntax ''SJMP'' for "smart jump" that will automatically be converted to a jump + GOTO if the address is outside the reachable range for a normal jump. ==== Other instructions ==== === CALL === ''CALL label'' * PC and SR are pushed to the stack * PC and the code segment part of SR are loaded with the 22-bit parameter address Because SR is pushed automatically, flags are always saved accross a CALL/RETF === GOTO === Like a call, but does not save PC and SR on the stack. In ISA 1.0, GOTO does not set the CS: so it is not possible to jump outside the current segment. In ISA 1.1 and above this problem is fixed. === RETF === Return from function. Pops PC and SR from the stack. === RETI === Return from interrupt. In addition to what RETF does, also restores the interrupt flag. === BREAK === Jumps to the BREAK software IRQ handler. ===== Interrupt control ===== === IRQ enable/disable === ^Instruction ^Notes ^Flags Affected^ |IRQ OFF |Disable interrupts | | |IRQ ON |Enable interrupts | | |FIQ OFF |Disable fast interrupts | | |FIQ ON |Enable fast interrupts | | |INT FIQ |Enable FIQ, disable IRQ | | |INT FIQ,IRQ |Enable FIQ and IRQ | | |INT IRQ |Disable FIQ, enable IRQ | | |INT OFF |Disable FIQ and IRQ | |