Sea16 is the working name for a 16bit processor designed for high level languages. It's based on Koei's VM used for their NES strategy games, where it provided a better machine to run C on than the 6502 does. This version is better suited for a hardware implementation with a rearranged and modified instruction set, and adds additional helpful instructions.
Table of contents:
Contents
Registers
name 
bits 
purpose 
A 
16/32 
"left" side of math operations 
B 
16/32 
"right" side of math operations 
SP 
16 
stack pointer 
FP 
16 
frame pointer 
PC 
16 
program counter 
A and B may be implemented as 32bit rather than 16bit. In this case, the top 16 bits are included in most math operations.
Addressing modes
mode 
description 
<fast> 
FP + offset from a lookup table 
<near> 
FP + 8bit signed offset 
<far> 
FP + 16bit signed offset 
<abs> 
16bit address 
This processor is heavily focused around accessing variables kept on the stack, so priority is given to using the frame pointer as a base.
The "fast" addressing mode uses 4 bits to select an item from the following table:
code 
location 
assembler name 
0 
FP  24 
local12 
1 
FP  22 
local11 
2 
FP  20 
local10 
3 
FP  18 
local9 
4 
FP  16 
local8 
5 
FP  14 
local7 
6 
FP  12 
local6 
7 
FP  10 
local5 
8 
FP  8 
local4 
9 
FP  6 
local3 
a 
FP  4 
local2 
b 
FP  2 
local1 

FP 
old_fp 

FP + 2 
old_pc 
c 
FP + 4 
arg1 
d 
FP + 6 
arg2 
e 
FP + 8 
arg3 
f 
FP + 10 
arg4 
16bit instructions
Fast addressing and 4bit constants
opcode 
mnemonic 
description 
0x 
lda fast 
A = <fast> 
1x 
ldb fast 
B = <fast> 
2x 
push fast 
push <fast> to the stack 
3x 
sta fast 
<fast> = A 
4x 
lda #x 
A = #x 
5x 
ldb #x 
B = #x 
6x 
push #x 
push #x to the stack 
7x 
add #x 
A += #x 
inc is an alias for add #1 and nop is an alias for add #0
Near/Far and 8/16bit constants
opcode 
mnemonic 
description 
80 xx 
lda near 
A = <near> 
81 xx xx 
lda far 
A = <far> 
82 xx 
ldb near 
B = <near> 
83 xx xx 
ldb far 
B = <far> 
84 xx 
push near 
push <near> to the stack 
85 xx xx 
push far 
push <far> to the stack 
86 xx 
sta near 
<near> = A 
87 xx xx 
sta far 
<far> = A 
88 xx 
lda #xx 
A = #xx (sign extend) 
89 xx xx 
lda #xxxx 
A = #xxxx 
8A xx 
ldb #xx 
B = #xx (sign extend) 
8B xx xx 
ldb #xxxx 
B = #xxxx 
8C xx 
push #xx 
push #xx to the stack 
8D xx xx 
push #xxxx 
push #xxxx to the stack 
8E xx 
add #xx 
A += #xx (sign extend) 
8F xx xx 
add #xxxx 
A += #xxxx 
Absolute instructions
opcode 
mnemonic 
description 
90 xx xx 
lda abs xxxx 
A = <abs> 
91 xx xx 
ldb abs xxxx 
B = <abs> 
92 xx xx 
push abs xxxx 
push <abs> to the stack 
93 xx xx 
sta abs xxxx 
<abs> = A 
94 xx xx 
blda abs xxxx 
A = <byte abs> 
95 xx xx 
bldb abs xxxx 
B = <byte abs> 
96 xx xx 
bpush abs xxxx 
push <byte abs> to the stack (zero extended) 
97 xx xx 
bsta abs xxxx 
<byte abs> = A 
Additional "far" instructions
opcode 
mnemonic 
description 
98 xx xx 
blda far 
A = <byte far> 
99 xx xx 
bldb far 
B = <byte far> 
9A xx xx 
bpush far 
push <byte far> to the stack 
9B xx xx 
bsta far 
<byte far> = A 
9C xx xx 
leaa far 
A = &<far> 
9D xx xx 
leab far 
B = &<far> 
9E xx xx 
pea far 
push &<far> to the stack 
9F xx xx 
?? far 
reserved 
Miscellaneous/subroutine calling
opcode 
mnemonic 
description 
A0 
deref 
A = *A 
A1 
popstore 
pop B, *B = A 
A2 
bderef 
A = (byte)*A 
A3 
bpopstore 
pop B, (byte)*B = A 
A4 
pha 
push A to the stack 
A5 
plb 
pop B from the stack 
A6 xx 
unstack #xx 
sp += #xx (for cleaning up after a function) 
A7 xx xx 
unstack #xxxx 
sp += #xxxx 
A8 xx xx 
call xxxx 
call an address (push PC, PC = xxxx, push FP, FP = SP) 
A9 
callptr 
call A 
AA xxxxyy 
call xxxx, #yy 
call an address, decrease the stack pointer by yy 
AB yy 
callptr #yy 
call A, decrease the stack pointer by yy 
AC 

