Moo CPU

Copyright (C) 2021 - Hammer Data Systems, LLC

Moo CPU uses FLTK 1.3.3 (C) 1996-2021 - Bill Spitzak and others.

Please take no undeserved credit and assign me absolutely no blame. Otherwise, this a free program, download, redistribute, and use at your own risk.

Download
Overview
Instruction Types
User Interface
Instructions Reference
Addressing Modes

Download

Moo CPU and Bits

Moo CPU is a simplified CPU simulator with a built-in assembler for the very fictional MOO Cpu Assembly language. It is intended to be an aid in learning the basics of assembly language and the basics of computer operation "under-the-hood." Bits is a simple converter between binary, hexadecimal, and decimal number formats. The download includes both programs for both Windows and Linux.

Overview

All CPUs execute instructions. These very simple instructions are called "machine language". They are entirely numeric and are not human readable (by normal humans). The source code to produce machine language is called "assembly language". Assembly is human readable (just barely). It "assembles" into machine language with a one-for-one correspondence between an assembly language instruction and a machine language instruction. Higher level languages compile on instruction to many machine language instructions.

A CPU starts reading memory at an address and finds a machine language instructon. Following the instruction, there are often several bytes of parameters for that instruction. As the CPU executes the instruction, it advances a special register, (a tiny amount of memory built onto the chip) called a program counter, to "point at" the memory being read and interpreted.

Typical machine language instructions include moving a value from memory to a general purpose register or back, doing math on values in memory or in registers, making comparisons between memory and registers, or affecting the program counter to read and execute a different portion of memory.

Machine language instruction must be told how to address memory or registers. This is referred to as an "addressing mode" or "scheme". Typically the addressing modes include (among many others):

immediate mode - The value to be moved is right there after the instruction

absolute mode - The memory address where the value is is specified after the instruction

indirect mode - The address or register after the instruction holds the address used to find the value.

The syntax of the assembly langauge is used to specify the instruction and its addressing mode. Different chips, and different assemblers, often use differing syntaxes to specify the same addressing modes.

The Moo CPU emulator emulates in software a completely fictional CPU. The Moo CPU uses a very basic machine language with only 42 instructions. These instructions are intended to be typical of those used by a very simple CPU. The purpose of the emulator is to teach programmers already familiar with higher level languages the basics of assembly language and CPU operation.

The Moo CPU has 8 general purpose registers and 8 floating point registers. It has also has a stack pointer and a program counter. The registers are labeled A though H. The stack pointer is labeled "SP" and the program counter is labeled "CP". All of the registers, including the stack pointer and the program counter are 32 bit.


Instruction Types

The following instruction types are supported:

Load Instructions - move a value to a register

Store Instructions - move a value from a register

Math Instructions - add, subtract, multiply, and divide

Compare and Branch Instructions - Comnpare two values and change the program counter to a new address based on outcome of the comparison

Stack Instructions - Push and pop values on the hardware stack

Control Flow Instructions - Change the program counter to jump off to other portions of the program

Bitwise Instructions - Operations that affect the individual bits in a value

All of the Moo CPU instructions have zero, one, or two arguments. Add, for example has two arguments, a register specifier (A-H) and an address specifier. So to add 82 to the value in register A, you would write "load A #82". The "#" indicates immediate mode. To add the value at memory address 20020 to the value in register B, you would write "add B 20020". Because there is no "#" or parenthesis around the 20020, absolute mode is assumed. The add instruction finds the value in memory address 20020 and adds it to the value in register B, leaving the result in register B. If you have loaded register C with the address value 20020, you could accomplish the same thing by specifying

load C #20020
add B (C)

The parentheses around "C" indicate that the add instruction should get the address out of C, then get the value to be added in from that address, leaving the result in register B.

Most of the instructions operate on full integers (32-bits, 4 bytes). Some instructions are duplicated to work on single bytes or on the floating point numbers. This is indicated by a "B" or an "F" preceeding the instruction name.

load A #22 ; load integer 22 into A
fload B #22.89 ; load floating point value 22.89 into floating point register B
bload C #65 ; load byte 65 (ASCII "A") into register C

The built in assembler also supports a handful operations that specify how to assemble the source code, rather than resulting in machine language instructions. These include the ability to set a portion of memory to a constant ascii string, declare labels in place of addresses, and notate code with comments.

