Extended Brookshear Machine Emulator
This is an emulator for the machine described in Computer Science: An Overview, 10th edition, by J. Glenn Brookshear (Pearson Education, 2008). It is extended with three additional instructions and the machine is equipped with a bitmapped display.
The machine may be programmed by entering machine code into the emulator, or using assembly language.
This implementation copyright © 2008 University of Sussex and David Young
Main memory has 256 cells with addresses from 00 to FF (hex). Each cell holds 1 byte (8 bits) of data.
There are 16 registers, numbered from 0 to F (hex), each able to hold 1 byte of data.
The program counter is additional to the general-purpose registers, and holds a 1-byte memory address. It is incremented by 2 after each instruction, unless a jump has taken place.
Each instruction occupies 2 consecutive bytes of memory, and is written as 4 hex digits. The first digit represents the opcode; the other three specify registers, memory addresses or data. The first two digits are stored in the byte of memory with the lower address.
In the list below, r, s, t, x and y stand for hexadecimal digits. The instructions with opcodes 1 to C are from Appendix C of Brookshear's book. Those with opcodes D and E are based on extensions by Prof. I.K. Lundqvist of MIT. Those with opcodes 0 and F are Sussex additions.
No operation. Carry on to the next instruction. The data fields must all be F.
Load from memory (direct addressing). Copy the data at memory address xy into register r.
Load value (immediate addressing). Copy xy into register r.
Store (direct addressing). Copy the contents of register r into memory at address xy.
Move. Copy the contents of register r into register s.
Add as integers. Add the contents of register s to the contents of register t as twos complement integers. Put the result into register r.
Add as floats. Add the contents of register s to the contents of register t as floating point values. Put the result into register r. The floating point format is SEEEMMMM where S is the sign bit (1 for negative), EEE is the exponent in 3-bit excess format, and MMMM is the mantissa.
OR. Carry out the bitwise OR operation on the contents of register s and the contents of register t. Put the result into register r.
AND. Carry out the bitwise AND operation on the contents of register s and the contents of register t. Put the result into register r.
XOR. Carry out the bitwise exclusive or operation on the contents of register s and the contents of register t. Put the result into register r.
Rotate the contents of register r by x bits to the right. Update register r with the result.
Jump to memory location xy. That is, the program counter is set to xy just before the next instruction is executed. Note that this is really a special case of the next instruction.
Jump if equal. If the contents of register r equal the contents of register 0, jump to memory location xy.
Halt. Stop execution.
Load from memory (register indirect addressing). Copy the data from the memory location whose address is in register s. Place it in register r.
For example, if register s contains the value 86, and memory location 86 contains the value 7B, register r will be given the value 7B.
Store in memory (register indirect addressing). Copy the data from register r. Place it in the memory location whose address is in register s.
For example, if register r contains the value EC, and register s contains the value 41, the value EC will be placed in memory at address 41.
Jump to register address. Jump to the memory address stored in register t. That is, the contents of register t are copied to the program counter. Note that this is really a special case of the next instruction.
Jump to register address with test. The contents of register r are compared to the contents of register 0 using a test which depends on x. If the result of the test is true, a jump is made to the memory address stored in register t.
The allowable values of x and the corresponding tests are:
x Jump if - ------- 0 Rr == R0 (equal) 1 Rr != R0 (not equal) 2 Rr >= R0 (greater than or equal) 3 Rr <= R0 (less than or equal) 4 Rr > R0 (greater than) 5 Rr < R0 (less than)
The register values are treated as unsigned integers for the comparisons.
For example, if the command is FA4B, then if the value of register A is greater than the value of register 0, the program will jump to the instruction whose address is stored in register B.
Each column of the display gives a different view of the memory contents. All the entries in a row are different ways of showing the same underlying data, which is stored in memory at the address on the left.
The addresses to which the program counter points are highlighted.
The Binary column gives the view that is nearest to how the data are held in hardware. The Hex column is the most useful for interpreting the data as code: instructions and addresses. The remaining columns show how the data can be read if it is being used to represent characters or numbers. The numerical columns are particularly relevant if ADDI or ADDF operations are carried out on the data.
The data at a memory location may be changed by editing any of its representations. Click in the relevant box, and edit the text as you would any other editable text display. Press return or the up or down arrow, or click in another part of the display to complete editing. Provided the new entry is valid, memory will be updated and the other entries in the row will change accordingly. Floating point numbers will be rounded to the nearest value that can be represented.
If an entry can be interpreted as a machine instruction, a textual representation will be shown on the right against even addresses only, since each instruction occupies two memory locations. (If the program counter is set to an odd value, the text is shown against odd memory locations.) The text can be switched between an informal description of the action carried out by the instruction and assembly language (as defined here) using the radio buttons at the bottom of the panel.
Thirty-two memory locations are displayed. To move through memory, use the slider at the left of the display, or press the Page Up or Page Down key when a memory cell has been selected.
The Reset button enters 00 into all memory locations.
The Save in file button allows you to store the current contents of memory in a file. Only memory locations which contain non-zero values are stored. The file that is written is a plain text file, so it is a good idea to give it a .txt extension. You can view and edit it with a text editor such as the Matlab editor, WordPad, Notepad, Emacs or vi. The addresses and data are recorded in hex, and the instruction text is also recorded as either informal descriptions or assembly language, as set by the radio buttons.
The Load from file button allows you to select a file containing machine code in hex. Its contents are loaded into memory. Only addresses that are specified in the file are updated, so it may be sensible to reset the memory before loading a file. The file can be one that has been previously saved with the Save button, or one that you have written or modified with a text editor.
The format of a memory file is as follows. The file is structured into lines which are loaded in order. Each line can have three parts:
ADDRESS DATA COMMENT
Each of these may be omitted. Any white space (spaces or tabs) is ignored.
ADDRESS has the format hh: where hh is two hexadecimal digits (0-9, A-F or a-f). Values from the DATA field are loaded into memory starting from the address specified. If ADDRESS is omitted then:
- if this is the first line of the file, the address is set to 00
- otherwise, if some data were loaded by the previous line, the address is set to 1 greater than the address of the last byte loaded
- otherwise, the address is the same as for the previous line
DATA is an even number of hexadecimal digits. Each pair of digits specifies the data for a byte of memory. The address given is used for the first pair, and is incremented by 1 for each subsequent pair.
COMMENT has the format //aaaa where aaaa is any text. The comment is ignored.
A simple memory file for a program that is to be loaded from address 0 need therefore only contain the data. For example, a program that causes the machine to count up indefinitely from 0 could be stored in a file containing just the following single line of text:
2000 2101 5001 B004
The spaces are not necessary, and the entries could be spread over different lines. However, the program is clearer if stored with comments and addresses:
// Brookshear Machine memory 00: 2000 // Load register 0 with value 00 02: 2101 // Load register 1 with value 01 04: 5001 // Put reg 0 + reg 1 (ints) in reg 0 06: B004 // Jump to 04
BM programs may be written in assembly language, described here. The Assemble and load button in the main memory panel allows you to specify a file containing an assembly language program. This will be read and if it compiles without error the machine code will be loaded into memory. If an error is found a message will be displayed and memory left unchanged.
An assembly language program may also be compiled to machine code which is stored in a file without affecting memory. The Assemble to file button carries this out. The file produced may be subsequently loaded into memory using the "Load from file" button.
The assembly language description may be accessed using the Assembler help button.
The registers are displayed in the same way as the main memory. However, the fields for the general purpose registers can not be edited: the only way to change their contents is to run a program.
The program counter is only displayed in hex. Its value can be edited to change the memory locations from which the next instruction will be taken.
The Reset button sets all the registers and the program counter to 00. It does not affect main memory.
The List instructions button displays a condensed list of the machine instructions for quick reference.
The Help button displays the information you are reading.
The Assembly help button displays assembly language information.
Error messages are displayed in the area below the buttons.
The Step button executes the next instruction (the instruction at the address in the program counter).
The Continue button starts the machine running from the address currently in the program counter, and with the values currently in the registers. Execution continues until a HALT instruction is executed, an error occurs, or the "Halt" button is pressed.
The Reset & Run button resets the registers and program counter to 00 and then starts the machine running as for the "Continue" button.
When the machine is running, the "Reset & Run" becomes a Halt button. This stops the machine after the current instruction has executed. The program may be resumed from the same point by pressing "Continue".
The Speed slider controls the time between execution steps when the machine is running. It may be adjusted during execution.
This particular machine has a bitmapped display attached to it. When the display has been activated by pressing the Display on/off button, it shows an image generated from part of main memory.
Memory locations 80 to FF are used for the display. (This does not prevent their being used for programs or data.) Each bit in this part of memory is mapped to a pixel of the display. If the bit is 0, the pixel is black, if the bit is 1, the pixel is white. The display has 32 rows and 32 columns.
Bits are mapped to pixels in the following way. Take the bits in the order they appear in the memory display, starting with the leftmost bit at address 80. Take the pixels starting from the top-left corner of the display, going along each row of the display in turn. Associate each bit with the corresponding pixel.
Alternatively, number the bits in a byte like this: 76543210. That is, bit 7 is the leftmost bit and bit 0 is the rightmost bit. Identify a single bit in memory as hh:n where hh is the address of a byte and n is the number of a bit within that byte. Memory bits are laid out below in the way that corresponds to pixels in the display.
80:7 80:6 80:5 80:4 80:3 80:2 80:1 80:0 81:7 ... 83:2 83:1 83:0 84:7 84:6 ... ... 87:1 87:0 88:7 88:6 ... ... 8B:1 8B:0 ... ... ... ... F8:7 F8:6 ... ... FB:1 FB:0 FC:7 FC:6 FC:5 ... FE:0 FF:7 FF:6 FF:5 FF:4 FF:3 FF:2 FF:1 FF:0
The display is updated whenever memory is changed, either by a program or by editing the memory display, so it is possible to see graphically the effects of setting different values.
The current display can be saved to an image file in a variety of formats, by clicking on the Save display button, selecting a format and entering a file name in the pop-up window.