1. Starting to Dream in Code

Developing Demo Effects #1
This series shows you how to make effects from 1985 to 2015. When our demo timeline starts, how impressive your demo is and how great you are depends greatly on how good your knowledge of the hardware is.

It's not over until the Fat Lady sings!
All of the novel demo effects really came from programmers, often fitting any logical, brainy, and nerdy prejudice you'd like to choose, but also imaginative people dreaming of what they'd like to see on the screen... On a platform like Amiga, knowledge of the hardware helps in developing your imagination and making your dreams come true!

The Amiga Chipset

Not just a motherboard with one or two helper chips, the Amiga chipset is a chipset in the modern sense of the word. It has the equivalent of a southbridge with many DMA channels, and like graphics cards, it has a Blitter chip. Like some sounds cards, it has sound DMA. It has a standardized bus for expansions. Unlike the prevalent motherboard-upgradable-to-about-4x designs, it supports CPU expansion boards, able to multiply the speed and amount of memory of an Amiga by a factor of 100.
Its OS supports pre-emptive multitasking and so in many respects is similar to today's hardware, and its OS can be used just like a Windows, Mac, or Linux computer. You do pretty much everything the same way.
Enough said about the OS – lovely as it is, for the entire series, it will be turned off – to not steal CPU time from our effect!

Hardware Overview

I will explain demo effects in chronological order, and these ran on quite modest specs. To begin with, you will have to adjust your expectations to a CPU that is a factor of 1000 slower than today's popular CPUs, and also to only 0.5 MB of RAM, and restrict your imagination to what can be done with such specs.

Processor

Runs at about 1 MIPS, same as 8-bit CPUs on the same MHz, what is there to say? But it has quite a readable instruction set, unlike other CPUs. Can you figure out what this code does?
	move Base,d0
	move Height,d1
	mulu d0,d1
	divu #2,d1
	move d1,Area

(Answer: It calculates the area of a triangle: Area = Base * Height / 2.)

Memory


Usable memory is shown in yellow (program code, sounds, graphics, and data for use by custom chip DMA) and green (other data and program code).
Booting a base model OCS Amiga (unexpanded A500, A1000, or A2000) straight to command-line (CLI) is the best environment for coding or running demos.
Here, the OS and the CLI screen buffer leaves ~256K available for programs, editor/assembler, and source workspace. Amiga has two memory types called Chipmem and Fastmem. When fastmem is added, it gets priority so that almost all OS data is put there, leaving chipmem from address $12000 free. With a lean editor/assembler, chipmem from $17800 can be available for your demo to use while developing it. Measurements are for Kickstart/Workbench version 3.1 Amigas, others will leave up to 72 KB less chip memory.

Blitter

On the base model Amigas, the Blitter is faster than the slow CPU at performing bit operations on graphics and other blocks of data, and at drawing lines and filling polygons.

Copper

The Copper is a list of instructions executed by the chipset that builds up the screen layout. It can be modified by the CPU in real time, and this is often vital to making an interesting effect on this platform.

Audio

Let's not forget that a demo is an audiovisual experience! A song that fits the demo may make the whole demo more memorable, and even the simplest sound will add to a demo.
These three are the main functions used in demo effects, distributed over the Amiga's chipset using DMA channels. You initialize some custom chip registers with the CPU and supply a pointer to a structure in chipmem, and then the DMA runs in the background, taking care of everything for you without the CPU having to do the work.
So it is with audio - unlike previous computers, it has digital audio. Initialize 4 channels, and they will play 4 instruments from sample windows in memory with almost zero penalty for the CPU.
The only two DMA channels that can cause a heavy load on the CPU bus is the Display DMA (at high resolutions or many colors) and the Blitter.

Tools

