Assembly Cursus 7

Mensen kwamen naar mij toe en vonden de theorie erg interessant, maar wat kun je er verder mee. Begrijpelijk, ik kan je vertellen hoe een auto werkt, maar je zult er ook een keer in moeten rijden. Vandaar de eerste hands-on. En om deze gelijk visueel te maken, gaan we met sprites aan de slag. We gaan in deze hands-on een sprite maken, een kleur geven en op het scherm laten zien. En omdat alle hekken van de dam zijn, laten we hem ook nog heen en weer gaan, gekkegeid ten top natuurlijk! U vraagt, wij draaien.

CursusAssembly Deel1 hdr

Assembly, deel 7Computer: Commodore 64
Datum: 2014
Type: Programmeertaal
Door: Addy van Ladesteijn

Nu we de commando’s een keer gezien hebben, we van lussen weten en adressen geen geheimen meer hebben gaan we met sprites aan de gang. Je kunt er zes tegelijkertijd op het scherm hebben. Het is essentieel dat deze opdrachten een tweede natuur worden, want deze zullen vaak gebruikt worden! Zorg dus dat je dit stuk met de voorbeelden begrijpt. Ik heb zoveel mogelijk voorbeelden gebruikt om de werking aan te tonen. Mocht je nog vragen hebben, kun je altijd mailen!

Sprite editorSprites
Sprites zijn onmisbaar voor de assembly programmeur. Ze worden ook wel Movable Object Blocks (MOB’s) genoemd en worden omschreven als graphics die kunnen bewegen los van andere graphics of text op het scherm. De chip die dit allemaal mogelijk maakt is de Video Interface Chip, of kortweg VIC genaamd. Deze chip ondersteunt 8 sprites tegelijkertijd op het scherm, maar door gebruik van een raster interrupt (ander keer) kunnen er meer getoond worden. En nu denken de meeste mensen dat dit wel een erg lastig stukje programmeerwerk zal zijn, maar niets is minder waar, zoals we met deze module zullen aantonen. We gaan eerst een sprite maken, en deze op het scherm tonen.

Hoe maak je een sprite op de Commodore 64
Het patroon van een sprite wordt gemaakt in een raster van 24 pixels (bits) breed en 21 pixels (bits) hoog. Een enkele sprite beslaat totaal 504 pixels/bits, die weer in 63 bytes passen.  Deze 63 bytes worden opgeslagen in het RAM van de VIC-bank. Fijn zo, maar nu dus het ontwerp. We gebruiken hiervoor zoals eerder gezegd een raster. In onderstaande grid kun zie je 21 rijen  van elk 3 kolommen (bytes). Elke kolom bestaat per regel weer uit 8 bits. Je kunt nu een sprite ontwerpen door simpelweg de vakjes in te kleuren. Elke pixel/bit van het raster heeft twee mogelijke waardes: 1 (vakje ingekleurd) of 0 (vakje transparant).Sprite rooster
Als we dus bijvoorbeeld een cirkel willen maken, kleur je de vakjes zoals hieronder in, en de sprite is ontworpen.  Maar hoe vertalen we dit nu naar assembly? Dat doen we door het bitpatroon van elke byte te zetten. Huh? Ik leg het uit. In het grid hierboven, zie je in de (header) rij de bitwaarden staan.  Voor elk van de 21 rijen moeten we vertellen welke pixel (bit) ingelkleurd (1) is, of transparant (0) is. Deze bitwaarde bereken je door de de waarde die bij de ingekleurde vakjes hoort (zie header regel) op te tellen. Je krijgt dan drie getallen (bytes). Kijk naar de eerste rij van de cirkel hieronder.Sprite voorbeeldIn de eerste rij is geen van de eerste 8 pixels ingekleurd, dus de waarde van het eerste byte is 0. Vervolgens hebben we een lege bit, zes gevulde en weer een lege. Als je dan kijkt welke waardes hierbij horen (zie het eerste hierboven, in de header) zie je dat dat 64+32+16+8+4+2 is, wat totaal 126 is. (Zie de eerste cursus voor het omrekenen van binaire getallen)De laatste drie zijn weer leeg, dus de waarde is 0. En zo is dat voor elke rij gedaan. Vandaar dat in het raster hierboven aan het einde van elke regel ruimte is om de drie getallen in te vullen. Kan dat nu niet makkelijker? Gelukkig zijn er verschillende applicaties die dit doen, online zelfs! Maar ik vind het wel belangrijk dat je weet wat er gebeurt en hoe de bitpatronen berekend worden. Okee, we hebben een cirkel getekend in het raster. We hebben elk van elke byte het bitpatroon omgerekend en hebben nu 21 rijen met 3 getallen. Als we deze 21 rijen op de juiste adres in de VIC opslaan, hebben een een cirkel in het geheugen staan.

