; Internal library routines - always included by the compiler
; Generic machine independent 6502 code.

		.section BSS_NOCLEAR
orig_stackpointer	.byte  ?	; stores the Stack pointer register at program start
		.send BSS_NOCLEAR

program_startup_clear_bss    .proc
	; this is always ran first thing from the start routine to clear out the BSS area
	.if  prog8_bss_section_size>0
		; reset all variables in BSS section to zero
		lda  #<prog8_bss_section_start
		ldy  #>prog8_bss_section_start
		sta  P8ZP_SCRATCH_W1
		sty  P8ZP_SCRATCH_W1+1
		ldx  #<prog8_bss_section_size
		ldy  #>prog8_bss_section_size
		lda  #0
		jsr  prog8_lib.memset
	.endif
		rts
		.pend



read_byte_from_address_in_AY_into_A	.proc
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		ldy  #0
		lda  (P8ZP_SCRATCH_W2),y
		rts
		.pend

read_byte_from_address_in_AY_into_A_65c02	.proc
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		lda  (P8ZP_SCRATCH_W2)
		rts
		.pend


write_byte_X_to_address_in_AY	.proc
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		ldy  #0
		txa
		sta  (P8ZP_SCRATCH_W2),y
		rts
		.pend

write_byte_X_to_address_in_AY_65c02	.proc
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		txa
		sta  (P8ZP_SCRATCH_W2)
		rts
		.pend


reg_less_uw	.proc
		;  AY < P8ZP_SCRATCH_W2?
		cpy  P8ZP_SCRATCH_W2+1
		bcc  _true
		bne  _false
		cmp  P8ZP_SCRATCH_W2
		bcc  _true
_false		lda  #0
		rts
_true		lda  #1
		rts
		.pend

reg_less_w	.proc
		; -- AY < P8ZP_SCRATCH_W2?
		cmp  P8ZP_SCRATCH_W2
		tya
		sbc  P8ZP_SCRATCH_W2+1
		bvc  +
		eor  #$80
+		bmi  _true
		lda  #0
		rts
_true		lda  #1
		rts
		.pend

reg_lesseq_uw	.proc
		; AY <= P8ZP_SCRATCH_W2?
		cpy  P8ZP_SCRATCH_W2+1
		beq  +
		bcc  _true
		lda  #0
		rts
+		cmp  P8ZP_SCRATCH_W2
		bcc  _true
		beq  _true
		lda  #0
		rts
_true		lda  #1
		rts
		.pend

reg_lesseq_w	.proc
		; -- P8ZP_SCRATCH_W2 <= AY ?   (note: order different from other routines)
		cmp  P8ZP_SCRATCH_W2
		tya
		sbc  P8ZP_SCRATCH_W2+1
		bvc  +
		eor  #$80
+		bpl  +
		lda  #0
		rts
+		lda  #1
		rts
		.pend


memcopy16_up	.proc
	; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
	;    clobbers register A,X,Y
		source = P8ZP_SCRATCH_W1
		dest = P8ZP_SCRATCH_W2
		length = P8ZP_SCRATCH_B1   ; (and SCRATCH_ZPREG)

		stx  length
		sty  length+1

		ldx  length             ; move low byte of length into X
		bne  +                  ; jump to start if X > 0
		dec  length             ; subtract 1 from length
+		ldy  #0                 ; set Y to 0
-		lda  (source),y         ; set A to whatever (source) points to offset by Y
		sta  (dest),y           ; move A to location pointed to by (dest) offset by Y
		iny                     ; increment Y
		bne  +                  ; if Y<>0 then (rolled over) then still moving bytes
		inc  source+1           ; increment hi byte of source
		inc  dest+1             ; increment hi byte of dest
+		dex                     ; decrement X (lo byte counter)
		bne  -                  ; if X<>0 then move another byte
		dec  length             ; we've moved 255 bytes, dec length
		bpl  -                  ; if length is still positive go back and move more
		rts                     ; done
		.pend


memset          .proc
	; -- fill memory from (P8ZP_SCRATCH_W1), length XY, with value in A.
	;    clobbers X, Y
		stx  P8ZP_SCRATCH_B1
		sty  P8ZP_SCRATCH_REG
		ldy  #0
		ldx  P8ZP_SCRATCH_REG
		beq  _lastpage

