*************************************************************************
*																		*
*						Apple //GS Utility Macros						*
*								by										*
*							Lou Infeld									*
*																		*
*					Copyright Apple Computer, Inc. 1986-1990			*
*							All Rights Reserved							*
*																		*
*	Revision History													*
*	----------------													*
*	v1.0R1 (04/29/87) --	First Release								*
*							This file is a subset of the old			*
*								'M16.Utility'							*
*	v2.0R1 (01/22/88) --	First AsmIIGS release						*
*	v2.0R2 (02/11/88) --	"writelin" renamed "writeline"				*
*	v2.0R3 (03/29/88) --	"|" added to pea instrs to avoid Assembler	*
*								warning									*
*							"pushlong" was incorrect if &offset was "s"	*
*								or not specified						*
*	v2.0R4 (04/14/88) --	"writestr" and "writeline" didn't restore	*
*							STRING setting								*
*	v2.0R5 (08/22/88) --	"inc4" and "dec4" redefined to support only	*
*								positive 4 byte integers				*
*							"sub4" fixed so that there will not be any	*
*								truncation warnings generated			*
*							Pull macros now check for missing parameter *
*							  	or extra parameter						*
*	v2.0R6 (09/06/88) --	"dec4" fixed to support positive 4 byte		*
*								integers								*
*   v2.0R7 (4/4/89) -- changed "long" to "longmx", "short" to "shortmx" *
*                      and added stack based macros (rls3)              *
*************************************************************************

;............................................................
;
;        Pull long (4 bytes) from stack
;
;    pulllong address             - pull off stack to address
;    pulllong addr1,addr2         - pull off stack into 2 places
;    pulllong [zeropage],offset   - pull off stack and store indirectly             
;    pulllong                     - pull off stack and leave in A
;...............................................................
	MACRO
	pulllong &addr1,&addr2
	IF &addr1='' THEN
	IF &addr2­'' THEN 
		AERROR 'Illegal 2nd parameter'
	ENDIF
		pullword
		pullword
	ELSEIF &addr2='' THEN
		pullword  &addr1
		pullword  &addr1+2
	ELSEIF &addr1[1:1]='[' THEN
		ldy   #&addr2
		pullword  &addr1,y
		ldy   #&addr2+2
		pullword  &addr1,y
	ELSE
		pullword  &addr1
		sta   &addr2
		pullword  &addr1+2
		sta   &addr2+2
	ENDIF
	MEND
;...............................................................
;
;        Pull 3 bytes from stack
;
;    pull3 addr    -- pulls bytes off stack and stores in 'loc'
;...............................................................
	MACRO
	pull3 &addr
	IF &addr='' THEN
		AERROR 'A parameter must be specified'
	ENDIF
		pull1	&addr
		pullword  &addr+1
	MEND
;...............................................................
;
;        Pull 2 bytes from stack
;
;    pullword loc     -- pulls bytes off stack and stores in 'loc'
;    pullword loc,x   -- pulls bytes off stack and stores in 'loc,x'
;    pullword         -- pulls bytes off stack and leaves in A
;...............................................................
	MACRO
	pullword &addr,&reg
		pla
	IF &addr­'' THEN
		IF &reg­'' THEN
			sta		&addr,&reg
		ELSE
			sta		&addr
		ENDIF
	ELSEIF &reg­'' THEN 
		AERROR 'Illegal 2nd parameter'
	ENDIF
	MEND
;...............................................................
;
;        Pull 1 byte from stack
;
;    pull1 loc     -- pulls byte off stack and stores in 'loc'
;    pull1 loc,x   -- pulls byte off stack and stores in 'loc,x'
;    pull1         -- pulls byte off stack and leaves in A
;...............................................................
	MACRO
	pull1 &addr,&reg
		shortm
		pullword	&addr,&reg
		longm
	MEND
