Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

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: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von zatzen »

Hallo Thomas!

Während der Programmlaufzeit die Uhr "Up to Date" halten fühlt sich für mich einfach irgendwie besser an, und es sind ja auch nur ein paar Zeilen Code mehr. Es könnte ja auch sein, dass man Highscores oder sonstiges speichert, und dann ist es irgendwie schöner, wenn Datum und Uhrzeit der Dateien stimmen.

Und ja genau, meine Unit bietet beides, so wie es in Basic war und aber auch so, dass nur ein Flag gesetzt wird und Du dann in der Hauptschleife btplay_playtick (entspricht der Basic Gosub BT.Play, habe es playtick genannt weil pro Aufruf einen 50 Hz Tick weitergegangen wird) "selber" aufrufst, wenn btplay_next_tick = true. Diese Boolean-Variable kannst Du auch als Flag nutzen wann es im Spiel weitergehen soll. Wenn Du die Musik lieber "im Hintergrund" hast und trotzdem noch eine Flag-Variable haben willst, muss man das in der introut_playtick nur ergänzen (mov btplay_next_tick, 1).
Der Grund warum man so eine Abspielroutine in die Hauptschleife verlegen könnte wäre der, dass die Routine ein "wenig" lang ist für ne ISR, daher war ja auch meine Intention das ganze durch Assembler wenigstens etwas zu verkürzen und zu beschleunigen. Bis jetzt sind die IF-Kaskaden und die Variablenzuweisungen da drin noch 1:1 vom Original übernommen, aber insofern schon optimiert, dass die Offsets für die Array-Zugriffe nicht immer wieder neu berechnet werden und überhaupt etwas intelligenter mit den Registerinhalten umgegangen wird.

Ja, Set/GetIntVec schreibt/liest nur je einen Pointer an ner Adresse, wäre blöd gewesen dafür extra die Unit DOS zu brauchen.
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: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von DOSferatu »

Naja, INT-Vektoren setzen/holen ist ja wohl total einfach. Geht in Pascal oder Assembler. Man sollte nur beim Setzen vorher CLI und nachher STI machen (damit während des Setzens nicht der Interrupt zuschlägt).
Und wo die Vektoren liegen, dürfte ja wohl klar sein: Ganz vorne. (also bei $0000:$0000 bis $0000:$03FF)
In Pascal also:

Code: Alles auswählen

var INTVEC:array{0..255]of Pointer absolute $0000:$0000;
und damit holen/setzen.

in Assembler (mit Pascal-Header) zB sowas:

Code: Alles auswählen

