Write a 0815 interpreter I like the concept of 0815, except the interpreter on the creator's website returns an Error 404. So I decided to ask you all to help!

The Basics

0815 is based around three (3) registers and a queue. The registers are named X, Y, and Z, with X being write-only, Z being read-only, and Y being a "helper" register that can't be directly accessed. All registers start out set to 0. All numbers are in hexadecimal.

Instructions

Note: Some instructions take parameters which are formatted like }:hello: where the :hello: is the parameter. Other note: Some of the instruction descriptions are unclear, so I took some liberties with  them. (Original instructions here)

~ means the parameter is required

----------|---|--------------------------------------------
  ~Move   | &lt; | &lt;:8: will write 8 to X (parameter required) 
----------|---|--------------------------------------------
   Swap   | x | Swap X and Y
----------|---|--------------------------------------------
  ~Label  | } | }:hello: makes a label called 'hello'
----------|---|--------------------------------------------
  Input   | | | Set X to an integer from input in 
 Number   |   | hexadecimal greedily (regex: [0-9A-F]+)
----------|---|--------------------------------------------
  Input   | ! | Set X to the ASCII code of one ASCII
  ASCII   |   | character in the input
----------|---|--------------------------------------------
  Print   | % | Prints an integer stored in Z 
  number  |   | (hexadecimal base)
----------|---|--------------------------------------------
  Print   | $ | Prints an ASCII char stored in Z
  ASCII   |   |
----------|---|--------------------------------------------
  Roll    |   | Rolls all registers to the left
Registers | ~ | X &lt;- Y &lt;- Z &lt;- X
  Left    |   | After roll: X = Y, Y = Z and Z = X
----------|---|--------------------------------------------
  Roll    |   | Rolls all registers to the right
Registers | = | X -&gt; Y -&gt; Z -&gt; X
  Right   |   | After roll: Y = X, Z = Y and X = Z
----------|---|--------------------------------------------
 ~Jump if | ^ | ^:loop: Jumps to the label 'loop' if Z is
 not zero |   | not zero
----------|---|--------------------------------------------
 ~Jump if | # | #:loop: Jumps to the label 'loop' if Z is
   zero   |   | zero
----------|---|--------------------------------------------


Queue Instructions

----------|---|--------------------------------------------
   Clear  | ? | Clears the queue
----------|---|--------------------------------------------
  Enqueue | &gt; | Enqueue the number stored in Z
