Trackermodul-Engine (sehr einfach)

Diskussion zum Thema Programmierung unter DOS (Intel x86)
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Ziemlich verspätet nun noch ein paar Antworten an DOSferatu:
DOSferatu hat geschrieben: Auf C64 konnte man sich ja auf die Hardware-Sprites verlassen - da mußte man keinen "Hintergrund löschen".
Ich habe es nicht genau durchgedacht, aber die Idee (die sicher auch manch
anderer schon hatte), hatte ich schon früher:
Statt mit Hintergrund komplett wieder drüber kopieren und Pufferung könnte man
sich vielleicht merken bzw. bestimmen, welche Bildschirmbereiche von
einem Sprite überdeckt sind und bei Umpositionierung des Sprites,
welche Hintergrundbereiche wieder frei werden.
Und dann eben nur immer das "umzeichnen" was sich wirklich verändert hat.
Ist bei der praktischen Umsetzung wahrscheinlich langsamer und aufwändiger
als die Puffer-Neuzeichnen-Methode, aber ein Gedanke daran kostet ja nix.
Allerdings wäre diese Idee vielleicht eine mögliche Lösung bei zu langsamem
Datendurchsatz der VGA Karte.
Ich glaube, das war zu meinen BASIC-Zeiten mein erster Gedankenanstoß, wie
bewegte Grafik vor Hintergrund machbar wäre. In QBasic war Bildschirmpufferung
nicht ohne weiteres möglich (Datenfelder insgesamt max. 64K meine ich) -
aber ohne Assembler ist so eine Idee natürlich erst recht nicht umzusetzen.
Ich finds ganz interessant, werd mal drüber nachdenken, ganz egal ob ich
dann früher oder später merke dass es vielleicht Blödsinn ist.

DOSferatu hat geschrieben:[...ZVID clipping...] Ich weiß nicht, was Du in diesem Zusammenhang mit Clipping meinst. Betrifft das das "Verhalten am Bildrand" (z.B. bei halb-gescrollten "Kacheln")?
Ja, es geht einfach darum, dass man ein Sprite von einer Breite von sagen wir 16
Pixel auch z.B. an der x-Koordinate 316 darstellen kann, und dann eben nur die
ersten 4 senkrechten Zeilen dargestellt werden sollen und der Rest abgeschnitten.
Ebenso oben, unten, und links. Das ist mit simplen Sprites einfach, aber in so
einem komplexen Gefüge, aus 8x8 großen Blöcken mit jeweils unterschiedlichen
Bit-Tiefen zusammengesetzt, und dabei auch noch je nachdem bei gleichen
aufeinanderfolgenden Blöcken mit blockweiser Lauflängenkodierung, das überfordert mich erstmal.

DOSferatu hat geschrieben: Also für etwas, das keine Animation ist, sondern etwas interaktives, sind so "Delta-Film" artige Animationsformate meiner bescheidenen Meinung nach eher weniger geeignet.
Dieses Delta-Feature hat ZVID nur nebenbei. Ich habe damit nur die Kompressionsraten
demonstriert - habe einfach die Transparenzoption von ZVID genutzt, erster Frame komplett,
die anderen transparent und nur gespeichert was sich verändert hat.
Aber man kann ZVID natürlich auch nicht als Delta verwenden.
Also einfach statt der bildschirmfüllenden Frames, Sprites verwenden, die trotzdem
noch eine gute Kompression erfahren würden, auch weil ich in dem Format redundante
Blöcke berücksichtige, d.h. wenn z.B. bei Sprites der Kopf immer gleich bleibt, dann
wird dieser Bereich nur einmal gespeichert und fortan nur indiziert.
Leider aber kriege ich das vorerst nicht mit dem Clipping hin, ich kann also ZVID
nur für Hintergründe bzw. für "Level-Steine" benutzen, oder eben für Sprites in
einem Spiel wo die Sprites niemals über den Bildschirmrand hinausgehen.


Ich kann mich an deinem Musikprogramm gerne mal versuchen, allerdings tue
ich mich schwer mit Benutzerinterfaces die anders sind als die von meinem
Tracker, der im Borland-Stil gehalten ist.


Ich habe übrigens noch ein paar Basic-Spiele, die bis 1992 zurückgehen,
aber alles schon mit 256 Farben VGA Grafik. Die habe ich nur bisher nicht
präsentiert, weil sie auf einem 286er konzipiert wurden und natürlich auf
späteren Rechnern zu schnell liefen. Aber in dosbox lässt sich das ganz
gut regeln.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

wobo hat geschrieben:Den ersten Puffer höre ich nicht doppelt - kann aber auch an meinen Ohren liegen.

Abstürze gibt es bei mir keine mehr, auch nicht bei no-coke.zsm. Alle zsm laufen nach unten stehender Modifikation sauber durch. Ich musste zuvor aber auch bei ZSMP003b.zip wieder in der Unit PLAYZSM3.PAS, dort in der PROCEDURE Read_Row zweimal ein Sample<>0 abfangen, da es sonst bei Hamster.ZSM und Melodie.ZSM Abstürze gab:

Einmal vor dem Aufruf

Code: Alles auswählen

    play_sample_looped[track] :=  smpinfo[pattern_row[track].smp].loopstart
       < smpinfo[pattern_row[track].smp].length;
ein "if (pattern_row[track].smp > 0) then"

und dann noch einmal in der Schleife

Code: Alles auswählen

  for track := 0 to 15 do
   if trk_active[track] then
   if pattern_row[track].event then
     for i := 1 to zsmheader.sample_entries do
{--->} if (pattern_row[track].smp > 0) then {<---}
           sync[smpinfo[pattern_row[track].smp].marker] := pattern_row[track].vol;
Ich weiß natürlich nicht, ob das von der Player-Logik richtig ist. Ich habe nur gesehen, dass Du im Code an anderen Stellen immer ein Sample=0 abgefangen hast...
Ich habe heute noch ein bisschen an dem Player weitergearbeitet.
Ein bisschen am scope...
Und Soundblaster Parameter mit pos() und upcase ist umgesetzt.
Gegen den Griff ins Leere bei der Zeilenlese-Routine habe ich
das Array um einen 0-Index erweitert und darin alles auf 0 gesetzt.
Bei obigem Code Beispiel mit den Markern weiss ich gerade selber
nicht warum ich das in einer i-Schleife gemacht habe, wo doch
auf i gar nicht referenziert wird in der Schleife. Das habe ich
provisorisch daher ersteinmal rauskommentiert... Habe
natürlich jetzt keinen Tänzer vorübergehend ;)

Das mit dem Soundpuffer richtig anlegen habe ich nicht ganz verstanden...
Warum muss ich mit getmem 4x so viel Speicher reservieren wenn ich nur zwei
und nicht vier Puffer habe... Ich habe mich damit aber auch noch nicht
richtig auseinandergesetzt, die TPU habe ich mir irgendwann vor 18 Jahren
gebaut und für den Player nur noch angepasst.
Vielleicht zeigst du mir einfach mal den entsprechenden Code aus deiner
geforkten Version.
Dass einige Module bei mir einen Absturz verursachten war Dosbox verschuldet.
Es blieb dann hängen mit der Nachricht "DMA segbound wrapping".
Die Vorgängerversion von Dosbox schmiert nicht ab, aber das ein oder
andere Modul wird mit Müll im Puffer abgespielt - das ist wahrscheinlich das,
was ich umgehen kann, wenn ich den Puffer vernünftig anlege.
mov ax, 13h
int 10h

while vorne_frei do vor;
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: Trackermodul-Engine (sehr einfach)

Beitrag von wobo »

Der DMA-Chip, den IBM zum PC damals eingeführt hat, stammt meines Wissen von einem 8-Bit-Vorgänger des PC, nämlich dem IBM DataMaster. Wie die meisten 8-Bit-Chips werden zwei 8-Bit-Register zusammen verwendet, um eine 16-Bit-Adresse anzusprechen. Der DMA-Controller des IBM-PC kann daher eigentlich nur 16-bit adressieren, also einen Bereich von 64kb. Damit aber der gesamte Adressraum des 8088 von 1 MB adressiert werden kann, hat IBM den Chip etwas erweitert und ein 4-Bit-großes Page-Register vorgeschaltet. Damit kann der DMA-Controller zwar einen MB adressieren. Da aber das Page-Register unabhängig vom eigentlichen DMA-Adressregister ist, führt ein Überlauf im DMA-Adressregister nicht zu einer Erhöhung der im Page-Register eingestellten Page. Es gibt vielmehr einen Wrap-Around im Adressregister, was wohl DosBox mit der von Dir festgestellten Fehlermeldung "DMA segbound wrapping" abfängt.