funktion SetGetVec(IntNr:byte;NewVec:Pointer):Pointer; assembler;
asm
pushf
cli
push ES
xor BX,BX
mov ES,BX
mov BL,IntNr
shl BX,2
mov AX,ES:[BX]
mov DX,ES:word ptr[BX+2]
db $66;mov CX,word ptr NewVec
db $67;jcxz @NoSet
db $66;mov ES:[BX],CX
@NoSet:
pop ES
popf
end;
Setzt einen neuen Vektor(wenn er nicht NIL (also 0:0) ist und gibt jederzeit den alten zurück.
Macht also beides gleichzeitig und beides kann "ignoriert" werden. Wenn man nur Vektor holen will:

Code: Alles auswählen

OldVector:=GetSetVec(Nr,NIL);
Wenn man nur Vektor setzen will:

Code: Alles auswählen

GetSetVec(Nr,OwnVector);
Wenn man beides will:

Code: Alles auswählen

OldVector:=GetSetVec(Nr,OwnVector);
Wie wär's damit?
(Hab ich nur mal fix zusammengetackert.)
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von zatzen »

Eine sehr elegante Funktion. Könnte in Zukunft bei mir Einzug halten als Ersatz für das was Pascal bietet.
Aber ich hab eben nur erstmal eine Umgehung der DOS-Unit gesucht und es für den Timer hardgecodet:

Code: Alles auswählen

procedure set_timer_vec(p: pointer); assembler;
asm
   xor ax, ax; mov es, ax
   db 66h; mov ax, word ptr p
   cli; db 66h; mov es:[32], ax; sti
end;
Und so ähnlich für's "getten".
In der Get-Routine wird noch abgefragt, ob schonmal gegettet wurde und dann eben nicht mehr, sonst verliert man hinterher das Original.

EDIT: JCXZ, hatte ich schon ganz vergessen weil ich es in Loops nie brauchte. Damit kann ich ja die Sprungkaskaden von BTPLAY noch etwas optimieren. Dieses db 067h ist das richtig davor? Oder doch db 066h? Also weil man ECX will...
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Thomas
DOS-Kenner
Beiträge: 426
Registriert: Mi 22. Jun 2016, 12:29
Wohnort: Nähe von Limburg / Lahn

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von Thomas »

Zatzen hat geschrieben: Es könnte ja auch sein, dass man Highscores oder sonstiges speichert, und dann ist es irgendwie schöner, wenn Datum und Uhrzeit der Dateien stimmen.
Ist ein stichfestes Argument das ich total außen vor gelassen habe.
Zatzen hat geschrieben: Der Grund warum man so eine Abspielroutine in die Hauptschleife verlegen könnte wäre der, dass die Routine ein "wenig" lang ist für ne ISR, daher war ja auch meine Intention das ganze durch Assembler wenigstens etwas zu verkürzen und zu beschleunigen. Bis jetzt sind die IF-Kaskaden und die Variablenzuweisungen da drin noch 1:1 vom Original übernommen, aber insofern schon optimiert, dass die Offsets für die Array-Zugriffe nicht immer wieder neu berechnet werden und überhaupt etwas intelligenter mit den Registerinhalten umgegangen wird.
Klar, Optimierung ist immer gut aber um diese Jagd nach dem Topspeed nachzuvollziehen, fehlt mir die nötige Kenntnis. Ich kenne bisher nur Basic, bzw. Habe damit mein erstes, großes Programm geschrieben und wenn meine Grafik 30FPS stemmt trotz der "langsamen" Musik Abspielroutinen, denke ich mir, langt doch. Klar, knapperer Code bringt auch immer weniger Speicherhunger mit sich.
zatzen hat geschrieben: Ja, Set/GetIntVec schreibt/liest nur je einen Pointer an ner Adresse, wäre blöd gewesen dafür extra die Unit DOS zu brauchen.
DAS stimmt allerdings.
Dosferatu hat geschrieben: Naja, INT-Vektoren setzen/holen ist ja wohl total einfach.
Stimmt, sagte ich bereits.
Dosferatu hat geschrieben: Und wo die Vektoren liegen, dürfte ja wohl klar sein: Ganz vorne. (also bei $0000:$0000 bis $0000:$03FF)
Ja, ist auch klar.
Dosferatu hat geschrieben: Wie wär's damit?
(Hab ich nur mal fix zusammengetackert.)
Super. Praktische, kleine Routine.
zatzen hat geschrieben: Dieses db 067h ist das richtig davor? Oder doch db 066h? Also weil man ECX will...
En waaaas? :shock:
Ein bisschen DOS kann oft mehr als ein Haufen Fenster.

Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von zatzen »

Thomas hat geschrieben:Klar, Optimierung ist immer gut aber um diese Jagd nach dem Topspeed nachzuvollziehen, fehlt mir die nötige Kenntnis. Ich kenne bisher nur Basic, bzw. Habe damit mein erstes, großes Programm geschrieben und wenn meine Grafik 30FPS stemmt trotz der "langsamen" Musik Abspielroutinen, denke ich mir, langt doch. Klar, knapperer Code bringt auch immer weniger Speicherhunger mit sich.
Wirklich wichtig ist Geschwindigkeit z.B. bei Sprite-Routinen. Wie nötig das mit der Übersetzung der Player Routinen nach Assembler war sei mal dahingestellt, aber es gibt mir ein besseres Gefühl wenn das möglichst knapp gehalten wird, und ich habe das einfach mal aus einem Idealismus heraus gemacht, auch weil ich eine Weile lang nicht mehr programmiert habe, hatte einfach Lust drauf. Als ich zuletzt in echtem Dos programmiert habe hatte ich einen Pentium 200 MMX, der frisst sich natürlich so ziemlich durch alles schnell durch, und so ist es keine Kunst für so einen ein Spiel zu schreiben das performt.

So ein db 66h im Assembler-Code ist ein Operand-Size Prefix. In Pascal sind erstmal alle Zugriffe auf 16 Bit Daten ausgelegt. Will ich jetzt aber ein 32 Bit Register verwenden (also z.B. ECX statt CX), muss ich vor einer entsprechenden Anweisung ein db 66h schreiben. Also man könnte es so erklären, "db 66h; mov ax, 0" entspricht letztlich "mov eax, 0". Der integrierte Assembler von Pascal kennt eax, ebx, ecx usw. nicht, daher muss man das so machen.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Thomas
DOS-Kenner
Beiträge: 426
Registriert: Mi 22. Jun 2016, 12:29
Wohnort: Nähe von Limburg / Lahn

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von Thomas »

Aaah ok, alles klar. Ein workaround sozusagen. Stimmt, nur 286 kompatibel, da war etwas.
Idealismus find ich Klasse. Ich mein, ich wollte das ja auch nicht kritisieren, es kam nur so ehrgeizig rüber. Ich will halt kein professionelles DOS Spiel machen wie es seiner Zeit zB von LucasArts kam.
Danke für die Aufklärung.
Ein bisschen DOS kann oft mehr als ein Haufen Fenster.

Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von DOSferatu »

Kurze Erklärung: Bei JECXZ (im Gegensatz zu JCXZ) würde man ja Präfix $66 erwarten, das stimmt. Aber bei DIESEM EINEN Befehl hat intel sich damals wohl für $67 entschieden - keine Ahnung wieso, aber es ist definitiv so - vielleicht, weil es ein Sprung ist, vielleicht weils der einzige bedingte Sprung ist, der nicht auf ein Flag, sondern ein generelles Register reagiert... keine Ahnung. Aber für Wandlung von JCXZ in JECXZ muß es definitiv mit Präfix $67 erfolgen.
(Als es damals, wo ich das ausprobiert habe, mit db $66 nicht ging, war ich schon davon ausgegangen, daß es unter 16bit gar nicht möglich ist, damit auf ECX zu reagieren. Dann hatte ich recherchiert und das mit der $67 herausgefunden.)

Achja, noch was:
1. Ich finds schade, daß man damit nur auf =0, aber nicht auf <>0 prüfen kann.
2. Bei dem Befehl muß man beachten, daß es von dem Befehl (JCXZ/JECXZ) NUR eine SHORT JMP Variante gibt (also max. -128 bis +127 Bytes vor/nach dem Befehl), d.h. kein NEAR JMP (-32768..+32767).

Noch etwas zu CALLs (weil die Frage aufkam): CALLs im gleichen ASM-Block sind (außer wenn anders angegeben) immer NEAR. Und die NEAR brauchen auch maximal ne Handvoll Zyklen (auf 486er z.B. 3, auf 386er ein paar mehr). Mehr Zyklen brauchen schon FAR CALLs. Die meisten werden für so Taskwechsel gebraucht (klar). Aber sowas machen wir ja nicht unter 16bit-DOS.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von zatzen »

Ich habe gestern den Code aktualisiert mit ein paar JCXZ hier und da, und da ist mir das mit dem Short Jump auch begegnet, es gab einen "Fehler beim erzeugen des Codes". Habe ich mir dann aber gedacht dass eben nur short jumps möglich sind.
Danke für die Erklärung mit den CALLs und dem 67h, letzteres ist ja etwas verwirrend das es ja eigentlich ein Address Size Prefix ist.
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: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von zatzen »

Ich habe eine neue Version: http://www.zatzen.net/btp11.zip

Ich hatte Lust, nochmal etwas an der Komprimierung der Patterns zu ändern.
Vorher war es ja so, dass ich einfach pro Pattern für jede Zeile ein Bit hatte, das angab ob die Zeile genutzt wird oder nicht. Also für 64 Zeilen 64 Bits, macht 8 Byte. Dadurch musste das Auslesen auch sequentiell erfolgen. Ich habe das jetzt auf den ersten Blick etwas komplizierter gemacht, aber letztlich ist es zum einen deutlich effektiver und man hat sozusagen "Random Access", wodurch sich das Auslesen der Patterns vereinfacht.

Nur falls Dich das näher interessiert:
Die neue Kompression greift umso stärker, je redundanter die Inhalte sind. Die Redundanzen werden rausgekürzt und die Ereignisse (also Byte-Triplets) in einer Tabelle gehalten. Das ergibt z.B. oft so um die 16 verschiedene Zeileneinträge pro Pattern. Manchmal mehr, manchmal aber auch nur eine Hihat, das wird dann besonders klein. Um richtig aus so einer Tabelle zu lesen braucht man jetzt noch pro Zeile, also 64x, eine Referenz in die Tabelle, und die muss jeweils nur so viel Bits haben wie zur Indizierung in die Tabelle nötig. Also wenn die Tabelle 9-16 Positionen hat dann braucht man 4 Bit, hat die Tabelle aber z.B. nur 2 Einträge (wie o.g. Beispiel mit Hihat) dann reicht 1 Bit. Also ergeben sich z.B. für ein Pattern mit 16 verschiedenen Ereignissen 3 x 16 + 0,5 (4 Bit) x 64 Zeilen = 80 Bytes, im Vergleich zu 192. Bei nur zwei verschiedenen Ereignissen ergibt sich 3 x 2 + 1/8 x 64 = 14 Byte statt 192. Grenzwertig wird es wenn es auf die 32 verschiedenen Ereignisse zugeht, dann haben wir 3 x 32 + 5/8 x 64 = 96 + 40 = 136 ... aber es lohnt sich noch. Bei 64 verschiedenen Ereignissen kann man natürlich nicht mehr komprimieren, allerdings noch für den Fall 33 verschiedene Ereignisse, 33 * 3 + 6/8 x 64 = 99 + 48 = 147 und noch ein bisschen weiter. Worst case wäre 64 verschiedene, dann haben wir 192 + 48 Byte, also 48 mehr, kommt aber eigentlich nie vor. Dann braucht es noch zwei Offsets und eine Information über die Bitbreite, das sind pro Pattern 5 Bytes.

So, aber ich denke das genügt jetzt auch, Deine Stage-Musik braucht als PCS jetzt nur noch 3476 Bytes auf dem Heap.

Von den Instrumenten werden auch nur noch die benötigten geladen.

Und ich habe hier und da noch etwas optimiert, und die Interrupt-Player-Routine setzt auch die Flag-Variable, so dass man ein Spiel danach takten könnte, auch wenn die Musik "von selbst" spielt.

Ich empfehle jedenfalls, im Endergebnis PCS Dateien (oder nenne sie wie Du willst) zu nutzen, weil die Konvertierung spürbar langsam ist ("loadmod" konvertiert ja beim Laden in das komprimierte Format, weil der Player dafür ausgelegt ist), und wie gehabt, load_pcs zu nutzen und loadmod nicht, erzeugt deutlich weniger Kompilat.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Thomas
DOS-Kenner
Beiträge: 426
Registriert: Mi 22. Jun 2016, 12:29
Wohnort: Nähe von Limburg / Lahn

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von Thomas »

Ja super!
Damit ich das richtig verstehe: Ich baue mir wie gehabt mein Mod und wenn es fertig ist lasse ich deinen Konvertierer darüber laufen damit ich das gewünschte PCS erhalte? Muss ja so sein da der Tracker noch kein PCS kann.
zatzen hat geschrieben: Von den Instrumenten werden auch nur noch die benötigten geladen.
Gute Idee aber leider unnötig. Wenn man im Tracker Instrumente erstellt, sie aber im Mod nicht nutzt, speichert der Tracker die nicht mit ab.
Ein bisschen DOS kann oft mehr als ein Haufen Fenster.

Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von zatzen »

Ja genau, ein MOD (oder PIS) fertig machen und zu PCS konvertieren.

Instrumente: Damit meinte ich, dass nur so viel Speicher reserviert wird wie benötigt, um die Anzahl der Instrumente zu halten. Vorher hatte ich das statisch auf 32 Instrumente gemacht, weil Instrument 32 (Stille) im Player benötigt wird. Hab ich jetzt anders gelöst.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Thomas
DOS-Kenner
Beiträge: 426
Registriert: Mi 22. Jun 2016, 12:29
Wohnort: Nähe von Limburg / Lahn

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von Thomas »

Ach so, ja jetzt hab ich es verstanden. Cool.
Ein bisschen DOS kann oft mehr als ein Haufen Fenster.

Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von zatzen »

Ich möchte zwischendurch noch auf etwas hinweisen: Die Interrupt-Routinen setzen voraus, dass das Register DS aufs Datensegment zeigt. Daher darf zu keinem Zeitpunkt irgendwo anders das Register DS geändert werden, da es sonst zu fehlerhaften Lese-/Schreib-Prozessen kommt. Pascal selbst ändert DS vermutlich nicht, aber das ist nicht sicher. Du selbst solltest in Assembler-Code DS auch nicht ändern. Ich kann das Problem lösen wenn ich dazu Zeit finde, die Interrupt-Routinen müssen dazu mit einem gewissen Trick modifiziert werden.
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: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von zatzen »

So, das Problem ist gelöst, die Interruptroutinen sind jetzt "idiotensicher", d.h. DS darf überall anders im Programm verändert werden. Hier ist die neue Version: http://www.zatzen.net/btp111.zip (Version 1.1 Revision 1)
mov ax, 13h
int 10h

while vorne_frei do vor;
funkheld
HELP.COM-Benutzer
Beiträge: 27
Registriert: Di 25. Feb 2020, 10:42

Re: Allgemeine Hilfestellung zum Programmieren unter DOS gesucht

Beitrag von funkheld »

Wer benutzt das Ding eigentlich.
Für wen ist es gedacht?

Oder wie bei mir die Programme , ein Lustmodell..
Antworten