Programm Entwicklung für Apple 1!?

  • cc65 besteht grundsätzlich aus zwei Bestandteilen:


    1. Der Codegenerierung. Der C Compiler 'cc65' erzeugt aus C Quelltexten Assembler Code, der dann durch den Makro Assembler 'ca65' zu Objektdateien assembliert wird. Zum Schluß verbindet der Linker 'ld65' die Objektdateien zum fertigen Programm. Dies ist i.A. völlig unabhängig vom Rechner, für den das Programm sein soll. Statt die Programme einzeln aufzurufen kann man das auch 'cl65' überlassen.


    2. Die C Bibliothek. Da sind dann Funktionen wie printf() implementiert, Diese C Bibliothek hat zwar auch ein paar Teile, die unabhängig vom Rechner sind - z.B. memcpy(), aber der Großteil ist für jeden Rechner anders.


    Wenn nun also gesagt wird, dass cc65 den Apple I nicht unterstützt, dann bedeutet das, dass die Apple I-spzifischen Teile der C Bibliotheken fehlen. So weit so schlecht. Nur ist es aber auf der anderen Seite so, dass ein Assembler wie xa "noch schlechter" ist, denn der hat weder eine fertige Bibliothek für den Apple I noch die Möglichkeit, ein Programm oder Teile eines Programms in C zu schreiben.


    Oder anders herum gesagt, wenn man eigentlich gerne mit C arbeiten will, dann ist cc65 auch ohne Unterstützung für den gewünschten Rechner immer noch viel besser als ein 0815-Assembler - zumal cc65 ja auch einen konkurrenzfähigen Assembler beinhaltet (und die Mischung von C und Assembler toll unterstützt).


    Es gibt ein ganze Reihe von Möglichkeiten, wie man nun konkret vorgehen kann, um in C für einen Rechner zu programmieren, den cc65 "nicht unterstützt". Die zumindest für die ersten Schritte einfachste dürfte aus diesen Punkten bestehen:


    1. Nutzung des 'none' Targets in Stil von 'cl65 -t none ...'. Damit macht man zunächst "keine" Aussagen über den Zielrechner.


    2. Explizite Definition des vom Programm zu verwendenden Speichers (weil cc65 das bei einem "nicht unterstützten" Rechner ja nicht wissen kann).

    2.1 Niedrigste zu verwendende Adresse: --start-addr $xxxx

    2.2 Höchste zu verwendende Adresse: -Wl -D,__STACKSTART__=$xxxx

    2.3 Niedrigste zu verwendende Zeropage Adresse: -Wl -D,__ZPSTART__=$xx

    Siehe https://github.com/cc65/cc65/blob/master/cfg/none.cfg#L1-L12 für die Arithmetik dahinter.


    3. Nutzung von _sys() zum Aufruf vom ROM-Funktionen, z.B. für die Ausgabe eines Zeichens auf dem Bildschirm. Siehe https://cc65.github.io/doc/funcref.html#_sys und https://github.com/cc65/cc65/b…er/include/6502.h#L72-L79

    Um z.B. eine (hypothetische) ROM Funktion an Adresse $FEDC mit 65 im Accumulator aufzurufen, könnte man schreiben:


    struct regs r;

    r.a = 65;

    r.pc = $FEDC;

    _sys(&r);


    4. Nutzung von PEEK()/POKE() zum Zugriff auf bekannte Adressen. Siehe https://cc65.github.io/doc/funcref.html#ss2.36


    Viel Spaß!

  • Danke. Sehr interessant.

    Warum man >2000 Zeilen C Code schreiben kann, aber keine 20 Zeilen readme.txt entzieht sich allerdings meinem Verständnis :(

    Das am Anfang des C Sourcecodes vieles steht ist gut!


    Aber gerade .STORE ist da angegeben mit:

    .STORE BASIC_ROM,$2000,"basic.rom" write binary image file "basic.rom"



    Aber erst nach dem Studium des Quelltextes, kann ein C Sourcecode Leser sehen:

    => d.h.:

    ;.STORE <startaddress>,<length>,"filename"

    .STORE $280,$100,"out.bin"


    Dabei MUSS man eine Länge angeben - für Eproms sicher ok. Für raw binare aber unschön.

    Hier fehlt ein Parameter, der als Platzhalter für die Filelänge dient!


    Genauso die Startadresse - kann mal doch aus der ORG / * = Anweisung nehmen..
    (z.B. -1 für beide = Start aus org/*= oder 0 und Länge = Binärlänge)


    Noch ein weiterer Punkt. Source muss zwingend .asm heißen.

    Stört nicht, muss man aber wissen.


    Ansonsten schöner Assembler!


    LG Peter

    github.com/petersieg

  • Sowohl xa als auch asm6502 akzeptieren inc alleine, obwohl das eine 65C02 Anweisung ist, ohne zu warnen.

    bsa wirft Error aus, wenn 6502 aktiv ist = richtig!


    Dem asm6502 habe ich mal schnell beigebracht raw binary auszugeben - siehe zip - falls es wer braucht.


    Als Simulator habe ich py65mon bis jetzt: https://github.com/mnaberez/py65


    LG Peter

  • Wenn man ein CBM Programm erstellen will, ist es nützlich auch den Pseudo Op:

    .LOAD

    zusätzlich zum .STORE zu verwenden.

    Dann wird die Anfangsadresse des Binary vor das Binary geschrieben, dem CBM Standard für PRG Dateien entsprechend.

    START = $0801

    * = START

    .LOAD

    .STORE START,EndOfProgram-START,"program.prg"


    schreibt die Bytes $01 $08 und dann den im STORE Befehl angegebenen Bereich.

  • Sowohl xa als auch asm6502 akzeptieren inc alleine, obwohl das eine 65C02 Anweisung ist, ohne zu warnen.

    bsa wirft Error aus, wenn 6502 aktiv ist = richtig!


    Es gibt einen immerwährenden Disput um die Adressierung A (Accumulator) unter den 6502 Programmierern.

    Ich bevorzuge die Schreibweise:

    Code
    ASL A
    ROL A
    INC A ;(65C02 & 45GS02)

    weil diese Schreibweise im ersten offiziellen Handbuch zur 6502 Programmierung von MOS so vorgegeben wurde.

    Da aber viele Programmierer darauf beharren diese Befehle ohne 'A' zu schreiben, bin ich pragmatisch vorgegangen:

    Der BSA akzeptiert beide Schreibweisen. Somit werden die Befehle

    Code
    ASL A
    ASL

    als synonym aufgefasst.

    Aber

    Code
    INC A
    INC

    sind wie von Peter beschrieben, keine Instruktionen, die der 6502 beherrscht.

  • Hmm. Irgendwas geht noch nicht so, wie es soll:


    Code:

    ;.STORE <startaddress>,<length>,"filename"

    .STORE START,EndOfProgram-START,"out.bin"


    START = $280


    LG Peter

    github.com/petersieg

  • EndOfProgram ist keine vordefinierte Variable sondern sollte als Beispiel dienen.

    Jeder beliebige Name kann verwendet werden. Auch START ist nicht reserviert sondern ein Beispiel.


  • Ein Feature, das ich nicht mehr missen möchte, ist es lokale Label und Konstanten zu verwenden.

    Wenn man einen Bereich mit "Module" und "EndMod" deklariert, so lassen sich darin lokale

    Variablen verwenden, deren Gültigkeitsbereich (Scope) auf das Module beschränkt ist.

    Lokale Namen müssen zur Unterscheidung von globalen Namen mit einem Unterstrich _ beginnen.

    Das erspart die Suche nach immer neuen Labelnamen und macht den Code übersichtlicher.

    Ein Beispiel mit zwei Modulen, die konfliktfrei beide das Label "_loop" verwenden.

  • Ja, richtig, wobei das "= *" funktioniert aber überflüssig ist, weil das Label ja schon durch die Position

    den Wert des PC erhält.