项目作者: paulmooreparks

项目描述 :
Maize Virtual CPU implemented on Tortilla CPU Emulation Platform
高级语言: C#
项目地址: git://github.com/paulmooreparks/Tortilla.git
创建时间: 2016-09-13T16:06:01Z
项目社区:https://github.com/paulmooreparks/Tortilla

开源协议:Apache License 2.0

下载


The Maize Virtual CPU

Implemented on the Tortilla Emulation Platform

UPDATE UPDATE UPDATE
This version of the project is no longer being updated.
Please see the new version implemented in C++ instead.

This project implements a 64-bit virtual CPU called “Maize” on a library that enables the creation of virtual CPUs (called “Tortilla”).
See the file Maize.txt for more details on the Maize CPU assembly
language and the simple operating system that runs on it.

The near-term goal is to implement a set of devices to bridge from the virtual CPU environment to the host machine, create a “BIOS” layer
above the virtual devices, implement a simple OS and a subset of Unix/Linux system calls (interrupt $80),
and finally port a C/C++ compiler (likely Clang or GCC) that will generate Maize machine code.

How To Use Maize

Maize is currently implemented in .NET 5, which means it will run on Windows, Linux,
and macOS. It also means it’s slower than I’d like it to be, despite my attempts at squeezing as much performance out of .NET as I could.

To compile a Maize assembly file (like HelloWorld.asm),
compile and run the mazm project, providing the path to the
assembly file as the command-line parameter.

  1. mazm /path/to/HelloWorld.asm

The above command will output HelloWorld.bin into the same path as HelloWorld.asm.

To run a Maize binary, compile and run the maize project, providing
the path to the binary in the -img parameter:

  1. maize -img /path/to/HelloWorld.bin

Project Status

It’s very early days for Maize, so don’t expect too much in the way of application usability. So far, I’ve enabled a basic
text-mode console for input and output. Next, I’ll start creating a file-system device. In the future I plan to port Clang or GCC to
work with Maize binaries so that I can eventually port Linux to the virtual CPU.

In the short term, I’m implementing a very basic OS over a simple BIOS (core.asm).
It will provide a basic character-mode CLI to allow building and running simple Maize
programs from within the virtual CPU environment.

I’ll eventually port Maize to a much lower-level language, likely either C++ or Rust. I started out with .NET and C# because I wanted
to play with .NET Core, and that bought me immediate multi-platform support and a lot fewer headaches for writing code quickly and
for implementing devices.

Hello, World!

Here is a simple “Hello, World!” application written in Maize assembly, targeting the basic OS I’ve written and the
system calls it implements.

  1. INCLUDE "core.asm"
  2. INCLUDE "stdlib.asm"
  3. ; The CPU starts execution at segment $00000000, address $00001000,
  4. ; so we'll put our code there.
  5. LABEL hw_start $00001000
  6. ;******************************************************************************
  7. ; Entry point
  8. ; The AUTO parameter lets the assembler auto-calculate the address.
  9. LABEL hw_string AUTO
  10. LABEL hw_string_end AUTO
  11. hw_start:
  12. ; Set stack pointer. The back-tick (`) is used as a number separator.
  13. ; Underscore (_) and comma (,) may also be used as separators.
  14. LD $0000`2000 S.H0
  15. ; Set base pointer
  16. LD S.H0 S.H1
  17. ; If you don't want to bother setting the stack and base pointers, they're
  18. ; already set to $FFFF`F000 by default, which should give you plenty of space.
  19. ; The basic ABI is for function arguments to be placed, from left to
  20. ; right, into the G, H, J, K, L, and M registers. Any additional parameters
  21. ; are pushed onto the stack.
  22. ; Get string length.
  23. LD hw_string G ; Load the local pointer (current segment) to the string into G register
  24. CALL stdlib_strlen ; Call stdlib function
  25. LD A J ; Return value (string length) is in A. Copy this to J for the write call
  26. ; Write string
  27. ; The kernel also implements a subset of Linux syscalls.
  28. ; The syscall number is placed into the A register, and the first
  29. ; six syscall arguments are placed, from left to right, into the
  30. ; G, H, J, K, L, and M registers. Any remaining arguments are
  31. ; pushed onto the stack.
  32. LD G H.H0 ; Load the local pointer (current segment) to the string into H.H0 register
  33. CLR H.H1 ; We're running in segment zero
  34. LD $01 A ; Load syscall opcode $01 (write) into A register
  35. LD $01 G ; Load file descriptor $01 (STDOUT) into G register
  36. INT $80 ; Call interrupt $80 to execute write syscall
  37. ; "Power down" the system, which actually means to exit the Maize CPU loop and return to the host OS.
  38. LD $A9 A ; Load syscall opcode $A9 (169, sys_reboot) into A register
  39. LD $4321FEDC J ; Load "magic" code for power down ($4321FEDC) into J register
  40. INT $80
  41. ;******************************************************************************
  42. ; This label points to the start of the string we want to output.
  43. hw_string:
  44. STRING "Hello, world!\0"

Instruction Description

Numeric values are represented in Maize assembly and Maize documentation in binary, decimal, or
hexadecimal formats. The % character precedes binary-encoded values, the # character precedes
decimal-encoded values, and the $ character precedes hexadecimal-encoded values.

  1. %00000001 binary value
  2. #123 decimal value
  3. $FFFE1000 hexadecimal value

The underscore, back-tick (`) and comma (,) characters may all be used as numeric separators in
all encodings.

Examples:

  1. %0000`0001
  2. %1001_1100
  3. #123,456,789
  4. $0000_FFFF
  5. $FE,DC,BA,98
  6. $1234`5678
  7. #123,456_789`021

Instructions are variable-length encoded and may have zero, one, or two parameters. An instruction
opcode is encoded as a single eight-bit byte defining the opcode and instruction flags, and each
instruction parameter is defined in an additional byte per parameter. Immediate values are encoded
following the opcode and parameter bytes in little-endian format (least-significant bytes are
stored lower in memory).

When an instruction has two parameters, the first parameter is the source parameter, and the second
parameter is the destination parameter.

Example: Load the value $01 into register A.

  1. LD $01 A

