235 lines · 7.0 KB · Text
RawDownload
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 instructionset 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 timerfires, 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 beyondrestarting 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 deviceaddress 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.