Bildschirm füllen beschleunigen

  • Bildschirm füllen beschleunigen

    Hallo zusammen,
    Ich bin jetzt schon einige Tage am rumschauen aber mit dem Problem werde ich noch nicht so ganz firm. Ich weiß zwar aus manchen Spielen das es bestimmt besser geht aber irgendwie komme ich nicht dahinter vielleicht kann mir von euch ja wer einen Anstoß geben.

    Ich möchte nahezu den kompletten Bildschirmspeicher mit Zeichen füllen, welche als DATA vorliegen. Das soll dann ein Level ergeben.
    Mein Code dazu sieht wie folgt aus:

    Quellcode

    1. 160 dim c1(3):dim h(3)
    2. 180 h(0)=230:h(1)=32:h(2)=122:h(3)=120: rem pO1,53:
    3. 190 c1(0)=1:c1(1)=7:c1(2)=10:c1(3)=3
    4. 200 a=55296+120:b=1144:c=49152:jr=56320:sr=54272:pe=.:ps=8
    5. 12000 rem *sub print levels
    6. 12001 read sx:read sy:px=sx:py=sy:
    7. 12002 pO 53264,pz:pO 53248,sx:pO 53249,sy:rem ?"px";px;"py";py
    8. 12005 ta=ti
    9. 12050 for i=0to879:reads:pOa+i,c1(s):pOb+i,h(s):next
    10. 12100 tb=ti:di=tb-ta: rem ? di
    11. 12200 return
    Alles anzeigen
    Interessant ist eigentlich nur die Zeile 12050. In dieser Schleife werden die Daten in s eingelesen und dann das Zeichen und die Farbe anhand von s gepoket.
    Die Werte der Zeichen liegen in h() und die Farbe in c1(). Das funktioniert auch soweit, leider ist es abartig lahm, zum Füllen des Bildschirmspeichers benötigt es ~15 Sekunden.
    Das dauert zwar nur am Anfang jedes Levels solange aber dennoch ist irgendwie doch etwas zu lahm. Ich benutze auch Sprites.

    Ich habe auch schon mal etwas gelesen, dass man den Bildschirmspeicher umhängen kann. Könnte man da nun z.B. 2 Bildschirme halten und während man im 1 Level (1 Bildschimspeicher) ist, nach und nach die Daten in den 2 Bildschirmspeicher laden z.B: von 2048 ($0800) beginnend, dann beim Beenden des 1 Level den Bildschirm-Pointer auf den zweiten Speicherbereich setzen und dann für das 3te Level wieder nach und nach den Speicher des 1 Bildschirm füllen. Ich meine aber auch gelesen zu haben, das man dann auch die Spritepointer umhängen muss und werden dann die Daten ab 2048 nicht von meinem Basic Programm überschrieben? Muss ich dann auch die Startadresse von meinem Basicprogramm verschieben? Es wäre echt toll falls mir das mal jemand etwas detailierter erklären könnte.
    Besten Dank schonmal
    Grüßle Thomas
    Experto credite - 3d-druck-community.de
  • Das ging aus der Unterroutine deines Beispielprogramms nicht hervor. Da wurden 880 Bytes mit einem Zeichen und einer Farbe gefüllt. (Edit: ich hab' das READ S übersehen, nun gut. Die 'Indirektion' über H() und C1() macht das aber auch nicht schneller) Aber O.K.

    Was spricht dagegen, einfach die komplette Hintergrundgrafik mit PRINT auszugeben? Du brauchst die Daten dazu noch nicht mal extra in Zeichenketten packen, Du hältst einfach für jeden Level eine Unterroutine vor, die diesen Level auf den Bildschirm schreibt. Mit den Steuerzeichen kannst Du auch für jedes Zeichen eine eigene Farbe vorgeben.

    Schau dir einfach mal die Abtipp-Listings in den Computer-Magazinen aus den 80'ern an. Die haben das da nicht anders gemacht.
  • Meine Erfahrung in BASIC ist, daß POKE, PEEK, IF...THEN und GOTO sehr langsam sind. Für das Füllen des Bildschirms rat ich dir dringend den Befehl COLOR & CHAR zu benutzen. Wenn du es noch schneller willst, definier deine Tiles vor mit Strings und setze diese dann via CHAR ein. Noch schneller geht es leider nicht, meiner Erfahrung nach.


    Ups, ist ja die C64er Ecke.
    Für das Setzen der Zeichen benutz vordefinierte Strings und POKE 211,x:POKE 214,y:SYS58732:PRINT"DEINSTRING". Oder wenn du es noch schneller möchtest, aber nicht komplett in die MS reinrutschen möchtest:

    Quellcode

    1. 0 rem ...cursor positionierung...
    2. 1 z=704:for i=0 to 25:readj:pokez+i,j:next
    3. 2 data 32,253,174,32,158,183,138,72,32,253,174,32,158
    4. 3 data 183,104,168,24,32,240,255,32,253,174,76,164,170
    5. 4 rem beispiel:sysz,0,0,"text"
    Diese Variante ist noch schneller, als POKE211..214 und SYS! Definier Tiles mit jeweiligen Farbcode vor und setz diese dann.
  • Noch ein Nachtrag (nur um das noch etwas abzurunden):

    Was die Geschwindigkeit angeht ist das durchaus Unterschiedlich, ich habe das mal ausprobiert:

    Läuft das Programm in Interpreter, so ist das natürlich deutlich schlanker im Code und spart damit Platz wenn man es oft benutzt.

    Möchte man das Compilieren (davon mal abgesehen, dass die Compiler oft ein Problem mit dem Konstrukt sys x,z,a... haben und dies als Extension betrachten, was im Prinzip ja auch richtig ist), so ist das nachher Tatsächlich langsamer (und zwar grob geschätzt um den Faktor 2) als eine Variante mit X=10:Y=19:GOSUB möglichstKleineZeilennummer. Dort poken und den sys und return, danach Print.

    Gruß, Gerd
    Wer andern eine Bratwurst brät braucht ein Bratwurstbratgerät.
  • Hallo Leute,
    danke schonmal für die Hinweise! Ich habe das so gemacht, weil ich damit sehr bequem ein Level/Map mittels dem C64 Studio erstellen kann, daher habe ich diesen Ansatz gewählt. Also wenn ich das richtig verstehe, dann sollte ich anstatt die Daten per READ auszulesen und dann in den Bildschirmspeicher zu POKEn, lieber jede Zeile des Levels z.B. als String halten und dann per PRINT auf den Bildschirm bringen bzw. mit dem sys Befehl von @RKSoft ausgeben.
    @RKSoft, dein Programm funktioniert bei mir soweit, jedoch geht der sys 704 Befehl nach dem ausdrucken immer in eine neue Zeile,
    das heißt, dass nach der letzten Zeile des Levels, also Zeile 25 der gesamte Bildschirm 1 Zeile nach oben geschoben wird. Kann man das irgendwie unterbinden?
    Experto credite - 3d-druck-community.de
  • zumili schrieb:

    jedoch geht der sys 704 Befehl nach dem ausdrucken immer in eine neue Zeile
    Das macht PRINT auch ...

    ... es sei denn, man schließt die Parameterliste mit einem Semikolon ab. Das unterbindet bei PRINT, und vermutlich auch bei diesem Hilfsprogramm (welches einfach den PRINT-Befehl im Interpreter nach der Cursor-Positionierung aufruft), den Zeilenumbruch und der gedachte Cursor bleibt direkt hinter der Ausgabe stehen. Also z.B. PRINT"TEST";:PRINT"TEXT" druckt TESTTEXT und dann geht es erst in die nächste Zeile.

    Das allerletzte Zeichen geht am einfachsten aber tatsächlich mit zwei POKEs in den Bildschirmspeicher+Farb-RAM. Es gibt zwar auch einen Trick, das alleine mit PRINT zu machen - der ist aber ziemlich äh:

    - zunächst druckt man das letzte Zeichen in der vorletzten Bildschirmposition,
    - dann geht man mit Cursor links zurück auf die Position dieses Zeichens,
    - nun schiebt man mit INSERT (=CHR$(148)) das Zeichen in seine Position und zum Schluß,
    - schreibt man das vorletzte Zeichen aus (und geht am besten direkt danach mit HOME in ungefährliche Gefilde).

    Da sind mir die zwei POKEs doch lieber. ^^
  • RKSoft schrieb:

    Meine Erfahrung in BASIC ist, daß POKE, PEEK, IF...THEN und GOTO sehr langsam sind.
    Wobei die Langsamkeit von POKE und PEEK nicht an POKE und PEEK selbst liegt, sondern am Drumherum: Bei einem simplen "POKE A,V" werden die Argumente ausgewertet und dann der Speicherzugriff gemacht, das ist also durchaus so schnell wie es nur geht - aber damit hat man halt nur eine einzige Speicherstelle geändert. Will man nun eine Zeile, oder den ganzen Bildschirm, oder mehrere I/O-Register beschreiben, so baut man sich eine Schleife drum herum: Der Interpreter muss nun die Schleife abarbeiten und zusätzlich für jedes Byte die POKE-Anweisung erneut interpretieren. Dass das langsamer ist als mit PRINT etliche Zeichen in einem Rutsch auszugeben, sollte klar sein - aber das heißt eben nicht, dass PEEK/POKE irgendwie suboptimal implementiert seien oder so.
    Yes, I'm the guy responsible for the ACME cross assembler
  • Hey zusammen, danke für die Hinweise, also das mit dem Semikolon hat funktioniert, stand da auf dem Schlauch, da ich nicht wusste, das dieser Sys-Befehl wie PRINT funktioniert. Wäre es also schneller per READ DATA zu lesen und dann zu PRINTen anstatt zu POKEn?
    Ich schätze mal ja, aber nur wenn man ganze Zeilen printet und nicht nur jedes einzelne Zeichen. Dann muss man aber zuerst den String aus den Daten zusammenbauen und das dauert ja auch wieder Zeit... hmm

    Ich habe nun so oder so Probleme mit meinem Code bekommen als ich ein neues Level, also weitere Level DATA hinzugefügt habe. Entweder es kommt ein ?OUT OF DATA ERROR obwohl noch genügend Daten verfügbar sein sollten, oder aber ein ?FORMULA TOO COMPLEX ERROR. Mir kommt es so vor als ob die Datenmenge einfach zu viel wird und irgendwas überschrieben wird?! Ebenfalls funktioniert das copy/paste von Notepad++ in Vice nicht mehr ab ca. 16.700 Bytes. Pro Level werden ja 1000Byte an Daten benötigt und es kommen nochmal ~970 Kommas dazu. Das ist zuviel, entweder ich specke die Level nun soweit ab, dass es nur noch zwei Tiles gibt, dann kann ich diese bitweise in Bytes ablegen und diese dann als DATA speichern, anschließend per READ lesen und dann PRINTen oder aber komplett per Strings abspeichern und komplett ohne DATA leben. Hmm da steh ich nun ich armer Tor, und bin so klug als wie zuvor :). Naja nicht ganz etwas weniger dumm bin ich nun ^^
    Experto credite - 3d-druck-community.de
  • ... das gleiche gilt im übrigen auch für IF...THEN - zwischen IF und THEN wird ganz normal ein Ausdruck ausgewertet und auf ungleich Null verglichen. Das kann man mit einem komplizierten Ausdruck schon beliebig langsam gestalten, aber die Ausführungszeit ist entweder nur die des Ausdrucks, wenn er 0 ist und der THEN-Zweig nicht ausgeführt wird oder Ausdruck plus THEN-Zweig falls die IF-Bedingung wahr ist. Der eigentliche Code von IF im Interpreter besteht nur aus einem dutzend Befehlen.

    Bei bedingten Ausdrücken wird hingegen immer der komplette Ausdruck mit allen Bedingungen ausgewertet - das ist sicher nicht schneller als eine IF-Kette, Beispiel:

    Quellcode

    1. 10 FORT=1TO1000:NEXT
    2. 11 T1=TI
    3. 12 FORS=1TO100:A=-ATN(S)*((SAND3)=0)-LOG(S)*((SAND3)=1)-SIN(S)*((SAND3)=2):NEXT
    4. 13 T2=TI
    5. 14 FORS=1TO100
    6. 15 A=0
    7. 16 IF(SAND3)=0THENA=ATN(S)
    8. 17 IF(SAND3)=1THENA=LOG(S)
    9. 18 IF(SAND3)=2THENA=SIN(S)
    10. 19 NEXTS
    11. 20 T3=TI
    12. 21 PRINTT2-T1
    13. 22 PRINTT3-T2
    Alles anzeigen
    Die erste FOR-S-Schleife braucht 719 Jiffies, die zweite hingegen nur 275.

    Und bei GOTO hat die Zeilennummern-Auswertung optimierte Routinen, die nur mit Ziffern und 16-Bit-Zahlen arbeiten, das wird nicht mit der normalen Formelauswertung gemacht! Trotzdem ist es natürlich langsamer (aber nicht langsam per se), wenn man mit 5-stelligen Zeilennummern hantiert. Und daß die Zeilennummern-Suche bei einem Rückwärtssprung vom Programmanfang aus und bei einem Vorwärtssprung ab der Zeile mit dem GOTO sucht, sollte man eben wissen. Da hängt die Geschwindigkeit eben von der Anzahl der Link-Pointer ab, die die Routine überspringen muß.

    Heißt, eine enge, oft ausgeführte GOTO-Schleife (evtl. von einer Sub-Routine) gehört möglichst an den Programmanfang und nicht irgendwo tief unten im Programm.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Mike ()

  • zumili,

    mit der von dir gewählten Struktur hast Du nunmal vor allem das Problem, daß in BASIC V2 der Zugriff auf DATAs nicht wahlfrei ist. RESTORE setzt den Lesezeiger auf den Programmanfang, Du kannst aber nicht mit RESTORE N eine bestimmte DATA-Zeile "anfahren".

    Nochmal: es ist einfach und problemlos möglich, die Hintergundgrafik für einen Level-Hintergrund mit PRINT-Befehlen auszugeben. Du hast dann erstmal irgendwo einen Verteiler mit ON LV GOTO aaa,bbb,ccc,... der fix angesprungen wird, wenn ein neuer Level gezeichnet werden soll. Dieser Verteiler wird mit GOSUB angesprungen und die Levelausgaben beenden ihre Arbeit nach zwei dutzend PRINT-Befehlen mit einem RETURN.
  • zumili schrieb:

    Hey zusammen, danke für die Hinweise, also das mit dem Semikolon hat funktioniert, stand da auf dem Schlauch, da ich nicht wusste, das dieser Sys-Befehl wie PRINT funktioniert. Wäre es also schneller per READ DATA zu lesen und dann zu PRINTen anstatt zu POKEn?
    Ich schätze mal ja, aber nur wenn man ganze Zeilen printet und nicht nur jedes einzelne Zeichen. Dann muss man aber zuerst den String aus den Daten zusammenbauen und das dauert ja auch wieder Zeit... hmm

    Ich habe nun so oder so Probleme mit meinem Code bekommen als ich ein neues Level, also weitere Level DATA hinzugefügt habe. Entweder es kommt ein ?OUT OF DATA ERROR obwohl noch genügend Daten verfügbar sein sollten, oder aber ein ?FORMULA TOO COMPLEX ERROR. Mir kommt es so vor als ob die Datenmenge einfach zu viel wird und irgendwas überschrieben wird?! Ebenfalls funktioniert das copy/paste von Notepad++ in Vice nicht mehr ab ca. 16.700 Bytes. Pro Level werden ja 1000Byte an Daten benötigt und es kommen nochmal ~970 Kommas dazu. Das ist zuviel, entweder ich specke die Level nun soweit ab, dass es nur noch zwei Tiles gibt, dann kann ich diese bitweise in Bytes ablegen und diese dann als DATA speichern, anschließend per READ lesen und dann PRINTen oder aber komplett per Strings abspeichern und komplett ohne DATA leben. Hmm da steh ich nun ich armer Tor, und bin so klug als wie zuvor :). Naja nicht ganz etwas weniger dumm bin ich nun ^^
    Auch aus diesem Forum, Thread weiss ich aber grad nicht; RESTORE X:

    Quellcode

    1. 40 rem ...restore(x)...
    2. 41 poke785,19:poke786,166:sys47083x,0:j=peek(usr(95))+peek(usr(96))*256
    3. 42 j=j-1:poke66,j/256:poke65,j-peek(66)*256:return
    4. Beispiel:
    5. 10 rem ...restore 21...
    6. 11 x=21:gosub40,restore(x):reada:printa
    7. 12 end
    8. 20 data 20
    9. 21 data 21

    Zeilen 41 und 42 einfach als Unterprogramm irgendwo reinpacken und via x=Zeilennummer:gosub ...zum Unterprogramm... = fertig.
    Wenn du übrigens immer noch das Problem wegen dem letzten Zeichen unten rechts hast, gibt es eine einfache Lösung, wenn du auf 2 Cursorblöcke verzichten kannst. Mach den Rahmen einfach dicker!

    Quellcode

    1. poke53270,0:rem singlecolor && big border
    2. poke53270,8:rem singlecolor && normal border
    3. poke53270,16:rem multicolor && big border
    4. poke53270,24:rem multicolor && normal border
    Damit müßtest du pro Zeile 2 Zeichen weniger auf dem Bildschirm zimmern plus dich nicht mehr um das Problem mit dem letzten Zeichen unten rechts kümmern.
  • Um das Schirm-Rollen zu unterbinden gibt es mehrere Möglichkeiten.
    1. poke214,.
    2. poke213,40
    3. Veränderung der Zeilentabeile ab 217

    z.B: wenn Zeile 24 erreicht ist:
    :poke213,80:printa$"[home]"
    :poke214,.:printa$;:

    Schönen Gruß.
  • Ersetzt die null, ist wohl minimal schneller weil der Interpreter erst nach dem Punkt sucht und ist darüber hinaus Biftypisch schlechter lesbar und damit wartungsunfreundlich.
    GREETINGS PROFESSOR FALKEN
    A STRANGE GAME.
    THE ONLY WINNING MOVE IS NOT TO PLAY.
    HOW ABOUT A NICE GAME OF CHESS?
  • BladeRunner schrieb:

    Ersetzt die null, ist wohl minimal schneller weil der Interpreter erst nach dem Punkt sucht und ist darüber hinaus Biftypisch schlechter lesbar und damit wartungsunfreundlich.
    Genau. Und damit es nicht zu schnell läuft, unbedingt noch ein paar :::::::::::::::::::::::::::::::::::::::: einbauen! :prof:
    ___________________________________________________________
    Meine Kreationen: Deviant Art | Toonsup | Flickr | Youtube
    | Twitter
    Avatar: Copyright 2017 by Saiki