Example: Encoding of “LD $FFCC4411 D”, which loads the immediate value $FFCC4411 into register D.
$41 is the opcode and flags for the instruction which loads an immediate value into a register.
$02 is the parameter byte specifying a four-byte immediate value as the source parameter. $3E is
the parameter byte specifying the 64-bit register D as the destination parameter. The bytes
following the parameters bytes are the immediate value in little-endian format.

  1. $41 $02 $3E $11 $44 $CC $FF

Immediate values may used as pointers into memory. This is represented in assembly by the ‘@’
prefix in front of the immediate value.

Example: Load the 64-bit value at address $0000`1000 into register B.

  1. LD @$0000`1000 B

Register values may be used as pointers into memory by adding a ‘@’ prefix in front of the
register name.

Example: Store the value $FF into the byte pointed at by the A.H0 register:

  1. ST $01 @A.H0

Example: Load the quarter-word located at the address stored in D.H0 into sub-register Z.Q3:

  1. LD @D.H0 Z.Q3

Registers

Registers are 64-bits wide (a “word”) and are each divided into smaller sub-registers.

  1. A General purpose
  2. B General purpose
  3. C General purpose
  4. D General purpose
  5. E General purpose
  6. G General purpose
  7. H General purpose
  8. J General purpose
  9. K General purpose
  10. L General purpose
  11. M General purpose
  12. Z General purpose
  13. FL Flag register
  14. IN Instruction register
  15. PC Program execution register
  16. SP Stack register

Sub-registers

Sub-registers are defined as half-word (H), quarter-word (Q), and byte (B) widths. The full
64-bit value of a register (for example, register A) may be coded as “A” or “A.W0”. The register
value may also be accessed as separate half-word (32-bit) values, coded as A.H1 (upper 32 bits)
and A.H0 (lower 32-bits). The 16-bit quarter-words are similarly coded as A.Q3, A.Q2, A.Q1, and
A.Q0. Finally, the individual byte values are coded as A.B7, A.B6, A.B5, A.B4, A.B3, A.B2, A.B1,
and A.B0.

Shown graphically, the 64-bit value $FEDCBA9876543210 would be stored as follows:

  1. FE DC BA 98 76 54 32 10
  2. [B7][B6][B5][B4][B3][B2][B2][B0]
  3. [Q3 ][Q2 ][Q1 ][Q0 ]
  4. [H1 ][H0 ]
  5. [W0 ]

In other words, if the following instruction were executed:

  1. LD $FEDCBA9876543210 A

The value stored in register A could then be represented as follows:

  1. A = $FEDCBA9876543210
  2. A.W0 = $FEDCBA9876543210
  3. A.H1 = $FEDCBA98
  4. A.H0 = $76543210
  5. A.Q3 = $FEDC
  6. A.Q2 = $BA98
  7. A.Q1 = $7654
  8. A.Q0 = $3210
  9. A.B7 = $FE
  10. A.B6 = $DC
  11. A.B5 = $BA
  12. A.B4 = $98
  13. A.B3 = $76
  14. A.B2 = $54
  15. A.B1 = $32
  16. A.B0 = $10

Special-purpose Registers

There are four special-purpose registers.

  1. FL Flags register, which contains a bit field of individual flags. Flags in FL.H1 may only
  2. be set in privileged mode.
  3. IN Instruction register, set by the instruction decoder as instructions and parameters are
  4. read from memory. This register can only be set by the decoder.
  5. PC Program execution register, which is the pointer to the next instruction to be decoded
  6. and executed. This is further sub-divided into PC.H1, which is the current code segment,
  7. and PC.H0, which is the effective program counter within the current segment. PC.H1 may
  8. only be written to in privileged mode.
  9. SP Stack register, which is the location within the current segment at which the stack
  10. starts (growing downward in memory). This is further sub-divided into SP.H1, which is
  11. the base pointer, and SP.H0, which is the current stack pointer.

Flags

(more coming on this)

Execution

The CPU starts in privileged mode, and the program counter is initially set to segment $00000000,
address $00001000. When in privileged mode, the privilege flag is set, and instructions marked
as privileged may be executed. When the privilege flag is cleared, instruction execution and
memory access are limited to the current segment, and certain flags, registers, and instructions
are inaccessible. Program execution may return to privileged mode via hardware interrupts or via
software-generated (INT instruction) interrupts.

Opcode Bytes

Opcodes are defined in an 8-bit byte separated into two flag bits and six opcode bits.

  1. %BBxx`xxxx Flags bit field (bits 6 and 7)
  2. %xxBB`BBBB Opcode bit field (bits 0 through 5)

When an instruction has a source parameter that may be either a register or an immediate value,
then bit 6 is interpreted as follows:

  1. %x0xx`xxxx source parameter is a register
  2. %x1xx`xxxx source parameter is an immediate value

When an instruction’s source parameter may be either a value or a pointer to a value, then
bit 7 is interpreted as follows:

  1. %0xxx`xxxx source parameter is a value
  2. %1xxx`xxxx source parameter is a memory address

When bit 5 is set (%xx1x`xxxx), all eight bits are used to define the numeric opcode value.

Instructions

  1. Binary Hex Mnemonic Parameters Description
  2. ---------- --- -------- ---------- --------------------------------------------------------------------------------------------------------------------------------------
  3. %0000`0000 $00 HALT Halt the clock, thereby stopping execution (privileged)
  4. %0000`0001 $01 LD reg reg Load source register value into destination register
  5. %0100`0001 $41 LD imm reg Load immediate value into destination register
  6. %1000`0001 $81 LD regAddr reg Load value at address in source register into destination register
  7. %1100`0001 $C1 LD immAddr reg Load value at immediate address into destination register
  8. %0000`0010 $02 ST reg regAddr Store source register value at address in second register
  9. %0000`0011 $03 ADD reg reg Add source register value to destination register
  10. %0100`0011 $43 ADD imm reg Add immediate value to destination register
  11. %1000`0011 $83 ADD regAddr reg Add value at address in source register to destination register
  12. %1100`0011 $C3 ADD immAddr reg Add value at immediate address to destination register
  13. %0000`0100 $04 SUB reg reg Subtract source register value from destination register
  14. %0100`0100 $44 SUB imm reg Subtract immediate value from destination register
  15. %1010`0100 $84 SUB regAddr reg Subtract value at address in source register from destination register
  16. %1110`0100 $C4 SUB immAddr reg Subtract value at immediate address from destination register
  17. %0000`0101 $05 MUL reg reg Multiply destination register by source register value
  18. %0100`0101 $45 MUL imm reg Multiply destination register by immediate value
  19. %1000`0101 $85 MUL regAddr reg Multiply destination register by value at address in source register
  20. %1100`0101 $C5 MUL immAddr reg Multiply destination register by value at immediate address
  21. %0000`0110 $06 DIV reg reg Divide destination register by source register value
  22. %0100`0110 $46 DIV imm reg Divide destination register by immediate value
  23. %1000`0110 $86 DIV regAddr reg Divide destination register by value at address in source register
  24. %1100`0110 $C6 DIV immAddr reg Divide destination register by value at immediate address
  25. %0000`0111 $07 MOD reg reg Modulo destination register by source register value
  26. %0100`0111 $47 MOD imm reg Modulo destination register by immediate value
  27. %1000`0111 $87 MOD regAddr reg Modulo destination register by value at address in source register
  28. %1100`0111 $C7 MOD immAddr reg Modulo destination register by value at immediate address
  29. %0000`1000 $08 AND reg reg Bitwise AND destination register with source register value
  30. %0100`1000 $48 AND imm reg Bitwise AND destination register with immediate value
  31. %1000`1000 $88 AND regAddr reg Bitwise AND destination register with value at address in source register
  32. %1100`1000 $C8 AND immAddr reg Bitwise AND destination register with value at immediate address
  33. %0000`1001 $09 OR reg reg Bitwise OR destination register with source register value
  34. %0100`1001 $49 OR imm reg Bitwise OR destination register with immediate value
  35. %1000`1001 $89 OR regAddr reg Bitwise OR destination register with value at address in source register
  36. %1100`1001 $C9 OR immAddr reg Bitwise OR destination register with value at immediate address
  37. %0000`1010 $0A NOR reg reg Bitwise NOR destination register with source register value
  38. %0100`1010 $4A NOR imm reg Bitwise NOR destination register with immediate value
  39. %1000`1010 $8A NOR regAddr reg Bitwise NOR destination register with value at address in source register
  40. %1100`1010 $CA NOR immAddr reg Bitwise NOR destination register with value at immediate address
  41. %0000`1011 $0B NAND reg reg Bitwise NAND destination register with source register value
  42. %0100`1011 $4B NAND imm reg Bitwise NAND destination register with immediate value
  43. %1000`1011 $8B NAND regAddr reg Bitwise NAND destination register with value at address in source register
  44. %1100`1011 $CB NAND immAddr reg Bitwise NAND destination register with value at immediate address
  45. %0000`1100 $0C XOR reg reg Bitwise XOR destination register with source register value
  46. %0100`1100 $4C XOR imm reg Bitwise XOR destination register with immediate value
  47. %1000`1100 $8C XOR regAddr reg Bitwise XOR destination register with value at address in source register
  48. %1100`1100 $CC XOR immAddr reg Bitwise XOR destination register with value at immediate address
  49. %0000`1101 $0D SHL reg reg Shift value in destination register left by value in source register
  50. %0100`1101 $4D SHL imm reg Shift value in destination register left by immediate value
  51. %1000`1101 $8D SHL regAddr reg Shift value in destination register left by value at address in source register
  52. %1100`1101 $CD SHL immAddr reg Shift value in destination register left by value at immediate address
  53. %0000`1110 $0E SHR reg reg Shift value in destination register right by value in source register
  54. %0100`1110 $4E SHR imm reg Shift value in destination register right by immediate value
  55. %1000`1110 $8E SHR regAddr reg Shift value in destination register right by value at address in source register
  56. %1100`1110 $CE SHR immAddr reg Shift value in destination register right by value at immediate address
  57. %0000`1111 $0F CMP reg reg Set flags by subtracting source register value from destination register
  58. %0100`1111 $4F CMP imm reg Set flags by subtracting immediate value from destination register
  59. %1000`1111 $8F CMP regAddr reg Set flags by subtracting value at address in source register from destination register
  60. %1100`1111 $CF CMP immAddr reg Set flags by subtracting value at immediate address from destination register
  61. %0001`0000 $10 TEST reg reg Set flags by ANDing source register value with destination register
  62. %0101`0000 $50 TEST imm reg Set flags by ANDing immediate value with destination register
  63. %1001`0000 $90 TEST regAddr reg Set flags by ANDing value at address in source register with destination register
  64. %1101`0000 $D0 TEST immAddr reg Set flags by ANDing value at immediate address with destination register
  65. %0001`0001 $11 INC reg Increment register by 1.
  66. %0001`0010 $12 DEC reg Decrement register by 1.
  67. %0001`0011 $13 NOT reg Bitwise negate value in register, store result in register.
  68. %0001`0100 $14 OUT reg imm Output value in source register to destination port
  69. %0101`0100 $54 OUT imm imm Output immediate value to destination port
  70. %1001`0100 $94 OUT regAddr imm Output value at address in source register to destination port
  71. %1101`0100 $D4 OUT immAddr imm Output value at immediate address to destination port
  72. %0001`0101 $15 LNGJMP reg Jump to segment and address in source register and continue execution (privileged)
  73. %0101`0101 $55 LNGJMP imm Jump to immediate segment and address and continue execution (privileged)
  74. %1001`0101 $95 LNGJMP regAddr Jump to segment and address pointed to by source register and continue execution (privileged)
  75. %1101`0101 $D5 LNGJMP immAddr Jump to segment and address pointed to by immediate value and continue execution (privileged)
  76. %0001`0110 $16 JMP reg Jump to address in source register and continue execution
  77. %0101`0110 $56 JMP imm Jump to immediate address and continue execution
  78. %1001`0110 $96 JMP regAddr Jump to address pointed to by source register and continue execution
  79. %1101`0110 $D6 JMP immAddr Jump to address pointed to by immediate value and continue execution
  80. %0001`0111 $17 JZ reg If Zero flag is set, jump to address in source register and continue execution
  81. %0101`0111 $57 JZ imm If Zero flag is set, jump to immediate address and continue execution
  82. %1001`0111 $97 JZ regAddr If Zero flag is set, jump to address pointed to by source register and continue execution
  83. %1101`0111 $D7 JZ immAddr If Zero flag is set, jump to address pointed to by immediate value and continue execution
  84. %0001`1000 $18 JNZ reg If Zero flag is not set, jump to address in source register and continue execution
  85. %0101`1000 $58 JNZ imm If Zero flag is not set, jump to immediate address and continue execution
  86. %1001`1000 $98 JNZ regAddr If Zero flag is not set, jump to address pointed to by source register and continue execution
  87. %1101`1000 $D8 JNZ immAddr If Zero flag is not set, jump to address pointed to by immediate value and continue execution
  88. %0001`1001 $19 JLT reg If Negative flag is not equal to Overflow flag, jump to address in source register and continue execution
  89. %0101`1001 $59 JLT imm If Negative flag is not equal to Overflow flag, jump to immediate address and continue execution
  90. %1001`1001 $99 JLT regAddr If Negative flag is not equal to Overflow flag, jump to address pointed to by source register and continue execution
  91. %1101`1001 $D9 JLT immAddr If Negative flag is not equal to Overflow flag, jump to address pointed to by immediate value and continue execution
  92. %0001`1010 $1A JB reg If Carry flag is set, jump to address in source register and continue execution
  93. %0101`1010 $5A JB imm If Carry flag is set, jump to immediate address and continue execution
  94. %1001`1010 $9A JB regAddr If Carry flag is set, jump to address pointed to by source register and continue execution
  95. %1101`1010 $DA JB immAddr If Carry flag is set, jump to address pointed to by immediate value and continue execution
  96. %0001`1011 $1B JGT reg If Zero flag is clear and Negative flag is not equal to Overflow flag, jump to address in source register and continue execution
  97. %0101`1011 $5B JGT imm If Zero flag is clear and Negative flag is not equal to Overflow flag, jump to immediate address and continue execution
  98. %1001`1011 $9B JGT regAddr If Zero flag is clear and Negative flag is not equal to Overflow flag, jump to address pointed to by source register and continue execution
  99. %1101`1011 $DB JGT immAddr If Zero flag is clear and Negative flag is not equal to Overflow flag, jump to address pointed to by immediate value and continue execution
  100. %0001`1100 $1C JA reg If Carry flag is clear and Zero flag is clear, jump to address in source register and continue execution
  101. %0101`1100 $5C JA imm If Carry flag is clear and Zero flag is clear, jump to immediate address and continue execution
  102. %1001`1100 $9C JA regAddr If Carry flag is clear and Zero flag is clear, jump to address pointed to by source register and continue execution
  103. %1101`1100 $DC JA immAddr If Carry flag is clear and Zero flag is clear, jump to address pointed to by immediate value and continue execution
  104. %0001`1101 $1D CALL reg Push PC.H0 to stack, jump to address in source register and continue execution until RET is executed
  105. %0101`1101 $5D CALL imm Push PC.H0 to stack, jump to immediate address and continue execution until RET is executed
  106. %1001`1101 $9D CALL regAddr Push PC.H0 to stack, jump to address pointed to by source register and continue execution until RET is executed
  107. %1101`1101 $DD CALL immAddr Push PC.H0 to stack, jump to address pointed to by immediate value and continue execution until RET is executed
  108. %0001`1110 $1E OUTR reg reg Output value in source register to port in destination register
  109. %0101`1110 $5E OUTR imm reg Output immediate value to port in destination register
  110. %1001`1110 $9E OUTR regAddr reg Output value at address in source register to port in destination register
  111. %1101`1110 $DE OUTR immAddr reg Output value at immediate address to port in destination register
  112. %0001`1111 $1F IN reg reg Read value from port in source register into destination register
  113. %0101`1111 $5F IN imm reg Read value from port in immediate value into destination register
  114. %1001`1111 $9F IN regAddr reg Read value from port at address in source register into destination register
  115. %1101`1111 $DF IN immAddr reg Read value from port at immediate address into destination register
  116. %0010`0000 $20 PUSH reg Copy register value into memory at location in S.H0, decrement S.H0 by size of register
  117. %0110`0000 $60 PUSH imm Copy immediate value into memory at location in S.H0, decrement S.H0 by size of immediate value
  118. %0010`0010 $22 CLR reg Set register to zero (0).
  119. %0010`0001 $23 CMPIND reg regAddr Set flags by subtracting source register value from value at address in destination register
  120. %0110`0001 $63 CMPIND imm regAddr Set flags by subtracting immediate value from value at address in destination register
  121. %0010`0100 $24 INT reg Push FL and PC to stack and generate a software interrupt at index stored in register (privileged)
  122. %0110`0100 $64 INT imm Push FL and PC to stack and generate a software interrupt using immediate index (privileged)
  123. %0010`0101 $25 TSTIND reg regAddr Set flags by ANDing source register value with value at address in destination register
  124. %0110`0101 $65 TSTIND imm regAddr Set flags by ANDing immediate value with value at address in destination register
  125. %0010`0110 $26 POP reg Increment SP.H0 by size of register, copy value at SP.H0 into register
  126. %0010`0111 $27 RET Pop PC.H0 from stack and continue execution at that address. Used to return from CALL.
  127. %0010`1000 $28 IRET Pop FL and PC from stack and continue execution at segment/address in PC. Used to return from interrupt (privileged).
  128. %0010`1001 $29 SETINT Set the Interrupt flag, thereby enabling hardware interrupts (privileged)
  129. %0011`0000 $30 CLRINT Clear the Interrupt flag, thereby disabling hardware interrupts (privileged)
  130. %0011`0001 $31 SETCRY Set the Carry flag
  131. %0011`0010 $32 CLRCRY Clear the Carry flag
  132. %1010`1010 $AA NOP No operation. Used as an instruction placeholder.
  133. %1111`1111 $FF BRK Trigger a debug break (INT 3)

Register Parameter

Register bit field

  1. %0000xxxx $0 A register
  2. %0001xxxx $1 B register
  3. %0010xxxx $2 C register
  4. %0011xxxx $3 D register
  5. %0100xxxx $4 E register
  6. %0101xxxx $5 G register
  7. %0110xxxx $6 H register
  8. %0111xxxx $7 J register
  9. %1000xxxx $8 K register
  10. %1001xxxx $9 L register
  11. %1010xxxx $A M register
  12. %1011xxxx $B Z register
  13. %1100xxxx $C FL register
  14. %1101xxxx $D IN register
  15. %1110xxxx $E PC register
  16. %1111xxxx $F SP register

Sub-register bit field

  1. %xxxx0000 $0 X.B0 (1-byte data)
  2. %xxxx0001 $1 X.B1 (1-byte data)
  3. %xxxx0010 $2 X.B2 (1-byte data)
  4. %xxxx0011 $3 X.B3 (1-byte data)
  5. %xxxx0100 $4 X.B4 (1-byte data)
  6. %xxxx0101 $5 X.B5 (1-byte data)
  7. %xxxx0110 $6 X.B6 (1-byte data)
  8. %xxxx0111 $7 X.B7 (1-byte data)
  9. %xxxx1000 $8 X.Q0 (2-byte data)
  10. %xxxx1001 $9 X.Q1 (2-byte data)
  11. %xxxx1010 $A X.Q2 (2-byte data)
  12. %xxxx1011 $B X.Q3 (2-byte data)
  13. %xxxx1100 $C X.H0 (4-byte data)
  14. %xxxx1101 $D X.H1 (4-byte data)
  15. %xxxx1110 $E X (8-byte data)

Immediate Parameter

Value Size Bit Field

  1. %xxxx`x000 $00 instruction reads 1 byte immediate (8 bits)
  2. %xxxx`x001 $01 instruction reads 2-byte immediate (16 bits)
  3. %xxxx`x010 $02 instruction reads 4-byte immediate (32 bits)
  4. %xxxx`x011 $03 instruction reads 8-byte immediate (64 bits)

Immediate Operation Bit

  1. %xxxx`0xxx Read immediate value as operand
  2. %xxxx`1xxx Perform math operation with value (not implemented yet)

Immediate Value Bit Field

  1. %xxxx`x000 instruction reads 1 byte immediate (8 bits)
  2. %xxxx`x001 instruction reads 2-byte immediate (16 bits)
  3. %xxxx`x010 instruction reads 4-byte immediate (32 bits)
  4. %xxxx`x011 instruction reads 8-byte immediate (64 bits)

Immediate Math Bit Field

  1. %0000`xxxx ADD immediate to previous operand
  2. %0001`xxxx SUB immediate from previous operand
  3. %0010`xxxx MUL previous operand by immediate
  4. %0011`xxxx DIV previous operand by immediate
  5. %0100`xxxx AND previous operand with immediate
  6. %0101`xxxx OR previous operand with immediate
  7. %0110`xxxx XOR previous operand with immediate
  8. %0111`xxxx NOR previous operand with immediate
  9. %1000`xxxx NAND previous operand with immediate
  10. %1001`xxxx SHL previous operand by immediate
  11. %1010`xxxx SHR previous operand by immediate
  12. %1011`xxxx reserved
  13. %1100`xxxx reserved
  14. %1101`xxxx reserved
  15. %1110`xxxx reserved
  16. %1111`xxxx reserved

BIOS Interface

BIOS calls will track as closely as possible to the “standard” BIOS routines found on typical
x86 PCs. The x86 registers used in BIOS calls will map to Maize registers as follows:

  1. AX -> A.Q0
  2. AL -> A.B0
  3. AH -> A.B1
  4. BX -> A.Q1
  5. BL -> A.B2
  6. BH -> A.B3
  7. CX -> A.Q2
  8. CL -> A.B4
  9. CH -> A.B5
  10. DX -> A.Q3
  11. DL -> A.B6
  12. DH -> A.B7

OS ABI

The first six arguments to OS-level routines will be placed, from left to right, into the
G, H, J, K, L, and M registers. Any remaining arguments will be pushed onto the stack.
For example:

  1. ; Call C function "void random_os_function(int32_t a, const char *b, size_t c, int32_t* d)"
  2. LD $0000`1234 G ; int32_t a
  3. LD A.H0 H ; const char* b, assuming the pointer is in A.H0
  4. LD $0000`00FF J ; size_t c
  5. LD B.H1 K ; int32_t* d, assuming the pointer is in B.H1
  6. CALL random_os_function

The called routine will preserve B, C, D, E, Z, and SP. Any other registers may not be
preserved. Return values will placed into the A register. For example:

  1. ; Implement C function "int add(int a, int b)"
  2. LD G A
  3. ADD H A
  4. RET

For syscalls, the same standard will be followed for syscall parameters. The syscall number
will be placed into the A register prior to calling the interrupt.

  1. ; Output a string using sys_write
  2. LD $01 A ; syscall 1 = sys_write
  3. LD $01 G ; file descriptor 1 (STDOUT) in register G
  4. LD hello_world H.H0 ; string address in register H
  5. LD hello_world_end J
  6. SUB hello_world J ; string length in register J
  7. INT $80 ; call sys_write

User applications should follow this convention where reasonable to do so.

Assembler Syntax

(This section is incomplete and a bit of a work in progress. Refer to HelloWorld.asm,
stdlib.asm, and core.asm for practical examples.)

Tokens with leading double-underscore (e.g., __foo) are reserved.

  1. %00000001 binary
  2. #123 decimal
  3. $FFFE1000 hexadecimal

Other syntax, to be described more fully later:

  1. LABEL labelName labelData | AUTO
  2. DATA dataValue [dataValue] [dataValue] [...]
  3. STRING "stringvalue"
  4. ADDRESS address | labelName

Opcodes Sorted Numerically

  1. Binary Hex Mnemonic Parameters Description
  2. ---------- --- -------- ---------- --------------------------------------------------------------------------------------------------------------------------------------------
  3. %0000`0000 $00 HALT Halt the clock, thereby stopping execution (privileged)
  4. %0000`0001 $01 LD reg reg Load source register value into destination register
  5. %0000`0010 $02 ST reg regAddr Store source register value at address in second register
  6. %0000`0011 $03 ADD reg reg Add source register value to destination register
  7. %0000`0100 $04 SUB reg reg Subtract source register value from destination register
  8. %0000`0101 $05 MUL reg reg Multiply destination register by source register value
  9. %0000`0110 $06 DIV reg reg Divide destination register by source register value
  10. %0000`0111 $07 MOD reg reg Modulo destination register by source register value
  11. %0000`1000 $08 AND reg reg Bitwise AND destination register with source register value
  12. %0000`1001 $09 OR reg reg Bitwise OR destination register with source register value
  13. %0000`1010 $0A NOR reg reg Bitwise NOR destination register with source register value
  14. %0000`1011 $0B NAND reg reg Bitwise NAND destination register with source register value
  15. %0000`1100 $0C XOR reg reg Bitwise XOR destination register with source register value
  16. %0000`1101 $0D SHL reg reg Shift value in destination register left by value in source register
  17. %0000`1110 $0E SHR reg reg Shift value in destination register right by value in source register
  18. %0000`1111 $0F CMP reg reg Set flags by subtracting source register value from destination register
  19. %0001`0000 $10 TEST reg reg Set flags by ANDing source register value with destination register
  20. %0001`0001 $11 INC reg Increment register by 1.
  21. %0001`0010 $12 DEC reg Decrement register by 1.
  22. %0001`0011 $13 NOT reg Bitwise negate value in register, store result in register.
  23. %0001`0100 $14 OUT reg imm Output value in source register to destination port
  24. %0001`0101 $15 LNGJMP reg Jump to segment and address in source register and continue execution (privileged)
  25. %0001`0110 $16 JMP reg Jump to address in source register and continue execution
  26. %0001`0111 $17 JZ reg If Zero flag is set, jump to address in source register and continue execution
  27. %0001`1000 $18 JNZ reg If Zero flag is not set, jump to address in source register and continue execution
  28. %0001`1001 $19 JLT reg If Negative flag is not equal to Overflow flag, jump to address in source register and continue execution
  29. %0001`1010 $1A JB reg If Carry flag is set, jump to address in source register and continue execution
  30. %0001`1011 $1B JGT reg If Zero flag is clear and Negative flag is not equal to Overflow flag, jump to address in source register and continue execution
  31. %0001`1100 $1C JA reg If Carry flag is clear and Zero flag is clear, jump to address in source register and continue execution
  32. %0001`1101 $1D CALL reg Push PC.H0 to stack, jump to address in source register and continue execution until RET is executed
  33. %0001`1110 $1E OUTR reg reg Output value in source register to port in destination register
  34. %0001`1111 $1F IN reg reg Read value from port in source register into destination register
  35. %0010`0000 $20 PUSH reg Copy register value into memory at location in S.H0, decrement S.H0 by size of register
  36. %0010`0001 $21 reserved
  37. %0010`0010 $22 CLR reg Set register to zero (0).
  38. %0010`0011 $23 CMPIND reg regAddr Set flags by subtracting source register value from value at address in destination register
  39. %0010`0100 $24 INT reg Push FL and PC to stack and generate a software interrupt at index stored in register (privileged)
  40. %0010`0101 $25 TSTIND reg regAddr Set flags by ANDing source register value with value at address in destination register
  41. %0010`0110 $26 POP reg Increment SP.H0 by size of register, copy value at SP.H0 into register
  42. %0010`0111 $27 RET Pop PC.H0 from stack and continue execution at that address. Used to return from CALL.
  43. %0010`1000 $28 IRET Pop FL and PC from stack and continue execution at segment/address in PC. Used to return from interrupt (privileged).
  44. %0010`1001 $29 SETINT Set the Interrupt flag, thereby enabling hardware interrupts (privileged)
  45. %0010`1010 $2A reserved
  46. %0010`1011 $2B reserved
  47. %0010`1100 $2C reserved
  48. %0010`1101 $2D reserved
  49. %0010`1110 $2E reserved
  50. %0010`1111 $2F reserved
  51. %0011`0000 $30 CLRINT Clear the Interrupt flag, thereby disabling hardware interrupts (privileged)
  52. %0011`0001 $31 SETCRY Set the Carry flag
  53. %0011`0010 $32 CLRCRY Clear the Carry flag
  54. %0011`0011 $33 reserved
  55. %0011`0100 $34 reserved
  56. %0011`0101 $35 reserved
  57. %0011`0110 $36 reserved
  58. %0011`0111 $37 reserved
  59. %0011`1000 $38 reserved
  60. %0011`1001 $39 reserved
  61. %0011`1010 $3A reserved
  62. %0011`1011 $3B reserved
  63. %0011`1100 $3C reserved
  64. %0011`1101 $3E reserved
  65. %0011`1110 $3E reserved
  66. %0011`1111 $3F reserved
  67. %0100`0000 $40 reserved
  68. %0100`0001 $41 LD imm reg Load immediate value into destination register
  69. %0100`0010 $42 reserved
  70. %0100`0011 $43 ADD imm reg Add immediate value to destination register
  71. %0100`0100 $44 SUB imm reg Subtract immediate value from destination register
  72. %0100`0101 $45 MUL imm reg Multiply destination register by immediate value
  73. %0100`0110 $46 DIV imm reg Divide destination register by immediate value
  74. %0100`0111 $47 MOD imm reg Modulo destination register by immediate value
  75. %0100`1000 $48 AND imm reg Bitwise AND destination register with immediate value
  76. %0100`1001 $49 OR imm reg Bitwise OR destination register with immediate value
  77. %0100`1010 $4A NOR imm reg Bitwise NOR destination register with immediate value
  78. %0100`1011 $4B NAND imm reg Bitwise NAND destination register with immediate value
  79. %0100`1100 $4C XOR imm reg Bitwise XOR destination register with immediate value
  80. %0100`1101 $4D SHL imm reg Shift value in destination register left by immediate value
  81. %0100`1110 $4E SHR imm reg Shift value in destination register right by immediate value
  82. %0100`1111 $4F CMP imm reg Set flags by subtracting immediate value from destination register
  83. %0101`0000 $50 TEST imm reg Set flags by ANDing immediate value with destination register
  84. %0101`0001 $51 reserved
  85. %0101`0010 $52 reserved
  86. %0101`0011 $53 reserved
  87. %0101`0100 $54 OUT imm imm Output immediate value to destination port
  88. %0101`0101 $55 LNGJMP imm Jump to immediate segment and address and continue execution (privileged)
  89. %0101`0110 $56 JMP imm Jump to immediate address and continue execution
  90. %0101`0111 $57 JZ imm If Zero flag is set, jump to immediate address and continue execution
  91. %0101`1000 $58 JNZ imm If Zero flag is not set, jump to immediate address and continue execution
  92. %0101`1001 $59 JLT imm If Negative flag is not equal to Overflow flag, jump to immediate address and continue execution
  93. %0101`1010 $5A JB imm If Carry flag is set, jump to immediate address and continue execution
  94. %0101`1011 $5B JGT imm If Zero flag is clear and Negative flag is not equal to Overflow flag, jump to immediate address and continue execution
  95. %0101`1100 $5C JA imm If Carry flag is clear and Zero flag is clear, jump to immediate address and continue execution
  96. %0101`1101 $5D CALL imm Push PC.H0 to stack, jump to immediate address and continue execution until RET is executed
  97. %0101`1110 $5E OUTR imm reg Output immediate value to port in destination register
  98. %0101`1111 $5F IN imm reg Read value from port in immediate value into destination register
  99. %0110`0000 $60 PUSH imm Copy immediate value into memory at location in S.H0, decrement S.H0 by size of immediate value
  100. %0110`0001 $61 reserved
  101. %0110`0010 $62 reserved
  102. %0110`0011 $63 CMPIND imm regAddr Set flags by subtracting immediate value from value at address in destination register
  103. %0110`0100 $64 INT imm Push FL and PC to stack and generate a software interrupt using immediate index (privileged)
  104. %0110`0101 $65 TSTIND imm regAddr Set flags by ANDing immediate value with value at address in destination register
  105. %0110`0110 $66 reserved
  106. %0110`0111 $67 reserved
  107. %0110`1000 $68 reserved
  108. %0110`1001 $69 reserved
  109. %0110`1010 $6A reserved
  110. %0110`1011 $6B reserved
  111. %0110`1100 $6C reserved
  112. %0110`1101 $6D reserved
  113. %0110`1110 $6E reserved
  114. %0110`1111 $6F reserved
  115. %0111`0000 $70 reserved
  116. %0111`0001 $71 reserved
  117. %0111`0010 $72 reserved
  118. %0111`0011 $73 reserved
  119. %0111`0100 $74 reserved
  120. %0111`0101 $75 reserved
  121. %0111`0110 $76 reserved
  122. %0111`0111 $77 reserved
  123. %0111`1000 $78 reserved
  124. %0111`1001 $79 reserved
  125. %0111`1010 $7A reserved
  126. %0111`1011 $7B reserved
  127. %0111`1100 $7C reserved
  128. %0111`1101 $7D reserved
  129. %0111`1110 $7E reserved
  130. %0111`1111 $7F reserved
  131. %1000`0000 $80 reserved
  132. %1000`0001 $81 LD regAddr reg Load value at address in source register into destination register
  133. %1000`0010 $82 reserved
  134. %1000`0011 $83 ADD regAddr reg Add value at address in source register to destination register
  135. %1010`0100 $84 SUB regAddr reg Subtract value at address in source register from destination register
  136. %1000`0101 $85 MUL regAddr reg Multiply destination register by value at address in source register
  137. %1000`0110 $86 DIV regAddr reg Divide destination register by value at address in source register
  138. %1000`0111 $87 MOD regAddr reg Modulo destination register by value at address in source register
  139. %1000`1000 $88 AND regAddr reg Bitwise AND destination register with value at address in source register
  140. %1000`1001 $89 OR regAddr reg Bitwise OR destination register with value at address in source register
  141. %1000`1010 $8A NOR regAddr reg Bitwise NOR destination register with value at address in source register
  142. %1000`1011 $8B NAND regAddr reg Bitwise NAND destination register with value at address in source register
  143. %1000`1100 $8C XOR regAddr reg Bitwise XOR destination register with value at address in source register
  144. %1000`1101 $8D SHL regAddr reg Shift value in destination register left by value at address in source register
  145. %1000`1110 $8E SHR regAddr reg Shift value in destination register right by value at address in source register
  146. %1000`1111 $8F CMP regAddr reg Set flags by subtracting value at address in source register from destination register
  147. %1001`0000 $90 TEST regAddr reg Set flags by ANDing value at address in source register with destination register
  148. %1001`0001 $91 reserved
  149. %1001`0010 $92 reserved
  150. %1001`0011 $93 reserved
  151. %1001`0100 $94 OUT regAddr imm Output value at address in source register to destination port
  152. %1001`0101 $95 LNGJMP regAddr Jump to segment and address pointed to by source register and continue execution (privileged)
  153. %1001`0110 $96 JMP regAddr Jump to address pointed to by source register and continue execution
  154. %1001`0111 $97 JZ regAddr If Zero flag is set, jump to address pointed to by source register and continue execution
  155. %1001`1000 $98 JNZ regAddr If Zero flag is not set, jump to address pointed to by source register and continue execution
  156. %1001`1001 $99 JLT regAddr If Negative flag is not equal to Overflow flag, jump to address pointed to by source register and continue execution
  157. %1001`1010 $9A JB regAddr If Carry flag is set, jump to address pointed to by source register and continue execution
  158. %1001`1011 $9B JGT regAddr If Zero flag is clear and Negative flag is not equal to Overflow flag, jump to address pointed to by source register and continue execution
  159. %1001`1100 $9C JA regAddr If Carry flag is clear and Zero flag is clear, jump to address pointed to by source register and continue execution
  160. %1001`1101 $9D CALL regAddr Push PC.H0 to stack, jump to address pointed to by source register and continue execution until RET is executed
  161. %1001`1110 $9E OUTR regAddr reg Output value at address in source register to port in destination register
  162. %1001`1111 $9F IN regAddr reg Read value from port at address in source register into destination register
  163. %1010`0000 $A0 reserved
  164. %1010`0001 $A1 reserved
  165. %1010`0010 $A2 reserved
  166. %1010`0011 $A3 reserved
  167. %1010`0100 $A4 reserved
  168. %1010`0101 $A5 reserved
  169. %1010`0110 $A6 reserved
  170. %1010`0111 $A7 reserved
  171. %1010`1000 $A8 reserved
  172. %1010`1001 $A9 reserved
  173. %1010`1010 $AA NOP No operation. Used as an instruction placeholder.
  174. %1010`1011 $AB reserved
  175. %1010`1100 $AC reserved
  176. %1010`1101 $AD reserved
  177. %1010`1110 $AE reserved
  178. %1010`1111 $AF reserved
  179. %1011`0000 $B0 reserved
  180. %1011`0001 $B1 reserved
  181. %1011`0010 $B2 reserved
  182. %1011`0011 $B3 reserved
  183. %1011`0100 $B4 reserved
  184. %1011`0101 $B5 reserved
  185. %1011`0110 $B6 reserved
  186. %1011`0111 $B7 reserved
  187. %1011`1000 $B8 reserved
  188. %1011`1001 $B9 reserved
  189. %1011`1010 $BA reserved
  190. %1011`1011 $BB reserved
  191. %1011`1100 $BC reserved
  192. %1011`1101 $BD reserved
  193. %1011`1110 $BE reserved
  194. %1011`1111 $BF reserved
  195. %1100`0001 $C1 LD immAddr reg Load value at immediate address into destination register
  196. %1100`0010 $C2 reserved
  197. %1100`0011 $C3 ADD immAddr reg Add value at immediate address to destination register
  198. %1110`0100 $C4 SUB immAddr reg Subtract value at immediate address from destination register
  199. %1100`0101 $C5 MUL immAddr reg Multiply destination register by value at immediate address
  200. %1100`0110 $C6 DIV immAddr reg Divide destination register by value at immediate address
  201. %1100`0111 $C7 MOD immAddr reg Modulo destination register by value at immediate address
  202. %1100`1000 $C8 AND immAddr reg Bitwise AND destination register with value at immediate address
  203. %1100`1001 $C9 OR immAddr reg Bitwise OR destination register with value at immediate address
  204. %1100`1010 $CA NOR immAddr reg Bitwise NOR destination register with value at immediate address
  205. %1100`1011 $CB NAND immAddr reg Bitwise NAND destination register with value at immediate address
  206. %1100`1100 $CC XOR immAddr reg Bitwise XOR destination register with value at immediate address
  207. %1100`1101 $CD SHL immAddr reg Shift value in destination register left by value at immediate address
  208. %1100`1110 $CE SHR immAddr reg Shift value in destination register right by value at immediate address
  209. %1100`1111 $CF CMP immAddr reg Set flags by subtracting value at immediate address from destination register
  210. %1101`0000 $D0 TEST immAddr reg Set flags by ANDing value at immediate address with destination register
  211. %1101`0001 $D1 reserved
  212. %1101`0010 $D2 reserved
  213. %1101`0011 $D3 reserved
  214. %1101`0100 $D4 OUT immAddr imm Output value at immediate address to destination port
  215. %1101`0101 $D5 LNGJMP immAddr Jump to segment and address pointed to by immediate value and continue execution (privileged)
  216. %1101`0110 $D6 JMP immAddr Jump to address pointed to by immediate value and continue execution
  217. %1101`0111 $D7 JZ immAddr If Zero flag is set, jump to address pointed to by immediate value and continue execution
  218. %1101`1000 $D8 JNZ immAddr If Zero flag is not set, jump to address pointed to by immediate value and continue execution
  219. %1101`1001 $D9 JLT immAddr If Negative flag is not equal to Overflow flag, jump to address pointed to by immediate value and continue execution
  220. %1101`1010 $DA JB immAddr If Carry flag is set, jump to address pointed to by immediate value and continue execution
  221. %1101`1011 $DB JGT immAddr If Zero flag is clear and Negative flag is not equal to Overflow flag, jump to address pointed to by immediate value and continue execution
  222. %1101`1100 $DC JA immAddr If Carry flag is clear and Zero flag is clear, jump to address pointed to by immediate value and continue execution
  223. %1101`1101 $DD CALL immAddr Push PC.H0 to stack, jump to address pointed to by immediate value and continue execution until RET is executed
  224. %1101`1110 $DE OUTR immAddr reg Output value at immediate address to port in destination register
  225. %1101`1111 $DF IN immAddr reg Read value from port at immediate address into destination register
  226. %1110`0000 $E0 reserved
  227. %1110`0001 $E1 reserved
  228. %1110`0010 $E2 reserved
  229. %1110`0011 $E3 reserved
  230. %1110`0100 $E4 reserved
  231. %1110`0101 $E5 reserved
  232. %1110`0110 $E6 reserved
  233. %1110`0111 $E7 reserved
  234. %1110`1000 $E8 reserved
  235. %1110`1001 $E9 reserved
  236. %1110`1010 $EA reserved
  237. %1110`1011 $EB reserved
  238. %1110`1100 $EC reserved
  239. %1110`1101 $ED reserved
  240. %1110`1110 $EE reserved
  241. %1110`1111 $EF reserved
  242. %1111`0000 $F0 reserved
  243. %1111`0001 $F1 reserved
  244. %1111`0010 $F2 reserved
  245. %1111`0011 $F3 reserved
  246. %1111`0100 $F4 reserved
  247. %1111`0101 $F5 reserved
  248. %1111`0110 $F6 reserved
  249. %1111`0111 $F7 reserved
  250. %1111`1000 $F8 reserved
  251. %1111`1001 $F9 reserved
  252. %1111`1010 $FA reserved
  253. %1111`1011 $FB reserved
  254. %1111`1100 $FC reserved
  255. %1111`1101 $FD reserved
  256. %1111`1110 $FE reserved
  257. %1111`1111 $FF BRK (INT 3) Trigger a debug break (INT 3)