----------|---|--------------------------------------------
  Dequeue | { | Dequeue a number into X
----------|---|--------------------------------------------
          |   | Rolls the queue to the left, wrapping as
   Roll   |   | necessary. The first number becomes the 
   Queue  | @ | last; second becomes first and so on. Can  
   Left   |   | take a parameter to roll a certain number
          |   | of times. @:b: rolls 11 times.
----------|---|--------------------------------------------
   Roll   |   | 
   Queue  | &amp; | The opposite of @
   Right  |   | 
----------|---|--------------------------------------------


Arithmetic instructions

----------|---|--------------------------------------------  
   Add    | + | Z = X + Y
----------|---|--------------------------------------------
 Subtract | - | Z = X - Y
----------|---|--------------------------------------------
 Multiply | * | Z = X * Y
----------|---|--------------------------------------------
  Divide  | / | Z = X / Y
          |   | Y = remainder
----------|---|--------------------------------------------


Example programs

Hello world: (Slightly golfed)

&lt;:48:~$&lt;:65:~$&lt;:6C:~$$&gt;&lt;:6F:~$&gt;@&lt;:2C:~$&lt;:20:~$&lt;:57:~${~$&lt;:72:~${~$&lt;:64:~$


Cat:

}:_t:!~$^:_t:


Truth Machine:

|~}:i:%^:i:


This is code-golf, so the submission with the least bytes wins!
Pip, 321 262 248 bytes

Greatly shortened using eval and some Kolmogorov-complexity tricks.

a@:`.(:.*?:)?`z:Y0w:2**64W++v&lt;#a{UnpWa@v^':;V("Yp57Syi7UybWb^@(b@?`[^0-9A-F]|$`)Yy57Y APOb7OzTB167O Cz7SyzSyi7SyiSyz7I2I!2l:[]7lPBz7Y3B3UDQl7z:(y+4-4*4//i)i:y%i"R2`zv:a@?"}:".p.':7`R3"POl7Lp?p5olP"R4"i)%w7z:(y"R5"FB16%w"^7"&lt;x|!%$~=^#?&gt;{@&amp;+-*/"@?n)}


Try it online! (The example is the Collatz/hailstone numbers program from the language author's website.)

Notes:


Takes the 0815 code as the first command-line argument and the input as the second. Yes, it's a bit awkward (much less so on TIO, where you get separate text boxes), but Pip isn't that great at reading all of stdin.
Numbers in input must use uppercase hex digits (as per OP: matching the regex [0-9A-F]+). If, at a | instruction, the next character of input is not 0-9A-F, the read fails and causes potentially weird behavior. This means the author's even-odd program doesn't work as written... I'm not sure how he expected multiple number reads to work, but I just implemented according to what the spec said.
If a label is duplicated, a jump goes to the first one. If a label doesn't exist, a jump terminates the program.




My ungolfed version with comments:

;;; INITIALIZATION ;;;

; a is the code, analyzed with regex into a list of instructions
a @: `.(:.*?:)?`
; b is the input
; x, y, z are registers (initially three 0s)
x:y:z:0
; l is the queue (initially empty list)
; v is the instruction pointer (initially -1, but gets incremented at top of loop)
; w represents the width of the registers
w:2**64

;;; MAIN LOOP ;;;

; Loop while IP hasn't gone past last instruction
W ++v&lt;#a {
 ; Unify n with first char of current iNstruction, p with the parameter (or nil)
 U np W a@v ^ ':

 ; If n eQuals "&lt;": move p value into x
 InQ'&lt;
  ; Convert p from hex and truncate to register width
  x : pFB16%w
 ; Swap x and y
 InQ'x
  Sxy
 ; Input number (in hex, uppercase only)
 InQ'| {
  ; Find index of first non-hex character in b and split at that index
  ; Unify left half with x and right with b
  U xb W b^@(b @? `[^0-9A-F]|$`)
  ; Convert x from hex and truncate to register width
  x : xFB16%w
 }
 ; Input ASCII
 InQ'!
  ; Pop 1st char of b and get ASCII code
  x : A POb
 ; Print number (in hex)
 InQ'%
  ; Output z converted to hex
  O zTB16
 ; Print ASCII
 InQ'$
  ; Output chr(z)
  O Cz
 ; Roll registers left
 InQ'~ {
  ; With three registers, a roll is just two swaps
  Sxz
  Sxy
 }
 ; Roll registers right
 InQ'= {
  Sxy
  Sxz
 }
 ; Jump (combines if-nonzero and if-zero cases)
 I nQ'^ &amp; z | nQ'# &amp; !z
  ; Construct the corresponding label, find its index in a, and set IP to that
  v : a @? "}:".p.':
  ; If the label doesn't exist, v becomes nil, which makes the loop condition nil,
  ; which (being falsy) exits the loop and terminates the program

 ; Clear queue
 InQ'?
  l:[]
 ; Enqueue
 InQ'&gt;
  ; Push z to back of l
  lPBz
 ; Dequeue
 InQ'{
  ; Pop l and assign to x
  x:POl
 ; Roll queue left
 ; NOTE: doesn't work if 0 is a valid parameter for roll operation
 ; If we want to handle that case, use #p?... instead of p?... for +1 byte
 InQ'@
  ; Loop specified number of times (converted from hex), or 1 if not specified
  L p ? pFB16 1
   ; Pop (front of) l and push that value to the back of l
   l PB POl
 ; Roll queue right
 InQ'&amp;
  L p ? pFB16 1
   ; Dequeue from back of l and push that value to (the front of) l
   l PU DQl

 ; Add (truncating as needed)
 InQ'+
  z:(x+y)%w
 ; Subtract (truncating as needed)
 InQ'-
  z:(x-y)%w
 ; Multiply (truncating as needed)
 InQ'*
  z:x*y%w
 ; Divide (truncating not needed)
 InQ'/ {
  z:x//y
  y:x%y
 }
}