Video Interface Chip (VIC)
De VIC chip is een memory-mapped-device, wat niets anders betekent dan dat hij werkelijk voorkomt als een stuk van de geheugenmap van de Commodore. Hij is dus aan te roepen; hij neemt 46 lokaties in vanaf 53248 ($D000) tot en met 53294 ($D02E). Deze lokaties worden ook wel de registers van de VIC genoemd, omdat je een reeks verschijnselen en effecten kan veroorzaken door er bepaalde waardes in te zetten (POKEn). Ik doe dit aan de hand van een BASIC programma tegen assembly programma. Sprites invoeren in BASIC is:

D000-D02E MOS 6566 VIDEO INTERFACE CONTROLLER (VIC)
D000 53248 Sprite O X Pos
D001 53249 Sprite O Y Pos
D002 53250 Sprite 1 X Pos
D003 53251 Sprite 1 Y Pos
D004 53252 Sprite 2 X Pos
D005 53253 Sprite 2 Y Pos
D006 53254 Sprite 3 X Pos
D007 53255 Sprite 3 Y Pos
D008 53256 Sprite 4 X Pos
D009 53257 Sprite 4 Y Pos
D00A 53258 Sprite 5 X Pos
D00B 53259 Sprite 5 Y Pos
D00C 53260 Sprite 6 X Pos
D00D 53261 Sprite 6 Y Pos
D00E 53262 Sprite 7 X Pos
D00F 53263 Sprite 7 Y Pos

Status registerHet Statusregister
In Deel 2 is het statusregister al voorbij gekomen. Bovenstaande commando’s gebruiken het zogenaamde statusregister (SR). Het statusregister is net als de accumulator en de X/Y registers een 8 bits register. Dit register wordt echter op een totaal andere wijze gebruikt. De andere registers worden gebruikt om bytes op te slaan en te manipuleren. Het statusregister wordt echter behandeld alsof het uit acht afzonderlijke bits (van betekenis) bestaat die als signaal of vlag worden gebruikt. Elke bit representeert dus één of andere gezette status/vlag. De processor bekijkt maar één statusvlag (zoals ze worden genoemd) tegelijk. Hij maakt dan het bit ‘0’ of ‘1’ of hij test of het bit gezet is (bit = ‘1’) of niet (bit =’0′).

Z-vlag
Een voorbeeld van een statusvlag is de Z-vlag (of nul-vlag). Wanneer een berekening (of een verschuiving) als resultaat een nul oplevert in het desbetreffende register (A, X of Y) dan wordt de Z-vlag op ‘1’ gezet. Als het resultaat niet nul is, wordt de Z-vlag op ‘0’ gezet. Je kunt ook zeggen dat een ‘0’ waarde betekent dat een conditie (of status) WAAR is en als de waarde ‘1’ is de conditie (of status) FOUT/NIET WAAR is. Dit zullen we aan de hand van een voorbeeld duidelijker maken. Een voorbeeld van een opdracht die de Z-vlag zet is de DEX opdracht. Bekijk onderstaande programma:

OPCODE OPERAND
LDX #100 (Laad 100 in register X)
DEX      (Verlaag de waarde in het X register met 1)

De mogelijkheid om het X- en Y register te verhogen/verlagen (indexeren) heeft deze registers de naam indexregisters gegeven. Na dit programma zal het X-register de waarde ’99’ bevatten en zal de Z-vlag dus op de waarde ‘0’ staan omdat de verlaging er niet voor heeft gezorgd dat het X-register op nul is komen te staan. Om nu gelijk onze eerste voorwaardelijke vertakking te gebruiken, moeten we nog een instructie toevoegen die gaat vergelijken of een conditie WAAR danwel NIET WAAR is. hiervoor gebuiken we de BEQ instructie. Deze instructie controleert de status van de Z-vlag en vertakt als deze gezet is (waarde = ‘1’).

ADRES OPCODE OPERAND
$8000 LDX #100        (Laad 100 in register X)
$8003 DEX             (Verlaag de waarde in het X register met 1)
$8004 BEQ $8007       (X=0? dan Z-vlag gezet met een '1' en naar STX 1024)
$8006 JMP $8003       (Als X geen 0 is, springen we terug naar DEX)
$8009 STX 1024        (Zet een 0 in 1024)
$800C STX 55296       (Geef teken op lokatie 1024 de kleur zwart) $800F RTS

