                          - SEPTIC'S DEMOSKOLA -

                                 Lektion 1
                                 ¯¯¯¯¯¯¯¯¯
                        Skriven av Vicious / Septic

                                 13 Apr 93



                                 Inledning
                                 ¯¯¯¯¯¯¯¯¯
Välkommen till första lektionen i denna utomordentliga demoskola. Här
kommer du att få möjlighet att lära dig allt du gått och drömt om att
kunna, men inte lyckats/haft tid med. Vi kommer tröska igenom allt tänkbart
inom demovärlden, så som copperbars, sprites, bobs, textscrollar, linje-
ritning, vektorer, cirklar och mycket mer skojigt som man kan ha nytta av i
sin framtida demoprogrammering.
  Jag förutsätter att grundkunskaper i assemblerprogrammering finns innan
ni börjar läsa, för detta är enbart en Demoskola, som ska lära ut demo-
programmering, inte en assemblerskola. Exempelprogrammen är skrivna i
AsmOne, men om du använder en annan assembler så bör det inte vara så stora
problem att anpassa sourcen efter den.
  Kursen kommer att i viss mån strida mot allt vad Commodore förespråkar om
systemvänlig programmering, men jag anser att det är en nödvändighet
inom demoprogrammering att skriva sina egna "quick-and-dirty" rutiner för
att pressa ut det maximala av Amigan. Använder man Commodores egna
libraryrutiner så blir allt alldeles för segt, och dessutom får man inte
den rätta kontrollen över programmet.
  Om du inte delar den uppfattningen, utan enbart vill göra program som
garanterat kommer fungera på alla kommande Amigor så rekommenderar jag dig
att sluta läsa nu.
  Sådär, då var vi förbi formaliteterna och kan raskt kasta oss över
väsentligheterna...


                                 Register
                                 ¯¯¯¯¯¯¯¯
Under den här rubriken (som ni så småningom kommer att vara ganska bekanta
med) ska jag i varje Lektion gå igenom olika register vi behöver för att
åstadkomma våra effekter. Alla dessa register finns utförligt förklarade i
Commodores Hardware Reference Manual och jag rekommenderar att du skaffar
den boken om du inte har den, för den är varje demoprogrammerares bibel.
Jag kommer dock förklara de register jag använder, vad de är bra för och
hur de används.
  Till att börja med kanske jag ska förklara lite runtom dessa omtalade
hårdvaru register. Genom att läsa eller skriva till ett hårdvaru register
kan man kommunicera direkt med hårdvaran i Amigan. Commodore säger att man
inte bör programmera dem direkt utan gå via resources eller libraries, men
det allra bästa är att inte göra det, för att spara rastertid. Man kan till
exempel läsa av var videostrålen befinner sig på för rasterrad och synca en
rutin direkt efter den, eller byta bakgrundsfärg för varje rasterrad och
skapa vackra coppereffekter. Allt det här blir smidigare (när man väl kan
det) och framför allt snabbare om man använder hårdvaran direkt.
  Ett mycket viktigt register som vi ska börja med är DMACON ($dff096). Det
styr Amigans DMA, dvs vi kan reglera vad vi vill använda och vad vi inte
behöver. De saker man styr med registret är:

   * Blitter prioritet över DMA
   * Bitplans DMA (så att man kan visa grafik)
   * Copper DMA
   * Blitter DMA
   * Sprite DMA
   * Disk DMA
   * Ljud DMA

Som ni ser så är det ungefär allt vad man kan tänka sig ha med i ett demo,
och det här registret styr vad som ska synas. Precis som de flesta andra
registren är DMACON ($dff096) ett WRITE ONLY register, dvs man kan bara
skriva till det. Vill man läsa av nuvarande DMA får man läsa från DMACON's
motsvarighet, DMACONR ($dff002). De register som innehåller intressant
information har oftast en READ ONLY-motsvarighet.
  När man gör ett demo ska man alltid planera i förväg vad man egentligen
ska göra, hur det ska se ut och så vidare...Då blir det också lätt att
avgöra vad man ska sätta för DMA. Behöver man tex inte sprites i sitt demo
ska man slå av Sprite DMA, för att slippa "spriteflimmer" och att onödig
DMA-tid går till sprites som inte visas.
  Vi ska nu titta på hur registret är uppbyggt. Det är ett WORD-register,
