## Copyright (C) 2016 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

:_start
	'0' SP BP NO_SHIFT MOVE_ALWAYS  ; Setup Base Pointer

	;; Prepare argv
	!4 R0 ADD BP ARITH_ALWAYS       ; ARGV_address = BP + 4
	{R0} PUSH_ALWAYS                ; Put argv on the stack

	;; Prepare envp
	'0' BP R0 NO_SHIFT MOVE_ALWAYS  ; Address we need to load from
	!0 R0 LOAD32 R0 MEMORY          ; Get ARGC
	!2 R0 ADD R0 ARITH_ALWAYS       ; OFFSET = ARGC + 2
	'0' R0 R0 '1' MOVE_ALWAYS       ; OFFSET = OFFSET * WORDSIZE
	'0' R0 R0 ADD BP ARITH2_ALWAYS  ; ENVP_address = BP + OFFSET
	{R0} PUSH_ALWAYS                ; Put envp on the stack

	;; Stack offset
	!4 BP ADD BP ARITH_ALWAYS       ; Fix BP

	^~FUNCTION_main CALL_ALWAYS     ; Jump right into main
	!1 R7 LOADI8_ALWAYS             ; Setup for final exit
	SYSCALL_ALWAYS                  ; Exit

# Unsigned Divide
:divide
	{R4} PUSH_ALWAYS                ; Protect R4
	{R3} PUSH_ALWAYS                ; Protect R3
	{R2} PUSH_ALWAYS                ; Protect R2

	'0' R0 R3 NO_SHIFT MOVE_ALWAYS  ; MOV R3,R0
	'0' R1 R2 NO_SHIFT MOVE_ALWAYS  ; MOV R2,R1

	!0 R0 LOADI8_ALWAYS             ; MOV R0,#0
	!0 CMPI8 R2 IMM_ALWAYS          ; CMP R2,#0
	!1 R0 SUB R0 ARITH_LT           ; SUBLT R0,R0,#1
	!0 CMPI8 R3 IMM_ALWAYS          ; CMP R3,#0
	!0 R3 RSUB R3 ARITH_LT          ; RSBLT R3,R3,#0
	'0' R0 R0 MVN_LT                ; MVNLT R0,R0
	'0' R0 R4 NO_SHIFT MOVE_ALWAYS  ; MOV R4,R0

	!32 R0 LOADI8_ALWAYS            ; MOV  R0,#32.
	!0 R1 LOADI8_ALWAYS             ; MOV  R1,#0
:divide_loop
	'0' R2 R2 ADDS R2 ARITH2_ALWAYS ; ADDS R2,R2,R2
	'0' R1 R1 ADCS R1 ARITH2_ALWAYS ; ADCS R1,R1,R1
	'0' R3 CMP R1 AUX_ALWAYS        ; CMP  R1,R3
	'0' R3 R1 SUB R1 ARITH2_GE      ; SUBGE  R1,R1,R3
	!1 R2 ADD R2 ARITH_GE           ; ADDGE  R2,R2,#1
	!1 R0 SUB R0 ARITH_ALWAYS       ; SUB  R0,R0,#1
	!0 CMPI8 R0 IMM_ALWAYS          ; CMP  R0,#0
	^~divide_loop JUMP_NE           ; BNE  loop

	'0' R2 R0 NO_SHIFT MOVE_ALWAYS  ; MOV R0,R2

	{R2} POP_ALWAYS                 ; Restore R2
	{R3} POP_ALWAYS                 ; Restore R3
	{R4} POP_ALWAYS                 ; Restore R4
	'1' LR RETURN

# Signed Divide
:divides
	{R4} PUSH_ALWAYS                ; Protect R4
	{R3} PUSH_ALWAYS                ; Protect R3
	{R2} PUSH_ALWAYS                ; Protect R2

	'0' R0 R3 NO_SHIFT MOVE_ALWAYS  ; MOV R3,R0
	'0' R1 R2 NO_SHIFT MOVE_ALWAYS  ; MOV R2,R1

	!0 R0 LOADI8_ALWAYS             ; MOV R0,#0
	!0 CMPI8 R2 IMM_ALWAYS          ; CMP R2,#0
	!0 R2 RSUB R2 ARITH_LT          ; RSBLT R2,R2,#0
	!1 R0 SUB R0 ARITH_LT           ; SUBLT R0,R0,#1
	!0 CMPI8 R3 IMM_ALWAYS          ; CMP R3,#0
	!0 R3 RSUB R3 ARITH_LT          ; RSBLT R3,R3,#0
	'0' R0 R0 MVN_LT                ; MVNLT R0,R0
	'0' R0 R4 NO_SHIFT MOVE_ALWAYS  ; MOV R4,R0

	!32 R0 LOADI8_ALWAYS            ; MOV  R0,#32.
	!0 R1 LOADI8_ALWAYS             ; MOV  R1,#0
:divides_loop
	'0' R2 R2 ADDS R2 ARITH2_ALWAYS ; ADDS R2,R2,R2
	'0' R1 R1 ADCS R1 ARITH2_ALWAYS ; ADCS R1,R1,R1
	'0' R3 CMP R1 AUX_ALWAYS        ; CMP  R1,R3
	'0' R3 R1 SUB R1 ARITH2_GE      ; SUBGE  R1,R1,R3
	!1 R2 ADD R2 ARITH_GE           ; ADDGE  R2,R2,#1
	!1 R0 SUB R0 ARITH_ALWAYS       ; SUB  R0,R0,#1
	!0 CMPI8 R0 IMM_ALWAYS          ; CMP  R0,#0
	^~divides_loop JUMP_NE          ; BNE  loop

	!0 CMPI8 R4 IMM_ALWAYS          ; CMP R4,#0
	!0 R2 RSUB R2 ARITH_NE          ; RSBNE R2,R2,#0
	'0' R2 R0 NO_SHIFT MOVE_ALWAYS  ; MOV R0,R2

	{R2} POP_ALWAYS                 ; Restore R2
	{R3} POP_ALWAYS                 ; Restore R3
	{R4} POP_ALWAYS                 ; Restore R4
	'1' LR RETURN

# Unsigned Modulus
:modulus
	{LR} PUSH_ALWAYS                ; Prepare to leverage divide
	^~divide CALL_ALWAYS            ; Use divide
	'0' R1 R0 NO_SHIFT MOVE_ALWAYS  ; MOV R0,R1
	{LR} POP_ALWAYS                 ; Prepare for return
	'1' LR RETURN

# Signed Modulus
:moduluss
	{LR} PUSH_ALWAYS                ; Prepare to leverage divide
	^~divides CALL_ALWAYS           ; Use divides
	'0' R1 R0 NO_SHIFT MOVE_ALWAYS  ; MOV R0,R1
	{LR} POP_ALWAYS                 ; Prepare for return
	'1' LR RETURN
