P37X ISA
The ISA will will implement in CSE 371/372 is called P37X. It is a simple RISC ISA that resembles a combination of MIPS and LC3. The following analogy is roughly true: P37X is to LC3 as MIPS is to x86.
P37X shares the following features with LC3 (and differences with MIPS):
Machine Instructions
The following table contains the P37X machine instructions, the ones the hardware can execute directly.
The P37X assembler is of course symbolic, and it allows you to specify PC
offsets as symbolic label references rather than numeric constants. A label
can (optionally) prepend every instruction or be on a line by itself. A label
is a string consisting of letters, digits, and underscores, e.g., FOO, FOO0, or _FOO_0:. A
label cannot begin with a digit. This is similar to the LC3 assembler.
Notice, that in P37X all PC-relative instructions are actually relative to
PC+1.
Assembly syntax | Semantics | Encoding |
Arithmetic, Logical, and Shifts | ||
unused | undefined | 0000 ddds sstt t000 |
unused | undefined | 0000 ddds sstt t001 |
unused | undefined | 0000 ddds sstt t010 |
unused | undefined | 0000 ddds sstt t011 |
ADD rd, rs, rt | regs[rd] = regs[rs] + regs[rt] | 0000 ddds sstt t100 |
SUB rd, rs, rt | regs[rd] = regs[rs] - regs[rt] | 0000 ddds sstt t101 |
MUL rd, rs, rt | regs[rd] = regs[rs] * regs[rt] | 0000 ddds sstt t110 |
unused | undefined | 0000 ddds sstt t111 |
OR rd, rs, rt | regs[rd] = regs[rs] | regs[rt] | 0001 ddds sstt t000 |
NOT rd, rs | regs[rd] = ~regs[rs] | 0001 ddds ssXX X001 |
AND rd, rs, rt | regs[rd] = regs[rs] & regs[rt] | 0001 ddds sstt t010 |
XOR rd, rs, rt | regs[rd] = regs[rs] ^ regs[rt] | 0001 ddds sstt t011 |
SLL rd, rs, rt | regs[rd] = regs[rs] << (regs[rt] & 15) | 0001 ddds sstt t100 |
SRL rd, rs, rt | regs[rd] = ZEXT(regs[rs] >> (regs[rt] & 15)) | 0001 ddds sstt t101 |
SRA rd, rs, rt | regs[rd] = SEXT(regs[rs] >> (regs[rt] & 15)) | 0001 ddds sstt t110 |
unused | undefined | 0001 ddds sstt t111 |
Traps and Returns from Trap | ||
TRAP imm8 | regs[r7] = PC + 1 PC = imm8 PSR[15]=1 |
0010 XXXX iiii iiii |
RTT rd | PC = regs[rd] PSR[15] = 0 |
0011 dddX XXXX XXXX |
Calls and Jumps | ||
JUMP imm12 (label) | PC = PC + 1 + SEXT(imm12) | 0100 iiii iiii iiii |
JUMPR rd | PC = regs[rd] | 0101 dddX XXXX XXXX |
JSR imm12 (label) | regs[r7] = PC + 1 PC = PC + 1 + SEXT(imm12) |
0110 iiii iiii iiii |
JSRR rd | regs[r7] = PC + 1 PC = regs[rd] |
0111 dddX XXXX XXXX |
Conditional Branches | ||
NOOP | PC = PC + 1 | 1000 XXX0 00XX XXXX |
BRP rd, imm6 (label) | if (regs[rd] > 0) PC = PC + 1 + SEXT(imm6) | 1000 ddd0 01ii iiii |
BRZ rd, imm6 (label) | if (regs[rd] == 0) PC = PC + 1 + SEXT(imm6) | 1000 ddd0 10ii iiii |
BRZP rd, imm6 (label) | if (regs[rd] >= 0) PC = PC + 1 + SEXT(imm6) | 1000 ddd0 11ii iiii |
BRN rd, imm6 (label) | if (regs[rd] < 0) PC = PC + 1 + SEXT(imm6) | 1000 ddd1 00ii iiii |
BRNP rd, imm6 (label) | if (regs[rd] != 0) PC = PC + 1 + SEXT(imm6) | 1000 ddd1 01ii iiii |
BRNZ rd, imm6 (label) | if (regs[rd] <= 0) PC = PC + 1 + SEXT(imm6) | 1000 ddd1 10ii iiii |
BRNZP rd, imm6 (label) | PC = PC + 1 + SEXT(imm6) | 1000 ddd1 11ii iiii |
Constants | ||
CONST rd, imm9 | regs[rd] = SEXT(imm9) | 1001 dddi iiii iiii |
INC rd, imm9 | regs[rd] = regs[rd] + SEXT(imm9) | 1010 dddi iiii iiii |
LEA rd, imm9 (label) | regs[rd] = PC + 1 + SEXT(imm9) | 1011 dddi iiii iiii |
Loads and Stores | ||
LDR rd, rs, imm6 | regs[rd] = mem[regs[rs] + SEXT(imm6)] | 1100 ddds ssii iiii |
STR rd, rs, imm6 | mem[regs[rs] + SEXT(imm6)] = regs[rd] | 1101 ddds ssii iiii |
LD rd, imm9 (label) | regs[rd] = mem[PC + 1 + SEXT(imm9)] | 1110 dddi iiii iiii |
ST rd, imm9 (label) | mem[PC + 1 + SEXT(imm9)] = regs[rd] | 1111 dddi iiii iiii |
Memory-Mapped I/O Devices
The P37X has several memory-mapped I/O devices and control registers:
Input:
Output:
Control:
Static Data and Assembler Directives
Like the LC3 assembler, the P37X assembler supports static data declarations and a number of other assembler directives. An assembler directive is different than an instruction or even a pseudo-instruction in that it doesn't actually correspond to any instruction execution at runtime. P37X uses the same exact directives as LC3, and they all start with a period. Directives can also be labeled.
In case you forgot, the following table summarizes the P37X (LC3) directives.
Syntax | Comment |
.ORIG imm16 | Following code/data will be placed at static address imm16 |
.FILL imm16 | imm16 is statically written into the current code/data address |
.BLKW imm16 | A block of imm16 words are reserved starting current static address |
.STRINGZ "string" | Equivalent to the following loop: for (i = 0; i < strlen(string); i++) .FILL string[i] .FILL 0x0000 |
.END | Marks the end of the program source |
"OS" Trap Routines
P37X also supports all of LC3's trap codes and the associated "OS" routines, although it sometimes gives them different mnemonic names. It also implements them in a different way. In LC3, the trap-vector is a vector of addresses and the TRAP instruction saves the return address, changes privilege mode, dereferences this vector at the proper position, and jumps to the dereferenced address. Basically an LC3 TRAP does a load and a call in one instruction.
Since such a compound operation would be complex to implement in hardware, P37X TRAP instructions work differently. A P37X TRAP saves the return address, changes the privilege mode, and jumps to the trap-vector. The trap-vector itself contains not the address of the routine, but an instruction that jumps to that address. Instead of doing a load and a call in one instruction, P37X implements traps by doing a call (the TRAP) and a jump in two instructions.
At this point you may be wondering "Why doesn't LC3 implement traps using separate load and call instructions?" The answer has to do with the fact that a call actually performs two steps: i) it saves the return address (link), and ii) jumps to the target address. In an LC3 TRAP, these two steps have to take place on either side of the load: the link has to happen before the load, the jump has to happen after.
Anyway, implementation aside, the traps work the same from the programmer's perspective. In case you forgot, the following table summarizes the P37X (LC3) trap codes and routines.
Syntax | Comment |
TRAP x20 (GETC) | Reads a single, un-echoed character from the console to r0 |
TRAP x21 (PUTC) | Writes a single character in r0 to the console |
TRAP x22 (PUTS) | Writes an unpacked zero-terminated string (e.g., .STRINGZ) at address regs[r0] to the console |
TRAP x23 (EGETC) | Prints a prompt and reads a single, echoed character from the console to r0 |
TRAP x24 (PUTSP) | Writes a packed zero-terminated string (e.g., .STRINGZP) at address regs[r0] to the console |
TRAP x25 (HALT) | Halts the processor |