;
; egfx_core.asm -- graphics functions for Egfx
;
; This file is included by "egfx.asm".
; See egfx.inc for all the neat things that can be done.
;
; variable notes:
;  egfx_column is current mux column times two
;  egfx_numcolors is number of colors (including off) minus one
;  egfx_baseline is the baseline value (off value) minus one
;  egfx_spt is the scanline pointer table, which is 60 bytes long.


;
; Script functions
;

; ScriptResource calls another script
; resource ID in b.

Egfx_ScriptResource subroutine
	jsr	Param_B
ScriptResource
	pshx
	jsr	Resource_GetResource	; get resource pointer
	bcs	.bad			; if bad, ignore it
	jsr	ExecuteScript		; otherwise, do the script
.bad
	pulx
	rts


;
; Buffer functions
;

; Wipe erases the entire video buffer with the baseline color.

Egfx_Wipe subroutine
Wipe
	psha
	pshb
	pshx

	ldd	egfx_bufend		; get pointer to end of buffer
	xgdx				; put it in x
	ldaa	egfx_baseline		; get the baseline - 1
	inca				; make it the baseline value
.wipeloop
	dex				; decrement pointer
	staa	0,x			; store it
	cpx	egfx_bufferptr	; are we done?
	bne	.wipeloop		; if not, keep wiping.	

	pulx
	pulb
	pula
	rts


;
; Drawing functions
;

; Dot draws a pixel at the cursor location using the current drawing mode
; and settings.  If automove is set, it also adjusts the cursor location.

Egfx_Dot subroutine
Dot
	psha
	pshb
	pshx

	ldx	egfx_cursorptr	; get a pointer to the cursor
	jsr	CurrentDmodeVec		; draw the pixel
	jsr	AutoMoveVec		; do the automove
	stx	egfx_cursorptr	; store the new cursor pointer

	pulx
	pulb
	pula
	rts


; Skip draws nothing, but does the automove if it is set.

Egfx_Skip subroutine
Skip
	psha
	pshb
	pshx

	ldx	egfx_cursorptr	; get a pointer to the cursor
	jsr	AutoMoveVec		; do the automove
	stx	egfx_cursorptr	; store the new cursor pointer

	pulx
	pulb
	pula
	rts


; Xline draws a horizontal line using the current mode.
; If width is positive, the line is drawn width pixels to the right
; of the cursor; otherwise, it is drawn width pixels to the left.
; The cursor is moved to the end of the line, and then the automove,
; if any, is performed.
; width is in a.

Egfx_Xline subroutine
	jsr	Param_A
Xline
	psha
	pshb
	pshx

	ldx	egfx_cursorptr	; get cursor pointer
	tsta				; is a plus or minus?
	bmi	.dominus		; if negative, draw to the left
	beq	.done			; if zero, draw nothing

.rightloop
	jsr	CurrentDmodeVec		; draw the pixel
	deca				; decrement counter
	beq	.done			; if done, go away
	jsr	MoveRight		; move to the right
	bra	.rightloop		; do some more

.dominus
.leftloop
	jsr	CurrentDmodeVec		; draw the pixel
	inca				; increment negative counter
	beq	.done			; if done, we're done
	jsr	MoveLeft		; move to the left
	bra	.leftloop		; do some more
.done
	jsr	AutoMoveVec		; do the automove
	stx	egfx_cursorptr	; store the new cursor pointer

	pulx
	pulb
	pula
	rts


; Yline draws a vertical line using the current mode.
; It is just like Xline except vertical.
; height is in b.

Egfx_Yline subroutine
	jsr	Param_B
Yline
	psha
	pshb
	pshx

	tba				; put thingee in a
	ldx	egfx_cursorptr	; get cursor pointer
	tsta				; is a plus or minus?
	bmi	.dominus		; if negative, draw to up
	beq	.done			; if zero, draw nothing

