A virtual computer/CPU written in python, with an assembler and debugger.
Has a 16 bit CPU with 4 all-purpose registers.
Assembly language based loosely on 8086.
Can use 64K 2-byte memory locations.
python Main.py <sourcefile> [--debug]
Example:
python Main.py programs/prog0.iasm --debug
Running without the debugger currently just runs the program and then shows the variables and their final value.
// Calculate Number1 * Number2 and store result in Product
DATA // Start the DATA block
Number1: 7, word[1] // Reserve a location in memory to hold a number and initialize it to 7
Number2: 6, word[1] // A second variable with value 6
Product: 0, word[1] // A third veriable
Stack: 0x5555, word[32] // Reserve 32 words for the stack (not used in this program)
CODE // Start the CODE block
Main: // Entry point
JMP DummyLabel // Jump to DummyLabel
PUSH AX // PUSH the value of register on the stack (this code is never reached)
POP AX // POP a value from the stack and place it in register AX
DummyLabel: // Define a label
MOV AX, Number1 // Load address of Number1 in AX
MOV BX, [AX] // Load value of Number1 in BX
MOV AX, Number2 // Load address of Number2 in AX
MOV CX, [AX] // Load value of Number2 in CX
MUL CX, BX // CX = CX * BX
MOV AX, Product // Load address of Product in AX
STOR CX, [AX] // Store value of CX in memory location of Product
HLT // Halt the CPU
- Implement NOT
- Implement/Fix CF and OF
- Check setting of CPU Flags
- Test on Linux
- Test on Mac
- Scope variables to the file they are defined in
- Add support for 'System-Code': code that always runs at full speed in the debugger
- Add support for #define to preprocessor
- Implement array initializers to assembler
- Add new screenshot(s)
None
Tested with Python 3.7.0, but should also work with 3.5.0 and above.
Developed on Windows 10.
Runs in terminal (CMD.exe), powershell terminal and mintty (Git Bash).
Tested on a Raspberry PI 3B+ using PuTTY.
Contains:
- sourcecode
- preprocessor result
- assembler result, this is the intcode for the CPU
A 64 x 16 8 color text display.
Preprocesses source files, preparing them for the assembler. This includes processing IMPORT statements.
Can convert preprocessed assembler code into intcode.
Not implemented yet
Contains:
- Memory: 64K 2-byte words
- CPU
The computer can load a program into memory and use the CPU to execute it
Has access to the computers memory.
Can execute intcode instructions from memory.
Defines labels/variables.
Defines a CPU instruction.
Defines a patrameter to an instruction.
Displays memory contents, CPU registers and flags, current intruction and variables.
Allows executing a program step by step.
Misc definitions used by all modules.
Used by the debugger to write to the screen.
Used by the debugger to process keystrokes.
The CPU is loosely based on the 8086 and so are the mnemonics.
A program must have a label called Main. This is where execution will start.
A program must also have a variable called Stack. The stackpointer will be pointed to that variable when execution starts.
There is no guard against stack overflow, so make the stack large enough.
The assembler currently supports 3 data types: word, byte and string.
Examples:
TheAnswer: 42, word[1]
Greeting: "Hello world", string[30]
OtherNumber: 7, byte[1]
Stack: 0x5555, word[32]
Currently there is no variable scope; all variables are global.
Variable names are truncated to 12 characters.
The assembler adds 2 instructions to the beginning of the program:
MOV SP, Stack // Initialize the stackpointer
JMP Main // Jump to Main
IP : Instruction pointer
SP : Stack pointer
IX : Index register
AX
BX
CX
DX
SF : Sign flag
OF : Overflow flag
ZF : Zero flag
CF : Carry flag
PF : Parity flag
IF : Interrupt flag (Not used yet)
Unconditional jump
JMP Labelname
Move values to registers
MOV AX, BX
MOV BX, 1234
MOV CX, 0xABBA
MOV DX, 0b11110000
MOV AX, [BX]
MOV BX, Labelname
Set memorylocation to a value
STOR 123, [AX]
STOR BX, [CX]
Calls a subroutine.
CALL Subroutine
Return from a subroutine.
RET
Push a value on the stack
PUSH AX
PUSH BX
Pop a value from the stack
POP BX
POP AX
Push the flags to the stack
PUSHF
Pop the flags from the stack
POPF
Addition.
ADD AX, 7 // AX = AX + 7
ADD BX, CX // BX = BX + AX
ADD CX, 0x10 // CX = CX + 16
Subtraction.
SUB AX, 7 // AX = AX - 7
SUB BX, CX // BX = BX - AX
SUB CX, 0x10 // CX = CX - 16
Multiplication.
MUL AX, 7 // AX = AX * 7
MUL BX, CX // BX = BX * AX
MUL CX, 0x10 // CX = CX * 16
Integer division. Remainder is in DX.
DIV AX, 7 // AX = AX / 7
DIV BX, CX // BX = BX / AX
DIV CX, 0x10 // CX = CX / 16
CMP DX, 0
JZ NoRemainder
Bitwise AND.
AND AX, 7
AND BX, CX
AND CX, 0b10101010
Bitwise OR.
OR AX, 7
OR BX, CX
OR CX, 0b10101010
Bitwise XOR.
XOR AX, 7
XOR BX, CX
XOR CX, 0b10101010
Decrement.
DEC AX // AX = AX - 1
Increment.
INC BX // BX = BX - 1
Bit shift left.
SHL AX
Bit shift right.
SHR BX
Jump if ZF is set.
CMP AX, 10
JZ Loop
Jump if ZF is not set.
CMP AX, 10
JNZ Loop
Jump if OF is set.
MUL AX, 10
JO Loop
Jump if OF is not set.
MUL AX, 10
JNO Loop
Jump if CF is set.
ADD AX, 10
JC Loop
Jump if CF is not set.
ADD AX, 10
JNC Loop
No operation. Does nothing.
NOP
Halts the CPU.
HLT
The value for the parameter is provided in the instruction:
MOV AX, 5
MOV AX, 0xABBA
MOV BX, 0b10101010
MOV CX, Variablename
When the parameter is a register
MOV AX, BX
CMP AX, CX
NOT DX
To reference memory, the address must be loaded in one of the registers, like so:
MOV AX, VariableName // Loads the address of 'VariableName' in AX.
Then that memory location can be accessed:
MOV BX, [AX] // Load the value at memorylocation [AX] in BX.
STOR CX, [AX] // Store the value of CX at the memory location AX is pointing at.
MOV BX, [AX+IX] // Load the value at memorylocation [AX+IX] in BX.
STOR CX, [AX+IX] // Store the value of CX at the memory location AX+IX is pointing at.
- Add support for floating point instructions.
- Add support for input.
- Write a library with common functions.
- Add a compiler that can compile (transpile ?) a higher level language to assembly sourcecode.