Artikel top billede

(Foto: Computerworld)

Guide: Retro-programmering

Tag et smut tilbage til 1970’erne samt 1980’erne og genopdag den ikoniske 6502-cpu.

Af Kenneth Geisshirt, Alt om Data

Denne artikel er oprindeligt bragt på Alt om Data. Computerworld overtog i november 2022 Alt om Data. Du kan læse mere om overtagelsen her.

I dag er maskinkode/assembler-programmering et forsvundet håndværk, men for 30 år siden kunne mange teenagere skrive små programmer, som cpu’en umiddelbart forstod. Godt nok skrev de fleste i BASIC, men en gang imellem var maskinkode en nødvendighed.

6502 og varianten 6510 er en af de ikoniske cpu’er i computerhistorien. Computere som Atari 2600, Apple IIe, BBC Micro, VIC-20 og Commodore 64 har alle været drevet af medlemmer af denne familie.

Alt om DATA bragte tilbage i 1986 en serie af artikler om assembler-programmering af 6502. Ud over at introducere læserne for håndværket, pointerede skribenten Morten Christensen også, at 6502’eren var så udbredt, at de små kodestumper og funktioner med lethed kunne bruges på en lang række computere.

I denne artikel forsøger vi at genoplive det forsvundne håndværk og samtidig vise, hvordan du kan lege med 6502 uden at have gemt det på en computer i over 30 år!

Tekniske detaljer

Den oprindelige 6502 fra 1975 består af 3510-transistorer og har en clockfrekvens på 1 MHz. I sammenligning er der over 19 mia. transistorer ved 2,7 GHz i AMD’s seneste server-cpu fra 2017. Du skal tænke på, at denne udvikling er sket på under 45 år. Vi har ikke tidligere i civilisationens historie set en så hurtig udvikling af en teknologi.

Det er ikke kun i den højere clockfrekvens, en moderne cpu finder hastighed. En 6502 har ingen primær eller sekundær cache, og der er heller ingen spekulativ udførelse af instruktioner. De mange transistorer giver os en masse ekstra ydeevne, men vi har omkring årsskiftet 2017/18 set, at det også kan sætte vores it-sikkerhed under pres.

Indrømmet, maskinkode/assembler er ikke et programmeringssprog som Python, Java eller bare C. Du programmerer direkte i de instruktioner, som cpu’en kan udføre. Det stiller store krav til programmørernes dygtighed, men der er også en frihed i at have så begrænsede udtryksmåder.

Til tider er moderne programmeringssprog frustrerende at bruge, for det kan være svært – nærmest umuligt – at have en god intuition for, hvad der vil være den mest effektive løsning af et problem. 6502’eren er en 8-bit cpu, men har en 16-bit adressebus. De tre registre accumulator (A), X og Y er 8-bit, mens program counter (PC) er 16-bit.

Endvidere er der syv status flag (1 bit hver: sat eller ikke sat) samt en 8-bit stack pointer (SP). Med PC og en adressebus på 16-bit, er den maksimale hukommelse/adresserum på 64 KB (216 = 65536 bytes). I langt de fleste 6502-baserede computere blev dele af adresserummet brugt på skærm, ROM (kerne, BASIC, etc.) og I/O-enheder.

En Commodore 64 har f.eks. 38911 bytes fri hukommelse til brugeren. Det skal nævnes, at nogle computere benyttede bank switching. Det betyder, at programmøren kan aktivere forskellige dele af hukommelsen. Ikke det hele kan være aktivt samtidig, men det giver mulighed for at have mere end 64 KB til rådighed.

Som sagt er der tre registre. Accumulator bruges primært til at behandle data. Det vil sige, at du adderer eller trækker fra og dermed kan udføre beregninger. De to registre X og Y bruges til at indeksere med. De er med andre ord primært brugt som tælleren i løkker og indekserer ind i, hvad vi i almindelige programmeringssprog vil kalde arrays.

