Beiträge von Mike

    Alter, ich habe eine ganz normale Frage gestellt.

    Gegenfrage, welche anderen Konstrukte außer deinen zwei angegebenen Programmstücken in deinem Beitrag #17 hätte ich denn gemeint haben können?


    Nochmals: in üblichem 65xx-Code versucht man nach Möglichkeit, solche Tänze und "Herumprogrammiererei" um den Befehlssatz zu vermeiden wo's irgendwie geht.


    Nichts anderes habe ich bereits im Beitrag #3 hier im Thread geschrieben, mit "Du kannst natürlich immer vorher noch den Pointerwert von $02A4/$02A5 in die ZP kopieren, üblicherweise organisiert man Code auf dem 65xx aber so, daß das nicht ständig nötig ist."


    Hervorhebung ergänzt.


    Und weil's gerade gut paßt - hier ein Thread in Denial, wo nach einem hypothetischen LDA ($4000) Befehl gefragt wird, wo ich auch meine Finger drin hatte: LDA ($4000) and self-modifying code - Denial (sleepingelephant.com)


    Das Thema kommt also immer wieder dann auf, wenn jemand versucht, Hochsprachencode 1:1 auf einen 65xx zu bringen und dann daran festhängt, daß beim 65xx Pointer generisch in der Zeropage gehalten werden.

    Welches Konstrukt meinst du genau?

    Ist das nicht klar? Die unnötige Kopiererei von Pointern von einer Stelle an eine andere ist gemeint!

    Und wie würde der bessere Code z.B. aussehen?

    Der zeichnet sich dadurch aus daß, ich wiederhole mich, so ein Konstrukt überhaupt nicht nötig ist.


    Wenn Du willst, hat dieser "bessere Code" einfach 0 Byte Größe, ist also überhaupt nicht vorhanden.

    der "Rucksack" tut genau was?

    Wer hat den "Rucksack" erfunden? - Programmieren - Forum64


    Im konkreten Fall wird die Adresse vorne mit einem LDA $xxxx-Opcode und dahinter mit einem RTS ergänzt, so daß der "Ansprung" des ganzen Konstrukts mit einem JSR $02A3 so wirkt also würde der hypothetische Befehl "LDA ($02A4)" ausgeführt.


    Das ganze sieht der o.g. Rucksack-Technik sehr ähnlich, war aber eigentlich gar nicht so gemeint.


    Tatsächlich werden Rucksäcke verwendet, wenn in bestehendem Code innendrin weiterer Code eingefügt werden soll, der Quellcode aber nicht vorliegt. Dann wird an der betroffenen Stelle ein (Unterprogramm-)Sprung eingefügt, der neuen Code an einer freien Stelle im Hauptspeicher ausführt. Dieser Rucksack wird dann ausgeführt und kehrt dann wieder zum ursprünglichen Programm zurück. Genaueres, siehe meinen Beitrag #38 im verlinkten Thread.

    Für die Zeropage aber.

    Der Pointer steht aber zunächst mal *nicht* in der Zeropage, was das eigentliche Problem ist und welches durch deinen Vorschlag einer nicht-existierenden Adressierungsart nicht gelöst wird.

    Er will aber nicht dahin springen, sondern den Wert auslesen.

    Du hast nicht verstanden, wozu die beiden Bytes 173 und 96 da sind.

    Nun ja,


    Du kannst schon zusätzlich in Adresse $02A3 den Wert 173 und in Adresse $02A6 den Wert 96 schreiben und dann leistet JSR $02A4 das verlangte. ;)


    Eigentlich müßte/sollte der Pointer in der Zeropage liegen, und dann gibt es dafür die (zp),Y oder (zp,X) Adressierungsarten. Du kannst natürlich immer vorher noch den Pointerwert von $02A4/$02A5 in die ZP kopieren, üblicherweise organisiert man Code auf dem 65xx aber so, daß das nicht ständig nötig ist.


    Viele Grüße,


    Michael


    P.S.

    LDA ($02A4),Y

    ... gibt es nicht.

    hier eine Fassung mit Gegner (die KI steht auf einem Level mit der grafischen Gestaltung des Gegners, also ganz unten)

    In den weiteren Levels wird der Gegner wohl etwas schneller, läßt sich aber immer noch in eine Ecke locken und "klebt" dann fest. Er sollte mindestens am Rand noch mitlaufen können, sonst geht immer sowas, wie das hier:



    Gegner in die Ecke rechts oben gelockt und "eingeklemmt", dann nach links, nach unten und dann - kurz bevor der Gegner die Spur berührt - wieder nach rechts.

    In meinem Projekt-Archiv fand sich sogar noch ein bereits zum C64 konvertiertes Exemplar der Routine. Allerdings findet die Foren-Suche dieses Exemplar nicht wieder, obwohl ich schon meine es hier gepostet zu haben, darum ...

    Klassisches Käseknabber-Programm: im Gegensatz zu einer Version mit RND() kann dieses hier Vollzug melden (einfach ausprobieren und in den Zeilen 29 und 30 USR() durch RND() ersetzen!). In VICE ggfs. "Maximum Speed > No Limit" setzen. :D


    Die neue USR()-RND()-Funktion übernimmt den Seed von RND(), man kann also wie gehabt eine negative Zahl mit RND() verwenden um USR() zu seeden. In jedem Fall wird die maximal mögliche Periodenlänge von 2^32 erreicht.


    Allerdings ist QDRand trotzdem mit Vorsicht zu genießen: eine gängige Methode aus [0,1[-gleichverteilten Zufallszahlen Gauß-normverteilte Zufallszahlen zu erzeugen (12 aufeinander folgende Werte aufaddieren und 6 abziehen) erzeugt mit QDRand eine Verteilung, die signifikant in der Form abweicht!

    Dateien

    • usr-rnd.prg

      (1,01 kB, 2 Mal heruntergeladen, zuletzt: )

    Gibt es denn andersherum einen Weg, sicher "gute" Seeds für RND(1) zu erzeugen?

    Es *gibt* keine guten Seeds für RND(1). Die maximal "erreichbare" Periodendauer von RND(1) liegt bei ca. 2^16 Iterationen, was aber weit unter den Möglichkeiten eines 32-Bit-Seeds liegt.

    Ja, man kann natürlich einen ganz eigenen PRNG nehmen, aber der wird wohl noch langsamer sein...

    Langsamer? Nicht unbedingt. QDRand aus den Numerical Recipies ist relativ schmerzfrei umsetzbar:

    (Quelle: Making a random number in Assembly - Page 2 - Denial (sleepingelephant.com))


    Etwas weiter unten in dem Thread ist dann noch eine Anpassung des obigen Maschinencodes an die USR()-Funktion. Da brauch' ich nur gerade noch ein paar Minuten, um die relevanten Aufrufe vom VC-20 an das C64-BASIC anzupassen ...

    Kleines Update: den kritischen Seed kann man auch auf etwas kompaktere Weise erzeugen:

    Code
    1. 1 X=RND(-8008/10371):CLR
    2. 2 PRINTRND(1)
    3. 3 PRINTRND(1)
    4. 4 PRINTRND(1)
    5. 5 PRINTRND(1)

    8008/10371 entsteht bei der Kettenbruchentwicklung von 1658186197/2147483648 und ist bereits so genau genug, daß die beiden Zahlen für den C64 im Fließkommaformat gleich sind.


    Im Startpost mußten die beiden größeren Zähler und Nenner noch als Ergebnis je einer Multiplikation dargestellt werden, da sie mit ihren mehr als 9 Dezimalstellen bei der Umwandlung nach Fließkomma gerundet wurden. Dieser Zwischenschritt ist mit 8008 und 10371 nicht mehr nötig.

    Ich dachte, der [Archimedes] hat einfach 4096 Farben, aus denem man (maximal 256 gleichzeitig) auswählen kann.

    Nein, ganz so einfach war es nicht. Der VIDC im Archimedes hatte nur 16 Palettenregister. Im 256-Farben-Modus bestimmte das obere Nibble das oberste Bit von Blau, die beiden obersten Bits von Grün und das oberste Bit von Rot, und das untere Nibble wählte dann eins der Paletten-Register aus, von dem dann die fehlenden Bits übernommen wurden.


    Also praktisch eine Mischung aus Direkt-Color (wie heutzutage bei 16- oder 24-Bit-RGB) und Palette.


    Die 16 Paletten-Register wurden in den 256-Farben-Modi nun standardmäßig so belegt, daß wie oben von mir beschrieben, je 2 Bits für RGB und auch noch mal 2 Bits für einen Weißanteil zuständig waren. Praktisch RGBI im Quadrat. Es war aber schon noch möglich, die Paletten-Register umzuprogrammieren, das wurde aber praktisch nicht genutzt (und die Benutzeroberfläche ging in den 256-Farben-Modi von eben dieser Standard-Palette aus).


    In den 16-, 4- oder 2-Farb-Modi gab es dann natürlich schon die freie Wahl aus den 4096 Farben. Aber damit war man auch nicht besser als der Amiga. Einige Sachen, die dem Amiga völlig easy von der Hand gingen (u.a. der Dual-Playfield-Modus) bedeuteten reichlich viel Speichergeschaufel beim Archimedes. Na, ja, der ARM war da schon schnell genug für, und auch "dick" angebunden an den Videospeicher: sowohl ARM-CPU als auch der Videochip hatten einen 32-Bit-Datenbus und einen Speichertakt von mindestens 8 MHz (spätere Geräte mehr). Auch da allerdings wieder: CPU, Video und - mit Einschränkungen auch - Sound mußten sich diese Speicherbandbreite teilen, und gerade bei höheren Auflösungen und Farbtiefen wurde die CPU schon merkbar ausgebremst. Dafür kam bei späteren Archimedes ein Cache in der CPU dazu.

    Meinst Du, die Farbübergänge beim C64 wären schlechter, wenn die Farben unterschiedliche Farbamplituden hätten?

    Stärker gesättigte Farben innerhalb eines Farbverlaufs könnten zumindest den Eindruck erwecken, daß dort an der Stelle eine irgendwie geartete Lichtquelle anstelle einer rein reflektierten (Körper-)Farbe vorhanden ist.


    Heißt, wenn schon mehrere Sättigungsstufen, dann bitte auch für alle Farben und Helligkeiten.


    Die Standard-256-Farben-Palette beim Archimedes hatte als Beispiel effektiv die Wahl zwischen 4 Sättigungsstufen (0%, also Graustufen, und 30%/60%/90% grob gesagt). Aufgebaut war die Palette aus je 2 Bit für Rot/Grün/Blau und dann konnte als "TINT" noch ein kleiner Weißanteil hinzugefügt werden.


    Das machte dann 16 Graustufen, 6 leicht gesättigte Farbtöne mit 12 unterschiedlichen Helligkeiten, 12 mittel gesättigte Farbtöne mit 8 unterschiedlichen Helligkeiten und 18 stark gesättigte Farbtöne mit 4 unterschiedlichen Helligkeiten. 16 + 6x12 + 12x8 + 18x4 = 256. Es wurde also fast der komplette RGB-Farbraum abgedeckt. Eigentlich ideale Voraussetzungen für Pixelkünstler, aber was ich seinerzeit so an Kunstwerken oder Spielegrafik auf dem Archimedes gesehen hatte, sah eher nach Kindergekrakel aus, mit übertriebenem Pillow-Shading, damit man alle 256 Farben auch einmal sieht. ;(


    Dabei wäre ohne weiteres schon so etwas wie hier gegangen:



    Das Bild nutzt exakt die genannte 256-Farb-Palette vom Archimedes und ist vom Original ausgehend durch einen meiner Konverter gelaufen. Es werden fast alle Farben der Palette benötigt (genau gesagt, 246 Stück), was bedeutet, daß nicht nur unterschiedliche Helligkeiten und Farben sondern auch Farbsättigungen vorkommen. Letztere ergeben sich aber jeweils aus den Objekten selbst und nicht wie oben von mir beschrieben, durch Lichtquellen.

    Und daß die doch glatt nicht binär-kompatibel waren.

    Dieser Teil hat sich jedenfalls bis heute nicht geändert.

    Jo, tatsächlich. Mittlerweile ist aber die Software-Technik bzw. -Abstraktion einerseits und die verfügbare Rechenleistung andererseits soweit, daß fehlende Binär-Kompatibilität keine große Rolle mehr spielt. Alternativ ist (hoffentlich bzw. ohnehin) der Source-Code verfügbar, und dann wird eben neu kompiliert. :)

    Das ist doch lächerlich.

    Das ist mal eine gute Grundlage um eine Diskussion weiter zu führen. Nicht.


    Mal so ganz allgemein gefragt - bist Du zu der genannten Zeit überhaupt schon aktiv dabei gewesen? Also nicht nur auf irgendeiner Kiste, die vielleicht auch ein paar Schulkameraden (für einfacheren Software-Tausch) hatten, gedaddelt, sondern auch selbst programmiert, vielleicht mal über den Tellerrand geschaut, was es so an anderen Rechnern gibt, etc.?


    Da gab es (jetzt mal aus meiner Sicht) eben den C64 von Commodore, vorher noch den VC-20, versuchsweise mal den C116, der eine oder andere Mitschüler hatte einen Atari XL, der ZX81 war vom Onkel bekannt, irgendwo geisterte noch der Schneider CPC herum. Apple war für das was geboten wurde viel zu teuer. Wenn ein Mitschüler Zugriff auf einen PC hatte, dann war der "sponsored by Papa", hieß der Elternteil hatte den aus beruflichen Gründen zuhause und wenn der PC mal nicht gebraucht wurde, durfte man mal dran. Nichts zum angeben also.


    Erst Anfang der 90er wurden PCs für Privatanwender interessant. Da war Commodore aber ohnehin schon auf dem absteigenden Ast: nach OCS und ECS kam noch AGA und damit (ab Werk) nichts mehr. Ein Kumpel von mir hatte seinen A2000 mit Picasso II und 68040 Turbokarte hochgerüstet und damit lief dann z.B. Nemac IV richtig flott.


    Für mich kamen nach dem C128 weder ein Amiga noch Atari irgendwie in Frage, PCs waren auch noch zu teuer und dann habe ich mir eben einen Acorn Archimedes geholt. Und damit 1991 schon alles gemacht, worauf hier teilw. beim Mega65 fast 30 Jahre später gehofft wird, daß man das damit auch hinbekäme (sorry in die Richtung. Ist aber so.).


    Heute geht auf dem PC nun mal alles recht easy. Beliebig viele Farben bei beliebig hoher Auflösung (so daß bei weiterer Verbesserung Auge und Gehirn keinen wesentlichen Unterschied mehr feststellen könnten) - da muß man dann schon damit rechnen, daß einem schlichtweg das Gefühl dafür verloren geht, warum das nicht damals™ auch schon alles möglich war. Und daß es überhaupt unterschiedliche Rechner gab. Und daß sich diese unterschiedlichen Rechner glatt erdreisteten, unterschiedlich gute Video- und Sound-Subsysteme zu haben. Und daß die doch glatt nicht binär-kompatibel waren. Also echt.


    Ansonsten gilt: Hätte... Hätte... Fahrradkette. Die Entwicklung hat nun mal den Lauf so genommen und so ist es jetzt. Mit der Dominanz der Intel/AMD-Schiene bei PC-Hardware, Apple dahinter, irgendwo dann noch andere Workstations. Windows und div. Unix-Derivate (incl. Apple) bei Betriebssystemen. Kommerziell spielen die früheren Home-Computer keine Rolle mehr, außer bei auf Emulation basierten Nachbauten um die Spiele von damals auf den HD-Wohnzimmerfernseher zu bringen.

    Der Flickerfixer wäre völlig überflüssig gewesen wenn man den ab 1989 verfügbare ET4000 Chip eingesetzt hätte. Die Zeit wo jeder das Rad neu erfinden musste waren langsam vorbei.

    1. Commodore hatte eine eigene Halbleiterfertigung und konnte sich diesen Luxus erlauben.

    2. Der Videospeicher war beim Amiga zu der Zeit direkt an die CPU angebunden. Beim PC mußten die CPU-Daten über den Erweiterungsbus laufen, was bis zur Einführung von PCI ein echter Flaschenhals in der PC-Architektur war.


    Es stand also gewiß für Commodore außerhalb jeder Diskussion, irgendwelche Videochips von Fremdherstellern für die eigenen Rechner zu verwenden. Dann hätte Commodore gleich komplett auf die Fertigung von PCs umschwenken können und wäre dann eben nur noch einer von mehreren Dutzend damaliger PC-Klitschen gewesen.


    ...


    Was ein Videochip ausgeben kann, hängt neben bewußt gewählten Design-Interna und der ansprechbaren bzw. nutzbaren Speichergröße wesentlich von der verfügbaren Speicherbandbreite ab! Die liegt beim VIC-II im C64 im Extremfall bei 2 MB/s (während einer Badline und alle 8 Sprites sind aktiv). Bei normalen Grafikmodi ohne Sprites sind das eher nur 1 MB/s - und dann hat man eben Hires-Pixel, 320x200 und 2 Farben pro Kachel, oder Multicolor-Pixel, 160x200 und 4 Farben pro Kachel - den Extra-DMA für die Attribut-Daten kann man da fast vernachlässigen. NUFLI nutzt alle 8 Sprites über die ganze Bildschirmhöhe, und FLI in jeder zweiten Rasterzeile - da sind wir dann bei ca. 1,5 MB/s während der sichtbaren Bildhöhe. Was an verfügbaren Zyklen auf dem Bus für die CPU übrigbleibt (ca. 40 von 130 für jede Doppelrasterzeile) wird benötigt um Farben und Speicherort der Overlay-Sprites umzuschalten. Das ergibt dann einen 320x200 Hires-Modus, dem man bei gängiger Motiv-Wahl kaum noch irgendwelche Einschränkungen ansieht, geradeso als ob die 16 Farben frei verwendbar wären. Sind sie zwar tatsächlich nicht, aber dazu muß man schon genauer hinschauen.


    Um jetzt aber bei 320x200 16 Farben frei darstellen zu können, müßte der VIC-II in jeder Mikrosekunde (so viel Zeit nimmt eine Zeichenbreite ein) 8 Pixel mit je 4 Bit aus dem Speicher lesen. Anders gesagt, über eine Speicherbandbreite von 4 MB/s verfügen. Die hat er nicht und damit gehen erst recht keine 256 Farben bei 320x200 (die dann gleich 8 MB/s bräuchten).


    Ähnliche Kalkulationen kann man auch für den Atari XL und spätere Rechner machen.


    ...


    Insgesamt ist das Video-Subsystem des C64 m.M.n. aber ein gelungener Kompromiß aus Auflösung und Farbanzahl bei verfügbarem Speicher. Der Fokus liegt tendenziell bei der Auflösung und kommt damit den Erfahrungen der Wahrnehmungspsychologie sehr entgegen: Auge und Gehirn bewerten die Helligkeit stärker als den Farbton und erst danach kommt noch die Farbsättigung. Hat wohl diesen Grund, daß alle Farben (mit Ausnahme der 3 Graustufen, Weiß und Schwarz) beim C64 gleich gesättigt sind. :D

    Noch als Ergänzung, die Funktion wurde ja schon in den Beiträgen 3 und 4 hinreichend geklärt:

    [...] ich würde wohl entweder unsichtbare "Basiczeilenende"-Charaktere auf den Bildschirm schreiben, [...]

    dazu hat man im Bildschirmspeicher selbst gar keine Möglichkeit. Jeder der 256 Bildschirmcodes könnte in irgendeiner Form dort auftauchen, es ist also kein Code für ein Markierungszeichen übrig. Bleibt also nur eine externe Datenstruktur, welche verbundene Zeilen nachhält, und genauso wurde das im Bildschirmeditor des KERNALs realisiert.


    Diese Methode ist übrigens so flexibel gehalten, daß sie auch mehr als 2 Zeilen verbinden kann. Auf dem C64 wurde das nicht (mehr) genutzt, wohl aber auf dem VC-20 (wodurch dort 4x22 = 88 Zeichen Zeilenlänge gehen). Beim C16/C116/+4 und C128 sind aufgrund der Window-Technik im Editor auch noch eine größere Anzahl verbundener Zeilen möglich, wenn ein relativ schmales Window eingestellt wird (plus, der C128 hat einen Zeileneingabepuffer mit 160 Zeichen!).


    Auf dem Bildschirm sieht alles ganz normal aus, als ob 2 Zeilen da sind, aber es ist in Wirklichkeit nur eine.

    Ist einer der lustigeren Fallstricke beim Eintippen, ja. :D

    Nach meinen neuesten Versuchen steht da tatsächlich das erste Byte des Funktionsterms, [...]

    Ich habe keine Ahnung, warum es nützlich sein könnte, das erste Byte des Funktionsterms noch einmal im Deskriptor vorrätig zu halten.

    "Nützlich" in irgendeiner Form braucht dieses Byte nicht zu sein. Der geschriebene Wert entsteht durch einen Seiteneffekt im Code des DEF-FN-Befehls wenn der Variablendeskriptor angelegt wird. Ein Blick in ein gängiges ROM-Listing ist da völlig ausreichend, um das aufzuklären: $B3B3 -

    Um $B44F sauber ausführen zu können, müssen fünf Bytes auf dem Stack liegen. $B3CF..$B3DA legen den Zeiger auf die Variable und den Zeiger auf den FN-Programmtext drauf. Das erste PHA bei $B3CE legt irgendeinen Wert auf den Stapel - einigermaßen zufälligerweise handelt es sich hier um das Byte, welches die Testroutine bei $AEFF abholt, die zuvor den Programmtext auf "=" (Token $B2) geprüft hat (in AAY64 steht aufgrund der automatischen Kommentarerstellung Test ',' - ich hab das oben korrigiert).


    Edit: Ich sehe, Du bist selber drauf gekommen. :D

    Ich bring hier gerade mal mein Beispiel aus dem Denial-Forum (jetzt vollständig) an:


    Der Code ist jetzt exemplarisch nach $2000 assembliert worden. Es spielt aber keine Rolle, wohin man ihn (in freien Speicher) lädt - er läuft überall! Das darf jeder gerne mit einem Monitor des eigenen Vertrauens und dessen L- und T-Befehle überprüfen.


    In ähnlicher Weise, wie hier die zwei Adressen für das Operanden-Feld des Trampolins und die eigentliche Startadresse der IRQ-Routine durch konstante Offsets von der "Ich bin hier!"-Adresse abgeleitet wurden, kann man auch Zugriffe auf Tabellen, Sprünge und Unterprogrammaufrufe synthetisieren. Das geht alles, es ist nur halt super-umständlich.

    Dateien

    • beispiel.prg

      (85 Byte, 2 Mal heruntergeladen, zuletzt: )

    Am Ende ist das dann optimiert nur noch ein einziger Befehl an der bekannten Adresse: RTS


    [...]


    [...] habe ich irgendwas übersehen, [...][?]

    Die Rücksprungadresse auf dem Stack ist nicht mehr sicher, wenn Du sie erst nach der Rückkehr vom JSR abholst. Ein Interrupt könnte sie jederzeit überschreiben.


    Die kleine Routine in der Zeropage kann man wohl verschmerzen. Auch so wird der Code, welcher auf die gefundene Adresse Bezug nimmt auch immer komplizierter sein als wie wenn der Code für eine feste Adresse assembliert wurde. Dem 65xx fehlen dazu einfach die nötigen Adressierungsarten, aber das war ja auch schon gesetzt.


    In Denial haben Jason Justian und ich mal eine frei verschiebbare IRQ-Routine gebastelt, die Diskussion dazu ist hier: IRQ Installer - Denial (sleepingelephant.com) - und da hab ich auch meine Hilfsroutine her.