Close

20241114 - Virtual Machine Summary

A project log for ROM Disassembly - Cefucom-21

Peering into the soul of this obscure machine

ziggurat29ziggurat29 11 hours ago0 Comments

Yesterday I mentioned this apparent 'virtual machine' embedded in the Cefucom product, and I spent some time figuring how the it do.  The machine workings are pretty clear now, and I have done 66 of the opcodes (10 more to figure out).  Here's the scoop so far:

Overview

*  basic unit of execution is a 'block'

*  block structure is:
  *  opcode
  *  parameters...  Number of parameters is opcode-specific.  So a block is between 1 and 7 bytes.
*  a 'program' is a series of blocks
*  rst 8 is the 'primitive block executive'.  It is not typically used directly.
*  rst 10 is the 'sequenced block executive', and is the primary way of executing 'programs'
*  is BIG-ENDIAN
*  has absolute addresses
*  indices are 1-relative
*  has various functional groups:
  *  load/store; 8 and 16 bit, references and constants
  *  memset/memcpy
  *  arithmetic; addition/subtraction, usually accumulator form (e.g. *parm1 += *param2)
  *  bitwise; and, or, xor (note, no 'not', though I suppose you can synthesize that from xor)
  *  shift
  *  goto
  *  computed goto (well, 'indexed'; the computation would be done separately)
  *  if (and if-not)
  *  next
  *  'usr' (call out to an assembly routine)
  *  'run' (another program)
  *  some Cefucom specific opcodes; probably added by the company


other notable aspects

*  The C register is used to store flags, where appropriate.  80h = carry, 1 = non-zero/no-carry, 0 = zero.  The C register is actively preserved between block execution.

*  A couple instrucstion place the result in B or DE, but these are not preserved between blocks.  (I need to look more into this when I find a program that invokes them; and maybe there are no instances of such.)
*  the RST 10 implementation realizes a few more outside of the dispatch table:
  *  7F - NOP
  *  7E - exit on no-carry
  *  7D - exit on carry
  *  7C - exit on non-zero (or carry)
  *  7B - exit on zero
*  the implementation speculatively loads param1 into HL and param2 into DE, which is useful for most instructions, but some do have less parameters (e.g. END).  This is a potential out-of-bounds read, but will not cause problems on this hardware.
*  there are two opcodes 62h and 63h which have little-endian parameters param1 and param2, while param3 is big-endian.  I think this is a bug, though it might originate from a detail of their build process.
*  opcode 43 is also little-endian, which runs a rst 10 program.  This is interesting because there is already opcode 3e that does the same thing with a big-endian program pointer, so there must have been felt a special need.

The remaining opcodes deal with Cefucom structures which I do not yet understand, so I might put those off for the moment in the interest of making progress.

One vexing thing has been some functions that do some stack manipulation such that the execution is no longer linear.  I figured out some of those, like the ones that have a dispatch table that follows a call.  But there are some others that have more convoluted shenanigans.  I marked those as 'witchcraft' so that I can find them more easily when I come back to them.  Perhaps now is the time to look into this witchcraft.

Discussions