De syv status flag hejses (set) eller stryges (unset) alt efter, hvad resultatet af en udført instruktion er. Et eksempel er, når et tal lægges til accumulator, og resultatet giver anledning til en mente (resultatet er større, end hvad der kan gemmes i 8-bit). Flaget carry (engelsk for mente) sættes, og du kan bruge dette i næste instruktion.

Instruktionerne i 6502 er meget enkle. Der findes instruktioner, som kan indlæse en byte fra hukommelsen. For eksempel indlæser LDA $0400 (LoaD Accumulator) indholdet af adressen 1024 (0400 i 16-talssytemet er 1024 i 10-talssystemet – $ angiver, at det er 16-talssystemet) ind i accumulator.

Tilsvarende kan du skrive indholdet af accumulator til en adresse med instruktionen STA $0400 (STore Accumulator). De to andre registre har lignende instruktioner. Har du brug for at sætte værdien af et register til en bestemt værdi, kan du også det: LDA #$2A (2A er det samme som 42). Bemærk # foran tallet.

Du kan sammenligne et register med indholdet af en værdi eller indholdet af en adresse. For eksempel kan du sammenligne accumulator med 42 vha. instruktionen CMP #$42. Resultatet af denne instruktion kan du se i om de syv flag er sat.

Du kan teste om Z-flaget er sat og hoppe til adressen 1026 med instruktionen BEQ $0402. Hvis Z-flaget ikke er sat, går cpu’en videre til næste instruktion i hukommelsen. Har du brug for at foretage udregninger, kan du lægge til og trække fra med instruktionerne ADC og SBC.

6502’erens helt store styrke er i dens mange adresseringsmåder. Instruktionen LDA $0400 indlæser fra adressen 1024. Denne adresseringsmåde kaldes direkte, men der findes ikke mindre end 12 forskellige måder. En meget anvendt adresseringsmåde er at bruge enten register X eller Y sammen med en adresse.

Instruktionen LDA $0400,X vil først lægge indholdet af X til 1024 og så indlæse indholdet af denne adresse. Som du kan se, svarer det til at læse indholdet af det X’te element i et array, som begynder på adresse 1024.

I et programmeringssprog som C eller C++ vil det svare til accumulator = array[X]. I infoboks “Længden af en streng” kan du se, hvordan register X bruges som en tæller og indekserer ind i strengen. Infoboks “Længde af en streng – i C” er den tilsvarende implementering i C.

Macroassembler

Enhver cpu har maskinkode, og alle computere siden ruder konges tid har været af typen von Neumann. Det vil sige, at program og data gemmes i det samme lager. Lidt frisk kan du sige, at programmerne er data for cpu’en.

Eftersom du kan skrive en byte til en given adresse, kan du i princippet skrive dine programmer ind ved at gemme tal mellem 0 og 255 i de adresser, som programmet skal udgøre. Det er i praktisk al for besværligt og sandsynligheden for fejl er stor.

En macroassembler – eller bare assembler – er et program, som kan oversætte fra en tekstfil til de bytes, som repræsenterer programmet. Du kan i infoboksene “Længden af en streng” og “Små bogstaver til store” se to eksempler på programmer skrevet i 6502 maskinkode. Men som du kan se, er der defineret konstanter, f.eks. LOWERA.

Der er også mulighed for at bruge etiketter. Assembleren vil udregne adressen for etiketten igen således, at programmøren ikke selv skal gøre det. Konstanter og etiketter gør maskinkode-programmer mere læselige.

Til 6502 findes der mange assemblere. Oprindelig var der assemblere, som kørte på 6502, men i dag er det mere praktisk at bruge en assembler på din sædvanlige computer. Du oversætter programmet på din pc og overfører bagefter programmet til din 6502.

Der findes et utal af 6502-assemblere. De findes til både macOS, Linux, Windows og Android. Endvidere kan du finde web-baserede assemblere, så du slipper helt for at installere noget.

