Computer Architecture · MIPS16 Study Tool
v2.8 · offline
Computer Architecture

Computer Architecture

MIPS16 ISA study tool

0
MODULES DONE
AVG SCORE
0
QUESTIONS ANSWERED
HOW IT WORKS — Complete each module's content then take the quiz. Scores are saved in your browser and persist across sessions. The recommended next module is chosen based on what you haven't mastered yet. Each quiz re-tests weak areas first.
MODULE 01

The Toolchain Demystified

Compiler vs Assembler vs Linker. Where the MIPS16 reference tool fits. Why these are completely different beasts.

Concepts Diagrams Quiz: 12 questions

1.1 — THE CENTRAL QUESTION

You write text. The CPU executes bits. Something in the middle does the translation. But what? Most people collapse this entire process into one word — "compile" — and that's where the confusion starts.

There are actually four separate tools in the classic pipeline, each doing something fundamentally different. Let's name them and understand them before we look at the MIPS16 reference tool specifically.

1.2 — THE FOUR TOOLS

Source code main.c Preprocessor #include → expand Compiler understands meaning Assembler text → binary bits Linker combine objects CPU executes still C text → ASM text → .o object ▲ MIPS16 REFERENCE TOOL assembler + emulator The MIPS16 tool SKIPS the linker. Single-file asm loads directly into imem[] array and runs. WHAT EACH TOOL KNOWS ABOUT YOUR PROGRAM COMPILER ✓ types, semantics ✓ operator precedence ✓ optimization ✗ exact addresses ASSEMBLER ✓ opcode encodings ✓ label → address ✓ register numbers ✗ what code means LINKER ✓ cross-file symbols ✓ library resolution ✗ semantics ✗ individual opcodes CPU ✓ only sees bits ✓ fetch/decode/exec ✗ variable names ✗ comments, labels

The compiler: the smart one

The compiler is the only tool that understands what your program means. It parses grammar, enforces types, detects logical errors (int x = "hello" fails here), and applies optimizations. Its output is not an executable — it's assembly text. On its own, nothing runs.

The assembler: the mechanical translator

The assembler does zero semantic analysis. It reads assembly mnemonics and applies a lookup table. ADD is always opcode 0b0000. Period. It cannot catch logic errors. It cannot optimize. Its only "intelligence" is resolving label addresses — figuring out that when you write BEQ $r4, LOOP, the address of LOOP is word address 6. That single task requires the two-pass algorithm we'll study in Module 3.

KEY DISTINCTION
A compiler transforms semantic levels (C intent → assembly intent). An assembler transforms representations of the same level (mnemonic text → binary encoding). One understands meaning; the other does not.

The linker: the glue

When you call printf() in C, the assembler produces a placeholder — it doesn't know where printf lives. The linker's job is to stitch together all the object files (your code + standard library) and resolve these cross-file references. The MIPS16 reference assembler skips this step entirely because it is single-file — everything is resolved by the assembler itself.

1.3 — YOUR MIPS16 TOOL IN CONTEXT

The reference MIPS16 tool combines two things: an assembler (the assemble() function) and an emulator (the stepCPU() function). You type assembly text → the assembler converts it to an array of 16-bit words → the emulator fetches and executes those words one by one.

This pattern — self-contained assembler-emulator — is how educational processors work (LC-3, MARIE), and how embedded microcontroller tools worked in the 1980s-90s.

PRACTICAL IMPLICATION
Because there's no linker, every label you use in a MIPS16 program must be defined in the same source file. You can't have CALL external_function pointing to another file. This is a deliberate simplification for learning.

1.4 — IS AN ASSEMBLER A COMPILER?

Technically, an assembler is a compiler in the broad Computer Science sense (any program that translates from one language to another). But in practice, when engineers say "compiler" they mean a tool that understands semantics and transforms across abstraction levels. An assembler is categorically simpler — it's closer to a structured find-and-replace than a real compiler.

The clearest test: can the tool catch a semantic error? A compiler catches if (x = 5) (assignment where comparison was intended). An assembler cannot — if your logic is wrong, the assembler happily encodes your bug into bits.

