Crappy vm with vector instructions for Plan 9. This VM is a simplified virtual machine based off of the z3vm design. It has both scalar and vector operations and a minimal instruction set with very simple instruction encoding. Instructions are encoded into multiple 8 bit blocks [type|len] [instruction] [args...] Registers: r0 to r7 -- the 32 bit general purpose registers (first set) (0x10-0x17) s0 to s7 -- 32 bit general-ish purpose registers (second set) (0x18-0x1f) v0 to v7 -- 256 bit (32 byte, 8 numbers) vector registers (0x20-0x27) op -- offset pointer (0x30) pc -- program counter (0x31) sp -- stack pointer (0x32) fl -- 32-bit condition register (0x33) sy -- syscall function address (0x34) ti -- timer interrupt routine pointer (0x35) tr -- timer interrupt return address (0x36) tl -- timer interval (0x37) note: the sx register set is conventionally used for arguments to subroutines. types: (vpasm doesn't use these, but are available for use if wanted) # -- constant (in hex) (32-bits / 4 blocks) $ -- address (32-bits / 4 blocks) & -- scalar register (8-bits / 1 block) @ -- vector register (8-bits / 1 block) virtual types: *string -- replace label with address assembler directives: ; line is a comment .start string begin address counting at string .label string replace this string with the last instructions address .data string string that follows are a number to be inserted into the program at this point .vdata string that follows are numbers to be placed in each member of a vector .eof denotes the end of the source file type 0xf: implementation specific instructions (catimpl) 0x10, 2: bp breakpoint 0x11, 6: print address print some string at address 0x12, 3: rfmt reg print the formatted contents of register reg type 0x0: scalar register instructions (catsmem) 0x10, 7: loads reg address loads memory at address into reg 0x11, 7: stores reg address stores register to memory at address 0x12, 4: moves reg1 reg2 reg2 = reg1 0x13, 4: rloads reg1 reg2 loads memory at address in reg2 into reg1 0x14, 4: rstores reg1 reg2 stores reg1 at address in reg2 0x15, 3: push reg mem[sp] = reg; sp++ 0x16, 3: pop reg sp--; mem[sp] = reg 0x17, 7: loadc reg constant reg = constant 0x18, 3: clrs reg clear register 0x19, 7: loadb reg address loads a byte at address into register 0x1a, 7: storeb reg address stores the low byte of a register to address 0x1b, 4: rloadb reg1 reg2 loads the byte at the addresss in reg2 into reg1 0x1c, 4: rstoreb reg1 reg2 stores the low byte of reg1 at the address in reg2 type 0x1: vector register instructions (catvmem) 0x10, 7: loadv vreg address vector version of loads 0x11, 7: storev vreg address vector version of stores 0x12, 4: movev vreg1 vreg2 vector version of moves 0x13, 4: rloadv vreg1 reg2 vector version of rloads 0x14, 4: rstorev vreg1 reg2 vector version of rstores 0x15, 5:rloadsv reg1 vreg2 reg3 reg1 = vreg2[reg3] 0x16, 5: rstoresv vreg1 reg2 reg3 vreg1[reg3] = reg2 0x17, 3: clrv vreg clear register 0x18, 5: gather vreg1 vreg2 vreg3 gather operation. vreg1 will contain the non-empty elements of vreg2. vreg3 is the index vector. for(int i = 0; i < VECLEN; i++) vreg1[i] = vreg2[vreg3[i]]; 0x19, 5: scatter vreg1 vreg2 vreg3 scatter operation. vreg1 will contain the sparse vector and vreg2 contains the packed elements. vreg3 is the index vector. for(int i = 0; i < VECLEN; i++) vreg1[vreg3[i]] = vreg2[i]; 0x20, 5: idxcomp vreg1 vreg2 reg3 computes the index vector for scatter/gather ops. vreg1 is the index vector, vreg2 is the sparse vector, the constant defines what empty means. int j = 0; for(int i = 0; i < VECLEN; i++){ if(vreg2[i] != constant){ vreg1[i] = j; j++; } } type 0x2: control flow and other instructions (catctl) 0xff, 2: nop no-op. do nothing 0x10, 6: jmp address pc = address 0x11, 6: rjmp address pc = op + address 0x12, 3: jmpr reg pc = reg 0x13, 3: rjmpr reg pc = op + reg 0x14, 2: clrf set fl to 0 0x15, 6: cjmp address if fl > 0 then fl = 0 and pc = address 0x16, 6: crjmp address if fl > 0 then fl = 0 and pc = op + address 0x17, 3: cjmpr reg if fl > 0 then fl = 0 and pc = reg 0x18, 3: crjmpr reg if fl > 0 then fl = 0 and pc = op + reg 0x19, 6: setsys address sy = address 0x1a, 2: syscall increment pc and push it then set pc to sy 0x1b, 6: call address increment pc and push it then set pc to address 0x1c, 6: rcall address increment pc and push it then set pc to op + address 0x1d, 2: return pop pc 0x1f, 2: halt halt execution (implementation specific. must be implemented) 0x20, 2: tstart start timer 0x21, 2: tstop stop timer type 0x3: scalar math (catsmath) 0x10, 4: eqs reg1 reg2 if reg1 == reg2 then fl = 1 0x11, 4: gts reg1 reg2 if reg1 > reg2 then fl = 1 0x12, 4: lts reg1 reg2 if reg1 < reg2 then fl = 1 0x13, 5: adds reg1 reg2 reg3 reg1 = reg2 + reg3 0x14, 5: subs reg1 reg2 reg3 reg1 = reg2 - reg3 0x15, 5: muls reg1 reg2 reg3 reg1 = re2 * reg3 0x16, 5: divs reg1 reg2 reg3 reg1 = reg2 / reg3 0x17, 5: mods reg1 reg2 reg3 reg1 = reg2 % reg3 0x18, 5: nands reg1 reg2 reg3 reg1 = ~(reg2 & reg3) type 0x4: vector math (catvmath) note: vector math does not support overflows 0x10, 4: eqv reg1 reg2 if reg1 == reg2 then fl = 1 0x11, 4: gtv reg1 reg2 if reg1 > reg2 then fl = 1 0x12, 4: ltv reg1 reg2 if reg1 < reg2 then fl = 1 0x13, 5: addv reg1 reg2 reg3 reg1 = reg2 + reg3 0x14, 5: subv reg1 reg2 reg3 reg1 = reg2 - reg3 0x15, 5: mulv reg1 reg2 reg3 reg1 = re2 * reg3 0x16, 5: divv reg1 reg2 reg3 reg1 = reg2 / reg3 0x17, 5: modv reg1 reg2 reg3 reg1 = reg2 % reg3 Timer: The timer is an interrupt that fires every tl milliseconds. When the timer fires, the timer is stopped, the pc is stored in tr, the pc is set to ti, and execution is continued. The routine has no limits on what it can do beyond restarting the timer. When the interrupt routine calls return, the pc is set to tr, the timer is restarted, and execution is continued. Devices: (WIP) All addresses above 0xffffff00 are devices. (addr & 0x000000f0 >> 4) is the device address and (addr & 0x0000000f) is a device control address. say: byte *dev = (byte*)0xffffffn0; // n is the device address dev[0] is the device 'read' and 'write' register dev[1] is the status and command register dev[2:15] is the 'device memory' area (byte *dmem = &dev[2]) Default devices: Device 0: console dev[0] read will read a byte if available. 0 if none are available dev[0] write will put a byte on the console screen dev[1] is unused dmem is unused. Device 1: 'disk' storage dev[0] is unused dev[1] read will get current status. 0 if nothing new dev[1] write will write a command to the device dev[2] is used: u32int *disk_addr = &dev[2] u32int *mem_addr = &dev[6] u8int *len = &dev[10] disk_addr is the address on disk for the read/write operation mem_addr is the memory location where the data to be written to the disk is or where the data read from the disk should be put. len is the length of the read or write.