.downloop
	jsr	CurrentDmodeVec		; draw the pixel
	deca				; decrement counter
	beq	.done			; if done, stop
	jsr	MoveDown		; otherwise move down
	bra	.downloop		; and keep going

.dominus
.uploop
	jsr	CurrentDmodeVec		; draw the pixel
	inca				; increment negative counter
	beq	.done			; if done, stop
	jsr	MoveUp			; otherwise move up
	bra	.uploop			; and keep going
.done
	jsr	AutoMoveVec		; do the automove
	stx	egfx_cursorptr	; store the new cursor pointer

	pulx
	pulb
	pula
	rts


; Frame draws a rectangular frame using the current mode.
; The cursor is left in the same place, except for the automove.
; width is in a, height is in b.

Egfx_Frame subroutine
	jsr	Param_AB
Frame
	psha
	pshb

	psha				; stash width on stack
	ldaa	#OPCODE_RTS		; temporarily disable automove
	staa	AutoMoveVec
	pula
	jsr	Xline			; draw the first horizontal line
	jsr	Yline			; draw the first vertical line
	nega				; negate width
	jsr	Xline			; draw the second horizontal line
	ldaa	#OPCODE_JMPEXT		; enable automove
	staa	AutoMoveVec
	negb				; negate height
	jsr	Yline			; draw the second vertical line
	
	pulb
	pula
	rts


; Rect draws a filled rectangle using the current mode.
; The cursor is left in the same place, except for the automove.
; width is in a, height is in b.

Egfx_Rect subroutine
	jsr	Param_AB
Rect
	psha
	pshb
	pshx

	psha				; stash a
	ldaa	#OPCODE_RTS		; disable autmove
	staa	AutoMoveVec
	pula				; restore a

	ldx	egfx_cursorptr	; get the current cursor pointer
	tsta				; is width negative or positive?
	bmi	.dominus		; if negative, count backwards
	beq	.done			; it zero, do nothing.

.rightloop
	psha				; stash a
	ldaa	egfx_ycursor		; get cursor pos
	psha				; stash cursor position on stack
	jsr	Yline			; draw a vert line of the correct height
	pula				; get back cursor pos
	staa	egfx_ycursor		; store it
	pula				; get back a

	pshb				; save b
	jsr	MoveRight		; move a pixel to the right
	pulb				; restore b
	stx	egfx_cursorptr	; store the new cursor pointer
	
	deca				; decrement width counter
	bne	.rightloop		; if not done, do some more
	bra	.done			; that's all

.dominus
.leftloop
	psha				; stash a
	ldaa	egfx_ycursor		; get cursor pos
	psha				; stash cursor position on stack
	jsr	Yline			; draw a vert line of the correct height
	pula				; get back cursor pos
	staa	egfx_ycursor		; store it
	pula				; get back a

	pshb				; save b
	jsr	MoveLeft		; move a pixel to the right
	pulb				; restore b
	stx	egfx_cursorptr	; store the new cursor pointer
	
	inca				; increment negative counter
	bne	.leftloop		; if not done, do some more
.done
	ldaa	#OPCODE_JMPEXT		; enable automove
	staa	AutoMoveVec
	jsr	AutoMoveVec		; do the automove
	stx	egfx_cursorptr	; store the new cursor pointer

	pulx
	pulb
	pula
	rts


;
; screen functions
;
 
; ScreenSet adjusts the scanline pointer table so that the top-left corner
; of the visible screen is at the given coordinates in the video buffer.
; If the screen goes off the buffer in the horizontal direction, it
; wraps around.  In the vertical direction, it does NOT wrap, and will
; be garbage.
; x is in a, y is in b

Egfx_ScreenSet subroutine
	jsr	Param_AB
ScreenSet
	psha
	pshb
	pshx
	pshy

	pshb			; stash y value on stack
	ldab	egfx_ysize	; get buffer height
	mul			; multiply by x value
	xgdx			; put result in x register
	pulb			; get back y value
	abx			; add to result
	xgdx			; put result in d register
	addd	egfx_bufferptr	; add pointer to start of buffer
	ldx	#0		; start with scanline 0
	ldy	#egfx_hpt	; put in the header pointers too
