AssemblerCursus - Les 3 Nu gaan we verder met de praktijk,maar ik raad u aan om 68000.txt in een reserve (lege) textbuffer te laden als 'spiekie' en samenvatting van les 2. In deze les beginnen we om wat op het beeldscherm te zetten. Om dat te bereiken moeten we een COPPERLIST schrijven. Dat is een programma voor de COPPER-chip, het 'ding', dat Grafische weergave mogelijk maakt. We hebben de Copper al 'Stiekem' gebruikt om de beeldschermkleuren te veranderen. $dff180 is een register van de Copper dat Color00 heet. Tot nu toe echter hebben we de registers rechtstreeks met de processor aangesproken, en zoals u waarschijnlijk heeft gemerkt tijdens het debuggen, zien we enkel kort lichtflitsen in die kleur optreden die we naar het register toe schrijven. Maar deze kleuren flitsen zo omdat het systeem ze meteen weer verandert in haar eigen kleuren. Alleen met een lus, die voortdurend wordt doorlopen krijgen we het voor elkaar de kleuren wat langer te zien. Maar amper uit het programma, en de oude kleuren zijn weer terug. Dit komt doordat alles wat wij zien op het scherm (windows,letters en de rest) het resultaat is van een COPPERLIST, exacter nog, een SYSTEM-COPPERLIST. Deze 'list' (=lijst) is niets anders als: MOVE.W #$123,dff180 ; COLOR0 - Beschrijf Farbe 0 MOVE.W #$456,dff182 ; COLOR1 - Beschrijf Farbe 1 etc.... Deze wordt voortdurend uitgevoerd, en hierin ligt dan ook de verklaring waarom de originele kleuren steeds terugkeren, nml. omdat we met de processor de kleuren in de registers schrijven, terwijl de Copperlist elke 1/50-ste seconde (1 vijftigste!) alle kleuren opnieuw definieërt! U kunt zich nu wel voorstellen dat het bijna onmogelijk is om een figuurtje in alle rust over het scherm te laten bewegen als men steeds met de SYSTEM-List in een gevecht is die alles om-definieërt. We moeten onze EIGEN COPPERLIST schrijven en die op de plaats van de SYSTEM-LIST zetten. Niets makkelijker dan dat... Zoals ik al voorspeld heb, is de copperlist niets anders als een lange rij van move instructies, die waarden in de registers van de copper schrijven, dus de $dffxxx-registers. Deze move's worden echter niet door de processor gedaan maar door de Copper zelf, die onafhankelijk van de processor loopt en dus daarom de processor voor andere zaken vrijhoudt. Dat is een van de redenen waarom ze geen Lionheart of Project X op de PC hebben zoals deze spellen op da Amiga zijn. We moeten de copper dus een listing schrijven, zoals we die al van de 680x0 kennen, en daarna de copper vertellen waar deze staat en hem vertellen dat hij onze lijst moet uivoeren i.p.v de System-list. De copper kent maar 3 bevelen, waarvan we er in de praktijk meestal maar 2 gebruiken: Deze zijn MOVE en WAIT. De instructie die bijna niemand gebruikt is de SKIP instructie. Daarom behandelen we deze alleen als we een listing daarmee vinden. De move-instructie is kinderspel: Kunt u het zich nog herinneren? Move.w #$123,$dff180 ;Stop de waarde $123 (RGB) in COLOR00 In een Copper-list ziet dat er als volgt uit: dc.w $180,$123 ;De waarden worden direct met dc.w in het ;geheugen opgeslagen,maar we moeten wel de ;2 bevelen kennen. Men moet eerst het adres aangeven waar we wat willen hebben (zonder $dff, zoals we dat al gezien hebben als $180(a0). We gebruiken dus een offset!). De ontwikkelaars van de Amiga hebben als voor ons er aan gedacht dat het voldoende was om $180,$182 enz.. te typen zonder elke keer daar een $dff voor te hoeven typen. Dit komt omdat we met de copper werken,en deze adressen altijd beginnen met een $dff. Verder mogen we alleen op EVEN adressen schrijven zoals $180,$182 ..., NOOIT $181,$179 etc. En verder kunnen we alleen Word gebruiken. Zoals u ziet, wordt een Copperlist niet op dezelfde manier geassembleert als de instructies van de 680x0 waarvan de bevelen bestaan uit RTS,MOVE,LEA enz.. We moeten letterlijk bytes in het geheugen stoppen zoals de copper (=CoProcessor) ze lezen en begrijpen kan. Daarom gebruiken we de DC instructie om byte voor byte de bevelen te schrijven. Maar het is heel simpel! B.v. om de eerste 4 kleuren te defineëren: COPPERLIST: dc.w $180,$000 ;Color00 = Zwart dc.w $182,$f00 ;Color01 = Rood dc.w $184,$0f0 ;Color02 = Groen dc.w $186,$00f ;Color03 = Blauw De wait-instructie daarentegen dient ervoor om een bepaalde regel af te wachten. Als men bijvoorbeeld de achtergrondkleur (Color00) tot de helft van het scherm zwart wil laten, en van daar af blau maken wil, dan ziet het er zo uit: dc.w $180,$0 ;Color0 = Zwart gevolgd door een WAIT,die het midden van het beeldscherm afwacht, en dan dc.w $180,$00f ;Color0 = Blauw Met deze strategie kan met het totale palet in elke beeldlijn veranderen. Daarvan kunnen PC-bezitters met VGA en SVGA alleen maar dromen. Het WAIT-bevel ziet er zo uit: dc.w $1007,$FFFE ;WAIT Coördinaat X=$10,Y = $07 Dit bevel betekent: Wacht horizontale lijn $10,plaats 7 af (dus het 7e punt van links. Deze 'punten' worden 'Pixels' genaamd.). De $FFFE staat voor WAIT. Om positie 16,7 dus aan te duiden gebruiken we $1007. ( $10 = 16 ! ) Normaliter geeft men horizontale lijnen aan bij hun begin. Dit is $07 i.p.v. $1 zoals u wel gedacht zult hebben. Dit komt omdat $1 t/m $6 linksbuiten het beeld valt. Het Wait-bevel gebruiken we ook om het einde van de Copperlist aan te geven: dc.w $FFFF,$FFFE ;Einde Copperlist. Deze bovenstaande regel ziet de copper per definitie als eind, ook omdat deze regel zegt dat we een lijn moeten afwachten die er niet is! De Copper begint dan weer van voor af aan. Er zijn roddels geweest dat er bij enkele oudere Amiga-modellen 2 instructies nodig waren om de List af te sluiten, maar dat zal wel een nieuwe vorm van Massa-psychose zijn, omdat niemand er ooit twee heeft gebruikt, en alles toch altijd werkte. Om onze Copperlist te schrijven, die in het begin nog zonder plaatjes zijn zal enkel maar met wat kleurverlopen, moeten we het BITPLANE uitschakelen, dus het uit bits bestaande patroon dat het beeld regelt. Om dat te bereiken zetten we aan het begin van onze copper-list de instructie dc.w $100,$200. We stoppen dus de waarde $200 in register $dff100 dat het control-register van het bitplane is. NU ZIJN WE IN STAAT OM EEN VOLLEDIGE COPPER-LIST TE SCHRIJVEN DIE HALVERWEGE HETSCHER COLOR00 VERANDERT. COPPERLIST: dc.w $100,$200 ;BPLCON0 Geen Schermen, alleen achtergrond dc.w $180,$0 ;Color00 zwart dc.w $f707,$fffe ;Wait - wacht lijn $7f af (127) dc.w $180,$00f ;Color00 blauw dc.w $FFFF,$FFFE ;Einde Copperlist. De Amiga bezit 32 registers voor datzelfde aantal kleuren: $dff180 ;Color00 (Achtergrond) $dff182 ;Color01 $dff184 ;Color02 $dff186 ;Color03 ... $dff1be ;Color31 In ieder register kan men 1 van de 4096 mogelijke kleurwaarden stoppen. (Voor AGA is dit anders. Lees verder, dit werkt ook op AGA computers.) We kunnen dus uit 3 grondkleuren kiezen: Rood,groen en blauw. En we kunnen ze elk 16 'helderheden' geven van 0 tot en met 15. Als u dit narekent zal blijken dat er 4096 mogelijkheden zijn: 16 X 16 X 16 = 4096. De waaede kan men óf door de processor in het register stoppen,óf door de copper zelf in het register laten stoppen: move.w #$000,$dff180 ;Kleur zwart in COLOR00 dc.w $180,$FFF ;Kleur wit in Color00 In dit voorbeeld heb ik meteen twee extreme waarden genomen: $FFF (wit) en $000 (Zwart).Om de kleur te kiezen moet men als volgt denken: Een kleur bestaat uit een WORD dat zo opgebouwd is: dc.w $0RGB waarvan de vierde 0 (helemaal links) niet gebruikt wordt en geldt: R = Rood $0 t/m $f G = Groen $0 t/m $f B = Blauw $0 t/m $f Bits 15 t/m 12 worden niet gebruikt, 11 t/m 8 zijn het rood-aandeel,7 t/m 4 zijn het groen-aandeel en bit 3 t/m 0 zijn het blauw-aandeel.Elke kleur kan een waarde van 0 tot 15 hebben, dus van $0 tot $f in hexadecimaal. Een kleur mengen is dus simpel: $FFF = Wit $D00 = Rood $F00 = Fel-Rood $F80 = Rood-Oranje $F90 = Oranje $fb0 = Goud-Geel $fd0 = Cadmium-geel $FF0 = Citroen $8e0 = Lichtgroen $0f0 = Groen $2c0 = Donkergroen $0b1 = Boomgroen $0db = Water $1fb = Water licht $6fe = Hemelsblauw $6ce = Licht Blauw $00f = Blauw $61f = Brillant Blauw $06d = Donker blauw $c1f = Violet $fac = Rose $db9 = Beige $c80 = Bruin $a87 = Donker bruin $999 = Middel grijs $000 = Zwart Nu is het enige probleem de copper ONZE lijst te laten uitvoeren, en niet die van het systeem. Maar dan duikt er weer een probleem op: Hoe krijgen we na afloop van het programma ons originele beeld terug? Antwoord: Men moet op een 'papiertje opschrijven' waar de originele List staat. D.w.z. we schrijven een bepaald longword het adres waar de originele list te vinden is. We zullen dit Longword OLDCOP noemen. Maar wie vragen we waar de Copper-list staat? Het bedrijfssysteem natuurlijk! Om dat te vragen hebben we een routine uit de Rom-Chip (Kickstart) nodig. Om dat te doen hebben we altijd als uitgangsadres adres $4 nodig. Hierin staat waar de inhoud van de kickstart staat en waar de routines dus staan. Vanuit dat adres kunnen we 'gewoon' vaste offsets gebruiken. Deze zijn voorgedefinieërd, maar daar kletsen we straks nog wel over. Om het Long van adres $4 op te halen is een move.l $4,a6 ;in a6 hebben we nu ExecBase voldoende. Beter nog: Move.l 4.w,a6 ; 4 is een klein getal, daarom kan 4.w geschreven ;worden, wat plaats spaart. Er wordt dus $0004 ;i.p.v. $00000004 geschreven,omdat de eerste 4 nullen ;niet gebruikt worden. GE'MOVE'-ED WORDT ECHTER ALTIJD ;EEN LONGWORD! Dus het Long dat in adres 4,5,6 en 7 ;staat. Is eenmaal de inhoud van $4 gekopieërd naar a6 dan kunnen we de routine uit de 'kick' oproepen. Hiervoor gebruiken we een JSR gecombineerd met een OFFSET, want er zijn verschillende offsets die verschillende routines aanroepen. Nu weten we dat we met b.v. een JSR -$78(a6) multitasking kunnen uitschakelen. Er wordt dus alleen ONS programma uitgevoerd, anders niets. Meteen testen! Laadt Listing3a.s in en probeer 'm! Maar Exec zorgt niet voor alles. De kickstart (256 kb lang bij versies 1.2 en 1.3, en 512 kb lang bij versies 2.0 en 3.0) is in libraries opgedeeld, een soort verzameling van routines die al klaar zijn en aangeroepen kunnen worden. En omdat iedere kickstart anders is (hardwarematig dan), in de zin dat in kick 1.3 b.v. op adres $fc1000 de routine ligt die multitasking uitschakelt en bij 'kick' 2.0 op een heel ander adres. Daarom hebben de ontwikkelaars van Amiga eraan gedacht om dat adres vast te leggen op adres $4!. Vanuit het opgeslagen adres in $4 kunnen we dan altijd met een JSR en een Offset naar een bepaalde routine springen. (BSR is hetzelfde als JSR, alleen kan men met JSR het gehele geheugen gebruiken,terwijl BSR alleen maar werkt binnen een bereik van 32768 bytes naar voren en naar achteren.) Om bijvoorbeeld multitasking uit te schakelen,(Disable) wordt bij elke Kickstart hetzelfde gedaan: move.l 4.w,a6 ;Adres van Exec in a6 jsr -$78(a6) ;Disable - blokkeer multitasking bsr.w MijnProgramma ;Ga naar MijnProgramma jsr -$7e(a6) ;Enable - Schakel multitasking weer in In elke kickstart ligt deze routine op een ander adres, maar met deze methode zijn we er zeker van dat alles goed werkt. Men moet alleen alle offsets kennen, en de rest is 'a piece of cake'. Voor deze offsets raad ik u aan er een naslagwerk bij te houden als 'Amiga Intern' van Data Becker (vanaf pagina 379) of een ander dergelijk betrouwbaar boek. Ons interesseert nu eigenlijk alleen het afzetten van de System-copperlist, en deze op te slaan op een plaats waar we hem weer kunnen vinden. Om dat te kunnen bereiken, moeten we een routine uit het 'Kick' aanroepen die Graphics.library heet. Het is de library die zich bezig houdt met de graphische functies. Maar alleen onder het bedrijfssysteem, dat is duidelijk. Niet op HardwareNiveau. Om deze Bibliotheek (Library) te kunnen gebruiken, moeten we deze eerst openen: Move.l 4.w,a6 ;ExecBase in a6 zetten Lea GfxName,a1 ;Adres van de te openen librarynaam in a1 Jsr -$198(a6) ;OpenLibrary. Een routine uit Exec die een ;Bibliotheek opent en het resultaat van het ;basisadres terug levert in d0. Hiervandaan kunnen ;we dan de offsets gebruiken. Move.l d0,GfxBase ;Sla basisadres op in GfxBase. ... ... GfxName: dc.b "graphics.library",0,0 ;Opmerking: Om letters in het ;geheugen op te slaan, gebruiken ;we altijd dc.b en zetten de ;letters tussen "" of ''. GfxBase: dc.l 0 ;Hierin slaan we het resultaat op. In dit voorbeeld hebben we de routine uit exec gebruikt die een library opent met "OpenLibrary". Deze routine verlangt dat in a1 het adres staat, waar de naam van de te openen library staat. We zouden ook "dos.library" kunnen openen om met files om te gaan, of "intuition.library" voor vensters en screens e.d kunnen openen. Eenmaal uitgevoerd, levert OpenLibrary in d0 het basisadres van de gevraagde bibliotheek.Deze gaan we gebruiken als Offset om met de JSR instructie de verscheidene routines aan te roepen. Buiten de JSR instructies weten we ook waar het adres in staat van de actuele copperlijst. Dit is adres $26 vanaf GfxBase. Dus gaan we verder met ons programma, waar we nu het adres van de te verwijderen Copperlist in opslaan in OLDCOP. Move.l 4.w,a6 ;ExecBase in a6 zetten Lea GfxName,a1 ;Adres van de te openen librarynaam in a1 Jsr -$198(a6) ;OpenLibrary. Een routine uit Exec die een ;Bibliotheek opent en het resultaat van het ;basisadres terug levert in d0. Hiervandaan kunnen ;we dan de offsets gebruiken. Move.l d0,GfxBase ;Sla basisadres op in GfxBase. Move.l d0,a6 ;We gaan hem nu gebruiken als basisadres move.l $26(a6),Oldcop ;Hier slaan we het adres van de ;System-Copperlist in OLDCOP op. ... GfxName: dc.b "graphics.library",0,0 ;Opmerking: Om letters in het ;geheugen op te slaan, gebruiken ;we altijd dc.b en zetten de ;letters tussen "" of ''. GfxBase: dc.l 0 ;Hierin slaan we het resultaat op. Oldcop: dc.l 0 ;Hierin komt het adres van de originele ;CopperList Nu kunnen we onze eigen Copperlist aansturen, een Waitmouse toevoegen en dan weer de toestand herstellen. Met 'aansturen' bedoel ik dat we het adres van onze copperlist in register COP1LC stoppen, dat is $Dff080. Dit adres is een 'wijzer' (pointer) naar het adres waar onze Copperlist staat. D.w.z. de copper voert de list uit die op het adres in COP1LC staat. Eenmaal dat adres in COP1LC gestopt, moeten we onze copper nog stoppen. Als we in register $dff088 (COPJMP1) een willekeurig getal schrijven (gewoon wat troep of wat dan ook, als we er maar naar schrijven of er wat uit lezen) dan begint de Copper de list op adres $dff080 uit te voeren. Dit komt omdat COPJMP1 een STROBE register is; elke verandering van dat register zorgt ervoor dat we de copper activeren. Het is als een soort knop die we maar heel zacht hoeven aan te raken. Gebruik echter geen CLR.w $dff088, dat geeft komische problemen. Nu wordt onze copperlist uitgevoerd zolang er in $dff080 geen ander adres komt te staan. Een probleem is echter dat $dff080 een Write-Only register is; we kunnen het niet uitlezen. Om de oude Copperlist weer op zijn plaats te zetten moeten we het bedrijfssysteem vragen welk adres op $dff080 staat, omdat we dit register zelf niet kunnen uitlezen. En daarvoor gebruiken we (jawel!) een routine uit het Kick. Als we dit adres gekregen hebben, slaan we het op in een Longword van ons programma,en starten onze eigen Copperlist. Aan het einde van het programma zetten we dan het opgeslagen adres weer terug in COP1LC ($dff080). Move.l 4.w,a6 ;ExecBase in a6 zetten JSR -$198(a6) ;Disable - Schakel multitasking uit. Lea GfxName,a1 ;Adres van de te openen librarynaam in a1 Jsr -$198(a6) ;OpenLibrary. Een routine uit Exec die een ;Bibliotheek opent en het resultaat van het ;basisadres terug levert in d0. Hiervandaan kunnen ;we dan de offsets gebruiken. Move.l d0,GfxBase ;Sla basisadres op in GfxBase. Move.l d0,a6 ;We gaan hem nu gebruiken als basisadres move.l $26(a6),Oldcop ;Hier slaan we het adres van de ;System-Copperlist in OLDCOP op. move.l #Copperlist,$dff080 ;COP1LC - We zetten het adres van onze eigen ;list in COP1LC move.w d0,$dff088 ;We starten onze list als we wat 'troep' in ;$dff088 schrijven. Mouse: btst #6,$bfe001 bne.s mouse move.l OldCop(PC),$dff080 ; COP1LC - We zetten de oude list terug move.w d0,$dff088 ; en starten hem. move.w 4.w,a6 jsr -$7e(a6) ;Enable - schakel multitasking weer in. move.l GfxBase(PC),a1 ;Base v.d. te sluiten bibliotheek. jsr -$19e(a6) ;CloseLibrary - Sluit Graphicslib rts ;Einde Programma! GfxName: dc.b "graphics.library",0,0 ;Opmerking: Om letters in het ;geheugen op te slaan, gebruiken ;we altijd dc.b en zetten de ;letters tussen "" of ''. GfxBase: dc.l 0 ;Hierin slaan we het resultaat op. Oldcop: dc.l 0 ;Hierin komt het adres van de originele ;CopperList CopperList: dc.w $100,$200 ;BPLCON0 - Geen schermen,alleen achtergrond. dc.w $180,$0 ;Color00 zwart. dc.w $7f07,$FFFE ;Wacht op lijn $7f (127) dc.w $180,$00f ;Color00 Blauw. dc.w $FFFF,$FFFE ;einde Copperlist END U kunt dit programma met verschillende voorbeelden vinden in listing3b.s. Dit is het eerste programma uit deze cursus dat de chips van de Amiga laat zien wat de Amiga nu echt zo'n "Power under your ASS" maakt. Heeft u de experimenten met de CopperList geprobeerd? Goed, dan proberen we nu een paar bewegende effecten toe te voegen. Maar voordat we daarme gaan beginnen,moet ik u vertellen dat om een beweging te krijgen we de routine met de electronenstraal (het ding dat het beeld tekent in uw tv/monitor) moeten synchroniseren (Gelijk laten lopen). Voor degenen die het nog niet weten: Het beeld wordt 50 maal per seconde opnieuw getekend, en de bewegingen die vloeiend moeten lopen mogen dan ook niet meer, en liefst ook niet minder dan dat aantal keren opnieuw getekend worden. De betere spellen zijn ook op de 50-ste seconde gesynchroniseerd. We hebben register $dff006 als eens gebruikt, en we weten dat het telkens van waarde verandert. Dit register verandert zo snel,omdat het de positie van de electronenstraal, die het beeld met 50 Beelden per seconde, bevat. Hij start bij 0 (dus links boven in de monitor), en doorloopt het totale beeldtot rechtsonder. Als we nu een routine gaan schrijven dat het beeld laat veranderen (beweging),dan moeten we zorgen dat we het 'rustig' aan doen; d.w.z. dat als we geen timing gebruiken, het beeld met de snelheid van de processor wordt aangepast, en dat is veel te snel voor de electronenstraal,en dus zeker voor ons om te zien. Om een bepaalde lijn af te wachten, moeten we het eerste Byte van $dff006 uitlezen. Daarin staat de bereikte lijn (dus de vertikale positie) van de electronenstraal (Hetzelfde als WAIT voor de copper). Wacht: cmpi.b #$f0,$dff006 ;VHPOSR - Zijn we op lijn $f0? (240) bne.s Wacht ;zoniet,controleer nogmaals. Deze lus wacht lijn 240 af, en als die bereikt is gaan we pas met de volgende bevelen verder,bijvoorbeeld de routine die op een Muisdruk wacht. Voegen we dit toe: mouse: cmpi.b #$f0,$dff006 ;VHPOSR - Zijn we op lijn $f0? (240) bne.s Wacht ;zoniet,controleer nogmaals. Bsr.s RoutineMetTiming ;Deze routine wordt maar 1 keer per beeld ;uitgevoerd, dus 50 maal per seconde. bsr.s MoveCopper ;De EERSTE BEWEGING! btst #6,$bfe001 ;Linker muisknop gedrukt? bne muose ;Zo niet, terug naar mouse RTS Nu hebben we een routine die 1 keer per FRAME (BEELD) uitgevoerd wordt. OPMERKING: De beelden worden met een RASTER-techniek getekend, die met een 'electronisch penseel' links boven begint, naar rechts gaat tot het einde van het beeld, dan terug springt nar links en aan lijn 2 begint. Net als schrijven: We schrijven een regel vol, en beginnen weer vooraan op een nieuwe regel. Hebben we een pagina vol (Zijn we linksonder) dan beginnen we met een nieuwe. Laad listing3c.s maar eens in en probeer 'm uit. Deze beweegt een WAIT naar onder, en daardoor de volgende kleur als u op de rechter muisknop drukt. Linker muisknop om te stoppen. Listing3c.s begrepen? Dan gaan we het een stukje moeilijker maken. Laad Listing3c2.s maar eens in en bekijk 'm. Ik heb een Lijncontrole toegevoegd om de scroll te stoppen. Alles begrepen uit Listing3c2.s? Dan gaan we verder met Listing3c3.s , waar de balk met 10 WAIT verschoven wordt i.p.v. 1 wait. Steeds moeilijker! Leeft u nog na listing 3c3.s? Dan concentreer u maar eens op de volgende listing Listing3c4.s, waar we van 10 Labels Balk naar 1 enkel overstappen om offsets te gebruiken. Na,dat was toch niet moeilijk,wel? Probeer listing3d.s dan maar eens,waar we de balk op en neer laten gaan,en de snelheid veranderen. U bent al bij listing3e.s aangekomen! Hier verklaren we hoe men een RasterBar tevoorschijn tovert. Een apart geval: Hoe bereik ik de PAL-zone (na $FF) met de WAIT van de Copper? Zie listing3f.s ! Om les 3 af te sluiten,kijkt u nog maar eens naar Listing3g.s en Listing3h.s waar we een verloop van links naar rechts maken, i.p.v. naar boven en beneden. Daarna bent u stoofklaar voor les 4, waar we kleurige plaatjes en allerlei mogelijke effecten gaan behandelen. **************************************************************************** Mijn complimenten dat u al zover bent gekomen! Het zwaarste loodje is eraf! Vanaf hier gaan we met lichte stof verder, zoals de logica van assembler- programmering. ****************************************************************************