
Implement an emulator for a fantasy CPU in JavaScript - codeguppy
https://dev.to/codeguppy/implement-an-emulator-for-a-fantasy-cpu-in-javascript-3617
======
_nalply
Many years ago when I was bored I «designed» a fantasy CPU with 6 (six) bits
and only one usable register, the accumulator. I thought why not. The result:

18 bit address bus

12 bit stack pointer

6 flags zero, negative, carry, overflow, float and user

7 addressing modes accumulator, immediate, absolute, relative, stack, stack
without pop, stack special

64 opcodes like NOP, OR, JMP, ROL, STO, ...

I discovered that it is well possible to have a 6 bit CPU but of course this
is just a game and probably not useful at all. I wonder whether I would be
able to create the circuit for the CPU in a simulator. Probably not even if I
omit the floating point handling or only if I dedicated about 10 years of free
time. I would need to design a microcode or PLA (like MOS 6502, see
[https://news.ycombinator.com/item?id=5353198](https://news.ycombinator.com/item?id=5353198))
system.

I find it amusing that this is the one CPU where octal base is really the
primary way to display numbers.

~~~
tn1
You'd be surprised, it may not take quite that long. With something like
Chisel you can create that idea with little friction. Plus, it's "just" Scala
so if you already know that it's a lot easier than, say, learning Verilog
which has a very different syntax.

------
mikestaszel
If you’re looking for a good next step I recommend CHIP-8.

~~~
RodgerTheGreat
And if you've written a CHIP-8 interpreter, I recommend playing around with
Octo[0] and then participating in the Octojam[1] this October!

[0] [https://github.com/JohnEarnest/Octo](https://github.com/JohnEarnest/Octo)

[1] [http://octojam.com](http://octojam.com)

~~~
eatonphil
Tangent but much larger audience is the original GB game jam coming up next
week.

[https://itch.io/jam/gbjam-8](https://itch.io/jam/gbjam-8)

~~~
RodgerTheGreat
The GBJam isn't about making software that runs on a legacy platform; you're
using modern game engines, languages, and techniques while imitating the
_aesthetic_ limitations of the original Game Boy. (And pretty loosely, at
that.)

It's a very different style of creative constraint compared to programming a
specific virtual machine with limited RAM and not a multiplier or floating-
point unit in sight.

~~~
eatonphil
Gotcha, I assumed that it was programming against the original exactly. But I
guess the emulators they allow are more flexible/modern takes on the original.

------
charlesdaniels
Nice article! I would definitely recommend for everyone to write a CPU
emulator at least once. It will give you a lot of perspective on how computers
really work. As someone else pointed out CHIP-8[0] is a good one that's not
too complicated.

If you want something more "real", MIPS and RISC-V are also not too hard, if
you just do a subset of the instruction set and not the whole thing. You can
skip most of the instructions that deal in bytes and half-words. It's also
easier to implement main memory as being word-addressable only (but this can
introduce incompatibilities with real assembler programs).

I wrote a cycle-accurate, pipeline simulator for MIPS for a course, which
totaled only about 1200 SLOC. If you didn't need to simulate all the pipeline
registers (you probably don't) and use a higher level language, this is
probably a <750SLOC project.

0 -
[https://en.wikipedia.org/wiki/CHIP-8](https://en.wikipedia.org/wiki/CHIP-8)

~~~
triyambakam
Do you have any suggested resources for learning to implement a CPU emulator,
such as for RISC-V?

~~~
thethirdone
I'm not the person you asked, but I made an educational simulator for RISC-V
[0].

The most critical resource even if it is not super convenient is the official
RISC-V specification [1] (this is the latest draft as of writing this
comment).

There are a bunch of open source simulators [2] to look at for reference if
you are unsure of how something is suppose to work. I would recommend
compiling a C program to RISC-V and looking at disassembled machine code to
get an idea for how things are normally done in terms of RISC-V assembly.

There isn't any particularly nice documentation that I am aware of, but enough
universities teach using it that there should be some resources that would be
helpful for making a simulator. I can probably come up with some links if you
are serious about making a simulator, but its a little too much effort to do
for a drive-by comment.

[0]:
[https://github.com/TheThirdOne/rars](https://github.com/TheThirdOne/rars)

[1]: [https://github.com/riscv/riscv-isa-
manual/releases/download/...](https://github.com/riscv/riscv-isa-
manual/releases/download/draft-20200727-8088ba4/riscv-spec.pdf)

[2]: [https://github.com/riscv/riscv-software-
list#simulators](https://github.com/riscv/riscv-software-list#simulators)

~~~
charlesdaniels
Hey, thanks for making RARS! I am TA-ing a class this semester where we are
using RARS. It's my university's "build a CPU with Verilog class", and we just
switched to RISC-V. We previously used MIPS with MARS, and lacking a MARS
alternative was one of our blockers for switching to RISC-V.

------
vorticalbox
Low level javascript [1] has a great set of videos on this and other topics

[1]
[https://www.youtube.com/playlist?list=PLP29wDx6QmW5DdwpdwHCR...](https://www.youtube.com/playlist?list=PLP29wDx6QmW5DdwpdwHCRJsEubS5NrQ9b)

------
Hnrobert42
Reading through that was a great refresher. Thanks.

One question: would a real CPU have a JL instruction or would that be
implemented some other way, like a series of smaller instructions?

~~~
oso2k
`JL` and other conditional jumps/branches are a common type of instruction
[0][1][2].

Depending on the ISA [3][4][5], there can be jumps, branches, and subroutine
transfers (or even a subset of those). Some can be based (conditional) on what
value is in a primary register (often traditionally called the accumulator), a
condition/flag/status register, a memory location, and even well known
constants (0, 1, -1). Many ISAs have ways branching to a
function/subroutine/procedure that can include adjusting a calling stack or
transferring control to an OS kernel. x86 is notable in that you can cause a
branch or jump without calling a branch or jump instruction.

[0]
[https://www.tutorialspoint.com/assembly_programming/assembly...](https://www.tutorialspoint.com/assembly_programming/assembly_conditions.htm)

[1] [https://thinkingeek.com/2013/01/19/arm-assembler-
raspberry-p...](https://thinkingeek.com/2013/01/19/arm-assembler-raspberry-pi-
chapter-5/)

[2]
[https://www.ibm.com/developerworks/library/l-powasm3/index.h...](https://www.ibm.com/developerworks/library/l-powasm3/index.html)

[3]
[https://en.wikipedia.org/wiki/Instruction_set_architecture](https://en.wikipedia.org/wiki/Instruction_set_architecture)

[4]
[https://en.wikipedia.org/wiki/Branch_(computer_science)](https://en.wikipedia.org/wiki/Branch_\(computer_science\))

[5]
[https://en.wikipedia.org/wiki/Subroutine](https://en.wikipedia.org/wiki/Subroutine)

~~~
showdead
> x86 is notable in that you can cause a branch or jump without calling a
> branch or jump instruction.

I'm curious what you are referring to here. Are you talking about CMOV? Or
INT/SYSCALL/SYSENTER? Or just the ability to trigger a fault handler? Or
semantic games like push followed by ret?

------
re
A related topic (that the title initially made me think of) is fantasy
consoles like the PICO-8, although those appear to commonly use high-level
programming languages like Lua.

[https://github.com/paladin-t/fantasy](https://github.com/paladin-t/fantasy)

------
pnonplussed
okok

~~~
pnonplussed
asdfasdfas

------
MuffinFlavored
I've written a SuperH and TriCore emulator from scratch (qemu wouldn't cut
it). I couldn't do it in JavaScript because it falls apart when it comes to
bit-shifting and integer arithmetic. A lot of processors rely on the C-like
functionality of int32_t, uint32_t, etc.

>>> 0 in JavaScript just doesn't cut it.

~~~
dnautics
You can use typedarrays, no? I built a fantasy emulator several years ago, and
that was my strategy.

~~~
MuffinFlavored
like this?

const a = new Uint8Array([0xFE])

const b = new Uint8Array([0x02])

a[0] = a[0] + b[0]

~~~
dnautics
yes, although IIRC what I did was more like this:

    
    
        const r_idx = {ax: 0, bx: 1...}
        const registers = new Uint8Array([...])
        add = (r1, r2) => registers[r_idx[r1]] = registers[r_idx[r1]] + registers[r_idx[r2]]
    

which lets you do:

    
    
        add("ax", "bx")
    

which is one step closer to making it look a lot like asm syntax.