.loop
	pshx
	ldx	egfx_headerptr	; get the header pointer
	stx	0,y		; store it in the table
	iny			; next entry
	iny
	pulx
.wrap
	cpd	egfx_bufend	; is it past the end of the buffer?
	blo	.nowrap	; if not, no need to wrap
	subd	egfx_bufsize	; if so, subtract a buffer length
	bra	.wrap		; and try again
.nowrap
	std	egfx_spt,x	; store the pointer in the scanline table
	addd	egfx_ysize16	; go to next line in buffer
	inx			; go to next scanline pointer
	inx
	cpx	#58		; is it done yet?
	bls	.loop		; if not, keep going.
	
	puly
	pulx
	pulb
	pula
	rts


; RangeSet sets a range of scanlines to the given coordinates.
; x is in a, y is in b, starting column number is in x, width is in y

Egfx_RangeSet subroutine
	jsr	Param_XYAB
RangeSet
	psha
	pshb
	pshx
	pshy

	xgdx			; put x into d
	aslb			; multiply column number by two
	xgdx			; put back into x
	pshx			; stash on stack for later

	pshb			; stash y value on stack
	ldab	egfx_ysize	; get buffer height
	mul			; multiply by x value
	xgdx			; put result in x register
	pulb			; get back y value
	abx			; add to result
	xgdx			; put result in d register
	addd	egfx_bufferptr	; add pointer to start of buffer
	pulx			; get back column number thing
	
.loop
	cpd	egfx_bufend	; is it past the end of the buffer?
	blo	.nowrap		; if not, no need to wrap
	subd	egfx_bufsize	; if so, subtract a buffer length
	bra	.loop		; and try again
.nowrap
	std	egfx_spt,x	; store the pointer in the scanline table
	addd	egfx_ysize16	; go to next line in buffer
	inx			; go to next scanline pointer
	inx
	dey			; decrement width counter
	bne	.loop		; if not zero yet, keep going.

	puly			; get back counter in y	
	pshy

	xgdx			; put x (last column plus one times two) in d
	addd	#egfx_hpt-2	; add offset to header ptr table minus two
	xgdx			; put ptr to last header ptr back in x
	ldd	egfx_headerptr	; get the header ptr
.hptloop
	std	0,x		; store it
	dex			; go to previous one
	dex
	dey			; decrement counter
	bne	.hptloop	; if not zero, keep going

	puly
	pulx
	pulb
	pula
	rts


; Pan moves the entire screen within the video buffer, according to the
; current pan setting

Egfx_Pan subroutine
Pan
	psha
	pshb
	pshx
	pshy

	ldy	#58		; go to the last scanline
	ldx 	#egfx_hpt+58	; go to the last entry in the header ptr table
.loop
	pshx			; store the table ptr
	ldx	0,x		; put the header ptr in x
	jsr	PanVec		; do the pan function
	pulx			; get back table ptr
	dex			; go to previous table entry
	dex
	dey			; go to previous scanline
	dey
	bne	.loop		; if not done, loop
	ldx	0,x
	jsr	PanVec		; do the last one

	puly
	pulx
	pulb
	pula
	rts


; PanThis pans the entire screen according to the given direction.
; Direction is in a.

Egfx_PanThis subroutine
	jsr	Param_A
PanThis
	pshx

	ldx	PanVec+1	; get the old pan vector
	pshx			; stash it
	jsr	SetPan		; set a new pan mode
	jsr	Pan		; do the pan
	pulx			; get back the old pan mode
	stx	PanVec+1	; restore it

	pulx
	rts			; very cheesy implementation, yes...
	

; PanRange pans a range of scanlines according to the current pan setting.
; starting column is in a, range width is in b

Egfx_PanRange subroutine
	jsr	Param_AB
