;; File stud_io.inc for both Linux and FreeBSD.
;;
;; Copyright (c) Andrey Vikt. Stolyarov, 2009, 2015, 2017, 2021
;;
;; I, the author, hereby grant everyone the right to use this file for any
;; purpose, in any manner, in it's original or modified form, provided that
;; modified versions are clearly marked as such AND the original author's
;; copyright notice is kept along with the other authors' copyright notices
;; as appropriate within the file.
;;
;; Only the file as such must retain the copyright notice.  Programs created
;; using this file (e.g. binaries) don't need to have any mentions of the fact
;; this file was used to create them.
;;
;; This file is provided as is, with absolutely NO WARRANTY, and this
;; statement must be taken literally: "NO" means "NO", period.  Please be
;; sure to search Internet for various types of warranty (e.g., implied,
;; hidden, etc) and take into account that you don't have them all.
;;

%define STUD_IO_LINUX
;%define STUD_IO_FREEBSD

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; system dependend part

%ifdef STUD_IO_LINUX

; generic 3-param syscall
%macro _syscall_3 4
	push edx
	push ecx
	push ebx ; it is senseless to save eax as it holds the return
	push %1
	push %2
	push %3
	push %4
	pop edx
	pop ecx
	pop ebx
	pop eax
	int 0x80
	pop ebx
	pop ecx
	pop edx
%endmacro

; syscall_exit is the only syscall we use that has 1 parameter
%macro _syscall_exit 1
	mov ebx, %1	; exit code
	mov eax, 1	; 1 = sys_exit
	int 0x80
%endmacro

%elifdef STUD_IO_FREEBSD

; generic 3-param syscall
%macro _syscall_3 4
	push	%4
	push	%3
	push	%2
	mov	eax, %1
	push	eax
	int	0x80
	add	esp, 16
%endmacro

%macro _syscall_exit 1
	push	%1	; exit code
	mov	eax, 1	; 1 = sys_exit
	push	eax
	int	0x80
	; no cleanup - this will never return anyway
%endmacro

%else
%error You must define either STUD_IO_LINUX or STUD_IO_FREEBSD
%endif

;; system dependent part ends here
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; %1: descriptor   %2: buffer addr   %3: buffer length
; output: eax: read bytes
%macro _syscall_read 3
	_syscall_3 3,%1,%2,%3
%endmacro

; %1: descriptor   %2: buffer addr   %3: buffer length
; output: eax: written bytes
%macro _syscall_write 3
	_syscall_3 4,%1,%2,%3
%endmacro

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%macro 	PRINT 1
	pusha
	pushf
	jmp %%astr
%%str	db	%1
%%strln	equ	$-%%str
%%astr:	_syscall_write 1, %%str, %%strln
	popf
	popa
%endmacro

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%macro	PUTCHAR 1
	pusha
	pushf
%ifstr %1
	mov	al, %1
%elifnum %1
	mov	al, %1
%elifidni %1,al
	nop
%elifidni %1,ah
	mov	al, ah
%elifidni %1,bl
	mov	al, bl
%elifidni %1,bh
	mov	al, bh
%elifidni %1,cl
	mov	al, cl
%elifidni %1,ch
	mov	al, ch
%elifidni %1,dl
	mov	al, dl
%elifidni %1,dh
	mov	al, dh
%else
	mov	al, %1  ; let's hope it is a memory location such as [var]
%endif
	sub	esp, 4  ; reserve memory for buffer
	mov	edi, esp
	mov	[edi], al
	_syscall_write 1, edi, 1
	add	esp, 4
	popf
	popa
%endmacro

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%macro	GETCHAR 0
	pushf
	push	edi
	sub	esp, 4
	mov	edi, esp
	_syscall_read 0, edi, 1
	cmp	eax, 1
	jne	%%eof_reached
	xor	eax, eax
	mov	al, [edi]
	jmp	%%gcquit
%%eof_reached:
	xor	eax, eax
	not	eax 		; eax := -1
%%gcquit:
	add	esp, 4
	pop	edi
	popf
%endmacro

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%macro FINISH 0-1 0
	_syscall_exit %1
%endmacro