Letztlich bedeutet das, dass der DMA-Controller Daten nur immer innerhalb einer der 16 je 64kb-Großen Pages übertragen kann. Die DMA-Pages sind also etwas ganz anderes als die 8088-Segmente.

Wenn Du nun über Turbo Pascal (TP) einen Speicherbereich allozierst, dann legt TP diesen Bereich immer an durch 8 teilbare Speicheradressen. Allozierst Du z.B. einen Buffer von 1024 Byte, dann kann es passieren, dass der Buffer z.B. physikalisch auf der Adresse $2FF00 liegt. Aus Sicht der 8088 CPU liegt der (normierte) Buffer dann im Segment = $2FF0 und im Offset $0000. Aus Sicht des DMA-Controllers aber liegt dieser Bereich in der Page 2 und an Offset $FF00 und ist damit nur noch 256 groß, d.h. die restlichen 768 byte werden dann beginnend von der Adresse $20000 übertragen, wo ja soundtechnisch nur Datenmüll liegt.

Da man vorher nie weiß, wo ein solcher Buffer im Speicher zur Laufzeit angelegt wird, alloziere ich immer die doppelte Menge an Speicher. Benötige ich einen Buffer von 1k, dann alloziere ich eben 2k und prüfe, wo die Page-Überlaufgrenze liegt. Denn entweder liegt das erste Kilobyte in einer Page, oder eben zwangsläufig das zweite Kilobyte. Es kann ja in dem 2kb-Buffer nur eine einzige Stelle mit Page-Überlauf geben.

Du hattest die beiden Buffer für das Doublebuffering ja so angelegt

Code: Alles auswählen

  for i := 0 to 1 do
  begin
    getmem(soundbuf_byte[i], bufsize);
    fillchar(soundbuf_byte[i]^, bufsize, 128);
    linear_bufaddr[i] := seg(soundbuf_byte[i]^);
    linear_bufaddr[i] := (linear_bufaddr[i] shl 4) + ofs(soundbuf_byte[i]^);
  end;
Da ich möglichst wenig an Deinem Code verändern wollte, habe ich dann nur global die Variable

VAR SoundDMABuffer : POINTER;

und noch als lokale Hilfs-Variable zur Aufnahme der linearen Adresse dieses Buffers die lokale

VAR LinearDMABuffer : LongInt;

angelegt und folgendem Code Deinem Code vorgeschalten:

Code: Alles auswählen

  GetMem (SoundDMABuffer, bufsize*4);
  SoundBuf_byte[0] := SoundDMABuffer;
  SoundBuf_byte[1] := SoundDMABuffer;
  Inc(word(SoundBuf_byte[1]),bufsize);

  LinearDMABuffer := seg(SoundDMABuffer^) shl 4 + ofs(SoundDMABuffer^);
  if (LinearDMABuffer div 65536) <> 
    ((LinearDMABuffer+BufSize*2-1) div 65536) then begin
     Inc(word(SoundBuf_byte[0]),bufsize*2);
     Inc(word(SoundBuf_byte[1]),bufsize*2);
  end;
Der Buffer wird also einmal in vierfacher Größe in SoundDMABuffer angelegt. Vierfach, weil ich die beiden DoubleBuffers zusammenfasse und dann das Doppelte hiervon nehme, um den DMA-Page-Überlauf später ggf. abzufangen. Dann werden Deine beiden SoundBuf_byte[0..1]-Pointer auf die ersten beiden logischen Soundbuffer innerhalb von SoundDMABuffer^ angelegt. Wenn dann die DMA-Page von SoundDMABuffer^ verschieden zur DMA-Page von SoundDMABuffer^-Ende ist, dann werden die Deine SoundBuf_byte[0..1]-Pointer eben auf die logischen Soundbuffer drei und vier gelegt.

Das geht sicher noch eleganter. Mir ging es aber primär darum, möglichst schnell und möglichst ohne viel Eingriffe in Deinen Code die Anpassung vorzunehmen.

Dann kommt unmittelbar Dein Code zum Vorbelegen der Buffer mit Stillebytes und der Berechnung der jeweiligen linearen Adresse, natürlich ohne GetMem-Aufrufe, im gesamten also

Code: Alles auswählen

  GetMem (SoundDMABuffer, bufsize*4);
  SoundBuf_byte[0] := SoundDMABuffer;
  SoundBuf_byte[1] := SoundDMABuffer;
  Inc(word(SoundBuf_byte[1]),bufsize);
  LinearDMABuffer := seg(SoundDMABuffer^) shl 4 + ofs(SoundDMABuffer^);
  if (LinearDMABuffer div 65536) <> 
    ((LinearDMABuffer+BufSize*2-1) div 65536) then begin
     Inc(word(SoundBuf_byte[0]),bufsize*2);
     Inc(word(SoundBuf_byte[1]),bufsize*2);
  end;

  for i := 0 to 1 do
  begin
    fillchar(soundbuf_byte[i]^, bufsize, 128);
    linear_bufaddr[i] := seg(soundbuf_byte[i]^);
    linear_bufaddr[i] := (linear_bufaddr[i] shl 4) + ofs(soundbuf_byte[i]^);
  end;
PS: Ich hatte bei meinem letzten Posting etwas zu laut getönt :-). Ich habe natürlich nicht Deinen Player geforked, sondern lediglich auf Brueggis OS 1:1 portiert (=Anpassung der Laderoutinen). Ein echter Fork, also eine Abspaltung und technologische Weiterentwicklung Deines Players, wäre mir nicht nur know-how-mäßig schon gar nicht möglich. Freue mich schon auf die neuen Features wie Sample-Reducing und alles, was Dir sonst noch einfällt :-)
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Ah, danke!
Ich denke so werde ich das verstehen wenn ich mich da nochmal reindenke,
bin nur momentan zu kreativ im Real-Life was am basteln, naja, aber gestern
hatte ich einfach mal Lust das Scope umzuwandeln von Punkte auf Linien
zeichnen. Muss aber noch dran gefeilt werden.

Also, wenn ich wieder Gehirnkapazität habe bau ich das ein mit dem
Puffer einrichten. Bin ja auch gespannt ob es dann bei wirklich jedem
ZSM sauber klingt. Einzig doof ist wohl an der Sache nur, dass eben
bei der Reservierung mit getmem doppelt so viel Speicher draufgeht
als es eigentlich müsste. Aber da die Puffer sowieso eher nur so um
die 1K groß sind ist das nicht derart tragisch...
Aber zum Thema getmem und Heap:
Ich hatte mir da vorher noch nie Gedanken drüber gemacht, aber es ist wohl so:
Der Speicher ist wohl auch von Fragmentierung betroffen, und wenn ich etwas
mit mehreren freemem-Befehlen freigebe heisst das nicht unbedingt, dass auch
genausoviel wieder verwendet werden kann, wie die Summe der einzelnen
freigebenen Bereiche.

Sample-Reducing? Meinst du die Samplerate halbieren wenn die Module zu groß
für den Speicher sind?
Ach nee, jetzt lese ich meinen Text nochmal und ich glaube du meinst, was
ich unten geschrieben habe, dieses Stutzen der Samples auf das allernötigste,
dass nichts gespeichert ist was nicht abgespielt wird.

Ich hatte auch noch an so simple Features gedacht wie beenden nach dem Abspielen
oder direkt im Scope Mode starten, wäre ja praktisch für Batch-Abläufe.

Ich fände aber mehr grafische Features schön, hatte schon an einen Super Mario
Mode gedacht wo Super Mario von der Musik gesteuert durch die Gegend titscht...

Aber mal sehen.

Nebenbei höre und prüfe ich (auf Kompatibilität) ab und an auch ein paar MODs
durch und mache neue ZSMs.
Es ist ganz interessant was sich da so ergibt. Die total virtuosen und von Effekten
strotzenden Module fallen ja alle flach, und leider ist dann das was bleibt oft
furchtbarer größtenteils dilettantischer Einheitsbrei mit den immer gleichen
Amiga-Standard-Samples. Aber immer wieder finden sich dann doch ziemlich
anhörbare und originelle Musikstücke, die ohne viele Effekte auskommen.

