Beiträge von M. J. im Thema „Assembler 65xx - Vergleich mit heute“

    Das hatte Intel sogar gemacht, war aber ein Griff ins Klo: Bitte melde dich an, um diesen Link zu sehen.

    Das ist noch höflich ausgedrückt.

    Zitat von Wikipedia

    The iAPX 432 programming model was a stack machine with no visible general purpose registers. [..]
    The iAPX 432 instruction had variable length, and peculiarly were bit rather than byte aligned, with an instruction taking between 6 and 321 bits. [..]
    The iAPX 432 was "designed to be programmed entirely in high-level languages", with Ada being primary and it supported object-oriented programming and garbage collection directly in hardware and microcode. [..]
    Although engineers saw ways to improve a next generation design, the iAPX 432 Capability architecture had now started to be regarded more as an implementation overhead rather than as the simplifying support it was intended to be. [..]
    usw.

    Mit anderen Worten: Dieses Design war das totale Gegenstück vom ARM und damit, wie beklagt, ein typischer Intel.

    mc71

    Du fragst ernsthaft nach, ob Du in einem Diskussions-Forum diskutieren darfst?

    Naja, ich bin halt neu hier und möchte gerne vermeiden, unabsichtlich wegen schlecht formulierter Äußerungen jemanden auf die Füße zu treten. Das ist überhaupt nicht mein Ding, denn im Grunde genommen bin ich ein zurückhaltender, langweiliger Typ und mag es daher nicht, irgendwie besserwisserisch oder angeberisch zu klingen. Zumal ich viel mehr sehr froh darüber bin, wenn andere Leute von ihren Erfahrungen und Meinungen berichten. (Soviel weiß ich nämlich gar nicht.)

    Ja, schon. Und unter heutigen Aspekten von Speicherschutz, virtuellem Speicher etc. ist ein getrennter Satz Adreßregister gar nicht mal so verkehrt- ich meine die Architektur kam sogar mal aus dieser Überlegung heraus zustande?

    Ich bin nicht sicher, ob das wirklich so dahinter stand. Es könnte auch an anderen Gründen gelegen haben. Trivial: Für die Befehlscodierung waren nicht genügend freie Bits vorhanden (3 vs. 4 Bits). Auch besteht ein Unterschied zwischen Daten- und Adreßregister darin, daß Operationen auf Adreßregistern bewußt nicht die Flags setzen, um parallele Operationen auf den Datenregistern nicht zu beeinflussen. Und solange man den Speicher auch direkt adressieren kann (ohne dazwischengeschaltetes Adreßregister), läßt sich virtueller Speicher etc auch damit allein nicht realisieren.

    Man behauptet immer wieder, der RCA COSMAC habe keienn Call-Befehl

    Sehr interessant. Den kannte ich noch gar nicht. War noch vor meiner Zeit. Ich muß gestehen, daß ich ein vom Luxus der 80er verwöhntes und verzogenes Kind bin. Übrigens hat der ARM (weil RISC) eigentlich auch keinen Call-Befehl. BL schreibt ja nur den PC ins Linkregister und ersetzt ihn dann durch einen neuen Wert. Das Pushen auf den Stack muß man selbst übernehmen.

    Nach meiner Erfahrung ist das nur selten ein Problem (und vor allem dann, wenn der Programmierer auf Z80 gelernt hat), denn oftmals braucht man eh einen Index auf den Pointer.

    Meiner Erfahrung nach ist das andauernd ein Problem. Bitte melde dich an, um dieses Bild zu sehen. Wenn ich z. B. Graphik programmiere, brauche ich immer mehr Pointer/Zähler als vorhanden. Oftmals sehen dann Programme so aus, daß man X auf 0 setzt, um mehrere Pointer anzusprechen, die dann aber händisch mit 'INC zp' inkrementiert werden müssen.

    Ja, die Implementierung ist suboptimal. Aber denk Dir einen Cache dazwischen, setze vor jede Zeropageadresse ein 'R' und Du hast die Semantik eines 256-Byte-Registerfiles.

    Naja, denken kann ich mir eine ganze Menge. Bitte melde dich an, um dieses Bild zu sehen. Die Frage war aber, was unterscheidet einen 65xx von heutigen (oder genauer: fortschrittlicheren) Prozessoren, und der 6502 hatte nun einmal weder einen Cache noch ausreichend Befehle, um die Zeropage wie ein Register ansprechen zu können. Der 68000 konnte da bei seiner 'Zeropage' immerhin mit Konstanten vergleichen, subtrahieren, addieren...

    Was heißt hier 'auch'? der Z80 ist mein absoluter Lieblingsprozessor, sowohl software- wie hardwaretechnisch.

    Ups, tschuldigung, nehme alles zurück. Es ist nur so, daß ich vor paar Jahren einen Z80-Emulator geschrieben habe für die Z80-Karte des AppleII. Dabei mußte ich mich dann auch durch einigen Code von CP/M durchwühlen. Sagen wir es mal so: Ich war nicht beeindruckt.

    ich habe eine gewisse Vorliebe für Prozessoren, die irgendwas 'anders' machen als üblich.

    Verzeih, aber das klingt sehr interessant. Dürfte ich fragen, woher Du die kennst bzw. ob Du die alle selber programmiert hast?

    Zeig mir _irgendeinen_ 8-bitter mit orthogonalem Befehlssatz.

    Der 8086 war aber nun mal kein 8-Bitter mehr. Und deswegen hätte man wirklich mehr erwarten können (s. u.).

    Diese Quellcode-Beinahe-.Kompatibilität war aber ein wichtiges Verkaufsargument, weil man mit geringem Aufwand ein leistungsfähigeres System zusammenschustern konnte.

    Sehr schön zusammengefaßt. Bitte melde dich an, um dieses Bild zu sehen. Ein typischer Intel eben: Profit geht vor Design. (Man merkt, ich bin kein Geschäftsmann. Bitte melde dich an, um dieses Bild zu sehen.) Nach diesem Prinzip arbeiten die bis heute. Mein Verdacht: Wäre AMD nicht mit x64 vorgeprescht, hätten wir heute noch kein 64-Bit Linux/Windows (auf PCs). Ich weiß nicht, wieviel Erfahrung Du mit der Programmierung dieser Kiste hast, aber ich für meinen Teil hätte mehr als einmal in die Tastatur beißen können vor lauter Wut und Frust. Macht einfach keinen Spaß. Der 8086 ist IMHO im Vergleich zum 68000 von der Programmierseite her schlicht nicht durchdacht, sondern eine einzige Katastrophe: "String"-Befehle, die in 99% der Fälle für alles andere als Strings verwendet werden. Register (und schlimmer noch: Befehle), die an Segmente gekoppelt sind und mittels Override-Befehl umgepolt werden müssen usw. usf. Ich bin froh, daß ich das hinter mir habe.
    Sinnvoller (wenn vielleicht auch nicht im geschäftlichen Sinne) wäre es gewesen, wenn sich Intel vom alten Design getrennt und etwas neues aufgebaut hätte. Meine Heldin ist hier Sophie Wilson. Die hat genau das gemacht: Sch..ß auf die alten Prozessoren. Hinsetzen und was neues entwerfen. Und heraus kam der ARM. Das nenne ich genial.

    für XLAT verweise ich mal auf die RISC-Diskussion. Natürlich geht das eleganter und orthogonaler- aber wenn genau dieser Befehl von einem Compiler gebraucht wird, mit genau diesen Parametern? Warum nicht?

    XLAT ist der Befehl, den ich nie, nie, nie verwendet habe, weil er völlig überflüssig ist. Ich habe mir zudem schon einige Male einen vom Compiler erzeugten Code angesehen, und nie auch nur ein XLAT darin gefunden. Bitte melde dich an, um dieses Bild zu sehen. Und ehrlich gesagt, kann ich mir auch keinen Compiler vorstellen, der diesen Befehl verwenden möchte. Soviel ich weiß ist es eher so, daß gerade Compilerbauer über solche spezialisierten Befehle gar nicht erfreut sind, weil das die Registerbelegung total durcheinanderbringt. (Gleiches gilt auch für MUL, DIV, STOS etc.) Compiler(bauer) lieben orthogonale Befehlssätze.

    Eine Sache hatte ich übrigens vergessen, die den 6502 von neueren Prozessoren unterscheidet: PC-relative Adressierung. Auf dem 68000 kann ich problemlos (ohne einen speziellen Lader) relokatible Programme erzeugen. Auf dem 6502 geht sowas nicht. Die paar Sprungbefehle reichen dafür nicht. Theoretisch könnte man einen Lader entwickeln, der alle Adreßbezüge beim Laden umrechnet. Das ist aber sehr umständlich und wird daher auch bis auf sehr, sehr wenige Ausnahmen (DOS 3.3 Masterdisk) nicht praktiziert. So hat z. B. UCSD-Pascal dies auch nur mit Einschränkungen implementiert, um Bibliotheken wie Turtlegraphics oder Transcend dynamisch nachladen zu können. Aber dabei war es z. B. nicht möglich folgendes zu schreiben:

    Code
    LDA	#<label
    	STA	zp
    	LDA	#>label
    	STA	zp + 1
    	...
    label:

    weil Assembler und Lader nicht in der Lage waren, die 8-Bit Konstanten als Label zu identifizieren und anzupassen.

    mc71
    Danke für die Antwort. Bitte gestatte mir jedoch eine Antwort auf Deine Antwort.

    Doch, das geht sehr wohl- entweder durch Push-Call-Sequenzen oder JMP(xxxx). Ist auch nicht schlimmer als vieles andere, das beim 6502 in mehrere Operationen zerfällt- was ihm ja den Ruf einer gewissen RISC-Ähnlichkeit eingebracht hat.

    Die Betonung lag auf "keinen Befehl zum indirekten Aufruf". Natürlich kann man mit dem 6502 alles mögliche programmieren. Schließlich ist der 6502 Turing-vollständig. Magnetic Scrolls emulierten bekanntlich für ihre Adventures eine Untermenge des Befehlssatzes vom 68000. Und ausreichend viele Disketten vorausgesetzt könnte der 6502 auch Linux oder Windows 64-Bit emulieren. Aber das ist nicht der Punkt. Der 6502 hat nun mal den Nachteil, daß der Mehraufwand von JSR abs zum indirekten Aufruf viel höher ausfällt als beim 68000 oder x86. Außerdem muß man bei Push-Call aufpassen, daß man die Adresse anpaßt (-1 wegen RTS), und JMP (xxxx) ist, wie Du sicherlich weist, wegen eines Bugs beim Original 6502 mit Vorsicht zu genießen.

    Du hast bis zu 128 Stacks mit vollen 65K Adreßraum... Daten-Stacks gehen beim 6502 über Zeropage-Pointer.


    Es gibt aber für die Zeropage-Pointer keine Befehlssunterstützung z. B. zum vollständigen 16-Bit Inkrementieren und Dekrementieren oder direkten Schreiben von Werten. Alles muß stets über den Akkumulator laufen. Auch benötigt man auf dem Original 6502 immer das X- oder Y-Register für die Adressierung, wodurch dieses Register für anderweitige Aufgaben verloren geht. Der 65c02 hat nicht umsonst die Adressierungsart (zp) hinzubekommen. Beim 6502 braucht man (für einen 16 Bit-Stack) z. B. folgende Befehlsfolge:

    Code
    LDY	#0
    	LDA	(sp), y
    	INC	sp
    	BNE	skip
    	INC	sp + 1
    skip:


    Verglichen mit

    Code
    MOVE.L	(sp)+, d0


    ist das halt weit, weit mehr.

    156 8-Bit-Regtister sind wenig? Aber hallo...!


    Die Zeropageadressen zählen nicht wirklich als Prozessorregister, auch wenn man sie sich auf einer abstrakten Ebene so vorstellen kann. Das Ansprechen benötigt generell einen Taktzyklus mehr, da der Wert erst aus dem Speicher in den Prozessor geladen werden muß. Will man z. B. Vergleichsoperationen durchführen, muß man den Wert erst in ein (freies) Register laden und dann vergleichen. Kostenpunkt: mindestens 5 Taktzyklen. Wenn man so denkt, wie Du es formulierst, dann hätte der 68000 65536 Register mehr, weil er auch die gekürzte Adressierungsart abs.W kennt.

    ich bin irgendwie auch nur damit beschäftigt, die in den Akku zu schaufeln, wen ich irgendwas sinnvolles damit tun will. Beim i86 wird das tendenziell etwas besser, aber leider auch langsamer. Und der 68000 nervt mit seiner strikten Trennung zwischen Adress- und Datenregistern.


    Yep, ich muß gestehen, daß ich den 6502 auch stets dem Z80 vorgezogen habe. Der alte 8086 war m. M. n. ein typischer Intel: Kein orthogonaler Befehlssatz, statt dessen spezialisierte Register, überflüssige Befehle (XLAT), eine umständliche Speicheradressierung (Segmente!), kurz: Schrott. Was die Register des 68000 anbelangt, so fiel es mir damals (geklautes TM) nicht so auf, als ich vom 6502 rüberwechselte. Da war man froh, daß man überhaupt so viele Register hatte. Nachdem ich mich aber mit diversen RISC-Prozessoren beschäftigt habe, fällt es mir auch schwerer, zum 68000 zurückzukehren, obwohl ich ihn immer noch x86 vorziehen würde.

    Mir fallen da spontan drei Unterschiede ein zwischen 6502 und x86 oder auch 68000, die einem das Leben als Programmierer auf letzteren um einiges erleichtern:

    1.) Der 6502 kennt keinen Befehl zum indirekten Aufruf einer Funktion bzw. Methode.
    Normalerweise programmiere ich Objekte in x86 so, daß der Objektzeiger in ESI vorliegt, um dann mittels

    Code
    CALL	[esi + methoden_offset]


    eine (dynamische) Methode aufzurufen. Beim 68000 kennt man ein ähnliches Verfahren, z. B. vom Amiga:

    Code
    JSR	methoden_offset(Ax)


    worauf dann allerdings noch ein JMP abs.L folgt zum eigentlichen Methodenanfang.
    Um dies beim 6502 zu simulieren, benötigt man eine Reihe von Befehlen:

    Code
    LDY	#methoden_offset
    	LDA	(op), y
    	STA	patch + 1
    	INY
    	LDA	(op), y
    	STA	patch + 2
    patch:	JSR	$ffff


    Kommt dies häufig im Programm vor, so kann man die Kernroutine auf die Zeropage auslagern:

    Code
    call:	LDA	(op), y
    	STA	patch + 1	; spart einen Takt durch Zeropageadressierung
    	INY
    	LDA	(op), y
    	STA	patch + 2	; spart einen Takt durch Zeropageadressierung
    patch:	JMP	$ffff		; JMP abs umgeht außerdem den Bug in JMP (abs)


    Der Aufruf erfolgt dann mit

    Code
    LDY	#methoden_offset
    	JSR	call


    Jedoch verbraucht dieser Aufruf im Verhältnis zu den großen Brüdern sehr viel mehr Taktzyklen. Theoretisch ist eine objektorientierte Programmierung auf dem 6502 also zwar möglich, aber im Regelfall zu umständlich und zu langsam.

    2.) Der Stack beim 6502 ist relativ klein bemessen. 256 Bytes ist wahrlich nicht viel. Spätestens bei einer Rekursionstiefe von mehr als 128 stürzt das Programm ab. Nun kommen die meisten Programme sicherlich auch ohne große Rekursionen aus. Was aber wirklich stört, ist, daß der 6502-Stack nicht geeignet ist, die von Hochsprachen wie C oder Pascal (und entsprechend alle Nachfolger) verlangten lokalen und temporären Daten dort abzulegen. Bei x86 oder 68000 ist dies kein Problem. Der Zugriff gestaltet sich zudem recht einfach:

    Code
    MOV	eax, [esp + lokale_variable]

    oder

    Code
    MOVE.L	lokale_variable(a7), d0


    Wollte man auf einem 6502 dies nachahmen, müßte man zuerst die Werte mittels PHA auf den Stack bringen, dann mit TSX den Stackzeiger nach X laden und die Adressierungsart abs, x verwenden, um auf die einzelnen Stackelemente zugreifen zu können. Da der Stack aber zu beschränkt ist für eine größere Anzahl an Daten, ist diese Methode nicht zu empfehlen. 'Pascal auf dem C64', UCSD-Pascal und andere Hochsprachenimplementierungen verwalten daher einen eigenen getrennten 16-Bit Stack, auf dem sie ihre Daten speichern können.

    3.) Zuletzt kann auch die Anzahl der Register von Bedeutung sein, wenn es z. B. um die Parameterübergabe geht. Drei Register sind wirklich nicht viel hierfür. Beim x86 werden die Parameter (standardmäßig) auf dem Stack übergeben. Dabei ist genau definiert, welche Register von einer Funktion überschrieben werden dürfen und welche nicht. (Schreibt man direkt in Assembler, hat man natürlich die Freiheit, die Register zu verwenden, wie man will.) Das Betriebssystem des Amigas übergibt im Regelfall die (ersten vier) Parameter in den Registern d0/d1 und a0/a1, welche gleichzeitig als Scratchregister fungieren. Die meisten in 6502-Assembler geschriebenen Programme arbeiten jedoch statt dessen mit globalen Werten, auch bei den Parametern. Es ist nicht untypisch folgendes in einem Programm zu sehen:


    Um den Overhead bei der Parameterübergabe zu sparen, sind die Programmierer dann bei (zumeist) konstanten Parametern dazu übergegangen, diese direkt an den JSR-Aufruf hinten dranzukleben, so daß der PC als Zeiger auf die Daten dient. Klassisches Beispiel:

    Code
    JSR	schreibe_string
    	ASC	'Hier ist der String'
    	HEX	00	; Endmarker des Strings
    	...		; Hier geht das Programm nach Aufruf von schreibe_string weiter


    Diese Aufruftechnik ist meines Wissens auf anderen Prozessoren nicht so geläufig. (Wer mehr weiß, bitte melden.)

    Typisch für viele Programme auf dem 6502 ist auch, auf der Zeropage Variablen (wie aus dem obigen Beispiel) zu deklarieren, die einer ganz bestimmten Semantik unterliegen, z. B. 'xcoo' oder 'taste', auch wenn diese nur temporär gebraucht werden. Beim x86 oder 68000 werden solche temporären Variablen in den Registern gelagert, so daß eine explizite Definition als Variable im Speicher weniger häufig ist. Anders gesagt: Die Speichernutzung ist (besonders bei alten Programmen) auf dem C64 oder AppleII weniger optimal.

    Zusammengefaßt:
    Der 6502 programmiert sich schon anders als der 68000 oder x86 oder ARM. Bei letzteren ist die Umsetzung von Hochsprache auf Prozessor viel näher am Originaltext. Beim 6502 muß man manchmal um die Ecke denken und auch längere Wege in Kauf nehmen, um das Ziel zu erreichen. Oder man verzichtet von vornherein auf bestimmte Konstrukte und begnügt sich mit einer einfachen prozeduralen Programmierung. Meiner Meinung nach ist 6502-Assembler eine Sprache für sich, und man muß in 6502-Assembler denken, um das Ziel optimal zu erreichen. Bei den anderen Prozessoren kann man sich viel mehr an den Hochsprachen orientieren.