[BITS 16]
[ORG 0x7c00]

;;; Initialization

; Switch to 320x200x256 vga mode
mov ax, 0x0013
int 10h

; front buffer
; Computer's y axis goes down, but our's goes up.
; So when we set pixels, we do (y * -320) + x,
; with y starting at 1.
; But the screen area is 320*200*1 = 64000 bytes long,
; and the segment size is 2^16     = 65536 bytes long.
; Thus our first (bottom) line's start is shifted 1536
; bytes past the end of the screen area. So we substract
; this value from the segment base.
; And since the segment base is measured in multiple of
; 16, we substract only 1536 / 16 = 96
; To avoid counting y starting at 1, one could add a
; 'inc di' in the setpixel procedure (see below)
push 0xa000 - 96
pop fs

;;; Main program
mov bx,160 ; half screen width
mov dx,101 ; half screen height + 1 (or put 'inc di' in setpixel)
mov cl,0xdf
mainloop:
	call setpixel
	
	; From this separator to next one, the code checks if a
	; character is available, if not goes back to mainloop.
	; If there's something to read, it clears the keyboard
	; buffer by effectively reading the character
	; It then increments or decrements either
	;    dx (vertical position) or
	;    bx (horizontal position)
	; depending on the arrow key pressed (up/down/left/right)
	; 
	; THE WHOLE STUFF IS ONLY 29 BYTES OF MACHINE CODE
	; 
	; =======================================================
	
	; Check for next key
	mov ah, 0x01
	int 16h
	jz mainloop
		; Manage pressed key and jump back to mainloop
		xor ax,ax
		int 16h   ; clear keyboard buffer
		
		; We create an opcode ex nihilo, store it to
		; some memory place and jump to it.
		; 
		; inc / dec opcodes look like this :
		; 
		;   0100 drrr
		; 
		; Where d = 1 when decrementing, 0 otherwise
		; Where rrr is a register. Possible values are:
		; 
		;   000 ax    100 sp
		;   001 cx    101 bp
		;   010 dx    110 si
		;   011 bx    111 di
		; 
		; We choose 0100 d010 (inc/dec dx) for y movement,
		; and       0100 d011 (inc/dec bx) for x movement
		
		mov al,ah
		
		and al,0x01             ; bit 1: switch from dx to bx when it's a horiz key.
		
		and ah,0x12             ; zero = positive move, nonzero = negative move
		jz .positive
			or al,0x08          ; in opcode, set 'd' bit
		.positive:
		
		or al, 0x42             ; in opcode, set 'inc/dec' bit, and 'dx/bx' bit
		
		; insert generated opcode just before mainloop. since we never go before
		; mainloop, it's just dead code now, so we don't care about overwriting it.
		; Then jump to it. if you don't put an instruction (nop, jmp, ...) between
		; the mov and the place where you stored the opcode, it's not gonna work.
		
		mov [mainloop - 1],al
		jmp short mainloop - 1
		
	; =======================================================

; Setpixel at x=bx,y=dx,color=cl. Changes di
setpixel:
	; Compute pixel position : y*-320 + x
	; We must invert y because computer
	; graphics' y axis points downwards
	mov di, dx
	imul di, -320
	add di, bx
	
	; Set pixel
	mov byte [fs : di], cl
	ret

times 510-($-$$) db 0
dw 0xaa55
