************************************************
* 256 Color demo for Color Computer 3 
* Copyright Chris Lomont 2007 www.lomont.org
************************************************
* Theory - although there are several programs showing how to get
* many simultaneous colors onscreen for the CoCo3, none (I have seen)
* are suitable for animation, since they involve many pages of data
* and long preprocessing times. I wanted to see if I could get many 
* colors with as little overhead as possible for animation, games, 
* and my Mandelbrot generator. Here are some ways to get many colors:
* 1. Palette flipping - this works well, except it introduces flicker
*    unless you are careful, and has the overhead of writing palette
*    registers a lot. You can do it per line, per screen, or any mix.
* 2. Page swapping - the idea is to draw on different images with 
*    different colors, and flip the images back and forth. Since the
*    CoCo3 is hardware limited to 60 frames per second (fps), a 2 page
*    flip will result in 30 fps, which is ok visually.
* 3. Dithering - totally unacceptable for realtime animation, due to 
*    the computational overhead.
*
* I initially wrote a two page flip, two palette version, but 
* mathematically it is very difficult to get good colors. The reason 
* is that for two colors A and B, the resulting color is (A+B)/2, and 
* you get many different ways to hit the same result color. For
* example, if you have RGB palette color (2,0,0) in the first palette, 
* color (1,0,0) in both palettes, and color (0,0,0) in the second 
* palette, you end up with two ways to get 
* (1,0,0) = ((2,0,0)+(0,0,0))/2 = ((1,0,0)+(1,0,0))/2. 
*
* This effectively loses a color. This coupled with the fact you want
* nice colors like pure white and pure black leads to a host of 
* problems trying to pick a palette.
*
* Next solution is to do two page cycle, but let one page last twice 
* as long as the other, leading to an effective 20 fps, which is 
* about as slow as you can visually go (and even 20 fps is sorta 
* flickery). This time the color mixing is effectively (2A+B)/3, which
* leads to an easier solution to the palette choice. Furthermore, you 
* can find a single palette which behaves nicely, removing the 
* requirement that you change palettes to get a wide range of colors.
*
* In this case a nice palette I hand constructed leads to 240 unique 
* colors, with a nice mix of colors. Computer perturbing this over 
* many iterations led to several palettes which have 256 unique 
* colors. In the code below Palette240 is the one leading to 240 
* colors, and Palette256 is the one leading to 256 colors. However, 
* so far I have not found a palette leading to 256 colors that also 
* includes pure white and pure black. The palette included here gets 
* close.
*
* So, in short, the best idea so far is a 2 page flip, with a single 
* palette for all pages, where one page is shown 2 frames and then 
* the other once, and then repeat.
*
* This is just a proof of concept for fun, and it could be enhanced in
* many ways. The first thing to do is pick a nice palette mapping, and 
* see if the palette can be improved, so you can write some call like 
* SetPixel(x,y,r,g,b) in a nice manner.
*
* Finally, this palette is chosen for a RGB demo, but you can convert 
* it to get an equivalent one for composite.
*
* to be continued ...... perhaps...... Next time draw bouncing lines?
*
*
*
* How this program works:
* 1. Disable interrupts, high speed poke
* 2. Display 320x192x16 graphics mode at GIME page $30(=$60000 address)
* 3. Set 16 palette colors 
* 4. Load GIME pages $30-33 into CPU RAM $8000-$FFFF, draw horiz lines image 
* 5. Load GIME pages $34-37 into CPU RAM $8000-$FFFF, draw vert lines image
* 6. Install interrupt handler to fire on vertical blank to toggle images
* 7. Infinite loop 
  
INIT0		equ	$FF90	* GIME Initialization register 0		
IRQENR		equ	$FF92	* IRQ interrupt source		
FIRQENR		equ	$FF93	* FIRQ interrupt source
VMODE		equ	$FF98	* Video mode - sets lines per row, text/graphics		
VRES		equ	$FF99	* Video resolution - sets size		
VIDSTART	equ	$FF9D	* video memory start register  
FIRQADDR	equ	$FEF4	* where FIRQ jumps

	org 	16384
* program start  	
start	
* 1. Disable interrupts, high speed poke
	orcc	#$50		* disable interrupts
	sta	$FFD9		* high speed poke
	