Retroprogrammering Emacs har et glimrende mode til redigering af maskinkode.

Jeg bruger macOS og Linux, og kommandolinjen er min primære arbejdsplads (sammen med min elskede editor Emacs). Derfor har jeg valgt at bruge Ophis, som understøtter både macOS, Linux og Windows. Til Windows er der en regular installer, mens til macOS og Linux kan du downloade en kommando-linje udgave.

Til at downloade kan du bruge kommandoen wget https://github.com/michaelcmartin/Ophis/releases/download/v2.1/Ophis-2.1.tgz og pakke filerne ud med tar xzf Ophis-2.1.tgz. I folderen Ophis-2.1 finder du nu kommandoen ophis samt dokumentation og eksempler.

Langt de fleste editorer har understøttelse af maskinkode i et eller andet omfang. Til den hippe Visual Studio Code findes de fleste udvidelser til udvikling i maskinkode, mens der til gode, gamle Emacs følger et mode med i grundinstallationen. 

Oversættelse af programmer med Ophis er let. Lad os antage, at du har gemt programmet fra infoboks “Længden af en streng” i filen strlen.asm. Kommandoen ./ophis strlen.asm -o strlen.bin vil oversætte programmet og gemme det oversatte program i filen strlen.bin.

Filen strlen.bin består af de bytes, som passer med de instruktioner, som findes i kildeteksten. strlen.bin er den binære udgave af programmet. I eksemplerne finder du en linje med .org. Tallet er den adresse, som programmet skal placeres i lageret.

Emulator

Jeg forventer ikke, at nutidens læsere har en computer baseret på en 6502 stående. Du kan købe en Atari 2600 eller en Commodore 64 brugt i Den Blå Avis for 6-700 kroner. Som med assembleren er det nu bare lettere at bruge en emulator på din vanlige computer. Igen ses det, at 6502 er populær for der findes en stor mængde af emulatorer.

Jeg har valgt at bruge emulatoren Symon. Grunden er, at den er implementeret i Java og kan derfor fint køre på macOS, Linux og Windows. Den virker meget enkel at bruge. Endvidere er emulatoren et open source-projekt, hvilket giver mig en mulighed for at se, hvordan sådan en emulator virker.

Lad os downloade emulatoren. Det gør du med kommandoen curl https://loomcom.com/symon/dist/symon-0.8.5.jar -O. Under macOS og Linux kan du starte emulatoren med kommandoen java -jar symon-0.8.5.jar.

Du har allerede set, hvordan et program oversættes fra en tekstrepræsentation til en binær fil. Denne binær fil kan du indlæse i emulatoren og klikke på knappen RUN. Klikker på derimod på STEP vil kun næste instruktion blive udført (register PC angiver, hvad der er næste instruktion).

Længden af en streng

.alias MAXCHR 80

.org $0300

start: LDX #0

igen: CMP string,X

BEQ fundet

INX

CPX #MAXCHR

BCC igen

CLC

fundet: RTS

string: .byte “Alt om Data”,0

Infoboks - Længde af en streng - i C

#define MAXCHAR 80

char *str = “Alt om Data”;

int X = 0;

while (str[X] != ’\0’ && X < MAXCHAR) { X++; }

Infoboks - Små bogstaver til store

.alias LOWERA 97

.alias LOWERZ 122

.alias DELTA 97 - 65

.org $0300

start: LDX #0 ; X = 0

loop:

LDA string,X ; ACC = string[X]

BEQ done ; if ACC = 0 goto done

CMP #LOWERA

BCC next

CMP #LOWERZ

BEQ flip

BCS next

flip: CLC

SBC #DELTA - 1

STA string,X

next:

INX

JMP loop

done:

RTS

string:

.byte “Alt om Data”,0

Til højre kan du se, hvad indholdet af de forskellige registre er samt se, om de syv flag er sat eller ej. Ved at bruge STEP kan du gå langsomt frem og finde dine programmeringsfejl.

