This document is a work in progress.
Installing and running mic1
Writing IJVM programs
General mic1 simulator questions
Questions about sample program echo.jas
Java Development Kit (JDK) 1.0 or later for Win95/98/NT or some flavor of Unix. There is not currently a distribution available for Macintosh.
Some of the java programs may be used in a text-only environment (ijvmasm, mic1asm, mic1dasm, dump) but the simulator itself and certain other programs (mic1sim, gijvmasm, gmic1asm) require a graphical environment. For Win95/98/NT users this should not a problem, but for Unix users, this means that you will need X-Windows to run the simulator or GUI assemblers.
Also, for the Win95/98/NT distribution, you will need approximately 700Kbytes of free disk space to do the install. The Unix installation requires about 600Kbytes.
Before you run any of the mic1 software, you must set several environment variables.
C:\>path PATH=C:\WINDOWS;C:\WINDOWS\COMMAND
If the java bin directory is not in the PATH, you will need to add it to the path defined in env.bat. In the env.bat file you should add
path C:\JDK1.2\BIN;%PATH%
where you would replace "C:\JDK1.2\" with the path of your JDK directory.
If you don't know the path of the JDK directory, select Find-->Files or Folders... from the Start menu. Enter "javac.exe" in the Named field. Make sure the Look in: field indicates the correct drive, and the Include subfolders option is selected. Click Find Now. Set PATH as the value in the In Folder column. If no value appears, you do not have a JDK installed. If multiple values appear, you might have multiple JDK's installed. In this case, use any of the directories (preferably the directory containing the newer JDK).
Bourne-compatable shell (sh, ksh, bash, zsh...)
export PATH=/usr/lib/jdk1.1/bin:$PATH
C shell-compatable shells (csh, tcsh)
set path=( /usr/lib/jdk1.1/bin $PATH )
.method getchar() start: IN // Read character from keyboard buffer DUP // Duplicate char, one copy for comparison, one for return value IFEQ clear // If char = 0 (keyboard buffer empty) goto clear IRETURN // else, return with char as return value clear: POP // Remove 0 from top of stack GOTO start // Try reading character again .end-methodThis method will loop until a key has been pressed. The keyboard buffer can be cleared by clicking the Reset button in the mic1sim window. IN does not echo characters to the standard out text area. See echo.jas for an example of how to echo key strokes to the standard out text area.
BIPUSH 0x48 // Push ASCII value for "H" OUT // Print "H" BIPUSH 0x49 // Push ASCII value for "I" OUT // Print "I"
If you are using the included microprogram (mic1ijvm.mic1), the following is the proper procedure for declaring and invoking a method in an IJVM program. To illustrate this, we will write a sample method called DIFF_NEG_YN, which takes two parameters, calculates the difference, prints "Y" if the difference is negative, "N" if positive, and sets as a return value the difference.
.method DIFF_NEG_YN (p1,p2)The parameter list is comma-seperated. The names in the list are automatically declared as local variables and assigned the values passed to the method. If a method takes no parameters, the method name should be followed by empty parenthesis, ie, .method PRINT ().
.var diff .end-var
ILOAD p1 // Push the first parameter ILOAD p2 // Push the second parameter SUB // Subtract ISTORE diff // Store the difference in diff ILOAD diff // Push diff IFLT lt // If diff < 0, goto lt BIPUSH 0x4E // else, print "N" OUT GOTO return lt: BIPUSH 0x59 // (diff < 0) OUT // print "Y" return: ILOAD diff // Push diff IRETURN // Return (value of diff will be pushed onto the // top of the invoking method's stack) .end-methodThe method must end with the .end-method directive. The IRETURN instruction is necessary for the method to properly execute.
LDC_W OBJREF
BIPUSH 0x10 BIPUSH 0x15When the method is called, p1 will be assigned the value 0x10 and p2 will be assigned the value of 0x15.
INVOKEVIRTUAL DIFF_NEG_YN
POP
// --- Start program --- .constant OBJREF 0x10 .end-constant .main LDC_W OBJREF BIPUSH 0x10 BIPUSH 0x15 INVOKEVIRTUAL DIFF_NEG_YN POP HALT .end-main .method DIFF_NEG_YN (p1,p2) .var diff .end-var ILOAD p1 // Push the first parameter ILOAD p2 // Push the second parameter SUB // Subtract ISTORE diff // Store the difference in diff ILOAD diff // Push diff IFLT lt // If diff < 0, goto lt BIPUSH 0x4E // else, print "N" OUT GOTO return lt: BIPUSH 0x59 // (diff < 0) OUT // print "Y" return: ILOAD diff // Push diff IRETURN // Return (value of diff will be pushed onto the // top of the invoking method's stack) .end-method // --- End program ---
Because you might not want it to. The IN instruction reads from a device that is strictly an input device, and the OUT instruction writes to a device that is strictly an output device. If you want to display what the user is typing while running your IJVM program, you will need to cause this to happen by doing an explicit OUT instruction for each character that you read and want to echo.
It seemed reasonable that the control store should begin execution at control store location 0x000. This happens to be the location for the NOP IJVM instruction, so we begin our interpretation loop there. We then proceed to "Main1" to increment the PC, and begin a fetch from this incremented address (the fetch happens at the end of the clock cycle and the fetched value is not available from MBR until the end of the next cycle). Since we want execution of the ISA-level program to begin at 0x00000000, we have accomplished this by pre-setting the PC to 0xFFFFFFFF and allowing the initial increment along with an initial NOP, this "wastes" one micro-instruction every time we RESET the machine.
We could have used the same approach (with PC 0xFFFFFFFF) and started at "Main1" instead of "nop1", in the mic1ijvm.mal microprogram, but then we would have had to anchor "Main1" at some particular location which would then have to be well-known to the "hardware" designers. It seemed more reasonable to agree that the first micro-instruction to be executed would always be the one at control store location 0x000 which is fine for the mic1ijvm interpreter, and probably a reasonable design choice for many microprograms (opcode 0x00 is often a NOP instruction for many ISA-level designers).
Another approach would have been to designate some other location as the first micro-instruction and require that a "fetch" happen with PC 0x00000000 (and then another instruction to wait for the fetch into the MBR to complete) before entering the main loop. Since many microprograms have similar increment/fetch loops, we felt that it was reasonable to require that the PC be started at 0xFFFFFFFF as part of the hardware design.
Feedback on this item is particularly welcomed (rayo@ontko.com).
In order for "goto(MBR)" to work when MBR contains the opcode for NOP, the control store location 0x000 must contain the microinstruction "nop1". This means that "Main1" must be at some other location not used by one of the other opcodes, and that if we wanted to start our execution of the microprogram at "Main1" this location would have to be well known to the hardware designers for the mic1 hardware. Since we may want to be able to run other microprograms on the mic1 architecture, it seemed reasonable to pick control store location 0x000 as our starting address for all such microprograms.
While any address would do as a starting location, this one seemed particularly appropriate, since many microprograms might employ "goto(MBR)" and many ISA-level languages might employ opcode 0x00 as a NOP instruction.
Feedback on this item is particularly welcomed (rayo@ontko.com).
Because the simulator output device (a Java TextArea) doesn't handle backspace characters. It's hard to decide whether this is a feature or a limitation. The "Standard Out" window is not meant to simulate a cursor-addressable screen display, but only a serial device that displays the next printable character it receives.
We could modify the behavior of the standard output device to interpret the backspace character as "delete the last character (if any) on the current line in the output buffer", but we feel this would be more confusing an implementation of a simple output device than one which ignores non-printable characters.
The MAR stores a number that is a word address (not a word-aligned byte-address), and the PC stores a byte address. This is specified in Chapter 4, of Tanenbaum. When the MAR is expressed on the memory bus, it is at that point shifted by two bits.