---
# System prepended metadata

title: LC3 Startup and Memory Protection
tags: [technical]

---

###### tags: `technical`

# LC3 Startup and Memory Protection

In short:
  - LC3Tools will set PSR bit 15 to 1 (user mode) only [if the reset PC at or above 0x3000](https://github.com/chiragsakhuja/lc3tools/blob/da513156e633738caea2f022b8ef1c495c658e9b/src/backend/event.cpp#L33-L68)
  - the reset PC is inferred from the [first orig in the loaded obj file](
https://github.com/chiragsakhuja/lc3tools/blob/da513156e633738caea2f022b8ef1c495c658e9b/src/backend/event.cpp#L152-L161)
  - on startup the OS is not actually run, instead a [`SetupEvent`](https://github.com/chiragsakhuja/lc3tools/blob/da513156e633738caea2f022b8ef1c495c658e9b/src/backend/event.cpp#L32) is [produced](https://cs.github.com/chiragsakhuja/lc3tools/blob/74d5e971506716c0979e21c3a06d228fbb719a94/src/backend/simulator.cpp?q=SetupEvent#L60-L72) which mimicks the output of running the [OS init](https://github.com/chiragsakhuja/lc3tools/blob/da513156e633738caea2f022b8ef1c495c658e9b/src/backend/lc3os.cpp#L532-L544)

We take a slightly different approach:
  - we have [our OS init](https://github.com/ut-utp/core/blob/1e9a83d39fdaa630b988108749f55d26e8134ac3/os/src/os.rs#L608-L644) perform the "only drop to user mode if the first orig is above 0x3000" instead of having this be special cased into the simulator
  - this requires the assembler to, at assemble time, [bake in the address of the first orig](https://github.com/ut-utp/assembler/blob/b0efc5f93c70b4705a3879b1a194f05da16f2e4f/assembler/src/assembler.rs#L50-L53) so that the os can [read it on startup](https://github.com/ut-utp/core/blob/1e9a83d39fdaa630b988108749f55d26e8134ac3/os/src/os.rs#L1813-L1814)
  - we have the TUI [skip to the first orig](https://github.com/ut-utp/tui/blob/b956716e550882c32e76ac8f53a4651d8937e4bc/tui/src/widgets/modeline.rs#L155-L207) (i.e. the program start) on load _unless_ there's a breakpoint in the OS or if there's no OS

The end result is that we have behavior that's equivalent to LC3Tools but avoid a little special casing and allow curious users to step through our OS in the simulator.

---

Our approach works fine with, for example, [EE 306 Lab 5]( https://docs.google.com/document/d/1ni1zcClKYaRgznKJtX8ds_YI02_5P0334m3NgiJgB58/edit?usp=sharing).

The [given starter code](https://docs.google.com/document/d/1gu1QiIaDvt7KI6_q0OKhzdv1aozMlHX1YWH4SuYYS1w/edit
) (reproduced below) assumes the OS has set up the stack for it (notice the pushes) but not dropped the machine into user mode (the starter code manipulates protected regions of memory and ultimately RTIs to get to user mode):

```asm=
        .ORIG x800
        ; (1) Initialize the interrupt vector table.
        LD R0, VEC
        LD R1, ISR
        STR R1, R0, #0

        ; (2) Set bit 14 of KBSR.
        LDI R0, KBSR
        LD R1, MASK
        NOT R1, R1
        AND R0, R0, R1
        NOT R1, R1
        ADD R0, R0, R1
        STI R0, KBSR

        ; (3) Set up the system stack to enter user space.
        LD R0, PSR
        ADD R6, R6, #-1
        STR R0, R6, #0
        LD R0, PC
        ADD R6, R6, #-1
        STR R0, R6, #0
        ; Enter user space.
        RTI
        
VEC     .FILL x0180
ISR     .FILL x1000
KBSR    .FILL xFE00
MASK    .FILL x4000
PSR     .FILL x8002
PC      .FILL x3000
        .END
```

This works fine with our approach; `.orig x800` will cause our OS to `RTI` to the program without switching to user mode (which will result in the stacks [not being swapped](https://github.com/ut-utp/core/blob/f9ed93dc959040a481499214a6582a8d741c7840/baseline-sim/src/interp.rs#L1027-L1031)). From there the init code at `x800` can `RTI` to user code as expected.

A TRAP based approach (register a trap and call it instead of adding init code that runs in privileged mode) works fine with our approach too (i.e. use `.orig`s to register a trap in the trap table statically, at assemble time and then call it from user code as a way of gaining privilege).