_fullpage	sta  (P8ZP_SCRATCH_W1),y
		iny
		bne  _fullpage
		inc  P8ZP_SCRATCH_W1+1          ; next page
		dex
		bne  _fullpage

_lastpage	ldy  P8ZP_SCRATCH_B1
		beq  +
-         	dey
		sta  (P8ZP_SCRATCH_W1),y
		bne  -

+           	rts
		.pend


memsetw		.proc
	; -- fill memory from (P8ZP_SCRATCH_W1) number of words in P8ZP_SCRATCH_W2, with word value in AY.
	;    clobbers A, X, Y
		sta  _val
		sty  _val+1
		ldx  P8ZP_SCRATCH_W1
		stx  P8ZP_SCRATCH_B1
		ldx  P8ZP_SCRATCH_W1+1
		inx
		stx  P8ZP_SCRATCH_REG                ; second page

		ldy  #0
		ldx  P8ZP_SCRATCH_W2+1
		beq  _lastpage

_fullpage
		lda  _val
		sta  (P8ZP_SCRATCH_W1),y        ; first page
		sta  (P8ZP_SCRATCH_B1),y            ; second page
		iny
		lda  _val+1
		sta  (P8ZP_SCRATCH_W1),y        ; first page
		sta  (P8ZP_SCRATCH_B1),y            ; second page
		iny
		bne  _fullpage
		inc  P8ZP_SCRATCH_W1+1          ; next page pair
		inc  P8ZP_SCRATCH_W1+1          ; next page pair
		inc  P8ZP_SCRATCH_B1+1              ; next page pair
		inc  P8ZP_SCRATCH_B1+1              ; next page pair
		dex
		bne  _fullpage

_lastpage	ldx  P8ZP_SCRATCH_W2
		beq  _done

		ldy  #0
-
		lda  _val
		sta  (P8ZP_SCRATCH_W1), y
		inc  P8ZP_SCRATCH_W1
		bne  +
		inc  P8ZP_SCRATCH_W1+1
+		lda  _val+1
		sta  (P8ZP_SCRATCH_W1), y
		inc  P8ZP_SCRATCH_W1
		bne  +
		inc  P8ZP_SCRATCH_W1+1
+               dex
		bne  -
_done		rts
		.section BSS
_val	.word ?
		.send BSS
		.pend



ror2_mem_ub	.proc
		; -- in-place 8-bit ror of byte at memory location in AY
		sta  P8ZP_SCRATCH_W1
		sty  P8ZP_SCRATCH_W1+1
		ldy  #0
		lda  (P8ZP_SCRATCH_W1),y
		lsr  a
		bcc  +
		ora  #$80
+		sta  (P8ZP_SCRATCH_W1),y
		rts
		.pend

rol2_mem_ub	.proc
		; -- in-place 8-bit rol of byte at memory location in AY
		sta  P8ZP_SCRATCH_W1
		sty  P8ZP_SCRATCH_W1+1
		ldy  #0
		lda  (P8ZP_SCRATCH_W1),y
		cmp  #$80
		rol  a
		sta  (P8ZP_SCRATCH_W1),y
		rts
		.pend


strcpy		.proc
		; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
		; it is assumed the target string is large enough.
		; returns the length of the string that was copied in Y.
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		ldy  #$ff
-		iny
		lda  (P8ZP_SCRATCH_W2),y
		sta  (P8ZP_SCRATCH_W1),y
		bne  -
		rts
		.pend

strcmp_expression	.proc
		; -- compare strings, result in A
		lda  _arg_s2
		ldy  _arg_s2+1
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		lda  _arg_s1
		ldy  _arg_s1+1
		jmp  strcmp_mem
		.section BSS
_arg_s1		.word  ?
_arg_s2		.word  ?
		.send BSS
		.pend

strcmp_mem	.proc
		; --   compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).
		;      Returns -1,0,1 in A, depending on the ordering. Clobbers Y.
		sta  P8ZP_SCRATCH_W1
		sty  P8ZP_SCRATCH_W1+1
		ldy  #0
_loop           lda  (P8ZP_SCRATCH_W1),y
		beq  _c1_zero
		cmp  (P8ZP_SCRATCH_W2),y
		beq  _equal
		bmi  _less
		lda  #1
		rts
_less           lda  #-1
		rts
_equal          iny
		bne  _loop
_c1_zero        lda  (P8ZP_SCRATCH_W2),y
		beq  +
		lda  #-1