MODULE 1 QUIZ

Question 1 of 12
MODULE 02

The MIPS16 ISA

16 registers. 16-bit words. 16 opcodes. Every bit of every instruction format decoded from the source.

Bit layouts All 16 instructions Quiz: 15 questions

2.1 — WHAT IS AN ISA?

An Instruction Set Architecture (ISA) is the contract between software and hardware. It defines exactly: which operations the CPU can perform, how instructions are encoded as bits, how many registers exist, how memory is addressed, and how control flow works.

The MIPS16 ISA defined in the reference tool is a pedagogical 16-bit architecture. Every design decision is visible and motivated. Let's read it from the source code.

2.2 — REGISTERS

MIPS16 has 16 registers, each 16 bits wide. They are numbered r0–r15 and have conventional aliases:

NumberAliasPurposeSpecial rule
r0$zeroAlways zeroWrites are silently discarded
r1–r4$t0–$t3TemporariesCaller-saved
r5–r8$s0–$s3Saved varsCallee-saved
r9–r12$a0–$a3Arguments / return values
r13$fpFrame / base pointerUsed by LDW/STW for addressing
r14$spStack pointerGrows downward
r15$raReturn addressJLR writes PC+1 here
WHY r0 IS ALWAYS ZERO — FROM THE SOURCE
In stepCPU(), line: if(werf && wd!==null && Rd!==0){newRegs[Rd]=...} — the write-enable check includes Rd!==0. Writing to r0 simply does nothing. This is a classic RISC trick: you get a "free" zero operand without needing a special instruction, and MOV Rd, Rs is just ADD Rd, Rs, $zero.

2.3 — THE TWO INSTRUCTION FORMATS

Every MIPS16 instruction is exactly 16 bits. The top 4 bits are always the opcode. The remaining 12 bits depend on the format:

R-Format (Register)

Used by arithmetic/logic instructions that operate on registers.

bits 15–12 bits 11–8 bits 7–4 bits 3–0
OP (4 bits) Rd (4 bits) Rs (4 bits) Rt (4 bits)
opcode destination source 1 source 2

Example: ADD $r2, $r1, $r3 → opcode=0000, Rd=0010, Rs=0001, Rt=0011 → 0x0213

I-Format (Immediate)

Used by memory, branch, and load-immediate instructions. The second source is a signed 8-bit constant baked into the instruction itself.

bits 15–12 bits 11–8 bits 7–0
OP (4 bits) Rd (4 bits) Im8 (8 bits)
opcode register signed immediate [−128, 127] (unsigned [0,255] for LUI)

Example: ADI $r2, 5 → opcode=1101, Rd=0010, Im8=00000101 → 0xD205

2.4 — ALL 16 INSTRUCTIONS

The ISA is defined in the const ISA = {...} object in the source. Here is every instruction, its opcode, format, and exact operation:

MnemonicOpcodeFmtOperationNotes
ADD0000RRd = Rs + Rt (16-bit wrap)
SUB0001RRd = Rs − Rt
AND0010RRd = Rs & Rtbitwise
OR0011RRd = Rs | Rtbitwise
XOR0100RRd = Rs ^ Rtbitwise
SLT0101RRd = (Rs < Rt) ? 1 : 0signed compare
SLL0110RRd = Rs << (Rt & 0xF)shift left logical
MUL0111RRd = Rs × Rt (low 16 bits)
LDW1000IRd = MEM[$fp + Im8]base=$fp always
STW1001IMEM[$fp + Im8] = Rdno write to regfile
BEQ1010Iif Rd==0: PC = PC+1+Im8PC-relative offset
BNE1011Iif Rd≠0: PC = PC+1+Im8PC-relative offset
LUI1100IRd = Im8 << 8Im8 unsigned [0,255]
ADI1101IRd = Rd + Im8 (signed)adds to self
SRL1110R*Rd = Rs >>> (Rt & 0xF)logical (zero-fill)
JLR1111R*Rd = PC+1; PC = Rsjump+link; Rt unused

* SRL and JLR use R-format encoding but have special behavior. JLR ignores Rt.