PanRange
	psha
	pshb
	pshx
	pshy

	stab	egfx_stash1	; store width in stash1
	tab			; put column in b
	clra			; clear hibyte
	aslb			; multiply column by two
	ldx	#egfx_hpt	; get table pointer
	abx			; add column times two
	xgdy			; put column times two into y
.loop
	pshx			; store the table ptr
	ldx	0,x		; put the header ptr in x
	jsr	PanVec		; do the pan function
	pulx			; get back table ptr
	iny			; go to next scanline ptr 
	iny
	inx			; go to next table entry
	inx
	dec	egfx_stash1	; decrement width counter
	bne	.loop		; if not done, loop

	puly
	pulx
	pulb
	pula
	rts


;
; cursor functions
;

; CursorSet moves the cursor to the given absolute coordinates.
; If the values are outside the valid range, they wrap.
; x is in a, y is in b.

Egfx_CursorSet subroutine
	jsr	Param_AB
CursorSet
	psha
	pshb
	pshx

.xloop
	cmpa	egfx_xsize		; is x too big?
	blo	.yloop			; if not, try y
	suba	egfx_xsize		; if so, wrap around
	bra	.xloop			; keep at it until it's valid
.yloop
	cmpb	egfx_ysize		; if y too big?
	blo	.wrapdone		; if not, we're okay
	subb	egfx_ysize		; if so, wrap around
	bra	.yloop			; keep at it until it's valid
.wrapdone
	staa	egfx_xcursor		; store the new x value
	stab	egfx_ycursor		; store the new y value
	pshb				; stash y value on stack
	ldab	egfx_ysize		; get buffer height
	mul				; multiply by x value
	xgdx				; put result in x register
	pulb				; get back y value
	abx				; add to result
	xgdx				; put result in d register
	addd	egfx_bufferptr	; add pointer to start of buffer
	std	egfx_cursorptr	; store it as the cursor pointer

	pulx
	pulb
	pula
	rts


; Xset sets the x value of the cursor.
; x is in a

Egfx_Xset subroutine
	jsr	Param_A
Xset
	pshb
	ldab	egfx_ycursor	; we have to do the multiply anyway
	jsr	CursorSet	; so might as well do it the easy way
	pulb
	rts


; Yset sets the y value of the cursor.
; y is in b

Egfx_Yset subroutine
	jsr	Param_B
Yset
	psha
	pshb

.yloop
	cmpb	egfx_ysize		; is it too big?
	blo	.wrapdone		; if not, we're okay
	subb	egfx_ysize		; if so, wrap around
	bra	.yloop			; keep going until it works
.wrapdone
	clra				; put zero in high byte
	subb	egfx_ycursor		; sub current y value from new y value
	bpl 	.itsplus		; if it's positive, don't worry about it
	deca				; if it's negative, make high byte $FF
.itsplus
	addd	egfx_cursorptr	; add 16-bit signed value to cursor ptr
	std	egfx_cursorptr	; store it

	pulb
	stab	egfx_ycursor		; store the new y cursor
	pula
	rts


; Xjump moves the cursor horizontally by the specified amount.
; If it goes too far, it wraps.
; delta x is in a.

Egfx_Xjump subroutine
	jsr	Param_A
Xjump
	psha

	tsta
	bmi	.dominus

	adda	egfx_xcursor	; add to current cursor position
.plusloop
	cmpa	egfx_xsize	; is it off the screen?
	blo	.isgood		; if not, use it
	suba	egfx_xsize	; otherwise, do some wrapping
	bra	.plusloop	; and keep trying.

.dominus
	adda	egfx_xcursor	; add to current cursor position
	bcs	.isgood		; if it didn't go off the screen, it's good
.minusloop
	adda	egfx_xsize	; if it did, do a wrap
	bcc	.dominus	; if that still didn't do it, do it again

.isgood
	jsr	Xset		; go to this x position
	
	pula
	rts	