vilket alltså betyder att det innehåller 16 bitar. Dess uppbyggnad är
mycket vanlig och återkommer i många andra register. Bitarna lyder som
följer:

 BIT   FUNKTION   BESKRIVNING
 ¯¯¯   ¯¯¯¯¯¯¯¯   ¯¯¯¯¯¯¯¯¯¯¯
 15    SET/CLR    Anger om bitar med en 1 ska rensas eller sättas.
 14    BBUSY      Anger om blittern arbetar eller är ledig. (Read Only)
 13    BZERO      Talar om ifall en blitteroperation resulterade i
                  en destination med bara nollor.
 12    X
 11    X
 10    BLTPRI     Blittern prioriteras före processorn, vilket gör att
                  processorn inte kan stjäla några cyklar från blitter DMA.
 09    DMAEN      Tillåter nedanstående DMA.
 08    BPLEN      Bitplans DMA påslagen.
 07    COPEN      Copper påslagen.
 06    BLTEN      Blitter påslagen.
 05    SPREN      Sprite DMA påslagen.
 04    DSKEN      Disk DMA påslagen.
 03    AUD3EN     Ljud kanal 3 DMA påslagen.
 02    AUD2EN      "    " "  2  "   "   "
 01    AUD1EN      "    " "  1  "   "   "
 00    AUD0EN      "    " "  0  "   "   "

Först och främst i ett program, bör man spara den gamla DMA-inställningen
(om man ska återvända till systemet) innan man sätter sin egen. Det görs
enkelt genom att plocka ett WORD från DMACONR ($dff002) och spara det tills
man avslutar sitt program. Man måste dock sätta bit 15 innan man skriver
tillbaka till DMACON ($dff096) för att bitarna ska sättas.
  När man sparat den gamla inställningen brukar man rensa alla bitarna,
vilket man gör genom att skriva #$7fff till DMACON ($dff096). Detta gör att
alla bitar rensas (eftersom bit 15 är 0). När man rensat allt är det dags
att komponera ihop sin egna DMA och skriva den till DMACON ($dff096).
(Detta kan du se i demonstrations programmet Lekt1.s om något är oklart.)

Efter allt prat om DMA ska vi ta upp coppern en smula. Innan man
installerar sin egen copperlista bör man också här (likt DMA) spara undan
den gamla copperaddressen (om man ska återvända till systemet) för att
kunna återställa den gamla copperlistan vid programmets slut.
  Att hämta systemets copperlista är inte lika enkelt som att hämta gammal
DMA, eftersom det inte finns något READ-register för copperaddressen. Man
får istället gå via graphics.library (trots att jag inte gillar att använda
libraries :-) så blir man tvungen ibland...). Genom att öppna
graphics.library och hämta värdet som ligger på $26+gfxlib-startaddressen.
Efter att ha sparat den addressen kan vi lugnt skriva in vår egen
copperlista i COP1LC ($dff080). (OBS! Copperlistan måste ligga i CHIPMEM!)
Värdet skrivs in som ett LONGWORD. Efter det ska man helst skriva ett WORD
till COPJMP1 ($dff088) för att tvinga Amigan att börja använda din egen
copperlista. COPJMP1 ($dff088) är ett så kallat strobe-register, som inte
bryr sig om vilket värde som skrivs, utan bara att man skrivit till det.
Meningen med COPJMP1 ($dff088) och COPJMP2 ($dff08a) är att man kan ha två
copperlistor samtidigt på COP1LC ($dff080) och COP2LC ($dff084) som kan
hoppa mellan varandra.
  Om vi nu skulle ta och beskriva copperlistor. Coppern är ju som bekant en