2.5 — MEMORY MODEL

MIPS16 uses word addressing — each address refers to one 16-bit word, not one byte. So address 0x0001 is the second word (bytes 2–3). This is different from byte-addressed architectures like x86. The emulator displays addresses in byte terms (×2) for compatibility, but internally everything is word-addressed.

WORD ADDRESS (internal) 0x0000 0x0001 0x0002 0x0003 BYTE ADDRESS (display × 2) 0x0000 0x0002 0x0004 0x0006 Each cell = 16 bits (one word) bx(w) = "0x" + (w*2).toString(16) from the emulator source LDW offset is in WORDS, not bytes LDW $r1, 2 → reads address $fp+2

2.6 — BRANCH ADDRESSING

BEQ and BNE use PC-relative addressing. The Im8 field is a signed offset added to PC+1. So BEQ $r4, -3 jumps to 3 words before the next instruction — which is a backwards loop.

In the assembler: when you write BEQ $r4, LOOP, the assembler computes Im8 = address(LOOP) − (current_word_address + 1). This is done in Pass 2 using the labelMap built in Pass 1.

GOTCHA — BRANCH RANGE
Im8 is 8 bits signed: range [−128, 127]. A branch can only reach ±127 words from the current instruction. For longer jumps, you must use JLR (load address into register, then jump).

2.7 — THE SPECIAL CASES: LUI + ADI

Since Im8 is only 8 bits, you cannot load an arbitrary 16-bit value in one instruction. The solution is two instructions working together:

; Load 0xBEEF into $r1
LUI $r1, 0xBE    ; $r1 = 0xBE00  (shift left 8)
ADI $r1, -17    ; $r1 = 0xBE00 + (-17) = 0xBDEF... wait

There's a subtlety: if the low byte is ≥ 128, it will sign-extend to negative when used as Im8. So the assembler's LI pseudo-instruction adjusts the high byte upward by 1 to compensate. This is exactly what the expandPseudo function does for LI.

MODULE 2 QUIZ

Question 1 of 15
MODULE 03

How the Assembler Works

Two-pass algorithm. Label resolution. The forward-reference problem. Every function in assemble() explained.

Tokenisation Pass 1 & Pass 2 Quiz: 14 questions

3.1 — WHY TWO PASSES?

Here's the fundamental problem. You're writing this program:

BEQ $r4, DONE     ; jump to DONE — but where IS Done?
ADD $r1, $r2, $r3
DONE: JLR $r0, $r15

When the assembler reads line 1, it hasn't seen DONE: yet. It doesn't know the address. This is the forward reference problem — you're referencing a label that's defined later in the file.

The solution is to read the file twice:

SOURCE TEXT BEQ $r4, DONE ADD $r1,$r2,$r3 DONE: JLR $r0,$r15 PASS 1 count words, record labels addr=0: BEQ → 1 word addr=1: ADD → 1 word DONE → labelMap[DONE]=2 labelMap DONE → 2 (word address) PASS 2 encode every instruction BEQ: Im8=DONE-(0+1)=1 ADD: 0x0213 JLR: 0xF00F program[] 0xA401 BEQ 0x0213 ADD 0xF00F JLR

3.2 — PASS 1: TOKENISATION AND LABEL COLLECTION

Pass 1 iterates over every line, doing three things:

  1. Strip commentsstripComment() removes everything after ; or #, respecting quoted strings.
  2. Extract labels — anything before a : gets added to labelMap with the current word address.
  3. Count words — figure out how many 16-bit words each line emits, and advance the address counter. This is where expandPseudo() is called to check if a pseudo-instruction expands to 1 or 2 real instructions.
SOURCE REFERENCE
Key variables built in Pass 1:
labelMap — maps label names (uppercase) to word addresses
equMap — maps .equ constants to values
tokList — tokenized version of every source line with its word address

3.3 — PASS 2: ENCODING

Pass 2 iterates over tokList (not the raw source again). Now it has the complete labelMap, so every forward reference can be resolved. For each instruction:

  1. Look up the mnemonic in ISA to get the opcode and format.
  2. If R-format: parse three register operands, pack as (op<<12)|(Rd<<8)|(Rs<<4)|Rt.
  3. If I-format: parse register + immediate, resolve labels, pack as (op<<12)|(Rd<<8)|Im8.
  4. Push the result word into the program[] array.