; Yjump moves the cursor vertically by the specified amount.
; If it goes too far, it wraps.
; delta y is in b.	

Egfx_Yjump subroutine
	jsr	Param_B
Yjump
	pshb
	tstb
	bmi	.dominus

	addb	egfx_ycursor	; add to current cursor position
.plusloop
	cmpb	egfx_ysize	; is it off the screen?
	blo	.isgood		; if not, use it
	subb	egfx_ysize	; otherwise, do some wrapping
	bra	.plusloop	; and keep trying.

.dominus
	addb	egfx_ycursor	; add to current cursor position
	bcs	.isgood		; if it didn't go off the screen, it's good
.minusloop
	addb	egfx_ysize	; if it did, do a wrap
	bcc	.dominus	; if that still didn't do it, do it again

.isgood
	jsr	Yset		; go to this y position
	
	pulb
	rts	


; CursorStore stores the current cursor position in reg 0 for later retrieval.

Egfx_CursorStore subroutine
CursorStore

	psha
	pshx

	ldx	egfx_headerptr		; get buf header ptr
	ldaa	egfx_xcursor		; get the x position
	staa	HEADER_CursorX0,x	; store it
	ldaa	egfx_ycursor		; get the y position
	staa	HEADER_CursorY0,x	; store it

	pulx
	pula
	rts


; CursorStoreNum stores the cursor position in one of the 4 registers.
; register num in b

Egfx_CursorStoreNum subroutine
	jsr	Param_B
CursorStoreNum

	pshb
	pshx

	ldx	egfx_headerptr		; get buf header
	abx				; add offset for register num
	ldab	egfx_xcursor		; get the x position
	stab	HEADER_CursorX0,x	; store it
	ldab	egfx_ycursor		; get the y position
	stab	HEADER_CursorY0,x	; store it

	pulb
	pulx
	rts


; CursorRestore restores the cursor from register 0.

Egfx_CursorRestore subroutine
CursorRestore	
	psha
	pshb
	pshx

	ldx	egfx_headerptr		; get buffer header ptr
	ldaa	HEADER_CursorX0,x	; get x position
	ldab	HEADER_CursorY0,x	; get y position
	jsr	CursorSet		; go there

	pulx
	pulb
	pula
	rts


; CursorRestoreNum restores the cursor from one of the registers.
; register num in b

Egfx_CursorRestoreNum subroutine
	jsr	Param_B
CursorRestoreNum	

	psha
	pshb
	pshx

	ldx	egfx_headerptr		; get buffer header ptr
	abx				; add offset for register num
	ldaa	HEADER_CursorX0,x	; get x position
	ldab	HEADER_CursorY0,x	; get y position
	jsr	CursorSet		; go there

	pulx
	pulb
	pula
	rts


;
; setting functions
;

; SetBuffer sets the current drawing buffer to the given resource.
; resource ID is in b.

Egfx_SetBuffer subroutine
	jsr	Param_B
SetBuffer
	jsr	Resource_GetResource	; get the resource ptr
	bcs	.bad			; if bad resource id, do nothing
	stx	egfx_headerptr
	ldaa	HEADER_xsize,x		; otherwise, transfer data
	staa	egfx_xsize
	ldaa	HEADER_ysize,x
	staa	egfx_ysize
	ldd	HEADER_bufferptr,x
	std	egfx_bufferptr
	ldd	HEADER_bufsize,x
	std	egfx_bufsize
	ldd	HEADER_bufend,x
	std	egfx_bufend

	ldaa	egfx_xcursor		; set the cursor position in this buf
	ldab	egfx_ycursor		; (wrapping if necessary)
	jsr	CursorSet	
.bad
	rts


; SetColor sets the current color value.
; color is in a.

Egfx_SetColor subroutine
	jsr	Param_A
SetColor
	staa	egfx_color		; store it
	rts				; that's all


; SetDelta sets the current delta value.
; delta is in a.

Egfx_SetDelta subroutine
	jsr	Param_A