;...............................................................
;
;        Push 1 byte onto stack
;
;    push1 loc     -- pushes byte onto stack from 'loc'
;    push1 loc,x   -- pushes byte onto stack from 'loc,x'
;    push1 #n      -- pushes constant #n onto stack
;    push1         -- pushes byte onto stack (from A)
;...............................................................
	MACRO
	push1 &addr,&reg
		shortm
		pushword &addr,&reg
		longm
	MEND
;............................................................
;
;        Push long (4 bytes) onto stack
;
;    pushlong address             - push contents of address
;    pushlong address,x           - push contents of address,x
;    pushlong const,s             - push contents of stack+const    
;    pushlong #address/const      - push address or constant
;    pushlong [zeropage],offset   - push using indirect address
;...............................................................
	MACRO
	pushlong &addr,&offset
	IF &addr[1:1]='#' THEN
		pea		&addr[2:255]>>16
		pea		|&addr[2:255]
	ELSEIF &addr[1:1]='[' THEN
		ldy		#&offset+2
		pushword &addr,y
		ldy		#&offset
		pushword &addr,y
	ELSEIF &offset='s' THEN
		pushword &addr+2,s
		pushword &addr+2,s
	ELSEIF &offset='' THEN
		pushword  &addr+2
		pushword  &addr
	ELSE
		pushword &addr+2,&offset
		pushword &addr,&offset
	ENDIF
	MEND
;...............................................................
;
;        Push 3 bytes onto stack
;
;    push3 addr    -- pushes bytes onto stack from 'loc'
;    push3 addr,x  -- pushes bytes onto stack from 'loc,x'
;    push3 #n      -- pushes constant #n onto stack
;...............................................................
	MACRO
	push3 &addr,&reg
	IF &reg­'' THEN
		lda		&addr+1,&reg
		pha
		phb
		lda		&addr,&reg
		sta		1,s
	ELSEIF &addr[1:1]='#' THEN
		lda		#&addr[2:255]>>8
		pha
	GOTO .a
	ELSE
		lda		&addr+1
		pha
.a		phb
		lda		&addr
		sta		1,s
	ENDIF
	MEND
;...............................................................
;
;        Push 2 bytes onto stack
;
;    pushword loc     -- pushes bytes onto stack from 'loc'
;    pushword loc,x   -- pushes bytes onto stack from 'loc,x'
;    pushword #n      -- pushes constant #n onto stack
;    pushword         -- pushes bytes onto stack (from A)
;...............................................................
	MACRO
	pushword &addr,&reg
	IF &addr[1:1]='#' AND &SETTING('LONGA')='ON' THEN
		pea		|&addr[2:255]
	ELSE
		IF &addr­'' THEN
			IF &reg­'' THEN 
				lda		&addr,&reg
			ELSE
				lda		&addr
			ENDIF
		ENDIF
		pha
	ENDIF
	MEND
;...............................................................
;
;        Add 2 byte integers
;
;    add loc1,loc2,loc3 - adds 'loc1' to 'loc2' and stores in 'loc3'
;    add loc1,loc2      - adds 'loc1' to 'loc2' and leaves in A
;    add ,loc2,loc3     - adds A with 'loc2' and stores in 'loc3' 
;
;        'loc1' and 'loc2' can be constants
;...............................................................
         MACRO
	add   &a1,&a2,&a3
	IF &a1­'' THEN
		lda		&a1
	ENDIF
		clc
		adc		&a2
	IF &a3­'' THEN
		sta		&a3
	ENDIF
	MEND
;...............................................................
;
;        Add 4 byte integers
;
;    add4 loc1,loc2,loc3 - adds 'loc1' to 'loc2' and stores in 'loc3'
;    add4 ,loc2,loc3     - adds A to 'loc2' and stores in 'loc3'
;
;        'loc1' and 'loc2' can be constants
;...............................................................
	MACRO
	add4	&a1,&a2,&a3
	IF &a1­'' THEN
		lda		&a1
	ENDIF
		clc
		adc		&a2
		sta		&a3
	IF &a1='' THEN
		lda		#0
	ELSEIF &a1[1:1]='#' THEN
		lda		#^&a1[2:255]
	ELSE
		lda		&a1+2
	ENDIF
	IF &a2[1:1]='#' THEN
		adc		#^&a2[2:255]
	ELSE
		adc		&a2+2
	ENDIF
		sta		&a3+2
	MEND