Encoding R-format — from the source

// From assemble(), Pass 2, R-format branch:
const Rd = parseReg(a[0]), Rs = parseReg(a[1]);
const Rt = m==="JLR" ? 0 : parseReg(a[2]||"$r0");
word = ((op & 0xF) << 12) | ((Rd & 0xF) << 8)
     | ((Rs & 0xF) << 4)  |  (Rt & 0xF);

Encoding I-format branch offset

// BEQ/BNE: offset = target_address − (current_word_address + 1)
imm = combined[lbl] - (wa + 1);
// wa = current word address, combined = labelMap merged with equMap

3.4 — THE COMPLETE ASSEMBLY PIPELINE

source.asm raw text stripComment tokenise extract labels labelMap equMap tokList Pass 2 encode resolve refs bit-pack words program[] 16-bit words imem[] → CPU runs

MODULE 3 QUIZ

Question 1 of 14
MODULE 04

Pseudo-Instructions & Directives

NOP, MOV, LI, BEQ/BNE expansion. The .equ, .org, .word, .asciiz directives. How the assembler rewrites these before encoding.

Pseudo expansion Assembler directives Quiz: 12 questions

4.1 — WHAT IS A PSEUDO-INSTRUCTION?

A pseudo-instruction is a mnemonic that looks like a real instruction but isn't — the CPU has no opcode for it. The assembler expands it into one or more real instructions before encoding. This is pure syntactic sugar: makes code more readable, generates real bits.

4.2 — THE FIVE PSEUDO-INSTRUCTIONS

PseudoSyntaxExpands toWhy
NOPNOP ADD $r0, $r0, $r0 No-op: add zero to zero, discard result. Zero cycles wasted conceptually.
JRJR Rs JLR $r0, Rs Jump to Rs, but discard return address (write to $r0 which ignores writes)
MOVMOV Rd, Rs ADD Rd, Rs, $r0 Rd = Rs + 0 = Rs. Register copy using $zero as addend.
LILI Rd, imm16 LUI Rd, hi8 + ADI Rd, lo8 Load full 16-bit value. Two instructions because Im8 is only 8 bits.
BEQ/BNE
(3-arg)
BEQ Rs, Rt, label SUB $r1, Rs, Rt + BEQ $r1, label Hardware BEQ tests Rd==0. To compare two regs, subtract first, then test zero.
LI — THE TRICKY CASE
To load 0xBEEF: hi=0xBE, lo=0xEF=239. As signed Im8, 239 → -17. So ADI would subtract 17 instead of adding 239. The assembler compensates: if lo ≥ 128, it increments hi by 1 (so LUI loads 0xBF00), then ADI adds the negative offset (0xBF00 + (-17) = 0xBEEF). This is exactly the code in expandPseudo('LI').

4.3 — ASSEMBLER DIRECTIVES

Directives are not instructions at all — they're commands to the assembler itself about how to organize the output. They produce no CPU opcodes (except .word/.space/.asciiz which emit data words).

DirectiveSyntaxEffect
.equNAME .equ VALUEDefine a symbolic constant. Used like a number anywhere in the source. Handled in Pass 1.
.org.org ADDRSet the current word address counter. Next instruction/data goes at ADDR.
.word.word VALUEEmit one 16-bit data word at the current address.
.space.space NEmit N zero-words. Used to allocate uninitialized data.
.ascii.ascii "str"Emit each character as one word (ASCII code). No null terminator.
.asciiz.asciiz "str"Like .ascii but appends a zero word at the end (C-string style).

Example: placing data at a specific address

        .org  0x0000         ; code starts at word 0

_start:
        LI   $fp, 0x0020    ; point $fp at our buffer
        LDW  $t0, 0          ; load first word of buffer
        JLR  $r0, $r15       ; halt

        .org  0x0020         ; data section at word 0x20