Vor dem nächsten Player Release möchte ich aber auch den Optimizer fertig haben -
zu viele MODs haben in der Samples-Liste einige Lücken und blähen dadurch
die Patterndaten auf (8 Samples zu referenzieren braucht ein Bit weniger als 9 usw...),
das muss also gerafft werden. Zudem passiert es schonmal dass manche Samples
im ganzen Stück gar nicht gespielt werden und nur als Leiche in der Datei rumliegen.
Hier muss also auch durch die Patterns gescannt und nichtgespielte Samples
rausgenommen werden.
Dann wie vorher schonmal erwähnt noch prüfen, wie lang die Samples überhaupt
ausklingen. Angenommen man hat eine Bassdrum die eigentlich lange ausklingt,
in den Patterns aber immer direkt von einer Hihat gefolgt angeordnet sind,
dann kann man da je nachdem ruhig entsprechend abschneiden - nur ein Beispiel.
Und natürlich eventuelle effektive Stille am Ende der Samples abschneiden.

Ich hatte bisher vom Wort "forken" sowieso nie etwas gehört.
Aber es ehrt meinen Player dass er nun auch auf BonnyDOS portiert wurde.

Ich habe noch mit allerlei Bastelei zu tun, ich denke mal mit dem nächsten
Release ist im Dezember zu rechnen.
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von DOSferatu »

zatzen hat geschrieben:Ziemlich verspätet nun noch ein paar Antworten an DOSferatu:
DOSferatu hat geschrieben: Auf C64 konnte man sich ja auf die Hardware-Sprites verlassen - da mußte man keinen "Hintergrund löschen".
Ich habe es nicht genau durchgedacht, aber die Idee (die sicher auch manch anderer schon hatte), hatte ich schon früher:
Statt mit Hintergrund komplett wieder drüber kopieren und Pufferung könnte man sich vielleicht merken bzw. bestimmen, welche Bildschirmbereiche von einem Sprite überdeckt sind und bei Umpositionierung des Sprites, welche Hintergrundbereiche wieder frei werden. Und dann eben nur immer das "umzeichnen" was sich wirklich verändert hat. Ist bei der praktischen Umsetzung wahrscheinlich langsamer und aufwändiger als die Puffer-Neuzeichnen-Methode, aber ein Gedanke daran kostet ja nix.
Das wäre eine ziemliche Aufgabe. Ich klamüsere das mal kurz auseinander:
Zuerst wird ein Sprite, z.B. 24x21 Pixel, gesetzt, der komplette vorherige Hintergrund in einen Puffer geschrieben. Nun wird das Sprite "verschoben", um 5 Pixel nach rechts und 3 Pixel nach oben. Das bedeutet: Es muß ein L-förmiger Bereich aus dem o,g, Puffer auf den Hintergrund wiederhergestellt werden, nämlich dort, wo vorher die "Ecke" des Sprites war, die jetzt nicht mehr dort ist.

Der Puffer muß intern ebenfalls verschoben werden um 5 Pixel nach links und 3 Pixel nach unten, dann das "Wiederhergestellte" wird nun nicht mehr gebraucht - außerdem muß ja Platz für das neu zu speichernde geschaffen werden. Beim Neuspeichern wird also ein L (eher 7-) förmiger Bereich aus dem Hintergrund gelesen (der Rest ist ja schon gelesen) und an den nun freigewordenen Bereich im Puffer geschrieben. Dieses Lesen muß natürlich passieren, bevor das Sprite neu gesetzt wird - eventuell auch während das Sprite gesetzt wird - dann aber nur für den neuen Bereich.

Somit braucht man soviele Puffer, wie man maximal Sprites darstellen will (auch für 2 gleiche Sprites natürlich 2 verschiedene Puffer). Eine andere Möglichkeit wäre, als Puffer gleich eine virtuelle Pixelmatrix zu verwenden, die quasi "hinter" dem Grafikbildschirm gedacht ist, mit der gleichen Größe und die Hintergründe immer gleich da hinein zu puffern, an der entsprechenden Stelle.