Bovenstaande programma zet het X register op 100 en verlaagt dit vervolgens met de DEX instructie. De BEQ instructie wordt pas uitgevoerd als het X-register op nul staat (en daarmee de Z-vlag van het statusregister op ‘1’ komt te staan). Tot die tijd wordt de BEQ instructie niet uitgevoerd waardoor de daarop volgende JMP weer terugspringt naar het DEX commando. Als alles goed gaat, zal er een zwarte “@” verschijnen in de linker bovenhoek (postitie 1024). We hadden in plaats van het X register ook het Y register kunnen gebruiken met LDY, DEY en STY. De BNE instructie doet hetzelfde als BEQ alleen dan omgekeerd, hij vertakt als de Z-vlag NIET is gezet (en dus de waarde ‘0’ heeft) wat ons weer een JMP instructie scheelt.

ADRES OPCODE OPERAND
$8000 LDX #100        (Laad 100 in register X)
$8003 DEX             (Verlaag de waarde in het X register met 1)
$8004 BNE $8009       (Z-vlag gezet met een '0'? Dan terug naar DEX)
$8006 JMP $8003       (Spring terug naar DEX)
$8009 STX 1024        (Zet een 0 in 1024)
$800C STX 55296       (Geef teken op lokatie 1024 de kleur zwart) $800F RTS

In plaats van registers te verlagen, kunnen we ze ook verhogen doormiddel van INX, en INY. Natuurlijk is een gewone controle op nul niet mogelijk als we de registers ophogen. De registers moeten in dit geval vergeleken worden met een bestaande waarde die we van tevoren ergens hebben opgeslagen. De processor kent hiervoor drie opdrachten: CPX, CPY en CMP. Stel je maakt gebruik van het CPX. Wat je in feite doet is de inhoud van een geheugenplaats aftrekken van de waarde in het X register. Het resultaat kan vervolgens postief, negatief of nul zijn. De opdracht CPX $8003 zorgt er dus voor dat de inhoud op geheugenlocatie $8003 vergeleken wordt met de inhoud van het X-register. Indien de waarden gelijk zijn krijgt de Z-vlag in dit gevaal de waarde ‘0’ (WAAR), zo niet dan krijgt de Z-vlag de waarde ‘1’ (NIET WAAR). Tijdens de CPX instructie wordt er niets veranderd aan de waarde van zowel het X-register als de vergeleken geheugenlocatie. Het resultaat van onderstaande listing zal een witte diamant in de linkerbovenhoek zijn, of een witte ‘Z’ afhankelijk of je in uppercase of lowercase mode staat.

ADRES OPCODE OPERAND
$8000 LDA #90         (Laad 90 in de Accumulator)
$8002 STA $0890       (Sla de inhoud van A op in $0890)
$8005 LDX #0          (Laad de waarde '0' in register X)
$8007 INX             (Verhoog de waarde in het X register met 1)
$8008 CPX $0890       (Vergelijk inhoud X register met inhoud $0890)
$800B BEQ $8010       (Inhoud gelijk? Dan naar $800D)
$800D JMP $8007       (Inhoud niet gelijk? Dan terug naar INX)
$8010 STX 1024        (Zet karakter 90 op het scherm links bovenaan)
$8013 LDA #1          (Laad 1 in de Accumulator)
$8015 STA 55296       (Zet inhoud Accumlator op adres 55296 - kleur wit) $8018 RTS

De derde verlgelijkopdracht is CMP. Deze is bijzonder handig omdat de resultaten van alle rekenkundige operaties automatisch in de Accumulator gezet worden. Met CMP kun je een gegeven waarde en een ‘antwoord’ direct met elkaar vergelijken, zoals in onderstaande voorbeeld:

ADRES OPCODE OPERAND
$8000 LDX #0          (Laad 0 in het X register)
$8002 LDA #83         (Laad 84 in de Accumulator)
$8004 INX             (Verhoog de waarde in het X register met 1)
$8005 STX $0900       (Sla de inhoud van het X register op in $0900)
$8008 CMP             (Vergelijk inhoud X register met inhoud Accumulator)
$800B BNE $8004       (Inhoud niet gelijk? Dan naar $8004)
$800D STX 1024        (Inhoud wel gelijk? Zet karakter "hartje" linksboven )
$8010 LDA #1          (Laad 1 in de Acuumulator)
$8012 STA 55296       (Geef het hartje een witte kleur)
$8015 RTS

N-vlag
De 7e bit, de N-vlag wordt ook wel de Nagetief-vlag genoemd. Deze wordt op 1 gezet als het antwoord van een operatie negatief is. Dit kan getest worden door de opdrachten BMI en BPL en laten dit nu net de vertakkingsopdrachten zijn die we in dit deel gaan behandelen! In onderstaande programma geef ik een voorbeeld van het gebruik van BMI. De inhoud van het Y register wordt net zolang met 1 verhoogd tot het CPY commando geen negatief resultaat meer geeft.

