COSIM — Simulator Manual

Computer Organization and Embedded Systems

Open the simulator

COSIM — Computer Organization Simulator: reference manual

COSIM runs the generic RISC-style assembly language of Hamacher, Vranesic, Zaky and Manjikian, Computer Organization and Embedded Systems, 6th ed. (Chapters 2–3, also used in Chapters 5, 6 and 8), with the keyboard/display/timer interfaces of Chapter 3, a five-step datapath view (Ch. 5), a 5-stage pipeline timing model (Ch. 6) and a cache model (Ch. 8). The core is plain JavaScript (ES modules, no dependencies) and runs in the browser and in Node.

  • simulator/core/ — simulator core (this manual, sections 1–9 and 12)
  • simulator/targets/ — other instruction sets (“targets”, section 14); the book’s ISA is the default target cosim
  • simulator/cli.mjs — command-line interface (section 10)
  • simulator/examples/*.asm — the book’s programs (section 11)
  • simulator/tasks/ — lab tasks, format in tasks/README.md
  • node --test simulator/test — test suite
  • simulator/cosim.bundle.js — the whole simulator (core, UI, examples, tasks) as one classic script, loaded by index.html. Because of it the simulator also works when index.html is opened directly from disk (file://), where browsers block ES modules. It is generated: after editing core/, targets/, ui/, tasks/ or examples/, run node simulator/build.mjs (uses esbuild via npx; --check tells whether it is stale). The build also regenerates targets/manifest.js (the list of targets and task files).

Things that are not in the book but added by COSIM are marked (extension).


1. Registers

Register Use
R0 always 0 (writes are ignored) — §2.4.2, so Move = Add Rd, R0, … and absolute mode = X(R0)
R1 – R26, R30 general purpose
R27 = SP stack pointer (§2.6); initialised to the top of RAM, 0x00100000 (first push stores at 0x000FFFFC)
R28 = FP frame pointer (§2.7.3)
R29 = IRA interrupt return address (Example 5.5, lecture 06) — written by the processor on every interrupt; do not use it for data when interrupts are enabled
R31 = LINK = LINK_reg link register: Call saves the return address here (§2.7, Fig. 5.17, lectures 05/06)
PC, IR program counter, instruction register

Register names are case-insensitive (r5, sp, Link_reg). The aliases follow the Nios II convention (sp = r27, fp = r28, ea = r29, ra = r31), which is the processor closest to the book’s generic ISA (Appendix B).

Processor control registers (Fig. 3.7), accessed only with MoveControl:

Name Bits
PS b0 = IE (interrupt enable)
IPS copy of PS saved at interrupt entry
IENABLE b1 KBD, b2 DISP, b3 TIM (book); b4 BTN, b5 PORT (extension)
IPENDING same bits, read-only, shows the active requests; b0 = software interrupt in progress (extension)

2. Memory

  • Byte-addressable, 32-bit words, little-endian (the byte at the word address is the least significant byte, as in the Python interpreters of lectures 02/03; Fig. 2.3b).
  • Load/Store need word-aligned addresses (multiple of 4) — otherwise a misaligned access exception stops the program. LoadByte/StoreByte work at any address.
  • RAM: 0x00000000 – 0x000FFFFF (1 MB, config memorySize). Accessing an address that is neither RAM nor an I/O register stops the program with an error.
  • Memory-mapped I/O (section 6) occupies 0x4000 – 0x406F and 0xFFFFFFF0 – 0xFFFFFFF7. Device registers take priority over RAM; the assembler warns if a program places code or data in 0x4000 – 0x40FF.
  • Uninitialised memory reads as 0. RESERVE does not initialise memory (§2.5.1).

3. Instructions

Operand order: Operation Rdst, Rsrc1, Rsrc2|#value; Load Rdst, source; Store Rsrc, destination (§2.3.4). Mnemonics are case-insensitive.

Instruction Effect Notes
Load Rd, addr Rd ← [EA] (word) EA = word address
Store Rs, addr [EA] ← [Rs]
LoadByte Rd, addr Rd ← byte [EA], upper 24 bits cleared §2.8.2
StoreByte Rs, addr byte [EA] ← [Rs]7–0
Add Rd, Ri, Rj / Add Rd, Ri, #V Rd ← [Ri] + [Rj] / V V sign-extended
Subtract Rd, Ri, Rj / #V Rd ← [Ri] − [Rj] / V V sign-extended
Multiply Rd, Ri, Rj / #V Rd ← low 32 bits of [Ri] × [Rj] / V §2.8.3, Fig. 2.35 uses #10
Divide Rk, Ri, Rj Rk ← [Rj] / [Ri] the book’s operand order (§2.8.3)! signed, truncated, remainder lost; [Ri] = 0 → divide-by-zero exception; no immediate form
And, Or, Xor Rd, Ri, Rj / #V bitwise V zero-extended (§2.8.1, §5.3.4)
Not Rd, Ri Rd ← NOT [Ri]
OrHigh Rd, Ri, #V Rd ← [Ri] OR (V × 2¹⁶) §2.9
LShiftL, LShiftR, AShiftR Rd, Ri, Rj / #n shift [Ri] by n n = 0…31; a register count uses its low 5 bits. AShiftL = LShiftL
RotateL, RotateR Rd, Ri, Rj / #n rotate (RotateLC/RotateRC need the C flag — not available, no condition codes)
Branch LOC PC ← LOC
Branch_if_[Ri]cond[Rj] LOC if [Ri] cond [Rj] then PC ← LOC cond: > = ≠ < ≥ ≤, also != <> >= <= ==; signed comparison
Branch_if_[Ri]cond0 LOC compare with 0 = Branch_if_[Ri]cond[R0]
BGT, BEQ, BNE, BLT, BGE, BLE Ri, Rj, LOC same as Branch_if Fig. 2.13 mnemonics; Branch_greater_than also accepted
Call LOC LINK ← [PC]; PC ← LOC [PC] = address after the Call
Call_register Ri LINK ← [PC]; PC ← [Ri] Fig. 5.17 (also Call_Register)
Return PC ← [LINK]
MoveControl Rd, CR / MoveControl CR, Rs copy between a GPR and PS, IPS, IENABLE, IPENDING §3.2.4
Return-from-interrupt PC ← [IRA]; PS ← [IPS] also Return_from_interrupt, RFI
Software-interrupt as an accepted interrupt: IRA ← [PC], IPS ← [PS], IE ← 0, PC ← ILOC §4.6 “Trap or Software-interrupt”; also Trap. Not maskable; IPENDING b0 = 1 until Return-from-interrupt
Nop nothing §6.4.2
Halt stop the simulation (extension)

Pseudo-instructions (expanded by the assembler, shown in the listing):

Pseudo Assembled as
Move Rd, Rs Add Rd, Rs, R0
Move Rd, #V Add Rd, R0, #V if −32768 ≤ V ≤ 32767; Or Rd, R0, #V if V ≤ 0xFFFF; otherwise OrHigh Rd, R0, #hi (+ Or Rd, Rd, #lo if lo ≠ 0) — two words, with a note (§2.9)
MoveByte Rd, #V Or Rd, R0, #V (V = 0…255; used in Fig. 3.4)
MoveImmediateAddress Rd, LOC OrHigh Rd, R0, #HI(LOC) ; Or Rd, Rd, #LO(LOC) (always two words, §2.9)
Clear Rd Add Rd, R0, R0
aliases LD ST CLR MOV ADD SUB MUL BR (Fig. 2.13); ADDI R2, R3, 5 (§2.5, # optional)

Not available (CISC, §2.10): two-operand arithmetic, memory operands in arithmetic, condition codes (Branch>0, Compare, TestBit, SetBit, …). The assembler explains this when such an instruction is used.

Addressing modes (Table 2.1)

Mode Syntax EA Encoding
Immediate #V operand = V I format, 16 bits
Register Ri —
Absolute LOC LOC LOC(R0): LOC must fit a sign-extended 16-bit value: 0…0x7FFF or 0xFFFF8000…0xFFFFFFFF (§2.4.1). Larger addresses: error with the hint to use MoveImmediateAddress
Indirect (Ri) [Ri] 0(Ri)
Index X(Ri) [Ri] + X X signed 16 bits; X may be a label (TABLE(R5)) or negative (-4(FP))
Base with index (Ri,Rj), X(Ri,Rj) [Ri] + [Rj] + X R format, X = 11-bit signed (−1024…1023)
Autoincrement (Ri)+ [Ri], then Ri += size CISC, only with the cisc option (§2.10.1)
Autodecrement -(Ri) Ri −= size, then [Ri] CISC, only with the cisc option

Branch and Call targets are PC-relative: offset = target − (address of the branch + 4), i.e. relative to the updated PC (§2.13, Fig. 5.15).

Machine encoding (Fig. 2.32 / Fig. 5.12)

R format  | 31-27 Rsrc1 | 26-22 Rsrc2 | 21-17 Rdst | 16-6 0 (or X)  | 5-0 OP |
I format  | 31-27 Rsrc  | 26-22 Rdst  | 21-6 16-bit immediate       | 5-0 OP |
J format  | 31-6 26-bit Call offset                                 | 5-0 OP |

Load: Rsrc = base/index register, Rdst = destination. Store: Rsrc = base, the “Rdst” field holds the register to store (Fig. 5.14: RB ← [R6]). Conditional branch: Ri in 31-27, Rj in 26-22, offset in the immediate field. Return holds LINK in 31-27, Return-from-interrupt holds IRA in 31-27 (Fig. 5.18). MoveControl holds the control register number (PS 0, IPS 1, IENABLE 2, IPENDING 3) in the immediate field.

OP codes (the book gives none; Add/Subtract/And/Or/Load/Store/Branch/Return equal the lecture 05 demo assembler, so BGT R2, R0, LOOP at 128 in Fig. 2.12 is 0x103FFB31):

OP Instruction OP Instruction OP Instruction
0x01 Add (R) 0x11 Add #V 0x20 Load X(Ri)
0x02 Subtract (R) 0x12 Subtract #V 0x21 Store X(Ri)
0x03 And (R) 0x13 And #V 0x22 LoadByte X(Ri)
0x04 Or (R) 0x14 Or #V 0x23 StoreByte X(Ri)
0x05 Xor (R) 0x15 Xor #V 0x24–0x27 Load/Store/LoadByte/StoreByte X(Ri,Rj) (R)
0x06 Multiply (R) 0x16 Multiply #V 0x28–0x2B … (Ri)+
0x07 Divide (R) 0x18 LShiftL #n 0x2C–0x2F … −(Ri)
0x08 LShiftL (R) 0x19 LShiftR #n 0x30 Branch
0x09 LShiftR (R) 0x1A AShiftR #n 0x31–0x36 Branch_if > = ≠ < ≥ ≤
0x0A AShiftR (R) 0x1B RotateL #n 0x37 Call (J)
0x0B RotateL (R) 0x1C RotateR #n 0x38 Return
0x0C RotateR (R) 0x1D OrHigh 0x39 Return-from-interrupt
0x0D Not (R) 0x3A / 0x3B MoveControl Rd,CR / CR,Rs
0x0E Call_register (R) 0x3C Software-interrupt
0x00 illegal 0x3E / 0x3F Nop / Halt

The processor executes the encoded words (it decodes what is in memory), so a program that overwrites its own code or jumps into data behaves like real hardware.

4. Assembly language

LABEL:  Operation   operand, operand, …    ; comment
NAME    EQU         value
  • Labels end with : and may stand alone on a line (they then label the next statement). Names: letters, digits, _, .; case-sensitive (the assembler suggests the right spelling when only the case differs). Register names cannot be labels.
  • Comments: ; or // to the end of the line. Text after the last operand without a comment character is also treated as a comment, so programs copied from the book (which has a separate comment column) assemble; a note reports how many lines did this.
  • Characters copied from the PDF (−, –, ’, “) are normalised.
  • Numbers (§2.5.3): 93, 0x5D, $5D, %01011101, 0b01011101, 'A', '\n' (escapes \n \r \t \0 \\ \' \" \xHH), negative -5.
  • Expressions: + - * /, unary - and ~, parentheses, labels, HI(e) / LO(e) (high/low 16 bits) (extension), e.g. LIST+4, #HI(TABLE).

Directives (§2.5.1)

Directive Meaning
ORIGIN addr following code/data start at addr (before the first ORIGIN: address 0)
NAME EQU value define a symbol (also NAME: EQU value); forward references allowed
DATAWORD v1, v2, … 32-bit words (values may be labels)
DATABYTE v1, "text", … bytes; strings allowed (strings are an extension) (Fig. 3.12)
DATASTRING "text" bytes of the string, no automatic terminator — write "text\0" or "text\r" (extension)
RESERVE n n bytes, not initialised
ALIGN n align the location counter to a multiple of n (extension)
END [START] end of the source; the optional operand is the start address

Instructions and DATAWORD are automatically aligned to a word boundary.

Start address: the END operand, else the label START, else the first instruction.

Predefined symbols (extension): all device register names of section 6 (KBD_DATA, DISP_STATUS, TIM_INIT, SEVEN, LEDS, …) and CR = 0x0D, LF = 0x0A. A program may redefine them (Fig. 3.18 does SEVEN EQU 0x4030).

Error messages give the line, what is wrong and a hint, for example:

Error on line 2: Add needs three operands (destination, source 1, source 2) but has two.
      2 |        Add R3, R5
   Hint: RISC-style instructions are three-address instructions (§2.3.4). Did you mean Add R3, R3, R5?

All errors of a program are reported at once. Warnings (code in the I/O area, text after END) and notes (Move expanded to two words) do not stop assembly.

Run settings in the source (extension): comment lines of the form ; @cosim key=value … set machine options for the CLI and the UI, e.g.

; @cosim input="Hello\n" timerTicksPerInstruction=1000000
; @cosim cache="size=32,block=4,assoc=1" pipeline="forwarding=off" maxSteps=50000

5. Program execution

  • step executes one instruction; run executes until the program stops.
  • The program stops normally when it executes Halt, when execution runs past the last instruction (the book’s programs end with “next instruction”), or at X: Branch X while interrupts are disabled.
  • It stops with an error on: illegal instruction, misaligned word access, access to an unmapped address, divide by zero, a jump to an address that holds no assembled instruction (e.g. a wrong Return address), an interrupt without an ISR at ILOC. The message names the source line and gives a hint.
  • It pauses at breakpoints and when it is waiting for keyboard input (it has polled KBD_STATUS 200 times with KIN = 0 and no queued characters, without storing anything in between); it continues after input.
  • The CLI stops after 1 000 000 instructions (endless loop?) — option --max-steps.
  • Cycle count: 1 per instruction; with a cache, plus the miss stall cycles; with the pipeline model, the pipeline’s cycle count.
  • Step back: any number of instructions can be undone (checkpoints every 500 instructions plus deterministic re-execution, including typed keys and switch changes).

6. I/O devices and memory map

All device registers can be accessed with LoadByte/StoreByte and with Load/Store. A byte access reads/writes one byte of the 32-bit register (little-endian).

Address Name Bits Source
0x4000 KBD_DATA received character (reading clears KIN) Fig. 3.3a
0x4004 KBD_STATUS b1 KIN (character available), b0 KIRQ (= KIN ∧ KIE) Fig. 3.3a
0x4008 KBD_CONT b1 KIE (interrupt enable) Fig. 3.3a
0x4010 DISP_DATA character to display (writing clears DOUT) Fig. 3.3b
0x4014 DISP_STATUS b2 DOUT (ready), b0 DIRQ (= DOUT ∧ DIE) Fig. 3.3b
0x4018 DISP_CONT b2 DIE Fig. 3.3b
0x4020 TIM_STATUS b2 TON (running), b1 ZERO, b0 TIRQ; reading clears ZERO and TIRQ Fig. 3.14
0x4024 TIM_CONT b3 UP, b2 FREE, b1 RUN, b0 TIE Fig. 3.14
0x4028 TIM_INIT initial count (writing also loads the counter) Fig. 3.14
0x402C TIM_COUNT current count (read/write) Fig. 3.14
0x4030 SEVEN = HEX0 7-segment display: a = b6, b = b5, … g = b0 (Fig. 3.17), b7 = decimal point (ext.) Example 3.6
0x4031–0x4035 HEX1–HEX5 five more 7-segment displays (a word store to 0x4030 sets HEX0–HEX3) (extension)
0x4040 LEDS 10 LEDs (b9–b0), read/write (extension)
0x4050 SWITCHES 10 slide switches (b9–b0), read only (extension)
0x4060 BTN_DATA 4 push buttons, 1 = pressed, read only (extension)
0x4064 BTN_EDGE edge capture: bit set when a button is pressed; write 1 to clear a bit (extension)
0x4068 BTN_CONT interrupt mask, request = EDGE ∧ CONT → IPENDING b4 (extension)
0xFFFFFFF0–F7 PAIN PAOUT PADIR PBIN PBOUT PBDIR PSTAT PCONT Ch. 10 parallel port, byte registers, PADIR/PBDIR 1 = output, direct pin mode only (PSTAT reads 0, no handshake) Fig. 10.6 (minimal)

The bit positions of Fig. 3.3 were checked against the figure in the PDF (KIN and KIE in b1, DOUT and DIE in b2, KIRQ/DIRQ in b0) and agree with the programs (And R4, R4, #2 tests KIN, #4 tests DOUT, Fig. 3.4).

Keyboard: characters typed (UI input box, CLI --input, typeInput()) are queued. When KIN = 0 the next character is loaded into KBD_DATA and KIN is set, keyboardDelay instructions (default 0) after the previous one was read. The Enter key / "\n" is delivered as CR (0x0D), which the book’s programs test for.

Display: writing DISP_DATA appends the character to the terminal and clears DOUT; DOUT returns to 1 after displayDelay instructions (default 20), so polling loops are meaningful. A character written while DOUT = 0 is lost and a warning is shown (config displayStrict: false accepts it). CR (and LF) start a new line, backspace deletes.

Timer (Example 3.5): the counter changes by timerTicksPerInstruction (default 1) per executed instruction — the book’s 100-MHz clock would need 10⁹ instructions for 10 s, so examples set e.g. ; @cosim timerTicksPerInstruction=1000000. With RUN = 1 it counts down (UP = 0) or up (UP = 1). When it reaches 0 it sets ZERO, and TIRQ if TIE = 1; with FREE = 1 it reloads TIM_INIT and continues, otherwise it stops (TON = RUN = 0). Starting a down-counter whose count is 0 reloads TIM_INIT first.

7. Interrupts and exceptions (§3.2, Figs. 3.7–3.10, Example 5.5)

  1. A device raises a request (KIRQ, DIRQ, TIRQ, button edge); the request appears in IPENDING.
  2. At the end of each instruction, if PS.IE = 1 and (IPENDING ∧ IENABLE) ≠ 0 the processor
    • saves the return address (the address of the next instruction) in IRA = R29,
    • copies PS to IPS and clears PS.IE,
    • loads PC ← ILOC.
  3. The interrupt handler at ILOC finds the device in IPENDING (Fig. 3.9) and services it; reading KBD_DATA / writing DISP_DATA / reading TIM_STATUS removes the request.
  4. Return-from-interrupt does PC ← [IRA], PS ← [IPS].

ILOC is the address of the label ILOC if the program defines one (all book examples do), otherwise 0x20 (config iloc overrides). There are no vectored interrupts: one handler polls IPENDING, as in Fig. 3.9. For nested interrupts the handler must save IRA and IPS (MoveControl Ri, IPS) on the stack before setting IE (§3.2.2). The handler must save every register it uses, including LINK if it calls subroutines (Fig. 3.9).

Exceptions (§3.2.6: illegal OP code, divide by zero, misaligned access, bad address) stop the simulation with a message instead of jumping to an exception handler.

8. Datapath view (Ch. 5)

For each executed instruction COSIM shows the five steps of Fig. 5.4 with the book’s register transfers (Figs. 5.11, 5.13–5.17) and the contents of PC, IR, RA, RB, RZ, RM, RY and PC-Temp after each step:

Load R6, 1000(R9)
  1 Fetch    Memory address ← [PC], Read memory, IR ← Memory data, PC ← [PC] + 4
  2 Decode   Decode instruction, RA ← [R9]
  3 Compute  RZ ← [RA] + Immediate value X
  4 Memory   Memory address ← [RZ], Read memory, RY ← Memory data
  5 Write    R6 ← [RY]

As in lecture 06: both register-file ports are always read in step 2 (RA ← [IR31-27], RB ← [IR26-22]), RM ← [RB] in step 3, and inter-stage registers keep their values when a stage does nothing. Multiplexer settings (MuxB, MuxY, MuxC, MuxPC, MuxINC, Extend) are included. Base-with-index stores need a third register read (noted in the text).

9. Pipeline model (Ch. 6) and cache model (Ch. 8)

Pipeline

A timing model over the dynamic instruction trace (the functional result never depends on it). Stages F D C M W; an instruction enters a stage when the previous one has left it.

Option Values (default first) Book
forwarding true / false §6.4.1
branchStage 'decode' (penalty 1, Fig. 6.10) / 'compute' (penalty 2, Fig. 6.9) §6.6.1
delaySlot false / true — must match the CPU option delaySlot (the instruction after a branch is then always executed; Call saves PC + 8) §6.6.2
prediction 'not-taken', 'backward-taken' (static, decided in Decode), '1bit', '2bit' (Fig. 6.12a/b, initial state LNT) §6.6.3
btb true — dynamic predictors predict in the Fetch stage with a branch target buffer (correct prediction = no penalty) §6.6.4
memoryStalls true — cache-miss cycles stall F/M (Fig. 6.7) §6.5
tableLimit 200 — rows kept for the timing diagram

Timing rules: without forwarding a dependent instruction reads its operands in Decode in the cycle after the producer’s Write (enters Compute at W + 2, Fig. 6.3); with forwarding it enters Compute after the producer’s Compute (from RZ, Fig. 6.4) or one cycle later after a Load (from RY, Fig. 6.8). A branch decided in Decode that needs a register produced by the previous instruction waits for it (forwarding into the comparator, as in lecture 07, Exercise 7.2). Unconditional branches, Call and Return are treated like taken branches. An accepted interrupt flushes the pipeline (approximation).

Output: total cycles, CPI, stall cycles by cause (data, load-use, branch, memory, fetch), branch statistics, and a timing table (F D C M W; lower case f d c m = stalled; X = discarded wrong-path fetch). It reproduces Figs. 6.3, 6.4, 6.6, 6.8 and the 8 cycles per iteration of the list-sum loop (lecture 07).

Cache

Option Default Notes
size 1024 bytes, power of 2
blockSize 16 bytes, power of 2, ≥ 4
assoc 1 1 = direct mapped, n = n-way, 'full' = fully associative
replacement 'lru' 'lru', 'fifo', 'random' (seeded, deterministic)
writePolicy 'write-back' or 'write-through'
writeAllocate true for write-back, false for write-through
hitTime, missPenalty 1, 10 cycles (AMAT and stalls)

Address split: tag | set index | byte offset. Statistics: accesses, reads, writes, hits, misses, hit rate, write-backs, AMAT = hit time + miss rate × miss penalty. Stall cycles per access: miss penalty on a miss, plus one more miss penalty for writing back a dirty victim; write-through writes go to a write buffer (no stall). Data are always kept up to date in memory (the cache view reads the block’s words from memory). I/O registers are not cached. A separate instruction cache (icache) is optional.

The §8.6.3 example (Figs. 8.19–8.23) is reproduced with a 32-byte cache of 4-byte blocks (examples/sec8-6-3-cache-example.asm): read hits direct 2, associative 8, 4-way set-associative 4.

10. Command-line interface

node simulator/cli.mjs prog.asm [options]
Option
--input "text" keyboard input (\n → CR, escapes \r \t \xHH)
--max-steps N instruction limit (default 1 000 000)
--regs all registers (default: non-zero ones)
--mem LABEL[:count[:b]] memory dump in words, or bytes with :b; LABEL or address; repeatable
--listing, --symbols assembly listing (address, machine code, source), symbol table
--trace [N] the first N executed instructions with register/memory changes
--datapath [N] five-step view of the first N instructions
--pipeline [spec], --pipeline-table [N] e.g. forwarding=on,branch=decode,predict=2bit,delay=off,btb=on
--cache [spec], --icache [spec] e.g. size=1024,block=16,assoc=2,policy=lru,write=wb,penalty=10 (size=1k ok; assoc=full; write=wt)
--cisc, --delay-slot CISC addressing modes; delayed branching
--switches N, --buttons 0,2 lab-board inputs
--timer-ticks N, --display-delay N, --keyboard-delay N device timing
--task ID [--tasks-dir DIR] check the program against a lab task
--json, --quiet machine-readable summary; only output and errors
--target ID, --list-targets simulate another instruction set (section 14) / list them
--set ID=VALUE other targets: board input before the run (switch/button 1/0, slider value); repeatable
prog.hex Intel HEX file, for targets that load binaries (--target …)

Exit codes: 0 finished (or task passed), 1 runtime error (or task failed), 2 assembly error / bad usage, 3 instruction limit reached.

11. Example programs (simulator/examples/)

examples/index.js exports examples = [{id, file, chapter, title}] (ids such as fig2-8 for index.html?example=fig2-8).

File Book Result
fig2-08-listsum.asm Fig. 2.8/2.12 list sum SUM = 150
fig2-11-marks.asm Figs. 2.10/2.11 index mode SUM1..3
fig2-13-mnemonics.asm Fig. 2.13 (LD/CLR/MOV/BGT) SUM = 24
fig2-17-subroutine-regs.asm Fig. 2.17 SUM = 100
fig2-18-subroutine-stack.asm Figs. 2.18/2.19 SUM = 15
fig2-21-nested.asm Figs. 2.21/2.22 nested frames (book addresses 2000/2100/3000) RESULT = 17
fig2-24-bcd-pack.asm Fig. 2.24 PACKED = 0x47
fig2-27-dot-product.asm Fig. 2.27 DOTPROD = 12
fig2-30-string-search.asm Figs. 2.29/2.30 RESULT = T + 4
fig2-33-string-length.asm Example 2.1 LENGTH = 12
fig2-34-smallest.asm Example 2.2 (ORIGIN 400/1000) SMALL = 1
fig2-35-decimal-to-binary.asm Example 2.3 BINARY = 1987
fig2-36-add-columns.asm Example 2.4 column 2 = 4, 12, 20
fig3-04-echo-polling.asm Fig. 3.4 polling echo echo of the input line
fig3-08-keyboard-interrupt.asm Fig. 3.8 interrupt-driven input, polled echo
fig3-09-interrupt-handler.asm Fig. 3.9 (ISRs completed) text printed by DIRQ, line read by KIRQ
fig3-12-hex-display.asm Fig. 3.12 2F80A1C3
fig3-15-timer-interrupt.asm Fig. 3.15 (DISPLAY completed) 1 2 3
fig3-18-seven-segment.asm Fig. 3.18 HEX0 shows 7
fig6-03-data-dependency.asm, fig6-08-load-use.asm Figs. 6.3/6.4, 6.8 9 / 8 cycles
ex6-loop-scheduling.asm lecture 07, Exercise 7.2 8 cycles / iteration
sec8-6-3-cache-example.asm §8.6.3, Fig. 8.20 read hits 2 / 8 / 4
ex-factorial-recursive.asm, ex-switches-leds.asm, ex-timer-polling.asm, ex-cisc-autoincrement.asm COSIM examples

Where the PDF text lost a symbol (e.g. Branch_if_[R8]=[R9] NOMATCH in Fig. 2.30 and Branch_if_[R3]=[R2] RTRN "Return if not CR" in Fig. 3.8, which must be ≠), the examples use the condition the comments require, with a note in the file header. The CISC programs (Figs. 2.26, 2.28, 2.31, 3.5, 3.10, 3.11, 3.13, 3.16, 3.19) use condition codes and memory operands and are not runnable.

12. JavaScript API

import { createMachine, assemble, formatMessage } from './core/index.js';

const m = createMachine({ displayDelay: 10, pipeline: { forwarding: true } });
const asm = m.load(sourceText);              // assemble + load + reset
if (!asm.ok) showErrors(asm.errors);         // [{line, message, hint, severity, source}]
m.typeInput('Hello\n');
function tick() {                            // non-blocking run loop for a UI
  const r = m.run(5000);                     // at most 5000 instructions
  render();
  if (r.status === 'running') requestAnimationFrame(tick);
}
tick();

Module core/index.js (facade)

Export
createMachine(config) / new Machine(config) a simulated computer (below)
assemble(src, {cisc, origin, predefined, symbols}) assembly result (below)
formatMessage(msg), formatListing(asm) text for errors and the listing
sourceSettings(src) {config, input, maxSteps} from ; @cosim lines
runProgram(src, {config, input, maxSteps}) assemble, load and run → {machine, asm, result}
datapathSteps(record), previewNext(machine), formatDatapath(d) Ch. 5 view
PipelineModel, simulatePipeline(trace, opts), formatTimingTable(rows), rowCells(row), parsePipelineSpec(s) Ch. 6
Cache, simulateCache(addrs, cfg), parseCacheSpec(s) Ch. 8
checkTask(task, src), runTest(task, test, src), resolveAddress(spec, asm), findTask(lists, id) lab checker
isa instruction set: decode(word), disassemble(word, pc, labelOf), encR/encI/encJ, parseRegister, regDisplayName, REG, CONTROL_REGS, INSTRUCTION_REFERENCE (table for help pages), OPCODES
IO_SYMBOLS, sevenSegToChar(bits), DEFAULT_CONFIG, VERSION

Assembly result

{ ok, errors, warnings, image, symbols, symbolInfo, predefined, listing, addrToLine, lineToAddr, codeAddrs, statements, entry, iloc, lines }

  • errors / warnings: {line, message, hint, severity: 'error'|'warning'|'info', source} (sorted by line).
  • listing: per source line {line, addr, words: [machine words], bytes: [data bytes], source, kind: 'code'|'data'|'dir'|'equ'|'label'|'end', size}.
  • symbols: {LOOP: 112, …} (labels and EQU); symbolInfo[name] = {value, line, type}.
  • addrToLine (Map address → line, for highlighting the current line), lineToAddr (Map line → address, for breakpoints in the gutter), codeAddrs (Set of instruction addresses).

Machine configuration (DEFAULT_CONFIG)

memorySize (0x100000), stackTop (= memorySize), cisc (false), delaySlot (false), iloc (null = label ILOC or 0x20), strictFetch (true), selfLoopHalts (true), stopOnInputWait (true), inputWaitPolls (200), keyboardDelay (0), displayDelay (20), displayStrict (true), newlineAsCR (true), timerTicksPerInstruction (1), traceLimit (10000), checkpointInterval (500), maxCheckpoints (400), cache (null | cache config), icache (null), pipeline (null | pipeline options).

Machine methods and properties

Method
load(srcOrAsm) → asm assemble (if a string), load the image, reset; check asm.ok
reset() reload the image, clear registers/devices/statistics (switch positions are kept)
step() → {status, record, message} one instruction (plus interrupt entry)
run(maxSteps = 1e6, {ignoreBreakpoints}) → {status, steps, message} run a chunk; status 'running' = limit reached, call again
stepBack(n = 1) → bool, canStepBack undo instructions
status, statusMessage, canRun 'empty', 'ready', 'running', 'breakpoint', 'waiting', 'halted', 'error'; errorInfo = {kind, pc, line, message, hint}
pc, ir, steps, cycles, getStats() counters; getStats() → {instructions, cycles, cpi, interrupts, dcache, icache, pipeline}
getRegister(r), getRegisters(), setRegister(r, v) r = 0…31, 'R5', 'SP', 'PC', 'PS' … (unsigned values)
getControl(name), getControls(), setControl(name, v) PS, IPS, IENABLE, IPENDING
readMemory(addr, 1|4), dumpMemory(addr, len) debugger reads, no device side effects (null if unmapped)
writeMemory(addr, value, 1|4) debugger write
symbol(name), lineOf(addr), asm symbol lookup, source line of an address, the loaded assembly result
addBreakpoint(where), removeBreakpoint, toggleBreakpoint, clearBreakpoints(), breakpoints where = address, label, or {line}
getTrace(n) last n trace records (below)
typeInput(text) keyboard input
setSwitch(i, on), setSwitches(mask), pressButton(i), releaseButton(i), setPortPins('A'|'B', v) lab board
getDeviceState() {terminal, keyboard:{DATA,KIN,KIE,KIRQ,queue,queueLength}, display:{DOUT,DIE,DIRQ,text,bytes,lost}, timer:{COUNT,INIT,TON,ZERO,TIRQ,TIE,RUN,FREE,UP,events}, sevenseg:{digits:[6], chars}, leds:{value,bits}, switches:{value,bits}, buttons:{pressed,edge,mask,IRQ}, parallel:{…}, ipending}
output terminal text
messages runtime warnings/errors {level, text, step, pc, line} (e.g. lost display character)
snapshot(), restore(s) complete deep-copied state
dcache, icache, pipeline model objects or null; setModels({cache, icache, pipeline}) replaces them and re-executes up to the current instruction so statistics are complete
on(event, fn) → unsubscribe events 'step' (record), 'run' ({status, steps}), 'message', 'change', 'reset'

All external inputs (typed keys, switches, buttons, register/memory edits) are recorded with the instruction count, so step back and setModels replay them exactly.

Trace record

{ step, pc, word, line, cls, name,
  text,            // disassembly, e.g. "Add R4, R0, #208"
  src,             // source statement, e.g. "Move R4, #NUM1"
  reads: [regs], writes: [{r, old, v}],
  mem: {addr, size, value, write, io} | null,
  branch: {conditional, taken, target, call?, ret?, rfi?, swi?} | null,
  ctrl: {reg, old, v} | null,         // control-register write
  interrupt: {pending, returnAddress, iloc, software?} | null,  // accepted after this instruction
  dp: {RA, RB, RZ, RM, RY, PCTemp}, dpPrev: {...},  // datapath registers after / before
  memStall, fetchStall, nextPc, fault? }

Datapath

datapathSteps(record) → {text, machineText, pc, word, cls, fields, signals, steps: [{n, name, rtn, regs: {PC, IR, RA, RB, RZ, RM, RY, PCTemp}, mem?, write?, immediate?}]}. previewNext(machine) gives the same for the instruction at PC without changing the state.

Pipeline

m.pipeline.stats() → {cycles, instructions, cpi, stalls: {data, loadUse, branch, memory, fetch, interrupt}, branches: {total, taken, mispredicted, penaltyCycles}, options}; m.pipeline.table() → rows {n, step, pc, text, F, D, C, M, Mend, W, stall, penalty, prediction, taken} or {discarded: true, F}; rowCells(row) → {cycle: 'F'|'D'|'d'|…}.

Cache

m.dcache.stats() → {accesses, reads, writes, hits, misses, readMisses, writeMisses, writeBacks, memoryWrites, stallCycles, hitRate, missRate, amat}; geometry() → {size, blockSize, blocks, sets, ways, tagBits, indexBits, offsetBits, mapping, …}; contents(addr => m.readMemory(addr)) → sets[set][way] = {valid, dirty, tag, blockAddr, lruRank, data: [words]}; split(addr) → {tag, index, offset}; log = last 64 accesses.

Lab tasks

Task files tasks/example.js, tasks/lab1.js … lab8.js export tasks = [{id, lab, title, description, template, solution?, config, maxSteps, tests, hints}]. Each test has setup (memory by label, registers, input, switches, buttons), optional timed actions, and expect (memory, registers, output exact/regex/contains, leds, sevenSeg, halted, maxInstructions, cacheHitRate, pipelineCycles, custom check). Full description: simulator/tasks/README.md. checkTask(task, source) → {id, title, pass, passed, total, assembled, errors, tests: [{name, pass, messages, steps, status, output}]}.

13. Decisions where the book leaves a choice

Topic Decision
R0 hard-wired 0 (§2.4.2)
SP, FP, IRA, LINK R27, R28, R29, R31 (Nios II convention; LINK = R31 as in lectures 05/06)
Endianness little-endian (lectures 02/03)
Return address of interrupts general-purpose register IRA (Example 5.5, lecture 06), not the stack
ILOC label ILOC, else 0x20; no vectored interrupts
KIRQ / DIRQ bit b0 (Fig. 3.3 in the PDF)
Immediates 16 bits; sign-extended for Add/Subtract/Multiply and addresses, zero-extended for And/Or/Xor (§2.8.1, §5.3.4)
Divide Rk ← [Rj] / [Ri] as written in §2.8.3, remainder discarded
Branch offsets 16-bit, relative to PC + 4; Call offset 26-bit, relative to PC + 4
OP codes own table (section 3), consistent with lecture 05 where it defines them
Unconditional Branch I format with Ri = Rj = R0 (as lecture 05)
(Ri,Rj) / X(Ri,Rj) R format with an 11-bit X (Table 2.1 lists the mode for RISC)
Autoincrement/decrement CISC only, behind the cisc option
Condition codes, RotateLC/RC not implemented (CISC)
End of a book program (“next instruction”) running past the last instruction ends the program normally

14. Targets (other instruction sets)

COSIM can simulate other instruction sets and microcontrollers besides the book’s ISA. Each one is a target in simulator/targets/<dir>/; the book’s ISA is the target cosim and is the default, so every ?task= / ?example= link, task file and CLI command above works unchanged.

Target
cosim the book’s RISC-style ISA (sections 1–13), with the Datapath, Pipeline and Cache tabs
toy TOY, a nine-instruction 8-bit accumulator machine — the documented template for adding an ISA (targets/_template/README.md)
avr-atmega328p, avr-attiny85, avr-atmega324pb Microchip AVR (Arduino Uno / ATtiny85 / ATmega324PB Xplained Pro), when targets/avr/ is installed (targets/avr/README.md)

node simulator/cli.mjs --list-targets prints the installed targets.

Choosing a target. In the browser: the menu at the top left, or index.html?target=<id> (remembered for the next visit); ?task=<id> switches to the task’s target. CLI: --target <id>, else the task’s target (--task), else a source line ; @cosim target=<id>, else cosim.

What changes with the target. The program menu lists that target’s examples and tasks; the editor highlights its syntax; the Registers tab shows its register groups and flags (click a flag to toggle it); the Memory tab has a memory-space selector (program memory is shown one word per row with its disassembly); the I/O tab shows the target’s board (LEDs, push buttons that are held while the mouse button is down, switches, sliders, 7-segment displays, a terminal, a pin table) and a tree of the device state; the status bar shows the simulated time when the target has a clock. Datapath, Pipeline and Cache exist only for cosim. Targets that load binaries show Load .hex…: the editor then shows the disassembly, which can be stepped and breakpointed like a source program. Help (?) contains the target’s instruction reference and a link to its documentation.

Lab tasks may name a target (target: 'avr-atmega328p'); the checker then uses that target’s machine and the generic expectations (registers, flags, memory, terminal output, board state, cycle limits, cycle-timed input actions) — see tasks/README.md.

Adding an ISA. The interface every target implements (descriptor, assembly result, machine, UI description, checker, CLI, build) is specified in targets/target-api.md. Copy targets/_template/, change the descriptor, implement assembler and machine, then run node simulator/build.mjs and node --test simulator/test — the conformance suite (test/conformance.mjs) checks every registered target automatically. JavaScript use:

import { getTarget, listTargets } from './targets/index.js';
import { checkTask } from './targets/checker.js';
const t = getTarget('toy');
const m = t.createMachine(t.defaultConfig);
m.load(sourceText);          // → AsmResult, as for cosim
m.run(100000);
m.getRegisterList(); m.getFlags(); m.getBoardState(); m.output;