The emulator also features 1 megabyte of main memory, which includes 2,000 bytes of video memory (addresses 0 - 1999) and 8,000 bytes of dedicated stack memory (addresses 2000-9999). The remaining memory (addresses 10000 - 1048575) is reserved for the user's program. The emulator includes a 80 column by 25 row ASCII based "video screen", and a simple text editor for loading, saving, and assembling Moo CPU source code.

Here is a simple "Hello World" Moo code example:

:hello                ; label the "hello world" constant
                      ; the first "." on the following line indicates that the rest of the line is a constant to be put in memory
.hello world.
jmp :begin            ; Jump over the two subroutines to the start of the main program

:index                ; label the indexing subroutine that calculates the X,Y position at which to print
mult B #80            ; multiply the B register (holding X) with 80 because there are 80 columns
add B A               ; add the A register into B to produce "address = (Y * 8) + X"
ret                   ; return to the calling routine. "call" pushes the return address on the stack. "ret" pops the address and jumps to it

:print                ; label the print subroutine. The address of the string is in A. The address at which to print is in B.
bload C (A)           ; load C with the value from the address that is held in A
bstore C (B)          ; store the value in C at the address that is held in B
add A #1              ; add 1 to the address in A, moving along the NULL terminated string
add B #1              ; add 1 to the address in B
cmp C #0              ; compare the value in C with 0 (the NULL terminator)
ifne :print           ; If it is not 0, jump to the start of the print subroutine
ret                   ; return

:begin                ; label the start of the main routine
load A #10            ; put 10 in A, the X position
load B #5             ; put 5 in B, the Y position
call :index           ; calculate the video memory address from X and Y, leaving the result in B
load A #:hello        ; load the address of the "hello" constant string into A
call :print           ; call the print subroutine
end                   ; end of the program

The User Interface


The user interface is divided into several sections. In the upper left corner is the "CPU Status" section. This section has "lights" indicating different CPU conditions.


CPU Status


The "GT", "EQ", and "LT" lights indicate if the last comparison operation resulted in the number being less than, equal to, or greater than the compared number. The "ERR" light indicated that the CPU experienced a runtime failure and has stopped. The "Exec" light indicates that the CPU is executing a single instruction. Because this CPU runs in slow motion, this light will flicker on and off while the program is executing.


Registers


The register section displays the values in the 18 registers. The first 8 registers are general purpose registers that can only hold 32 bit integers. They are labeled "A" through "H". The next 8 registers are floating point registers. These can only hold 32 bit floating point numbers. Unlike the general purpose registers, these can not be used for indirect addressing. They are labeled "FA" through "FH". Note: while these are labeled with a leading "F", when referenced in Moo assembly language the leading "F" is omitted. The "SP" element shows the current value of the stack pointer. The "PC" element shows the current value of the program counter. This is the address in memory of the currently executing CPU instruction. All of the register fields are read-only and do not allow user editing.


Memory View

The memory view panel shows the memory of the pretend computer. It is divided into four columns. The first column displays the memory address, labeled from 0 to 1048575. The second column show the byte value at that memory address. The third and fourth columns show the source value of any instructions at that memory cell. The video memory is colored cyan. The stack memory is colored blue. Depending on mode, accessed memory will be highlighted in yellow. In SP, PC, MEM, and VID modes, the view will automatically scroll to show memory being accessed. When in USER mode, the view can be scrolled by clicking in the view and using the scrollbar, Page Up, Page Down, Home, and End keys. Page Up and Home scroll toward lower addresses. Page Down and End scroll toward higher addresses.


Command Buttons


The command buttons are the primary means of operating the Moo CPU.

Edit - The Edit button brings up the simple text editor. From the text editor, you can load, save, edit, and assemble your Moo CPU assembly source code. This is typically the first button you will want to press.

Assemble - The Assemble button assembles the source code into a machine language that the Moo CPU is able to execute. If successful, it also places the resulting program into the CPU's memory at address 10000 and creates the source view (see: Source View) with the execuatable source on numbered lines.

Execute - The Execute button is inactive until a source program has been assembled. When pressed it executes the Moo program, waiting for the number of milliseconds specified in the "Delay" input next to the button. As the program is executed, depending on view mode, the memory will be highlighted and track activity, the registers will change, the source view will update and scroll, and lights in the status area may update. The program will continue to execute until it reaches an "end" instruction, or the user pressses the "Stop" button.

Step - The Step button is inactive until a source program has been assembled. It is also inactive if a program is currently executing. This button allows the user to execute a program one instruction at a time.