In beiden Fällen ist aber noch nicht davon geredet worden, was passiert, wenn sich Sprites (teilweise oder vollständig) überdecken. Hier muß dann natürlich erst Sprite 1 bis 20 gesetzt werden und beim "Löschen" (bzw Hintergrund wiederherstellen) von Sprite 20 bis Sprite 1 vorgegangen werden, bevor man erneut Sprite 1 bis 20 setzt - d.h. in solchen Fällen müßten trotzdem alle Sprites mindestens 1x gesetzt/gelöscht werden. Will man nur die gerade genutzten Sprites bewegen, geht das nur ohne Überdecken - denn sonst ergäben sich Probleme, wenn ein Sprite, das von einem anderen verdeckt wird, verschoben wird (es würde den unter ihm liegenden Hintergrund wiederherstellen - es sei denn, es das darüberliegende Sprite würde zusätzlich eine Bitmatrix setzen für nicht wiederherzustellende Bereiche.

Das ganze geht natürlich nur, wenn man Sprites nur rechteckig dreht und spiegelt, nicht in "krummen" Winkeln (wie es z.B. bei meinen Units möglich ist). Außerdem dürfte man, bei der "ein Puffer pro Sprite" Methode (s.o.) natürlich die Sprites nicht vergrößern - oder müßte die Puffer entsprechend groß anlegen.

Bei der "Wiederherstellen" Methode gehe ich davon aus, daß man nur mit einem Bild (also kein Double-/Triple-/... usw. Buffern) arbeiten will, denn anders wäre es ohnehin nicht möglich.
Dann sollte man nicht mit zu vielen Sprites arbeiten, wenn diese sich überdecken. Denn das Setzen Sprite 1 bis 100, dann Löschen Sprite 100 bis 1 und dann wieder Setzen Sprite 1 bis 100 usw. fällt irgendwann durch "Flackern" auf - es sei denn, man setzt zusätzliche Flags für verdeckte und nicht verdeckte Sprites.

Will man das ganze dann auch noch Scrollen und manche der Sprites sollen mitscrollen und manche nicht - oder Sprites sollen halb angeschnitten dargestellt werden - ergäbe das ganze eine nochmalige Steigerung der Schwierigkeit der Bearbeitung.

Ob diese ganzen komplexen Dinge wirklich den zusätzlichen Rechenzeitverbrauch, der durch permanentes Neuzeichnen entstehen würde, am Ende kompensieren, müßte wohl getestet werden. Ich selbst scheue mich davor, das wäre mir zuviel Geraffel.

Es gäbe noch eine weitere Methode, über die ich auch schon mal nachgedacht hatte: Man reduziert das ganze Spiel auf 128 Farben.
125 wäre eine gute Zahl: 5 Rot-, 5 Grün-, 5 Blau-Phasen, jeweils 0, 64, 128, 192, 255 für jede Phase. Von den restlichen 3 Farben wäre eine für die "Volltransparenz" bei Sprites gedacht, die anderen beiden beliebig belegbar, könnten z.B. dauerhaft "blinken" oder so, damit kann man im Hintergrund bestimmte Effekte erzielen.

Der Witz wäre nun der, für Sprites ausschließlich die Farben 0-127, für Hintergrund ausschließlich die Farben 128-255 zu benutzen - somit wäre eindeutig festlegt, was Sprite und was Hintergrund ist - dann wäre das Überdecken von Sprites vielleicht weniger ein Problem. Allerdings würde dies natürlich dazu führen, wieder mehr Auslesen zu müssen - und Auslesen von Grafikspeicher ist in der Regel langsamer als Schreiben (weil Schreiben in Grafikspeicher wesentlich häufiger vorkommt, wird dies von der Hardware meist schneller umgesetzt als Lesen).

Welches Bit man für Sprite/Hintergrund benutzt, ist hier unerheblich, es wäre auch z.B. statt Bit7 das Bit0 möglich: Gerade Farbnummer=Sprite, Ungerade Farbnummer=Hintergrund - oder umgekehrt.
zatzen hat geschrieben:Allerdings wäre diese Idee vielleicht eine mögliche Lösung bei zu langsamem Datendurchsatz der VGA Karte.
Ja, hier wäre vieles möglich. Ich frage mich immer noch, wie die Typen von ID-Software es bei Commander Keen geschafft haben, mit EGA Grafik (wirklich extrem besch...eiden anzusteuern) Scrolling und Sprites und dann noch auf 286er-Leistung hinzukriegen...
Naja, es sind immerhin die Leute, die dann Wolfenstein 3D[tm], Doom[tm] (und seine Nachfolger), Quake[tm] (und seine Nachfolger) gemacht haben - da frage ich mich sowieso öfter, ob die eine Hardware-Erweiterung im Gehirn haben oder sowas...
zatzen hat geschrieben:Ich finds ganz interessant, werd mal drüber nachdenken, ganz egal ob ich dann früher oder später merke dass es vielleicht Blödsinn ist.
Ja, wie man oben vielleicht lesen kann, habe ich das Problem schon von mehreren Seiten beleuchtet (das alles habe ich mir nicht gerade jetzt ausgedacht, das sind auch so die Gedanken, die mir über die ersten Jahre dazu gekommen waren) - und für mich persönlich (das kann natürlich jeder anders sehen) hatte sich hier schon recht früh der "Blödsinn" ganz klar herauskristallisiert.

Aber da ich auf PC sowieso vorhabe, meine 2D-Spiele mit Scrolling auszustatten, hatte sich diese Frage ohnehin nie wirklich gestellt - es waren eher "was wäre wenn" Überlegungen meinerseits.
zatzen hat geschrieben:
DOSferatu hat geschrieben:[...ZVID clipping...] Ich weiß nicht, was Du in diesem Zusammenhang mit Clipping meinst. Betrifft das das "Verhalten am Bildrand" (z.B. bei halb-gescrollten "Kacheln")?
Ja, es geht einfach darum, dass man ein Sprite von einer Breite von sagen wir 16 Pixel auch z.B. an der x-Koordinate 316 darstellen kann, und dann eben nur die ersten 4 senkrechten Zeilen dargestellt werden sollen und der Rest abgeschnitten.
Ebenso oben, unten, und links. Das ist mit simplen Sprites einfach, aber in so einem komplexen Gefüge, aus 8x8 großen Blöcken mit jeweils unterschiedlichen Bit-Tiefen zusammengesetzt, und dabei auch noch je nachdem bei gleichen aufeinanderfolgenden Blöcken mit blockweiser Lauflängenkodierung, das überfordert mich erstmal.
Ja, das würde mich wohl auch überfordern.

Ich habe auch schon mal ein neueres Spriteformat angedacht, das, ähnlich wie mein DBM-Bildformat, die Daten senkrecht speichert, aber dabei zussätzlich bitbasiert arbeitet - um damit entsprechend der Farbanzahl des Sprites entscheiden zu können.

Ich supporte ja derzeit 15-Farb und 255-Farb-Sprites (1 Farbe jeweils für Transparenz), wobei es aber oft mal so wirkt, als wären 15 Farben zu wenig und 255 zuviel, 31 Farben sähen schon besser aus - aber 5 Bit ist echt eine blöde Zahl, um damit zu arbeiten.

Der Plan sähe nun vor, daß ein Sprite zu Anfang eine Festlegung hat, wieviele Bit UND wieviele Farben es hat, nehmen wir an 5 Bit, 20 Farben. Dann würde immer 5 Bit-weise aus dem "Datenstrom" des Sprites die Daten gezogen werden, 0 wäre die Transparenz, die Werte 1 bis 20 wären in dem Fall die ungepackten Farben, die Werte 21 bis 31 würden für die Lauflängencodierung oder ähnliches benutzt werden. Beim "Packen" des Sprites würde nun für 5 Bit, 6 Bit, 7 Bit, 8 Bit und 9 Bit geprüft werden (also mindestens ab Farbanzahl aufgerundet auf ganze 2er Potenz), ab wo sich die geringste Größe einstellt.

Die Lauflängencodierung könnte ihrerseits weiter gesplittet werden, z.B. könnte man für eine Anzahl von mehr als 2 Pixeln einen Bereich nehmen, z.B. Werte 21 bis 27, damit könnte man dann 3 bis 9 Pixel "packen" (wobei ein Pixel auch der 0-Pixel wäre). Ab 3 deshalb, weil dies 2 "Frames" (5-bit Werte) bräuchte: Anzahl und Farbnummer. Werte 28 bis 31 (2 Bit) wären dann die oberen oder unteren 2 Bit einer Adresse im Sprite, gefolgt von den restlichen 5 bit, rückwärts ausgehend vom derzeitigen Standpunkt, um auf dortige Daten hinzuweisen, um diese zu "wiederholen" (ähnlich GIF). Damit ein Sprite nur "halb" dargestellt werden kann, ohne alle Spalten vorher zu lesen, gäbe es eine Konvention, daß gepackte Daten nicht über das untere Ende von Spalten hinausgehen dürften.
Beim Lauflängen-Packen (s.o. mit dem Beispiel Werte 21 bis 27) wäre ja auch die Farbnummer wieder nur 0 bis 20, somit wären die Werte 21 bis 31 invalid, könnten aber natürlich eine Erweiterung der Anzahl darstellen, d.h. es gäbe dann so viele 5-Bit-Werte 21 bis 31, die immer weiter multipliziert/aufaddiert würden, bis die gewünschte Anzahl erreicht wäre und dann käme ein Farbwert 0 bis 20.

Klingt alles schick, würde weniger Sprite-Speicher verbrauchen, aber mehr Rechenzeit, um Sprites darzustellen. Außerdem wären damit nur Sprites darstellbar, die gespiegelt, in 90° Winkeln gedreht und evtl vergrößert/verkleinert wären möglich, aber NICHT solche, die in beliebigen krummen Winkeln gedreht wären (außer man entpackt das Sprite in einen Puffer und stellt es dann erst dar - für die Methode mit dem "Verweis auf andere Bereiche" müßte dies ohnehin geschehen).

Enthielte ein 20-Farb-Sprite nun viele Flächen oder viele "wiederholende Bereiche", könnte eine Erweiterung auf 6-Bit hier sogar Vorteile/Einsparungen bringen. Der Sprite-Packer würde somit die entsprechenden Möglichkeiten durchtesten und die mit dem geringsten Speicherverbrauch am Ende ausgeben. Das Problem mit den Spalten könnte nun so gelöst werden, daß das Sprite, wenn es größer ist, am Anfang optional eine Tabelle für die Spaltenanfänge enthält (wäre nicht nötig, wenn sowieso vorher in einen Puffer entpackt wird), die angibt, an welchem BIT (nicht Byte!) in den Spritedaten als nächstes gelesen werden soll, d.h. der Byte-Offset stünde z.B. in den oberen 13 bit und der Bit-Offset dann in den unteren 3 (für eine Tabelle aus Words).


Das sind nur so Ideen, die ich so habe, wenn es um das Speicher-Spar-Thema geht. Aber ich versuche ja immer, einen guten "Trade-Off" zwischen Speichersparen und Rechenzeitsparen zu finden. Außerdem kann es auch manchmal passieren, daß man, um Datenspeicher zu sparen, soviel Code schreibt, daß der zusätzliche Code die Menge des gesparten Datenbereichs überschreitet und zusätzlich auch mehr Rechenzeit verbraucht. In solchen Fällen rudere ich dann gern auch mal zurück. Die komplizierte Lösung ist nicht in jedem Fall die beste.

Außerdem gibt es Grafiker, die auch ein 15-Farb-Sprite (15 aus 255 Farben) gut aussehen lassen können und andererseits welche, die selbst mit 255 Farben nicht auskommen, weil sie so an ihre 16 Millionen-Farb-Malprogramme gewöhnt sind, daß darunter gar nichts mehr geht.

Gerade im Bereich der 640kB Speichergrenze, der 50 bis 200 MHz Leistung usw. sind aber wohl eher die die wahren Helden, die mit wenigen Mitteln viel leisten können als umgekehrt. Solche Leute sucht man, wenn man ein DOS-Spiel bauen will.
zatzen hat geschrieben:Leider aber kriege ich das vorerst nicht mit dem Clipping hin, ich kann also ZVID nur für Hintergründe bzw. für "Level-Steine" benutzen, oder eben für Sprites in einem Spiel wo die Sprites niemals über den Bildschirmrand hinausgehen.
Ja, ist eben etwas schade. Du bist Dir natürlich klar, daß, selbst wenn man ZVID für ein 2D-Spiel einsetzen wollen würde, natürlich ein Block-/Sprite-/Level-Editor vonnöten wäre, der dann alles im entsprechenden Format speichert.

Die Stärken von ZVID sehe ich da vor allem bei 2D-Point&Click-Adventures.
zatzen hat geschrieben:Ich kann mich an deinem Musikprogramm gerne mal versuchen, allerdings tue ich mich schwer mit Benutzerinterfaces die anders sind als die von meinem Tracker, der im Borland-Stil gehalten ist.
Oh, den Borland-Stil wirst Du in prISM auch wiedererkennen. Hält man ALT gedrückt oder schiebt den Mauszeiger auf die oberste Bildzeile, erscheint oben diese Menüzeile mit den entsprechenden Auswahlpunkten, die dann jeweils ein Pulldown erzeugen. Die untere Zeile ("Statuszeile") ist kontextabhängig, je nachdem, ob man gerade in einem der Musikfenster arbeitet oder in einem der Pulldowns steht.

Es ist einfacher als es auf den ersten Blick den Anschein hat.
Ich habe soeben auf meinen Webspace unter
http://www.imperial-games.de/z/prism.zip
hochgeladen. Dieses enthält:
* prISM.exe, wav2smp.exe, sndcheck.exe, outwave3
* die ISM-Beschreibungstexte _ISM.TXT und _ISMFILE.TXT
* die Beispielfiles playbox.ism und shooter.ism
* Ente.wav (als Hörbeispiel)

Du kannst ja mal schauen, ob Du etwas damit anfangen kannst.
zatzen hat geschrieben:Ich habe übrigens noch ein paar Basic-Spiele, die bis 1992 zurückgehen, aber alles schon mit 256 Farben VGA Grafik. Die habe ich nur bisher nicht präsentiert, weil sie auf einem 286er konzipiert wurden und natürlich auf späteren Rechnern zu schnell liefen. Aber in dosbox lässt sich das ganz gut regeln.
Ich interessiere mich bekanntermaßen immer für die Arbeit von Programmierer-Kollegen.

Naja, man könnte auch nachträglich Timerschleifen oder Timing mit dem DOS-Ticker einbauen. Für Timerschleifen habe ich übrigens mal eine Unit geschrieben, die den DELAY Befehl ersetzt und mit einem 63-Bit (statt 16-Bit) Wert arbeitet.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Ersteinmal meine weiteren Gedanken zur altenativen Sprite-vor-Hintergrund-Methode.
An Datenfeldern bräuchte man 64000 Byte für die Hintergrund-Grafik, und zwei 8000 Byte
Felder als Information welche Pixel von Sprites bedeckt wären.

Wenn ich nun ein Sprite setze, wird im ersten der beiden 8000 Byte Felder per Bits
vermerkt, welche Pixel das Sprite überdeckt. Setze ich nun im nächsten Zeitframe
das Sprite an eine ganz andere Stelle, dann vermerke ich die überdeckten Pixel
im zweiten 8000 Byte Feld.
Die "Radier"-Routine vergleicht bei jedem Frame-Fortschritt die beiden 8000 Byte
Felder. Sie lässt die neueren Sprite-Pixel stehen und überschreibt die aus dem
vorherigen 8000er Feld markierten Pixel mit entsprechenden Pixeln aus dem
Hintergrund-Array.

Die Sprites sollen also normal gesetzt werden, wir brauchen lediglich immer eine
Information, welche Bereiche im nächsten Schritt wieder vom Hintergrund über-
schrieben werden sollen.

Dazu muss also ausgewertet werden:
- alle im neueren Datenfeld markierten Pixel sollen stehen bleiben
- alle in älteren Datenfeld markierten Pixel sollen vom Hintergrund
überschrieben werden, es sei denn sie sind im neueren Datenfeld markiert.

Ob und mit welcher logischen Verknüpfung man das lösen kann fällt mir gerade nicht ein.
Theoretisch könnte das ganze so machbar sein.
Es kann 8-Pixel-Weise durch die 8000 Byte Arrays gescannt werden um das ganze etwas
zu raffen, solange ein Byte 0 ergibt sind ja auch die Bits alle 0. 16- oder 32-Bit weise
wäre auch denkbar, dann hat man nur 2000 Vergleiche.

Wie sinnvoll das ganze ist sei mal dahingestellt, aber es scheint mir machbar zu sein.
Man bräuchte immerhin keinen Bildschirmpuffer und hätte somit abzüglich der Pixel-
Info-Felder 48000 Byte mehr frei, oder sogar 56000, wenn man es anders anstellt
und vielleicht sogar eines mit 8000 Byte genügt.
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von DOSferatu »

zatzen hat geschrieben:Ersteinmal meine weiteren Gedanken zur altenativen Sprite-vor-Hintergrund-Methode.
Ja, habe das mal durchgelesen. Nun, das wäre natürlich eine Möglichkeit, löst aber nicht wirklich das Problem sich überdeckender Sprites. Wenn Sprite A von Sprite B teilweise verdeckt wird und sich dann Sprite A unter Sprite B wegbewegt ODER Sprite A sich über Sprite B davonbewegt... In beiden Fällen wäre nicht klar, ob hier im Bitfeld die Bits gelöscht werden sollen oder nicht. Außerdem kann man das Ganze auch beliebig mit 3 oder mehr Sprites weiterspinnen, die sich überdecken/teilweise überdecken.
Zur Frage, wie man auf Bits reagieren kann, die "alt"=0 und "neu"=1 sind:
Resultat=((not alt)and neu)
Dann sind in Resultat alle entsprechenden Bits gesetzt.
Ich weiß nicht, ob Du vorhast, dann entsprechend die 8000-Byte-"Bitmaps" bei jedem Frame anzugleichen - aber nehmen wir an, die zu setzenden/herzustellenden Pixel müßten nicht aus dem 64kB-Feld (dem "virtuellen Hintergrund" refresht werden, sondern aus den Sprites - dann müßten alle Sprites durchsucht werden, um festzustellen, welches Sprite dahingehört und aus welcher Position innerhalb des Sprites der Pixel zu holen wäre.

Anmerkung dazu: Im C64-Emulator, den ich programmiert habe, habe ich die hardwaremäßige (pixelgenaue) Sprite-Sprite-Kollision und Sprite-Hintergrund-Kollision noch nicht umgesetzt. Da ich (genau wie der C64, bzw der VIC) hier zeilenweise vorgehe (und es nur 8 Sprites pro Zeile geben kann), sieht der Plan vor, die Bitmaske des jeweils gesetzten Sprites an die Pixel zu setzen - allerdings wäre das Ganze dann trotzdem noch nicht zyklusgenau, weil ich erst nachträglich testen würde. Der VIC löst ja das Flag (und wenn eingestellt, auch den IRQ) aus, sobald sich zwei Sprite-Pixel (oder Sprite+Hintergrund) berühren...

Das fällt mir nur gerade so ein, weil die einzige Möglichkeit, wie man hier vorgehen könnte, wenn man Deinen Plan verfolgt, wäre meiner Meinung nach für jeden Pixel eine Sprite-Bitmaske entsprechend des Sprite-Indexes zu setzen. Aber das würde Unmengen von Speicher erfordern ODER sehr wenige Sprites.

Ich kann mich hier natürlich auch irren. Es könnte sein, daß ich Dein Vorhaben irgendwie mißverstanden habe.

Ja, ich hatte auch schon darüber nachgedacht, wie man hier möglichst Rechenzeit sparen kann, auch im Bezug auf langsame Grafikkarten.
Die beste Möglichkeit auf PC scheint mir aber zu sein:
* Den kompletten Hintergrund in den RAM (z.B. 64000 Bytes) erzeugen.
* Alle Sprites in den RAM reinschreiben (drüberschreiben).
* Den kompletten RAM in den Grafikspeicher kopieren.
(Hier kann man dann natürlich mt REP MOVSD etwas Zeit rausholen.)
Auf diese Art würde man jeden der 64000 Pixel nur einmal in die Grafikkarte schreiben.
Wenn es einem wirklich darum geht, nur die pro Frame geänderten Pixel in die Grafikkarte zu schreiben, dann muß man mit 2 64000Byte-RAM-Puffern arbeiten, genauso vorgehen, aber jedesmal dann nur die Bytes/Words/DWords in die Grafikkarte kopieren, die sich in beiden unterscheiden (natürlich aus der jeweils "neueren" Pufferhälfte).
Dann hätte man die geringstmögliche Zahl der Grafikkartenzugriffe (bei gleichzeitig höchstmöglichem Speicherverbrauch).

Und für die Variante, nur die Sprites und ihre "Änderungen" zu schreiben, OHNE den Hintergrund immer neu zu schreiben, sehe ich nur die Möglichkeit, die ich schon beschrieben habe: Sprite 1 bis X setzen, dabei jeweils Hintergrund in extra Puffer lesen. Und vor dem nächsten Vorgang Sprite X bis 1 löschen, Hintergrund wiederherstellen und dann wieder Sprite 1 bis X setzen. Und ja, kann könnte den SPRITES dann ein Flag mitgeben (oder "pre-Koordinaten") und das Sprite nur löschen/neusetzen, wenn sich seine Position geändert hat (besser: 2 Flags, eins heißt löschen. eins heißt setzen: für Sprites, die neu "entstehen" oder welche, die restlos gelöscht werden sollen (ohne neu setzen)). Dann bräuchte man nur die "geflagten" Sprites beachten. In diesem Fall kann man aber wieder NICHT das Überdecken von Sprites mit anderen Sprites supporten.

Und nein, damit will ich Dir nicht Deine Ideen kaputtreden. Ich finde es ja gut, wenn sich jemand Gedanken darüber macht, wie man Rechenzeit und/oder Speicher sparen kann. Es ist nur so, daß ich auch schon eigene Erfahrungen damit gemacht habe, mühevoll eine komplexe Idee umzusetzen, die ich für gut/funktionierend hielt und die dann am Ende nicht das tat, was sie eigentlich sollte und habe damit monatelang Arbeit für die Mülltonne produziert. Bei dem bißchen Zeit und Kraft, die den meisten von uns wirklich noch für Programmieren bleibt, wäre das schade.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Zu obigem Thema:
Was mich am meisten stört sind die 64000 Byte Puffer - wenn man einfach direkt in den Grafikspeicher
schreiben könnte, und nur einen kleinen Zwischenpuffer bräuchte, könnte man diese knapp 64K noch
anderweitig nutzen. Wie auch immer.


Zu ZSM:
Es ist jetzt Mitte Dezember und ich hatte einen Release angekündigt, aber ich habe mittlerweile
noch mehr Ideen, und die möchte ich vorher noch umsetzen.

Nebenbei arbeite ich mich durch das komplette modarchive.org, das dauert eine Weile, aber
man findet doch immer wieder sehr schöne Module die kompatibel zu den Einschränkungen
in ZSM sind.
Dabei kommen mir auch so ein paar Gedanken, was man am Format noch ändern oder
ergänzen könnte. Für die paar ZSM die ich bisher hier zum Download reingestellt habe
lohnt es sich nicht, den Player abwärtskompatibel zu gestalten. Ich kann ja die bisherigen
das nächste mal im neuen Format mit hochladen.

Features die mir so einfallen:

- Datum des Originals speichern
Mir fallen nämlich teilweise richtige Oldies aus den 80er Jahren in die Hände.

-Vielleicht noch ein kleiner Message-Block, für die Nachrichten der jeweiligen Composer.

- Ich könnte mich dazu durchringen mehrere Abspielgeschwindigkeiten
innerhalb eines Patterns zu ermöglichen. Damit ließen sich eine
ganze Stange MODs konvertieren ohne dass ich das vorher alles
auseinanderdröseln muss. Ich selber benutze aber meistens
keine Geschwindigkeitsänderungen innerhalb Patterns, aber naja,
vielleicht wäre ein Ritardando oder das Gegenteil davon ja mal nett.

- Viele Module die ich durchgehört habe benutzen den Offset
Befehl, mit dem das Sample auf eine bestimmte Position gesetzt wird.
Ich könnte es implementieren, brauche es aber für eigene Stücke nicht,
und eigentlich ist ZSM für eigene Kreationen gedacht.

- In MODs wird sehr viel der Volume Slide Befehl genutzt. Man kann einen
sehr ähnlichen Effekt auch mit Volume Befehlen erledigen. Möglicherweise
konvertiere ich die Volume Slides zu statischen Volume-Befehlen, auch wenn
er bei ZSM sehr stufig verlaufen würde.


Aber die bedeutendste Neuerung wäre:
4 Bit Delta Sample-Kompression.
4 Bit Delta in verschiedenen Tonhöhen abszuspielen erfordert
etwas Nachdenken beim Programmieren, ist aber nicht unmöglich.

Bisher ist diese 4-Bit-Delta Komprimierung immer an der resultierenden
Klangqualität gescheitert. Dabei hatte ich keine bzw. eine lineare
Delta-Wert-Tabelle verwendet. Also -8 bis +7.
Bei 8 Bit Samples ist aber viel mehr relative Bewegung vorhanden,
nämlich bis zu +/- 255. Und so klang es bei linearer Delta-Codierung
extrem dumpf und verzerrt.
Nun habe ich einmal eine ungefähr exponentielle Tabelle angelegt.
Und siehe da, es klingt fast so, als wäre da gar keine Kompression im Spiel.
Es klingt nur manches ein wenig weicher und sehr starke Höhen werden bedämpft,
und gut, es rauscht bei manchen Samples etwas mehr, allerdings keineswegs so
viel als wären es 4-Bit Direktdaten.

Aber ich hadere noch mit der Sache. Dafür, dass es nur 4 Bit wären klingen
meine Tests wirklich super, aber verglichen mit dem Unkomprimierten ist es
doch ein Rückschritt.
Von einer anderen Seite betrachtet aber: Es wäre eigentlich Verschwendung
wenn ich das nicht einbaue und nur wegen einem Eckchen mehr an Tonqualität
direkt den doppelten Sample-Speicher beanspruche. Außerdem hatte ich ZSM
ja ursprünglich auch mit Sample-Kompression konzipiert.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Der Überlauf ist mein Freund!
Bisher haben die 4 Bit Delta Samples etwas dumpf geklungen, weil
die Sprünge doch noch limitiert waren. Von 0 auf 255 zu kommen
ist mit einer Tabelle, die als shortint von minus bis plus geht, keine gute Lösung.
Stattdessen kann man ja mit Überlauf arbeiten, und so z.B. auch von 255
auf 0 springen indem man einfach eine 1 addiert.
Meine Tabelle sieht nun so aus:

Code: Alles auswählen

(0, 1, 2, 4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252, 254, 255)
Jetzt klingt es schön frisch und die Qualitätsunterschiede sind nur marginal.
Motivation genug für mich, die 4-Bit Sache umzusetzen.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Jetzt habe ich wieder eine Frage...
Es geht um folgendes Code-Beispiel:

Code: Alles auswählen

  @loopvol0:
  db 64h; mov ah, ds:[si+bx];
  sar ax, 10; add es:[di], ax
  db 66h; add bx, dx; adc bx, 0
  db 66h; add di, bp
  jc @loopvol0; jmp @end
Zur Frage relevant ist die vorletzte Zeile.
Hier wende ich DOSferatus Methode an, um EDI gleichzeitig als Zähler und
Indexregister zu benutzen. EBP ist auf den Wert $FFFF0002 gesetzt.
Nun würde ich aber gerne, angesichts meines nächsten Vorhabens, so
viele Register wie möglich frei haben. Daher würde ich statt bp gerne
einen immediate Wert hinschreiben.
Aber das will nicht so wirklich funktionieren. Ich habe schon alles mögliche
probiert (word ptr, oder auch db 66h; add di, 2; dw 0ffffh), auch ein extra
Testprogrämmchen mit diversen Versuchen um DI bzw. EDI.
Bis jemand antwortet werde ich noch weiter probieren.

Übrigens habe ich ein sehr praktisches Online-Tool gefunden: https://defuse.ca/online-x86-assembler.htm


***** EDIT *****

OK, ich hab's schon:

Code: Alles auswählen

db 66h; db 081h, 0c7h, 002h, 000h, 0ffh, 0ffh { add edi, 0ffff0002h }
Da muss man erstmal drauf kommen, dass der Code trotzdem noch ein db 66h vorne braucht.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Eigentlich wollte ich ja so eine Art Spiel in den ZSM-Player integrieren.
Aber nachdem ich schon einiges stehen hatte, habe ich mich dann doch
dazu entschlossen, den Player lieber technisch und trocken zu halten,
oder zumindest nicht so ein 256K Spiel dranzuklatschen, was die die
Größe der abspielbaren Module einschränkt.

Aber zum Trost mal ein Video von dem Quatsch, vielleicht könnte ich
ihn ja mal als extra Spiel rausbringen und nicht als Teil des Players.

https://youtu.be/uhuQgSfkg5M

Das Spielprinzip ist aber so über-simpel, dass man gar nicht wirklich
von einem Spiel reden kann. Also ich glaube, zusehen macht eigentlich
genauso viel (oder wenig) Spass wie selber ein paar mal L drücken
(für Lemming starten).


Mittlerweile habe ich aber das 4-Bit-Delta Format gut in den Player und
die ZSMs integriert, und bis zum Release werde ich noch in aller Ruhe
weiter optimieren.
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von DOSferatu »

@zatzen: Entschuldige, daß ich erst jetzt antworte. Ich war in den letzten Wochen sehr beschäftigt.
zatzen hat geschrieben:Zu obigem Thema:
Was mich am meisten stört sind die 64000 Byte Puffer - wenn man einfach direkt in den Grafikspeicher
schreiben könnte, und nur einen kleinen Zwischenpuffer bräuchte, könnte man diese knapp 64K noch
anderweitig nutzen. Wie auch immer.
Deshalb tue ich das ja. 64kByte nur "mal eben zum Zwischenspeichern" sind mir auch zu viel.
zatzen hat geschrieben:Der Überlauf ist mein Freund!
Bisher haben die 4 Bit Delta Samples etwas dumpf geklungen, weil
die Sprünge doch noch limitiert waren. Von 0 auf 255 zu kommen
ist mit einer Tabelle, die als shortint von minus bis plus geht, keine gute Lösung.
Stattdessen kann man ja mit Überlauf arbeiten, und so z.B. auch von 255
auf 0 springen indem man einfach eine 1 addiert.
Meine Tabelle sieht nun so aus:

Code: Alles auswählen

(0, 1, 2, 4, 8, 16, 32, 64, 128, 192, 224, 240, 248, 252, 254, 255)
Jetzt klingt es schön frisch und die Qualitätsunterschiede sind nur marginal.
Motivation genug für mich, die 4-Bit Sache umzusetzen.
Ja, das klingt in der Tat interessant.
zatzen hat geschrieben:Eigentlich wollte ich ja so eine Art Spiel in den ZSM-Player integrieren.
[...]
Aber zum Trost mal ein Video von dem Quatsch, vielleicht könnte ich
ihn ja mal als extra Spiel rausbringen und nicht als Teil des Players.

https://youtu.be/uhuQgSfkg5M

Das Spielprinzip ist aber so über-simpel, dass man gar nicht wirklich
von einem Spiel reden kann. Also ich glaube, zusehen macht eigentlich
genauso viel (oder wenig) Spass wie selber ein paar mal L drücken
(für Lemming starten).

Mittlerweile habe ich aber das 4-Bit-Delta Format gut in den Player und
die ZSMs integriert, und bis zum Release werde ich noch in aller Ruhe
weiter optimieren.
Ja, schon sehr lustig, was Du da angestellt hast.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Ich glaube, Du hattest das schonmal umrissen, aber ich frage nochmal nach, bevor ich mich an die Arbeit
mit einem kleinen Spiel mache: Was nutzt du denn als Puffer, wenn nicht diese bspw. 64000 Byte im RAM?

Ich möchte auch mal versuchen, das nächste Spielchen mit horizontalem und vertikalem scrolling
umzusetzen, indem ich den Bildausschnitt kleiner mache und auch nur eher kleine Bildobjekte
verwende. Dadurch umgehe ich die Notwendigkeit des clippings, was ZVID momentan noch nicht
kann. Der Bildschirmpuffer ist dabei z.B. an allen Seiten 31 Pixel größer als der Ausschnitt, der
davon angezeigt wird, das würde dann eine generelle Sprite- oder Hintergrundelement-Größe
von bis zu 32 Pixeln erlauben.

Beim ZSM Player habe ich die Mixrate jetzt einmal auf 44100 Hz hochgesetzt.
Dadurch klingt es jetzt trotz der 4-Bit-Delta Samples ziemlich sauber.
Wegen der 4-Bit-Decodierung macht es keine bedeutenden Unterschiede mehr,
mit welcher Samplerate man mischt. Der Datendurchsatz der Samples ist
immer derselbe, und der schlägt mit Abstand am meisten zu Buche.
Ich weiss nur nicht, ob ein standard Soundblaster 44100 Hz unterstützt.
In der Dosbox funktioniert es... Aber natürlich kann man auch weiterhin
mit 22050 Hz mischen und jetzt auch mit 11025 Hz.

Heute kam mir noch ein kleiner Gedanke... Dosbox emuliert ja auch
die Gravis Ultrasound. Die kann 1 MB Samples halten, und wenn man
sie nutzen würde, hätte man weder Rechen- noch Speicheraufwand.
Aber zum einen ist sie kein Standard, nicht viele User haben oder hatten sie,
und außerdem macht es mehr Spass, Software-Mixing zu programmieren...
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von DOSferatu »

zatzen hat geschrieben:Ich glaube, Du hattest das schonmal umrissen, aber ich frage nochmal nach, bevor ich mich an die Arbeit
mit einem kleinen Spiel mache: Was nutzt du denn als Puffer, wenn nicht diese bspw. 64000 Byte im RAM?
Vom HEAP? Gar nichts.
Der Unterschied zwischen uns beiden ist dabei wahrscheinlich, daß Du sicher den MCGA-Mode (320x200), also den normalen Mode 13h benutzt, während ich diesen als "Mode-X" bekannten VGA-Tweak benutze, mit dem ich etwas andere Auflösungen als die bekannten und auch mehr Bildseiten benutzen kann, da damit die ganzen 256kByte VGA zur Verfügung stehen. Leider mit einer etwas komischen Adressierung. (4 nebeneinanderliegende Pixel haben alle die gleiche Adresse, liegen aber jeweils in Plane 0 bis 3, somit weiterhin 64k-Adressen).
Daher sind meine Grafik-Engines für Arcade-Zeug alle auf "senkrecht" ausgelegt, d.h. Levels und Sprites werden senkrecht gezeichnet. Für jedes Levelbild und jedes Sprite wird insgesamt nur je 4x die Plane gewechselt, weil ich immer erst die Spalten für Plane 0, Plane 1, Plane 2 und Plane 3 zeichne.

Dadurch, daß ich mehrere Seiten habe, kann ich das nutzen für sogenanntes Double-Buffering (oder auch Triple-/Quadruple....), ich zeichne auf einer "Seite", die gerade NICHT angezeigt wird und erst wenn Level/Sprites/Anzeigen etc. fertig sind, schalte ich die Anzeige auf diese Seite um. Ist der Rechner schnell genug (d.h. so schnell, daß das Zeichnen einer Seite weniger als die Bildschirmfrequenz braucht) warte ich am Ende auch auf den Vertical-Retrace (Rasterstrahlrücklauf) und es ist scrollt ruckelfrei. Ist der Rechner zu langsam, ist das Spiel noch genauso schnell, eben mit weniger FPS (und ruckelt natürlich etwas - aber besser als gar nicht spielen zu können, nur weil man keine Superkiste hat).

D.h. der "Puffer" ist der Grafikspeicher und wenn ich fertig bin, brauche ich nichts umkopieren, sondern einfach nur auf die andere "Seite" schalten.

Ich hoffe, das war einigermaßen verständlich.
zatzen hat geschrieben:Ich möchte auch mal versuchen, das nächste Spielchen mit horizontalem und vertikalem scrolling
umzusetzen, indem ich den Bildausschnitt kleiner mache und auch nur eher kleine Bildobjekte
verwende. Dadurch umgehe ich die Notwendigkeit des clippings, was ZVID momentan noch nicht
kann. Der Bildschirmpuffer ist dabei z.B. an allen Seiten 31 Pixel größer als der Ausschnitt, der
davon angezeigt wird, das würde dann eine generelle Sprite- oder Hintergrundelement-Größe
von bis zu 32 Pixeln erlauben.
Wie ich vielleicht bereits mal erwähnte, kann man die "Scanline" - also die Bildschirmzeile - auch breiter machen als das angezeigte Bild. Damit wäre das Ganze auch möglich. Und das Bild ist sowieso insgesamt Scanline x Y hoch, wobei Y=262144 div Scanline (wenn man Mode-X benutzt).
Benutzt man natürlich MCGA, bringt das nichts, weil der kaum noch Platz läßt, um breiter/höher zu werden. Mit Breite 328 wären schon keine 200 Zeilen mehr möglich.
Eine andere Möglichkeit wäre natürlich VESA, was wirklich wie MCGA funktionieren KANN und man kann dann auch mehrere Bildseiten haben. Leider supportet VESA selten den 320x200 als VESA-Mode, jedenfalls nicht mit 8-Bit.
zatzen hat geschrieben:Beim ZSM Player habe ich die Mixrate jetzt einmal auf 44100 Hz hochgesetzt.
Dadurch klingt es jetzt trotz der 4-Bit-Delta Samples ziemlich sauber.
Wegen der 4-Bit-Decodierung macht es keine bedeutenden Unterschiede mehr,
mit welcher Samplerate man mischt. Der Datendurchsatz der Samples ist
immer derselbe, und der schlägt mit Abstand am meisten zu Buche.
Ich weiss nur nicht, ob ein standard Soundblaster 44100 Hz unterstützt.
In der Dosbox funktioniert es... Aber natürlich kann man auch weiterhin
mit 22050 Hz mischen und jetzt auch mit 11025 Hz.
Hm. Bei mir (ISM/prISM) ist es eigentlich (fast) total egal, welche Samplerate ich nehme. Damit aber die Längen nicht irgendwie "unscharf" werden, werden die Raten immer auf ganze 128er gerundet (deshalb z.B. 11008 statt 11025 Hz) - das macht aber keinen hörbaren Unterschied. Der Unterschied ergäbe sich eher, wenn man es nicht macht - weil dann die Stimmen irgendwann zueinander leicht unsynchron werden (ISM bearbeitet bekanntlich die Stimmen getrennt voneinander.) Daß die Sampleraten bei mir (fast) beliebig sein können, liegt daran, daß ich ja die Tonfrequenz (d.h. den "Addierwert") sowieso vorher berechnen lassen muß - da kann ich auch gleich die Samplerate mit einrechnen und brauche keine 2er-Potenzen als "Teiler" zu berücksichtigen.
zatzen hat geschrieben:Heute kam mir noch ein kleiner Gedanke... Dosbox emuliert ja auch
die Gravis Ultrasound. Die kann 1 MB Samples halten, und wenn man
sie nutzen würde, hätte man weder Rechen- noch Speicheraufwand.
Aber zum einen ist sie kein Standard, nicht viele User haben oder hatten sie,
und außerdem macht es mehr Spass, Software-Mixing zu programmieren...
Den Gedanken an die Ultrasound hatte ich auch schonmal - aber habe ihn wieder verworfen. Dafür müßte ich alles komplett anders programmieren und wäre auch in dem Zeug nicht mehr so flexibel wie ich es gerne habe. Daher kommt für mich dieser Weg nicht in Frage. Interessanter wäre da schon dieser "AdLib-Trick". Ich habe irgendwo (war es in der SWAG? Ich weiß nicht mehr) etwas gelesen, wie man auch die AdLib dazu bringen kann, digitale Sounds (also Soundpuffer) abzuspielen. Somit könnte ich außer PC-Speaker und Soundblaster auch die AdLib supporten.

Wenn man übrigens den PC-Speaker mit Sound beschicken kann, kann man auch gleich den LPT (diese Disney Sound Source Dinge und ihre Verwandten) mit supporten. Lediglich der Port ist ein anderer (nicht $42, sondern $378) und man muß keine Tabelle vorschalten (wie beim Speaker), sondern kann die 8bit-Werte direkt raushauen - ansonsten ist der Vorgang identisch: Ticker hochsetzen und Sound aus Port ausgeben.

Neulich habe ich endlich meine Joystickunterstützung eingebaut - mit Option, den Joystick in "Tasten" umzuwandeln, damit es intern wie eine Tastatursteuerung abgefragt werden kann. Ein Skript für Umwandlung von Tasten/Stick/u.ä. in diese "Steuer-Bitmasken" die ich nutze, habe ich auch schon entwickelt - das wird demnächst (wieder in ASM) umgesetzt.

P.S.: Hast Du Dir schonmal die neue prISM-Version (derzeit v0.11) - mit eingebautem Tutorial - von meiner Seite gezogen? Ich wäre natürlich etwas interessiert, was Du so davon hältst.
(Der Link steht in dem "Nicht-Tracker prISM" Thread.)

OK - erstmal genug für heute.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Das ist natürlich ziemlich genial wenn man mehrere Bildschirmseiten nutzen kann. Ich glaube bei EGA
geht das auch. Mode-X hat ja, wenn ich nicht irre, auch den Vorteil wirklich quadratischer Pixel.
Aber ich bin ersteinmal so im Trott mit MCGA, dass ich wohl die 64000 Byte opfern werde.
Bzw. etwas weniger, siehe meine Methode für das Scrolling.

Das mit der Scanline klingt interessant, und ich würde mich für ein Spiel auch mit sichtbaren 320x160
zufrieden geben. Allerdings brauche ich ja, zumindest für zweidimensionales Scrolling mit ZVID,
rundherum unsichtbare Fläche. Das ist natürlich alles andere als optimal, ich habe lediglich momentan
nicht die Nerven dazu, ZVID ein Clipping zu verpassen.

Es ist eine Weile her, aber den AdLib-Trick habe ich auch einmal gelesen, es war ungefähr so:
Man stellt z.B. auf einem Kanal einen Sinus ein, stellt eine bestimmte Frequenz ein, startet
den Ton auf diesem Kanal und setzt in dem Moment, wenn der Sinus auf dem höchsten Punkt
ist, die Frequenz auf null. Danach kann man durch variieren der Lautstärke digitalen Sound
abspielen.
Ich weiss nur nicht mehr genau, welche Auflösung der Lautstärkeparameter hat, 16 Stufen
wären ein bisschen wenig.

Das mit dem LPT hätte ich umgekehrt vor 22 Jahren wissen müssen. Dann hätte es
Kotzman II offiziell mit Sprachausgabe gegeben. Oder ich hätte den AdLib Trick
nutzen können... Das war aber in QBASIC alles etwas beschränkt.

Ich habe mir prISM 0.11 gerade angesehen und habe schonmal begriffen, wie man
einen Klang defininiert und Noten setzt. Ich glaube, damit kann man gut arbeiten,
wenm man einmal "drin" ist. PLAYBOX finde ich auch sehr schön, schon ein recht
komplexes Stück mit einem gewissen "8 Bit" Charme. Allein daher finde ich, dass
ISM sehr viel Sinn macht.

Vielleicht hast Du oder jemand einen Musikwunsch den ich in prISM umsetzen würde.
Das wäre mir die Erfahrung wert, allerdings brauche ich für sponante Improvisationen
doch eher einen Tracker, deshalb würde ich in prISM nur etwas programmieren, das
schon steht.


Für ZSMPLAY schreibe ich gerade einen "Starter" oder wie ich es nennen soll. Ich habe
mich ja durch die Archive gegraben und mittlerweile ein paar hundert Module konvertiert.
Deshalb wollte ich schon allein für mich ein Programm haben, das die Dateien mit
Titeln usw. auflistet und sortiert, und aus dem man dann den Abspielvorgang starten kann
oder sich auch eine Playlist zusammenstellt. Ich wollte es ursprünglich spontan "zsmshell"
nennen, aber meine Recherche ergab dass eine Shell vielmehr soetwas wie eine
Kommandozeilen-Konsole ist. Also, wenn jemand noch einen besseren Namen
hat als "ZsmPlay Starter", würde ich mich über Vorschläge freuen.
mov ax, 13h
int 10h

while vorne_frei do vor;
Antworten