Skip to content

Latest commit

 

History

51 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CI

Quack-Quack - A Tiny Computer in C

Quack is a small computer simulator written in C, together with its own assembler. It simulates a simple CPU with memory, registers, and memory-mapped I/O, and runs compiled programs stored in .duck binary files, which qasm assembles from .qasm source. By the end of the project the simulator can run a fully interactive maze game.

Demo

./quack run programs/maze.duck

The starting frame:

####################
#@.................#
#.##..#..######.##.#
#....##......#.....#
###.######.#.#.###.#
#.....#....#.#...#.#
#.###.#.####.###.#.#
#...#.#......#...#E#
#.#.#.########.###.#
####################

The maze is driven entirely by memory-mapped keyboard input and byte load/store instructions.

Architecture

  • 4096 bytes of memory, shared between code, data, the stack, and I/O
  • Four general-purpose registers (R0 to R3), plus a program counter, a stack pointer, and a zero flag
  • Every instruction is exactly 4 bytes: one opcode byte, one register byte, and two further bytes for a second register or a 16-bit immediate value
  • Keyboard and screen access are memory-mapped I/O, not special instructions

The full instruction set, memory map, and encoding are in docs/ISA.md.

Quickstart

See docs/SETUP.md if you don't already have a C compiler and make. Once you do:

make
./quack run programs/sum10.duck

Expected final value: R0(final)=0037. That's hexadecimal; 0x0037 = 55 decimal, and 1+2+...+10 = 55.

Then try the maze:

./quack run programs/maze.duck

Type one key and press Enter:

  • W = up
  • A = left
  • S = down
  • D = right
  • Q = quit

Goal: reach the exit E. Player is @, walls are #.

Debug mode

Debug mode prints the CPU state every step:

./quack run --debug programs/sum10.duck

Use it to check whether pc advances correctly and whether a program is looping.

Scripted input

Instead of typing, you can provide a script of keys:

./quack run --script "DDSSAAQ" programs/maze.duck

This makes debugging and test runs reproducible.

What we implemented

The scaffolding came with the course: the memory map, opcode definitions, program loader and command-line interface. We wrote the implementations inside it.

  • The four memory functions (mem_read8, mem_write8, mem_read16, mem_write16), with bounds checking and 16-bit little-endian access.
  • The fetch/decode/execute loop in cpu_step, covering data movement, the ALU, byte and word load/store, control flow, and the stack with CALL and RET.
  • Memory-mapped keyboard and screen handling in io_read8 and io_write8.
  • Four instructions of our own: INC (0x12), DEC (0x13), CLR (0x14) and OUTDEC (0x42), plus an instruction counter that prints at HALT.

Report.pdf explains the design and records who did what.

Writing your own programs with qasm

.qasm source assembles into a .duck binary:

./qasm programs/yourprogram.qasm -o programs/yourprogram.duck

If you omit -o, the output path is the input path with a trailing .qasm replaced by .duck (or .duck appended if it has no such suffix).

A program is one instruction, directive, or label per line. Registers are r0 through r3; immediates are $5 or $0x0b; addresses are decimal, 0xHEX, or a label name. For example:

# Sum 1..10 into r0
    irmovb $0, r0
    irmovb $1, r1
    irmovb $11, r2
    irmovb $1, r3
loop:
    addw r1, r0
    addw r3, r1
    cmpw r1, r2
    je done
    jmp loop
done:
    halt

The full instruction list, byte encodings, and directives (.byte, .word, .string, .org) are in docs/ISA.md.

Testing

make memtest    # unit tests for the four memory functions
make test       # everything below

make test runs:

  • tests/roundtrip.sh: assembling every committed .qasm file must reproduce its committed .duck binary byte-for-byte.
  • tests/e2e.sh: running every committed program must produce exactly its expected stdout.
  • tests/errors.sh: feeding qasm malformed source must fail with the right error.
  • tests/coverage.sh: every opcode defined in quack.c must be exercised by at least one program with readable .qasm source.
  • tests/runtime_errors.sh: programs that hit a documented runtime failure mode must exit non-zero with the specific stderr message for that failure, not a generic one.

Project for the Computer Architecture course at Maastricht University (Ashish Sai, Guangzhi Tang).

License

All rights reserved. See LICENSE.

This repository is published for viewing only. Parts of the code were provided as course material and remain the property of their authors; the LICENSE says which.

About

A CPU simulator and assembler in C, with a 24-instruction ISA and memory-mapped I/O.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages