We will skip the declaration of a separate structure for the processor; instead, its state will be stored on a stack. However, we do need to make some preparations. First of all, we need to make the Flat Assembler understand our mnemonics and create a proper binary output. For this purpose, we will create an additional source file and name it vm_code.asm. As it will contain declarations of macro instructions and the VM code, which will be treated as data, the inclusion of the file in the main source would be done by adding the following:
include 'vm_code.asm'
Add this line somewhere in the data section. The next step-we have to define macro instructions that can be translated into a binary output that our virtual processor can understand. This is a very powerful feature of FASM, as one may add support for almost any architecture with a set of macro instructions (which, by the way, is the exact idea behind the Flat Assembler G):
macro vm_load_key
{
db 0x00
}
macro vm_nop
{
db 0x01
}
macro vm_load_data_length
{
db 0x02
}
macro vm_loop loopTarget
{
db 0x10
dd loopTarget - ($ + 4)
}
macro vm_jump jumpTarget
{
db 0x11
dd loopTarget - ($ + 4)
}
macro vm_exit
{
db 0x12
}
macro vm_encrypt regId
{
db 0x20
db regId
}
macro vm_decrement regId
{
db 0x21
db regId
}
macro vm_increment regId
{
db 0x22
db regId
}
macro vm_load_data_byte regId
{
db 0x30
db regId
}
macro vm_store_data_byte regId
{
db 0x31
db regId
}
; Let's give readable names to registers
register_a = 0
register_b = 1
register_cnt = 2