Assembler Crossentwicklung

  • Durch diese Zusammenstellung - insbesondere für Assembler Quelltexte:

    https://github.com/ggnkua/Atari_ST_Sources


    Bin ich da mal durchgegangen und ca. 20h Arbeitszeit später, habe ich für mich die "Spreu vom Weizen" getrennt.

    Ich wollte nur Quelltexte, die sich auch assemblieren ließen und wo das Programm dann auch lief.

    Fast überall mussten absolute Pfade bei INCBIN angepasst werden.

    Einige Unterverzeichnisse waren leer?

    Manchmal ließen sich die Sourcen nicht assemblieren, manchmal lief das Programm dann nicht.

    Alle diese Fälle habe ich in meinem Auszug NICHT übernommen.


    Als Assembler habe ich dafür VASM genutzt unter Linux Mint 20.1: http://sun.hasenbraten.de/vasm/

    Getestet wurde mit Hatari 2.3.1 mit den jeweiligen Einstellungen für ST-RGB/Mono, STE-RGB oder Falcon.

    In x bzw. xx sind Scripten zum assemblieren von main.s drin. Ich habe in den Verzechnissen, die Hauptquelldatei jeweils auf main.s kopiert, ggf. angepasst und dann assembliert.

    Da findet sich dann auch das main.tos.


    Es sind eine Handvoll STE Projekte darunter und eher 2 Handvoll Falcon. Der Rest (90%) dann für Standard ST-RGB.


    Fragen:

    Warum ist Programm X... unter Verzeichnis A-F?

    Weil X... irgendwo unterhalb ursprünglich mal A-F in der ursprünglichen Zusammenstellung war ;)


    Hinweise:

    A-F/Mono_Emu: Emuliert Mono Auflösung auf RGB-Mid: Mono_EM6 benötigt Blitter. Damit läuft dann aber z.B. Super Breakout auf RGB.

    A-F/life: Ist eine schöne Life Simulation für ST-Mono.

    G-O/Hemoroids/Sources: 0.PRG ist Assembler in FR. Kann man aber einfach bedienen. Die *.S können damit assembliert werden. MONST.PRG ist ein Disassembler.


    Eine schöne Seite mit Tools/Tipps ist auch: https://dhs.nu/files.php?t=single&ID=138


    und: https://nguillaumin.github.io/perihelion-m68k-tutorials/


    Die DHS Demosysteme für ST und Falcon sind ebenfalls in meiner Zusammenstellung enthalten.


    Viel Spaß damit und evtl. steigt ja doch nochmal jemand in die Assembler/Intro/Demo Programmierung für Atari ST/Falcon ein ;)


    Ich stelle demnächst dann auch Tips/Links/Sourcen zusammen für:

    • C+SDL Entwicklung
    • Basic (STOS) Entwicklung


    Stay tuned.


    VG Peter

  • Ich habe mich die letzten Tage viel damit beschäftigt und als aller erstes hatte ich ein "Grundgestell" programmiert, dass ich den ganzen Code nicht jedes Mal nochmal neu tippen muss.. Und includieren von Sources finde ich immer so unübersichtlich. Das Grundgestell beinhaltet folgende Subroutines, die man eigentlich immer braucht.


    1. Sichern von Auflösung und Farbpalette.

    2. Wieder herstellen von Auflösung und Farbpalette für einen sauberen Programmabschluß.

    3. Supervisor Modus einschalten

    4. User Modus einschalten

    5. Auf Tastendruck warten


    Es ist jetzt nichts bahnbrechendes. Ich hab erst vor 14 Tagen mit 68k Assembler angefangen, aber vielleicht hilft es ja dem ein oder anderen ?! Ich hab mir darin noch ein paar Notizen gemacht, die lass ich stehen... wir sind ja unter uns.. :)


    EDIT: ui, da hatte sich ein Fehler eingeschlichen... ich hatte mich vertippt und ein Word mit einem Longword vertauscht... Anbei die neue Version...


    Gruß Jan

  • Ich habe jetzt mal ein kleines Programm geschrieben zum Thema Interrupt. Um genauer zu sagen, im Bezug auf den vertical blanc interrupt. Für die, die es nicht wissen.... Vertical blanc ist die Zeit, wenn der Elektronenstrahl unten rechts abschaltet, dann diagonal nach links oben wandert und dann wieder einschaltet. Diese Zeit kann man für Rechen- bzw. Programmroutinen nutzen. Die Vectoradresse ist beim Atari ST $70 für den vblanc. Wird ein verticalblanc ausgelöst (das sind bei einer 50 Hz Kiste 50 Mal in der Sekunde), wird jedes Mal diese Routine ausgelöst, deren Adresse an der Speicherstelle $70 steht.


    Um es nochmal zu sagen. Bei solchen Operationen muß der Prozessor stets im Supervisor-Modus sein - anderenfalls gibts Bomben...


    Ohne jetzt nochmal näher auf zB das Sichern von Auflösung und Palette etc. einzugehen, macht mein Programm folgendes:


    - Ändere die Farbe 15 (Schriftfarbe beim ST) auf weiß

    - Schreibe irgendwas auf den Bildschirm

    - Lese die die Sprungadresse aus der Vectoradresse $70 aus und sichere den Wert an einer definierten Speicherstelle

    - Schreibe statt dessen die Sprungadresse zu meiner vblanc Routine in die Vectoradresse $70

    - Endlosschleife.... hier schnurrt jetzt das Programm, bis man eine Taste drückt, dann beendet es..

    - Schreibe den Usprünglichen vblanc wieder zurück

    - Stelle Auflösung, Colorpalette wieder her

    - Beende das Programm.


    Während des Interrupts läuft dann folgendes ab.


    Die Hintergrundfarbe ist Farbnummer 0, das Register hat Word-Länge. Bei jedem vblanc wird der Wert in ein Datenregister gelesen und darin dann inkrementiert. Die oberen 13 Bit werden ausmaskiert, so dass das Programm nur mit den unteren 3 Bit arbeitet, die die Helligkeit für die Farbe blau beinhalten.


    Also wenn man das Programm startet steht da die ganze Zeit die Textmeldung in weiß und die Farbe blau wabert die ganze Zeit von dunkel nach hell..


    Im Anhang ist der Source und die TOS-Datei



    Gruß Jan

  • Dann ist es ja nicht mehr weit bis zum Rasterbalken ... :)

    Ja, daran arbeite ich. Man muß halt auch immer die Zeit dafür haben :) ... Die Abfrage der Rasterzeile ist beim Atari ST etwas komplizierter als zB beim C64. Beim ST muß man parallel den Timer B laufen lassen, der die Zeilen mitzählt und dann erst an der richtigen Stelle auslösen... Da blick ich noch nicht ganz durch... dann muß ja auch immer noch das Timing stimmen, sonst siehts beschissen aus oder funktioniert überhaupt nicht...


    Gruß Jan

  • Bitte was ist ein Rasterbalken ?

    Gruß Torsten

    BFZ MFA, ZX80Core, AX81, ZX81, ZX81NU, Spectrum+, Harlequin, MSX VG8010, Amstrad NC100, Cambridge Z88, C64, C128D, Amiga 500 & 1200, Atari Portfolio, HP200LX, IBM PC5155, TP755c, TP755cx, T20, T41, T61, PS/2 (Model 40SX), PS/2E, Accura 101, Apple //e, Sharp PC1401 & PC1403H, TI59 m. PC-100c, HP48SX & HP48GX


    ::matrix::

  • zum Anschauen worum es geht


    https://www.youtube.com/watch?v=x59N57eY8pY&t=254s


    https://www.youtube.com/watch?v=jCZBe2_2nig (nur die erste Minute)


    https://www.youtube.com/watch?v=HJUMqU_QOc0&t=1102s (eine ziemlich advanced'te Variante)


    https://www.youtube.com/watch?v=RNLZJBuvCCs&t=50s (von 2020)



    Beim ST muß man parallel den Timer B laufen lassen, der die Zeilen mitzählt und dann erst an der richtigen Stelle auslösen... Da blick ich noch nicht ganz durch... dann muß ja auch immer noch das Timing stimmen, sonst siehts beschissen aus oder funktioniert überhaupt nicht...


    Eigentlich kann man das auch mit Warteschleife machen. Dafür reicht dann eine Tabelle wo man die Werte reinschreibt und dann ein Register runterzählt. Zumindest für einen Balken ist das problemlos. Damit es schön aussieht, muß man aber auch mit Timer eine Ausgleichstabelle haben - war zumindest am Plus/4 so. Da wurde dann die Werte je Line solange angeglichen bis die hellen Rasterpunkte weg waren oder hinterm Bildschirmrand verschwanden.

  • Ja, um eine Rasterbar zu erstellen, mach ich das natürlich auch mit einer Tabelle.. Erstmal geht es jetzt darum einen sauberen Rasterzeileninterrupt hin zu kriegen.. :)


    Bzgl. Demos.. dieses ganzen Sinescroller etc. werden ja auch alle über Tabellen und Speedcode gemacht. Lange Zeit dachte ich immer, das würde in Echtzeit berechnet werden.. :D Aber dafür sind die Rechner ja viiiieeeeeel zu langsam...


    Bei Demos gilt oft: "....nichts ist, wie es scheint..."


    Gruß Jan

  • Das stimmt schon: Es ist oft ganz verblüffend anders gemacht, als man so denkt. Ist gewissermaßen eine Form von Magie oder Zauberei. :)



    PS: ich kann mir eigentlich gar nicht vorstellen, daß es nicht in dem Atari Videochip ein Register gibt, was die aktuelle Rasterzeile "weiß". Denn diese Variante mit dem indirekten Timer nach Vblank Interrupt ist schon irgendwie eine ganz schön eigenartige Sache; zwei verschachtelte Interrutpts für eine Farbinformation ist auch gut aufwendig.

  • Das stimmt schon: Es ist oft ganz verblüffend anders gemacht, als man so denkt. Ist gewissermaßen eine Form von Magie oder Zauberei. :)



    PS: ich kann mir eigentlich gar nicht vorstellen, daß es nicht in dem Atari Videochip ein Register gibt, was die aktuelle Rasterzeile "weiß". Denn diese Variante mit dem indirekten Timer nach Vblank Interrupt ist schon irgendwie eine ganz schön eigenartige Sache; zwei verschachtelte Interrutpts für eine Farbinformation ist auch gut aufwendig.

    Es ist aber tatsächlich so. Der Videocontroller, also der "Shifter", hat dafür kein Register. Der Timer B zählt mit den Rasterzeilen, aber nicht von 0-199, sondern einfach immer weiter. Wenn ich also über den vblanc interrupt dem Timer B jedes Mal resette, friemele ich mir den Timer B um als Rasterzeilencounter... Das ist der Kenntnisstand bis jetzt. Ich habe auch schon einen Code dazu geschrieben. Aber bis jetzt flackert nur eine Rasterzeile in der Mitte...


    Grundsätzich ist es schwierig, da gute Informationen zum ST zu finden. Kein Vergleich zu anderen Retrorechnern, wo man ja quasi mit Tutorials bombadiert wird... Aber das macht es natürlich auch spannender.


    Gruß Jan

  • PS: ich kann mir eigentlich gar nicht vorstellen, daß es nicht in dem Atari Videochip ein Register gibt, was die aktuelle Rasterzeile "weiß". Denn diese Variante mit dem indirekten Timer nach Vblank Interrupt ist schon irgendwie eine ganz schön eigenartige Sache; zwei verschachtelte Interrutpts für eine Farbinformation ist auch gut aufwendig.

    Es ist aber tatsächlich so. Der Videocontroller, also der "Shifter", hat dafür kein Register. Der Timer B zählt mit den Rasterzeilen, aber nicht von 0-199, sondern einfach immer weiter. Wenn ich also über den vblanc interrupt dem Timer B jedes Mal resette, friemele ich mir den Timer B um als Rasterzeilencounter... Das ist der Kenntnisstand bis jetzt. Ich habe auch schon einen Code dazu geschrieben. Aber bis jetzt flackert nur eine Rasterzeile in der Mitte...

    Hm - ja, das stimmt so weitgehend. Der Shifter bietet aber immerhin ein paar read-only Register, die einem mitteilen, welche Bildschirmadresse zuletzt vom Shifter gelesen wurde:


    $FF8205: Video Adresszähler Highbyte

    $FF8207: Video Adresszähler Midbyte

    $FF8209: Video Adresszähler Lowbyte


    Für den Bereich auf dem Screen, wo wirklich Daten dargestellten werden, lässt sich darüber schon auf eine Position innerhalb des Bildes schließen. Aber das taugt für Rasterbars natürlich nix, da man diese Register einerseits ständig "pollen" müsste, was hoffnungslos ineffizient ist, und man andererseits darüber nur schwer genau den Anfang einer Rasterzeile ableiten kann, da diese ja außerhalb des Bereichs liegt wo der Shifter wirklich Daten liest...


    Also der Weg über den Timer B, den man dann in jedem VBL resettet ist schon der richtige! Das hab ich damals zumindest auch immer so gemacht! ;)


    Der Vollständigkeit halber: den Horizontal Blank gibt's übrigens auch direkt auf einem 68k Interrupt Eingang und nicht nur am MFP! Am 68k hängt der HBL aber auf Interrupt Level 2 - und der hat ja niedrigste Priorität. Ich erinnere mich noch daran, das meine ersten Raster-Versuche damals mit diesem HBL waren. Aber einerseits wird dieser Interrupt dann wirklich bei jedem einzelnen Horizontalen Blank ausgeführt (und nicht nur beim x-ten, was man über den Timer B konfigurieren kann), und andererseits haben alle anderen Interrupts (VBL, MFP) ja eine höhere Priorität - was die Rasterbars immer zum Zittern gebracht hat. Das war für mich eine Sackgasse. Aber über den Timer B funktionierts!

  • Ja, da haste natürlich Recht ! Das war auch mein Gedankengang - wenn man es über die Adresszähler machen würde, müsste er ständig kucken, wo er ist. Also polling, wie du schon sagtest.. Letzten Endes ineffizient und das Timing des Programms würde es gewiss nicht danken..


    Gruß Jan

  • Hab gerade mal was rausgesucht, wo ich das glaube ich über den Timer B gemacht hatte:

    Intro zur Module Compilation #11 - Magrathea


    Einerseits für die Rasterbars im Hintergrund von der Voxel-Landschaft, und andererseits erinnere ich mich noch dran, dass ich auch bei dem Feuer-Effekt zwischen dem Scroller oben drüber und dem Feuer unten drunter die Palette umgeschaltet hab.


    Wobei: bei dem Voxel zittern die Bars im Hintergrund schon ein bissel... :grübel: Keine Ahnung was ich da vermurkst hab :nixwiss:

  • so so ... vom Sirius also ... :)


    Sehr schön !


    Die Wellenberge sind auch hübsch, v.a. wenn sie Echtzeit gerechnet sind. Das Flimmern im Rasterhintergrund sieht man aber auch nur, wenn man so explizit drauf hingewiesen wird.

    Hehe - jaja - Jugendsünden im Scrolltext :tüdeldü:


    Ja, die Berge sind schon in Echtzeit berechnet. Jedenfalls die Darstellung derselben. Die Höhenkarten ansich sind allerdings vorberechnete Daten - aber das ist ja normal so bei Voxel-Grafik. Ich fand damals Comanche auf dem PC so krass - und dachte: das muss doch auf dem ST auch gehen :mrgreen:


    Ja stimmt - das mit dem Flimmern in den Rastern ist nicht sehr extrem - trotzdem hat's mich heute gewundert, da mir das gar nicht in Erinnerung war. Mit HBL statt Timer B dürfte es aber wirklich noch deutlich krasser gewabert haben...

    Ich muss mal schauen ob ich den Source Code noch finde/habe.

  • So, ich hatte wieder etwas Zeit und ich habe nun eine Routine bzgl. Rasterzeileninterrupt programmiert. Zum grundsätzlichen Ablauf bzw. gewonnene Erkenntnisse:


    1. Die einzelnen Rasterzeilen, also die Scanlines werden von oben nach unten gezählt. Ganz wichtig ist hierbei von 0-199. D.h. die Gesamtsumme darf 199 nicht überschreiten, sonst flackerts !

    2. Der Vblank wird nach der letzten Scanline ausgelöst. Die Routine wird während des vblanks, also während der Elektronenstrahl von unten rechts nach oben links wandert, ausgeführt und startet dann ab der ersten Scanline.

    3. Der Timer B wird mit jeder aufsteigenden Scanline, also von oben nach unten, dekrementiert ! D.h., das Zählen ist hier gegenläufig. Wenn der Timer B bei 1 (NICHT BEI 0) angekommen ist, löst er die dementsprechende Routine aus, deren Vectoradresse an $120 steht.

    4. Der Timer B muss im Event-Mode, also Ereignismodus sein. Nur dann kann er als Rasterzeilenzähler funktionieren.

    5. Vor dem Setzen eines neuen Wertes für den Timer B, muß dieser durch das löschen die niederwertigsten Bits im Kontrollregister gestoppt werden.

    6. Der Timer C löst alle 5ms aus. Dieser muß deaktiviert werden, weil es sonst verticale Positionsstörungen der Rasterzeilen gibt.

    7. Ich habe eine ganze zeitlang nach einer Lösung zur horizontalen Sync gesucht. Das ist leider beim ST nicht ohne einen Trick möglich. Man lädt den Rasterzeilencounter aus der Timeradresse und vergleicht ihn mit dem gesetzten Wert. Vorher hat man den Timer aber wieder gestartet. Solange der Counter in der gleichen Zeile wie der gesetzte Wert ist, tut er erst mal nicht. Da aber im Hintergrund der Timer weiterläuft und erneut eine Zeile dekrementiert, ist das Programm in der Lage, diese Schleife zu verlassen und setzt erst dann die neue Farbe bzw, führt dann erst die gewünschte Subroutine aus. Das bewirkt, dass es erst in der nächsten Scanline los geht.



    Der Ablauf: (nur ein Beispiel, von der Farbdarstellung nicht im Bezug auf meinen Sourcecode unten)


    1. Im Hauptprogramm wird der Timer B eingerichtet. Auf Eventmode geschaltet und die Interrupts zugelassen.

    2. Vertical blank löst aus und setzt einen Counter in Timer B, zB mit dem Wert 40. Außerdem legt er die Routine in die vectoradresse für den (ersten) Timer B Interrupt fest. Wir setzen die Hintergrundfarbe auf rot.

    3. Jetzt wird er erstmal die ersten 40 Rasterzeilen in rot darstellen.

    4. Hat der Zähler dann den Wert 1, wird die Interruptroutine an Vectoradresse $120 ausgeführt.

    5. In dieser Routine wird der Timer gestoppt und auf einen neuen Wert, zB 50, gesetzt und wieder im Eventmode gestartet. Es wird die nächste Routine an die Vectoradresse $120 geschrieben. Wir setzen auf die Hintergrundfarbe blau.


    6. Bis jetzt haben wir 40 Scanlines in rot und dann 50 in blau.


    7. Hat der Zähler dann den Wert 1, wird die Interruptroutine an Vectoradresse $120 ausgeführt.

    8. Jetzt wird der Timer wieder gestoppt und wieder auf einen neuen Wert gesetzt, sagen wir 109. Wir starten wieder im Eventmode und setzen die Hintergrundfarbe auf grün. Hier setzen wir KEINE neue Routine an Vectoradresse $120. Wir haben ja jetzt 199 Scanlines dargestellt und es folgt der Vblank-Interrupt und es geht von vorne los.

    9. Wir sollten jetzt auf unserem Bildschirm 40 rote, 50 blaue und 109 grüne Scanlines sehen...


    ...mal schauen, was ich als nächstes programmiere.. :)



    Momentan beschäftigt mich noch das einbinden von Musik...


    Gruß Jan

  • Hübsch, hübsch.


    Interessante Konstruktion. Verstanden hat man es wohl, wenn man weiß, warum beim zweiten Malen des Screens oben wieder mit rot angefangen wird - und nicht gelb weiterläuft.



    Man kann es übrigens auch positiv sehen: Die Rasterlines sind dann wenigstens mal in ordentlicher Reihenfolge durchnummeriert - nämlich die 200 oben am Bildschirmrand und die 0 halt unten, weshalb sich damit eine schöner X-Y "Plot" ergibt. Bei den allermeisten BASICs und Co sind ja die Y Werte so, daß 0 oben ist und nach unten gezeichnet wird und richtigherum wird es dann nur mit Y=200-Y.


    Die Variante mit dem Laden der Adresse in a0 und dann schreiben der Werte für die Rasterzeile indirekt sieht irgendwie eigenwillig aus. Kann man da nicht auch die z.B. #$80 direkt hineinbekommen - z.B. so ala move.b #$80,$adresse ??


    Das ist aber nur die Nebenbemerkung. Viel interessanter wäre die Rasterzeile noch aus einer Tabelle auszulesen (man muß halt den Index "merken" und jeweils erhöhen). Und erst diesen Wert zu schreiben. Dann kommt man nämlich, in dem Fall eines Balkens, sehr schön schnell zu einer Animation - was ja vielleicht schon die gesuchte nächste Aufgabe sein könnte. ;)

  • Hübsch, hübsch.


    Interessante Konstruktion. Verstanden hat man es wohl, wenn man weiß, warum beim zweiten Malen des Screens oben wieder mit rot angefangen wird - und nicht gelb weiterläuft.

    Ich habe auch bewusst ein Bild vom Monitor gemacht und keinen Screenshot vom Emulator gemacht, weil man da den Overscan sieht bzw. den Rahmen. Im Emulator geht es direkt oben mit rot los, denn die erste Rasterzeile ist ja rot. Weil der Atari diesen Rahmen hat, läuft das gelbe unten weiter und kommt oben wieder. Es gibt auch einen Trick, den Rahmen auf zu klappen und benutzbar zu machen.. Das steht hier schon auf der "to do"-Liste.. aber erst später mal... :)


    Die Variante mit dem Laden der Adresse in a0 und dann schreiben der Werte für die Rasterzeile indirekt sieht irgendwie eigenwillig aus. Kann man da nicht auch die z.B. #$80 direkt hineinbekommen - z.B. so ala move.b #$80,$adresse ??

    Code
        clr.b      $fffffa1b.w        ; Stop Timer*
        move.w     #$fa21,a0          ; Timer B counter address to reg A0
        move.b     #80,(a0)           ; Next Raster
        move.l     #new_timerb,$120   ; Set new vector address
        or.b       #$08,$fffa1b       ; Set Timer to event mode
        move.b     (a0),d0            ; Move value from Reg. A0 to D0
    wait_vbl: 
        cmp.b      (a0),d0            ; While value in Register A0 is the same like in Register D0...    
        beq        wait_vbl           ; DO NOTHING.. So, it waits for the next line.
        bsr        raster1            ; Branch to subroutine raster1

    Ich hatte es in der Tat vorher genau so, wie du beschrieben hast. Also "move.b #$80,$fa21". Das war bevor die "wait_vbl"-Schleife drin war. Diese bewirkt, dass er die Farbe noch nicht ändert, bevor die aktuelle Rasterzeile nicht ungleich der zählenden Rasterzeile ist. Sonst ändert sich nämlich die Farbe mitten in der Rasterzeile und da flackerts dann. Ich hätte also vor der Schleife den Wert aus dem Speicher dann lesen müssen, was einen weiteren/erneuten Speicherzugriff zur Folge hätte, welcher länger dauert als der Zugriff auf ein Register. Ich versuche immer möglichst viel in Registern zu arbeiten. Davon hat der 68k ja genug.


    Das ist aber nur die Nebenbemerkung. Viel interessanter wäre die Rasterzeile noch aus einer Tabelle auszulesen (man muß halt den Index "merken" und jeweils erhöhen). Und erst diesen Wert zu schreiben. Dann kommt man nämlich, in dem Fall eines Balkens, sehr schön schnell zu einer Animation - was ja vielleicht schon die gesuchte nächste Aufgabe sein könnte. ;)

    Ja, das ist richtig. Ich hab ja extra schon aus den Interruptroutinen in Subroutinen springen lassen, wo nur die Farbe 0 bzw. die Hintergrundfarbe gesetzt wird - zwecks Übersichtlichkeit. Die Farben dienen ja nur der Visualisierung - abgehackt in einer Farbe in voller Intensität, dass man klipp und klar sieht, wo es losgeht mit der dementsprechenden Routine. Selbstverständlich kann man hier auch eine Routine reinschreiben, die die Farben zB alle 2 Scanlines ändert, um so ein paar schöne Rasterbalken dar zu stellen. Aber eins nach dem anderen.. :)


    Gruß Jan

  • Endlich hab ich wieder etwas Zeit gefunden, um weiter zu machen. Nach Vertiefung der bisher gewonnen Erkenntnisse, wollte ich jetzt unbedingt ein kleines Intro programmieren mit Rasterblanken, Schrift, einer Grafik und Sound.... und habe es geschafft... :)


    1. Zu den Rasterbalken.


    Die Farbwerte für die obere Rasterbar stehen in Tabelle 1, die Farbwerte für die untere Rasterbar stehen in Tabelle 2. Wie man nun verschiedene Farben in einem einzigen Farbregister via Interrupt auf dem Bildschirm darstellen kann, war ja bereits klar (siehe oben).

    Die große Frage war jetzt, wie bzw. an welcher Stelle man den neuen Farbwert aus der Tabelle in das Farbregister lädt. Naheliegend ist da ja der horizontal blank interrupt. Die Vectoradresse für den hblank liegt beim ST an $68. Der Plan war, diese Vectoradresse zu verbiegen und damit in eine eigene hblank routine zu springen, die einfach den nächsten Farbwert aus der Tabelle lädt, um sie dann dementsprechend in der nächsten Scanline aus zu geben...


    Allerdings ist es beim ST so, dass der hblank-Interrupt die niedrigste Priorität in der IRQ Tabelle hat. D.h. jeder andere Interrupt ist wichtiger ! Eine Realisierung über den hblank ist mir nicht gelungen. Klar, ich hätte während dessen alle anderen Interrupts ausschalten können, aber dann wäre mein Timer IRQ ebenfalls futsch gewesen und der Code wäre definitv nicht gelaufen.


    Manchmal verrennt man sich in eine vermeindliche Lösung. Dabei war es letzten Endes doch so einfach. Ich habe die Adresse von der Tabelle in ein Adressregister geschrieben und diese mit jeder Scanline incrementiert und dann dementsprechend darstellen lassen. In dem Schleifenzähler befindet sich dann die Anzahl der Tabellenwerte. Da die Rasterbars sehr hoch sind, habe ich jeden Wert zweimal in die Tabelle geschrieben.



    2. Zum Text


    Den Text habe ich einfach mit der gemdos Routine 9 auf den Bildschirm geschrieben. Die Farbe des Textes kommt aus dem Farbregister 15. Dieses musste ich in jedem vblank refreshen, da sonst der Wert der Farbtabelle des Bildes noch drin gestanden hätte. Je nachdem, welches Bild man verwendet bzw. welche Farbtabelle das Bild verwendet, hätte die Textfarbe dann nicht mehr gepasst andernfalls.



    3. Zur Grafik


    Die Grafik habe ich natürlich hier geklaut. Das in Windows abgespeicherte png file habe ich auf einen, im Paint erzeugten ,320x200 schwarzen background gelegt. Dieses habe ich dann im GIMP geöffnet, die Farbpalette auf 16 Farben reduziert und es als *.IFF Datei exportiert. Am Atari habe ich dann in Degas Elite die IFF-Grafik geöffnet und dann wieder als *.PI1 File exportiert, welches ich gut im Assembler verwenden kann.


    Da ich oben und unten die Rasterbars habe, mußte ich nicht das komplette Bild in den Framebuffer laden, sondern nur die Anzahl der benötigten Scanlines. Diesen Offset musste ich natürlich an die "ladende Adresse" und an die "speichernde Adresse" schreiben, das ist soweit klar.


    Die große Frage für mich war jetzt allerdings, an welcher Stelle im Programm ich das Bild lade....


    Wenn ich es im Hauptprogramm lade, wird es durch die Interrupts zerstört, weil sich jedes Mal die Farbregister ändern. Also war ich davon überzeugt, das komplette Bild nebst Farbregistern in TimerB Interrupt zu laden und dar zu stellen. das Bild hat 140 Scanlines. Mit Schleifencode hab ich maximal 25 Scanlines dargestellt bekommen. Mit Speedcode habe ich 70 Scanlines dargestellt bekommen. Habe ich mehr Scanlines erzwungen, hat das ganze Bild geflackert.. Also auch doof..


    Nach einigen, weiteren vergeudeten Stunden meiner Lebenszeit bin ich dann auf die Lösung gekommen. Die Lösung ist, das komplette Bild inkl. passender Farbregister am Anfang zu laden... und dann im Interrupt jedes Mal nur die Farbregister zu refreshen.. :)



    4. Zum Sound


    Die Musik hab ich natürlich irgendwo geklaut. Dass in Demos die Musik anderer verwendet wird, ist ja gang und gäbe. Selbstverständlich gehört es da zum guten Ton bzw. es ist als Pflicht zu betrachten, den Programmierer/Komponist derselben wenigstens in der Demo zu erwähnen. Leider weiß ich in dem Fall nicht, wer die Musik gemacht hat und dem zu Folge kann ich es nicht angeben. Ich mache auch hier kein Tutorial, geschweige denn verfolge ich kommerzielle Absichten - ich teile lediglich meine Lernerfolge und auch die Rückschritte mit den Vereins-/Forums-Mitgliedern.


    Da es so schön einfach ist, habe ich in diesem Intro eine *.MUS-Datei verwendet. MUS-Dateien haben einen Player im header. Die Play-Routine wird im vblank jedes Mal aufgerufen. Initialisiert wird die Musik mit 1 im Datenregister D0 und anschließendem Anspringen der Musikdatei ohne irgendeinen Offset - beendet wird sie mit 0 im Datenregister D0 und anschließendem Anspringen der Musikdatei ohne irgendeinen Offset.




    Das Programm läuft zwar, ABER:


    Was mir noch gedanklich Probleme macht, ist das Timing der Interrupts. Wenn ich die Timer Zähler der Interrupts zusammen zähle, sollte ich doch auf 200 Scanlines kommen. Wenn ich die Schleifenzähler der Scanlines für die beiden Rasterbars und der Grafik zusammen zähle sollte ich doch ebenfalls auf 200 kommen. So funktioniert es aber nicht. Ich musste mit diesen Werten "spielen", um es hin zu kriegen.

    Klar, braucht der Computer auch zwischendurch Zeit, um das Programm an sich ab zu arbeiten, aber das müsste der ST doch schaffen mit 8MHZ.



    Hier noch ein Screenshot:




    Gruß Jan

  • Ich hab wieder Zeit gefunden, ein bißchen weiter zu machen....


    Dieses Mal zwischendurch ein anderes Thema. Ein "Hallo Welt"-Programm, aber mal anders....


    Das Ziel war es, mit einem alternativen Zeichensatz zu arbeiten aus einer Fontdatei, welche Ziffern bzw. Buchstaben in einer Pixelgröße von 32x32 enthält. Sicherlich hat sich der ein oder andere schonmal gefragt, wie man eine große Schrift, ggf. als Laufschrift auf den Bildschirm bekommt. Man könnte jetzt irgendwie eine dementsprechende, vorgefertigte Grafikdatei einbinden, die den gewünschten Text enthält. Allerdings ging es mir darum, ein Programm zu schreiben, dessen Source einen "Klartext" enthält, der dann dementsprechend mit den grafischen Fontzeichen dargestellt wird...


    Da meine künstlerisch gestalterischen Fähigkeiten doch sehr begrenzt sind und ich eher "technisch kalt" bin, musste ich wieder auf "Diebestour" gehen und wurde auf der Suche nach schicken Fonts hier fündig:


    https://github.com/ianhan/BitmapFonts


    Die Wahl fiel auf diese File:



    32X32-F1.png


    Das PNG-File habe ich in GIMP wieder als IFF exportiert und am Atari in Degas Elite als PI1-Datei exportiert.


    Wenn man sich jetzt dieses Bild anschaut und eine ASCII-Tabelle daneben hält, wird man feststellen, dass die Zeichensetzung (jetzt mal abgesehen von den grafischen Spielereien) die gleiche Reihenfolge hat. Der Unterschied ist, dass diese Tabelle erst bei dem 32. ($20.) Zeichen ASCII anfängt, also SPACE.


    Also muß man im Grunde genommen nur den Ascii-Wert aus seinem Text-String laden und von diesem 32 subtrahieren, dann weiß man schon, welche bzw. die wievielte Zelle man aus der Grafik laden muss, um das dementsprechende Zeichen dar zu stellen.


    Jetzt ist es allerdings so, dass dieses Fontfile nur eine Grafikdatei, will heißen nur ein Bild, ist. Deswegen muß dieses erstmal in Zellen unterteilt werden, um dann später darauf zugreifen zu können. Dazu müssen wir kurz auf die Darstellung eingehen.


    Ein Zeichen ist 32x32 Pixel. Die Auflösung des ST´s ist 320x200. Der ST hat pro Scanline 160 byte. Geteilt durch 10 ergibt das eine Zeichenbreite von 16 byte !! Die Zeichenhöhe beträgt 32 byte. Um einen Buchstaben zu laden, muß ich also zwei verschachtelte Schleifen programmieren. Auf der x Achse werden 16 byte übertragen, dann werden in der Quelldatei und im Framebuffer 144 byte (160 byte insgesamt minus 16 byte Zeichenbreite) addiert. Somit springt er dann in die nächste Scanline wieder an den Anfang und ich kann erneut die X-Achsen-Schleife laufen lassen.


    Soviel zu der Darstellung bzw. zu dem kopieren der Grafik. Die große Frage war jetzt, wie ich das mit der Zellenunterteilung mache. Nun, ich habe ganz einfach eine Tabelle erstellt, die die Koordinaten der Buchstaben enthält gemäß der "Ascii-Reihenfolge".

    Jetzt ist es allerdings so, dass der ST eine Adressbreite von 24 Bit hat. Dem zu Folge konnte ich die Tabelle natürlich nicht in Bytes oder Words definieren. Ich musste die Tabelle in Longwords definieren. Nur so ist man in der Lage, die passende Adresse zu seinem Ascii-Zeichen an zu springen.


    Nächstes Problem... Wenn ich eine "1" darstellen will, ist das Ascii $31, wenn ich eine "2" darstellen will, ist das Ascii $32. Der Ascii-Wert wird also lediglich um 1 inkrementiert. Wir sind im nächsten Byte. Da die Tabelle aber aus Longwords besteht, reicht es nicht in das nächste Byte zu springen, man muß ins nächste Longword springen. Ein Longword besteht aus 4 bytes, also muß man das aktuelle Byte einfach mit 4 multiplizieren, dass er in der Tabelle an die richtige Stelle springt. Die elegantere Lösung ist hier ein zweifacher logical shift left... :)


    Also nochmal in Stichpunkten:


    - Lade den darzustellenden Buchstaben aus dem Textstring. zB ein 'A'

    - Dieses A steht im Speicher natürlich als $41

    - Subtrahiere $20. Das ergibt $21 = die 33. Stelle in der Grafik (siehe Bild) - ACHTUNG, wir fangen bei 0 an zu zählen !

    - Multipliziere das Ergebnis mit 4

    - Lade den Anfang der "Translation table" in das Adressregister

    - Addiere das obige Ergebnis zu dem Anfang der "translation table" und ermittele somit die Position des Buchstabens.



    Hier noch das Ergebnis:




    Bei diesem Programm ist es so, dass es immer nur in die erste Zeile schreibt. Es ist auch völlig egal, wie lange der Textstring im Source ist.

    Trotzdem habe ich eine kleine Prüfroutine eingebaut, welche tatsächlich erst später von Bedeutung wird. Nach dem Ende des Textstrings wird dieser mit dem Byte 00 abgeschlossen. Damit weiß das Programm, dass nichts neues mehr kommt. Also so, wie mit dem $ bei CP/M oder MS-DOS.

    Selbstverständlich ist der nächste Schritt, ein Textscroller zu programmieren und da ist diese Funktion natürlich von Bedeutung.



    Gruß Jan

  • Auf dem Atari ist das was, was eigentlich der Blitter machen kann. Wenn man weiß wie ...



    Ansonsten: man kann die Schleife vereinfachen, indem man das Bild anders einliest - und zwar so, daß zunächstmal aller Zeilen des ersten Buchstabens hintereinander im Speicher liegen, dann nachfolgend alle Zeilen des zweiten Buchstabens usf. (dann klappt natürlich der Blitter nicht mehr, aber man spart ein paar Additionen für Wechsel in der Grafik).

    Außerdem kann die Tabelle auch einfach weiter vorn beginnen - dann muß man nicht die $20 subtrahieren. Da man niemals vorhat unterhalb von $20 was zu lesen, ist auch relativ egal was da drunter steht (unterhalb im RAM).



    Die BitMap Fonts Sammlung ist recht beeindruckend.

  • Auf dem Atari ist das was, was eigentlich der Blitter machen kann. Wenn man weiß wie ...

    Ganz genau so ist es ! Bevor ich aber auf die Customchips und "Spezialfunktionen" der verschiedenen ST-Modelle eingehe, wollte ich erstmal im "Grundstock" programmieren. Viele STs haben gar keinen Blitter. Deswegen, eins nach dem anderen... :)


    Ansonsten: man kann die Schleife vereinfachen, indem man das Bild anders einliest - und zwar so, daß zunächstmal aller Zeilen des ersten Buchstabens hintereinander im Speicher liegen, dann nachfolgend alle Zeilen des zweiten Buchstabens usf. (dann klappt natürlich der Blitter nicht mehr, aber man spart ein paar Additionen für Wechsel in der Grafik).

    Außerdem kann die Tabelle auch einfach weiter vorn beginnen - dann muß man nicht die $20 subtrahieren. Da man niemals vorhat unterhalb von $20 was zu lesen, ist auch relativ egal was da drunter steht (unterhalb im RAM).


    Bzgl. der Tabelle war mein Gedankengang, dass ich dann letzten Endes ja 20 Longwords, bzw. 80 bytes unnötigerweise im Speicher hätte. Da dachte ich ne Subtraktion wäre eleganter, man muss den Rechner ja auch mal rechnen lassen... :)

    Wie ich das Bild anders reinladen kann, weiß ich nicht. Natürlich hört sich das plausibel an. Aber bedenke, dann müsste ich das Bild nochmal zwischen kopieren...



    Gruß Jan

  • Hey, cool, dass du dich mit sowas auf dem ST ganz neu befasst! :mrgreen::thumbup: Gefällt mir! Bin gespannt, wohin es führen wird! ;)


    Ansonsten: man kann die Schleife vereinfachen, indem man das Bild anders einliest - und zwar so, daß zunächstmal aller Zeilen des ersten Buchstabens hintereinander im Speicher liegen, dann nachfolgend alle Zeilen des zweiten Buchstabens usf. (dann klappt natürlich der Blitter nicht mehr, aber man spart ein paar Additionen für Wechsel in der Grafik).

    Wie ich das Bild anders reinladen kann, weiß ich nicht. Natürlich hört sich das plausibel an. Aber bedenke, dann müsste ich das Bild nochmal zwischen kopieren...


    Der "Trick" ist, das nicht erst beim Reinladen im Assembler-Code zu machen, sondern eher einen kleinen Converter zu basteln, der einem die Daten "offline" aus dem PI1 Bild einfach in eine andere Reihenfolge umformatiert und in einem eigenen Format abspeichert. Also das Format hat dann natürlich nichts mehr direkt mit PI1 zu tun, sondern ist was "eigenes". Aber das eigene Format kann dann eben optimal auf das jeweilige Problem zugeschnitten sein.


    Früher hab ich solche Daten-Umformatierungs-Dinge direkt auf dem ST mit nem 20-Zeiler in Omikron Basic gemacht. Einfach die *.PI1-Datei in einen mem-block 1:1 reinladen, dann per peeks/pokes in einen zweiten mem-block umformatieren und dann den zweiten als eine *.DAT Datei auf Disk/Platte schreiben. Leider hab ich Omikron inzwischen komplett verlernt. :nixwiss:


    Im Assembler-Code liest man dann nur das *.DAT file ein und weiß ja, in welcher Reihenfolge die Daten da drin stehen. Für so einen Font - oder generell für "Tilesets" ist es am einfachsten, wenn die kompletten Daten des einzelnen Buchstaben direkt nacheinander im Speicher liegen. Und dann einfach ein Buchstabe nach dem anderen im Speicher liegt. Dann wird der Assembler-Code etwas einfacher & performanter. Man braucht nur die Adresse des Anfangs des ersten Zeichens und berechnet dann den Offset zu dem Start des gesuchten Zeichens per left-shifts des Ascii Codes. Die Longword-Tabelle kann man sich so komplett sparen.

    Und auch das abziehen der $20 vom Ascii-Wert ist nicht nötig, wenn man als Startadresse der gesamten Daten-Tabelle nicht die nimmt wo die sichtbaren Zeichen im Speicher anfangen, sondern einfach einmalig $20 Zeichen-Größen (also $20*16*32 Bytes) im Speicher von der Startadresse abzieht und dann immer diese "korrigierte" Startadresse nimmt.


    Achja - in dem Bilder-Lade-Thread hatte ich ja schon geschrieben, dass mein Ziel beim PNG-zu-Atari Konvertieren nicht nur ein *.PI1 File als Ergebnis-Format ist, sondern "eigene Formate". Was ich damit meinte war genau sowas wie hier beschrieben: eine beliebig große PNG-Datei mit einem "Tileset" entsprechend so umformatieren, dass die einzelnen Tiles einfach nacheinander im Speicher liegen...

    (Mit meinem Konverter bin ich aber auch noch nicht fertig. Ich kann inzwischen aber schon mal beliebige PNGs per libpng einlesen. Aber das eigentliche konvertieren in die Bitplanes und Umsortieren im Speicher fehlt noch...)

  • muß mal bißchen angeben ... ;) ... und sagen, daß der FONT wirklich toll aussieht _! Insbesondere "in motion". Und das Beste dran ist aber, daß man, wenns einmal läuft, sich die ganzen anderen Fonts in gleicher Größe als direkter Ersatz eignen - man muß nur das Bildchen austauschen. Das ist schon wirklich eine andere Qualität des Bastelns, wenn man solche Sachen, quasi "da hat". (Der knallgrüne 32x32 sieht auch schräg aus, z.B.)




    zum Anschauen in Bewegung -> out0.mp4.zip



    PS, ist natürlich schnöde Hochsprache und daher gar nicht vergleichbar, außerdem auf einem GHz Gerät - nicht 8MHz. Das macht manches einfacher.

  • Hey, cool, dass du dich mit sowas auf dem ST ganz neu befasst! :mrgreen::thumbup: Gefällt mir! Bin gespannt, wohin es führen wird! ;)

    Mir ist es ganz wichtig, nicht nur zu sammeln, sondern auch, mich mit den Geräten zu befassen. In den letzten 2 Jahren habe ich 6502-und 8080/8085 Assembler gelernt, außerdem ein bißchen 8086 Assembler. 68k Assembler jetzt erst seit 4 Wochen. Der Atari ST war mein erster Computer. Ich habe zu Weihnachten 1991 einen 1040 STE bekommen. Zu meinem persönlichen Jubiläum "30 Jahre Atari" wollte ich den STe mal wieder etwas "würdigen" und habe mir gedacht, da jetzt auch mal was zu programmieren. Zu Hochsprachen finde ich irgendwie nicht hin. Eigentlich wollte ich C lernen am Atari. Aber irgendwie finde ich Assembler am meisten spannend !


    Selbstverständlich sind meine Codes nicht perfekt ! Allerdings ist es mir immer ganz wichtig, zu verstehen was ich mache.. Ich mache hier also kein blindes copy und paste mit vorhandenen Sources. Ich skizziere hier am Tisch den Programmablauf auf einem Blatt Papier und setze es dann in Code um.

    Wenn es läuft, ist es erstmal gut. Perfektionieren kann ich immer noch... Wichtig ist für mich erstmal, dass es funktioniert und dass ich es selber hinbekommen habe..


    Früher hab ich mich übrigens auch mal kurzzeitig mit Omikron-Basic beschäftigt. Bin dann aber relativ schnell zu GFA-Basic gewechselt. GFA-Basic ist super. Irgendwo hier hab ich sogar noch die ganzen Lehrbücher von damals aus der internetlosen Zeit.. :) Aber auch wenn ich es toll fand, will ich mich heute nicht mehr damit befassen..


    Gruß Jan

  • Mir ist es ganz wichtig, nicht nur zu sammeln, sondern auch, mich mit den Geräten zu befassen. In den letzten 2 Jahren habe ich 6502-und 8080/8085 Assembler gelernt, außerdem ein bißchen 8086 Assembler. 68k Assembler jetzt erst seit 4 Wochen. Der Atari ST war mein erster Computer. Ich habe zu Weihnachten 1991 einen 1040 STE bekommen. Zu meinem persönlichen Jubiläum "30 Jahre Atari" wollte ich den STe mal wieder etwas "würdigen" und habe mir gedacht, da jetzt auch mal was zu programmieren. Zu Hochsprachen finde ich irgendwie nicht hin. Eigentlich wollte ich C lernen am Atari. Aber irgendwie finde ich Assembler am meisten spannend !

    Klingt gut! Ich hab das ähnlich vor. Ich möchte gar nicht eine 3-stellige Anzahl alter Rechner zusammen sammeln um sie dann nur rumstehen zu haben. Ich hab lieber eine überschaubare "kleine" Menge von vielleicht 10-15 unterschiedlichen alten, lauffähigen Rechnern und entsprechende Programmier-Doku dazu und würde gerne auf den verschiedenen Rechnern was coden (hardware-nah in Assembler). Aber irgendwie ist selbst dafür die Zeit gerade eher zu knapp, so dass ich meistens doch nur den ST anmache - der ist meistens direkt aufgebaut und den kenne ich ja schon ;)


    Aber zumindest stehen ein paar entsprechende Bücher zur Hardware des ST, Amiga, C64, Atari XL und klassischem PC und 68k, x86 und 6502 Assembler schon griffbereit im Regal.