SetDelta
	staa	egfx_delta		; store it
	rts				; that's all


; SetBaseline sets the current baseline value.
; baseline is in a.

Egfx_SetBaseline subroutine
	jsr	Param_A
SetBaseline
	deca				; egfx_baseline is baseline - 1
	staa	egfx_baseline		; store it
	inca				; restore entry value
	rts				; that's all


; SetDrawingMode sets the drawing mode for subsequent drawing operations.
; drawing mode code is in a.

Egfx_SetDrawingMode subroutine
	jsr	Param_A
SetDrawingMode
	psha
	pshb
	pshx

	cmpa	#EGFX_NUMDMODES		; is the number invalid?
	blo	.allgood		; if not, continue
	clra				; if so, set the nothing mode
.allgood
	tab				; put code in b
	aslb				; multiply by two
	ldx	#DmodeTable		; get ready for a cheesy lookup table
	abx				; add offset
	ldd	0,x			; get pointer to function
	std	CurrentDmodeVec+1	; store it in the vector

	pulx
	pulb
	pula
	rts


; SetAutoMove sets the automove function for subsequent drawing operations.
; If automove >= AUTOMOVE_SCRIPT, then the graphics script with resource
; ID autmove-AUTOMOVE_SCRIPT is executed.
; automove code in is a.

Egfx_SetAutoMove subroutine
	jsr	Param_A
SetAutoMove
	psha
	pshb
	pshx

	cmpa	#AUTOMOVE_SCRIPT	; is it a real automove?
	blo	.allgood		; if so, continue
	suba	#AUTOMOVE_SCRIPT	; if not, it's a script!  Get the ID.
	staa	egfx_automovescript	; store it
	ldd	#MoveScript		; get pointer to automove function
	bra	.storeit		; go store it in the vector
.allgood
	tab				; put code in b
	aslb				; multiply by two
	ldx	#AutoMoveTable		; get ready for a cheesy lookup table
	abx				; add offset
	ldd	0,x			; get pointer to function
.storeit
	std	AutoMoveVec+1		; store it in the vector

	pulx
	pulb
	pula
	rts


; SetPan sets the pan function for subsequent pan operations.
; pan code in is a.

Egfx_SetPan subroutine
	jsr	Param_A
SetPan	
	psha
	pshb
	pshx

	cmpa	#EGFX_NUMPANS	; is the number invalid?
	blo	.allgood	; if not, continue
	clra			; if so, set the nothing mode
.allgood
	tab			; put code in b
	aslb			; multiply by two
	ldx	#PanTable	; get ready for a cheesy lookup table
	abx			; add offset
	ldd	0,x		; get pointer to function
.storeit
	std	PanVec+1	; store it in the vector

	pulx
	pulb
	pula
	rts


;
; private functions
;

; DmodeTable is a lookup table for the mode functions

DmodeTable
	dc.w	DmodeNone
	dc.w	DmodeCopy
	dc.w	DmodeBlend
	dc.w	DmodeShade
	dc.w	DmodeInvert
	dc.w	DmodeErase
	

; The DMode functions assume that the cursor position is in x,
; and they destroy the b register.

; DmodeCopy copies the color variable to the cursor location.

DmodeCopy subroutine
	ldab	egfx_color	; get the color value
	stab	0,x		; store it
	rts

; DmodeBlend averages the color variable and the value already at
; the cursor position.

DmodeBlend subroutine
	ldab	0,x		; get the current pixel value
	addb	egfx_color	; add the color value
	rorb			; divide by two, accounting for carry out
	stab	0,x		; store it
	rts

; DmodeShade adds the delta value to the current pixel value.
; It clips at 0 or 255, depending on the sign of delta.

DmodeShade subroutine
	ldab	0,x		; get the current pixel value
	addb	egfx_delta	; add the delta value
	bcc	.noclip		; if no carry out, everything's fine

	ldab	egfx_delta	; get the sign of delta
	bmi	.darken		; are we darkening?
	ldab	#$FF		; if not, clip to 255
	bra	.noclip
