Compare commits

...
7 Commits
38 changed files with 726 additions and 1 deletions
+4
View File
@@ -1,3 +1,7 @@
html { html {
scroll-padding-top: 5rem; scroll-padding-top: 5rem;
} }
h3.hx\:sr-only + * {
margin-top: 0 !important;
}
+118 -1
View File
@@ -1,5 +1,122 @@
+++ +++
title = "LC-2" title = "LC-2"
weight = 100 weight = 100
draft = true
+++ +++
LC-2 is the first architecture in the Little Computer family that looks like a
real ISA. Built as a successor to the minimalist LC-1,[^lc1-successor] it
introduced eight general-purpose registers and a three-state condition code
system, the foundation that every subsequent architecture in the family would
inherit.[^lc-family-evolution]
[^lc1-successor]: {{< cite-ics edition="1" chapter="1. Welcome Aboard" page="2" >}}
[^lc-family-evolution]: {{< cite-talk
author="Patt, Yale N."
title="LC-3, x86, or MIPS: The First ISA for Students to Study"
type="Keynote"
event="Workshop on Computer Architecture Education"
location="San Diego, CA"
date="June 9, 2007"
url="https://www.csc2.ncsu.edu/faculty/efg/wcae/ISCA2007/FinalProgram.html"
format="PowerPoint presentation"
accessed="June 8, 2026"
url-archived="https://web.archive.org/web/20250129103001/https://www.csc2.ncsu.edu/faculty/efg/wcae/ISCA2007/FinalProgram.html"
url-archived-date="January 29, 2025"
>}}
LC-2 operates on a 16-bit data bus and a 16-bit address bus, giving a linear
address space of 65,536 word-sized locations, 128 KiB of addressable memory in
total. Every instruction is exactly 16 bits wide, with the top 4 bits reserved
for the opcode. This fixed encoding means that the opcode, operands, and any
immediate values must all fit within those 16 bits, a constraint that shapes the
design of every instruction in the set.[^lc2-overview]
[^lc2-overview]: {{< cite-ics edition="1" chapter="Appendix A: The LC-2 ISA" page="429" >}}
## Registers
LC-2 has a small but complete set of registers. All registers are 16 bits wide,
matching the data bus width of the architecture.
### General-Purpose Registers
LC-2 provides eight general-purpose registers, named R0 through R7. They are
symmetric: no register has a special hardware role, and any of them can be used
as a source or destination in any instruction that operates on
registers.[^lc2-overview] That said, two registers have a conventional role: R6
is typically used as the stack pointer,[^lc2-stack] [^lc2-rti] and R7 is used
by some instructions to store the return address.[^lc2-jsr-jsrr] [^lc2-ret]
[^lc2-trap] See the [instructions page](instructions/) for details.
[^lc2-stack]: {{< cite-ics edition="1" chapter="10.1.3 Implementation in Memory" page="197" page-end="200" >}}
[^lc2-rti]: {{< cite-ics edition="1" chapter="Appendix A.3 The Instruction Set" page="444" >}}
[^lc2-jsr-jsrr]: {{< cite-ics edition="1" chapter="Appendix A.3 The Instruction Set" page="436" page-end="437" >}}
[^lc2-ret]: {{< cite-ics edition="1" chapter="Appendix A.3 The Instruction Set" page="443" >}}
[^lc2-trap]: {{< cite-ics edition="1" chapter="Appendix A.3 The Instruction Set" page="448" >}}
### Special-Purpose Registers
Beyond the general-purpose registers, LC-2 has some special-purpose registers
that control the execution of the processor. None of these registers are
directly accessible from assembly, with the exception of the CC, which is
implicitly read by conditional branch instructions.
* The **Program Counter** (**PC**) holds the address of the next instruction to
be fetched from memory. It is incremented by 1 after each fetch, before the
instruction is executed, so that by the time the instruction runs, the PC
already points to the following one.[^lc2-fetch]
[^lc2-fetch]: {{< cite-ics edition="1" chapter="4.2.2 The Instruction Cycle" page="82" page-end="83" >}}
* The **Instruction Register** (**IR**) holds the instruction currently being
executed. After the PC is used to fetch an instruction from memory, the
instruction is loaded into the IR, where it remains for the duration of the
decode and execute phases.[^lc2-fetch]
* The **Memory Address Register** (**MAR**) holds the address of the memory
location to be accessed. Before any memory operation, the address is loaded
into the MAR, which then drives the address bus during the read or write
cycle.[^lc2-memory]
[^lc2-memory]: {{< cite-ics edition="1" chapter="4.1.1 Memory" page="75" page-end="77" >}}
* The **Memory Data Register** (**MDR**) holds the data being transferred to or
from memory. On a read, the MDR receives the value fetched from the location
addressed by the MAR. On a write, the MDR holds the value to be stored before
it is placed onto the data bus.[^lc2-memory]
* The **Condition Code** register (**CC**) is a 3-bit register that tracks the
sign of the last value written to any general-purpose register. It has three
mutually exclusive states: N (negative), Z (zero), and P (positive). Exactly
one of the three bits is set at any given time.[^lc2-overview]
Not all instructions update the CC. Only instructions that write a value to a
general-purpose register will modify it. As an example, an `ADD` instruction
that stores its result in R0 will update the CC based on the sign of that
result, while a `STR` instruction that writes to memory will
not.[^lc2-condition-codes]
[^lc2-condition-codes]: {{< cite-ics edition="1" chapter="5.1.7 Condition Codes" page="95" >}}
## Startup Behavior
The ISA does not specify a default starting address or reset vector. Where the
PC is initialized when the processor powers on or resets is left entirely to
the implementation. {{< citation-needed >}}
## Interrupt Support
LC-2 provides support for vectored interrupts. When an interrupt is serviced,
the processor pushes the current PC and CC onto the stack, then reads an 8-bit
value from the interrupting device, called the interrupt vector (`INTV`).
`INTV` is zero-extended to 16 bits and used as a memory address into the
interrupt table: the value stored at that address is loaded into the PC,
transferring control to the interrupt handler.{{< citation-needed >}}
The `RTI` (Return from Interrupt) instruction reverses this process, popping
the PC and CC from the stack to resume the interrupted
program.{{< citation-needed >}}
Beyond this, the ISA does not specify the interrupt protocol in further detail:
the bus signaling and acknowledgment mechanism used to deliver `INTV` are left
to the implementation.
Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

