Skip to content

Instantly share code, notes, and snippets.

@zenoamaro
Last active March 28, 2018 09:56
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zenoamaro/1df3728f4291c5090dc3 to your computer and use it in GitHub Desktop.
Save zenoamaro/1df3728f4291c5090dc3 to your computer and use it in GitHub Desktop.
Functional-style emulator of a simple 8-bit CPU.
/*
A functional-style emulator of a simple 8-bit CPU.
III AAAAA
00000 011 00000 INC
00001 010 00000 JMP 0
$ babel-node chip.js
1 CPU reset->fetch1 RST:no READ:no WRITE:no A:0 AR:0 DR:0 IR:0 PC:0
RAM RST:no READ:no WRITE:no AR:0 DR:0
2 CPU fetch1->fetch2 RST:no READ:yes WRITE:no A:0 AR:0 DR:96 IR:0 PC:0
RAM RST:no READ:yes WRITE:no AR:0 DR:96
3 CPU fetch2->inc1 RST:no READ:no WRITE:no A:0 AR:0 DR:96 IR:3 PC:1
RAM RST:no READ:no WRITE:no AR:0 DR:96
4 CPU inc1->fetch1 RST:no READ:no WRITE:no A:1 AR:0 DR:96 IR:3 PC:1
RAM RST:no READ:no WRITE:no AR:0 DR:96
5 CPU fetch1->fetch2 RST:no READ:yes WRITE:no A:1 AR:1 DR:64 IR:3 PC:1
RAM RST:no READ:yes WRITE:no AR:1 DR:64
6 CPU fetch2->jmp1 RST:no READ:no WRITE:no A:1 AR:0 DR:64 IR:2 PC:2
RAM RST:no READ:no WRITE:no AR:0 DR:64
7 CPU jmp1->fetch1 RST:no READ:no WRITE:no A:1 AR:0 DR:64 IR:2 PC:0
RAM RST:no READ:no WRITE:no AR:0 DR:64
8 CPU fetch1->fetch2 RST:no READ:yes WRITE:no A:1 AR:0 DR:96 IR:2 PC:0
RAM RST:no READ:yes WRITE:no AR:0 DR:96
9 CPU fetch2->inc1 RST:no READ:no WRITE:no A:1 AR:0 DR:96 IR:3 PC:1
RAM RST:no READ:no WRITE:no AR:0 DR:96
10 CPU inc1->fetch1 RST:no READ:no WRITE:no A:2 AR:0 DR:96 IR:3 PC:1
RAM RST:no READ:no WRITE:no AR:0 DR:96
11 CPU fetch1->fetch2 RST:no READ:yes WRITE:no A:2 AR:1 DR:64 IR:3 PC:1
RAM RST:no READ:yes WRITE:no AR:1 DR:64
12 CPU fetch2->jmp1 RST:no READ:no WRITE:no A:2 AR:0 DR:64 IR:2 PC:2
RAM RST:no READ:no WRITE:no AR:0 DR:64
...
*/
// SYSTEM ----------------------------------------------------------------------
function systemCycle(state) {
var {cpu, ram} = state;
cpu = cpuCycle(cpu);
ram = { ...ram, read:cpu.read, write:cpu.write, ar:cpu.ar, dr:cpu.dr }; /* BUS */
ram = ramCycle(ram);
cpu = { ...cpu, dr:ram.dr }; /* BUS */
const cycle = state.cycle + 1;
return { ...state, cycle, cpu, ram };
}
function systemReset(state) {
return {
cycle: 1,
cpu: cpuReset(),
ram: ramReset(),
};
}
function systemDebug(state) {
return `${state.cycle}\t${cpuDebug(state.cpu)}\n`
+ `\t${ramDebug(state.ram)}`;
}
// CPU -------------------------------------------------------------------------
const phases = {
fetch1: cpuFetch1,
fetch2: cpuFetch2,
add1: cpuAdd1,
add2: cpuAdd2,
and1: cpuAnd1,
and2: cpuAnd2,
jmp1: cpuJmp1,
inc1: cpuInc1,
};
const opcodes = {
0b000: 'add1', /* ADD X - Adds X to A */
0b001: 'and1', /* AND X - ANDs X and A */
0b010: 'jmp1', /* JMP X - Jumps to X */
0b011: 'inc1', /* INC - Increments A by 1 */
};
function cpuCycle(state) {
state = { ...state, phase:state.next };
if (state.rst) var receiver = cpuReset;
else receiver = phases[state.phase];
return { ...state, ...receiver(state) };
}
function cpuReset(state) {
return {
phase: 'reset',
next: 'fetch1',
rst: false,
read: false,
write: false,
a: 0,
dr: 0,
ir: 0,
ar: 0,
pc: 0,
};
}
function cpuFetch1(state) {
return {
next: 'fetch2',
read: true,
ar: state.pc,
};
}
function cpuFetch2(state) {
const ir = (state.dr & 0b11100000) >> 5;
const ar = (state.dr & 0b00011111) >> 0;
const next = opcodes[ir];
return {
next, ir, ar,
read: false,
pc: state.pc + 1,
};
}
function cpuAdd1(state) {
return {
next: 'add2',
read: true,
};
}
function cpuAdd2(state) {
return {
next: 'fetch1',
read: false,
a: (state.a + state.dr) % 255,
};
}
function cpuAnd1(state) {
return {
next: 'and2',
read: true,
};
}
function cpuAnd2(state) {
return {
next: 'fetch1',
a: (state.a & state.dr) % 255,
read: false,
};
}
function cpuJmp1(state) {
return {
next: 'fetch1',
pc: state.dr & 0b00011111,
};
}
function cpuInc1(state) {
return {
next: 'fetch1',
a: state.a + 1,
};
}
function cpuDebug(state) {
const rst = state.rst? 'yes' : 'no';
const read = state.read? 'yes' : 'no';
const write = state.write? 'yes' : 'no';
const {phase, next, a, dr, ir, ar, pc} = state;
return `CPU ${phase}->${next} RST:${rst} READ:${read} WRITE:${write} `
+ `A:${a} AR:${ar} DR:${dr} IR:${ir} PC:${pc}`;
}
// RAM -------------------------------------------------------------------------
function ramCycle(state) {
if (state.rst) var receiver = ramReset;
else if (state.read) receiver = ramRead;
else if (state.write) receiver = ramWrite;
if (receiver) return { ...state, ...receiver(state) };
else return state;
}
function ramReset(state) {
return {
rst: false,
read: false,
write: false,
size: 0b100000,
data: new Array(0b100000),
ar: 0,
dr: 0,
};
}
function ramRead(state) {
const ar = state.ar;
const dr = state.data[ar];
return { ar, dr };
}
function ramWrite(state) {
const {ar, dr} = state;
var data = state.data.slice();
data[ar] = dr;
return { data };
}
function ramDebug(state) {
const rst = state.rst? 'yes' : 'no';
const read = state.read? 'yes' : 'no';
const write = state.write? 'yes' : 'no';
const {ar, dr} = state;
return `RAM RST:${rst} READ:${read} WRITE:${write} AR:${ar} DR:${dr}`;
}
// RUN -------------------------------------------------------------------------
var system = systemReset();
system.ram.data = [
/* IIIAAAAA */
/* 00000 */ 0b01100000, /* INC */
/* 00001 */ 0b01000000, /* JMP 0 */
];
for (var i=0; i<32; i++) {
console.log(systemDebug(system));
system = systemCycle(system);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment