lc3db
is a functional simulator/debugger for the LC-3
architecture. It provides a GUI using the GNU ddd
debugger.
This is the First Edition of Using the LC-3 Debugger, 2004-04-19, for
lc3db
Version 0.3.0.
lc3db
session.lc3db
's
builtin assemblerlc3db
startup code
including a description of the boot process.lc3db
.The goals of lc3db
are to create a robust and complete
simulator package for the LC-3 architecture. It differs from other LC-3
simulators in a few ways. It is structured more like a debugger than a
simulator. This was done to leverage existing GUI interfaces. The current
GUI mode is more or less as functional as the existing simulator GUI (and has
a few additional features). However, like all new code, there could be some
bugs. Feel free to report any behavior you find to be odd or things that you think are just plain bugs to Anthony Liguori.
The first thing you need to do is run the simulator. You will need to either be on a UT CS machine or using some sort of remote client capable of displaying X. Basically, the same requirements for the other LC-3 simulator.
You can run the simulator with the following command:
aliguori@bubble:~$ /p/bin/lc3db --ddd
This should bring up a Window that looks as shown below. If a window
pops with the queryThe very top portion of the screen is the data window. The GUI allows you to plot data graphs. This is an advanced feature that we will not cover in full except to say that this plot contains all of the machine state. It will automatically be updated every cycle and the data that changed will be highlighted.
The middle portion of the screen contains a disassembled version of the memory for about a thousand bytes after the PC. Some important things to note about this code:
The command window is the very bottom portion of the screen. All aspects
of lc3db
can be accessed via this command interface. Before we
discuss some of the important commands, we'll cover the three buttons that
appear on the top of the window.
This command does what you'd expect it to. It boots the LittleOS operating system. If you are writing code that uses interrupts, it is very important that you do this before loading/running anything else. This command will also launch an additional IO console which is useful if you want the input/output for the simulator to be separate from the console.
The step command steps through a single instruction in the simulator. This means it will enter into JSR and TRAP instructions.
The next command will step through a single instruction in the simulator. It differs from the step command in that it will not enter into a JSR or TRAP instruction. This is very useful if you do not want to sift through all of the builtin TRAP routines.
You can see all of the builtin console commands by typing help into the console.
After you've compiled your program (see the section on Assembling for more
info on this), you can load your program by using the File => Open
Program
menu (or the console). Using the File => Open
Program
menu would look something like:
lc3db
has a builtin in assembler. This section will discuss
some of the differences in syntax of the assembly code and how to use it to
compiler your programs. It is perfectly fine to use the assembler from the
previous simulator to compile your programs. However, if you wish for it to
show up in Open Program dialog, it must have execute privileges. Also, the
symbol table format is not compatible between assemblers so you will lose all
debugging information.
There is a special script installed on the UT CS network to make using the builtin compiler quite simple. Its usage is pretty straight forward. To compile filename.asm, use the following command:
aliguori@bubble:~$ /p/bin/lc3asm filename.asm
This will produce two files: filename.obj and filename.dbg.
lc3db
can be run locally on an Intel-based Linux machine. The
only requirement is that GNU ddd
is installed if you wish to use
a GUI interface. GNU ddd
is available from the GNU
ddd
website.
There is no support provided if you run lc3db
locally. It's
import to run the static version from the directory you have it installed in.
For instance, if you install lc3db
in a directory called
lc3 in your home directory, it may look something like:
[anthony@rockhopper lc3]$ ./lc3db --ddd
Source: lc3db-1.0.tgz (32k)
LittleOS is a basic operating system for the LC-3. It supports all of the
standard traps as defined in P&P (except for PUTSP). The one thing that
makes LittleOS unique is that it defines a boot sequence (including a boot
interrupt). The following is the code for this boot sequence:
You'll notice this code simply loads R6 with the address of a supervisor stack
(with some default entries on it), adjusts R6, and executes an RTI. This
effectively boot straps the SSP register with a stack that will be used for the
rest of the machines execution. You'll notice the stack is initially populated
with a PC, PSR, and default USP. The reasons for this are discussed in the
next section.
.BLKW x80
.FILL BANNER
.FILL x0000
.FILL x8000
SUPERVISOR_STACK
STARTUP
LEA R6, SUPERVISOR_STACK
ADD R6, R6, -3
RTI
You can access the source code for LittleOS on the UT CS network under
/p/share/lc3db/los/los.asm
.
lc3db
is the only LC-3 simulator (that I know of at least)
that has a complete implementation of interrupts. However, the implementation
is slightly different than described in P&P mainly to support pre-emptive
multi-tasking. An interrupt is essential a function call that is called by
the processor during normal execution. Just like a function call, a stack has
to be used to be stored things in a particular way for interrupts to work
correctly.
How interrupts are trigger will not be covered here. Instead, assume that for whatever reason, the processor has decided to take an interrupt. As programmers, we're interested in what information we're given in the interrupt handler itself.
When the interrupt begins, the only register with a known value is R6. This register will hold the top of the supervisor stack. The top three elements on this stack will also be known. The top of the stack will contain the PC before an interrupt occurred. The next element of the stack will be the value of R6 before the interrupt occurred. The third element will be the PSR before the interrupt. The interrupt handler is free to modify these values and, indeed will be forced to in implementing a timer interrupt.
The timer interrupt relies on two special registers: the MCR and the MCC.
The MCR is the machine control register and is located at address xFFFE. The
values of MCR are as follows:
MCR[15] = clock enable
MCR[14] = timer interrupt enable
MCR[13:0] = cycle interval between timer interrupts
Additionally, the MCC, or machine cycle counter, is located at xFFFF. This
value is incremented every time a cycle occurs. The timer interrupt is
actually triggered when MCC[15:0] >= ZEXT(MCR[13:0])
.