+		rts
		.pend

strncmp_mem	.proc
		; --   compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).
                ;      Compares up to maximum length specified in X.
		;      Returns -1,0,1 in A, depending on the ordering. Clobbers X & Y.
		sta  P8ZP_SCRATCH_W1
		sty  P8ZP_SCRATCH_W1+1
		ldy  #0
_loop           lda  (P8ZP_SCRATCH_W1),y
		beq  _c1_zero
		cmp  (P8ZP_SCRATCH_W2),y
		beq  _equal
		bmi  _less
		lda  #1
		rts
_less           lda  #-1
		rts
_equal          dex
		bne +
		lda #0
		rts
+		iny
		bne  _loop
_c1_zero        lda  (P8ZP_SCRATCH_W2),y
		beq  +
		lda  #-1
+		rts
		.pend


strlen          .proc
        ; -- returns the number of bytes in the string in AY, in Y. Clobbers A.
		sta  P8ZP_SCRATCH_W1
		sty  P8ZP_SCRATCH_W1+1
		ldy  #0
-		lda  (P8ZP_SCRATCH_W1),y
		beq  +
		iny
		bne  -
+		rts
		.pend


containment_bytearray	.proc
	; -- check if a value exists in a byte array.
	;    parameters: P8ZP_SCRATCH_W1: address of the byte array, A = byte to check, Y = length of array (>=1).
	;    returns boolean 0/1 in A.
		dey
-		cmp  (P8ZP_SCRATCH_W1),y
		beq  +
		dey
		cpy  #255
		bne  -
		lda  #0
		rts
+		lda  #1
		rts
		.pend

containment_linearwordarray	.proc
	; -- check if a value exists in a linear word array.
	;    parameters: P8ZP_SCRATCH_W1: value to check, P8ZP_SCRATCH_W2: address of the word array, Y = number of elements in the array (>=1).
	;    returns boolean 0/1 in A.
		dey
		tya
		asl  a
		tay
-		lda  P8ZP_SCRATCH_W1
		cmp  (P8ZP_SCRATCH_W2),y
		bne  +
		lda  P8ZP_SCRATCH_W1+1
		iny
		cmp  (P8ZP_SCRATCH_W2),y
		beq  _found
		dey
+		dey
		dey
		cpy  #254
		bne  -
		lda  #0
		rts
_found		lda  #1
		rts
		.pend

containment_splitwordarray	.proc
	; -- check if a value exists in a split lsb/msb word array. (Assuming lsb array comes first, immediately followed by msb array)
	;    parameters: P8ZP_SCRATCH_W1: value to check, P8ZP_SCRATCH_W2: start address of the lsb word array, Y = number of elements in the array (>=1).
	;    returns boolean 0/1 in A.

	; store the needle value in SCRATCH_B1(lsb) and SCRATCH_REG(msb)
		lda  P8ZP_SCRATCH_W1
		sta  P8ZP_SCRATCH_B1
		lda  P8ZP_SCRATCH_W1+1
		sta  P8ZP_SCRATCH_REG

	; calculate where the msb array starts and put this in P8ZP_SCRATCH_W1  (_W2 is the start of the lsb array)
		tya
		clc
		adc  P8ZP_SCRATCH_W2
		sta  P8ZP_SCRATCH_W1
		lda  P8ZP_SCRATCH_W2+1
		adc  #0
		sta  P8ZP_SCRATCH_W1+1

	; search needle
		dey
-               lda  P8ZP_SCRATCH_REG
		cmp  (P8ZP_SCRATCH_W1),y
		bne  +
		lda  P8ZP_SCRATCH_B1
		cmp  (P8ZP_SCRATCH_W2),y
		beq  _found
+               dey
		cpy  #255
		bne  -
		lda  #0
		rts
_found          lda  #1
		rts
	.pend


memcopy_small   .proc
		; copy up to a single page (256 bytes) of memory.
		; note: only works for NON-OVERLAPPING memory regions!
		; P8ZP_SCRATCH_W1 = from address
		; P8ZP_SCRATCH_W2 = destination address
		; Y = number of bytes to copy  (where 0 means 256)
		cpy  #0
		beq  _fullpage
		dey
		beq  _lastbyte
_loop           lda  (P8ZP_SCRATCH_W1),y
                sta  (P8ZP_SCRATCH_W2),y
                dey
                bne  _loop