;...............................................................
;
;        Increment a positive 4 byte integer
;
;    inc4 loc - increments 'loc'
;...............................................................
	MACRO
	inc4  &a1
		inc		&a1
		bne		@a
		inc		&a1+2
@a 
	MEND
;...............................................................
;
;        Subtract 2 byte integers
;
;    sub loc1,loc2,loc3 - subtracts 'loc2' from 'loc1' and stores in 'loc3'
;    sub loc1,loc2      - subtracts 'loc2' from 'loc1' and leaves in A
;    sub ,loc2,loc3      - subtracts 'loc2' from A and stores in 'loc3' 
;
;        'loc1' and 'loc2' can be constants
;...............................................................
	MACRO
	sub   &a1,&a2,&a3
	IF &a1­'' THEN
		lda		&a1
	ENDIF
		sec
		sbc		&a2
	IF &a3­'' THEN
		sta		&a3
	ENDIF
	MEND
;...............................................................
;
;        Subtract 4 byte integers
;
;    sub4 loc1,loc2,loc3 - subtracts 'loc2' from 'loc1' and stores in 'loc3'
;    sub4 ,loc2,loc3     - subtracts 'loc2' from A and stores in 'loc3'
;...............................................................
	MACRO
	sub4	&a1,&a2,&a3
	IF &a1­'' THEN
	IF &a1[1:1]='#' THEN
		lda		#<&a1[2:255]
	ELSE
		lda		&a1
	ENDIF
	ENDIF
		sec
	IF &a1[1:1]='#' THEN
		sbc		#<&a2[2:255]
	ELSE
		sbc		&a2
	ENDIF
		sta		&a3
	IF &a1='' THEN
		lda		#0
	ELSEIF &a1[1:1]='#' THEN
		lda		#^&a1[2:255]
	ELSE
		lda		&a1+2
	ENDIF
	IF &a2[1:1]='#' THEN
		sbc		#^&a2[2:255]
	ELSE
		sbc		&a2+2
	ENDIF
		sta		&a3+2
	MEND
;...............................................................
;
;        Decrement a positive 4 byte integer
;
;    dec4 loc - decrements 'loc'
;...............................................................
	MACRO
	dec4  &a1
		pha
		sec
		lda		&a1
		sbc		#1
		bcs		@a
		dec		&a1+2
@a 		sta		&a1
		pla
	MEND
;...............................................................
;
;        Define string
;
;        Generates a Pascal type of string
;...............................................................
	MACRO
	str	&string
		lclc	&sset
&sset	SETC &SETTING('STRING')
		STRING PASCAL
		dc.B &string
		STRING &sset
	MEND
;...............................................................
;
;        Define pointer
;...............................................................
	MACRO
	dp	&pointer
	dc.L &pointer
	MEND
;...............................................................
;
;        Left Shift 4 bytes
;
;        asl4  loc,#n                   ;n is shift count (pos)
;        asl4  loc,lnum                 ;lnum contains shift count (pos)
;        asl4  loc                      ;X contains shift count (pos)
;...............................................................
	MACRO
	asl4 &loc,&num
		lda   &loc+2
	IF &num­'' THEN
		ldx   &num
	ENDIF
@a		asl		a
		asl		&loc
		adc		#0
		dex
		bne		@a
		sta		&loc+2
	MEND
