;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Jaguar Example Source Code
; Jaguar Workshop Series #6
; Copyright (c)1994 Atari Corp.
; ALL RIGHTS RESERVED
;
; Program: hv.cof   - GPU Interrupt Object Example
;  Module: hv_gpu.s   - GPU Interrupt Handler and Code Mover
;
		.include        "jaguar.inc"
		.include        "hv4.inc"

		.globl		InitGPU

		.extern		count_x
		.extern		count_y
		.extern		x_motion
		.extern		y_motion
		.extern		x_pos
		.extern		y_pos
		.extern		x_min
		.extern		x_max
		.extern		y_min
		.extern		y_max
		.extern		upd_freqx
		.extern		upd_freqy

		.text

InitGPU:
		movem.l a0-a2,-(sp)

		lea     gpu_code1,a0            ; Interrupt Dispatch Routine
		lea     _end_code1,a1
		move.l  #OP_INT,a2              ; Dest Address (GPU Int Object Handler)
		jsr     copy_block

		lea     gpu_code2,a0            ; GPU Interrupt Object Handler Code
		lea     _end_code2,a1
		move.l  #OP_HNDLR_ADDR,a2
		jsr     copy_block 

		lea     gpu_code3,a0            ; Set's up GPU and loops endlessly
		lea     _end_code3,a1
		move.l  #GPU_LOOP_ADDR,a2
		jsr     copy_block

		move.l  #G_OPENA,G_FLAGS        ; Enable GPU Interrupts from OP
		move.l  #GPU_LOOP_ADDR,G_PC     ; Address of GPU setup code
		move.l  #GPUGO,G_CTRL           ; Start GPU

		movem.l (sp)+,a0-a2
		rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Procedure: copy_block
;	      Copies a block of memory in LONGwords (max 65536 bytes)
;
;         Inputs: 	a0.l	- Block Start
;			a1.l	- Block End
;
; Register Usage:	d0.w	- DBRA counter
;
; Stupid copy routine (use Blitter for large blocks of GPU code)

copy_block:
		move.l  d0,-(sp)
			
		move.l  a1,d0           ; End of block
		sub.l   a0,d0           ; Start of block

		lsr.l   #2,d0           ; # of LONGs
.copy_loop:
		move.l  (a0)+,(a2)+
		dbra    d0,.copy_loop

		move.l  (sp)+,d0
		rts

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Interrupt Dispatcher for Object Processor (GPU Interrupt Object) Interrupts

gpu_code1:
		.gpu
		.org    G_RAM+$30

		movei   #OP_HNDLR_ADDR,r0
		jump    T,(r0)
		nop


		.68000
_end_code1:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Interrupt Handler
; Increments the background color register every time interrupt occurs.

gpu_code2:
		.gpu
		.org            G_RAM+$80

		movei   #G_FLAGS,r30    ; Enable other ints
		load    (r30),r29
		bclr    #3,r29          ; Clear IMASK
		bset    #12,r29         ; Clear pending interrupt
		load    (r31),r28       ; Address of last instruction
		addq    #2,r28          ; +2 to point to next
		addq    #4,r31          ; Correct stack

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Move 1. bitmap object - first calculate x_pos

		movei		#COUNT_BM,r8
		movei		#count_x,r12			; Adress of 1. count_x in r12

outer_loop:
		movei		#0,r11

		move		r12,r10						; Adress of act. count_x in r10

inner_loop:
		move		r10,r0						; Adress of count_xy in r0

		movei		#COUNT_BM*20,r9
		add			r9,r10
		move		r10,r3						; Adress of upd_freqxy in r3

		loadw		(r3),r1						; Value of upd_freqxy in r1

		movei		#donotx,r3

		loadw		(r0),r2						; Value of count_xy in r2

		addq		#1,r2
		storew	r2,(r0)

		movei		#COUNT_BM*18,r9		; Adress of next count_y

		cmp			r1,r2							; upd_freqxy = count_xy ?

		jump		MI,(r3)
		nop

		movei		#0,r2
		storew	r2,(r0)

		movei		#COUNT_BM*12,r9
		sub			r9,r10
		move		r10,r0						; Adress of xy_pos in r0

		movei		#COUNT_BM*4,r9
		add			r9,r10
		move		r10,r1						; Adress of xy_min in r1

		add			r9,r10
		move		r10,r2						; Adress of xy_max in r2

		movei		#COUNT_BM*12,r9
		sub			r9,r10
		move		r10,r3						; Adress of xy_motion in r3

		loadw		(r0),r4						; Value of xy_pos in r4
		loadw		(r1),r5						; Value of xy_min in r5
		loadw		(r2),r6						; Value of xy_max in r6
		loadw		(r3),r7						; Value of xy_motion in r7

		cmp			r5,r4							; xy_pos = xy_min ?
		jr			NE,testxmax
		nop

		neg			r7
		storew	r7,(r3)

testxmax:
		cmp			r6,r4							; xy_pos = xy_max ?
		jr			NE,nextxpos
		nop

		neg			r7
		storew	r7,(r3)

nextxpos:
		add			r7,r4
		storew	r4,(r0)						; Store xy_pos back to r0

		movei		#COUNT_BM*2,r9		; Adress of next count_y

donotx:
		sub			r9,r10						; Point to next count_y
		movei		#inner_loop,r0
		addq		#1,r11
		cmpq		#2,r11
		jump		NE,(r0)
		nop

		addq		#2,r12						; Point to next count_x
		movei		#outer_loop,r0
		subq		#1,r8
		cmpq		#0,r8
		jump		NE,(r0)
		nop

		movei		#0,r0
		movei		#OBF,r1						; Write any value to OBF
		storew	r0,(r1)						; to restart Object Processor

		jump		(r28)							; Return to GPU
		store		r29,(r30)       	; Update GPU_FLAGS

		.68000
_end_code2:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; GPU Program Code (Setup and Loop)
;
; Originally, the following code was a simple infinite loop. The following
; method has been proven to be better for interrupt performance, however.
; In this case, the GPU watches a semaphore and loops while it's 0. In this
; example, it will always be 0.

gpu_code3:
		.gpu

		.org            G_RAM+$400

		movei   #ISTACK,r31     ; Initialize Interrupt Stack
gpu_loop:              
		movei   #semaphore,r10  ; Address of semaphore
		loadw   (r10),r11       ; Load value
		cmpq    #1,r11          ; Loop while not equal to 1

		jr      T,gpu_loop
		nop

		movei   #G_CTRL,r10     ; Shut off GPU; Note, in this
		load    (r10),r11       ; code, this should never happen.
		bclr    #0,r11
		store   r11,(r10)

semaphore:      .dc.l   0

		.68000
_end_code3:

		.end