+390
View File
@@ -0,0 +1,390 @@
+++
title = "Instructions"
weight = 120
+++
{{% instruction
mnemonic="ADD"
short="Addition"
formats=`ADD DR, SR1, SR2
ADD DR, SR1, imm5`
encoding="../imgs/add.drawio.png"
encoding-dark="../imgs/add-dark.drawio.png"
operation=`if (bit[5] == 0) {
DR = SR1 + SR2;
} else {
DR = SR1 + SEXT(imm5);
}
setcc(DR);`
examples=`ADD R2, R3, R4 ; R2 ← R3 + R4
ADD R2, R3, #7 ; R2 ← R3 + 7`
%}}
If bit [5] is 0, the second-source operand is obtained from SR2. If bit [5] is
1, the second-source operand is obtained by sign-extending the imm5 field to 16
bits. In both cases, the second source operand is added to the contents of SR1,
and the result stored in DR. The condition codes are set, based on whether the
result is negative, zero, or positive.
{{% /instruction %}}
{{% instruction
mnemonic="AND"
short="Bitwise logical AND"
formats=`AND DR, SR1, SR2
AND DR, SR1, imm5`
encoding="../imgs/and.drawio.png"
encoding-dark="../imgs/and-dark.drawio.png"
operation=`if (bit[5] == 0) {
DR = SR1 & SR2;
} else {
DR = SR1 & SEXT(imm5);
}
setcc(DR);`
examples=`AND R2, R3, R4 ; R2 ← R3 AND R4
AND R2, R3, #7 ; R2 ← R3 AND 7`
%}}
If bit [5] is 0, the second-source operand is obtained from SR2. If bit [5] is
1, the second-source operand is obtained by sign-extending the imm5 field to 16
bits. In either case, the second-source operand and the contents of SR1 are
bitwise ANDed, and the result stored in DR. The condition codes are set, based
on whether the binary value produced, taken as a 2's complement integer, is
negative, zero, or positive.
{{% /instruction %}}
{{% instruction
mnemonic="BR"
short="Conditional Branch"
formats=`BR LABEL
BRn LABEL
BRz LABEL
BRp LABEL
BRnz LABEL
BRnp LABEL
BRzp LABEL
BRnzp LABEL`
encoding="../imgs/br.drawio.png"
encoding-dark="../imgs/br-dark.drawio.png"
operation=`if ((n && N) || (z && Z) || (p && P)) {
PC = PC[15:9] @ pgoffset9;
}`
examples=`BRzp LOOP ; Branch to LOOP if the last result was zero or positive.`
%}}
Test the condition codes specified by the state of bits [11:9]. If bit [11] is
set, test N; if bit [11] is clear, do not test N. If bit [10] is set, test Z,
etc. If any of the condition codes tested is set, branch to the location
specified by pgoffset9 on the same page as the branch instruction,
{{% /instruction %}}
{{% instruction
mnemonic=`JMP / JSR`
short=`Jump / Jump to Subroutine`
formats=`JMP LABEL (L = 0)
JSR LABEL (L = 1)`
encoding="../imgs/jmp-jsr.drawio.png"
encoding-dark="../imgs/jmp-jsr-dark.drawio.png"
operation=`if (L == 1) {
R7 = PC;
}
PC = PC[15:9] @ pgoffset9;`
examples=`JMP FOO ; Jump to FOO.
JSR FOO ; Jump to FOO, put return PC into R7.`
%}}
Unconditionally jump to the location specified by pgoffset9 on the same page as
the JSR/JMP instruction. If the link bit L is set, the PC is saved in R7,
enabling a subsequent return to the instruction physically following the JSR
instruction.
{{% /instruction %}}
{{% instruction
mnemonic=`JMPR / JSRR`
short=`Jump, Base + Offset / Jump to Subroutine, Base + Offset`
formats=`JMPR BaseR, index6 (L = 0)
JSRR BaseR, index6 (L = 1)`
encoding="../imgs/jmpr-jsrr.drawio.png"
encoding-dark="../imgs/jmpr-jsrr-dark.drawio.png"
operation=`if (L == 1) {
R7 = PC;
}
PC = BaseR + ZEXT(index6);`
examples=`JMPR R2, #10 ; Jump to R2 + #10.
JSRR R2, #10 ; Jump to R2 + #10, put return PC into R7.`
%}}
Unconditionally jump to the location specified by adding ZEXT(index6) to the
contents of the base register. If the link bit L is set, the PC is saved in R7,
enabling a subsequent return to the instruction physically following the JSRR
instruction.
{{% /instruction %}}
{{% instruction
mnemonic="LD"
short="Load Direct"
formats=`LD DR, LABEL`
encoding="../imgs/ld.drawio.png"
encoding-dark="../imgs/ld-dark.drawio.png"
operation=`DR = mem[PC[15:9] @ pgoffset9];
setcc(DR);`
examples=`LD R4, COUNT ; R4 ← mem[COUNT].`
%}}
Load the register specified by DR from the location specified by pgoffset9 on
the same page as the LD instruction. The condition codes are set, based on
whether the value loaded is negative, zero, or positive.
{{% /instruction %}}
{{% instruction
mnemonic="LDI"
short="Load Indirect"
formats=`LDI DR, LABEL`
encoding="../imgs/ldi.drawio.png"
encoding-dark="../imgs/ldi-dark.drawio.png"
operation=`DR = mem[mem[PC[15:9] @ pgoffset9]];
setcc(DR);`
examples=`LDI R4, POINTER ; R4 ← mem[mem[POINTER]].`
%}}
Load the register specified by DR as follows: Construct an address by
concatenating the top seven bits of the program counter with the pgoffset9
field of the LDI instruction. The contents of memory at that address is the
address of the data to be loaded into DR. The condition codes are set, based on
whether the value loaded is negative, zero, or positive.
{{% /instruction %}}
{{% instruction
mnemonic="LDR"
short="Load Base + Offset"
formats=`LDR DR, BaseR, index6`
encoding="../imgs/ldr.drawio.png"
encoding-dark="../imgs/ldr-dark.drawio.png"
operation=`DR = mem[BaseR + ZEXT(index6)];
setcc(DR);`
examples=`LDR R4, R2, #10 ; R4 ← contents of mem[R2 + #10].`
%}}
Load the register specified by DR from the location specified by a base
register and index, as follows: The index is zero-extended to 16 bits and added
to the contents of BaseR to form a memory address. The contents of memory at
this address are loaded into DR. The condition codes are set, based on whether
the value loaded is negative, zero, or positive.
{{% /instruction %}}
{{% instruction
mnemonic="LEA"
short="Load Effective Address"
formats=`LEA DR, LABEL`
encoding="../imgs/lea.drawio.png"
encoding-dark="../imgs/lea-dark.drawio.png"
operation=`DR = PC[15:9] @ pgoffset9;
setcc(DR);`
examples=`LEA R4, FOO ; R4 ← address of FOO.`
%}}
Load the register specified by DR with the address formed by concatenating the
top seven bits of the program counter with the pgoffset9 field of the
instruction. The condition codes are set, based on whether the value loaded is
negative, zero, or positive.
{{% /instruction %}}
{{% instruction
mnemonic="NOT"
short="Bitwise Complement"
formats=`NOT DR, SR`
encoding="../imgs/not.drawio.png"
encoding-dark="../imgs/not-dark.drawio.png"
operation=`DR = ~SR;
setcc(DR);`
examples=`NOT R4, R2 ; R4 ← NOT(R2).`
%}}
Perform the bitwise complement operation on the contents of SR and place the
result in DR. The condition codes are set.
{{% /instruction %}}
{{% instruction
mnemonic="RET"
short="Return from Subroutine"
formats=`RET`
encoding="../imgs/ret.drawio.png"
encoding-dark="../imgs/ret-dark.drawio.png"
operation=`PC = R7;`
examples=`RET ; PC ← R7.`
%}}
Load the PC with the value in R7. This causes a return from a previous JSR or
JSRR instruction.
{{% /instruction %}}
{{% instruction
mnemonic="RTI"
short="Return from Interrupt"
formats=`RTI`
encoding="../imgs/rti.drawio.png"
encoding-dark="../imgs/rti-dark.drawio.png"
operation=`NZP = mem[R6];
R6 = R6 - 1;
PC = mem[R6];
R6 = R6 - 1;`
examples=`RTI ; NZP, PC ← top two values popped off stack.`
%}}
Pop the top two elements off the stack; load them into NZP, PC.
<h4>Notes</h4>
On an external interrupt, the initiating sequence pushes the current PC onto
the stack before loading the PC with the starting address of the service
routine. The last instruction in the service routine is RTI, which returns
control to the interrupted program by popping the stack and loading the value
popped into the PC. (This instruction is included in this appendix for
completeness. Its purpose and use are beyond the scope of what is normally
covered in an introductory textbook.)
{{% /instruction %}}
{{% instruction
mnemonic="ST"
short="Store Direct"
formats=`ST SR, LABEL`
encoding="../imgs/st.drawio.png"
encoding-dark="../imgs/st-dark.drawio.png"
operation=`mem[PC[15:9] @ pgoffset9] = SR;`
examples=`ST R4, COUNT ; mem[COUNT] ← R4.`
%}}
Store the contents of the register specified by SR into the memory location
specified by pgoffset9 on the same page as the ST instruction.
{{% /instruction %}}
{{% instruction
mnemonic="STI"
short="Store Indirect"
formats=`STI SR, LABEL`
encoding="../imgs/sti.drawio.png"
encoding-dark="../imgs/sti-dark.drawio.png"
operation=`mem[mem[PC[15:9] @ pgoffset9]] = SR;`
examples=`STI R4, POINTER ; mem[mem[POINTER]] ← R4.`
%}}
Store the contents of the register specified by SR into the memory location
whose address is obtained as follows: Construct an address by concatenating the
top seven bits of the program counter with the pgoffset9 field of the STI
instruction. The contents of memory at that address is the address of the
location to which the data in SR is to be stored.
{{% /instruction %}}
{{% instruction
mnemonic="STR"
short="Store Base+Offset"
formats=`STR SR, BaseR, index6`
encoding="../imgs/str.drawio.png"
encoding-dark="../imgs/str-dark.drawio.png"
operation=`mem[BaseR + ZEXT(index6)] = SR;`
examples=`STR R4, R2, #10 ; mem[R2 + #10] ← R4.`
%}}
Store the contents of the register specified by SR into the memory location
whose address is specified as follows: The six-bit offset is zero-extended to
16 bits and added to the contents of BaseR to form a memory address. This is
the address of the location into which the contents of SR is to be stored.
{{% /instruction %}}
{{% instruction
mnemonic="TRAP"
short="Operating System Call"
formats=`TRAP trapvec8`
encoding="../imgs/trap.drawio.png"
encoding-dark="../imgs/trap-dark.drawio.png"
operation=`R7 = PC;
PC = mem[ZEXT(trapvect8)];`
examples=`TRAP x23 ; Direct the operating system to execute the "IN" system call.`
%}}
Load the PC with the contents of the memory location obtained by zero-extending
trapvec8 to 16 bits. This is the starting address of the system call
specified by trapvec8. Load R7 with the PC, which enables a return to the
instruction physically following the TRAP instruction in the original program
after the service routine has completed.
<h4>Notes</h4>
Memory locations x0020 through x00FF, 192 in all, are available to contain
starting addresses for system calls specified by their corresponding
trap vectors. This region of memory is called the trap vector table. See Table
A.3. Memory locations x0000 through x001F are not part of the trap vector
table; therefore, x00 through x1F may not be used as trap vectors.
{{% /instruction %}}
+81
View File
@@ -0,0 +1,81 @@
+++
title = "Memory"
weight = 110
+++
LC-2 has a 16-bit address bus, giving a linear address space of 65,536
locations, numbered from `0x0000` to `0xFFFF`. Memory is canonically divided
into 2<sup>7</sup> pages of 2<sup>9</sup> words each. There is no segmentation,
no paging, and no virtual memory: every address maps directly to a physical
location.[^lc2-overview]
[^lc2-overview]: {{< cite-ics edition="1" chapter="Appendix A: The LC-2 ISA" page="429" >}}
Addressability is 16 bits: each location in memory holds exactly one word,
matching the width of the data bus. A single memory access always transfers a
full 16-bit value, never a partial word, which is also why the concept of
endianness does not apply here: there is no multi-byte value to order within a
single access.
## Memory Map
The memory map below reflects the conventions of the standard LC-2 operating
system, not the ISA itself. The ISA only defines the trap vector table at
`0x0000 - 0x00FF`. Everything else, from the location of the OS to the boundary
of userspace, is a software convention rather than a hardware requirement.
| Range | Purpose |
|:-------------------:|:-----------------------------------------------------------------------------:|
| `0x0000` - `0x00FF` | Trap Vector Table[^lc2-trap] / [Interrupt Vector Table](..#interrupt-support) |
| `0x0100` - `0x2FFF` | Operating System[^matt-postiff-guide] |
| `0x3000` - `0xCFFF` | Userspace |
| `0xD000` - `0xFFFF` | Device Registers |
[^lc2-trap]: {{< cite-ics edition="1" chapter="Appendix A.3 The Instruction Set" page="448" page-end="449" >}}
[^matt-postiff-guide]: {{< cite-web
author="Postiff, Matthew A."
title="LC-2 Programmer's Reference and User Guide"
site="University of Texas"
url="https://www.cs.utexas.edu/~fussell/courses/cs310h/simulator/lc2.pdf"
format="PDF"
accessed="August 31, 2026"
url-archived="https://web.archive.org/web/20251205052350/https://www.cs.utexas.edu/~fussell/courses/cs310h/simulator/lc2.pdf"
url-archived-date="December 05, 2025"
>}}
## Stack
The ISA does not define a stack as a distinct hardware structure, but it
assumes one exists by convention. R6 is used as the stack pointer, and the
stack is assumed to grow toward `0xFFFF`.
This convention is not optional in practice: the `RTI` instruction relies on it
directly. When an interrupt is serviced, the CC and PC are pushed onto the
stack. `RTI` pops them back in reverse order to resume execution. Any deviation
from the R6 convention would break interrupt handling.[^lc2-rti]
[^lc2-rti]: {{< cite-ics edition="1" chapter="Appendix A.3 The Instruction Set" page="444" >}}
## Memory-Mapped I/O
LC-2 uses memory-mapped I/O: devices are accessed using the same load and store
instructions used for regular memory, with no dedicated I/O instructions in the
ISA.[^lc2-mmio]
[^lc2-mmio]: {{< cite-ics edition="1" chapter="8.1.2 Memroy Mapped I/O Versus Special Input/Output Instructions" page="158" >}}
The standard LC-2 operating system maps the following devices into the top of
the address space. These are not defined by the ISA: they are the devices
assumed by the reference implementation and its OS.
| Address | Register | Description |
|:--------:|:-------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------|
| `0xF3FC` | Video Status Register (`CRTSR`)[^lc2-devices-registers] | Bit 15, the ready bit, indicates whether the video device is ready to receive another character to print on the screen. |
| `0xF3FD` | Horizontal Screen Position[^matt-postiff-guide] | Not implemented. |
| `0xF3FE` | Vertical Screen Position | Not implemented. |
| `0xF3FF` | Video Data Register (`CRTDR`) | A character written in the low byte of this register will be displayed on screen. |
| `0xF400` | Keyboard Status Register (`KBSR`) | Bit 15, the ready bit, indicates whether the keyboard has received a new character. |
| `0xF401` | Keyboard Data Register (`KBDR`) | The lower byte contain the last character typed on the keyboard. |
| `0xFFFF` | Machine Control Register (`MCR`) | Bit 15 is the clock enable bit. When cleared, instruction processing stops because the clock signal stops pulsing. |
[^lc2-devices-registers]: {{< cite-ics edition="1" chapter="Appendix A: The LC-2 ISA" page="430" >}}
+1
View File
@@ -0,0 +1 @@
<sup>[<i><a href="https://en.wikipedia.org/wiki/Citation_needed" target="_blank">citation needed</a></i>]</sup>
+132
View File
@@ -0,0 +1,132 @@
{{- /*
instruction - Standardized instruction reference card, following the layout
used in the Patt & Patel textbook: mnemonic title, short description, assembler
formats, encoding diagram, high-level operation, and usage examples.
@param {string} mnemonic Instruction mnemonic(s), comma-separated if more
than one (e.g. "ADD" or "JSR, JSRR").
@param {string} short Short two-to-three word description (e.g.
"Addition").
@param {string} formats Assembler format(s). Use a backtick-quoted
string for multiple lines.
@param {string} encoding Filename of the encoding diagram
@param {string} encoding-dark Optional dark mode variant of the encoding
diagram. If set, `encoding` is only shown in
light mode and this is shown in dark mode.
@param {string} encoding-bits Optional accessible bit-level breakdown of the
encoding, for screen readers and crawlers. Use
lines of "bits:value:meaning", one per row.
Separate multiple variants with a line
containing only "---".
@param {string} operation High-level operation, typically pseudo-C. Use a
backtick-quoted string for multiple lines.
@param {string} examples Usage examples in LC-2 assembly. Use a
backtick-quoted string for multiple lines.
@example {{% instruction
mnemonic="ADD"
short="Addition"
formats=`ADD DR, SR1, SR2
ADD DR, SR1, imm5`
encoding="add.drawio.png"
encoding-dark="add-dark.drawio.png"
operation=`if (bit[5] == 0) {
DR = SR1 + SR2;
} else {
DR = SR1 + SEXT(imm5);
}
setcc(DR);`
examples=`ADD R2, R3, R4 ; R2 ← R3 + R4
ADD R2, R3, #7 ; R2 ← R3 + 7`
%}}
If bit [5] is 0, the second-source operand is obtained from SR2. If bit [5] is
1, the second-source operand is obtained by sign-extending the imm5 field to 16
bits. In both cases, the second source operand is added to the contents of SR1,
and the result stored in DR. The condition codes are set, based on whether the
result is negative, zero, or positive.
{{% /instruction %}}
*/ -}}
{{- $mnemonic := .Get "mnemonic" -}}
{{- $short := .Get "short" -}}
{{- $formats := .Get "formats" -}}
{{- $encoding := .Get "encoding" -}}
{{- $encodingDark := .Get "encoding-dark" -}}
{{- $encodingBits := .Get "encoding-bits" -}}
{{- $operation := .Get "operation" -}}
{{- $examples := .Get "examples" -}}
{{- $description := .Inner -}}
<!--
Sligtly edited version of Hextra's `details` shortcode
<https://github.com/imfing/hextra/blob/main/layouts/_shortcodes/details.html>
-->
<details class="hx:last-of-type:mb-0 hx:rounded-lg hx:bg-neutral-50 hx:dark:bg-neutral-800 hx:p-2 hx:mt-4 hx:group">
<summary class="hx:flex hx:items-center hx:cursor-pointer hx:select-none hx:list-none hx:p-1 hx:rounded-sm hx:transition-colors hx:hover:bg-gray-100 hx:dark:hover:bg-neutral-800 hx:before:mr-1 hx:before:inline-block hx:before:transition-transform hx:before:content-[''] hx:dark:before:invert hx:rtl:before:rotate-180 hx:group-open:before:rotate-90">
<strong class="hx:text-xl">{{ $mnemonic }}</strong>
</summary>
<div class="hx:p-2 hx:overflow-hidden">
<!-- Hidden heading: For TOC entry and permalink only -->
### {{ $mnemonic }} {.hx:sr-only}
> {{ $short }}
<h4>Assembler Formats</h4>
```
{{ $formats }}
```
<h4>Encoding</h4>
{{ if $encodingDark }}
<img src="{{ $encoding }}" alt="{{ $mnemonic }} instruction encoding" class="hx:dark:hidden">
<img src="{{ $encodingDark }}" alt="{{ $mnemonic }} instruction encoding" class="hx:hidden hx:dark:block">
{{ else }}
<img src="{{ $encoding }}" alt="{{ $mnemonic }} instruction encoding">
{{ end }}
{{ if $encodingBits }}
{{ $variants := (split $encodingBits "---") }}
{{ $html := printf "<div id=\"%s-encoding-table\" class=\"hx:sr-only\">" ($mnemonic | lower) }}
{{ range $variantIndex, $variant := $variants }}
{{ $html = print $html "<table>" }}
{{ $html = print $html (printf "<caption>%s encoding%s</caption>" $mnemonic (cond (gt (len $variants) 1) (printf ", variant %d" (add $variantIndex 1)) "")) }}
{{ $html = print $html "<thead><tr><th>Bits</th><th>Value</th><th>Meaning</th></tr></thead>" }}
{{ $html = print $html "<tbody>" }}
{{ range (split (trim $variant "\n") "\n") }}
{{ $parts := split . "|" }}
{{ $html = print $html (printf "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" (index $parts 0) (index $parts 1) (index $parts 2)) }}
{{ end }}
{{ $html = print $html "</tbody></table>" }}
{{ end }}
{{ $html = print $html "</div>" }}
{{ $html | safeHTML }}
{{ end }}
<h4>Operation</h4>
```c
{{ $operation }}
```
<h4>Description</h4>
{{ $description }}
<h4>Examples</h4>
```
{{ $examples }}
```
</div>
</details>