Stop - The Stop button is inactive unless a program is executing. It stops the program. The program can then be resumed where it was stopped using the Resume button.

Resume - The Resume button is inactive until a running program has been stopped with the Stop button or the program has been stepped using the Step button. It continues running the program where it was stopped.

User, SP, PC, MEM, VID - These buttons indicate the monitoring mode for the Memory View. The User mode allows the user to scroll the memory shown using the scrollbar, or the Page Up, Page Down, Home, and End key. The SP mode shows the memory dedicated to the stack and tracks memory accesses in the stack's range. The PC mode shows and tracks the memory address in the program counter. This is the address of the currently executing instruction. The MEM mode tracks the last memory address to be read or written by the CPU. The VID mode shows and tracks memory accesses in the portion of memory dedicated to the video screen.


Screen


The screen shows the memory from 0 to 1999. It is 80 columns by 25 rows. The bytes in that memory range are interpreted as ASCII and displayed on the screen in a position corresponding to that memory address, starting with the top left corner. So, memory 0 throgh 79 would be the first row. Memory 80 through 159 would be the second row. Etc. Placing 65 in memory 1000, would put a capital "A" (ASCII 65) in the center of the screen.

The screen has some silly controls along the bottom edge. The first is a button labeled "C". This cycles the text color between green, amber and white. The next button, labeled "R", toggles the screen between normal and reverse mode. In reverse mode the background (usually black) gets the text color and the text becomes black. There are two knobs. The leftmost controls brightness. The other controls contrast.


Source View


The source view shows the source code that resulted in executable machine language after it has been assembled. The line that is currently executing is shown and highlighted, otherwise the view scrolls using the scrollbar or navigation keystrokes. Source code that does not result in executable code (constants, labels, and comments) is not shown.


Instructions Reference

Load and Store

InstructionNameUsageDescription
loadInteger Loadload reg addrLoads an integer value from addr to reg.
bloadByte Loadbload reg addrLoads a byte value from addr to reg.
floadFloating Point Loadfload reg addrLoads a floating point value from addr to reg.
storeInteger Storestore reg addrStores an integer value from reg to addr.
bstoreByte Storebstore reg addrStores a byte value from reg to addr.
fstoreFloating Point Storefstore reg addrStores a floating point value from reg to addr.

Math

InstructionNameUsageDescription
addInteger Additionadd reg addrAdds the integer value at addr to the value in reg, leaving the result in reg.
faddFloating Point Additionfadd reg addrAdds the floating point value at addr to the value in reg, leaving the result in reg.
subInteger Subtractionsub reg addrSubtracts the integer value at addr from the value in reg, leaving the result in reg.
fsubFloating Point Subtractionfsub reg addrSubtracts the floating point value at addr from the value in reg, leaving the result in reg.
multInteger Multiplicationmult reg addrMultiplies the integer value at addr with the value in reg, leaving the result in reg.
fmultFloating Point Multiplicationfmult reg addrMultiplies the floating point value at addr with the value in reg, leaving the result in reg.
divInteger Divisiondiv reg addrDivides the integer value in reg by the value at addr, leaving the result in reg.
fdivFloating Point Divisionfdiv reg addrDivides the floating point value in reg by the value at addr, leaving the result in reg.
modInteger Modulomod reg addrResults in integer modulo of the reg by the value at addr, leaving the result in reg.
cvtInteger Conversion to Floatcvt reg addrConverts an integer value into a float value, leaving the result in a float register.
fcvtFloat Conversion to Integerfcvt reg addrConverts a float value into an integer value, leaving the result in a integer register.

Compare and Branch

InstructionNameUsageDescription
cmpInteger Comparisoncmp reg addrCompares the integer value in reg with the value in addr, setting the comparison flag to LT, EQ, or GT. The comparison flag will remain set until the next comparison operation
fcmpFloating Point Comparisonfcmp reg addrCompares the floating point value in reg with the value in addr, setting the comparison flag to LT, EQ, or GT. The comparison flag will remain set until the next comparison operation
ifeqIf Equalifeq addrSets the program counter (jumps) to addr if the comparison flag is set to EQ.
ifltIf Less Thaniflt addrSets the program counter (jumps) to addr if the comparison flag is set to LT.
ifgtIf Greater Thanifgt addrSets the program counter (jumps) to addr if the comparison flag is set to GT.
ifneIf Not Equalifne addrSets the program counter (jumps) to addr if the comparison flag is not set to EQ.

Stack

