BREV 3 Vi fortsätter med COPPER och BITPLANES LOGISKA OPERATIONER I detta kapitel ska vi se på BINÄR aritmetik (först och främst plus och minus), samt s.k. logiska operationer (AND, OR, NOT och XOR). Detta är något du får mycket användning för. Samtidigt kan det verka lite invecklat när du ser det första gången - men det är inte svårt! När vi i det DECIMALA talsystemet tar talet 9 och lägger till 1,så noll- ställer vi positionen för ettorna och höjer positionen för tiorna med 1.Så här: 9 + 1 = 10 DECIMALT har vi alltså 10 tal (från 0 till 9) till vårt förfogande när vi vill räkna. I det BINÄRA talsystemet har vi bara 2 tal: 0 och 1. Men detfungerar på samma sätt. Låt oss se på följande uppställning:1 + 1 = 10 Detta strider helt mot allt sunt förnuft i det DECIMALA talsystemet, men idet BINÄRA talsystemet är det helt korrekt. Detta har vi varit inne påtidigare (i BREV 1), men nu skall vi ta det mer ingående. Följande räkneexempel (BINÄRT) kommer att användas som förklarande exempeli detta avsnitt. Exempel nr. 1 % 1000 1001 +% 1010 1010 ------------ % 0011 0011 (CARRY = 1) Nu ska vi, bokstavligt talat, ta oss an detta räkneexempel BIT för BIT.Den BIT som kallas LSB (den längst till höger) i det översta talet (%10001001) ska läggas ihop med LSB i talet under (%1010 1010). Vi ska alltsålägga ihop de två BITsen 1 och 0 som blir 1. Förklaring: Fungerar på samma sätt som det DECIMALA talsystemet: 1 + 0 = 1 Nu tar vi BITarna till vänster om dessa: 0 och 1. Dessa läggs ihop och blir som i det DECIMALA talsystemet: 0 + 1 = 1 Nästa par BITs är nollor. Precis som i det DECIMALA talsystemet: 0 + 0 = 0 Nästa två BITs är ettor. Här kommer den funktion, som vi nämnde i det DECIMALA talsystemet när man går från 9 till 10, in i bilden. Man har jubara två tal att tillgå när man räknar BINÄRT (0 och 1). Så när två ettoradderas, får vi en nolla medan en etta sätts i minne. Alltså uppstår det som vi inledde detta kapitel med: 1 + 1 = 10. Dettaminne hamnar alltså i nästa position till vänster så att det blir 0 i denposition vi håller på med, och så tar vi med oss minnet till nästaoperation. Nästa par BITs är två nollor. Som vi visade förut är 0 + 0 = 0 i alla talsystem. Det enda som kan förändra den saken är alltså om det finns ett i minne, och det gör det ju. Så att det totala räkneexemplet för denna talposition blir: 0 + 0 + 1 = 1 Så resultatet för denna position blir 1. Nästa BIT-par är 0 och 1. Liksom tidigare blir svaret 1 (inget minne). I nästa position är det två nollor. Svaret blir här igen 0 (inget minne). I BIT-paret längst till vänster (MSB) står det två ettor. Resultatet blir alltså 1 + 1 = 10. Här noterar vi därför noll i talpositionen i svaret och sätter CARRYn till 1 för att markera att det blivit ett minne. Vi behöveralltså 9 BITS för att kunna ange korrekt svar. Uppgift 0301: Lägg ihop de två BINÄRA talen %10100111 och %10001101 Då borde du vara klar för att prova på SUBTRAKTION av BINÄRA tal. Det går till på samma sätt som vid subtraktion av DECIMALA tal. Reglerna kan viskriva så här: BINÄRT regel 1: 0 - 0 = 0 regel 2: 1 - 0 = 1 regel 3: 1 - 1 = 0 regel 4: 0 - 1 = ? (Talet skall bli NEGATIVT) Reglerna 1, 2 och 3 borde inte vara några problem. Låt oss ta dessa först, och sedan kommer vi tillbaka till regel 4 i ett senare brev i ett kapitelsom kommer att behandla SIGNERADE TAL (negativa tal). BINÄRT: 0 - 0 = 0 är exakt som i DECIMALT som du ser. Det samma gäller förregel 2 och 3. Vi kan inte tänka oss att du har problm med detta. AND - OR - NOT - XOR Nu ska vi försöka oss på DATA-LOGIK. En matematiker konstruerade dennalogik innan det fanns några datorer, hans "logik" värdesattes inte förrän datorn gjorde sitt intåg. Nu kan vi inte tänka oss att vara utan denna logik. Denna logik - som vi ska behandla om ett ögonblick - används bl.a när man ska sätta en BIT i ett register utan att störa de andra BITsen - eller närman vill "slå av" en BIT i ett register av samma anledning. Vi ska nuförklara detta närmare. Vi börjar med AND. Ordet AND betyder OCH. Om man tar två BYTEs och skall "ANDa" dem, så gör man en speciell operation för varje BIT i de två BINÄRA talen. Studera följande exempel noggrannt: Exempel nr. 2 % 1011 0101 AND % 1001 1110 ----------- % 1001 0100 Vi sa att ordet AND betydde OCH, och nu ska du få se hur det hela fungerar. BITsen längst till vänster i de två BINÄRA talen i EXEMPEL 2 är två ettor. Logiken säjer att om du ANDar två ettor (1 OCH 1) så blir resultatet ALLTID 1. Om du har något annat - som 1 och 0 eller 0 och 1, så blir svaret ALLTID 0. Om du då lägger samman BIT för BIT i de två BINÄRAtalen, så ser du att varje gång man ANDar två ettor, så blir svaret 1 -vid andra tillfällen blir svaret noll. Man kan sätta upp en tabell över detta. En sådan tabell för logiska operationer kallas en sanningstabell. Sanningstabell för AND: 0 AND 1 = 0 0 AND 0 = 0 1 AND 0 = 0 1 AND 1 = 1 Som du ser får du bara 1 som svar om du ANDar två BITs och bägge är satta:1 AND 1 (svenska: 1 OCH 1). Vid alla andra tillfällen får du som sagt noll. Denna funtion är väldigt bra att ha när du ska slå av (nollställa) BITs i t.ex. ett register där olika BITs har olika betydelse. Om du t.ex. i ett eller annat register ska slå av BIT nummer 3 (fjärde BITen räknat från höger - 3, 2, 1, 0), så kan du ANDa registret medföljande BINÄRA tal: % 1111 0111. Om registret tidigare såg ut så här:%0110 1011, så skulle du efter ANDningen få registret att se ut så här:%0110 0011 - och BIT nummer 3 är nollställd! Det blir lite meröverskådligt om vi sätter upp det som här nedanför: REGISTRET innehåller: % 0110 1011 Vi gör en AND med: % 1111 0111 ----------- Resultat: % 0110 0011 Här ser man tydligare att man har slagit av fjärde BITen (BIT nr. 3). Man behöver inte veta vilka BITs som är satta i det register man vill påverka.Det räcker att du anger den BIT som gäller, som noll när du andar - alla andra BITs låter du vara satta (1). Funktionen AND kommer du ofta attanvända dig av när du programmerar på AMIGA. Uppgift 0302: ANDa de två BINÄRA talen: % 1011 0110 och % 1000 1111. Nästa logiska funktion kallas OR - svenska: ELLER. Här menar man att när två BINÄRA tal ORas och om den ena BITen är satt ELLER den andra(egentligen en eller båda två), så blir svaret 1. Låt oss sätta upp ensanningstabell för OR: Sanningstabell för OR (ELLER): BINÄRT: 0 OR 0 = 0 BINÄRT: 1 OR 0 = 1 BINÄRT: 0 OR 1 = 1 BINÄRT: 1 OR 1 = 1 Av sanningstabellen för OR (ELLER) ser du att om en av två BITs som ORas -eller båda två - är satt, så blir resultatet av ORingen av de två nämndaBITsen: 1 I exemplet nedan ORas två BINÄRA tal. Studera det noga. % 1010 1010 OR % 1100 0101 ----------- % 1110 1111 Vid programmering i MC har denna logiska operation motsatt verkan mot AND.Med OR sätter du BITar i ett register. Låt oss ta samma exempel som vianvände vid förklaringeng av AND-funktionen. REGISTRET förutsattesinnehålla: % 0110 1011. Efter ANDningen innehöll samma register%0110 0011. Nu kommer vi att använda OR-operationen för detta register föratt sätta BIT 3 igen (den fjärde BITen räknat från höger - LSB). Registret innehåller: % 0110 0011 Vi gör en OR med: % 0000 1000 ----------- Resultat: % 0110 1011 Den fjärde BITen (BIT nr.3 från LSB räknat) är åter satt, och vi ärtillbaka där vi började. Detta är också en mycket användbar operation somdu kommer att använda dig av många gånger. Uppgift 0303: ORa följande tal: %0101 0101 och %1001 1001 Låt oss fortsätta med logiska operationer, och ta oss an NOT (svenska: INTE). Vi kan lika gärna sätta upp en sanningstabell med det samma: Sanningstabell för NOT (INTE): BINÄRT: NOT 0 = 1 BINÄRT: NOT 1 = 0 Som du ser har inte NOT något s.k. argument. NOT 0 blir med detsamma somatt säga: INTE 0 - och det måste ju bli 1. På samma sätt blir NOT 1 (INTE1) lika med 0. Med denna logiska operation utför man en s.k. invertering (vändning) av BITs. Skriver man alltså: NOT % 1010 1010, kommer svaret att bli: % 0101 0101. Mera logiskt än så här kan det väl inte bli? Operationen NOT kommer vi bl.a. att komma tillbaka till i samband med avläsningar av tangentbordet på din AMIGA. Och så var det den sista logiska operationen: XOR (uttalas: eks-år). Det kan översättas med EXKLUSIVT ELLER. Vi sätter upp en sanningstabell först: BINÄRT: 0 XOR 0 = 0 BINÄRT: 0 XOR 1 = 1 BINÄRT: 1 XOR 0 = 1 BINÄRT: 1 XOR 1 = 0 Av tabellen ovanför kan man lätt se att det är bara när den ena BITen är satt (1) som resultatet blir 1 när man gör en XOR. Vi visar med att göra en XOR på två BYTEs: Registret innehåller: % 1010 1010 Vi gör en XOR med: % 1101 1011 ----------- Resultat: % 0111 0001 En gång till: När två BITs XORas, så blir resultatet 1 (BITen sätts) om den ena BITen är satt - och bara då. Resultatet av en XOR blir 0 om båda BITsen är 0 eller om båda BITsen är 1. Denna logiska operation används sällan. Men vi kommer att få användning för den i ett senare brev. Egentligen är det väldigt logiskt, men man kan behöva lite övning för att man ska kunna använda dem utan att fundera på saken först. Så öva mycket på detta! Uppgift 0304: Gör en XOR med % 1001 0010 och % 1010 0111. MASKINKODSPROGRAMMERING Så ska vi kasta oss ut i MC igen. Vi skagöra ett litet program som läser in de 16 första minnescellerna - addresserna - i AMIGAN. Sedan placerar vi dem på ett annat ställe i datorns minne, i en egen uppsatt BUFFER. Vi kommer att skriva två exempel av samma program. Det första är lite tungrott för att göra det tydligare (men fullt användbart), det andra är lite mer avancerat: kortare, snabbare och smartare. Här är första versionen: 1 move.l #16,d0 2 move.l #$00,a0 3 lea.l buffer,a1 4 loop: 5 move.b (a0),d1 6 add.l #1,a0 7 move.b d1,(a1) 8 add.l #1,a1 9 sub.l #1,d0 10 cmp.l #0,d0 11 bne loop 12 rts 13 14 buffer: 15 blk.b 16,0 Rad 1: Flyttar in talet 16 (DECIMALT) i D0. Detta register använder vi som ett räkneverk när vi vill att en loop ska gå ett visst antal gånger (här 16 gånger). Tecknet "#" kan du översätta med "Ta följande tal." Alltså: Ta följande decimala tal (16) och placera det i dataregister d0. I rad 6: Addera följande tal (1); i rad 9: subtrahera följande tal (1), och i rad 10: Läg ihop följande tal (0) med värdet i dataregister d0. Rad 2: Lägger in talet $00 i adressregister A0. I och med att vi använder LONGWORD så nollställer vi därmed alla BITsen (32 stycken). Detta adressregister använder vi som en pekare (den pekar alltså vid detta tillfälle på address $000000): den adress vi vill läsa ifrån. Rad 3: Lägger in adressen till vår egen deklarerade BUFFER (se rad 4) i A1. Eller med andra ord: Vi har satt undan en del av AMIGANS minne som en lagringsplats för det vi läser in. Denna lagringsplats börjar på den adress i datorn där vår LABEL befinner sig. (LABELnamnet assembleras ju inte till ettor och nollor, som du vet. Det blir därför den första BYTEns adress som representerar början på vår BUFFER). Adressregistret A1 används alltså som en pekare (POINTER) till den address där vi vill att våra inlästa data ska lagras. Lägg märke till att vi inte förändrar något i de adresser vi läser. Datan kopieras bara in i vår BUFFER. Rad 4: En LABEL (en etikett i vårt program). Rad 5: Flyttar in talet som befinner sig i den BYTE (adress) som adressregistret A0 pekar på, i D1. Parentesen runt A0 betyder att man inte ska använda de data som adressregistret A0 innehåller från tidigare, men att man går till den adress som A0 innehåller (pekar på) och hämtar den data som ligger där. Detta sätt att adressera på kallas för: REGISTER INDIRECT (indirekt register- adressering) och används väldigt ofta i MC på AMIGAN. Lär dig därför detta med en gång! Rad 6: Adderar 1 till det tal som redan finns i A0. Resultatet blir att A0 kommer att peka på nästa adress som vi vill läsa ifrån. Rad 7: Flyttar in talet som befinner sig i D1 i den adress som adressregister A1 pekar på. Se kommentar till RAD 5. Rad 8: Adderar 1 till det tal som finns i A1 (höjer alltså A1 med ett). Detta resulterar i att A1 nu kommer att peka på den adress i minnet (i vår buffer) där vi ska lagra nästa BYTE. Rad 9: Subtrahera 1 från D0. Detta är vår räknare som förminskas med ett varje gång vi läst en BYTE - totalt 16 gånger. Rad 10: Kontrollera om D0 har blivit 0. Instruktionen CMP betyder "jämföra" och används här för att se om vi har läst 16 BYTEs (för då har ju dataregistret D0 fått värdet 0) Rad 11: Om D0 inte är lika med 0, hoppar programmet tillbaka till vår LABEL "loop:" (rad 4). Om d0 är lika med 0 är LOOPen utförd 16 gånger, och programmet kommer inte att hoppa tillbaka. I stället kommer programmet ta sig an nästa instruktion. Rad 12: ÅTERVÄND FRÅN SUBRUTIN (eller avslut) och låt K-SEKA åter ta kontroll. Rad 14: LABELn som pekar på början av vår egen BUFFER. Rad 15: Här har vi deklarerat (eller: satt av) 16 BYTEs till vår BUFFER. Vi kunde också ha skrivit: blk.l 4,0 eller blk.w 8,0. Det hade gett samma resultat. BLK står för "block", och 4,0 (och 8,0) visar antalet LONGWORDs (eller antal WORDs) vi vill reservera som BUFFER. Talet "0" läggs in i alla BYTEs som BUFFERn består av, så att vi samtidigt får BUFFERn "rensad" (eller tömd - om det skulle ligga gamla data där från tidigare). Se även kommande avsnitt. Låt oss se på några nya instruktioner: CMP (jämför) BNE, BEQ, BHI, BLO (olika "BRANCHER") BLK, DC (BLOCK- och DECLARE-kommandon) Vi tittar först på STATUS-registret innan vi går igenom instruktionerna. STATUSREGISTER MSB LSB INNEHÅLL: : : : : X : N : Z : V : C : Detta är en del av det s.k. STATUSREGISTRET i MC-68000. Vi kommer inte att skriva eller läsa direkt till detta register. Du behöver heller inte veta vilka BITnummer dessa olika FLAGGOR ligger på (ordet FLAGG förklaras nedanför). Allt du behöver veta är vilka FLAGG som finns och vad de används till. FLAGG är ett nytt uttryck som vi ska se lite närmare på. Här är först dess respektive beteckningar: C - Carry flag (minne) V - oVerflow flag (varning, talet har blivit för stort) Z - Zero flag (något har blivit noll) N - Negative flag (talet är negativt) X - eXtend flag (speciellt minne) Att man använder en BIT för att visa 0 om något särskilt har hänt, och 1 om något annat har hänt, kallas att använda sig av BITen som FLAGG. När FLAGGAN har blivit 1, säger man att den är SATT. Tänk dig en längdhoppstävling. Vid plankan där hopparen tar sats står det en typ och viftar med en röd flagga varje gång det blir övertramp - och vit om hoppet är godkänt. Samma princip gäller för FLAGGORNA i STATUS-REGISTRET. De talar om för den som programmerar när t.ex. ett tal är negativt (N-flaggan), när ett resultat har blivit noll (Z-flaggan), när ett tal har blivit för stort (C-flaggan och V-flaggan) och så vidare. Vi kommer att förklara varje FLAGGA mer ingående nedan. I följande exempel blir CARRY-flaggan 1 därför att summan av de två talen (20010 och 10010) överstiger det tal som en BYTE kan innehålla (255). move.b #200,d0 add.b #100,d0 rts Som tidigare nämnts kan ett 8-BITs tal, eller en BYTE bara innehålla tal från 0 till och med 255. I exemplet ovan lägger vi in 200 i D0, och lägger sedan till 100. Det borde alltså ligga 300 i vår BYTE. Då ett så stort tal inte får plats, blir CARRY-flaggan satt till 1 för att tala om för oss att talet har blivit för stort. 11001000 (200 decimalt) + 01100100 (100 decimalt) ---------- = 1 00101100 (300 decimalt) Som du ser måste vi använda en nionde BIT för att få rätt svar. Det är denna BIT (BITen längst till vänster i svaret) som hamnar i CARRY-flaggan (CARRY-BITen). Om resultatet blir mindre än 256 blir CARRY-flaggan istället nollställd. OVERFLOW-flaggan liknar CARRY-flaggan mycket. Skillnaden är att denna används som den "nionde BITen" i 2-KOMPLIMENTSTAL eller SIGNERADE tal. Mer om detta i ett senare brev. ZERO-flaggan är kanske den viktigaste flaggan i processorn. Den används i många olika sammanhang. Varje gång en operation resulterar i ett noll-resultat blir denna flagga satt. I vårt första exempel i detta kapitel står det i rad 10 följande: cmp.l #0,d0. Denna linje kunde gott strykas. Detta därför att så snart rad 9 resulterar i noll i dataregister d0, sätts ZERO-flaggan automatiskt. Ta en titt på denna instruktion: cmp.l #100,d0 Detta betyder: Ta följande tal (100 DECIMALT) och jämför det med det som finns i dataregister d0. Om d0 då innehåller 100 sedan tidigare kommer denna operation resultera i en likhet. Denna instruktion kommer också att sätta ZERO-flaggan för att resultatet blev en likhet (om d0 är 100), även om vi inte jämfört med noll. NEGATIVE-flaggan sätts för att visa att ett resultat är negativt (minus). Vi återkommer till detta senare. EXTEND-flaggan används vid rotering eller skiftning av BITs. Flaggan används också i komplicerade additions- eller multiplikations-rutiner och fungerar då som en CARRY-flagga. Vi återkommer till denna flagga senare. Instruktionen CMP för "COMPARE" (jämför) används för att jämföra tal. För att den ska vara till någon nytta måste den kombineras med en BRANCH. Vi visar några exempel: move.l #100,d0 cmp.l #50,d0 (här kommer en av följande BRANCHer:) beq talet är likt (BRANCH ON EQUAL) bne talet är olikt (BRANCH IF NOT EQUAL) bhi talet är högre (BRANCH ON HIGHER) blo talet är lägre (BRANCH ON LOWER) BEQ-instruktionen (Branch on EQual) kommer inte att utföra ett hopp, därför att D0 i vårt exempel (som innehåller 100) inte är likt 50. BNE-instruktionen (Branch if Not Equal) utför ett hopp, därför att D0 är olikt 50. BHI-instruktionen (Branch on HIgher) utför också ett hopp i.o.m. att D0 är "högre" än 50. Som du ser är talet 50 använt som referenspunkt. Du måste alltså läsa instruktionen "bakvänt" för att det ska bli rätt, alltså: "Är D0 större än 50?" BLO-instruktionen (Branch on LOwer) utför inte ett hopp, därför att D0 inte är "lägre" än 50. Det är mycket viktigt att du förstår ordentligt. Se också i program exemplet på rad 10. Vi kunde där ha använt BHI i stället för BNE. Nu ska vi se lite på BLK- och DC-instruktionen. BLK-instruktionen (BLocK) använder vi för att sätta av ett område i minnet till att behandla eller lagra data i. BLK är ingen instruktion som blir assemblerad till ettor och nollor. Låt oss se lite på hur man använder den: 1 blk.b 10,0 2 blk.b $10,0 3 blk.l 100,0 4 blk.b $2800,255 5 blk.w $2800,255 Rad 1: Kommer att sätta av 10 BYTEs, och lägga 0 i alla BYTEsen. Så här: $00 00 00 00 00 00 00 00 00 00 Rad 2: Kommer sätta av $10 ($10 = 16 DECIMALT) BYTEs, och lägga 0 i alla BYTEsen. Så här: $00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Rad 3: Kommer att lägga av 100 LONGWORDs (som är 400 BYTEs), och lägga 0 i alla LONGWORDen. Så här: $00000000 00000000 00000000 00000000 00000000..... (100 ggr) Rad 4: Kommer att lägga av $2800 (10240 DECIMALT) BYTEs, och lägga 255 i alla BYTEsen (255 DECIMALT = $FF HEXADECIMALT) Så här: $FF FF FF FF FF FF FF FF FF FF FF FF FF FF..... (10240 ggr) Rad 5: Kommer att lägga av $2800 WORDs, och lägga 255 i alla WORDen. Så här: $00FF 00FF 00FF 00FF 00FF 00FF 00FF 00FF 00FF..... (10240 ggr) DC-instruktionen (DeClare, översatt: deklarera) har samma funktion som BLK,men denna lägger bara av en BYTE, ett WORD eller ett LONGWORD. 1 dc.b 0 2 dc.b 255,0,10 3 dc.b "AMIGA" 4 dc.w $100 5 dc.l 123456 Rad 1: Lägger av en byte och sätter den till 0. Så här: $00 Rad 2: Det är möjligt att ha flera tal på samma rad. Då blir det lika många BYTEs (i.o.m att vi använder kommandot DC.B) undanlagda som du har angett tal till. Här blir de satta till 255 (=$FF), 0 och 10 (=$0A). Så här: $FF 00 0A Rad 3: Denna är lite speciell. Som du säkert vet tar ett tecken en BYTE. Studera följande: $41 4D 49 47 41. När man lagrar ett tecken i datorer, lagras de som tal. Vi har tidigare nämnt att bokstaven A lagras som talet 65 ($41). Längst bak i din handbok till AMIGA finns en tabell som visar vilka tal som representerar vilka bokstäver. Denna tabell kallas för ASCII-tabell. Förkortningen står för: American Standard Code for Information Interchange, översatt: Amerikansk Kod för Utbyte av Information. Denna data-standard för att lagra information standardiserades av Lyndon B. Johnson då han var president i USA, och den används som sagt för alla datorer idag. De HEXADECIMALA talen ovan kan du via ASCII-tabellen läsa ut till ordet: AMIGA. Fiffigt, eller hur? Rad 4: Lägger av ett WORD, och sätter det till $100 (=25610). Så här: $0100h Rad 5: Lägger av ett LONGWORD, och sätter det till 123456 (=$1E240). Så här: $0001E240 Detta var väl enkelt? Om du inte är helt säker, ska du läsa detta en gång till. Det är nämligen oerhört viktigt att du förstår detta. Det ska vara en grundsten i din programmeringskunskap. Och så andra versionen: 1 move.l #15,d0 2 move.l #$00,a0 3 lea.l buffer,a1 4 loop: 5 move.b (a0)+,(a1)+ 6 dbra d0,loop 7 rts 8 9 buffer: 10 blk.b 16,0 Rad 1: Lägger in talet 15 i D0: Här används D0 som en räknare igen. Rad 2: Lägger in talet $00 i A0. Rad 3: Lägger in adressen till vår BUFFER i A1. Rad 4: En LABEL Rad 5: Denna instruktion gör samma sak som instruktionerna på rad 5, 6, 7 och 8 i det andra exemplet. Instruktionen förklaras bättre nedan. Rad 6: Denna instruktion ersätter linjerna 9, 10 och 11 i det andra exemplet. Vi kommer även att förklara denna nedan. Rad 7: Avslutar programmet. Rad 9: LABELn till vår BUFFER. Rad 10: Lägg av 16 BYTEs, fyll dem med 0. Det utseende som MOVE-instruktionen har i rad 5 ska vi nu förklara för dig. Det används väldigt ofta, så låt oss ta det med en gång. Du har lärt dig att om du använder parenteser runt ett adressregister så som i rad 5 i första exemplet, så pekar adressregistret på adressen som skall användas. Denna nya version verkar på samma sätt, men har en extra finess. Se på detta exempel. move.b (a0)+,d0 Den utför det samma som: move.b (a0),d0 add.l #1,a0 Alltså: Den använder talet som ligger i A0 som en adress. Sedan går den till den adressen och hämtar talet som ligger där och lägger in den i dataregister d0. Sedan adderar den automatiskt 1 till A0 för att det står ett plus-tecken efter parentesen. Hade det stått "move.w" i stället för "move.b" skulle den ha adderat 2 till A0 (för att ett WORD består av två BYTEs). Hade det stått "move.l" i stället för "move.b" skulle den ha adderat 4 till A0 (därför att ett LONGWORD består av fyra BYTEs). Instruktionen "MOVE.B (A0)+,(A1)+" kommer att: 1: Hämta talet på den adress som A0 pekar på (eller lättare: som A0 innehåller), och lagra det på den adress som A1 pekar på (eller innehåller). 2: Addera 1 till A0 (Vi arbetar med storleken BYTE) 3: Addera 1 till A1 (Vi arbetar med storleken BYTE) Du kommer som sagt att använda detta adresseringssätt ofta. Speciellt när du behandlar data-tabeller och när du flyttar runt data i AMIGANS minne. Det är därför väldigt viktigt att du lär dig detta ordentligt. Nästa instruktion vi ska se på är DBRA. Studera exemplet nedan: move.l #9,d0 loop: dbra d0,loop rts Exemplet ovan utför det samma som: move.l #10,d0 loop: sub.l #1,d0 cmp.l #0,d0 bne loop rts Det DBRA egentligen gör är att: 1: Ta 1 från D0. 2: Jämföra D0 med -1. 3: Är det -1? Om inte, hoppa tillbaka. (Hur ett tal kan vara negativt i en BYTE, ett WORD eller ett LONGWORD ska vi återkomma till. För ögonblicket behöver du inte tänka på det problemet) När D0 har blivit -1 kommer den att fortsätta på nästa instruktion. Kom ihåg att DBRA hela tiden kontrollerar med -1, så vi måste ange tal i räknaren (D0) som är 1 mindre än det antal gånger vi vill att loopen ska utföras. COPPER och BITPLANES Nu ska vi kasta oss ut i den stora ocean som kallas BITPLANEs. Vi ska samtidigt försöka att förklara mer om COPPERn, detta kommer att medföra en hel del hoppande fram och tillbaka. Det kan därför löna sig att läsa igenom hela kapitlet en gång, för att få en smula översikt över vad som behandlas, för att sedan läsa det hela om igen från början. Låt oss starta med att förklara lite om ett HARDWARE-register som i datalitteraturen om AMIGA har fått namnet "DMACON" (DMA-CONTROL). Detta är ett namn på ett speciellt WORD (en address som består av två BYTEs) som betecknas REGISTER. Det bör påpekas att dessa HARDWARE-register inte har något att göra med D0, A0 eller något av registren i MC-68000. Dessa register ligger i en helt annan del av datorn, nämligen i AGNUS, PAULA och DENISE. Registrena har därför en address. Adressen till DMACON är $DFF096. Alla adresser på HARDWARE-registren börjar med $DFF---. Vi kallar då $DFF000 för BASADRESSEN för HARDWARE-registren. OBS! Alla HARDWARE-register är 16 BITs, alltså ett WORD. Låt oss få påpeka en mycket viktig sak innan vi går vidare. HARDWARE-registren är uppbyggda så att de antingen bara kan skrivas till, eller bara läsas från. T.ex. kan färgregistrena bara skrivas till. Om du försöker att läsa dem, kommer du bara få en nolla tillbaka, eller ett annat helt villkorligt tal. JOYSTICK-registren är av den andra typen - de som bara kan läsas från. Att skriva till dessa har ingen inverkan. (Du kan alltså inte be din JOYSTICK flyga och fara!) Lägg också märke till att några register har två adresser - en skriv-adress och en läs-adress. Ett av dessa sistnämnda register (DMACON) är det vi ska gå igenom nu. Om du ska skriva till registret måste du använda adressen $DFF096. Om du däremot ska läsa från det, skall du använda adressen $DFF002. DMACON är uppställt så här (OBS! Ett WORD, 16 BITs): $DFF096 WRITE - $DFF02 READ BIT NR. FUNKTION ACCESS 15 SET/CLR W 14 BLITTER BUSY R 13 BLITTER ZERO R 12 - 11 - 10 BLITTER NASTY R/W 9 DMA ENABLE R/W 8 BITPLANE DMA R/W 7 COPPER DMA R/W 6 BLITTER DMA R/W 5 SPRITE DMA R/W 4 DISK DMA R/W 3 AUDIO CHANNEL 3 DMA R/W 2 AUDIO CHANNEL 2 DMA R/W 1 AUDIO CHANNEL 1 DMA R/W 0 AUDIO CHANNEL 0 DMA R/W Studera tabellen ovanför. Vi har valt att använda de engelska beteckningarna, som du ser. Det finns två orsaker till det: För det första så kan du lättare referera till - och förstå - speciallitteratur på engelska, och för det andra så blir översättningen väldigt dålig då det ofta inte finns någon motsvarighet i svensk data-terminologi. Lägg märke till att vi i sista kolumnen (ACCESS = tillgång till) anger vilka BITs som kan läsas ("R" för READ) och skrivas ("W" för WRITE) till. OBS! Om en DMA ska vara AV (eller nollställas), sätts den till 0. Om däremot en DMA ska vara PÅ (eller aktiveras) sätts den till 1. BIT 0-3:DMAna för ljudkanal 0 till 3 av/på BIT 4: DISK-DMA av/på BIT 5: SPRITE-DMA av/på BIT 6: BLITTER-DMA av/på BIT 7: COPPER-DMA av/på BIT 8: BITPLANE-DMA av/på BIT 9: Denna BIT kallas för DMA ENABLE (översatt: aktivera DMA) och måste vara satt (1) för att DMAer ska kunna användas (kan liknas vid en huvudströmbrytare, av/på). Önskar man av en eller annan anledning att stänga av alla DMAer, så nollställer man alltså denna BIT. Man kan också lägga märke till att om DISK-DMAn och SPRITE-DMAn är PÅ när du stänger av DMAn, så kommer det bara att vara dessa två som aktiveras när du slår på DMAn igen BIT 10: BLITTER NASTY. Mer om denna BIT i brev 6 och 7. BIT 11-12: Används inte till någonting. BIT 13: BLITTER ZERO. Mer om denna BIT i brev 6 och 7. BIT 14: BLITTER BUZY. Mer om denna BIT i brev 6 och 7. BIT 15: SET/CLEAR. Detta kommer att förklaras nedan. Detta register - DMACON - är lite speciellt när det gäller skrivning. T.ex. om du ska skruva på COPPER-DMAn, måste du lägga in (skriva) följande data i registret ($DFF096) BIT 7 och 15 ska vara "1" och resten av BITsen ska vara "0", HEXADECIMALT: $8080, BINÄRT: %1000 0000 1000 0000. (Har du glömt omvandlingar från HEXADECIMALT till BINÄRT, är det klokt att gå igenom BREV 1 igen). För att skruva av COPPERn måste BIT 7 vara "1", och resten vara "0", HEXADECIMALT: $0080, BINÄRT %0000 0000 1000 0000 Alltså, om du vill skruva av t.ex. BITPLANE-DMAn och SPRITE-DMAn, måste BIT 15 vara 0, och BIT 5 och 8 vara 1 (resten ska vara 0). HEXADECIMALT blir detta $0120 (BINÄRT %0000 0001 0010 0000), eller som en instruktion: move.w #$0120,$DFF096 Om du nu vill skruva på samma DMAs igen,förändrar du bara BIT 15 till 1 move.w #$8120,$DFF096 (eller: move.w #%1000000100100000,$DFF096) Vi vill gärna förklara detta en gång till. Det är nämligen så att det finns 4 register (8 stycken - om du räknar både READ och WRITE registren) som fungerar på detta sätt, och det är därför väldigt viktigt att du förstår det fullt ut. Alltså: Om du sätter BIT 15 till "1" och anger vilka BITs som ska påverkas, kommer datorn själv att sörja för att få de av dig angivna BITsen satta till "1" också. Om du däremot sätter BIT 15 till "0", så kommer datorn sätta, de av dig, angivna BITsen till "0". En gång till - med andra ord: Alla de BITs du sätter till "1" får det värde som BIT 15 har. Alla andra BITs som är "0" förblir oförändrade. Enkelt och bra, eller hur? OBSERVERA! Om du ska skruva PÅ och AV några DMAer samtidigt, måste du skriva till registret två gånger - en gång för att skruva av DISK DMAn, och en gång för att skruva på BLITTER DMAn (om det nu är det du vill göra). Låt oss nu förklara COPPER-instruktionerna. Studera följande uppställning av de två WORDs som en komplett COPPER- instruktion består av: Första WORD: BIT NR: MOVE WAIT 15 A15 V7 14 A14 V6 13 A13 V5 12 A12 V4 11 A11 V3 10 A10 V2 9 A9 V1 8 A8 V0 7 A7 H8 6 A6 H7 5 A5 H6 4 A4 H5 3 A3 H4 2 A2 H3 1 A1 H2 0 0 1 Andra WORD BIT NR: MOVE WAIT 15 D15 BFD 14 D14 MV6 13 D13 MV5 12 D12 MV4 11 D11 MV3 10 D10 MV2 9 D9 MV1 8 D8 MV0 7 D7 MH8 6 D6 MH7 5 D5 MH6 4 D4 MH5 3 D3 MH4 2 D2 MH3 1 D1 MH2 0 D0 0 En COPPER-instruktion är alltid 4 BYTEs lång (2 WORDs). Vi brukar i regel dela den i två så som vi visar här. dc.w $5001,$FFFE (dc.w = DECLARE WORD) Låt oss ta oss an WAIT-instruktionen: I vår förklaring kommer vi att använda "VV" för att beteckna vertikal vänteposition. Med det menar vi att COPPERn får besked om att elektronstrålen skall nå den linje du anger innan den (COPPERn) hämtar nästa instruktion. "HH" använder vi för att beteckna horisontal vänteposition för COPPERn. Med det menar vi att COPPERn får besked om att elektronstrålen ska nå en speciell punkt på den linje du anger innan den (COPPERn) hämtar nästa instruktion. VVHH,$FFFE VV=$00 till $FF HH=$01 till $FE (endast udda tal kan användas) Vi kommer att använda värdet $01 som horisontell position i alla WAIT-instruktioner (alltså, punkten längst till vänster på linjen). Väntan på horisontella positioner större än $01 kommer förklaras i brev 12. När det gäller den vertikala positionen, gäller följande: Linje 0 är inte den översta vita linje du ser på t.ex. din WORKBENCH- skärm. Den är som regel linje $2C (44 DECIMALT). Elektronstrålen börjar egentligen 44 linjer längre upp än den översta linjen som du ser. Detta vill igen säga att den nedersta linjen på WORKBENCH- skärmen blir nummer 256 + 44 = 300 (WORKBENCH-skärmen blir därför 256 linjer hög). Under dessa 300 linjer ligger ytterligare 12 linjer - som du delvis inte kan se - så den totala storleken på en hel AMIGA-skärm blir 312 linjer. COPPERn har därför ett totalt vänteområde från linje 0 till och med linje 312. Det andra WORDet i WAIT-instruktionen har vi sällan eller aldrig behov av att förändra. Denna del kommer förklaras senare. Vi kommer att använda samma värde hela tiden, nämligen $FFFE Förklaring av MOVE-instruktionen: Move-instruktionen kan bara skriva data till HARDWARE-registren i området $DFF000 till $DFF200. Exempel: dc.w $0180,$0FFF Det första WORDet i MOVE-instruktionen ska innehålla addressen. Det andra WORDet i MOVE ska innehålla datan du ska lägga i adressen. Adressen räknas ut så här: $0180 + $DFF000 = $DFF180. Denna adress måste vara jämna tal - nästa adress blir därför $DFF182 (ett avstånd på ett WORD). Om du däremot skriver så här (med udda adresstal): dc.w $0181,$0FFF kommer COPPERn tolka det som en WAIT-instruktion i stället. COPPERn använder BIT 0 i det första WORDet för att skilja mellan en WAIT- och en MOVE-instruktion. Den tredje instruktionen till COPPERn heter SKIP. Denna instruktion har du praktiskt taget aldrig användning för, så vi kommer att skippa (...) den tills den förklarats kortfattat i ett senare brev. Förklaring till exemplet i BREV 2 (sidan 17). Rad 1: Skruvar av BITPLANE-, COPPER- och SPRITE-DMAna Rad 2: Lägger in adressen till COPPER-listan i A1. Rad 3: Lägger in innehållet i A1 i $DFF080. Vi måste lägga in addressen till vår egen COPPER-lista i detta register för att COPPERn ska veta var COPPER-listan befinner sig. Som du ser så använder vi i denna rad LONGWORD när vi kopierar adressen till A1, även om vi har sagt att du bara kan använda WORD när man adresserar HARDWARE-registren. Detta är ett undantag. Egentligen är COPPER-pekaren på två adresser: $DFF080 (HI) och $DFF082 (LO) Beteckningarna HI och LO står för HÖG (HIGH) och LÅG (LOW) adress. Som du vet är ett LONGWORD det samma som två WORDs. BIT 31 till 16 hamnar således i $DFF080 och representerar de högsta (mest "värdefulla") 16 BITs i den kompletta adressen, och BIT 15 till 0 hamnar i $DFF082 och representerar de lägsta (minst "värdefulla") 16 BITs i den kompletta adressen. Rad 4: Skruvar på COPPERn igen. Denna instruktion kommer att fungera så att vår egen COPPER-lista startas. Rad 6: En LABEL - i stället för "etikett" är det riktigare och bättre att beteckna den som en "referenspunkt". Rad 7: Denna instruktion har vi inte gått igenom ännu. Det den gör är att kontrollera BIT 6 (btst=BitTeST) i adressen $BFE001. Om BITen är 0 kommer den sätta ZERO-flaggan till 0. Är biten 1, sätts ZERO- flaggan till 1. När du trycker på vänster mus-knapp, kommer denna BIT i $BFE001 sättas till 0. Rad 8: Denna BRANCH kommer undersöka om ZERO-flaggan är 0, och om den är det, då har du tryckt på vänster mus-knapp, och programmet hoppar tillbaka till referenspunkten "wait". Rad 10: Skruvar av COPPER-DMAn. Rad 11: Lägger in det som finns i adress $000004 i A6. (Raderna 11 till 13 förklaras närmare i BREV 10. Kort kan vi säga att de hämtar tillbaka WORKBENCH-skärmen). Rad 12: Lägger in det som finns i adress A6 + 156 i A1. Rad 13: Lägger in det som finns i adress A1 + 38 i COPPER-POINTERn. Rad 14: Skruvar på COPPER-DMAn igen. Rad 15: Avslutar programmet. Rad 17: LABEL till COPPER-listan. Rad 18: WAIT ($01,$90) Rad 19: MOVE $00F0 -> $DFF180 Rad 20: WAIT ($01,$A0) Rad 21: MOVE $0FFF -> $DFF180 Rad 22: WAIT ($01,$A4) Rad 23: MOVE $000F -> $DFF180 Rad 24: WAIT ($01,$AA) Rad 25: MOVE $0FFF -> $DFF180 Rad 26: WAIT ($01,$AE) Rad 27: MOVE $0F00 -> $DFF180 Rad 28: WAIT ($01,$BE) Rad 29: MOVE $0000 -> $DFF180 Detta kommer att förklaras mycket mer - och utförligare - i BREV 4. BITPLANE-systemet i AMIGA. Vi börjar med att sätta upp en egen skärm. 1 move.w #$01a0,$dff096 2 3 move.w #$1200,$dff100 4 move.w #0,$dff102 5 move.w #0,$dff104 6 move.ww #0,$dff108 7 move.w #0,$dff10a 8 9 move.w #$2c81,$dff08e 10 move.w #$f4c1,$dff090 11 move.w #$38c1,$dff090 12 13 move.w #$0038,$dff092 14 move.w #$00d0,$dff094 15 16 lea.l copper,a1 17 move.l a1,$dff080 18 19 move.l a1,$dff080 20 21 wait: 22 btst #6,$bfe001 23 bne wait 24 25 move.w #$0080,$dff096 26 27 move.l $4,a6 28 move.l 156(a6),a1 29 move.l 38(a1),$dff080 30 31 move.w #$80a0,$dff096 32 33 rts 34 35 copper: 36 dc.w $2c01,$fffe 37 dc.w $0100,$1200 38 39 dc.w $00e0,$0000 40 dc.w $00e2,$0000 41 42 dc.w $0180,$0000 43 dc.w $0182,$0ff0 44 45 dc.w $ffdf,$fffe 46 47 dc.w $2c01,$fffe 48 49 dc.w $0100,$0200 50 51 dc.w $ffff,$fffe Nedan finner du en kort förklaring till varje instruktion. I BREV 4 kommer allt att förklaras utförligt. Ta det därför inte så hårt om du inte förstår vad som egentligen händer. Rad 1: Skruvar av BITPLANE-, COPPER- och SPRITE-DMAna Rad 3: Sätter LORES - låg upplösning - (320*256), och en BITPLANE, a lltså 2 färger. Rad 4: Sätter SCROLL-värde till 0. Rad 5: Sätter BITPLANE PRIORITY till 0. Rad 6: Sätter MODULO för ODD BITPLANES till 0. Rad 7: Sätter MODULO för EVEN BITPLANES till 0. Rad 9: Sätter översta vänstra hörnet på skärmen till position: Y (vertikalt) = $2C och X (horisontellt) = $81. Rad 10: Sätter nedersta, högra hörnet på skärmen till position: Y (horisontellt) = $F4 och X (vertikalt) = $C1. Rad 11: Adderar Y-positionen med $38, alltså $F4 + $38 = $12C. X-positionen blir den samma. Rad 13: Sätter DATAFETCH START till $0038. Rad 14: Sätter DATAFETCH STOP till $00D0. Rad 16: Lägger in adressen till COPPER-listan i A1 Rad 17: Lägger in värdet som finns i A1 i COPPER-POINTERn. Rad 19: Skruvar på BITPLANE- och COPPER-DMAna igen (inte SPRITE-DMAn). Rad 21: LABEL. Rad 22: Kontrollerar om vänster mus-knapp tryckts ner. Rad 23: Om mus-knappen inte är nedtryckt,så hoppa tillbaka till "wait" Rad 25: Skruvar av COPPER-DMAn. Rad 27: Hämtar in (OBS! egentligen: kopierar) talet som ligger i adressen $000004 i A6 Rad 28: Hämtar in talet som ligger på den adress som A6 innehåller + 156,i A1. Rad 29: Hämtar in talet som ligger på den adress som A1 innehåller + 38, i COPPER-POINTERn ($DFF080). Rad 31: Skruvar på COPPER- och SPRITE-DMAna igen. Rad 33: Avslutar. Rad 35: LABELn till COPPER-listan. Rad 36: WAIT ($01,$2C) Rad 37: MOVE $1200 -> $DFF100 Rad 39: MOVE $0000 -> $DFF0E0 Rad 40: MOVE $0000 -> $DFF0E2 Rad 42: MOVE $0000 -> $DFF180 (svart bakgrund) Rad 43: MOVE $0FF0 -> $DFF182 (gul förgrund) Rad 45: WAIT ($DF,$FF) Detta kommer att göra att datorn slår om till PAL AMIGA med 256 linjer. I USA, där man har ett annat system (NTSC), har AMIGA bara 200 linjer. Rad 47: WAIT ($01,$2C) Den vertikala positionen ($2C) kommer här att säga till $12C,därför att vi utförde denna PAL-WAITen på raden ovanför. Rad 49: MOVE $0200 -> $DFF100 Rad 51: Stoppar COPPERn och hoppar tillbaka till början av COPPER-listan (utför den ännu en gång). LÖSNINGAR TILL BREV 2 Uppgift 0201: Därför att det finns 8 SPRITE DMAer, 4 AUDIO DMAer, 4 BLITTER DMAer, 1 DISK DMA, 6 BITPLANE DMAer och en COPPER DMA. Uppgift 0202: En SUBRUTIN är en delrutin eller underprogram som utför en speciell uppgift. Uppgift 0203: I ett WORD är det två BYTEs, och 16 BITs. Uppgift 0204: I ett LONGWORD är det 4 BYTEs, och 32 BITs. Uppgift 0205: I MC-68000 finns det 8 ADRESSREGISTER, och de heter A0, A1,A2, A3, A4, A5, A6 och A7. Uppgift 0206: I MC-68000 finns det 8 DATAREGISTER, och de heter D0, D1, D2, D3, D4, D5, D6 och D7. Uppgift 0207: Instruktionen lägger in det som finns i adress $10 i ADRESSREGISTER 3. Uppgift 0208: MSB betyder MOST SIGNIFICANT BIT, och anger den högsta värde-BITen i en BIT-grupp. LSB betyder LEAST SIGNIFICANT BIT, och anger den lägsta värde-BITen i en BIT-grupp. Uppgift 0209: COPPERn har 3 instruktioner, och de heter MOVE, WAIT och SKIP. Uppgift 0210: En elektronkanon är den stråle som ritar bilden på skärmen.