We will get into peripheral tools for cross-platform development, scripting, porting etc much later in this article series. To start programming on Amiga, all you need is an Amiga, assembler software and documentation. If you don't have an Amiga, I recommend to use WinUAE (or FS-UAE if you don't have a Windows PC) to emulate your target Amiga model.
Assemblers for Amiga include DevPac, vasm, Asm-One/Asm-Pro, PhxAss, and more. Just try some and start using the one you like most. DevPac and Asm-One/Asm-Pro have a built in text editor, others are command-line assemblers where you will need a separate editor such as CygnusEd.

Asm-One, showing Edit menu shortcuts.

Start Writing Code!

This is it for the introduction. The rest of this series requires some Amiga coding knowledge already - to be able to follow the explanations and code snippets. I make sure to be as pedagogical as I can, but if it's still hard to follow, check out my Youtube videos. In the Downloads section, there is also a link to the Asm-One manual, which helps you understand 68000 Assembler quite a bit if you're new to the CPU.
To test out the snippets it's helpful with a good program template to open a screen buffer.

The Startup

So-called Startup sources are the staple of demo programming. They're good to have to just test out an idea you got, or in the case of our series, test out ideas of others! Download my MiniStartup and let's go through it in sections!

Turn off the OS

If you need to load files while the demo is running, you can't turn the OS off entirely. But few demos do this, since you want all of the CPU time for your effect. So we save away the current computer state, then turn off the interrupts and call our demo:
    *** MiniWrapper by Photon ***

Start:	move.l 4.w,a6			;Exec library base address in a6
	sub.l a4,a4
	btst #0,297(a6)			;68000 CPU?
	beq.s .yes68k
	lea .GetVBR(PC),a5		;else fetch vector base address to a4:
	jsr -30(a6)			;enter Supervisor mode

    *--- save view+coppers ---*

.yes68k:lea .GfxLib(PC),a1		;either way return to here and open
	jsr -408(a6)			;graphics library
	tst.l d0			;if not OK,
	beq.s .quit			;exit program.
	move.l d0,a5			;a5=gfxbase

	move.l a5,a6
	sub.l a1,a1			;blank screen to trigger screen switch
	jsr -222(a6)			;on Amigas with graphics cards

    *--- save int+dma ---*

	lea $dff000,a6
	bsr.s WaitEOF			;wait out the current frame
	move.l $1c(a6),-(sp)		;save intena+intreq
	move.w 2(a6),-(sp)		;and dma
	move.l $6c(a4),-(sp)		;and also the VB int vector for sport.
	bsr.s AllOff			;turn off all interrupts+DMA

    *--- call demo ---*

	movem.l a4-a6,-(sp)
	bsr.w Demo			;call our demo \o/


Control the hardware

This is done the same way as on all computers: by writing to hardware addresses. In the Amiga, these are fixed addresses that have names such as DMACON - DMA control. Typically, you will poke (write to) some initialization values and then enable a bit or two to start chip functions.
In the startup, and indeed in most demos and games, most of this poking (at least for the display stuff) is done by the Copper, by giving it a Copper list of simple instructions to either poke a $dffxxx address register or wait for a screen position:
*******************************************************************************
	SECTION ChipData,DATA_C		;declared data that must be in chipmem
*******************************************************************************

Copper:
	dc.w $1fc,0			;Slow fetch mode, remove if AGA demo.
	dc.w $8e,$2c81			;238h display window top, left
	dc.w $90,$2cc1			;and bottom, right.
	dc.w $92,$38			;Standard bitplane dma fetch start
	dc.w $94,$d0			;and stop for standard screen.

	dc.w $106,$0c00			;(AGA compat. if any Dual Playf. mode)
	dc.w $108,bwid-bpl		;modulos
	dc.w $10a,bwid-bpl

	dc.w $102,0			;Scroll register (and playfield pri)

Palette:				;Some kind of palette (3 bpls=8 colors)
	dc.w $180,$000			;black
	dc.w $182,$00f			;blue
	dc.w $184,$0f0			;green
	dc.w $186,$0ff			;cyan
	dc.w $188,$f00			;red
	dc.w $18a,$f0f			;magenta
	dc.w $18c,$ff0			;yellow
	dc.w $18e,$fff			;white