;...............................................................
;
;        Right Shift 4 bytes
;
;        lsr4  loc,#n                   ;n is shift count (pos)
;        lsr4  loc,lnum                 ;lnum contains shift count (pos)
;        lsr4  loc                      ;X contains shift count (neg)
;...............................................................
	MACRO
	lsr4  &loc,&num
	IF &num­'' THEN
		lda		&num
		eor		#$FFFF
		clc
		adc		#1
		tax
	ENDIF
		lda		&loc
@a		lsr		a
		lsr		&loc+2
		bcc		@b
		ora		#$8000
@b		inx
		bne		@a
		sta		&loc
	MEND
;...............................................................
;
;        Turn on native mode
;
;    The processor is put into 'native' mode.  8 bit or 16 bit
;    mode can be specified by using 'short' or 'long' as the
;    parameter.  If no parameter is specified, 'long' is assumed
;...............................................................
	MACRO
	native &mode
		clc
		xce
	IF &mode­'' THEN
		&mode
	ELSE
		longmx
	ENDIF
	MEND
;...............................................................
;
;        Turn on emulation mode
;
;    The processor is put into 'emulation' mode.
;...............................................................
	MACRO
	emulation
		sec
		xce
		longa	off
		longi	off
	MEND
;...............................................................
;
;        Set Memory & registers to 16 bits
;...............................................................
	MACRO
	longmx
		rep		#%00110000
		longa	on
		longi	on
	MEND
;...............................................................
;
;        Set Memory & A register to 16 bits
;...............................................................
	MACRO
	longm
		rep		#%00100000
		longa	on
	MEND
;...............................................................
;
;        Set X & Y registers to 16 bits
;...............................................................
	MACRO
	longx
		rep		#%00010000
		longi	on
	MEND
;...............................................................
;
;        Set Memory & registers to 8 bits
;...............................................................
	MACRO
	shortmx
		sep		#%00110000
		longa	off
		longi	off
	MEND
;...............................................................
;
;        Set Memory & A register to 8 bits
;...............................................................
	MACRO
	shortm
		sep		#%00100000
		longa	off
	MEND
;...............................................................
;
;        Set X & Y registers to 8 bits
;...............................................................
	MACRO
	shortx
		sep		#%00010000
		longi	off
	MEND
;...............................................................
;
;        Write string
;
;        writestr loc     - string at loc 'loc'
;        writestr #'ABC'  - string='ABC'
;        writestr         - A,Y has loc of string         
;...............................................................
	MACRO
	writestr &addr
	IF &addr­'' THEN
		IF &addr[1:1]='#' THEN
			bra		@b
			LCLC	&sset
&sset		SETC	&SETTING('STRING')
			STRING	PASCAL
@a			DC.B	&addr[2:255]
			STRING	&sset
@b			pea		@a>>16
			pea		|@a
		ELSE
			pea		&addr>>16
			pea		|&addr
		ENDIF
	ELSE
		phy
		pha
	ENDIF
		ldx		#$1C0C
		jsl		$E10000
	MEND