.darken
	clrb			; if so, clip to 0
.noclip
	stab	0,x		; store the new value
	rts

; DmodeInvert replaces the pixel with:
; maxcolor - (pixel - baseline) + baseline.
; If the pixel is between baseline and baseline+maxcolor,
; it appears to invert the brightness.

DmodeInvert subroutine
	ldab	egfx_numcolors		; maxcolor +
	subb	0,x			; - pixel
	addb	egfx_baseline		; + (baseline - 1)
	addb	egfx_baseline		; + (baseline - 1)
	addb	#2			; + 2
	stab	0,x			; store it
	rts

; DmodeErase copies the baseline to the cursor location

DmodeErase subroutine
	ldab	egfx_baseline		; get baseline - 1
	incb				; adjust for real baseline
	stab	0,x			; store it
	rts

; DmodeNone does nothing.  Eet daus nauthing.

DmodeNone subroutine
	rts


; AutoMoveTable is a lookup table for the automove functions

AutoMoveTable
	dc.w	MoveNone
	dc.w	MoveLeft
	dc.w	MoveRight
	dc.w	MoveUp
	dc.w	MoveDown


; The Move functions assume the cursor position is in x.
; They return the new cursor position in x.
; They destroy the b register. 

; MoveNone does not move the cursor

MoveNone subroutine
	rts

; MoveLeft moves the cursor one pixel to the left

MoveLeft subroutine
	dec	egfx_xcursor		; decrement xcursor
	xgdx				; put cursor pos in d
	subd	egfx_ysize16		; subtract the buffer height
	cpd	egfx_bufferptr	; did it wrap?
	bhs	.nowrap			; if not, it's all good
	addd	egfx_bufsize		; if so, wrap to the right side
	xgdx	 			; put cursor pos back in x
	ldab	egfx_xsize		; get buffer width
	decb				; minus one
	stab	egfx_xcursor		; store it as xcursor
	rts
.nowrap
	xgdx				; put cursor pos back in x
	rts

; MoveRight moves the cursor one pixel to the right

MoveRight subroutine
	inc	egfx_xcursor		; increment xcursor
	ldab	egfx_ysize		; get buffer height
	abx				; add to cursor position
	cpx	egfx_bufend		; is it too big now?
	blo	.nowrap			; if not, it's all good
	xgdx				; if so, put x into d
	subd	egfx_bufsize		; wrap to the left side
	xgdx				; put it back
	clr	egfx_xcursor		; set xcursor to zero
.nowrap
	rts

; MoveUp moves the cursor one pixel up

MoveUp subroutine
	dex				; decrement pointer
	dec	egfx_ycursor		; decrement ycursor
	bpl	.nowrap			; is that a wrap?
	ldab	egfx_ysize		; if so, get ready to add ysize
	abx				; add it to x
	decb				; put ysize - 1 in b
	stab	egfx_ycursor		; that's the new ycursor
.nowrap
	rts

; MoveDown moves the cursor down one pixel

MoveDown subroutine
	inx				; increment pointer
	ldab	egfx_ycursor		; get the ycursor
	incb				; increment it
	cmpb	egfx_ysize		; is it too big?
	blo	.nowrap			; if not, no wrap
	xgdx				; if so, put x into d
	subd	egfx_ysize16		; subtract buffer height
	xgdx				; put it back
	clrb				; say that ycursor is zero
.nowrap
	stab	egfx_ycursor		; store ycursor
	rts


; MoveScript calls the graphics script in egfx_automovescript.

MoveScript subroutine
	ldab	egfx_automovescript	; get the resource ID
	jsr	ScriptResource		; do it
	ldx	egfx_cursorptr		; get the cursor pointer in x
	rts


; PanTable is a lookup table for the pan functions.

PanTable
	dc.w	PanNone
	dc.w	PanLeft
	dc.w	PanRight
	dc.w	PanUp
	dc.w	PanDown


