Skip to content

Instantly share code, notes, and snippets.

@louisswarren
Last active February 23, 2021 03:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save louisswarren/001e6f2d3f79dfda1a7f40241ed5af22 to your computer and use it in GitHub Desktop.
Save louisswarren/001e6f2d3f79dfda1a7f40241ed5af22 to your computer and use it in GitHub Desktop.
Register machine

Notes:

  • There are 256 registers, which store 16 bit unsigned integers.
  • There are 65536 words (16 bits) in memory.
  • All instructions are 2-byte aligned.
  • The return stack has a maximum depth of 254.

Notation:

  • and denote registers R and S
  • [S] denotes memory with address at the value of S
  • @A@ denotes the instruction at position A

Instruction | Byte | Format | Operation

ret | 00 | 00__ | Return (or terminate) gsb @A@ | 0F | 0F__AAAA | Go to subroutine at A

lod | AA | AA--RRSS | Load from [S] into R sto | AF | AF--RRSS | Store R at [S]

inc | CA | CARR | Increment R jzd @A@ | CB | CBRRAAAA | Decrement R if R > 0, else jump to A jmp @A@ | CF | CF--AAAA | Jump to A

wrc | EA | EASS | Output from [S] rdc | EF | EFSS | Input to [S]

Memory locations

[FFFF]: stack counter [FFFE]-[FF00]: return stack [FEFF]-[????]: instruction memory [addr]-[0000]: data memory

addr is stored in register FF

gsb @doubler ; Go to the doubler part of the program
@printloop:
wrc [0A]
rdc [0A]
lod 0B [0A]
jzd 0B @end
jmp @printloop
@end: ret
; Nice to increment subroutines
@doubler:
inc 0C
rdc [0A]
lod 0B [0A]
sto 0B [0C]
wrc [0A]
wrc [0C]
ret
#include <stdio.h>
#include "register.h"
#define BUFLEN 16
const char *
skip_whitespace(const char *s)
{
for (;; ++s) {
switch (*s) {
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
return s;
}
}
}
enum instruction
get_instruction(const char *s)
{
return INSTR_GSB;
}
int
main(void)
{
char *s;
char buf[BUFLEN];
while (fgets(buf, BUFLEN, stdin) != NULL) {
s = buf;
while (*(s = skip_whitespace(s)) != '\0') {
if (*s = '@') {
}
}
}
return 0;
}
.PHONY: test test2
test: aprog.rma assembler
./assembler < $<
test2: register prog.rm
echo 'Hello, world!' | ./register
aprog.rm: aprog.rma assembler
./assembler < $< > $@
prog.rm: prog.rmh
xxd -r $^ > $@
assembler: assembler.c register.h
$(CC) $(CFLAGS) -o $@ $<
register: register.c register.h
$(CC) $(CFLAGS) -o $@ $<
.PHONY: clean
clean:
rm -f register prog *.o *.rm
00000000: 0F00 0004 EA0A 0000 CA0C EF0A AA00 0B0A
00000010: AF00 0B0C EA0A EA0C 0000
#include <stdint.h>
#include <stdio.h>
#include "register.h"
#define log(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
#define fst(X) ((X) >> 8)
#define snd(X) ((X) & 0xFF)
#define REG_SIZE 0x100
#define REG_MEM_MAX_ADDR 0xFF
#define MEM_SIZE 0x10000
#define MEM_PROG_START 0xFEFF
struct machine {
uint16_t pc;
uint16_t reg[REG_SIZE];
uint16_t mem[MEM_SIZE];
};
#define stack_ctr(M) ((M).mem[MEM_SIZE - 1])
#define stack_head(M) ((M).mem[MEM_SIZE - 1 - stack_ctr(M)])
uint16_t
get_next_instruction(struct machine *m)
{
return m->mem[MEM_PROG_START - m->pc++];
}
int
main(int argc, char **argv)
{
uint16_t inst;
uint16_t *memp;
char word[2];
FILE *f;
struct machine m = {0, {0}, {0}};
f = fopen("prog.rm", "r");
m.reg[REG_MEM_MAX_ADDR] = MEM_PROG_START;
while (fread(word, 2, 1, f)) {
m.mem[m.reg[REG_MEM_MAX_ADDR]--] = (word[0] << 8) + word[1];
}
while (1) {
inst = get_next_instruction(&m);
switch (fst(inst)) {
case INSTR_INC:
m.reg[snd(inst)]++;
break;
case INSTR_JZD:
if (m.reg[snd(inst)]) {
m.reg[snd(inst)]--;
m.pc++;
break;
}
/* FALLTHROUGH */
case INSTR_JMP:
m.pc = get_next_instruction(&m);
break;
case INSTR_RDC:
memp = &m.mem[m.reg[snd(inst)]];
fread(memp, 1, 1, stdin) || (*memp = 0);
break;
case INSTR_WRC:
memp = &m.mem[m.reg[snd(inst)]];
fwrite(memp, 1, 1, stdout);
break;
case INSTR_LOD:
inst = get_next_instruction(&m);
memp = &m.mem[m.reg[snd(inst)]];
m.reg[fst(inst)] = *memp;
break;
case INSTR_STO:
inst = get_next_instruction(&m);
memp = &m.mem[m.reg[snd(inst)]];
*memp = m.reg[fst(inst)];
break;
case INSTR_GSB:
if (stack_ctr(m)++ >= 0xFF) {
log("Stack overflow\n");
return 1;
}
stack_head(m) = m.pc + 1;
m.pc = get_next_instruction(&m);
break;
case INSTR_RET:
if (stack_ctr(m)) {
m.pc = stack_head(m);
--stack_ctr(m);
} else {
return 0;
}
break;
default:
log("ERROR at %d: bad instruction %c%c\n",
m.pc - 1,
fst(inst), snd(inst));
return 1;
}
}
}
enum instruction {
INSTR_RET = 0x00,
INSTR_GSB = 0x0F,
INSTR_LOD = 0xAA,
INSTR_STO = 0xAF,
INSTR_INC = 0xCA,
INSTR_JZD = 0xCB,
INSTR_JMP = 0xCF,
INSTR_WRC = 0xEA,
INSTR_RDC = 0xEF,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment