Virtual-Machine Architecture

A virtual machine (VM) consists of a fully functional processor hosted on an unrelated substrate machine. VM design has advantages over conventional coding. Each instruction may be highly optimized for performance—unlike a general-purpose interpreter like BASIC, which can do anything but inefficiently. VM instructions are compact like assembly but perform extremely complex tasks. Once a register and command set are devised, you can add new instructions to enhance the machine. The original instructions remain the same, which promotes modularity. Since the operational definition of the VM is rigid, firmware changes tend to be straightforward, even to the point of hosting the target architecture on a completely new substrate.

R0	Byte Input Reg		Assemble input data here
R1	Register Pointer	Pointer to R(0–ff)
R2	Register Source		Pointer to R(0–ff)
R3	Sample Preload L	Low byte of RAM addr to load to Spock
R4	Sample Preload H	High byte of RAM addr to load to Spock
R5	TRIG Logic Byte		Logic levels for Spock to match, loaded during Spock Init
R6	TRIG Mask Byte		Don’t Care bits in trigger match, loaded during Spock Init
R7	Spock OPTION byte	TRIG and PG1 setup in Spock 
R8	Trace Register		Trace Option controls Sample operation of BitScope
R9	Counter capture Lo	Counter low byte shifted out of Spock
R10	Counter capture Hi	Counter high byte shifted out of Spock
R11	DELAY-L			Post TRIG delay before halting
R12	DELAY-H			Post TRIG delay before halting
R13	TimeBase		TimeBase expander count
R14	Channel-A/B		Channel Range settings for Chop  
R15	Dump Length		Counter for number of samples transmitted per request
R16	EEPROM Data		Data register for EEPROM
R17	EEPROM Address		Address register for EEPROM
R18	POD Transmit		Register holds byte for POD
R19	POD Receive		Register gets byte from POD

Table iThe BitScope virtual machine has a set of 20 registers. The operation of the machine and all its instructions refer to these registers.

In this design, the PIC16F84 is a substrate to implement a custom BitScope machine with its instruction set becoming microcode to implement the VM. So, the BitScope VM has instructions and registers but they’re unrelated to the PIC native instruction set. The virtual registers are hosted by PIC memory registers but have meaning only to the BitScope. Similarly, BitScope has no use for XOR- or DECFSZ-type instructions. Instead, it has instructions for manipulating registers, starting sample RAM, and dumping captured data. BitScope registers may be option bits, timer constants, sample address, and so on. The exact function of the register set is detailed in Table i. Table ii shows the current command set.

00  €	Reset				Reset the machine and print its ID string
23  # 	Load Source Reg			Store R0 into R2. Set up R2 which is a source reg. A reg-to-reg move may be done by 
		pointing to a source (R2) and destination (R1).
2b  +	Inc REG				Incr the reg pointed to by R1
2d  –	Dec REG				Decr the reg pointed to by R1
30  0	Enter nibble 0			Incr R0 by 0 and nibble swap R0
31  1	Enter nibble 1			Incr R0 by 1 and nibble swap R0
32  2	Enter nibble 2			Incr R0 by 2 and nibble swap R0
33  3	Enter nibble 3			Incr R0 by 3 and nibble swap R0
34  4	Enter nibble 4			Incr R0 by 4 and nibble swap R0
35  5	Enter nibble 5			Incr R0 by 5 and nibble swap R0
36  6	Enter nibble 6			Incr R0 by 6 and nibble swap R0
37  7	Enter nibble 7			Incr R0 by 7 and nibble swap R0
38  8	Enter nibble 8			Incr R0 by 8 and nibble swap R0
39  9	Enter nibble 9			Incr R0 by 9 and nibble swap R0
3c  	Get ctr value from Spock	Shift the current 16 bit ctr value from Spock into R9, R10			
3e  	Program Spock from Registers	Load 5 bytes of data from R3–R7 into Spock. Previous contents of ctr are destroyed
3f?	Print Machine ID		Print CHAR8-CHAR1 where CHARn is part of a string identifying the type and revision of this device
40  @	Load Address Reg		Store R0 into R1. Use to set up reg ptr.
53  S	Dump Sample RAM (CSV)		Dump lines of 16 Sample RAM values (digital and analog)	 	
54  T	Trace with TRIG stop		Begin sample with Opt mode, until Trig then Delay, Halt Sample Clk, and print sample add. 
5b  [	Clear R0			Reg R0 is cleared. This usually precedes a nibble load
5d  ]	Nibble swapR0			R0:(0–3) is swapped with R0:(4–7). This command puts the entered nibbles in the correct order.
61  a	Enter nibble 'a' hex		Incr R0 by 10 and nibble swap R0
62  b	Enter nibble 'b' hex		Incr R0 by 11 and nibble swap R0
63  c	Enter nibble 'c' hex		Incr R0 by 12 and nibble swap R0
64  d	Enter nibble 'd' hex		Incr R0 by 13 and nibble swap R0
65  e	Enter nibble 'e' hex		Incr R0 by 14 and nibble swap R0
66  f	Enter nibble 'f' hex		Incr R0 by 15 and nibble swap R0
6c  l	Load R0 from @R2		Copy contents of reg pointed to by R2 toR0
6e  n	Next Address			Incr addr reg R1
70  p	Print REG value @R1		Print ASCII,ASCII
73  s	Store R0 to @R1			Copy contents of R0 to reg pointed to by R1
75  u	Update RAM pointers		Copy contents of R3,4  to  R9,10. Updates sample addr value from sample preload reg.
78 x	Exchange byte with POD		Transmit byte in POD_TX to POD  IO-0. Wait for reply byte on IO-1 and put it inPOD_RX then return it to host.
7c  |	Pass Through byte to POD	Transmit byte in POD_TX to POD IO-0. Connect IO-1 to Serial Out for host.			

Table iThe BitScope virtual machine has a set of 20 registers. The operation of the machine and all its instructions refer to these registers.

Most interpreters run from a program stored in memory. BitScope is different because it executes directly from the serial port. BitScope’s instruction set is designed to have no syntax, so there can be a maximum of 256 instructions and each is stand alone—just like a RISC instruction set. An atomic protocol means the software at both ends of the serial line is simple and does not have to preserve state information. In a PIC with 1024 words of program, it’s advisable to be economical with code, especially given the importance of reliably transmitting packets over a serial link.

I decided the BitScope command set should use common printable ASCII commands. Since the assignment of byte codes is arbitrary, any value could mean "enter hex nibble 3," but obviously 3 is a good choice. The general scheme for allocating byte-code values and their ASCII symbol is:

  • numerals—data entry
  • operators—manipulation of register values
  • lower case—general machine operation
  • upper case—major machine functions
  • nonprintables—reserved for future commands

An example script for loading R6 with 0x5a is[6]@[5a]s. It may seem obscure, but if you study it, it should make sense. Ultimately, a user interface will debug scripts and writing scripts will only be necessary if a user develops a new mode of operation or drives it directly from a terminal.

All BitScope operations, including wait on trigger, may be interrupted by any serial command. The first part of the software UART ensures that the sample clock is halted. When a serial byte is assembled and echoed, the UART turns on and, once activated, aborts all previous operation. In this sense, BitScope’s command protocol is truly atomic. Each command ends in a halt, if not prematurely aborted. ASCII code 00 is the reset vector, so it can get the PIC’s attention with a <break>.

Inevitably, a VM like this will get enhanced firmware. Microchip has devices that potentially double the number of byte codes implemented. To cope with the potential of other feature sets, ? returns a 32-bit ID code. User-interface software may keep a register of feature sets supported by each byte-code revision.