Subleq is an example of a One-Instruction Set Computer (OISC).

It is named after its only instruction, which is SUbtract and Branch if Less than or EQual to zero.

Task
Your task is to create an interpreter which emulates a SUBLEQ machine.

The machine's memory consists of an array of signed integers.   These integers may be interpreted in three ways:

   simple numeric values 
   memory addresses 
   characters for input or output
Any reasonable word size that accommodates all three of the above uses is fine.

The program should load the initial contents of the emulated machine's memory, set the instruction pointer to the first address (which is defined to be address 0), and begin emulating the machine, which works as follows:

   Let A be the value in the memory location identified by the instruction pointer;   let B and C be the values stored in the next two consecutive addresses in memory.
   Advance the instruction pointer three words, to point at the address after the address containing C.
   If A is   -1   (negative unity),   then a character is read from the machine's input and its numeric value stored in the address given by B.   C is unused.
   If B is   -1   (negative unity),   then the number contained in the address given by A is interpreted as a character and written to the machine's output.   C is unused.
   Otherwise, both A and B are treated as addresses.   The number contained in address A is subtracted from the number in address B (and the difference left in address B).   If the result is positive, execution continues uninterrupted; if the result is zero or negative, the number in C becomes the new instruction pointer.
   If the instruction pointer becomes negative, execution halts.
Your solution may initialize the emulated machine's memory in any convenient manner, but if you accept it as input, it should be a separate input stream from the one fed to the emulated machine once it is running. And if fed as text input, it should be in the form of raw subleq "machine code" - whitespace-separated decimal numbers, with no symbolic names or other assembly-level extensions, to be loaded into memory starting at address   0   (zero).

For purposes of this task, show the output of your solution when fed the below   "Hello, world!"   program.

As written, this example assumes ASCII or a superset of it, such as any of the Latin-N character sets or Unicode;   you may translate the numbers representing characters (starting with 72=ASCII 'H') into another character set if your implementation runs in a non-ASCII-compatible environment. If 0 is not an appropriate terminator in your character set, the program logic will need some adjustment as well.

15 17 -1 17 -1 -1 16 1 -1 16 3 -1 15 15 0 0 -1 72 101 108 108 111 44 32 119 111 114 108 100 33 10 0
The above "machine code" corresponds to something like this in a hypothetical assembler language for a signed 8-bit version of the machine:

start:
    0f 11 ff subleq (zero), (message), -1
    11 ff ff subleq (message), -1, -1    ; output character at message
    10 01 ff subleq (neg1), (start+1), -1
    10 03 ff subleq (neg1), (start+3), -1
    0f 0f 00 subleq (zero), (zero), start
; useful constants
zero: 
    00      .data 0  
neg1: 
    ff      .data -1
; the message to print
message: .data "Hello, world!\n\0"
    48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a 00


	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
	@@@	ARM SUBLEQ for Linux			@@@
	@@@	Word size is 32 bits. The program is	@@@
	@@@	given 8 MB (2 Mwords) to run in.	@@@
	@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.text
.global	_start
	@@@ Linux syscalls
.equ	exit,	1
.equ	read,	3
.equ	write,	4
.equ	open,	5
_start:	pop	{r6}		@ Retrieve amount of arguments
	cmp	r6,#2		@ There should be exactly 2 (incl program)
	ldrne	r1,=usage	@ Otherwise, print usage and stop
	bne 	die
	pop	{r0,r1}		@ Retrieve filename
	mov	r0,r1
	mov	r1,#0		@ Try to open the file in read mode
	mov	r2,#0
	mov	r7,#open
	swi	#0
	movs	r5,r0		@ File handle in R5
	ldrmi	r1,=efile	@ If the file can't be opened, error
	bmi	die
	ldr	r8,=prog	@ R8 = pointer into program
	mov	r6,#0		@ At the beginning, there is no data
rdnum:	bl	fchar		@ Skip past whitespace
	cmp	r0,#32
	bls 	rdnum
	mov	r9,#0		@ R9 = current number being read
	subs	r10,r0,#'-	@ R10 is zero if number is negative
	bleq	fchar		@ And get next character
1:	sub	r0,r0,#'0	@ Subtract ASCII 0
	cmp	r0,#9
	ldrhi	r1,=echar
	bhi	die		@ Invalid digit = error
	mov	r1,#10
	mla	r0,r9,r1,r0	@ Multiply accumulator by 10 and add digit
	mov	r9,r0 		
	bl	fchar		@ Get next character
	cmp 	r0,#32		@ If it isn't whitespace...
	bhi	1b		@ ...then it's the next digit
	tst	r10,r10		@ If the number should be negative,
	rsbeq	r9,r9,#0	@ ...then negate it
	str	r9,[r8],#4	@ Store the number
	b	rdnum		@ And get the next number.
setup:	ldr	r0,=prog	@ Zero out the rest of program memory
	sub	r0,r8,r0	@ Zero to 8-word (32-byte) boundary
	orr	r0,r0,#31	@ Find address of last byte within
	add	r0,r0,r8	@ current 31-byte block
	mov	r1,#0		@ R1 = zero to write
1:	str	r1,[r8],#4	@ Write zeroes,
	cmp	r0,r8		@ until boundary reached.
	blo	1b 
	mov	r0,#0		@ 8 words of zeroes in r0-r7
	umull	r2,r3,r0,r1	@ A trick to produce 2 zero words in one
	umull	r4,r5,r0,r1	@ go: 0*0 = 0, long multiplication 
	umull	r6,r7,r0,r1	@ results in 2 words.
	ldr	r9,=mem_end
2:	stmia	r8!,{r0-r7}	@ Write 8 zero words at a time
	cmp	r8,r9		@ Are we at mem_end yet?
	blo	2b		@ If not, keep going
	ldr	r8,=prog	@ R8 = IP, starts at beginning
	ldr	r6,=prog	@ R6 = base address for memory
	mov	r12,#0xFFFF	@ 0x1FFFFF = address mask
	movt	r12,#0x1F
instr:	ldmia	r8!,{r9-r11}	@ R9, R10, R11 = A, B, C
	cmp	r9,#-1		@ If A=-1, get character
	beq	rchar
	cmp	r10,#-1		@ Otherwise, if B=-1, write character
	beq	wchar
	and	r9,r9,r12	@ Keep addresses within 2 Mwords
	and	r10,r10,r12
	ldr	r0,[r6,r9,lsl #2]	@ Grab [A] and [B]
	ldr	r1,[r6,r10,lsl #2]
	subs	r1,r1,r0		@ Subtract
	str	r1,[r6,r10,lsl #2]	@ Store back in [B]
	cmpmi	r0,r0		@ Set zero flag if negative
	bne	instr		@ If result is positive, next instruction
	lsls	r8,r11,#2	@ Otherwise, C becomes the new IP
	add	r8,r8,r6
	bpl	instr		@ If result is positive, keep going
	mov	r0,#0		@ Otherwise, we exit
	mov	r7,#exit
	swi	#0
	@@@ 	Read character into [B]
rchar:	mov	r0,#0		@ STDIN
	and	r10,r10,r12	@ Address of B
	add	r10,r6,r10,lsl #2	@ Kept in R10 out of harm's way
	mov	r1,r10
	mov	r2,#1		@ Read one character
	mov	r7,#read
	swi	#0
	cmp	r0,#1		@ We should have received 1 byte
	movne	r1,#-1		@ If not, write -1
	ldreqb	r1,[r10]	@ Otherwise, blank out the top 3 bytes
	str	r1,[r10]
	b	instr
	@@@	Write character in [A]
wchar:	mov	r0,#1		@ STDIN
	and	r1,r9,r12	@ Address of [A]
	add	r1,r6,r1,lsl #2
	mov	r2,#1		@ Write one character
	mov	r7,#write
	swi	#0
	b	instr
	@@@	Read character from file into R0. Tries to read more
	@@@	if the buffer is empty (as given by R6). Buffer in R11.
fchar:	tst	r6,r6		@ Any bytes in the buffer?
	ldrneb	r0,[r11],#1	@ If so, return next character from buffer
	subne	r6,r6,#1
	bxne	lr
	mov	r12,lr		@ Save link register
	mov	r0,r5		@ If not, read from file into buffer
	ldr	r1,=fbuf
	mov	r2,#0x400000
	mov	r7,#read
	swi	#0
	movs	r6,r0		@ Amount of bytes in r6
	beq	setup		@ If no more bytes, start the program
	ldr	r11,=fbuf	@ Otherwise, R11 = start of buffer
	mov	lr,r12
	b	fchar
	@@@	Write a zero-terminated string, in [r1], to stdout.
print:	push	{lr}
	mov	r2,r1	
1:	ldrb	r0,[r2],#1	@ Get character and advance pointer
	tst	r0,r0		@ Zero yet?
	bne	1b		@ If not, keep scanning
	sub	r2,r2,r1	@ If so, calculate length
	mov	r0,#1		@ STDOUT
	mov	r7,#write	@ Write to STDOUT
	swi	#0
	pop	{pc}
	@@@	Print error message in [r1], then end.
die:	bl	print
	mov	r0,#255
	mov	r7,#exit
	swi	#0
usage:	.asciz	"Usage: subleq <filename>\n"
efile:	.asciz	"Cannot open file\n"
echar:	.asciz	"Invalid number in file\n"
	@@@ Memory
.bss
.align	4
prog:	.space	0x400000	@ Lower half of program memory
fbuf:	.space	0x400000	@ File buffer and top half of program memory
mem_end	=	.