coprocessor som arbetar helt oberoende av MC68000 processorn. Copper är en
mycket effektiv liten medhjälpare, som ligger bakom mycket av Amigans
grafikmöjligheter. Exempelvis kan man på ett enkelt sätt byta färger, DMA,
trigga interrupt eller till och med byta upplösning var som helst på
skärmen. Eftersom copperlistan körs om varje vertical blanking så förenklar
den register-uppdatering, på exempelvis bitplanspekare som måste sättas
varje vbl (vertical blanking), avsevärt. Man kan även kontrollera blittern
med den, och uppdatera sprites, osv osv...Kort sagt, coppern är mycket
använbar, men ändå ganska enkel att använda!
  Dess enkelhet ligger i att den endast har tre (3) instruktioner. WAIT,
MOVE och SKIP. Copperinstruktionerna består av ett LONGWORD, eller två
WORDS.
  Om vi börjar med instruktionen WAIT. Med den kan man vänta på att video-
strålen ska nå en viss position på skärmen och där tex ändra palette eller
något annat skojigt. De två WORDen ser ut så här:

                                  WORD 1

Bit 0    - Alltid satt till 1 (detta plus bit 0 i word 2 är den egentliga
           instruktions identifieraren).
Bit 1-7  - Horisontell strålposition att vänta på.
Bit 8-15 - Vertikal strålposition att vänta på.

                                  WORD 2

Bit 0    - Alltid satt till 0.
Bit 1-7  - Anger vilka bitar som måste stämma med det horisontella
           strålpositions värdet.
Bit 8-14 - Anger vilka bitar som måste stämma med det vertikala strål-
           positions värdet.
Bit 15   - Normalt satt till 1. (Se Hardware Reference Manual för mer
           information.)

Om vi tar ett exempel på en WAIT-instruktion så kan den se ut så här:

   DC.W   $8007,$FF00

Det här WAIT-kommandot väntar på den vertikala raden $80, medan
horisontella postitionen $07 ignoreras eftersom dess bitar är noll i WORD 2
($FF00).

   DC.W   $A019,$FFFE

Här har vi däremot ett WAIT-kommando där den horisontella positionen $19
inte ignoreras. Här väntar vi tills positionen $A0 vertikalt och $19
horis. är uppnådd (eller överstigen) innan nästa instruktion utförs.
  Notera att det horisontella värdet måste vara udda! Annars tolkas det som
ett MOVE-kommando. Det måste också ligga inom $0-$e2.

Ett MOVE-kommando flyttar ett WORD till ett hårdvaru register. Det kan till
exempel vara ett färgbyte eller en bitplans uppdatering. Här kommer MOVE-
kommandots utseende:

                                  WORD 1

Bit 0    - Alltid 0.
Bit 1-8  - Register destination.
Bit 9-15 - Oanvända. Sätt till 0.

                                  WORD 2

Bit 0-15 - 16 bitar som ska flyttas till hårdvaru registret.

MOVE kan skriva till alla register över $dff020. Om man har satt copperns
DANGER bit i COPCON ($dff02e) kan man även skriva till registren mellan
$dff010 och $dff020.
  För att byta bakgrundsfärg till vit skriver vi till COLOR00 ($dff180). Om
man gjorde det med processorn skulle det se ut så här:

   MOVE.W   #$FFF,$DFF180

Men med copperns MOVE blir det:

   DC.W   $0180,$0FFF

Till sist har vi SKIP-kommandot. Det används inte så värst mycket men för
den sakens skull behöver vi ju inte hoppa över det.
  SKIP är ganska likt WAIT. Den kollar också var videostrålen befinner sig,
och om positionen är lika eller mer hoppas nästa instruktion över.

                                  WORD 1

Bit 0    - Alltid satt till 1.
Bit 1-7  - Horisontell strålposition att vänta på.
Bit 8-15 - Vertikal strålposition att vänta på.

                                  WORD 2

Bit 0    - Alltid 1.
Bit 1-7  - Anger vilka bitar som måste stämma med det horisontella
           strålpositions värdet.
Bit 8-14 - Anger vilka bitar som måste stämma med det vertikala strål-
           positions värdet.
Bit 15   - Normalt satt till 1. (Se Hardware Reference Manual för mer
           information.)

SKIP kan tex användas för att skippa en strobe-skrivning till COPJMP2 som
gör att copperlista 2 börjar användas. Är man sådär haj på hur det funkar
kan man skapa snitsiga copperloopar för roliga effekter. Det kommer jag
dock inte gå in på närmare här.
  Om du fortfarande inte riktigt förstår hur copperlistan är uppbyggd kan
