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

    • Basic (angelehnt an Basic 7.0 o.ä., OHNE Zeilennummern und ohne GOTO

    Insgesamt finde ich die Aufzählung gut.


    Das ist allerdings ein Punkt, in dem ich widerspreche: Wenn ein 8bit-Basic, dann auch ganz stilecht mit Zeilennummern und vor allem Goto und Gosub. Gerade, wenn auch Poke und Peek beibehalten werden soll.

  • Ich hatte mir auch schon mehrfach Gedanken um eine neue 8-bit CPU gemacht.
    Sie soll so einfach sein, dass man sie hätte auch damals™ bauen können.


    Hier meine aktuellste Idee:

    • Register:

      • 8-bit: A, B, CC (die CC-bits wie beim AVR 8-bit, nur da heisst das register SREG)
      • 16-bit: X, Y, S, PC
    • 2-Byte Befehle:

      • Adressierungs-Modus: R, n7 und (R, n7)
        adca, adda, anda, bita, cmpa, eora, lda, ora, sta, sbca, suba
        ldb, stb
        cmpx, ldx, leax, stx
        cmpy, ldy, leay, sty
        leas
        jmp, jsr
        clr, dec, inc, lsl, lsr, rol, ror, tst
        R = X,Y,S,PC; Register relativ mit 7bit (-64..+63) Offset und ohne oder mit indirektion
        Im zweiten Byte wird das Offset und die indirektion gespeichert (diese Befehle belegen 50% der Opcodes)
      • Adressierungs-Modus: R, r; (R, r); R + r; (R + r); R+; R++; -R; --R; (R++); (--R)
        adca, adda, anda, bita, cmpa, eora, lda, ora, sta, sbca, suba
        ldb, stb
        cmpx, ldx, leax, stx
        cmpy, ldy, leay, sty
        leas
        jmp, jsr
        clr, dec, inc, lsl, lsr, rol, ror, tst
        R = X,Y,S,PC; r = a,b; R, r = r wird als signed angesehen; R + r = r wird als unsigned angesehen
      • Adressierungs-Modus: PC, n8
        brcc, brvc, brne, brpl, bric, brsc, brtc, bra
        brcs, brvs, breq, brmi, bris, brss, brts, brn
        bsr
      • Adressierungs-Modus: i8 (8-Bit-Konstante)
        andai, bitai, cmpai, eorai, ldai, orai, sbcai, subai
        cmpbi, ldbi, sbcbi, subbi
        andcci, bitcci, eorcci, orcci
      • Stack
        pull regs
        pullpc regs (hierdurch können mehrere Register gleichzeitig mit einer Konstante geladen werden: pullpc a,b,x; byte "a"; byte "b"; word "x"; nächste Operation)
        push regs
        regs = beliebige kombination von: A, B, CC, X, Y, S, PC
      • Sonstiges
        wai (gff. interrupt anschalten und warten dass einer eintritt)
        tfr/eab/ect (Transfer bzw. Austausch von 8-Bit-Registern, nur über diesen Weg kann der eingebaute 8-Bit-Port angesprochen werden; Austausch von C und T)
    • 3-Byte Befehle:

      • Adressierungs-Modus: i16 (16-Bit-Konstante)
        cmpxi, ldxi
        cmpyi, ldyi
        ldsi
        jmp, jsr
      • Adressierungs-Modus: addr (16-Bit-Adresse)
        cmpa, lda, sta
        ldb, stb
        cmpx, ldx, stx
        cmpy, ldy, sty
      • Adressierungs-Modus: R, n16 (Register mit 16-Bit-Offset)
        leas S,n16
        leax X,n16
        leay Y,n16
        jmp PC,n16
        jsr PC,n16
    • 1-Byte Befehle:

      • Adressierungs-Modus: Register
        clra, deca, inca, lsla, lsra, rola, rora, tsta
        clrb, decb, incb, lslb, lsrb, rolb, rorb, tstb
      • Adressierungs-Modus: Sonstige
        nop
        reset
        ret
        swi
    • Bemerkungen:
      Falls jemand addai und adcai vermisst, diese können durch die Subtraktion des negierten Wertes erreicht werden (und da das Carry bei add+sub gleich funktioniert ist dieses auch wie von einer Addition erwartet)
      reti gibt es nicht als eigenen Befehl, aber "pull cc,pc" hat das selbe Ergebnis.
      Die Befehle/Adressierungs-Modi sind einfach aus dem Opcode zu entnehmen.
      Das 1. Byte besagt bereits wie viele weitere Bytes folgen (dies ist z.B. beim 6809 anders, an den ich mich aber oft angelehnt habe).
      Der Befehlssatz und die Adressierungs-Modi wurden absichtlich gering gehalten ich bin aber dennoch davon überzeugt dass dies mehr als ausreichend ist.
  • Hier meine aktuellste Idee:

    SIeht interessant aus. Leider verstehe ich nicht so ganz, was einige Adressierungsarten oder Befehle bedeuten. :schande: Was meint z. B.
    dec R, r
    und was
    dec (R, r)?
    Oder
    dec R + r
    im Gegensatz zu
    dec (R + r)?
    Wieso wird da überhaupt ein 8 Bit-Register kombiniert mit einem 16 Bit-Register?

    R+; R++

    Gehe ich recht in der Annahme, daß R++ bedeutet "postinkrement um 2" und bei den 16-Bit-Registern angewendet wird? Rein syntaktisch würde ich da keine Unterscheidung machen zwischen dem Laden des Akkumulators A und des Indexregisters X und in beiden Fällen einfach (R)+ bzw. -(R) schreiben.

    Register relativ mit 7bit (-64..+63) Offset

    Das erinnert mich leider schmerzlich an den Z80 mit der Adressierungsart "(IX + signed 8 Bit Offset)". Das Offset hatte dort den Wertebereich -128..127, doch benötigt man meiner Erfahrung nach einen negativen Offset so gut wie nie, wohingegen man einen Offset > 127 durchaus gebrauchen kann.

    indirektion

    Meinst Du damit, daß
    a) zuerst eine Adresse aus einem 16 Bit-Register (X, Y, PC oder S) entnommen wird,
    b) dann zu dieser ein Offset (-63..64) dazuaddiert wird,
    c) danach von der sich daraus ergebenen Adresse ein 16 Bit-Wort geladen wird,
    d) welches endlich den Zeiger darstellt auf den Speicher, an dem die eigentliche Operation ausgeführt werden soll?

    1-Byte Befehle

    Hier hätte ich es nett gefunden, wenn es Befehle wie DEX, INX, DEY und INY gibt, damit man diese Register als (16 Bit)-Zähler gebrauchen kann, ein Feature, das dem 6502 leider fehlt.

    Adressierungs-Modus: R, n16 (Register mit 16-Bit-Offset)

    Auch hier kann ich mir nicht vorstellen, was damit gemeint sein könnte. Was würde ein
    leas S, $1234
    bedeuten?


    Und noch eine abschließende Frage: Wie kopiert man den Inhalt des X-Registers in das Y-Register (oder auch S) und umgekehrt?

  • "dec X, a" berechnet die Adresse indem X und (sign extended) a addiert werden. Der Inhalt der Adresse wird dann un 1 verringert.
    "dec X + a" berechnet die Adresse indem X und (zero extended) a addiert werden....
    "dec (X, a)" bedeutet dass eine Adresse aus X+(sign extended) a errechnet wird und dann an der stelle eine andere Adresse geladen wird und die Speicherstelle, auf die das zeigt wird dann um 1 verringert.


    (Edit2: 0x80 sign extended = 0xff80 = -128; 0x80 zero extended = 0x0080 = 128; wenn das oberste bit nicht gesetzt ist, also 0..127, dann machen beide das selbe)


    Gehe ich recht in der Annahme, daß R++ bedeutet "postinkrement um 2" und bei den 16-Bit-Registern angewendet wird?

    Ja, und wenn etwas in Klammern steht dann bedeutet das indirekte Adressierung, wie oben beschrieben. (und ich hatte ja auch geschrieben dass R = X,Y,S,PC und r = a,b ist).



    Und noch eine abschließende Frage: Wie kopiert man den Inhalt des X-Registers in das Y-Register (oder auch S) und umgekehrt?

    Mit "lea" - das ist ein operator der sehr nützlich ist, es ist die Abkürzung von load effective address. Es wird die Ziel-Adresse berechnet (wie oben) aber diese wird dann in das angegebene Register geschrieben (anstelle etwas von da zu laden/speichern). "leax y, -10" sorgt also dafür das y-10 in x geschrieben wird. (oder "leax x,-1" == "dex")



    Edit:
    Die Idee ist bei fast allen Computer die, dass du deine Variablen nicht an festen adressen hast sondern sie irgendwo sind, und sie über eine addition mit einer basis-adresse (z.B. in X oder Y) zu erreichen. Dann baucht man sehr oft nur ein kleiner Offset und der Opcode ist kurz. (Währen beim lda 0x1234 der opcode lang ist und auch noch nicht mal flexibel)
    Zudem ist es mit solchen mitteln temporäre Variablen ganz einfach auf dem Stack zu nutzen. Beispiel:
    leas s, -4 ; reserviere 4 bytes auf dem stack
    lda s, 3 ; liest das letzte reservierte byte
    leas s, 4 ; lösche die 4 bytes vom stack (die daten werden nicht angefasst nur der stackpointer geändert)

  • Ich hatte mir auch schon mehrfach Gedanken um eine neue 8-bit CPU gemacht.

    Erst einmal: Hut ab vor der detaillierten Ausarbeitung!


    Ich sehe mich hier im gedanklichen Zwiespalt:


    Wenn man schon einen Rechner neu entwickelt, warum nicht auch eine neue 8bit-CPU? Ist sicherlich reizvoll.


    Auf der anderen Seite: Geht man damit nicht zu weit in die Tiefe? Wenn man für eine Fantasie-CPU sich neu in die (nur dort verwendbare) Maschinensprache einarbeiten soll, verlangt man dem Nutzer nicht zuviel ab?


    Eine interessante Idee, keine Frage. Aber sie geht vielleicht "zu weit" ins Neue?

  • Erst einmal: Hut ab vor der detaillierten Ausarbeitung!


    Danke, das oben war die nette Beschreibung, hier die Auflistung aller opcodes - obwohl sich da noch viel ändern kann: https://pste.eu/p/LPrr.html (Farbe = Opcode-Größe)



    Auf der anderen Seite: Geht man damit nicht zu weit in die Tiefe? Wenn man für eine Fantasie-CPU sich neu in die (nur dort verwendbare) Maschinensprache einarbeiten soll, verlangt man dem Nutzer nicht zuviel ab?

    Wenn du dir die 6809 CPU anschaust, siehst du dass die fast alles dieses und viel mehr kann - ich hab's aber absichtlich einfach gehalten.

  • Wenn man schon einen Rechner neu entwickelt, warum nicht auch eine neue 8bit-CPU? Ist sicherlich reizvoll.

    Wenn ich jetzt mal AleX' Ansatz als 3. im Bunde der konkret vom Autor selbst realisierbaren (oder sich in Realisierung befindlichen) Projekte ansehe, die aus diesem Thread hervorgegangen sind bzw. deren Protagonisten den Threads als Anstoß sahen, sich mal wieder mit dem Thema zu beschäftigen, ist schon auffällig, dass bei allen dreien eine neue CPU der Kern des Projekts ist. Realisierungen, die auf dem Zusammensetzen noch existierender Alt-Chips basieren, scheinen nicht besonders attraktiv bei denen zu sein, die sowas wirklich umsetzen können. Mit neuen CPUs setzen wir automatisch entweder auf FPGAs oder Emulation.


    Alle drei schon existierenden Retro-Projekte (C256, M65 und X16) setzen hingegen auf real existierende CPUs (beim 4502 bin ich allerdings nicht sicher, wie weit der realisiert war), selbst wenn sie nicht als echte Hardware verwendet werden. Das ist schon mal ein großer Unterschied.


    Die Fähigkeiten der ausgedachten CPUs werden sicherlich unterschiedlich sein und auch unterschiedliche Dinge ermöglichen. Ich schätze, dass ihre Entwickler vor allem daran denken, dass sie sich komfortabel (in ASM) programmieren lassen, besser als 6502, 65816 oder sogar der 68000. Für ASM-Entwickler werden die CPUs sicherlich ein Eldorado an neuen Möglichkeiten und Bequemlichkeiten bieten aber wie sieht es für die Hochsprachen-Entwickler aus?


    Ein Wunsch, der immer wieder geäußert wurde, war, mit einer Hochsprache (nennen wir sie mal (kompilierbares) BASIC) ausreichend performante Arcade-Games (à la Pac-Man oder Space-Invaders, wenn man es niedrig ansetzt) AUF dem Rechner entwickeln zu können. Das ist sehr stark davon abhängig, wie schnell das System getaktet ist und auch, wie gut sich für den jeweiligen Prozessor kompilieren lässt. Ich habe jetzt gelernt, dass 8-Bit-CPUs nicht sonderlich Compiler-freundlich sind und selbst die frühen 16-Bitter noch Platz nach oben hatten, was gute Kompilate angeht. Das bedeutet, dass sich ein Compiler-Ergebnis noch stark von der Performance handgecodeten Assemblers unterscheidet. Je moderne die Architektur, desto besser werden Kompilate im Vergleich zu ASM, so wurde es mir zumindest erklärt. (Über die Qualität erzeugten Codes habe ich mir früher keine großen Gedanken gemacht, die Ergebnisse waren immer "schnell genug").


    Für unseren Wunsch, direkt auf dem Rechner Hochsprachen-Spiele zu schreiben ist es also essentiell, dass er eine schlau erdachte CPU besitzt – zumindest wenn man nicht mit Fantasie-MHz-Werten versucht, Architekturmängel auszugleichen.


    Ich bin gespannt, in wie weit die erdachten CPUs/Rechner in der Lage wären, den Wunsch nach komfortabler und performanter Hochsprachen-Programmierbarkeit zu erfüllen.

  • Ein Wunsch, der immer wieder geäußert wurde, war, mit einer Hochsprache (nennen wir sie mal (kompilierbares) BASIC) ausreichend performante Arcade-Games (à la Pac-Man oder Space-Invaders, wenn man es niedrig ansetzt) AUF dem Rechner entwickeln zu können. ...
    Für unseren Wunsch, direkt auf dem Rechner Hochsprachen-Spiele zu schreiben ...


    Sollen solche Spiele schon im unkompilierten Zustand schnell genug sein oder reicht auch ein schnell übersetzender Compiler aus?

  • Sollen solche Spiele schon im unkompiliertem Zustand schnell genug sein oder reicht auch ein schnell übersetzender Compiler aus?

    Mir persönlich wäre das schnuppe. Als realistisch sehe ich aber an, dass es bei den CPUs und Taktraten, von denen hier oftmals gesprochen wird und die irgendwo bei den frühen 16-Bittern angesiedelt werden können, nur kompilierter Code schnell genug sein wird – wenn überhaupt. Arcade-Games mit Interpreter-Code – da reden wir wahrscheinlich eher von GHz als von MHz.

  • Mir persönlich wäre das schnuppe. Als realistisch sehe ich aber an, dass es bei den CPUs und Taktraten, von denen hier oftmals gesprochen wird und die irgendwo bei den frühen 16-Bittern angesiedelt werden können, nur kompilierter Code schnell genug sein wird – wenn überhaupt.


    Wären denn Clones wie Paku Paku (zu sehr großen Teilen in C geschrieben) oder Manic Miner (Source, in reinem C geschrieben) akzeptabel?

  • Vom Ergebnis her vielleicht schon, was das Coden selbst angeht, kann ich dazu nur wenig sagen. In C kann man ja auch sehr Maschinen-nah entwickeln – das wäre sicherlich nicht das, was vielen hier vorschwebt. Der Wunsch von vielen war sowas ähnliches wie ein heruntergebrochener Pico-8 – auf schwacher Hardware. Das meint grob zusammengefasst:


    Programmieren (auch) AUF der Maschine, nicht (nur) mit einer externen PC-IDE.
    Komfortable Programmiersprache, u.a. mit Befehlen für Sprites, Tiles, Scrolling, Sound, Joystickabfragen, usw. (ohne Peek und Poke)
    Komfortable IDE (auf dem Gerät), also brauchbarer 80-Zeichen-Code-Editor mit Ergänzung um Editoren für Sprites, Chars, Tiles, Sound etc.
    Ausreichend schnelle Ausführung der programmierten Games – egal ob kompiliert oder interpretiert.


    Dass das auf einem C64 nicht gelingen kann, ist klar. Aber hier werden ja neue CPUs erfunden und die MHz-Werte liegen eher auf dem Niveau von frühen 16-Bit-Rechnern, vielleicht sogar darüber. Der zur Verfügung stehende Speicher ist (zumindest bei Compilern) natürlich auch ein großes Thema.


    Es kann sich ja auch herausstellen, dass die CPU-Entwürfe und die Wünsche der späteren User an dieser Stelle nicht zusammenkommen können und man herbe Abstriche von den Wunschvorstellungen machen muss. Da wäre es dann interessant, auf was man genau verzichten müsste oder wahlweise, was man an zusätzlicher Hardware-Power benötigen würde, um das doch noch umzusetzen.


    Vielleicht passt "16-Bit und ein- bis zweistellige MHz" und komfortables Game-Programmieren AUF der Kiste einfach nicht zusammen, sodass es zwangsläufig 2 Ideen wären, die man getrennt verfolgen müsste.

  • Die Idee ist bei fast allen Computer die, dass du deine Variablen nicht an festen adressen hast sondern sie irgendwo sind, und sie über eine addition mit einer basis-adresse (z.B. in X oder Y) zu erreichen.

    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. Daher befürchte ich, daß die Option auf negative Zahlenwerte letztendlich eine Verschwendung darstellt. Desweiteren dürfte die doppelte Indirektion kaum Verwendung finden. Der Gebrauch von X oder Y als Adreßregister zumal noch mit der Option der Addition eines Offsets dürfte für die meisten Programme völlig ausreichend sein. Die Benutzung von Zeigern auf Zeigern (, wobei letztere umständlich im Speicher liegen,) ist hingegen in der Praxis kaum erforderlich und auch sehr langsam. Das dürfte dazu führen, daß viele Programme sich den Zeiger einmalig in ein Adreßregister laden (z. B. als Zeiger auf ein struct) und damit die einzelnen Elemente ansprechen, nicht jedoch mittels einer Folge von Befehlen mit einer doppelten Indirektion. Besonders in Kombination mit Befehlen wie "DEC" dürfte die Wahrscheinlichkeit einer Benutzung bei unter 0,1% liegen, da dieser Befehl am ehesten bei Zählschleifen anzutreffen ist, deren Werte maximal als lokale Variable über (S + Offset) abgelegt werden.
    Die Semantik eines (R++) erschließt sich mir auch nicht. Soll hier das X-Register auf einen Zeiger im Speicher zeigen und nach der Operation um 2 erhöht werden, um dann auf den nächsten Zeiger zu zeigen? Für sowas fällt mir überhaupt kein brauchbarer Fall ein, der solch eine gesonderte Adressierungsart rechtfertigen würde. :/
    Vielleicht wäre es besser, den Befehlssatz ein wenig auf die Befehle hin zu optimieren, 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.

  • Also meines Wissens gibt es keinen 8-Bitter mit mehr als 64kB Adressraum (ohne Banking).

    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


    D.h., es können 16 MB adressiert werden. Zeropageadressierung bleibt, es würde dort allerdings enger werden, weil jede Adresse dann drei Bytes benötigt. Vorhandener 6502-Code wäre zwar nicht direkt kompatibel, könnte aber einfach konvertiert werden. Und man muss keine neue Assemblersprache lernen, weil die im Prinzip die gleiche, wie bei einer 6502-CPU ist.


    Das war jetzt aber nur eine spontane Idee... :rolleyes:

  • 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


    D.h., es können 16 MB adressiert werden. Zeropageadressierung bleibt, es würde dort allerdings enger werden, weil jede Adresse dann drei Bytes benötigt. Vorhandener 6502-Code wäre zwar nicht direkt kompatibel, könnte aber einfach konvertiert werden. Und man muss keine neue Assemblersprache lernen, weil die im Prinzip die gleiche, wie bei einer 6502-CPU ist.


    Das war jetzt aber nur eine spontane Idee... :rolleyes:

    Warum gleich 24bit?
    Erstmal 20bit aka 1MB dürfte wohl etwas einfacher sein.