operand size override prefix? 
AD 

address size override prefix? 
AE 
return 
return (SP = FP, pop FP, pop PC) 
AF 
swap 
A = B and B = A 
Comparisons and math
opcode 
mnemonic 
description 
B0 
ucmplt 
A = A < B (unsigned) 
B1 
ucmple 
A = A <= B (unsigned) 
B2 
ucmpgt 
A = A > B (unsigned) 
B3 
ucmpge 
A = A >= B (unsigned) 
B4 
scmplt 
A = A < B (signed) 
B5 
scmple 
A = A <= B (signed) 
B6 
scmpgt 
A = A > B (signed) 
B7 
scmpge 
A = A >= B (signed) 
B8 
cmpeq 
A = A == B 
B9 
cmpne 
A = A != B 
BA 
not 
A = !A (or == 0) 
BB 
neg 
A = A 
BC 
compl 
A = ~A 
BD 
and 
A = A & B 
BE 
or 
A = A  B 
BF 
xor 
A = A ^ B 
C0 
add 
A += B 
C1 
dec 
A 
C2 
rsub 
A = BA 
C3 
sub 
A = B 
C4 
lshift 
A <<= B (unsigned) 
C5 
double 
A <<= 1 
C6 
rshift 
A >>= B (unsigned) 
C7 
arshift 
A >>= B (signed) 
C8 
divu 
A /= B (unsigned) 
C9 
divs 
A /= B (signed) 
CA 
modu 
A %= B (unsigned) 
CB 
mods 
A %= B (signed) 
CC 
mult 
A *= B 
CD xy 
sloadbf x,y 
signed bit extract. a = b>>x, only keeping y bits (sign extended) 
CE xy 
uloadbf x,y 
unsigned bit extract. a = b>>x, only keeping y bits 
CF xy 
storebf x,y 
bit insert. a = (b&mask)(a<<x) 
Bitfield instruction implementation in C:
sloadbf x,y A = (B >> x) & (0xffff >> 15y); if(A & (1 << y)) A = 0xffff << (y+1); uloadbf x,y A = (B >> x) & (0xffff >> 15y); storebf x,y A = B & ~((0xffff >> 15y) << x)  (A & (0xffff >> 15y)) << x;
Jumps
opcode 
mnemonic 
description 
D0 xx 
jumpt xx 
PC += xx + 1 IF A==1 
D1 xx 
jumpt xx 
PC = xx + 1 IF A==1 
D2 xx xx 
jumpt xxxx 
PC = <abs> IF A==1 
D3 
? 
possibly a long jump 
D4 xx 
jumpf xx 
PC += xx + 1 IF A==0 
D5 xx 
jumpf xx 
PC = xx + 1 IF A==0 
D6 xx xx 
jumpf xxxx 
PC = <abs> IF A==0 
D7 
? 
possibly a long jump 
D8 xx 
jump xx 
PC += xx + 1 
D9 xx 
jump xx 
PC = xx + 1 
DA xx xx 
jump xxxx 
PC = <abs> 
DB 
? 
possibly a long jump 
DC ?? 
switchrange 
switch statement with a range (see example) 
DD ?? 
switchlist 
switch statement with a list of values (see example) 
Switch opcode examples:
switchrange .word 1, 5 ; range is 1 to 5, inclusive .word default ; jump here if outside the range .word was1 .word was2 .word was3 .word was4 .word was5 switchlist .word 5 ; 5 cases .word 1, was1 .word 2, was2 .word 3, was3 .word 4, was4 .word 5, was5 .word default
Miscellaneous 2
opcode 
mnemonic 
description 
DE xx 
in xx 
input on port x (A = read value) 
DF xx 
out xx 
output on port x (write A to port) 
E0 
extenda 
sign extend A from 8 bits to 16 
E1 
extendb 
sign extend B from 8 bits to 16 
E0 xx xx 
copy #xxxx 
memcpy(b, a, #xxxx) 
E1 xx xx 
fill #xxxx 
memset(b, a, #xxxx) 
E2 xx xx 
mcmp #xxxx 
memcmp(b, a, #xxxx) 
EC 
lda sp 
A = sp 
ED 
sta sp 
sp = A 
EE xx 
zalloc #xx 
decrease stack pointer by a constant and fill newly allocated bytes with zeros 
EF xx 
? 
directly increase/decrease a variable 
Direct count instructions take a parameter:
LLmm ffff  ++++ "Fast" stack reference, 12 local variables or 4 arguments ++ amount. index into 1, 2, 1, 2 ++ 0 no special effect 1 load A with the new count value 2 use old count value as a pointer and load A with it. (increase by 1 means zero extended byte, 2 means word) 3 use old count value as a pointer and load it with A. (increase by 1 means byte, 2 means word)
and have the following mnemonics:
syntax 
description 
count var, 1 
directly count a variable up/down 
ldcount var, 1 
directly count a variable up/down and load the new value into A 
plcount var, 1 
directly count a variable up/down and use the old value as a pointer to load 
pscount var, 1 
directly count a variable up/down and use the old value as a pointer to store 
32bit instructions
These are optional and would be invoked with an operandsize override prefix. Original Koei VM uses a second instruction set with different opcodes for 32bit instructions. Maybe that would make more sense?
opcode 
mnemonic 
description 
AC 0x 
lda32 fast 
A = <fast> 
AC 1x 
ldb32 fast 
B = <fast> 
AC 2x 
push32 fast 
push <fast> to the stack 
AC 3x 
sta32 fast 
<fast> = A 
AC 80 xx 
lda32 near 
A = <near> 
AC 81 xx xx 
lda32 far 
A = <far> 
AC 82 xx 
ldb32 near 
B = <near> 
AC 83 xx xx 
ldb32 far 
B = <far> 
AC 84 xx 
push32 near 
push <near> to the stack 
AC 85 xx xx 
push32 far 
push <far> to the stack 
AC 86 xx 
sta32 near 
<near> = A 
AC 87 xxxxxxxx 
sta32 far 
<far> = A 
AC 89 xxxxxxxx 
lda32 #xxxxxxxx 
A = #xxxxxxxx 
AC 8B xxxxxxxx 
ldb32 #xxxxxxxx 
B = #xxxxxxxx 
AC 8D xxxxxxxx 
push32 #xxxxxxxx 
push #xxxxxxxx to the stack 
AC 8F xxxxxxxx 
add32 #xxxxxxxx 
A += #xxxxxxxx 
AC 90 xx xx 
lda32 abs xxxx 
A = <abs> 
AC 91 xx xx 
ldb32 abs xxxx 
B = <abs> 
AC 92 xx xx 
push32 abs xxxx 
push <abs> to the stack 
AC 93 xx xx 
sta32 abs xxxx 
<abs> = A 
AC A0 
deref32 
A = *A 
AC A1 
popstore32 
pop B, *B = A 
AC A4 
pha32 
push A to the stack 
AC A5 
plb32 
pop B from the stack 
AC B0 
ucmplt32 
A = A < B (unsigned) 
AC B1 
ucmple32 
A = A <= B (unsigned) 
AC B2 
ucmpgt32 
A = A > B (unsigned) 
AC B3 
ucmpge32 
A = A >= B (unsigned) 
AC B4 
scmplt32 
A = A < B (signed) 
AC B5 
scmple32 
A = A <= B (signed) 
AC B6 
scmpgt32 
A = A > B (signed) 
AC B7 
scmpge32 
A = A >= B (signed) 
AC B8 
cmpeq32 
A = A == B 
AC B9 
cmpne32 
A = A != B 
AC BA 
not32 
A = !A (or == 0) 
AC C6 
rshift32 
A >>= B (unsigned) 
AC C7 
arshift32 
A >>= B (signed) 
AC C8 
divu32 
A /= B (unsigned) 
AC C9 
divs32 
A /= B (signed) 
AC CA 
modu32 
A %= B (unsigned) 
AC CB 
mods32 
A %= B (signed) 
AC E0 
extenda32 
sign extend A from 16 bits to 32 
AC E1 
extendb32 
sign extend B from 16 bits to 32 
AC EF xx 
direct count 
directly increase/decrease a variable (for plcount/pscount, always counts by 4) 
Code examples
Array reading
lda index add #array_address deref
Array writing
lda index add #array_address pha lda value_to_write popstore
Square table
lda #0 sta index loop: ; get address to write to lda index double add #array_address pha ; get index*index lda index ldb index mult popstore ; increase counter by 1 and then load it to then compare against the ending index ldcount index, 1 ldb #10 cmpne jumpt loop
Passing arguments to functions
push #1 push #2 push #3 push #4 call sum unstack #4*2 .proc sum lda arg1 ldb arg2 add ldb arg3 add ldb arg4 add return .endproc
Passing a reference
pea variable call function unstack #1*2 .proc function: ; writes 1 to the passed argument push arg1 lda #1 popstore return .endproc
C library examples
strlen
.proc strlen args text locals length zalloc #2 ; Clear out space for "length" loop: plcount text, 1 ; Load a character, increase pointer jumpf exit ; Exit if a null character is read count length, 1 ; Otherwise increase the length counter jump loop exit: lda length ; Return length counter in A return .endproc
strcpy
.proc strcpy args destination, source loop: plcount source, 1 pscount destination, 1 jumpt loop return .endproc
strchr
.proc strchr args string, character ldb character loop: lda string deref jumpf not_found ; null terminator cmpeq jumpt found_it ; was it found? ; didn't find it, so increase the pointer count string, 1 jump loop ; It was found! Return the position it was found at found_it: lda string return ; Character not found so return NULL not_found: lda #0 return .endproc
puts (assuming output device 0 is a character output)
.proc puts args input loop: plcount input, 1 ; read one character, step pointer forward jumpf exit ; exit if a null character is found out 0 ; output that character jump loop ; continue exit: lda #'\n' ; output line return out 0 return .endproc
abs
.proc abs args num lda num ; get the number ldb #0 scmplt ; less than zero? jumpt negative positive: ; positive, so keep it positive lda num return negative: ; negative, so negate it to positive lda num neg return .endproc