msg:    .asciiz "Hi!"        ; 4 words: 'H','i','!',0x00
val:    .word   0xBEEF       ; 1 word: 0xBEEF
HOW .ORG WORKS IN THE SOURCE
In Pass 1: if (mn===".ORG") { addr = parseImm(args[0],...); continue; } — it simply resets the word-address counter. Everything after it is placed starting at the new address. This is how you put code at 0x0000 and data at 0x0020 in the same file.

MODULE 4 QUIZ

Question 1 of 12
MODULE 05

CPU & Emulator Internals

Fetch. Decode. Execute. How stepCPU() works line by line. MMIO. Why the datapath is unified.

Datapath stepCPU walkthrough Quiz: 13 questions

5.1 — THE FETCH-DECODE-EXECUTE CYCLE

Every CPU, from your laptop to a microcontroller, runs the same basic loop indefinitely:

  1. Fetch — read the instruction word at address PC from memory
  2. Decode — split the 16-bit word into fields (op, Rd, Rs, Rt, Im8)
  3. Execute — perform the operation, update registers/memory/PC

In stepCPU() this happens in one JavaScript function call per instruction. The state object ({regs, mem, pc}) is immutable — each call returns a new state object. This is clean functional-style emulation.

5.2 — DECODING A WORD

// From stepCPU() — first thing after fetching:
const word = imem[pc];
const op  = (word >> 12) & 0xF;   // top 4 bits
const Rd  = (word >>  8) & 0xF;   // bits 11–8
const Rs  = (word >>  4) & 0xF;   // bits 7–4
const Rt  =  word        & 0xF;   // bits 3–0
const im8b =  word        & 0xFF;  // bits 7–0 (unsigned)
const Im8s =  sx8(im8b);           // sign-extended
SIGN EXTENSION
sx8(v) converts an 8-bit unsigned value to signed: if v ≥ 128, return v−256. So 0xFF → −1. This is how ADI $r1, -1 encodes 0xFF in the Im8 field and the CPU correctly subtracts 1.

5.3 — THE UNIFIED DATAPATH

Rather than a giant switch on opcode, stepCPU() uses a clever unified datapath: it computes a set of intermediate signals that are then muxed (selected) based on the opcode. This mirrors real hardware:

REGISTER FILE r0…r15 RD1 = regs[ra1] RD2 = regs[ra2] ALU aluExec(fn, A, B) fn = alufnOvr B = RD2 or Im8s MUX ra1 sel Rs or $fp MEMORY mem[fp+Im8s] LDW read / STW write WRITE-BACK MUX alu_res mem read Im8<<8 (LUI) PC+1 (JLR) PC UPDATE PC+1 / branch / JLR write back to regfile (if werf && Rd≠0) Control signals from opcode: alufnOvr · ra1 · ra2 · bsel · werf · we · bt These select which ALU function, which registers to read, whether to write back, whether to branch

5.4 — KEY CONTROL SIGNALS

// alufnOvr — overrides ALU function for non-ALU ops:
// LDW/STW/ADI → use ADD (0000) to compute address/value
// BEQ/BNE → use SUB (0001) to test zero
// LUI/JLR → null (ALU not used)
const alufnOvr =
  [0b1000,0b1001,0b1101].includes(op) ? 0b0000  // ADD
: [0b1010,0b1011].includes(op)         ? 0b0001  // SUB
: [0b1100,0b1111].includes(op)         ? null
: op;                                              // use op directly

// werf — write enable to register file
// STW, BEQ, BNE don't write to registers
const werf = ![0b1001,0b1010,0b1011].includes(op);

5.5 — MMIO: MEMORY-MAPPED I/O

Instead of special I/O instructions, MIPS16 puts I/O devices at specific memory addresses. Read/write those addresses and you talk to the device. The emulator intercepts LDW/STW at four magic word addresses:

