mat~wiki

My personal wiki

vic-sm8


/index

/prog/vicera/index

The VIC-SM8 is the next CPU that is going to replace the current vic-8p. Not much in the design has been currently planned, but know that this CPU is going to be stack-based and will be relatively easy to write a Forth using it.

Unlike the VIC-8P that had a really messy and unoptimized instruction set, this one will aim to be easier to use and easier to embed in other applications than the VICERA itself.

Overview

The architecture of this CPU consists of two stacks, a return stack and a data stack, 64 KiB of memory, an interrupt table and 256 "I/O" ports which the user can communicate with through 2 instructions.

The instruction set is heavily inspired by uxn and I highly recommend to check out this project because it seems really interesting and promising.

This CPU focuses on extensibility and ease to implement applications with it because this same CPU will be used by me on other applications than the VICERA. It will be sort of a toy VM to play around with and make other projects.

Current memory bindings

Address Size Description
0000 32KB ROM allocation
F300 512B Interrupt Table

Currently planned features...

Registers

Even though the VIC-SM8 is a stack machine, it still needs a few registers to store some pointers and the flags.

Interrupts

The VM has an interrupt table allowing the programmer to perform error handling or to define their own interrupt routines such as syscalls or stuff like that. Interrupts are either called by the CPU if an error occurs or explicitly called by the user. If the said interrupt points to 0x0000 in the table, the HALT flag will be set and the machine will stop working.

I/O

The I/O architecture allows up to 256 ports that are bindable using C structs. The io struct has 4 function pointers to start, stop and handle in/out opcodes. This has been made to ease the extensibility of the machine. With that, you can write any "device" in C like TTY, SDL graphics, networking, or bind it to actual I/O.

The I/O has a similar convention to the x86: It uses IN and OUT commands to interact the I/O. Once the instruction executed, it will pops a byte from the stack to know which port to go. The I/O have full access to the return stack, the data stack, the memory and the registers.

Instruction Set

The instruction set is quite simple. It uses reverse polish notation to operate. Just like Forth.

HF is the HALT flag

Instruction Opcode Input Output Description
HALT 00   HF Sets the halt flag
--- 01     Unused.
INT 02 a   Performs an interrupt call
IN 03 a   Sends IN command to an IO port
OUT 04 a   Sends OUT command to an IO port
ROT 05 a b c b c a Moves a to the top
SWP 06 a b b a Swaps a and b
DUP 07 a a a Dupliates a
OVER 08 a b a b a Duplicates a to the top
DROP 09 a   Pops a
ADD 0a a b c a + b = c
SUB 0b a b c a - b = c
MUL 0c a b c a * b = c
DIV 0d a b c a / b = c
MOD 0e a b c a % b = c
AND 0f a b c a AND b = c
OR 10 a b c a OR b = c
NOT 11 a b NOT a = b
XOR 12 a b c a XOR b = c
SR 13 a b a >> 1 = b
SL 14 a b a << 1 = b
INV 15 a b b = !a
LD 16 (a b) c Loads from memory at (a b)
ST 17 a (b c)   Stores from memory at (b c)
ADL 18   c Loads from AR
ADS 19 a   Stores a from AR
EQU 1a a b c a == b = c
NEQ 1b a b c a != b = c
GRT 1c a b c a > b == c
LRT 1d a b c a < b == c
GEQ 1e a b c a >= b == c
LEQ 1f a b c a <= b == c
JMP 20 (a b)   Jumps to (a b)
JMZ 21 a (b c)   Jumps to (b c) if a > 0
CAL 22 (a b)   Calls subroutine at (a b)
CAZ 23 a (b c)   Calls subroutine at (a b) if a > 0
RET 24     Pops from return stack and jumps
REZ 25 a   Same as RET if a > 0
ADR 26   c Reads the content of AR
ADW 27 (a b)   Stores (a b) into AR
ADI 28     Increments AR
SADD 29     signed ADD
SSUB 2a     signed SUB
SMUL 2b     signed MUL
SDIV 2c     signed DIV
SGRT 2d     signed GRT
SLRT 2e     signed LRT
SGEQ 2f     signed GEQ
SLEQ 30     signed LEQ

Every byte are formatted this way:

7 6 5 4 3 2 1 0
+ L B B B B B B

+ is the 16-bit mode. Meaning that if this bit is set, the opcodes will be run in 16-bit mode (basically manipulating the stack with 16-bit numbers instead of 8-bit numbers). Please note that this is not compatible with all bytecodes, mostly just the ones that manipulates numbers.

L is the literal mode, allowing the programmer to push numbers in the stack. The bytecode (B) will then be used as the number of bytes to push.

Example: 42 ac ab fe pushes 2 bytes into the stack, fe will not be pushed.

B is the space for instructions. Also used to define the number of bytes to push in literal mode.

Assembler

There is an assembler in the same repository as the VIC-SM8. It is really simple and I am not sure if I will make the final assembler in C. I hate parsing in C.

All NUMs must be written in hexadecimal. They can be written in pairs of 2 digits like this: 0f cafe deadbeef.

Command Description
$varname Defines a pointer to the memory, meant to be used as a variable.
#const NUM Defines a constant. NUM must be a 16-bit integer.
:label Sets a label.
.val Appends val to the bytecode with the NUM opcode.
,NUM Appends raw hexadecimals in the bytecode.
{ comment } A comment.
"string Appends a raw string in the bytecode.
NUM Appends a num in the bytecode with the NUM opcode.
OPCODE Appends an opcode.
+OPCODE Appends an opcode in 16-bit mode.

There is also |NUM which moves the location pointer but I couldn't fit it in the table because vimwiki doesn't seem to do escape sequences : pain:

Here is an hello world written in sm8 assembly. It uses an I/O port that interacts with the TTY.

.main JMP
:hello "Hello, ,20 "World! ,0a00

:main   .hello ADW
:l      ADL DUP INV .end JMZ 01 OUT ADI .l JMP
:end    HALT

Source Code

The source code is available and licensed under the MIT License. There is an example machine which is also used for testing.

https://codeberg.org/vicera/vic-sm8