Project 10: Simple Windowing Machine (SWIM)
Overview
The Simple Windowing Machine (SWIM) is, as its name implies, a simple but
functional operating system. It has the following features:
- Four distinct windows, each of which can run a program or edit a file.
- An additional window displays the process manager.
- An interpreter for a simple programming language.
- This will be provided for you to use.
- Dynamic memory allocation for user programs, with a copying garbage collector.
- A disk for persistent storage, simulated using a RAM disk
Step 1: Windows
Using the provided template code,
write a bare-metal program to support a windowing environment as follows:
- Divide the VGA buffer into the following areas:
- The rightmost 10 columns are reserved for process status information.
- Row 0 is reserved for entering filenames.
- The remaining area is divided into four equal-sized quadrants.
- The window boundaries are drawn using
.
characters.
- Row 1 is the top boundary.
- The currently selected window has its boundaries drawn with
*
characters.
- Function keys
F1
, F2
, F3
, and F4
are used to select the current window.
- When a window is selected, its boundary characters change immediately.
- At the midpoint of each window header, include the function key code for
selecting that window.
- Function key
F5
is used to select the filename-entry row. None of the windows
are highlighted when this row is active.
- If the file exceeds the buffer size,
F7
scrolls up one line, and F8
scrolls
down one line.
After completing Step 1, SWIM should look like this:
Step 2: Incorporating the File System
- Add the method
pub fn list_directory(&mut self) -> FileSystemResult<(usize, [[u8; MAX_FILENAME_BYTES]; MAX_FILES_STORED])>
to struct FileSystem
- It will return the total number of files stored, as well as the bytes in the
filename of each file.
- Add a link to your GitHub repository for your
Project 9 solution
in your
Cargo.toml
file. For example, here is what that line looks like in my
own Cargo.toml
: file_system_solution = {git = "https://github.com/gjf2a/file_system_solution"}
- Add a
FileSystem
object to your kernel, using the following constants:
const MAX_OPEN: usize = 16;
const BLOCK_SIZE: usize = 256;
const NUM_BLOCKS: usize = 255;
const MAX_FILE_BLOCKS: usize = 64;
const MAX_FILE_BYTES: usize = MAX_FILE_BLOCKS * BLOCK_SIZE;
const MAX_FILES_STORED: usize = 30;
const MAX_FILENAME_BYTES: usize = 10;
- When you add your
FileSystem
object, create four files and store them, so that
it has some contents when the OS starts up. Here are four files for you to use:
hello
nums
average
sum := 0
count := 0
averaging := true
while averaging {
num := input("Enter a number:")
if (num == "quit") {
averaging := false
} else {
sum := (sum + num)
count := (count + 1)
}
}
print((sum / count))
pi
sum := 0
i := 0
neg := false
terms := input("Num terms:")
while (i < terms) {
term := (1.0 / ((2.0 * i) + 1.0))
if neg {
term := -term
}
sum := (sum + term)
neg := not neg
i := (i + 1)
}
print((4 * sum))
- Each file is listed in each of the four main windows. Each file listing is
given in three columns, each of which may contain up to 10 rows.
After completing Step 2, SWIM should look like this:
Step 3: Selecting and Editing Files
- When
F5
is selected, the user can enter a filename of up to 10 characters
in the top window.
- When the user types the backspace (Unicode
\u{8}
), it erases the previous
character.
- When the user types the Enter key:
- An empty file with the given name is opened, created, and closed on the disk
- The filename is cleared from the top window
- The file listings in the four quadrant windows are updated to include the new file
- When the user hits one of the other function keys, it switches to the
designated buffer. Within the buffer, the arrow keys are used to navigate among
the columns. Exactly one filename is always highlighted within the selected
window.
- When the user hits the
e
key, the selected window switches into Editor mode:
- The selected filename appears as part of the window header, after the function
key.
- SWIM opens the file and reads its contents so as to be visible. It then closes
the file.
- As with the filename editing, each keystroke appears in the appropriate window,
with the backspace operating properly as well.
- Entering
F6
uses open_create()
to reopen the file. It writes the contents
of the editor’s buffer to disk, then closes the file, restoring the window to
the file selection screen.
- When the file is re-opened, from any of the windows, the changes saved earlier
should be reflected.
After completing Step 3, when creating a file named test
, SWIM should look like this:
When navigating to edit the file test
in the F1
buffer, it should look like this:
While editing the file in the F1
buffer, it should look like this:
After saving the file using F6
and reopening it in the F2
buffer, it should look
like this:
Step 4: A Copying Garbage Collector
- Implement a copying garbage collector.
- Use this template for building
your solution.
- Also examine the gc_headers
to familiarize yourself with the data structures you will be using.
Pointer
objects are the means by which entities using the heap
refer to memory.
- Each
Pointer
object includes:
- A block number that uniquely identifies the allocated block.
- An offset into that block, to access a specific memory location.
- The total size of the block.
- The malloc() method will perform an allocation, returning a
Pointer
to the newly allocated memory. The Pointer
will include a unique block
number for the allocation, along with its size. The offset
will be zero,
referencing the first memory location in the block.
- The
load()
and store()
methods in conjunction with Pointer
s are
used to retrieve and update values in heap-allocated RAM.
- Entities using the heap only use
Pointer
objects - they are never
given access to the specific memory address of the memory they are using.
- This is because the garbage collector might relocate memory to a
new address when collecting.
- By referencing memory exclusively through block numbers, the
relocation process is made invisible to the user.
- When
malloc()
is called but there is no more space in the heap,
the collector will call the trace()
method of its Tracer
parameter
to find out which blocks are in use.
- The collector should provide an array of
MAX_BLOCKS
boolean values
to trace()
. All of the values in the array should initially be false
.
- The
Tracer
will mark true
for each block it wants to keep.
- The collector will then copy each
true
block to the new heap,
updating their addresses as they are moved.
Step 5: Creating and Managing Processes
- When the user hits
r
when a file is highlighted, the file should run.
- To run a program, create an
Interpreter
object for it using Interpreter::new()
.
The program text (as a &str
) will be the parameter to new()
.
- The
Interpreter
object’s type
will be Interpreter<MAX_TOKENS, MAX_LITERAL_CHARS, STACK_DEPTH, MAX_LOCAL_VARS, WINDOW_WIDTH, CopyingHeap<HEAP_SIZE, MAX_HEAP_BLOCKS>>
The Interpreter
type is defined in the simple_interp
crate; a reference to it is included in the Cargo.toml
file in the template.
- You will need to create a data type that implements the
InterpreterOutput
trait in order to receive output from the interpreter.
- Use the
tick()
method to execute one instruction in the program.
- If
tick()
returns TickResult::AwaitInput
, block the process
until input is available.
- Once input is available, use the
provide_input()
method to send
the input to the program.
- While the file is running, if the user hits
F6
, the program should immediately
stop and return to the file selector.
- Ensure that all processes have a fair opportunity to run on the CPU.
- The number of instructions executed by each process should be shown in the
right window, as seen below:
Submissions
- Create a private GitHub repository for your SWIM kernel.
- Paste the repository URL into your Teams channel.
Assessment
- Partial: Completes Step 1 and either Step 2 or Step 4.
- Complete: Meets all requirements given above. Never panics!
Acknowledgement
This assignment was adapted from materials developed by
Philipp Oppermann.