The hack v2 ISA emulator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kaqu 4c5041ddf9 Pong auto restart & gamepad/kbd mingle fixed 1 day ago
.vscode Screen adjusted 2 days ago
OS_Test Pong auto restart & gamepad/kbd mingle fixed 1 day ago
doc Readme prepared 6 days ago
img Space Invaders entry added 3 days ago
rsc Cmd line args & full screen feature started 2 days ago
src Pong auto restart & gamepad/kbd mingle fixed 1 day ago
.gitignore Shift to V2 emulator started 3 weeks ago
Cargo.lock Active separate screen! 3 days ago
Cargo.toml Active separate screen! 3 days ago Spec. linked to readme 1 day ago

HV2E32, the Hack CPU V2 emulator

Source from

This is the 'advanced' emulator derived from the HE32 original version. It relies on a somewhat upgraded instruction set from the original one (which I dubbed 'hack version 2' ...). Build from Rust sources, it out-performs even the original VM-Emulator, thus making jack games actually playable!

Register set

The new instruction set architecture (ISA) features 16 registers (R0..R15). Though basically orthogonal (i.e. all registers may be used in identical form w/ all instructions), my compiler suite uses pre-defined register names (which will be all too familiar to hack students!).

R0  -   SP      Stack pointer
R1  -   LCL     Local variables base pointer
R2  -   ARG     Function arguments base pointer
R3  -   THIS    Object base pointer
R4  -   THAT    Array base pointer
R5  -   TEMP0   ->RAM[5]
R6  -   STATIC  Global/static variables base pointer
R7  -   ZERO    An 'empty' register (to be used w/ absolute addresses)
R8  -   ARG1/TEMP8  Argument #1/Temporary value #8
R9  -   ARG2/TEMP7  Argument #2/Temporary value #7
R10 -   ARG3/TEMP6  Argument #3/Temporary value #6
R11 -   ARG4/TEMP5  Argument #4/Temporary value #5
R12 -   TEMP4/ARG5  Temporary value #4/Argument #5
R13 -   TEMP3/ARG6  Temporary value #3/Argument #6
R14 -   TEMP2/ARG7  Temporary value #2/Argument #7
R15 -   TEMP1/ARG8  Temporary value #1/Argument #8

(Compiler shall decide ARG#/TEMP# usage - well, currently not used ;)

Instruction Set Architecture (ISA)

The A instruction

The original 'A' instruction mutated to:



dddd = destination register (direct or indirect), <dr>
ssss = source register (direct or indirect), <sr>
S=0 Load / =1 Store (store flag)
I   = indirect <sr> interpretation
X   = post-increment on stores and pre-decrement on loads
p   = use source register ssss
o   = constant used as offset for 1st operand
<const.> = 18-bits constant (C..C)
-   =not used

As this is an emulator instruction set, the distinction between Load/Store instructions & ALU instructions need not be made (which is not true for actual physical H/W!). Thus, combined load AND store in one instruction are possible, thereby reducing the shear number of instructions the emulator has to process. This should provide for a significant speed boost. As with RISC-V, additional pseudo-ops have been introduced for easier reading (& writing!).

The C instruction

The original 'C' instruction has been modified to:



dddd = destination register (direct or indirect), <dr>
ssss = source register (direct or indirect), <sr>
S    =   <dr> indirect (store)
I    = indirect <sr> interpretation
<rr> = 0..15 (rrrr, operand2 register)
c..c = ALU comp., const load or transfer
j..j = jump type
-=not used

(Yes, there are lots of empty entries for now ...)

Here is some example code (you get the idea!):

// L472 - function Memory.peek  0
// function: entry label
Memory.peek:     // Memory.peek entry point (Memory, line 00472)
                // using 0 local variables
// L473 - push static 0 // ram
// static_push: Memory.0
        PUSH [ZERO + #Memory.0]

// L474 - push argument 0 // address
// argument_push: TOS = RAM[ARG + #0]
        PUSH [ARG + #0]

// L475 - add
// add: [TOS] = [TOS] + [TOS-1]
        POP TEMP1       // Add [SP-1]+[SP-2] & remove TOS
        S [ZERO], TEMP1  
        ADDTOS [ZERO]
// L476 - pop pointer 1 // Array+index
// pointer_pop: THIS|THAT = TOS
        L TEMP2, #1                // Arg2=0|1 (=THIS|THAT target!)
        L TEMP1, #jne_Memory.00476     // <>0? -> THAT!
        JNE TEMP1, TEMP2                        
        POP THIS                        // THIS = TOS
        JUMP #jeq_Memory.00476
        POP THAT                        // THAT = TOS

// L477 - push that 0 // *(Array+index)
// that_push: TOS = RAM[THAT + #0]        
        PUSH [THAT + #0]

// L478 - return
// return

Also, function calling is really simple now:

// L1120 - push constant 3
// constant_push: 3 
        PUSH #3

// L1121 - call Sys.error 1
// call Sys.error 1
        CALL #Sys.error, #1       // Stack:0=retadr|1=LCL|ARG|THIS|THAT|(^[SP])            

// L1122 - pop temp 0 // void
// temp_pop: RAM[TEMP0 + #0] = TOS
        DEC SP                  // Adjust stack pointer
        S [TEMP0 + #0], [SP] // Pick up value as argument #0 -> RAM[<targetadr>]

CALL & RETURN work in conjunction and provide for the calling contract all in one instruction! Should give it a speed boost as well.

The third improvement - compared to the original hack IS - is the introduction of ready made MUL & DIV instructions (instead of calling Math.mul & Math.div library functions).

See spec. for more information.

'Hardware' Changes

  1. A Random number generator may be queried @ RAM[24578], yielding integers in the range 0..=100
  2. Up to 4 USB-Gamepads are now supported @ RAM[24580..24583] respectively. Player #1 buttons are mapped to keyboard as well!

Mappings are (gamepad -> keyboard #code):

// Joystick / axis cross
Joystick axis Y down -> [Cursor down] (#133)
Joystick axis Y up -> [Cursor up] (#131)
Joystick axis X right -> [Cursor right] (#132)
Joystick axis X left -> [Cursor left] (#130)

// Color buttons
X/Blue -> [X] (#88)
Y/Green -> [Y] (#89)
A/Red -> [A] (#65)
B/Yellow -> [B] (#66)

// Center buttons
Select -> [Enter] (#128) (Somewhat irregular ...)
Start -> [Space] (#32)

// Frontal 'levers'
Right lever -> [End] (#135)
Left lever -> [Home] (#134)


Source w/ renamed local vars from

Straight source from

Straight source from

On YouTube (compare speed w/ hv2e32!):



    Space Invaders:

    Lots of other jack games too ...


Use 'cargo run --release ./OS_Test/OS_Test.hv2' to run (as in debug mode the performance will be poor).

For autostart in full screen mode use 'cargo run --release ./OS_Test/OS_Test.hv2 --BigScreen --Run' ...