Hallo Besucher, der Thread wurde 180k mal aufgerufen und enthält 1853 Antworten

letzter Beitrag von Retrofan am

Neuer Retro-Computer im 8-Bit Style

  • Das wäre vielleicht auch eine Idee für eine virtuelle CPU: eine 6502-ähnliche CPU, nur mit 24-Bit Adressbus. Überall wo die 6502 CPU 2 Bytes für die Adressierung benutzt, benutzt diese CPU 3 Bytes.Also:
    LDA $ABCDEF statt LDA $ABCD

    Schade wäre dann:


    - kein irgendwo geschriebenes Progrann würde auf diesem Prozessor funktionieren weil alle 6502 basierenden Assembler auf maximal 2 Byte Adressierung laufen.


    - aus dem oben genannten Grund hast Du nichtmal einen Assembler um selbst Programme zu erstellen.

  • Erstmal 20bit aka 1MB dürfte wohl etwas einfacher sein.

    Warum vier Bit verschwenden? Man kann sowieso nur ganze Bytes aus dem Speicher holen.


    - kein irgendwo geschriebenes Progrann würde auf diesem Prozessor funktionieren weil alle 6502 basierenden Assembler auf maximal 2 Byte Adressierung laufen.


    - aus dem oben genannten Grund hast Du nichtmal einen Assembler um selbst Programme zu erstellen.

    Das stimmt. Richtig kompatibel zum 6502 wäre dieser Prozessor nicht. Da aber die Struktur beider Prozessoren übereinstimmt, wäre es vielleicht möglich, einen vorhandenen Assembler anzupassen. Ich denke mal, dass das weniger Aufwand wäre, als einen Assembler komplett neu zu schreiben.
    Aber das Ganze war mehr eine fixe Idee und nicht weiter durchdacht.

  • Grundsätzlich stimme ich diesem Ansatz voll zu :dafuer: , habe jedoch Bedenken bei der Umsetzung im Befehlssatz. Wie bereits erwähnt kommen negative Offsets so gut wie nie vor, wohingegen man positive Offsets (z. B. für lokale Variablen auf dem Stack oder Objektattribute) häufig benötigt.

    Ich hatte auch darüber nachgedacht alle Offsets nur positiv zu machen ausser lea (denn sonst könnte man x,y,s,pc nicht reduzieren)


    Aber eines meiner hauptziele ist es eine CPU zu erdenken die auch damals™ jemand hätte bauen können. Wenn man z.B. einen 6502 baut der die zeropage direkt in der cpu speichert würden all die operationen VIEL schneller gehen, aber niemand hätte so etwas baue können/wollen. Darum halte ich die Befehle und Adressierungs-Modi möglichst einfach und einfach zum dekodieren.


    [...], die häufiger gebraucht werden, z. B. "INX". Ein "LEAX X, 1" dürfte mit seinen 3 Speicherzugriffen für eine Zählschleife zu viele Takte verbrauchen, zumal unklar ist, ob bei diesem Befehl die Flaggen gesetzt werden. Sollte dies (wie beim 68000) nicht der Fall sein, so müßte anschließend noch ein Test auf 0 erfolgen.


    Na leax x,1 sind nur 2 bytes (opcode + 7bit offset) und nicht 3. Aber wenn du was kopierst machst du eh' "lda x+; sta y+", zum zählen kannst du perfekt b nutzen. Und was die flags betrifft würde ich die sehr ähnlich zum 6502 machen: bei rechen- und ladeoperationen. (aber nicht speichern, jmp und so) (der 6809 z.B. setzt die immer wenn ein register ausser PC betroffen ist, also auch beim sta; die AVR's ändern die flags beim laden/speichern nicht)

  • Darum halte ich die Befehle und Adressierungs-Modi möglichst einfach und einfach zum dekodieren.

    Bist Du Dir sicher, daß das Setzen der oberen Bits auf das Vorzeichenbit des Quelloperanden einfacher ist als das Setzen der oberen Bits auf 0? :gruebel

    leax x,1

    Sorry, habe den Befehlssatz noch nicht verinnerlicht. Zwei Bytes halte ich trotzdem immer noch für viel. Schnelles In- und Dekrementieren wäre halt mein persönlicher Wunsch. Nebenbei: Gibt es eine Möglichkeit die Befehle "leax x, 1" mit 8 Bit Ergänzung und "leax x, 1" mit 16 Bit-Ergänzung syntaktisch zu unterscheiden? Ansonsten könnte es beim Assemblieren (mit einem 2-Pass-Assembler) u. U. Probleme geben, wenn eine Konstante nicht vorab definiert wurde, so daß sich der Assembler im ersten Pass in der Befehlsbreite verschätzt und infolgedessen falsche Adressen berechnet.

    zum zählen kannst du perfekt b nutzen

    b ist doch nur 8 Bit? Abgesehen davon sind Kopieroperationen nur ein eher seltener Sonderfall und für größere Programme nicht wirklich aussagekräftig. :/


    Verzeih bitte die Frage, aber hattest Du den Befehlssatz bereits in einem Emulator ausprobiert?

    Das wäre vielleicht auch eine Idee für eine virtuelle CPU: eine 6502-ähnliche CPU, nur mit 24-Bit Adressbus. Überall wo die 6502 CPU 2 Bytes für die Adressierung benutzt, benutzt diese CPU 3 Bytes.

    Das könnte natürlich gehen, schließlich verfügt der 65816 über genau diese Möglichkeit. Knifflig wird es beim Programmzähler. Entweder gestaltet man diesen ebenfalls 24 Bit breit, was z. B. dazu führt, daß die Rücksprungadresse auf dem Stack länger wird, was Zeit kostet. Oder man verwendet sowas wie NEAR- und FAR-Calls (wie beim 65816), was andere Programmierschwierigkeiten mit sich bringt. Auf jeden Fall dürften Programme mit dem 3-Byte-Schema ein wenig länger und langsamer werden, doch um wieviel ist schwer zu sagen. :nixwiss:

    aus dem oben genannten Grund hast Du nichtmal einen Assembler um selbst Programme zu erstellen

    Generell gilt für alle Entwürfe eines neuen Rechners, daß das Schreiben eines kleinen Assemblers/Disassemblers und Emulators auf heutigen PCs mit einer Hochsprache wie C oder Java oder ... kein echtes Problem mehr darstellt. Sowas schreibt man an ein oder zwei freien Wochenenden. Für den Anfang würde auch ein befehlsgenauer Emulator ausreichen und eine Videoemulation, die nur jeweils ein ganzes Bild berechnet (also ohne Rasterinterrupts). Das ganze Emulatorsystem kann auch ruhig in sich langsam sein. Das interessiert auf heutigen PCs ohnehin nicht mehr. Eine simulierte Geschwindigkeit von 1 Mhz ist auf jeden Fall machbar, was für reale Programme schon ausreicht. Der einzig wichtige Punkt ist, daß jemand wie alx sich mal traut zu sagen "Hey Leute, ich habe hier einen Befehlssatz. Der sieht so und so aus. Was meint Ihr dazu? Sollte man das nicht mal austesten?" Und dann kann man loslegen. Und wenn man später feststellt, daß die Definition irgendwo Schwachpunkte hat und es anders irgendwie besser ginge, dann hat man was dazugelernt und macht es halt anders.
    Kurz gesagt: Es gibt nichts, was einen (bzw. eine Gruppe von Interessierten) davon abhalten würde, sowas zu tun. Macht doch mal einfach.

  • Wie bereits erwähnt kommen negative Offsets so gut wie nie vor,


    Negative Offsets sind Standard für Stackvariablen.


    Zitat

    Daher befürchte ich, daß die Option auf negative Zahlenwerte letztendlich eine Verschwendung darstellt. Desweiteren dürfte die doppelte Indirektion kaum Verwendung finden.


    Wie rechnet man dann? Ohne negative Zahlen würde das schwierig werden.


    Zitat

    Die Benutzung von Zeigern auf Zeigern (, wobei letztere umständlich im Speicher liegen,) ist hingegen in der Praxis kaum erforderlich und auch sehr langsam.


    Der Gebrauch von Sprungtabellen ist durchaus üblich. Mit Zeigern kann man Programme deutlich beschleunigen.


    Das könnte natürlich gehen, schließlich verfügt der 65816 über genau diese Möglichkeit. Knifflig wird es beim Programmzähler. Entweder gestaltet man diesen ebenfalls 24 Bit breit, was z. B. dazu führt, daß die Rücksprungadresse auf dem Stack länger wird, was Zeit kostet.

    Wenn der Rechner 3 Bytes (oder mehr) für Adressen zur Verfügung hat, dann müssten die Register ebenfalls breiter sein. Wenn man einen Zeiger berechnen will und man hat nur 8-Bit register, dann dürfte das schon ein wenig kompliziert werden. Dann hätte man ein High-, Low- und Middlebyte... Jedes Incrementieren eines Zeigers müsste dann die Adresse über drei Register schieben.

  • Negative Offsets sind Standard für Stackvariablen.

    Nein, es gibt zwei Verfahren, Stackvariablen zu handhaben:
    1.) Der aktuelle Stackpointer wird in ein gesondertes Register kopiert (z. B. BP bzw. EBP beim x86). Daraufhin wird der Stackpointer verringert, um Platz zu schaffen auf dem Stack. Nachfolgend werden die Variablen angesprochen über das Spezialregister und einem negativen Offset (z. B. [EBP - 4]). Am Ende des Unterprogramms wird der Inhalt des Spezialregisters in den Stackpointer kopiert. Damit wird der Speicherbereich auf dem Stack wieder freigegeben.
    2.) Der aktuelle Stackpointer wird verringert, um Platz zu schaffen auf dem Stack. Nachfolgend werden die Variablen angesprochen über den Stapelzeiger und einem positiven Offset (z. B. [ESP + 4]). Am Ende des Unterprogramms wird der Stackpointer erhöht, um den Speicherbereich auf dem Stack wieder freizugeben.


    Voraussetzung für die 1. Methode ist das Vorhandensein eines zusätzlichen Adreßregisters, um den Zeiger auf die lokalen Variablen aufzunehmen. Beim x86 war hierfür das Register BP (Base pointer) vorgesehen. Eine Compileroption beim gcc sorgt jedoch dafür, daß die 2. Methode angewendet wird, was das Register EBP frei läßt für andere Aufgaben. Beim 68000 gibt es den Befehl "LINK #<negativer Offset>, Ax", welcher die erste Methode ausführt. Dies ist möglich, weil der 68000 neben dem Stackzeiger über weitere 7 Adreßregister verfügt.
    Der von alx vorgeschlagene Prozessor besitzt jedoch nur 2 Adreßregister X und Y. Es wäre sehr ungünstig, eines von diesen als Adreßzeiger für lokale Variablen zu verwenden. Hier bietet sich vielmehr die zweite Methode an, d. h. lokale Variablen werden angesprochen über S und einem positiven Offset. Oder anders ausgedrückt: Für diesen Prozessortyp macht die erste Methode mit negativen Offsets keinen Sinn.

    Wie rechnet man dann? Ohne negative Zahlen würde das schwierig werden.

    Es ging um negative Adress-Offsets, nicht um negative Konstanten.

    Der Gebrauch von Sprungtabellen ist durchaus üblich. Mit Zeigern kann man Programme deutlich beschleunigen

    Weder Sprungtabellen noch Zeiger waren gemeint, sondern Zeiger auf Zeiger. Diese Adressierungsart ist in Prozessorbefehlssätzen nicht üblich. Weder 6502 noch Z80, 68000, x86, ARM... beherrschen dies aus oben genannten Grund.

    Jedes Incrementieren eines Zeigers müsste dann die Adresse über drei Register schieben.

    So kompliziert wäre das nicht, nur ein wenig länger:

    Code
    1. inc zp
    2. bne label
    3. inc zp + 1
    4. bne label
    5. inc zp + 2
    6. label:

    Und abwegig ist es auch nicht. Der 65816 arbeitet mit 24 Bits, aber hat die unschöne Eigenschaft, den Speicherbereich in 64kb-Banks zu teilen, so daß man für Sprünge von Bank zu Bank gesonderte Befehle braucht und eine Unterscheidung in NEAR und FAR-Calls.

  • Nein, es gibt zwei Verfahren, Stackvariablen zu handhaben:1.) Der aktuelle Stackpointer wird in ein gesondertes Register kopiert (z. B. BP bzw. EBP beim x86). Daraufhin wird der Stackpointer verringert, um Platz zu schaffen auf dem Stack. Nachfolgend werden die Variablen angesprochen über das Spezialregister und einem negativen Offset (z. B. [EBP - 4]). Am Ende des Unterprogramms wird der Inhalt des Spezialregisters in den Stackpointer kopiert. Damit wird der Speicherbereich auf dem Stack wieder freigegeben.


    Das gilt für lokale Variablen. Üblicherweise werden locale Variablen über EBP mit positivem Offset referenziert, während Funkitionsparameter über ESP mit negativen Offset referenziert werden. Auf 64 Bit System, werden die ersten via Parameter per Register übergeben. Alles was darüber geht ist dann am Stack. Bei 32Bit wird nur der Stack benutzt. Wobei es aber u.A. auch die fastcall Konvention gibt, bei der dann auch Register benutzt werden, aber das ist dann extra anzugeben.


    Zitat

    Und abwegig ist es auch nicht. Der 65816 arbeitet mit 24 Bits, aber hat die unschöne Eigenschaft, den Speicherbereich in 64kb-Banks zu teilen, so daß man für Sprünge von Bank zu Bank gesonderte Befehle braucht und eine Unterscheidung in NEAR und FAR-Calls.


    Kompliziert ist das nicht, aber hässlich. :) Die Alternative wäre dass man ein Segmentregister benutzen könnte/müsste (wie beim x86 früher). Auch nicht sonderlich schön. :)


    Was passiert eigentlich mit den "überzähligen" Adress Bits. Ist dann 0x00ffffff die gleiche Adresse wie 0x80ffffff weil die obersten Bits ignoriert werden, oder gibts einen Adressfault?

  • Segmentregister

    :gahh::wand:honk::tischkante::smoke: ( :D )


    Was passiert eigentlich mit den "überzähligen" Adress Bits. Ist dann 0x00ffffff die gleiche Adresse wie 0x80ffffff weil die obersten Bits ignoriert werden, oder gibts einen Adressfault?

    Welchen Prozessor meinst Du? Den 65816? Der hat nur 24 Adreßleitungen und daher gar keine andere Wahl, als alles darüber zu ignorieren. Adressfaults sind bei einem 8-Bit-Prozessor für gewöhnlich auch nicht anzutreffen. Dafür ist der Adreßraum viel zu klein, und wenn dann ein Programm ins Nirvana schreibt, interessiert es auch niemanden. ^^

  • Das ist z.T. das selbe: wenn du nicht "leas s, -5" schreiben kannst weil es keine negativen offsets gibt, wie willst du dann 5 bytes auf den stack pointer reservieren? (Oder X/Y decrementieren)?

    Entweder man unterscheidet zwischen LEA und den anderen Befehlen (nur bei LEA ist der Wert negativ), oder man benutzt für Operationen am Stapelzeiger bzw. fürs In-/Dekrementieren der Register eigene Befehle, oder man benötigt fürs Subtrahieren halt einen 3-Byte-Befehl.
    Was mir noch auf-/eingefallen ist:
    In der von Dir verlinkten Befehlsübersicht gibt es noch weiße Stellen (z. B. 05 und 08). Hast Du schon Ideen für Befehle, die man an den freien Stellen einbinden könnte?
    Welche Flaggen gibt es bei dem Prozessor und wann werden sie gesetzt?
    Hattest Du schon mal den Befehlssatz ausgetestet bzw. hast Du vor, Programme mit Hilfe eines Emulators zum Laufen zu bringen? (Leider habe ich wegen anderer Projekte keine Zeit, sonst würde ich mal sowas wie einen kleinen Emulator schreiben.)
    Nebenbei: Im Grunde genommen ist der von Dir angestrebte Prozessor eine Mischung aus 8 Bit-Rechner und 16 Bit-Rechner, erinnert daher eher an den Z80 als an den 6502. Besteht bei dem Design nicht eventuell die "Gefahr", daß die 16 Bit-Register für viele Operationen als 16 Bit-Akkumulator gebraucht würden? Wie sollen sich 16 Bit-Operationen unterscheiden von 8 Bit-Operationen? Wäre das nur eine verzögerte Ausführung (z. B. 1 Takt mehr) oder gäbe es irgendwelche Einschränkungen?

    ich denke mal, dass das rein Betriebssystemabhängig ist

    Nein.

  • Entweder man unterscheidet zwischen LEA und den anderen Befehlen (nur bei LEA ist der Wert negativ), oder man benutzt für Operationen am Stapelzeiger bzw. fürs In-/Dekrementieren der Register eigene Befehle, oder man benötigt fürs Subtrahieren halt einen 3-Byte-Befehl.Was mir noch auf-/eingefallen ist:
    In der von Dir verlinkten Befehlsübersicht gibt es noch weiße Stellen (z. B. 05 und 08). Hast Du schon Ideen für Befehle, die man an den freien Stellen einbinden könnte?

    Wie schon gesagt, versuche die die cpu einfach zu halten, also auch das decoding welche operation mit welchem modus ausgeführt werden soll die Anzahl an Operation und so weiter - darum will ich nicht alles bis zum letzten belegen.



    Welche Flaggen gibt es bei dem Prozessor und wann werden sie gesetzt?

    Wie die 8 bit avr, ohne dem H flag. Wie carry genutzt wird auch wie die. Gesetzt eher wie beim 6502.



    Hattest Du schon mal den Befehlssatz ausgetestet bzw. hast Du vor, Programme mit Hilfe eines Emulators zum Laufen zu bringen? (Leider habe ich wegen anderer Projekte keine Zeit, sonst würde ich mal sowas wie einen kleinen Emulator schreiben.)

    Nein, es ist bis jetzt nur ein Gedankenspiel. Und wenn dann würde ich versuchen den vice oder einen anderen C64 emulator auf die cpu umzubauen.


    Wie sollen sich 16 Bit-Operationen unterscheiden von 8 Bit-Operationen?

    Es gibt keine 16 Bit-Operationen ausser lea (und incx = leax x,1).


    Ich habe die cpu geändert, nun sind alle (16bit register , 8bit register) operationen in einem opcode versteckt, denn ja, es braucht nicht so viele. Zudem gibt es ein neues Register U, welches aber nur eingeschränkt genutzt werden kann.


    Neue Beschreibung

  • Ich habe die cpu geändert

    Danke für die neue Version. Ich finde es immer sehr lehrreich und inspirierend zu sehen, welche Gedanken Andere so verfolgen (z. B. bei Dir die saubere Strukturierung der Befehle) und zu welchen Schlüssen sie gekommen sind.

    Nein, es ist bis jetzt nur ein Gedankenspiel. Und wenn dann würde ich versuchen den vice oder einen anderen C64 emulator auf die cpu umzubauen.

    Dann weiterhin gutes Gelingen, und sollte die Idee voranschreiten, meld Dich bitte wieder.

  • Ein begrenzter Speicher ist eine Limitierung der bei Grafik, Musik/SFX und Programmdaten berücksichtigt werden muss. Man denke nur an die 64 KB RAM des C64 und was es allen Beteiligten die an einem größeren Projekt arbeiten (Demo oder Spiel ist dabei unberheblich) abverlangt. Beispiele dafür ist das Remake von Bomb Jack oder der neuen Version von Giana Sisters die am entstehen sind. Es verlangt von Grafiker(n), Musiker(n) und Programmierer(n) gleichermaßen Kompromisse und Lösungen (z. B. Grafikdaten im Speicher packen/depacken) um das bestmögliche Ergebnis zu erzielen.

  • Ein begrenzter Speicher ist eine Limitierung der bei Grafik, Musik/SFX und Programmdaten berücksichtigt werden muss. Man denke nur an die 64 KB RAM des C64 und was es allen Beteiligten die an einem größeren Projekt arbeiten (Demo oder Spiel ist dabei unberheblich) abverlangt. Beispiele dafür ist das Remake von Bomb Jack oder der neuen Version von Giana Sisters die am entstehen sind. Es verlangt von Grafiker(n), Musiker(n) und Programmierer(n) gleichermaßen Kompromisse und Lösungen (z. B. Grafikdaten im Speicher packen/depacken) um das bestmögliche Ergebnis zu erzielen.

    Es ist halt ein Unterschied, ob die Limitierung (z.B. des Speichers) vorhanden ist, weil es technisch und/oder finanziell nicht mehr hergab (früher) oder ob man diese Limitierung rein willkürlich ohne äußere Notwendigkeit festgelegt hat (heute).


    Eine Limitierung der Grafik oder des Sounds sehe ich deshalb anders (positiver) als eine Limitierung des Arbeitsspeichers. Grafik und Sound "sieht und hört" man. Das wirkt sichtbar. Ob man nun 64KB oder 4MB nutzt, sieht kein Mensch. Diese Limitierung sehe ich deshalb eher "nutzlos" in Hinsicht auf ein "8bit-Feeling". Ob z.B. die Levels für ein Spiel alle sofort im Speicher sind oder einzeln nachgeladen werden müssen, ist fürs Feeling pupsegal. ;)

  • Ob z.B. die Levels für ein Spiel alle sofort im Speicher sind oder einzeln nachgeladen werden müssen, ist fürs Feeling pupsegal.

    Das hat sogar ganz massiven Einfluss auf das Spiel- und Leveldesign.