Shop OBEX P1 Docs P2 Docs Learn Events
Emulating a 16-bit instruction machine - anyone else done this? — Parallax Forums

Emulating a 16-bit instruction machine - anyone else done this?

Mark_TMark_T Posts: 1,981
edited 2012-05-16 09:29 in Propeller 1
For a few PASM programs I've written doing arithmetic of various kinds I've run out of cog space and decided to
write an emulator for a 3-address 16-register processor for handling the numeric code. The problem with PASM
is that being a 2-address architecture with 32-bit instructions each arithmetic operation (which might be just an add
instruction or a call to a routine takes 3 to 4 longs (MOV two arguments, CALL, MOV back the result).

So code with a dozen arithmetic ops takes about 50 longs from the COG space. An emulated 16-bit instruction 3-address
machine takes 12 words / 6 longs for the same sequence, and of course the code can be in HUB RAM too...

For ordinary integer arithmetic this is very slow, but one of the programs was manipulating points on a discrete elliptic curve
where each add/sub/mult operation is many instructions anyway. An example there was coding up the calculation of inverses.

So the basic technique was to code up a 4-bit opcode and 3 4-bit "register" numbers into a WORD and decode in a little
emulator (sometime like this)
exec_script 
:sloop        rdword    op, script  wz
              add       script, #2
        if_z  jmp       #exec_script_ret  ' zero instruction means end of script

              call      #getarg   ' pick apart 16-bit instruction  " op  r, a, b"
              mov       b, arg
              call      #getarg
              mov       a, arg
              call      #getarg
              mov       r, arg     ' now op just contains the opcode field, so dispatch mechanism follows

              cmp       op, #1  wz
        if_z  call      #addd         ' the various operations of the emulated machine
              cmp       op, #2  wz
        if_z  call      #subtract
              cmp       op, #3  wz
        if_z  call      #mult
              cmp       op, #4  wz
        if_z  call      #halve_into   ' in this example a,b,r happen to be addresses of operands so no need to write back result

              jmp       #:sloop
exec_script_ret  ret

getarg        mov       arg, op
              shr       op, #4
              and       arg, #$F    ' got rightmost arg number
              add       arg, #X     ' add X's cog address  (X is the zero'th register)
              movs      mov_ins, arg ' overwrite instruction with correct register spec
              nop
mov_ins       mov       arg, X      ' modified instruction to move register
getarg_ret    ret


For tests and branches I just did that directly in PASM and each basic block was a separate script. A branch mechanism could just about be shoehorned in the emulated code though.

Comments

  • jazzedjazzed Posts: 11,803
    edited 2012-05-16 07:43
    Nice idea :thumb: :)
    There is more that can be done with it given time of course :thumb:

    Thanks for sharing.
    --Steve
  • ericballericball Posts: 774
    edited 2012-05-16 09:29
    I haven't done much PASM in a while, but this is the direction I would go:
    Note: PASM registers 0-15 are used for script registers 0-15
    
    sloop		rdword	op, script	wz
    		add	script, #2
    	if_z	ret			' opcode 0 = exit
    
    		mov	b, op
    		and	b, #$F
    		shr	op, #4
    		mov	a, op
    		and	a, #$F
    		shr	op, #4
    		mov	r, op
    		shr	op, #4
    		add	op, #:opjmp
    		movd	:opjmp, op
    		and	r, #$F
    :opjmp		jmp	:opjmp+1	
    		long	add		' opcode 1
    		long	subtract	' opcode 2
    		long	mult		' opcode 3
    		long	halve_into	' opcode 4
    		long			' opcode 5
    		long			' opcode 6
    		long			' opcode 7
    		long			' opcode 8
    		long			' opcode 9
    		long			' opcode 10
    		long			' opcode 11
    		long			' opcode 12
    		long			' opcode 13
    		long			' opcode 14
    		long			' opcode 15
    add		movs	:1, a
    		movd	:1, r
    		movs	:2, b
    		movd	:2, r
    :1		mov	r, a
    :2		add	r, b
    		jmp	#sloop
    
    The main difference is using PASM registers 0-15 as the script registers, bringing getarg into the main loop, and using a jump table instead of the cmp testing. Depending upon the different opcodes, it might be possible to fold the mov r,a commands into the main loop.
Sign In or Register to comment.