What is this?

This program demonstrates a full-screen tilt effect running on the GPU and 
working in conjuction with the OP. It also demonstrates a trivial
gradient horizon.

It's by no means useful, or even very interesting unless you want to
use the code concepts provided.

To run it, upload mou.cof with BJL. If you use something else, you can
work it out, I'm sure. You'll be presented with Tursi's mug on the
screen. Left and right will tilt, and up and down move the horizon.

You'll notice that you can see the edges of the tilted image -- this
is intentional. The point is to show you the regular screen behind. In
actual use you would want to run a lower resolution so the tilted
area fills the screen width.

The code: only the GPU code is meant to be of interest. The 68k code
is a hacked up version of Atari's sample and primarily runs the obj list
and joystick.

The first question that comes up is 'why is this useful?' The intent was
that if you had a complex scene with many objects, rendering them as
if the screen were flat, and then tilting after the fact would be
faster and easier than performing sprite and coordinate rotation on
each of them. Likewise, an effect like the gradient horizon in this
example would be relatively expensive to tilt with a scene, while it's
free in this code.

So it sounds useful! Unfortunately, at the moment, it seems to be
limited to about 2x overscan with large objects. I haven't tested yet
with small objects - you might get enough to be useful. You have to
run a fairly low resolution, too, about 256 pixels wide seems the
maximum useful resolution. At least till I get time to try to 
optimize it further.

So how does it work? You set up your OP list to draw your scene as
per normal, then at the end you insert a GPU interrupt object, 
followed by a normal bitmap object pointing to the GPU's frame buffer.

The interrupt function uses an old fake-rotation trick to simulate 
a rotate without any trig. First it copies the current scanline out 
of the line buffer and draws it into a frame buffer with a vertical tilt.
It then tweaks the OP list X coordinate for the frame buffer bitmap,
and returns. The OP draws that line from the frame buffer at the
specified position, and the whole sequence repeats for the next
scanline. 

In between scanlines, the GPU code calculates the current gradient
color for the background and sets it in the BG register.

A lot of work was done to optimize the performance of the GPU
interrupt code. Most of the data is preloaded in registers and
most of it is ready to go the moment we get control. We run
the blitter in high bus priority mode to assure it finishes
before the OP can resume. We run the blitter in phrase mode,
even though we aren't supposed to be able to step this way
(we can, just not per pixel. This limits us to a slope of 
about 4:1). 

There are a couple of bugs I couldn't correct. We do the
blitter stepping in the outer loop because the inner loop
in this config is apparently non-functional (conflicts and
both read and write addresses will step Y, something like that).

You'll note a black bar on the right edge of the rotated view.
The BG register only fills in the line buffer on the VISIBLE
part of the screen, even though the OP will draw beyond that.
So you're seeing the end of the horizon (note that Tursi's
ear is not cut off!)

There is some distortion in the tilt. This is because there's
too much reliance on integers in the tilt table. It could be
made more accurate with more fractions, if necessary.

Now, if you wanted to use this for something, you need to change
a couple of things to remove the demo aspect first.

First: in mou_init.s, find this block of code:
; Finish the system init
		move.l	d0,OLP				; Value of D0 from InitLister
;		move.w	#$6C1,VMODE			; Configure Video (Enable, 16b CRY, CSync, BGen, 4 clks/pix)
;		move.w	#$4C7,VMODE			; Configure Video (Enable, 16b RGB, CSync, BGen, 3 clks/pix)

		move.w	#$6C7,VMODE			; Configure Video (Enable, 16b RGB, CSync, BGen, 4 clks/pix)
;		move.w	#$8C7,VMODE			; Configure Video (Enable, 16b RGB, CSync, BGen, 5 clks/pix)
;		move.w  #$ac7,VMODE			; Configure Video (Enable, 16b RGB, CSync, BGen, 6 clks/pix) 266 pix (minus overscan)
;		move.w	#$CC7,VMODE			; Configure Video (Enable, 16b RGB, CSync, BGen, 7 clks/pix) 228 pix (minus overscan)
;		move.w	#$647,VMODE			; Configure Video (Enable, 16b RGB, CSync,       4 clks/pix)

And select a lower resolution display mode, probably the 228 is the
best in this example.

Second: in mou_list.s, find this code:
		; temp code - push him over a bit (THIS ONE TO CHANGE)
		add.w	#120,d3

And delete it (this pushes Tursi over to the right so you can see
both copies of him).

Finally, in mou_gpu.s, find this code:
	movei	#LBUFC+(120*2),r15		; read address of line buffer (16 bit only, NOTE ADD! In a real app you would remove the offset)

and remove the +(120*2) part so it looks like this:
	movei	#LBUFC,r15		; read address of line buffer (16 bit only)

That just changes the start offline in the line buffer - you want to
start right from 0, I was starting at 120.

Run it up with those changes, and you should see a full screen tilting
Tursi. You'll see see crap at the top of the screen - drop a status
display overtop of that. ;)

Massive thanks for help, encouragement, and inspiration for all this
goes to Kevv, without him I don't know if I would have gotten anything.
If you want to use this code, go ahead, but let me know before you
release your project. As per the terms of my license. Thanks. On
the other hand, if you have improvements for it, let's talk! ;)