ADRES OPCODE OPERAND
$8000 LDA #90         (Laad 90 in het de Accumulator)
$8002 STA $0900       (Sla de inhoud van de Accumulator op in $0900)
$8005 LDY #0          (Laad 0 in het Y register)
$8007 INY             (Hoog de waarde in het Y register met '1' op)
$8008 CPY $0900       (Vergelijk inhoud van het Y register met inhoud $0900)
$800B BMI $8007       (Test N-vlag, is deze negatief? Dan naar $8004)
$800D STY 1024        (Inhoud wel gelijk? Zet karakter "hartje" linksboven )
$8010 LDA #1          (Laad 1 in de Accumulator)
$8012 STA 55296       (Geef het hartje een witte kleur)
$8015 RTS 

Nu zul je jezelf misschien afvragen waarom de vergelijking CPY $0900 de N-vlag zet. Het is toch niet meer dan een vergelijking die waar of niet waar is? Weet je nog hoe het CPY (of CMP/CPX) commando in feite echt werkt?  Het is niet echt een vergelijking, maar meer aftreksom. In bovenstaande voorbeeld wordt de inhoud van het Y register dus afgetrokken van de Accumulator (A-Y). Als de waarde in het Y register dus groter wordt dan de waarde in de Accumulator (90-91) geeft dit een negatieve waarde en zal het probramma verder gaan met de STY instructie op $800D omdat de N-vlag gezet is. Een CMP, CPY, CPX opdracht is dus eerder een aftreksom dan een vergelijking!

De vlaggen ter afsluiting
We weten nu dat elke bit (of vlag) uit het statusregister een functie heeft; het vertelt een status. Van het statusregister interesseert ons dus niet zozeer de actuele hexwaarde, maar wel de toestand of status van de individuele bits. Van de acht registerbits worden er slechts zeven gebruikt – de resterende bit (bit nummer 5) is voortdurend geset (is dus altijd “1”). We hebben bit 1 (Z-vlag) en bit 7 (N-vlag) in dit deel behandeld, en de overige vlaggen zullen later nog besproken worden. Het is wel handig om alvast aan te geven welke commando’s de vlaggen gebruiken.

Bit 7 – N-vlag (Negatief vlag)
Deze wordt gezet als een rekenkundige operatie een negatief resultaat oplevert. De vlag wordt gebruikt door de opdrachten ADC, AND, ASL, BIT, CMP, CPY CPX, DEC, DEY EOR, INC, INX, INY, LDA, LDX, LDY, LSR, ORA, PLA, PLP, ROL, ROR TAX, TAY, TXA, TYA.

Bit 6 – V-vlag (Overloop vlag)
Deze wordt gezet als een rekenoperatie resulteert in een overloop van bit 6 naar bit 7. Deze vlag vertelt dat het resultaat fout is, tenzij men met de overloop werkt. De vlag wordt gebruikt door de opdrachten ADC, BIT, CLV, PLP, RTI, SBC.

Bit 4 – B-vlag (Break vlag)
De onderbrekingsvlag wordt gezet als BRK een geprogrammeerde interrupt teweegbrengt. Als een programma dus een BRK gekregen heeft.

Bit 3 – D-vlag (Decimaal vlag)
Deze wordt gezet als rekenkundige operaties decimaal moeten worden uitgevoerd. De vlag wordt gebruikt door de opdrachten CLD, PLP, RTI, SED.

Bit 2 – I-vlag (Interrupt vlag)
Deze wordt gezet als er een interrupt wordt uitgevoerd. De vlag wordt gebruikt door de opdrachten BRK, CLI, PLP, RTI, SEI.

Bit 1 – Z-vlag (Zero vlag)
De nulvlag wordt (zoals je nu weet) gezet als een rekenkundige operatie nul als resultaat geeft. De vlag wordt gebruikt door de opdrachten: ADC, AND, ASL, BIT, CMP, CPY CPX, DEC, DEX, DEY, EOR, INC, INX, INY, LDA, LDX, LDY, LSR, ORA, PLA, PLP, ROL, ROR, RTI, SBC, TAX, TXA, TYA.

Bit 0 – C-vlag (Carry vlag)
De overdrachtsvlag geeft de aanwezigheid van een ‘overdracht’ of, in wiskundige termen een lening, aan bij een rekenkundige operatie. Deze vlag wordt bestuurd door de opdrachten ADC, ASL, CLC, CMP, CPX, CPY, LSR, PLP, ROL, ROR, RTI, SBC, SEC.

(Bron: Programmeer cursus Assembly – P. Holmes/D. Bush)