_lastbyte       lda  (P8ZP_SCRATCH_W1),y
                sta  (P8ZP_SCRATCH_W2),y
                rts
_fullpage       lda  (P8ZP_SCRATCH_W1),y
                sta  (P8ZP_SCRATCH_W2),y
                dey
                bne  _fullpage
                rts
		.pend


long_not_equals     .proc
		; checks if the 32 bits long value pointed to by AY is NOT equal to the one pointed to by P8ZP_SCRATCH_W1
		; returns A=1 if NOT equals otherwise A=0
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		ldy  #3
-		lda  (P8ZP_SCRATCH_W1),y
		cmp  (P8ZP_SCRATCH_W2),y
		bne  _notequal
		dey
		bpl  -
		lda  #0
		rts
_notequal       lda  #1
		rts
		.pend


long_add_inplace        .proc
		; long pointed to by AY += long pointed to by P8ZP_SCRATCH_W1
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		clc
		ldy  #0
		lda  (P8ZP_SCRATCH_W2),y
		adc  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		adc  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		adc  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		adc  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		rts
		.pend

long_sub_inplace    .proc
		; long pointed to by AY -= long pointed to by P8ZP_SCRATCH_W1
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		ldy  #0
		sec
		lda  (P8ZP_SCRATCH_W2),y
		sbc  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		sbc  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		sbc  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		sbc  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		rts
		.pend

long_or_inplace       .proc
		; long pointed to by AY OR= long pointed to by P8ZP_SCRATCH_W1
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		ldy  #0
		lda  (P8ZP_SCRATCH_W2),y
		ora  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		ora  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		ora  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		ora  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		rts
		.pend

long_and_inplace       .proc
		; long pointed to by AY AND= long pointed to by P8ZP_SCRATCH_W1
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		ldy  #0
		lda  (P8ZP_SCRATCH_W2),y
		and  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		and  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		and  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		and  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		rts
		.pend

long_xor_inplace       .proc
		; long pointed to by AY XOR= long pointed to by P8ZP_SCRATCH_W1
		sta  P8ZP_SCRATCH_W2
		sty  P8ZP_SCRATCH_W2+1
		ldy  #0
		lda  (P8ZP_SCRATCH_W2),y
		eor  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		eor  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		eor  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		iny
		lda  (P8ZP_SCRATCH_W2),y
		eor  (P8ZP_SCRATCH_W1),y
		sta  (P8ZP_SCRATCH_W2),y
		rts
		.pend

long_shiftleftX_inplace         .proc
		; bit shift left X bits the long value pointed to by AY
		cpx  #0
		beq  _end
		sta  P8ZP_SCRATCH_W1
		sty  P8ZP_SCRATCH_W1+1
-		ldy  #0
		lda  (P8ZP_SCRATCH_W1),y
		asl  a
		sta  (P8ZP_SCRATCH_W1),y
		iny
		lda  (P8ZP_SCRATCH_W1),y
		rol  a
		sta  (P8ZP_SCRATCH_W1),y
		iny
		lda  (P8ZP_SCRATCH_W1),y
		rol  a
		sta  (P8ZP_SCRATCH_W1),y
		iny
		lda  (P8ZP_SCRATCH_W1),y
		rol  a
		sta  (P8ZP_SCRATCH_W1),y
		dex
		bne  -
_end		rts
		.pend

long_shiftrightX_inplace        .proc
		; bit shift right X bits the long value pointed to by AY
		cpx  #0
		beq  _end
		sta  P8ZP_SCRATCH_W1
		sty  P8ZP_SCRATCH_W1+1
-		ldy  #3
		lda  (P8ZP_SCRATCH_W1),y
		asl  a  ; save sign bit
		lda  (P8ZP_SCRATCH_W1),y
		ror  a
		sta  (P8ZP_SCRATCH_W1),y
		dey
		lda  (P8ZP_SCRATCH_W1),y
		ror  a
		sta  (P8ZP_SCRATCH_W1),y
		dey
		lda  (P8ZP_SCRATCH_W1),y
		ror  a
		sta  (P8ZP_SCRATCH_W1),y
		dey
		lda  (P8ZP_SCRATCH_W1),y
		ror  a
		sta  (P8ZP_SCRATCH_W1),y
		dex
		bne  -
_end		rts
		.pend