du kika i exempel-sourcen. Där finns en copperlista väl dokumenterad.

Detta var vad jag tänkte ta upp i registerväg här i Lektion 1. Som jag sa
förut finns alla hårdvaru register väl dokumenterade i Hardware Reference
Manual, och den BÖR du ha om du vill bekanta dig med Amigans möjligheter
inom demoprogrammering.


                                   Setup
                                   ¯¯¯¯¯
Programexemplet till den här lektionen är väldigt simpelt, men jag tyckte
jag skulle börja från grunden, så att alla hänger med ordentligt.
  Kort sagt sätter programmet bara upp demots "interface", dvs att det
stänger av Amigans multitasking system och byter till en egen skärm.
  Vad jag gör i min INIT rutin är följande -> Först hämtar jag något som
kallas VBR (Vector Base Register). VBR finns inte på vanliga 68000
processorer utan bara på 68010 och uppåt. Det är en offset till datorns
interrupt- och undantagsregister som på en 68000 ligger i början på minnet.
Meningen med VBR är att man ska kunna flytta dessa interrupt- och
undantagsregister till FAST MEM för att datorn skall bli snabbare. Därför
är det viktigt att man i sitt program inte antar att exempelvis vertical
blanking interruptet ligger på adress $6c utan att man kollar vad VBR-
registret är inställt på och läser relativt från den addressen plus $6c.
Om ni tycker detta verkar krångligt så är det inte så farligt. Lär er bara
att man ska hämta VBR i sitt program för att det ska funka.
  Nästa steg är att stänga av multitaskingen. Inget att orda om. Vill man
göra de häftigaste rutinerna måste man tyvärr sno all processortid
själv....
  Efter det kommer en liten villkorlig rutinsnutt (som du själv väljer om
du vill ha med genom att ändra variabeln DisableCache i början av
programmet). Rutinen stänger av cache på datorer som har det (dock ej på
maskiner med KS1.3 eller lägre...) och varför ska man göra det då? Tja,
instruktionscache kan strula till det ibland vid decrunching, likaså
datacache, så det bästa är väl att stänga av det, om man inte utnyttjar
dess snabbhet i programmet.
  Efter det öppnar jag graphics.library och kör OwnBlitter() för att ta
över blittern från systemet. Sen kör vi WaitBlit() för att vänta tills vi
fått den. Efter det gör vi en LoadView(0) så att vi kan sätta in vår egen
copperlista. För säkerhets skull väntar vi också med WaitTOF (två gånger
ifall usern kör i interlace...).
  Eftersom vi inte har några interrupt på den här gången så rensas bara
interrupt-kontroll registret. Vi rensar också DMAn (men den sätter vi
längre fram...)
  Nu kommer det ännu en villkorlig del, DisableMotor. En irriterande grej
när man stänger av interrupt och multitasking är att drivelampan ibland kan
fortsätta att lysa när demot kör. Den här rutinen släcker den genom att
stänga av motorn.
  När vi nu återvänder från INIT-rutinen sätter jag den DMA jag vill ha.
För enkelhetens skull har jag skrivit dit (vertikalt) vad de olika bitarna
står för så att det ska bli enklare att se. Vad jag gör är att sätta på
endast COPPER DMA.
  Sen sätter jag dit min egen copperlista och skriver till strobe registret
som jag berättade om i början, COPJMP1 ($dff088).
  Mainloopen innehåller bara en liten nonsensrutin som byter bakgrundsfärg.
Den loopar tills du trycker vänster musknapp.
  Efter det körs min UnInit-rutin. Man kan säga att den gör precis tvärtom
vad INIT-rutinen gör. Först återställer vi gamla interupt och DMA, sen ger
vi tillbaka blittern till systemet och efter det fixar vi tillbaka den
gamla viewporten och de två copperlistorna som används av systemet.
  Nu är det bara att stänga intuition.library + graphics.library och (ifall
vi stängde av det) sen återställer vi cacheminnet.
  Allra sist slår vi på multitaskingen igen.
  Mycket mer finns det inte att säga om det enkla programmet. Studera det
noga och titta hur det fungerar.