Der findes to andre nyttige funktioner til fejlretning. Den første er Memory View. Her kan du bladre igennem computerens hukommelse og se, hvad indholdet er. Eftersom der maksimalt er 64 kB, tager det ikke lang tid at bladre hele hukommelsen igennem.

Du skal dog være opmærksom på, at ændringer i hukommelse ikke automatisk opdaterer vinduet. Det letteste er at skifte til næste side og tilbage igen.

Den anden nyttige funktion er Trace. Her vil emulatoren skrive alle de instruktioner, som er blevet udført. Endvidere vil de syv flag og registrenes indhold blive vist.

Skulle du få lyst til at programmere i BASIC i stedet for maskinkode, kan du finde en udgave af EHBasic specielt til Symon på adressen https://github.com/sethm/symon.

Det er værd at bemærke, at EHBasic er skrevet i maskinkode. Kildeteksten finder du samme sted, og du kan få en masse inspiration til, hvordan godt skrevet 6502-maskinkode kan se ud.

Retroprogrammering I menuen File kan du indlæse binære filer til din 6502-computers hukommelse.
Retroprogrammering Hovedvinduet viser skærmen samt alle 6502’s registere.
Retroprogrammering BASIC er det klassiske programmeringssprog til computerne fra 1980erne.

 

Afslutning

Jeg er sikker på, at gamle dage ikke var bedre dage. 6502 er en charmerende cpu, og det er muligt at programmere i andet end maskinkode. Jeg husker tilbage på mine tidligere computere med stor glæde, men tiden var en anden.

6502 er en vigtig historisk milepæl. For det første sad den i en række vigtige computere og udgjorde sammen med Z80 basis for 1980’ernes revolution med hjemmecomputere.

Acorns udviklere var så dybt betaget af 6502, at de designede Acorn Risc Machine (ARM) på basis af 6502. I dag er ARM-serien en af de mest udbredte cpu’er i verden, idet du finder en ARM i stort set alle smartphones, tablets og i mange routere og NAS-bokse.

Faktisk bliver der stadig produceret 6502-lignende cpu’er. Firmaet Western Design Center sælger stadig i dag cpuen som microcontroller til Internet of Things. Endvidere har jeg set, at Elextra (tidligere Brinch) og Cypax har den i deres katalog.

Eftersom 6502 er meget enklere end en cpu i 2018, er den også lettere at forstå. Med andre ord, hvis du gerne vil forstå det grundlæggende i computere, er 6502 et godt sted at begynde.

Retroprogrammering Indholdet af hukommelse før og efter at programmet i Infoboks “Små bogstaver til store” er blevet kørt.
Tracing giver dig mulighed for at se, hvad et program har udført af instruktioner.

Få mere at vide

Fællesskab. Morten Christensen. Alt om DATA nr. 3, 1986.

Rundt om 6502’eren. Morten Christensen. Alt om DATA nr. 5, 6 og 12, 1986.

Mikroprocessorer: arkitektur, instruktioner, adresseringsmetoder. Jens Grysbjerg. Teknisk Forlag, 1986.

Stort website med alt om 6502: http://6502.org/

Kort og præcis gennemgang af instruktioner og adresseringsmåder: https://en.wikibooks.org/wiki/6502_Assembly

Dansk 8-bit arkiv: http://dansk8bit.dk/

6502-baseret computer med 8 chips: http://searle.hostei.com/grant/6502/Simple6502.html

Chris Bird bygger en 6502-computer (i 6 afsnit): https://www.youtube.com/watch?v=SDTCEztQZsI

6502 uden brug af chips: https://monster6502.com

Nutidig udgave: https://michaelcmartin.github.io/Ophis/">http://wdc65xx.com/boards/w65c134sxb-engineering-development-system/ Ophis assembler: https://michaelcmartin.github.io/Ophis/

6502-emulatoren Symon: https://loomcom.com/symon/