Word AddressNameDirectionMeaning
0x7FF8IN_STATUSRead1 if a character is waiting in the input queue; 0 if empty
0x7FF9IN_DATAReadRead next character from queue (consumes it)
0x7FFAOUT_STATUSReadAlways returns 1 (output always ready)
0x7FFBOUT_DATAWriteWrite a character to the terminal
POLLING PATTERN — FROM EXAMPLE PROGRAMS
pc_poll: LDW $t1, 0 ; read OUT_STATUS via $fp=0x7FFA
BEQ $t1, pc_poll ; loop until status=1
STW $a0, 0 ; write char to OUT_DATA

MODULE 5 QUIZ

Question 1 of 13
MODULE 06

Writing Real Programs

Calling conventions. Stack frames. Loops and conditionals. I/O. Building factorial, string output, and a calculator.

Stack discipline Calling conventions Quiz: 14 questions

6.1 — CALLING CONVENTIONS

When you call a function in MIPS16 assembly, both caller and callee must agree on how to pass arguments, return values, and preserve registers. This informal agreement is the calling convention.

RoleRegistersWho saves
Arguments in$a0–$a3 (r9–r12)Caller sets them before call
Return value$a0 (r9)Callee sets before returning
Return address$ra (r15)JLR writes it; callee must save if it calls others
Temporaries$t0–$t3 (r1–r4)Caller-saved: assume trashed after any call
Saved vars$s0–$s3 (r5–r8)Callee-saved: must restore before returning
Stack pointer$sp (r14)Must be same on return as on entry
Frame pointer$fp (r13)Flexible — often set to $sp at frame entry

6.2 — THE STACK FRAME

The stack grows downward (lower addresses). ADI $sp, -1 allocates one word on the stack. ADI $sp, 1 frees it. This is word-addressed, so each "slot" is one 16-bit word.

caller's frame ↑ higher addresses saved $ra ← $sp+1 (old $sp) saved $s0 local variable local variable ← $sp (current top) free space ↓ STW $ra, 1 STW $s0, 2 locals Frame setup: ADI $sp, -4 MOV $fp, $sp

6.3 — ANNOTATED EXAMPLE: SUM 1..N

; Sum integers 1..N, result in $a0
; Uses: $t0=counter, $t1=sum, $t2=N

        LUI  $r1, 0        ; $t0 = 0  (counter)
        ADI  $r1, 0
        LUI  $r2, 0        ; $t1 = 0  (sum)
        ADI  $r2, 0
        LUI  $r3, 0        ; $t2 = 8  (N)
        ADI  $r3, 8

LOOP:   ADI  $r1, 1        ; counter++
        ADD  $r2, $r2, $r1 ; sum += counter
        SUB  $r4, $r3, $r1 ; temp = N - counter
        BNE  $r4, LOOP     ; if temp≠0: loop
                            ; (BNE branches if Rd ≠ 0)
        ADD  $r9, $r2, $r0 ; $a0 = sum
        JLR  $r0, $r15     ; return / halt
LOOP PATTERN
MIPS16 has no "branch-if-less-than". The loop termination uses: SUB to compute N−counter, then BNE to check if the result is nonzero. When counter reaches N, SUB gives 0, BNE doesn't branch, and we fall through. This is the fundamental loop pattern.

6.4 — BUILDING A FUNCTION WITH STACK DISCIPLINE

; putchar($a0) — write one character to terminal
putchar:
        ADI  $sp, -1       ; allocate 1 stack slot
        MOV  $fp, $sp      ; $fp points to frame
        STW  $ra, 0        ; save return address

pc_poll:
        LI   $t0, 0x7FFA   ; $t0 = OUT_STATUS address
        MOV  $fp, $t0      ; use $fp for LDW base
        LDW  $t1, 0        ; read OUT_STATUS
        MOV  $fp, $sp      ; restore $fp
        BEQ  $t1, pc_poll  ; loop while not ready

        LI   $t0, 0x7FFB   ; $t0 = OUT_DATA address
        MOV  $fp, $t0
        STW  $a0, 0        ; write char
        MOV  $fp, $sp

        LDW  $ra, 0        ; restore $ra
        ADI  $sp, 1        ; free stack slot
        MOV  $fp, $sp
        JR   $ra            ; return

MODULE 6 QUIZ

Question 1 of 14
QUICK REFERENCE

MIPS16 ISA Reference Card

