Cheng Sun

ocreMutiny -- a tiny OCR assembly language emulator

ocreMutiny is a simple emulator, that implements the following assembler instruction set, with input error checking. The emulator is 1021 bytes of crushed JavaScript, which can be seen in the HTML source of this page.

To use it, type in the assembly code in the textarea. Click "Load" to load in the code into memory. Then click "Step" to run the code line by line. If errors were found it will tell you which line, otherwise the line number of the line that is about to be executed is displayed. Q is the output port, and A0 to A7 show the current contents of the registers.

The emulator understands the instructions shown in the table below. It is case insensitive. Byte literals are in hexadecimal, in the format $xx. Also supported are line comments (delimited by ;).

Assembler Function
MOVI Ad, n Copy the byte n into register Ad
MOV Ad, As Copy the byte from As to Ad
ADD Ad, As Add the byte in As to the byte in Ad and store the result in Ad
SUB Ad, As Subtract the byte in As from the byte in Ad and store the result in Ad
AND Ad, As Logical AND the byte in As with the byte in Ad and store the result in Ad
EOR Ad, As Logical EOR the byte in As with the byte in Ad and store the result in Ad
INC Ad Add 1 to Ad
DEC Ad Subtract 1 from Ad
IN Ad, I Copy the byte at the input port into Ad
OUT Q, As Copy the byte in As to the output port
JP e Jump to label e
JZ e Jump to label e if the result of the last ADD, SUB, AND, EOR, INC, DEC, SHL or SHR was zero
JNZ e Jump to label e if the result of the last ADD, SUB, AND, EOR, INC, DEC SHL or SHR was not zero
RCALL s Push the program counter onto the stack to store the return address and then jump to label s
RET Pop the program counter from the stack to return to the place the subroutine was called from
SHL Ad Shift the byte in Ad one bit left putting a 0 into the lsb
SHR Ad Shift the byte in Ad one bit right putting a 0 into the msb


movi a0, $ff
out q, a0
in a0, i
rcall loop

out q, a0
jp end

dec a0
jz done
rcall loop

Originally the code had two parts to it -- a bytecode assembler that would parse the input and generate a simple bytecode format, and an interpreter that would consume the bytecode. However in the interests of space I merged these two together. Lexing is still done in one go on load using a regex. However, some error checking is now deferred until the line which contains the error is executed.

The hand-minified code before crushing is shown below:

u=function(s,i){s=s.toString(2);f=Array(9-s.length).join(0)+s};e=function(s,i){b.children[2].disabled=!i;throw s||"error"};O=function(s,i){try{s()}catch(m){u(q);m+="<p>line: "+(1+p)+"<p>stack: "+M+"<p>q: "+f;for(i=0;i<8;++i)u(a[i]),m+="<p>a"+i+": "+f;b.children[3].innerHTML=m}};C=function(s,i){c=b.children[b.children[2].disabled=z=q=I=p=0].value.toLowerCase(a=[M=[]]).split('\n');L={};for(p in c)r=c[p]=/^\s*(?:([a-z]+)(?:\s+(a(\d)|(\w+))(?:\s*,\s*(a(\d)|\$([0-9a-f]{2})|(\w+)))?)?|(\w+)\s*:|)\s*(?:;.*|)$/i.exec(c[p])||e(),r[9]&&(L[r[9]]=r[9]in L?e(r[9]+": exists"):p);for(i=0;i<8;++i)u(a[i]=p=0);e("ok",1)};S=function(s,i){r=c[p]||e("end");r[1]&&(s="1ret1in1out1jp1jz1jnz1rcall1shl1shr1inc1dec1add1sub1and1eor1mov1movi1".indexOf(1+r[1]+1),1+s||e(),i=+r[3],v=+r[6],0==s&&(p=M.pop(r[2]&&e())),u(I),4==s&&(a[i]=I=i<8&&r[8]=="i"?parseInt(prompt("in",f),2)&255:e()),7==s&&(q=v<8&&r[4]=="q"?a[v]:e()),11>s||21<s||1+L[r[4]]&&!r[5]||e(),21==s&&M.push(p),p=11==s||14==s&&z||17==s&&!z||21==s?L[r[4]]:p,26<s&&i>7&&e(),26<s&&40>s&&r[5]&&e(),27==s&&(a[i]<<=1),31==s&&(a[i]>>>=1),35==s&&(a[i]++),39==s&&(a[i]--),42<s&&60>s&&v>7&&e(),43==s&&(a[i]+=a[v]),47==s&&(a[i]+=256-a[v]),51==s&&(a[i]&=a[v]),55==s&&(a[i]^=a[v]),59==s&&(a[i]=a[v]),26<s&&56>s&&(z=!(a[i]&=255)),63==s&&(a[i]=parseInt(r[7]||e(),16)));e("running",++p)};O(C)