;...............................................................
;
;        Write line (string+CR)
;
;        writeline loc     - string at loc 'loc'
;        writeline #'ABC'  - string='ABC'
;        writeline 'ABC'   - string='ABC'
;        writeline         - CR only
;
;...............................................................
	MACRO
	writeline &addr
	IF &addr­'' THEN
		IF &addr[1:1]='#' OR &addr[1:1]='''' THEN
			bra		@b
			LCLC	&sset
&sset		SETC	&SETTING('STRING')
			STRING	PASCAL
			IF &addr[1:1]='#' THEN
@a				DC.B	&addr[2:255]
			ELSE
@a				DC.B	&addr[1:255]
			ENDIF
			STRING	&sset
@b			pea		@a>>16
			pea		|@a
		ELSE
			pea		&addr>>16
			pea		|&addr
		ENDIF
	ELSE
		pea		@c+1>>16
		pea		|@c+1
	ENDIF
		ldx		#$1A0C
@c		jsl		$E10000
	MEND
;...............................................................
;
;        Write character
;
;        writech          - char in A register
;        writech #'A'     - char='A'
;        writech ch,x     - char in loc 'ch,x'
;...............................................................
	MACRO
	writech &addr,&reg
	IF &addr­'' THEN
		IF &reg­'' THEN
			lda		&addr,&reg
		ELSE
			lda		&addr
		ENDIF
	ENDIF
		pha
		ldx		#$180C
		jsl		$E10000
	MEND
;...............................................................
;
;              Read a char from keyboard
;
;        readch addr     - char stored in 'addr'
;        readch          - char left in A register
;
;...............................................................
	MACRO
	readch &addr
		pea		0
		pea		1
		ldx		#$220C
		jsl		$E10000
		pla
	IF &addr­'' THEN
		sta		&addr
	ENDIF
	MEND

;
; DSect &Value
;
; Begin local data section.  This defines a dummy PC
; register which can be used in equates to avoid error-
; prone linked equates for setting up direct page or
; stack offsets.  The dummy PC begins at &Value and
; is updated by the appropriate amount by the BYTE,
; WORD, LONG, and BLOCK macros.
;
 MACRO
 DSect &Value
DummyPC SET &Value
 MEND


;
; DefineStack
;
; This is a specialized data section which defines
; a dummy PC starting at 1 in order to define a
; series of stack offsets using the BYTE, WORD,
; LONG, and BLOCK macros.
;
 MACRO
 DefineStack
DummyPC SET 1
 MEND

;
; &lab byte
;
; Equate the given label with the current dummy
; PC (defined by DSect, GlobalDSect, or
; DefineStack), and update the dummy PC by the
; appropriate amount.
;
 MACRO
&lab BYTE
&lab equ DummyPC
DummyPC SET DummyPC+1
 MEND

;
; &lab word
;
; Equate the given label with the current dummy
; PC (defined by DSect, GlobalDSect, or
; DefineStack), and update the dummy PC by the
; appropriate amount.
;
 MACRO
&lab WORD
&lab equ DummyPC
DummyPC SET DummyPC+2
 MEXIT
 MEND

;
; &lab long
;
; Equate the given label with the current dummy
; PC (defined by DSect, GlobalDSect, or
; DefineStack), and update the dummy PC by the
; appropriate amount.
;
 MACRO
&lab LONG
&lab equ DummyPC
DummyPC SET DummyPC+4
 MEND

;
; &lab block &size
;
; Equate the given label with the current dummy
; PC (defined by DSect, GlobalDSect, or
; DefineStack), and update the dummy PC by the
; appropriate amount (&size bytes).
;
 MACRO
&lab BLOCK &Value
 if &lab = '' goto .skiplab
&lab equ DummyPC
.skiplab
DummyPC SET DummyPC+&Value
 MEND

;
; &lab DSectSize
;
; This macro equates the given label with the
; current dummy PC defined by the DSect,
; GlobalDSect, or DefineStack macros.  This is
; useful for determing the size of data sections
; or parts of data sections.
;
 MACRO
&lab DSectSize
&lab equ DummyPC
 MEND

;
; &lab EndLocals
;
; This macro equates the given label with the
; current DummyPC-1 defined by the DSect,
; GlobalDSect, or DefineStack macros.  This is
; useful for determing the stack usage of locals.
;
 MACRO
&lab EndLocals
&lab equ DummyPC-1
 MEND

;
; CheckSize &size,&limit
;
; This macro causes an assembler error if
; &size > &limit.  Useful in checking the size
; of direct page assignments.
;
 MACRO
 CheckSize &size,&limit
 IF &Eval(&size)<=&Eval(&limit) goto .DONE
 AError 'Programmer-specified size limit exceeded: ABORT ASSEMBLY!'
.DONE
 MEND

* BegParms and EndParms are used to determine the size of passed parameters.

 MACRO
 BegParms
BegParmsPC SET DummyPC
 MEND

 MACRO
&lab EndParms
&lab equ DummyPC-BegParmsPC
 MEND