; The Pan functions destroy d.  Each one only pans one scanline.
; They assume the column number, times two, is in y.
; They assume a pointer to the buffer header is in x.

; PanNone does nothing

PanNone subroutine
	rts 

; PanLeft shifts the scanline pointer to the left, wrapping if necessary

PanLeft subroutine
	ldd	egfx_spt,y		; get the scanline pointer
	subd	HEADER_ysize16,x	; go to the previous column
	cpd	HEADER_bufferptr,x	; did it go too far?
	bhs	.nowrap			; if not, continue
	addd	HEADER_bufsize,x	; if so, wrap
.nowrap
	std	egfx_spt,y		; store the scanline pointer
	rts		

; PanRight shifts the scanline pointer to the right, wrapping if necessary

PanRight subroutine
	ldd	egfx_spt,y		; get the scanline pointer
	addd	HEADER_ysize16,x	; go to the next column
	cpd	HEADER_bufend,x		; did it go too far?
	blo	.nowrap			; if not, continue
	subd	HEADER_bufsize,x	; if so, wrap
.nowrap
	std	egfx_spt,y		; store the scanline pointer
	rts		

; PanUp shifts the scanline pointer up.  No wrapping.  Not a bit.

PanUp subroutine
	ldd	egfx_spt,y		; get the pointer
	subd	#1			; subtract one
	std	egfx_spt,y		; put it back
	rts

; PanDown shifts the scanline pointer down.  No wrapping here either.

PanDown subroutine
	ldd	egfx_spt,y		; get the pointer
	addd	#1			; add one
	std	egfx_spt,y		; put it back
	rts



; RefreshHook handles muliplexing the display in enhanced mode.
; Effectively, a light is on for a fraction of its column's time slice equal to:
; (pixel value - baseline) / (number of colors - 1).
; If this fraction is less than 0 or greater than 1, it is clipped to 0 or 1
; respectively.  This interrupt hook is called VERY frequently, especially for
; high color depth, so it needs to be as fast as possible.  For best results,
; the variables used should be in the direct page.  The scanline pointer table
; (SPT) must be in the direct page. Note that egfx_baseline is actually the
; "baseline value" minus one.

RefreshHook subroutine

	ldaa	egfx_basecount	; get the color counter
	deca			; decrement by one
	bne	.sameline	; if not zero, we're still on the same scanline

	ldab	egfx_column	; get current column number (times 2)
	decb			; subtract two
	decb
	bpl	.noscreenwrap	; if not neg, no need to wrap to the right side
	ldab	#58		; otherwise, get the rightmost column number
.noscreenwrap
	stab	egfx_column	; store the new column number
	staa	PORT_Gfx_Data	; prevent shadowing by turning off the lights
				;   before moving
	stab	PORT_Gfx_Column	; change current column (because of
				;   hardware flaw, times 2 is okay)
	xgdx			; put in x (a is already zero, after the bne)
	ldd	egfx_spt,x	; get the scanline pointer from the table
	std	egfx_scanline	; store it as the scanline
	ldaa	egfx_numcolors	; set color counter to maximum
	
.sameline
	staa	egfx_basecount	; store new color count
	adda	egfx_baseline	; add the baseline for comparison below
	ldx	egfx_scanline	; get the scanline pointer

; start of scanline is in x
; current base value is in a (base goes from baseline+numcolors to baseline+1)
; output value is b

	cmpa	0,x		; if pixel > base, carry = 1
	rorb			; rotate carry into output value
	cmpa	1,x
	rorb
	cmpa	2,x
	rorb
	cmpa	3,x
	rorb
	cmpa	4,x
	rorb
	cmpa	5,x
	rorb
	cmpa	6,x
	rorb			; because of hardware flaw, leave
				;   result in top 7 bits.
	
	stab	PORT_Gfx_Data	; turn on the lights we want

RefreshHookEnd
	_endhook RefreshHook

