-- Leo's gemini proxy

-- Connecting to envs.net:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

Designing a simple processor architecture

Last edited: 04/12/2022


I've already mentioned this idea on my Mastodon account a while ago, but I feel like it's time to actually start working on it, now that I have very little schoolwork to do.

The main idea behind the project is to design a simple 16-bit RISC instruction set architecture that I will one day implement in hardware. Here are the design goals:

Be simple. I don't want to have more than 8 bits for the instructions

Support a reasonable address space, with byte addressing

Be able to support a simple(-ish) operating system

Base specs

So, with that in mind, here are the basic specification:

64K of memory, since that's all that 16-bit addressing supports. I originally wanted to add an 8-bit segment part too (resulting in 16M of address space being available), but I feel that that would just make everything complicated as hell for not so much benefit. I might change my mind on this, though.

Fixed length, two-word (4 byte) instructions

15 8-bit (b1 through b15) and 14 16-bit (w1 through w14) registers, a 16-bit stack pointer (sp), a 16-bit instruction pointer (ip) and 8-bit flag register (fl).

Basic ISA

The ISA is generally meant to be as simple as possible, with every instruction doing exclusively one thing. And by that, I mean /really/ only one thing: the only way to write constants into registers is the pl instruction, every other instruction only takes registers.

Here are the basic instructions (%W means word-sized register, %B means a byte-sized one, and $B and %W mean an integer constant):

Code  Mnemonic and usage      Encoding (in hex)               Action
-----------------------------------------------------------------------------------------------------------------------------------
00    nop                     00 00 00 00                     Nothing, it does nothing.
01    plw $W, %W              01 $---$ %W                     Place the constant into a word-sized register.
02    plb $B, %B              02 $$ %B 00                     Place the constant into a byte-sized register.
03    stw %W, %W              03 %W %W 00                     Store the contents of the first register to the address in the other.
04    stb %B, %W              04 %B %B 00                     Same as above, but storing only a byte.
05    ldw %W, %W              05 %W %W 00                     Load a word from the address in the first register into the second.
06    ldb %W, %B              06 %W %B 00                     Same as above, but loading only a byte.
07    cpw %W, %W              07 %W %W 00                     Copy the contents of the first register into the second.
08    cpb %B, %B              08 %B %B 00                     Same as above, but copy an 8-bit register.
09    eqw %W, %W              09 %W %W 00                     Compare two words and set or unset the EQ flag.
0A    eqb %B, %B              0A %B %B 00                     Same as above, but with bytes.
0B    gtw %W, %W              0B %W %W 00                     Compare two words and set the GT flag if the first value is bigger.
0C    gtb %B, %B              0C %B %B 00                     Same as above, but with bytes.
0D    ltw %W, %W              0D %W %W 00                     Same as above, with words and the LT flag.
0E    ltb %B, %B              0E %B %B 00                     Same as above, with bytes.
0F    c0w %W                  0F %W 00 00                     Set the ZR flag if the register is all zeros.
10    c0b %B                  10 %B 00 00                     Same as above, but with bytes.
11    adw %W, %W              11 %W %W 00                     Add a word to another word, store the result in the second register.
12    adb %B, %B              12 %B %B 00                     Same as above, with bytes.
13    sbw %W, %W              13 %W %W 00                     Subtract the first value from the second, store the result in the second.
14    sbb %B, %B              14 %B %B 00                     The same thing with bytes.
15    skz                     15 00 00 00                     Skip the next instruction if ZR is set.
22    skn                     22 00 00 00                     Skip the next instruction if ZR is /not/ set.
16    skg                     16 00 00 00                     Skip the next instruction if GT is set.
17    skl                     17 00 00 00                     Skip the next instruction if LT is set.
18    skq                     18 00 00 00                     Skip the next instruction if EQ is set.
19    cns %W, %B, %B          19 %W %B %B                     Fill the word by concatenating the two bytes together (first byte goes first).
1A    rdl %W, %B              1A %W %B 00                     Read the first byte of the word into the register.
1B    rdh %W, %B              1B %W %B 00                     Same, but with the second byte.
1C    anw %W, %W              1C %W %W 00                     AND the two words together, store the result in the first.
1D    anb %B, %B              1D %B %B 00                     Same, with bytes.
1E    xow %W, %W              1E %W %W 00                     XOR the two words and store the result in the first.
1F    xob %B, %B              1F %B %B 00                     Same with bytes.
20    orw %W, %W              20 %W %W 00                     OR two words together, store the result in the first.
21    orb %B, %B              21 %B %B 00                     Same with bytes.
23    ngw %W                  23 %W 00 00                     Negate (bit by bit) the word.
24    ngb %B                  24 %B 00 00                     Same with bytes.

As you've probably noticed, there's no conditional jump. That's because there's no need for one: you can just use plw to set the ip to whatever you need.

Also, here's a table of the registers and flags:

The ID is decimal this time

ID Name
----------
00 ip
01 w1
02 w2
03 w3
...
14 w14
15 sp
16 b1
...
31 b15

Flags (one bit each):
LT: lesser than
GT: greater than
EQ: equal
ZR: zero
<unallocated>
<unallocated>
<unallocated>
<unallocated>

The unallocated bits may be used for any purpose.

Technically, the stack pointer is also just a general purpose register, but it gets a special name to help prevent chaos. It can be used as a general purpose register if you want, though.


Extra notes

The processor is little-endian because the PDP-11 was too, and the PDP-11 did everything right.

All processor configuration and I/O will be done through the virtual address space, no I/O instructions!


Implementation

I'm still gonna work on this, it's not done yet. I promise I'll make it work soon-ish.

-- Response ended

-- Page fetched on Sun May 19 10:28:17 2024