InstructionNameUsageDescription
pushInteger Pushpush regPushes the integer value in reg onto the stack.
bpushByte Pushbpush regPushes the byte value in reg onto the stack.
fpushFloating Point Pushfpush regPushes the floating point value in reg onto the stack.
popInteger Pushpop regPops the top integer value off of the stack and places it in reg.
bpopByte Pushbpop regPops the top byte value off of the stack and places it in reg.
fpopFloating Point Pushfpop regPops the top floating point value off of the stack and places it in reg.
stackStack RegistersstackPushes all of the registers, both integer and floating point, onto the stack to preserve them.
stackRestore RegistersrestPops 8 floating point values and 8 integers off of the stack and places them in the corresponding registers.

Control Flow

InstructionNameUsageDescription
jmpJumpjmp addrSets the program counter to addr, jumping control flow to that address.
callCall Subroutinecall addrCalls a subroutine by pushing the program counter onto the stack then setting the program counter to the address of the subroutine.
retReturn from SubroutineretPops an addr from the stack and "returns" to that address.
endEnd ProgramendEnds a program. This must be the last instruction in the program and must appear.
exitExit ProgramendTerminates execution of a program. This can appear anywhere in the program and is optional.

Bitwise

InstructionNameUsageDescription
orInteger Bitwise ORor reg addrOr the bits in reg with the bits in addr, leaving the result in reg. (0010 OR 0001) = 0011
andInteger Bitwise ANDand reg addrAnd the bits in reg with the bits in addr, leaving the result in reg. (0010 AND 0011) = 0010
xorInteger Bitwise Exclusive ORxor reg addrExclusive or the bits in reg with the bits in addr, leaving the result in reg. (0010 XOR 1010) = 1000
notInteger Bitwise NOTnot reg addrNot the bits in addr, leaving the result in reg. (NOT 0010) = 1101
shiftlInteger Bitwise Shift Leftshiftl reg addrShift the bits in reg to left by the value in addr. (0001 shiftl 2) = 0100
shiftlInteger Bitwise Shift Rightshiftr reg addrShift the bits in reg to right by the value in addr. (1000 shiftr 2) = 0010

Assembler

InstructionNameUsageDescription
.Null Terminated String Constant.StringPlace a string constant in memory at the current address. String constants must appear at the start of the program, before any executable instructions. A label may appear before a string constant and that label may be used to refer to the address of the string.
#BByte Value#B 12Place a byte value at the current assembling address.
#IInteger Value#I 3271Place an integer value at the current assembling address.
#FFloat Value#F 12.928Place a float value at the current assembling address.
#(n)Reserve Memory#(100)Reserve n bytes of memory at the current assembling address
:Address Label:LabelPlace a label at the address where it appears. Labels can then be used by other instructions to refer to that address. Labels should only contain alphanumeric characters, the underscore character, and the initial colon.
;Comment;CommentComments can appear either at the start of a line, or after an instruction and its arguments. The comment continues until the end of the line.

Addressing Modes

ModeNameUsageDescription
#Immediate#NumberImmediate addressing gets the value for the instruction from the number immediately following the pound sign. So, "load A #82" will load the integer 82 into the register A. Floating point instruction require that the number contain a decimal point, even if there is no fractional value. So, "fload B #37.0" will load the floating point value 37.0 into floating point register B.
A-HRegisterA-HRegister addressing gets the value (integer or floating point depending on the operation) from the named register. There are 8 general purpose (integer) registers and 8 floating pointer registers, named in code as A, B, C, D, E, F, and G. So, "load A B" will load the value in B into A.
NumberAddress0-1048576Address addressing gets the value from the memory address. The address can either be specified numerically (i.e.: "load A 20000") or can use a labeled address (i.e.: load A :MyAddress").
(...)Vector(addr)Vector addressing gets an address value from a register or an address and then gets the value at that address. So, "load A (B)" will get the memory address that is in register B, get the value from that address, and place that value in register A. "load A (20000)" will get the address that is in memory address 20000 and then get that value in that memory address and place it in register A.
SPStack PointerA-HAddresses the value in the special Stack Pointer integer register. This mode is read only and can be used to place the value of the stack pointer into a general purpose register. By doing so, a subroutine can stack its local variables, and then address their values without popping them off of the stack.
NumberAddress0-1048576Address addressing gets the value from the memory address. The address can either be specified numerically (i.e.: "load A 20000") or can use a labeled address (i.e.: load A :MyAddress").