* 2. Display 320x192x16 graphics mode at GIME page $30(=$60000 address)
	lda 	#%01001100 	* Coco3, MMU on, DRAM constant, standard SCS 16K internal 16K external ROM
	sta 	INIT0
	lda	#%10000000	
	sta	VMODE 		* graphics mode, 60 hz, normal burst
	lda	#%00011110 	* width 320, height 192
	sta	VRES 	
	
	ldd	#$C000		* first block of graphics screen at $60000 / 8
	std	VIDSTART	* vidstart offset
	
* 3. Set 16 palette colors 
	ldx	#$FFB0		* set palette entries
	leay	Palette256,pcr
	ldb	16		* 16 entries
!	lda	,y+
	sta	,x+
	decb	
	bne	<

* 4. Load GIME pages $30-33 into CPU RAM $8000-$FFFF, draw horiz line image 
	lda	#$30		* this page default
	bsr	SetGraphicsMem	* set the memory pages
	
	ldx	#$8000		* screen start
	clra			* color 00
horiz	ldy	#1920		* bytes per 1/16 of screen	
!	sta	,x+
	leay	-1,y		* decrement counter
	bne	<
	adda	#$11		* increment both pixel colors
	cmpx	#$F800		* end of graphics
	blt	horiz	
	
	bsr	SetFIRQ		* set interrupt jump in the page $37


* 5. Load GIME pages $34-37 into CPU RAM $8000-$FFFF, draw vert line image
	lda	#$34		* next pages
	bsr	SetGraphicsMem
		
	* draw 16 vertical stripes
	ldx	#$8000		* screen start
	clra			* color 00, each strip = 320/16=20 pixels = 10 bytes
vert	ldb	#10
!	sta	,x+
	decb
	bne	<	
	adda	#$11		* increment both pixel colors
	bcc	>		* if carry, zero a 
	clra
!	cmpx	#$F800		* end of graphics?
	blt	vert	

	bsr	SetFIRQ		* set interrupt jump in the page $37

* 6. Install interrupt handler to fire on VSYNC to toggle images
	lda	#$08		* enable vertical blank interrupt
	sta	FIRQENR		* set the interrupt register
	clr	IRQENR		* disable these to be safe
	lda	INIT0		* get this
	ora	#$10		* enable GIME FIRQ interrupts
	sta	INIT0		* and save it
	andcc	#$BF		* enable FIRQ interrupts

* 7. Infinite loop
!	bra	<

************************************************
* SetFIRQ
* Sets the FIRQ handler to jump to intV
* REG: modifies REGD,REGY
************************************************
SetFIRQ	lda	#$7E		* opcode for JMP
	sta	FIRQADDR	* save it
	leay	intV,pcr 	* address to jump to
	sty	FIRQADDR+1	
	rts
		
************************************************
* Set graphics memory - sets 32K in 0x8000-0xFFFF 
* from MMU pages 0x60000 on up 
* interrupts should be disabled to use this
* REGA - first page to load (nice start pages are $30 and $34)
* Registers changed: REGX,REGD
************************************************
SetGraphicsMem 
	ldx	#$FFA4		* mapped to $8000-$9FFF, then more
	ldb	#4		* 4 pages to load			
!	sta	,x+
	inca			* next page
	decb
	bne	<		
	rts		

* palette leading to 240 colors
Palette240	
*	fcb	32,36,16,18,8,9,25,44,50,54,45,27,43,0,56,63

* palette leading to 256 colors
Palette256	
	fcb	44,45,32,39,3,2,8,9,25,50,54,19,43,4,56,63

vidPos	fcb	1		* frame 0,1... to draw
************************************************
* vertical interrupt - draw frame at GIME $C000, 
* then $D000, and then repeat 
* NOTE: set this to change as fast as possible - todo
* REGS: All registers must be saved   
************************************************
intV	pshs	d,x		* save these - we use FIRQ
	leax	vidPos,pcr	* what frame?
	lda	,x
	cmpa	#2
	bne	showC
	
showD	ldd	#$D000
	std	VIDSTART	* show it
	clr	,x		* reset
	bra	vidOut
showC	ldd	#$C000
	std	VIDSTART	* show it
	inc	,x		* next frame	
vidOut	ldd	FIRQENR		* read (and clears) the interrupt source
	puls	d,x		
	rti	
	
final	end	start		* set BASIC exec address


* end of file - Color256.asm