BplPtrs:
	dc.w $e0,0
	dc.w $e2,0
	dc.w $e4,0
	dc.w $e6,0
	dc.w $e8,0
	dc.w $ea,0
	dc.w $ec,0
	dc.w $ee,0
	dc.w $f0,0
	dc.w $f2,0
	dc.w $f4,0
	dc.w $f6,0			;full 6 ptrs, in case you increase bpls
	dc.w $100,bpls*$1000+$200	;enable bitplanes

	dc.w $ffdf,$fffe		;allow VPOS>$ff
	dc.w $ffff,$fffe		;magic value to end copperlist
CopperE:

Turn on the OS

When our demo exits, we simply restore the original hardware values and the View shown by the OS, restoring the computer state before running the demo. (Side note: This startup is compatible with and tested on non-native graphics cards, meaning that if you start the demo from a graphics card screen mode, it will automatically switch from and back to that mode nicely.)
Note that even though we explain the startup here, the whole point of a startup is to have a standard framework or wrapper that is just there and you never care about, so that you can focus on writing only the effect code!

This startup includes the startup code (wrapper which is quite small, but also a simple framework for a small demo that sets up a copper, starts an interrupt, and with a main loop to clear the screen and plot a secret object to it...

Clearing the screen buffer

To clear the screen, we set up a 1-channel blit (Blitter operation) with the right screen dimensions and start it. Immediately afterward, control is returned to the CPU and the Blitter continues. We must wait for the Blitter to finish before plotting to the same memory area as the Blitter was writing to, or they will overwrite each other's work.
ClearScreen:				;a1=screen destination address to clear
	bsr WaitBlitter
	clr.w $66(a6)			;destination modulo
	move.l #$01000000,$40(a6)	;set operation type in BLTCON0/1
	move.l a1,$54(a6)		;destination address
	move.w #h*bpls*64+bpl/2,$58(a6)	;blitter operation size
	rts

As you can see, we also wait for the Blitter to finish before clearing the screen, and this is to not modify Blitter registers if any previous blit operation should be running when this code is executed. This type of clearing takes a lot of time, and demo programmers would at all costs minimize or avoid this naïve, classical way to 'just clear the screen'. (Can you think of another way to do it in the source example?)

Plotting pixels

The Copper sets up a screen with 3 bitplanes, i.e. 3-bit color depth, and also sets a default palette for the resulting 8 indexed colors. The bitplanes work as overlays, where bitplane bits in the same x, y position combine in the following manner to form a pixel, a color-number for the palette lookup:
Bit in bitplane #	3	2	1	Resulting palette color number
----------------------------------------------------------------------------
			0	0	0	0
			0	0	1	1
			0	1	0	2
			0	1	1	3
			1	0	0	4
			1	0	1	5
			1	1	0	6
			1	1	1	7

Knowing this, the generic plot routine might now make sense:
Plot:					;d1=x, d2=y, d3=color, a1=screen
	movem.l d1-d5/a1,-(sp)
	add.w ObjX(PC),d1		;Add position of the amazing
	add.w ObjY(PC),d2		;secret dot object!

	muls #bwid,d2			;Address offset for line
	move.w d1,d4			;left-to-right x position,
	not.w d4			;to bit 7-0 (other bits unused by bset)
	asr.w #3,d1			;Byte offset for x position
	ext.l d1			;(big offsets for large screens?)
	add.l d1,d2			;added to final address offset.

	moveq #bpls-1,d5		;Loop through bitplanes:
.l:	ror.b #1,d3			;color bit for bitplane set?
	bpl.s .noset
	bset d4,(a1,d2.l)		;then set bit.
.noset: lea bpl(a1),a1		        ;go to next bitplane
	dbf d5,.l
	movem.l (sp)+,d1-d5/a1
	rts

So much for getting a feel for the prerequisites. Why not take this plot routine for a spin or try some of your own sweet dreams, ideas or experiments in the startup framework?