All instructions, registers, formats, and MMIO addresses at a glance.

REGISTERS

r#AliasRoler#AliasRole
r0$zeroAlways 0 (writes ignored)r8$s3Saved var (callee-save)
r1$t0Temporaryr9$a0Arg0 / return value
r2$t1Temporaryr10$a1Arg1
r3$t2Temporaryr11$a2Arg2
r4$t3Temporaryr12$a3Arg3
r5$s0Saved varr13$fpFrame/base ptr (LDW/STW base)
r6$s1Saved varr14$spStack pointer
r7$s2Saved varr15$raReturn address

R-FORMAT INSTRUCTIONS (op bits 15–12)

OpMnemonicOperation
0000ADDRd = Rs + Rt
0001SUBRd = Rs − Rt
0010ANDRd = Rs & Rt
0011ORRd = Rs | Rt
0100XORRd = Rs ^ Rt
0101SLTRd = (signed(Rs) < signed(Rt)) ? 1 : 0
0110SLLRd = Rs << (Rt & 0xF)
0111MULRd = Rs × Rt (low 16 bits)
1110SRLRd = Rs >>> (Rt & 0xF) logical right shift
1111JLRRd = PC+1 (word addr); PC = Rs

I-FORMAT INSTRUCTIONS

OpMnemonicOperation
1000LDWRd = MEM[$fp + Im8s]
1001STWMEM[$fp + Im8s] = Rd (no regfile write)
1010BEQif Rd == 0: PC = PC+1+Im8s
1011BNEif Rd != 0: PC = PC+1+Im8s
1100LUIRd = Im8 << 8 (Im8 unsigned 0–255)
1101ADIRd = Rd + Im8s (signed −128 to 127)

PSEUDO-INSTRUCTIONS

PseudoExpands to
NOPADD $r0, $r0, $r0
JR RsJLR $r0, Rs
MOV Rd, RsADD Rd, Rs, $r0
LI Rd, imm16LUI Rd, adj_hi + ADI Rd, adj_lo
BEQ Rs, Rt, lblSUB $r1, Rs, Rt + BEQ $r1, lbl
BNE Rs, Rt, lblSUB $r1, Rs, Rt + BNE $r1, lbl

MMIO ADDRESSES (word addresses)

AddrNameR/WMeaning
0x7FF8IN_STATUSR1 if char ready, 0 otherwise
0x7FF9IN_DATARRead next char from input queue
0x7FFAOUT_STATUSRAlways 1 (always ready)
0x7FFBOUT_DATAWWrite char to terminal

DIRECTIVES

DirectiveEffect
.equ NAME VDefine constant NAME = V (handled Pass 1, no words emitted)
.org ADDRSet word address counter to ADDR
.word VEmit one 16-bit word with value V
.space NEmit N zero words
.ascii "s"Emit each char as one word (no null)
.asciiz "s"Emit each char + null word at end
DISCLAIMER

Legal Notice & Attribution

Please read before using this tool.

Independent Project

This study tool is an independent project developed for pedagogic purposes in the context of Computer Architecture at . It is provided AS IS, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement.

No Endorsement

No institution or individual has endorsed, reviewed, sponsored, or approved this tool in any way.

No Guarantees

The content of this tool — including quiz questions, module text, assembler behavior, and emulator output — may contain errors or omissions. It is not a substitute for official course materials, lectures, or instructor guidance. Always refer to official sources for authoritative information.

Limitation of Liability

The author assumes no liability for any direct, indirect, incidental, or consequential damages arising from the use or inability to use this tool, including but not limited to errors in educational content, loss of progress data, or any reliance placed on this material in an academic context.

Intellectual Property

The MIPS16 ISA definition, assembler specification, and emulator design referenced in this tool are based on standard computer-architecture course materials. All rights to those materials remain with their respective owners. This tool does not claim ownership of any third-party intellectual property.

Open Use

This tool is shared freely for educational use. If you find it useful, please use it responsibly and do not misrepresent it as an official course resource.

TL;DR: This is a student-made study aid. It is not affiliated with or endorsed by any institution. Use it to learn, but verify everything against official course materials.