Eigenes Videoformat

Diskussion zum Thema Programmierung unter DOS (Intel x86)
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von DOSferatu »

zatzen hat geschrieben:
DOSferatu hat geschrieben:Ich plane schon länger, mal einen Inline-Assembler direkt für Pascal-Sourcen zu coden, der alle Befehle assembliert, aber nur, wenn er erkennt, daß es >=386er Befehle sind, diese dann "inlined" (als Bytes/Words/DWords) hinschreibt und dahinter auskommentiert {...} den vorher eingegebenen ("echten") Befehl.
Exakt die Idee hatte ich auch, aber wenn Du das machst überlasse ich das Dir, ich traue Dir da mehr zu.
Naja, zuviel der Ehre. Schauen wir erstmal, wie es wird. Obwohl ich wirklich sagen muß, daß ich so Parser-Zeug inzwischen im Halbschlaf mache. Muß bloß mal genug Zeit+Lust finden. Aber inzwischen ist es eben auch für mich so, daß ich immer mehr von dem 32bit-Zeug benutze und da wäre es auch für mich selbst weniger aufwendig, statt immer das Mod-R/M-Byte manuell hinzumurksen, das mal zu automatisieren. Wofür hat man schließlich 'n Computer?
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ja, die ASM86FAQ sollte man unbedingt immer griffbereit haben. Unverzichtbares Werk, IMO.
Benutzt Du da auch, so wie ich, meine FAQREAD.EXE?
Gut, im EGA-Modus zieht der FAQREAD gleich auf. Bisher war vor allem die Wortsuche im simplen Windows-Editor blitzschnell, und man kann nach oben wie nach unten suchen.
Bemerkungen dazu:
Wenn man im FAQREAD einfach irgendwelche Buchstaben- oder Zifferntasten drückt, geht automatisch die Eingabe für die Suchfunktion an, mit dem Zeichen als erstem Zeichen drin. Mit Leerzeichen auch, aber da steht dann die letzte Sucheingabe drin.

Als Somderfunktion extra drin: Wenn man mind. bei diesem ersten Zeichen Shift gedrückt hält, wird automatisch die senkrechte Linie (ASCII 179) und ein Leerzeichen davor gesetzt und dann erst das Zeichen. Das dient dazu, schneller die Befehle zu finden, weil die ja immer in so Rahmen stehen. (Bei den bedingten Sprüngen muß man nach JCC suchen, weil der das in ASM86FAQ zusammengefaßt hat für alle.)

Wenn man nur Enter drückt, wird die letzte Suche ab aktueller Stelle wiederholt, d.h. fortgesetzt.

Und ja, man kann nicht rückwärts suchen, aber: Wenn man Pos1 drückt, wird an den Anfang des "aktuellen" Textes gewechselt (also wenn man z.B. eine Sektion ausgewählt hat, dann an diese Sektion. Um an den Anfang des GANZEN Textes zu wechseln, muß man Strg+Pos1 drücken.

Beispiel, wenn man in der Befehlssatz-Sektion drin ist (wo die ganzen ASM-Befehle drin stehen:
Pos1 Shift+MOV Enter
Und er geht an die Stelle, wo | MOV steht.

FAQREAD ist ja, im Gegensatz zu anderen Viewern/Editoren direkt an ASM86FAQ.TXT angepaßt und hat deshalb entsprechende "Sonderfunktionen".

Na egal. Ich will Dir das nicht aufdrängen - wollte es nur erwähnt haben.
zatzen hat geschrieben:Aber der korrekten Ascii-Darstellung halber und auch um mir die Kapitel mal ordentlich gegliedert anzusehen, werde ich auch mal mit der FAQREAD arbeiten. Bloß wenn man nur mal eben schnell nochmal nachgucken will, wievel Zyklen ein Befehl wie und wann braucht, dann ist das einfach in meinem Fall über den simplen Windows-Editor direkter.
Ja, wie gesagt - siehe oben.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ich kriege schon immer die Krätze von 0f531h -Hexzahlen, weil ich seit jeher die $f531 -Nethode gewöhnt bin... Da seh ich dann 'ne Zahl... und erst am Ende: Achso, ein h, also hex...
Das kann ich nachvollziehen, ich schreibe beim Programmieren z.B. auch alles klein und bin irritiert sobald jemand Großbuchstaben verwendet. Das macht es für mich irgendwie auch unleserlicher. Bei Assembler schreibe ich lieber so, wie es offiziell Standard ist mit den Hex-Werten, auch wenn es innerhalb Pascal ist. 0x???? ist wohl auch Standard (in Pascal wohl nicht), aber das finde ich wiederum hässlich und unleserlich.
Großbuchstaben - ja, die verwende ich oft für Register, um sie von allem anderen abzugrenzen. Und ich bin eher irritiert von diesem "alles kleinschreiben"... Für mich ist das so 90er-Jahre-Mist: Als "das Internet/WWW" so bekannt/beliebt wurde und man die URLs eingegeben hat (und immer alles klein), wurde das so "modern": Plötzlich hatte jeder Verein/jede Firma und jeder Hanswurst irgendwelche Firmennamen/Logos, die alles kleingeschrieben haben, weil "internetmäßig" und damit "cool". Ich fand das damals schon lächerlich und daran hat sich nichts geändert. Sogar beim Chatten achte ich auf Großschreibung und Grammatik. Ist bei mir so drin.

Zum "Hex-Standard":
Also erstens gibt es nicht *EINEN* offiziellen Standard für Angabe von Hex-Werten - und zweitens ist die $-Methode MINDESTENS so alt wie die andere - denn die ganzen 8-Bit-Kisten benutzen $.

Falls es Dich interessiert: 0x???? ist übrigens die Notation, die in C und Verwandte benutzt wird - und in allem, was seine Syntax von C ableitet. Und ja, ich finde diese ebenfalls häßlich. (Außerdem: Gleich ZWEI Zeichen, um das auszudrücken... Nerv.)
zatzen hat geschrieben:Wie Taktefressend das Markieren der Spritebereiche wird werde ich erst erfahren wenn ich mich um die Programmierung davon kümmere.
Klar, vorher kann man bei so komplexen Dingen sowieso keine gültige Aussage treffen. Bewiesen ist, was auf der Maschine funktioniert.
zatzen hat geschrieben:Erstmal bange ich um die Daten meiner wichtigsten Festplatte, weil so ein Billigstromkabel abgeschmort ist. Kurzschluss zum Glück im Kabel und nicht in der Platte, das macht Hoffnung.
Ui! Ich hoffe, es ist nichts Schlimmes? Ich hätte auch gerne noch ein paar Ersatz-Festplatten in Größe 8 GB - aber schätze, durch die derzeitige Retro-Welle sind die schwer zu kriegen, weil: Welcher DOS-Fan würde die nicht selber haben wollen?
zatzen hat geschrieben:
DOSferatu hat geschrieben:Du mußt natürlich trotzdem den restlichen Offset mit einbeziehen, auch wenn Du die unteren 16 Bytes davon ignorierst! Richtig ist:

Code: Alles auswählen

inc(blkinfo_seg,succ(blkinfo_ofs shr 4)); blkinfo_ofs:=0;
Danke! Aber 16 Byte mehr alloziieren ist korrekt, oder reichen auch weniger? Sorry falls die Frage dumm ist, aber ich meinte Du sagtest mal sowas.
Naja, wenn man genau drüber nachdenkt, würden 15 Bytes mehr reichen, nicht wahr? Nur, dann wäre der Code etwas komplizierter, da macht das eine Byte auch keinen Unterschied. Denn, wenn man mit nur 15 Bytes mehr reserviert, könnte man ja Glück haben und es landet auf einer ganzen 16er Adresse und braucht nicht "adden". Dafür würde der Code also dann lauten:

Code: Alles auswählen

inc(blkinfo_seg,(blkinfo_ofs shr 4)+ord(blkinfo_ofs<>0)); blkinfo_ofs:=0;
Nur, wie ich ja immer erwähne: Wir haben Von-Neumann-Computer. Code und Daten benutzen denselben Speicher. Wenn man 10 Bytes mehr Code benutzt, um an anderer Stelle 1 Byte Daten zu sparen, das klingt nicht nur sinnlos, das IST es auch. Relativ häufig ist mir in den letzten Jahren aufgefallen, daß man mitunter mit der simpelsten Lösung den meisten Speicher UND Performance einspart. Klingt komisch, ist aber so. Manchmal wurstelt man ewig rum, hat am Ende eine dicke komplexe Routine, die riesengroß ist, also keinen RAM spart und auch noch langsam ist, also keine Rechenzeit spart. Da fragt man sich am Ende: Wozu brauch ich die dann...

Zu GetMem und "16 Bytes mehr reservieren":
Allerdings habe ich mal irgendwo gelesen, daß Pascal bei GetMem immer nur in 8er-Schritten Speicher reserviert, so daß untere 4bit des Offset also nur 0 oder 8 sein könnten. Weiß aber nicht, ob das stimmt. Ich verlasse mich da auf nichts - und mit 8er-Offset kann man ja genausowenig anfangen wie mit den restlichen außer 0, wenn man ganze Segmentanfänge braucht.

Und beim Reservieren für "speziellen RAM" (Du weißt schon: Den für den DMA-Puffer für den Soundblaster) muß man sowieso noch etwas kreativer vorgehen, um nicht genau in die Arschritze des Speichers zu fallen.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Man kann auch FS und GS direkt (mit Speicher) laden, da muß man auch nicht über Register gehen.
Das weiss ich, ich konnte nur dem Online Assembler keine konkreten Speicheradressen der Variablen übergeben...
Nur mal so für Dich

Code: Alles auswählen

db $8E,$26;dw Segment1 {=mov FS,Segment1}
db $8E,$2E;dw Segment2 {=mov GS,Segment2}
Gilt natürlich nur, wenn Segment1 und Segment2 globale Variablen sind (also an DS). Aber man kann ja auch hier den Segment Override Präfix nutzen, wenn der Kram woanders liegt. Naja, vielleicht bau ich wirklich mal diesen Pascal-Inline-Assembler, dann stellen sich diese Fragen nicht mehr.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ja, die Idee klingt zwar nett - aber ich wage blasphemischerweise zu glauben, daß man damit (gegenüber der "Einzelprüfung") nicht viel spart, weil der "kompliziertere" Test den Speedgewinn wieder auffrißt. Muß man sehen...
Ja, ich fand es erst gut, mit JCXZ mit 2 Takten direkt 2 Blöcke abfangen zu können, aber in der Praxis wird man stets mehr inaktive Blöcke haben als aktive, und daher sollte die Prüfung so ausgelegt sein, dass sie vor allem für den Fall "nichts tun" schnell ist.
Ja, immer auf den häufigeren Fall optimieren.
(Und ja, JCXZ kann zwar nützlich sein - aber weil es nur Short Jumps kann, muß man da manchmal zu viel Code "umbauen", um nicht außerhalb des Abstands zu geraten. Da muß man dann abwägen, ob etwas anderes an der Stelle praktischer ist.)

Wenn z.B. ein Fall selten genug passiert, kann sogar ein Raus-und-zurück-Springen (obwohl es 2 Sprünge sind) im Ganzen immer noch mehr performen als ein "Übersprung". Hatte ich ja schonmal erwähnt.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Aber an der Stelle LEA, wo steht {obere 16 bit egal}... Egal sind die nicht. Im 16bit-Mode müssen bei so Adressierungen die oberen 16bit der Zieladresse =0 sein, sonst rastet die Maschine aus.
Wenn ich aber doch adressiere mit mit z.B. "mov eax, fs:[si+bp]", ist es dann nicht egal wie die oberen 16 Bit von ESI und EBP sind? Wenn doch, stünde das etwas konträr dazu dass Du z.B. sagtest, man könnte durch Rotation sogar einen zweiten Stack haben. Oder überhaupt, wenn ich mich jetzt nicht total irre, habe ich in meinem ZSM Player EBP verwendet als 16 Bit Adressierung, und die oberen 16 Bit für den Carry-Trick.
Wenn ich natürlich ein 32 Bit-Konstrukt habe wie "mov eax, fs:[ebx*8 + ebp]", dann ist das was anderes.
Ja, aber ich glaube, ich hatte das in Bezug auf eine Stelle in Deinem Text geschrieben, wo Du genau so eine ebx*8-Geschichte benutzt hast und dahinter als Kommentar {obere 16bit=egal} oder sowas. Wenn man natürlich normale 16-Bit-Adressierung benutzt, SIND die oberen 16bit egal (denn sonst würde der erwähnte "Carry-Trick" ja keinen Sinn machen, weil man den ja für "zählende" Register benutzt und die "zählen" ja meist deshalb, weil sie irgendwas indizieren sollen.)
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ja, Du optimierst immer ausgehend vom 486er, oder?
Ja. Das hat zum einen damit zu tun, dass ein 386er zu langsam für meine Vorhaben wäre, ein Pentium dagegen so schnell dass man praktisch fast auch ohne Assembler auskäme. Und Dosbox spielt mit rein, weil heutige Rechner und wohl sogar Smartphones in der Lage sind, einen 486er zu emulieren. Bei Smartphones bin ich mir aber nicht sicher.
Ach, was interessieren mich Smartphones... Und: So'n 486er ist 'ne ziemliche Powermaschine - wenn man da bis an die Ecken optimiert, ist es echt erstaunlich, was diese alten Kisten können!
zatzen hat geschrieben:
DOSferatu hat geschrieben:Naja, es gibt außer Performance noch andere Eckdaten, die ein Programm haben kann. Selbstredend könnte man auch 16000x mov EAX,ES:[xxx];mov DS:[xxx],EAX; machen und so - die totale Oberklasse des "kopiere 320x200 Screen" - aber, wie ich immer so sage: Trade-Off (Speicher/Performance)...
Das wäre Irrsinn bzw. gar nicht möglich. So ein unrolled Block für einen 8x8-Block, das sind 192 Bytes Kompilat, das mal 1000 und es passt überhaupt gar nicht mehr in ein Segment, und ein Drittel des ganzen Speichers ist futsch, wenn es denn möglich wäre.
Natürlich wäre das Irrsinn - Dir ist ja hoffentlich aufgefallen, daß ich obige Aussage nur gebracht habe, um zu verdeutlichen, daß man es, bei aller Optimiererei, auch bis zum völligen Blödsinn übertreiben kann. D.h. ich WOLLTE, daß auffällt, was das für ein Unfug wäre.
Um z.B. 20-100 Zyklen außerhalb einer Schleife zu sparen (die sich also nicht "vermultiplizieren" würden, weil nicht in Schleife), würde ich z.B. nicht mehrere kB Speicher opfern. Das meinte ich mit "Trade-Off Speicher/Performance)".
zatzen hat geschrieben:
DOSferatu hat geschrieben:OK, jetzt hast Du es wohl ENDLICH selbst mal gemerkt, wie wenig DOSbox als Benchmark geeignet ist.
Ja allerdings. Ich habe nur leider keinen 486, könnte nur einen Pentium wieder fit machen, aber das hilft mir dann auch in dem Fall nicht viel. Ich kann nur versuchen, möglichst performant zu programmieren, auch wenn ich es nicht sicher überprüfen kann. Es kann mir aber helfen, Feedback z.B. von Dir zu bekommen, wie geschehen bei ZSMplay oder dem ollen ZVID(1).
Ja, immer gern. Kein Problem. Wie gesagt: Ich will damit nicht nerven und keinesfalls DOSbox schlechtreden! DOSbox ist ein großartiges Programm, um damit DOS-Zeug überall laufen lassen zu können! Es ist eben nur, weil Emulator, auch optimiert und daher für Benchmarking zum Vergleichen mit echten Maschinen nicht so geeignet. Aber DOSbox ist ja auch kein Benchmark-Tool und soll es auch nicht sein.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Also: Zugriff auf RAM ist *kein* gleich-schneller Ersatz für Arbeit mit Registern. Punkt.
Was auf ne Weise ja eher erfreulich ist, dann fühlt sich die Arbeit mit Registern direkt wieder viel sinnvoller an.
Ja, und das gilt für JEDEN Rechner und JEDE CPU: Immer Register gegenüber RAM bevorzugen! RAM-Zugriffe sind IMMER langsamer als CPU-internes Zeug.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Wenn man sowohl sowas will als auch Unroll-Performance, wäre es wohl am besten, so eine Art "Darstellungs-Skript" zu erfinden, wo die Sachen quasi von einem externen Tool in so ein Format "vorberechnet" werden und von einer simplen aber superschnellen Routine nur noch hingeballert werden müssen.
Das klingt interessant. Aber Du meinst nicht etwa dass Assembler-Code erzeugt wird?
Also naja: Man könnte irgendeinen Bytecode erzeugen, der dann interpretiert wird. Aber echte ASM-Opcodes wäre natürlich die Königsklasse. Nur sollte das Programm, das diese Daten/Code erzeugt natürlich keinen Mist bauen. Und es sollte "wissen", welche Register es zu benutzen hat - sonst könnte man das ja kaum irgendwo einbauen.

Will sagen: Echte Opcodes sind ja auch bloß Bytes. Da ist keine Magie drin - auch wenn es sich oft so anfühlt, wenn man sieht, wie die Kiste abgeht, sobald man mal mit Assembler draufhaut...
zatzen hat geschrieben:
DOSferatu hat geschrieben:Immer diese quadratischen Pixel. Was ist daran so wichtig?
Unter anderem wären die auch praktisch wenn man mal was z.B. auf Youtube zeigen will. Entweder muss man dann die Aspect Ratio über Bord werfen oder man handelt sich bei niedrigeren Auflösungen ziemliche Unschärfen ein. Die heutige "Computerwelt" hat nunmal quadratische Pixel, deshalb wäre es einfach praktisch.
"Die heutige Computerwelt" kann mich mal kreuzweise. Ich baue mein Zeug für VGA/VESA/SB auf 486er. Wenn "die heutige Computerwelt" es nicht schafft, das gescheit darzustellen, ist mir das ja sowas von egal. Die fühlen sich doch immer so toll mit ihrem Windows und ihren Gigahertz/Terabyte-Kisten. Dann sollten sie doch kein Problem damit haben, Zeug von 30 Jahre alter Technik darzustellen... Wenn sie das nicht können, d.h. nichtmal eine simple Skalierung hinbekommen, dann sollten sie ihre Nasen etwas weniger hoch tragen.
zatzen hat geschrieben:So wichtig ist es mir aber gar nicht, sonst würde ich mich aufs emsigste mit Mode-X beschäftigen. WENN ich dann mal ein Video von nem Spiel auf Youtube hochladen will darf ich dann eben nicht vergessen, die 320x200 auf z.B. 1440x1080 zu resamplen. Das klingt nach Verschwendung, aber auf Youtube kursieren längst Videos mit 4K und höher. Und es gibt da auch so Leute die 320x200 Spiele mit KI auf HD "upscalen". Ganz interessant.
Naja, das ist ja nur nötig, weil keine CRTs mehr benutzt werden, die das alleine so darstellen wie es muß, sondern diese TFTs und so Zeug, wo jede niedrigere als die Maximalauflösung immer irgendwie scheiße aussieht und man deshalb da software- (oder mittlerweile auch hardware-) mäßig interpolieren muß, um die Abscheulichkeit etwas abzumildern.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Also, wenn Du *DAS* schon schöne Grafik findest, hast Du, was Grafik angeht, wirklich außerordentlich niedrige Ansprüche.
Kommt immer drauf an. Ich hab viel am C64 gesehen was dagegen lieblos wirkte.
Naja, ich hab mir schon Mühe gegeben, damals (vor 27 Jahren!). Aber ich bin nunmal kein talentierter Grafiker und da hab ich schon wesentlich besseres gesehen - auch am C64.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Hab Zelda nie gespielt.
Ein Blätterer. Ich war fasziniert weil es das erste Spiel war das mir untergekommen war, wo man in alle vier Himmelsrichtungen die Welt erkunden konnte. Da war ich aber falsch motiviert, denn es ist ein Action-Spiel und es sowas kostet mich nerven. Hätte ich mal lieber direkt Point & Click Adventures entdeckt, aber sowas gab es lange auf dem NES nicht. Das erste dort war wohl Maniac Mansion.
Ach, ich hab nichts gegen Actionspiele. Nur diese sog. "rundenbasierten Kampfsysteme" und allgemein so Rollenspiel-Kram geht mir so richtig aufn Keks. Aber, auch wenn ich's nicht gespielt habe, weiß ich natürlich, wie Zelda aussieht.
zatzen hat geschrieben:
DOSferatu hat geschrieben:[TheGame3-GUI]Ich könnt's Dir ja mal schicken, damit Du siehst, wie es aussieht und ob Du damit arbeiten könntest/würdest. Aber ich vermute, Du würdest das sowieso nicht anfassen.
Ich kann es mir gerne ansehen. Nur würde ich nicht anfangen wollen damit etwas zu basteln. Das wäre anders wenn ich nur möglichst bald ein Spiel machen wollen würde egal wie, und ohne meine eigenen Techniken.
Naja, die Daten die das Ding auswirft, könnt man ja auch konvertieren, falls man das anders braucht. Ich bin sowieso immer mehr ein Freund von externen Konvertern geworden - weil, wenn man in ein Tool quasi so "eierlegende Wollmilchsau"-mäßig das alles einbaut, dann wird das Ding groß und langsam und belegt für seinen Code viel Speicher, den man lieber gern mit Daten belegen würde.

Aber ich muß da vielleicht nochmal dran - inzwischen mag ich das Ausgabeformat auch nur noch so halb, weil ich ja diese flexibleren Levels will (hatte das mal erwähnt).
Aber ich hau das einfach mal rein:
http://www.imperial-games.de/z/thegame3.zip
zatzen hat geschrieben:Du kennst vielleicht auch AGS (zum Point & Click Adventures machen). Das Ding hat ne dicke Anleitung, da muss man sich richtig reinknien. Ich hatte mich schonmal drangesetzt weil ich wirklich mal dachte, ich mache ein Adventure, hauptsache das Spiel wird fertig.
Kenn ich nicht. Habe aber mal selber sowas angefangen damals, das hieß naheliegenderweise auch AGS (=Adventure Game System). Ich sag's mal so: Ein Adventure-Bau-System könnt ich heutzutage noch leichter bauen als das, was ich derzeit so mache. Ich wüßte genau, wie ich ein Point&Click Adventure technisch bauen würde. Vielleicht mach ich auch mal irgendwann ein Adventure. Eine Idee dafür hab ich auch schon seit fast 30 Jahren.
zatzen hat geschrieben:Aber dann hab ich mir gedacht, bevor ich jetzt Wochen damit zubringe, dieses Programm zu lernen, da programmiere ich lieber selber von Grund auf.
Klar - ich bau auch lieber mein eigenes Zeug. Mir geht's ja (genau wie Dir) nicht nur darum, mit allen Mitteln Hauptsache ein Spiel fertig zu kriegen, sondern natürlich auch, das alles selbst zu machen, um sich dabei zu entwickeln und zu lernen. Immer nur fertiggemachtes Zeug zu benutzen - das ist was für Nicht-Coder...
zatzen hat geschrieben:
DOSferatu hat geschrieben:Deshalb jetzt also - standesgemäß für'n echten DOS-Fan! - ein Texteditor im 80x25-Zeichen-Textmode!
Könnte natürlich auch für mich praktisch sein, trotz Emulator. Kommt nur drauf an, wie man den Text reinbringt, auf den man antworten will. Ich kopiere direkt aus dem Forum in den Editor, muss dann aber alle Tags per Hand schreiben.
Naja, im Forum gehe ich auf zitierte Antwort und kopiere dann aus dem Eingabefeld das raus - dann sind die Tags drin. Und den Text editiere ich dann extern in meinem Editor und dann überschreibe ich das aus dem Eingabefeld später mit dem selbsteditierten Text.

Habe natürlich Hotkeys hier drin, um automatisch diese Fett/Unterstrichen/Kursiv zu setzen und auch Code und Quote. Der merkt sich beim Einlesen auch den ersten Quote-Tag (der dann auch den Nick enthält und so), so daß man den immer wieder benutzen kann... Weiß nicht, ob ich es gut genug erklären kann.

Na jedenfalls färbt er, farbig aufsteigend, gequoteten Text ein, so daß man gleich sieht, ob man irgendwo vergessen hat, einen quote oder anderes Tag zu schließen usw.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Sobald Du dann auch mal einen 4000000000 Zeichen langen Text damit tippst, gebe ich zu, daß das nützlich ist.
Darauf komme ich gerne in etwa 5000 Jahren zurück.
Ich kann natürlich nicht in die Zukunft sehen, aber basierend auf statistischen Daten vermute mal, daß ich dann tot sein werde.
zatzen hat geschrieben:Vielen Dank für Deinen 0-255 begrenz- bzw. saturier- Code. Da wäre ich wohl so nicht drauf gekommen.
Immer wieder gern. Ich freue mich ja auch, wenn jemand mal *richtig* programmiert - also nicht in irgendwelchem Interpreter- und/oder Skriptmist.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Kleiner Tip: Alles, was wie NEG zwei oder mehr Register setzt, kann man danach z.B. mit LAHF abfangen:
SIGN ist Bit7, Zero ist Bit 6, Bit5 ist immer=0... Nur mal so:

Code: Alles auswählen

lahf
mov BX,AX
shr BX,13
mov/and/or/xor irgendwas,CS:[@TABLE+BX]...
Wie findst'n das?
Und wenn man nach lahf noch ror ah,1 macht, hat man Bit7=Carry, Bit6=Sign, Bit5=Zero, Bit4=0... Und kann danach mit 'nem indizierten Sprung oder so per Entscheidungstabelle quasi 'ne Menge Kram abdecken...
(Anm.: Hatte mich da übrigens verschrieben, meinte natürlich Flags. Aber Du hast ja verstanden, was ich meinte.)
zatzen hat geschrieben:Das werde ich in Zukunft berücksichtigen, da ergeben sich ja wirklich neue Möglichkeiten, mit Flags direkt in eine Tabelle reinzugehen. Nur konkret für die Blockprüfung geht es wohl nicht schneller als ein Word einzulesen und dann auf sign/carry und parity zu prüfen.
Ja, selbstverständlich muß man immer den konkreten Fall berücksichtigen. Wenn man eine an sich coole Idee hat, dadurch aber nichts gewinnt oder spart, ist die Idee vielleicht trotzdem cool - nur für den aktuellen Fall nicht brauchbar.
zatzen hat geschrieben:Aber man lernt immer noch dazu von Dir. Gerade eben wurde auch mir klar, dass "jmp CS:[@TABLE+BX]" wohl auch gültig ist.
Ja, in Pascal muß man da "jmp CS:word ptr[@TABLE+BX]" schreiben. Man muß da öfter mal "word ptr" oder "byte ptr" schreiben, auch wenn es eigentlich klar ist, daß es selbsterklärend ist, weil es im aktuellen Fall für gar keine andere Wortbreite definiert ist. Aber irgendwann gewöhnt man sich's eben an...
zatzen hat geschrieben:Würdest Du sagen dass es besser ist einen Tabellensprung so zu formulieren anstatt erst ein Register mit "CS:[@TABLE+BX]" zu beladen und dann "jmp REG"? Man könnte ja meinen man spart einen Zyklus und braucht kein extra-Register bzw. kann BX hier auf seinem Wert belassen.
Da würde ich nie eine generelle Aussage dazu treffen, sondern: Hängt immer vom Fall ab. Beispiel: Man braucht den Sprung nur einmal. Dann natürlich gleich springen, ohne vorher noch ein Register laden und normal jumpen. Anders sieht's dann wieder aus, wenn man den gleichen Sprung öfter/mehrmals braucht. Da macht's natürlich Sinn, den Ergebniswert gleich in einem Register liegen zu haben. Kommt dann aber auch wieder drauf an, ob man noch ein Register übrig hat.

Und Du kennst mich ja: Wenn mir die Register ausgehen, fang ich an, selbstmodifizierenden Code zu fabrizieren. "Multitasker" sehen das aus naheliegenden Gründen gar nicht gerne und finden das "unsauber". Aber was interessieren die mich?
zatzen hat geschrieben:
DOSferatu hat geschrieben:Vor 'ner Weile (letztes Jahr, kurz bevor das alte Coding-Board geschlossen wurde), hatten die mal so Wettbewerb, mit möglichst kompliziertem undurchsichtigem Code irgendwas zu machen, z.B. "HELLO WORLD" auszugeben. Mein Beitrag war in 100% Assembler. Falls Interesse, kann ich das ja, inkl. Source, mal hochladen...
Ja, warum nicht. Vielleicht blicke ich ja doch ein klein wenig durch und kann noch was lernen. Was man so im Netz an Assembler findet ist meistens so trivial und ineffektiv wie meine ersten Assembler Programme Ende der 90er.
Naja, der Witz dran sollte ja sein, daß der Code möglichst undurchsichtig und aufwendig ist. Da ist 'ne Menge unnötiges Voodoo drin und manches absichtlich ineffizient gemacht, damit man schwerer durchschaut, was es macht...

Na, ich häng's mal an. Kannst es Dir ja compilieren und sehen, was es tut. Und dann den Kram auseinandernehmen um zu sehen, was es macht, um das zu tun. Ich sag's nur vorher: Falls Du keine Lust hast, von kryptischem Code genervt zu sein, dann tu es Dir nicht an.
http://www.imperial-games.de/z/hello.zip
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Sorry dass so viel Zeit vergangen ist.
Ich war noch beschäftigt mit neuen (naja, Hardware von 2013) Computer fit machen und dann habe ich für einen Freund noch ein Tool in Freepascal gemacht.
DOSferatu hat geschrieben:Obwohl ich wirklich sagen muß, daß ich so Parser-Zeug inzwischen im Halbschlaf mache.
Vielleicht kannst Du mir ein paar kleine Tipps geben, wie man am elegantesten eine XML parst. Im oben genannten Tool verarbeite ich .NIF Dateien, die enthalten Rohgrafiken und Definitionen für 3D Objekte, und es scheint so als wären die ähnlich wie XMLs, nur Binär. Scheinbar keine Längenangaben zu den Chunks, dafür sind die Chunknamen deutlich länger als 4 Bytes, so dass es unwahrscheinlicher wird dass sie zufällig entstehen.
DOSferatu hat geschrieben:[FAQREAD]Na egal. Ich will Dir das nicht aufdrängen - wollte es nur erwähnt haben.
Ja, es ist ein tolles Tool das ich auf einem reinen DOS-Rechner sicher benutzen würde. In einer Windows-Umgebung wo ich zwei Dos-Box'en laufen haben müsste um in einem Fenster programmieren zu können und im anderen das FAQREAD zu haben setzt sich pragmatisch dann doch eher der simple Editor als Betrachter durch. Das wäre anders wenn Windows noch 16 Bit Code unterstützen würde.
Ich denke es ist keine Schande wenn ich gestehe dass ich die Nutzung von Multitasking während der Softwareentwicklung praktikabel finde. Ich kann einen guten Hex-Editor (z.B. HxD) geöffnet haben und er updatet automatisch die geöffneten Dateien in die mein Programm schreibt, so kann ich sehr bequem debuggen ohne Programme ständig umschalten, beenden oder starten zu müssen. Und selbst wenn ich in Freepascal programmiere kann ich mir parallel ein Konsolenfenster (cmd) öffnen und bei einem Programm mit Parametern diese einfach dort eintippen (mit dem Komfort der Kommandozeile), und muss das nicht umständlich in der IDE tun.
Die Lösung was FAQREAD betrifft wäre eine 32 Bit Windows Version, evtl. mit Freepascal gemacht. Aber natürlich können wir uns einig darüber sein, dass tatsächlich Windows bzw. die Prozessorentwickler die Übeltäter sind, weil sie keine 16 Bit mehr unterstützen. Mutmaßlich hat das aber auch technische bzw. Performance-Gründe, und nicht nur verkaufspolitische.
DOSferatu hat geschrieben:Großbuchstaben - ja, die verwende ich oft für Register, um sie von allem anderen abzugrenzen. Und ich bin eher irritiert von diesem "alles kleinschreiben"...
Es ist alles Gewöhnung, zumindest bei mir. Ich habe angefangen mit QBASIC, und als ich auf Pascal umgestiegen bin fand ich es erstmal komisch dass Schlüsselwörter nicht komplett aus Großbuchstaben bestehen müssen. Ich habe wohl letztlich in Pascal aus Bequemlichkeit alles klein geschrieben und mich dann an dieses Schriftbild gewöhnt. Ein Grund ist bei mir auch noch, dass ich lieber alles klein schreibe als dass ich hinterher aus Flüchtigkeit etwas mal klein und mal groß schreibe, was den Compiler nicht kümmert , aber die optische Konsistenz des Codes beeinträchtigt. Und da lege ich schon Wert drauf dass ich mich da auch später noch zurechtfinde, deshalb rücke ich auch ein - mir hilft sowas.
DOSferatu hat geschrieben:Sogar beim Chatten achte ich auf Großschreibung und Grammatik.
Mach ich seit einiger Zeit auch so. Nur beim Programmieren lieber klein. Es gibt ja so gesehen keine Rechtschreibregeln für Code.

Hex-Werte ich Assembler-Blöcken schreibe ich ganz gerne als 0????h. Das unterstreicht für mich dass ich mich sozusagen nicht in einem Pascal-Teil befinde. Das ist vielleicht bei mir so durchgerieselt weil ich ein Assembler-Buch habe wo das so empfohlen wurde.
DOSferatu hat geschrieben:Falls es Dich interessiert: 0x???? ist übrigens die Notation, die in C und Verwandte benutzt wird - und in allem, was seine Syntax von C ableitet. Und ja, ich finde diese ebenfalls häßlich. (Außerdem: Gleich ZWEI Zeichen, um das auszudrücken... Nerv.)
C muss man mögen. Es ist wohl schnell zu schreiben, aber würde bei mir wiederum Gewöhnung erforden. Ich bin früher mal in die Situation gekommen etwas in C zu programmieren, besonders angenehm fand ich das nicht.
DOSferatu hat geschrieben:[FestplattenStromSteckerSchmor]Ui! Ich hoffe, es ist nichts Schlimmes?
Zum Glück hat die Platte keinen Schaden genommen.
DOSferatu hat geschrieben:Nur, wie ich ja immer erwähne: Wir haben Von-Neumann-Computer. Code und Daten benutzen denselben Speicher. Wenn man 10 Bytes mehr Code benutzt, um an anderer Stelle 1 Byte Daten zu sparen, das klingt nicht nur sinnlos, das IST es auch.
Guter Punkt. Man sollte immer auch die Größe des Programms im Hinterkopf haben wenn man überlegt ein paar Bytes in irgendwelchen Datenfeldern sparen zu wollen.
DOSferatu hat geschrieben:

Code: Alles auswählen

db $8E,$26;dw Segment1 {=mov FS,Segment1}
db $8E,$2E;dw Segment2 {=mov GS,Segment2}
Danke! Das Wissen könnte mir der Online-Assembler natürlich nicht vermitteln.
DOSferatu hat geschrieben:Wenn z.B. ein Fall selten genug passiert, kann sogar ein Raus-und-zurück-Springen (obwohl es 2 Sprünge sind) im Ganzen immer noch mehr performen als ein "Übersprung". Hatte ich ja schonmal erwähnt.
Ich kann es ja nicht anders angehen. Die Blöcke werden geprüft, für den Fall Block inaktiv wird zum nächsten Prüfschritt einfach ohne was zu tun weitergegangen, wenn ein Block aktiv ist muss dann aber zu einem speziellen Punkt gesprungen werden, der Parameter und Rücksprungadresse setzt, dann wird zu Blockanzeigeroutine gesprungen, und die springt zurück über die Rücksprungadresse zum nächsten Blockprüfschritt.
DOSferatu hat geschrieben:Also naja: Man könnte irgendeinen Bytecode erzeugen, der dann interpretiert wird. Aber echte ASM-Opcodes wäre natürlich die Königsklasse. Nur sollte das Programm, das diese Daten/Code erzeugt natürlich keinen Mist bauen. Und es sollte "wissen", welche Register es zu benutzen hat - sonst könnte man das ja kaum irgendwo einbauen.
Das mit den Registern könnte man ja klären, angenommen diese erzeugten Codes sind "Unterprogramme" von z.B. den Blockanzeigeroutinen. Also müsste man dann im Code eine gewisse Menge an Speicher freihalten, wo man dann den speziellen Code reinkopiert und dann ausführt. Aber ob das dann wirklich Geschwindigkeitszuwachs bringt? Jedenfalls eine interessante Sache, so könnte man eben halbwegs intelligent optimierten Code erzeugen, der die Bilddaten direkt in sich trägt und formuliert anstatt sie von A nach B zu kopieren. Wie das genau geht, ob man vom Heap ins Codesegment kopieren kann, weiss ich nicht.
DOSferatu hat geschrieben:[hochskalieren]Naja, das ist ja nur nötig, weil keine CRTs mehr benutzt werden, die das alleine so darstellen wie es muß, sondern diese TFTs und so Zeug, wo jede niedrigere als die Maximalauflösung immer irgendwie scheiße aussieht und man deshalb da software- (oder mittlerweile auch hardware-) mäßig interpolieren muß, um die Abscheulichkeit etwas abzumildern.
Ja, einfaches Interpolieren ist das eine, mit KI hochskalieren etwas anderes.
https://youtu.be/HUwi6HnkH9I
Davon kann man halten was man will, aber auf eine Weise interessant ist es schon. Wobei ich sagen muss dass ich ein Fan von 320x200 (oder x240) Pixelgrafik bin und nicht das Bedürfnis nach "remaster" Editionen habe.
DOSferatu hat geschrieben:Ach, ich hab nichts gegen Actionspiele.
Ich auch nicht. Nur einige von Nintendo waren mir irgendwie zu stressig. Commander Keen auf den PC ist zum Beispiel sehr gemütlich.

TheGame3:
Da stehe ich erstmal natürlich nur wie ein dummer User davor und verstehe nicht viel. Da könnte ich mich auch erst richtig reinknien wenn es ernst wird, ich also damit wirklich ein Spiel machen wollte. Auf jeden Fall ein in seinem Umfang beeindruckendes Programm, aber das einzige was ich bisher konnte (auch klar, ohne jegliche Grafikdaten) war "PlayLevel" - wiederum beindruckend das scheinbar beliebig große Level, scrollend in alle vier Himmelsrichtungen.
Bei meinem ollen Kotzman II war der Level-Editor sehr simpel, über wenige Shortcuts bedienbar und nur auf die Levels zugeschnitten. Es war nur die (auch sehr simple) Spielengine, alles hardgecodet, schnell um eine Editierfunktion ergänzt, die dann im Spiel nicht mehr drin war.

Kryptisches "Hello World":
Also bevor ich mir den Code näher ansehe, müsste ich da erstmal sehr viele Zeilenumbrüche einfügen... Und wenn ich da einfach mal die ersten Befehle lese, wird deutlich dass man sich wohl Notizen über die Registerinhalte machen muss um das ganze verfolgen zu können.
Interessant finde ich aber auch das Resultat, wenn ich das richtig interpretiere ist das im standard Textmodus gehalten mit modifiziertem Zeichensatz, und man merkt es zuerst eigentlich gar nicht.
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: Eigenes Videoformat

Beitrag von DOSferatu »

zatzen hat geschrieben: Fr 4. Sep 2020, 23:43 Sorry dass so viel Zeit vergangen ist.
Ich war noch beschäftigt mit neuen (naja, Hardware von 2013) Computer fit machen und dann habe ich für einen Freund noch ein Tool in Freepascal gemacht.
Ja, das Restleben. Wobei "Tool in Freepascal machen" ja nicht Restleben ist. Das ist ja wertige Zeit.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Obwohl ich wirklich sagen muß, daß ich so Parser-Zeug inzwischen im Halbschlaf mache.
Vielleicht kannst Du mir ein paar kleine Tipps geben, wie man am elegantesten eine XML parst. Im oben genannten Tool verarbeite ich .NIF Dateien, die enthalten Rohgrafiken und Definitionen für 3D Objekte, und es scheint so als wären die ähnlich wie XMLs, nur Binär. Scheinbar keine Längenangaben zu den Chunks, dafür sind die Chunknamen deutlich länger als 4 Bytes, so dass es unwahrscheinlicher wird dass sie zufällig entstehen.
Ich kenne keine .NIF Dateien. Wenn Du da etwas konkreter wirst oder mir ein Beispiel schickst, kann ich mal sehen, ob ich da was machen kann - aber versprich Dir da mal nicht zu viel. BINÄRE Files zu analysieren ist natürlich etwas aufwendiger als textbasiertes Zeug (wie z.B. Sourcecodes). Bei Binärfiles muß man oft mal "raten" und kann natürlich nicht voraussehen, ob es da bestimmte Optionen gibt, die nur in dem Beispielfile nicht vorkommen, aber theoretisch vorkommen könnten.
zatzen hat geschrieben:
DOSferatu hat geschrieben:[FAQREAD]Na egal. Ich will Dir das nicht aufdrängen - wollte es nur erwähnt haben.
Ja, es ist ein tolles Tool das ich auf einem reinen DOS-Rechner sicher benutzen würde. In einer Windows-Umgebung wo ich zwei Dos-Box'en laufen haben müsste um in einem Fenster programmieren zu können und im anderen das FAQREAD zu haben setzt sich pragmatisch dann doch eher der simple Editor als Betrachter durch. Das wäre anders wenn Windows noch 16 Bit Code unterstützen würde.
Ja, schon klar.
zatzen hat geschrieben:Ich denke es ist keine Schande wenn ich gestehe dass ich die Nutzung von Multitasking während der Softwareentwicklung praktikabel finde.
Doch! Schande! Häresie! ...
Ach, mir doch egal, wie Du das machst. Wenn's Dir hilft...
zatzen hat geschrieben:Ich kann einen guten Hex-Editor (z.B. HxD) geöffnet haben und er updatet automatisch die geöffneten Dateien in die mein Programm schreibt, so kann ich sehr bequem debuggen ohne Programme ständig umschalten, beenden oder starten zu müssen. Und selbst wenn ich in Freepascal programmiere kann ich mir parallel ein Konsolenfenster (cmd) öffnen und bei einem Programm mit Parametern diese einfach dort eintippen (mit dem Komfort der Kommandozeile), und muss das nicht umständlich in der IDE tun.
Ach, ich bau mir da meist einfache BAT-Files zum Testen, die das Ding mit so Testparametern aufrufen. WINDOWS und das ganze Zeug habe ich beim Programmieren wirklich noch nie vermißt.
zatzen hat geschrieben:Die Lösung was FAQREAD betrifft wäre eine 32 Bit Windows Version, evtl. mit Freepascal gemacht.
Ach, mir ist es ja egal, wer mein Zeug benutzt - war ja nur eine Anmerkung. Wer den Kram nicht benutzen kann, weil er keine DOS-Programme (mehr) ausführen kann, benutzt eben Alternativen. Es ist ja nicht so, daß die Grütze, die ich hier baue die softwaremäßige Traumerfüllung ist.
zatzen hat geschrieben:Aber natürlich können wir uns einig darüber sein, dass tatsächlich Windows bzw. die Prozessorentwickler die Übeltäter sind, weil sie keine 16 Bit mehr unterstützen. Mutmaßlich hat das aber auch technische bzw. Performance-Gründe, und nicht nur verkaufspolitische.
ICH denke, es sind NUR verkaufspolitische Gründe und künstliche Obsolität.
286er: (fast) 100% abwärtskompatibel zu 8086+186
386er: (fast) 100% abwärtskompatibel zu 8086+186+286
486er: (fast) 100% abwärtskompatibel zu 8086+186+286+386
Pentium... usw...
Jahrelang haben die es hinbekommen. Aber HEUTE, wo Prozessoren im GHz-Bereich agieren und schon 8 Kerne haben usw., soll das nicht mehr möglich sein?

Ich kann Dir sagen, woran es liegt: Inzwischen lassen sich die Prozessorhersteller von den Dumpfbacken bei Microsoft gängeln. Intel hatte den Protected Mode erfunden, der Speicherzugriff auf den Gesamtspeicher (bis 4GB) ermöglichte und zusätzlich Speicherschutzfunktionen usw. Und irgendwann war MS das zu kompliziert und die benutzen jetzt nur noch so ein "Flat Memory Model" - ja, im Prinzip sowas, was die DOS-Typen damals mit diesem Flat-4G-Mode gemacht haben. Das hebelt natürlich jeden hardwaremäßigen Speicherschutz aus, also supporten die neueren Windows nur noch CPUs, die dieses NX-Bit haben ("NX="Not eXecute") um trotzdem noch eine Art Speicherschutz zu haben, weil sie ZU FAUL sind, den abwärtskompatiblen Protected Mode zu supporten. Und so einen Flat-Mode kann man natürlich nicht mehr 100% kompatibel zum Real-Mode kriegen...
D.h. man könnte es schon: Die CPUs sind so schnell, daß die sogar hardwaremäßig in irgendeiner Ecke der CPU eine alte 286/386/486 "emulieren" könnten. Aber da haben MS und Konsorten ja wieder Angst, daß irgendwelche Virenschreiber das exploiten könnten. D.h. die neuen Modes sind an Dummheit und Faulheit angepaßt - mit Performance hat das nichts zu tun.

Ginge es um Performance, hätte man ja irgendwann mal den Eindruck, daß ein Windows auch mal schneller werden würde. Ich muß auf Arbeit diesen Windows 10 Dreck benutzen und es ist die reine Pest.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Großbuchstaben - ja, die verwende ich oft für Register, um sie von allem anderen abzugrenzen. Und ich bin eher irritiert von diesem "alles kleinschreiben"...
Es ist alles Gewöhnung, zumindest bei mir.
Natürlich ist es Gewöhnung - aber an sowas Häßliches WILL ich mich gar nicht gewöhnen.
zatzen hat geschrieben: Ich habe angefangen mit QBASIC,
Ich habe mit BASIC auf Ostcomputern (DDR) KC85/1, KC85/4 angefangen. Da war auch alles großgeschrieben, aber das war irgendwie normal bei diesen 8bit-Kisten. Auch beim C64, der kam bei mir nach den Ostrechnern.
zatzen hat geschrieben: und als ich auf Pascal umgestiegen bin fand ich es erstmal komisch dass Schlüsselwörter nicht komplett aus Großbuchstaben bestehen müssen.
Als ich auf Pascal kam, gab es erstmal eine MENGE, was ich komisch fand. Aber ich mochte schon auf Anhieb, daß es compiliert und war froh, von diesem Interpreter-Scheiß weg zu sein und seitdem will ich nichts mehr mit Interpreter-Zeug zu tun haben.
zatzen hat geschrieben:Ich habe wohl letztlich in Pascal aus Bequemlichkeit alles klein geschrieben und mich dann an dieses Schriftbild gewöhnt. Ein Grund ist bei mir auch noch, dass ich lieber alles klein schreibe als dass ich hinterher aus Flüchtigkeit etwas mal klein und mal groß schreibe, was den Compiler nicht kümmert , aber die optische Konsistenz des Codes beeinträchtigt.
Das ist einer der Gründe, wieso ich mit C nicht so klarkäme: Da muß man Variablen/Konstanten immer GENAU SO schreiben, wie man sie bei der Deklaration geschrieben hat, das ist Case-Sensitiv, da sind a und A unterschiedliche Variablen...
Bei Variablen schreib ich die mal so, mal so, je nachdem wo ich sie gerade benutze, weil ich mir damit manchmal bestimmte "Merkpunkte" mache.
zatzen hat geschrieben:Und da lege ich schon Wert drauf dass ich mich da auch später noch zurechtfinde, deshalb rücke ich auch ein - mir hilft sowas.
Ja, hilft vielen.
Mir nicht. Für mich ist "schicker Code" nur Zeitverschwendung. Wenn ich grade "im Flow" bin, knalle ich alles hin... Bei größeren verschachtelten Schleifen rück ich auch schonmal ein - aber eben nicht IMMER. Wenn ich alles "einzeilig und eingerückt" schreiben würde, zieht das zusämmenhängenden Code über etliche Zeilen... und ich habe einen Algorithmus oder Routine immer lieber auf einen Blick.

In Assembler bin ich z.B. froh über diese Borland-Turbo-Pascal Eigenart, daß man da die Befehle, genau wie in Pascal, mit Semikolon trennen kann und NICHT jeden Befehl auf eine Zeile schreiben muß. Mein Assembler-Zeug sind immer eher so Klötze. Ein einzelner Opcode für sich allein macht ja SO WENIG, und wenn ich da alles untereinander schreibe, hab ich so 20 Opcodes aufm Bildschirm, das reicht nicht, um Übersicht über eine komplexe Routine zu haben - da müßte ich dann ständig hin- und her scrollen.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Sogar beim Chatten achte ich auf Großschreibung und Grammatik.
Mach ich seit einiger Zeit auch so. Nur beim Programmieren lieber klein. Es gibt ja so gesehen keine Rechtschreibregeln für Code.
Naja, Richtig schreiben muß man's schon. Nur Groß-/Kleinschreibung nicht.
Wie gesagt: Außer bei C und seinen Derivaten: Da GIBT es diese Regeln - d.h. da ist es wichtig, was man groß-/kleinschreibt.
zatzen hat geschrieben:Hex-Werte ich Assembler-Blöcken schreibe ich ganz gerne als 0????h. Das unterstreicht für mich dass ich mich sozusagen nicht in einem Pascal-Teil befinde. Das ist vielleicht bei mir so durchgerieselt weil ich ein Assembler-Buch habe wo das so empfohlen wurde.
Ach, ich bin das $ so gewöhnt. Außerdem seh ich dann viel deutlicher, daß es Hex ist, weil das Zeichen nirgendwo sonst vorkommt:
bach = Variable Namens bach
0bach = Hex-Wert für dezimal 2988
Da ist mir $bac lieber.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Falls es Dich interessiert: 0x???? ist übrigens die Notation, die in C und Verwandte benutzt wird - und in allem, was seine Syntax von C ableitet. Und ja, ich finde diese ebenfalls häßlich. (Außerdem: Gleich ZWEI Zeichen, um das auszudrücken... Nerv.)
C muss man mögen. Es ist wohl schnell zu schreiben, aber würde bei mir wiederum Gewöhnung erforden. Ich bin früher mal in die Situation gekommen etwas in C zu programmieren, besonders angenehm fand ich das nicht.
Ja, ging mir genauso. Habe für den einen Kumpel mal so Ding in C geschrieben. Fand das meiste total umständlich. Als erstes hatte ich mir vernünftige Stringbefehle mit pascal-artigen Strings dafür gebaut. Nullterminierte Strings gehen für mich gar nicht. Und in C kann man keine Prozedures/Functions ineinander verschachteln. Das ist total arm!
zatzen hat geschrieben:
DOSferatu hat geschrieben:[FestplattenStromSteckerSchmor]Ui! Ich hoffe, es ist nichts Schlimmes?
Zum Glück hat die Platte keinen Schaden genommen.
Yeah! Es wäre schade um jedes Stück Technik - und NOCH MEHR um die Daten darauf!
zatzen hat geschrieben:
DOSferatu hat geschrieben:Nur, wie ich ja immer erwähne: Wir haben Von-Neumann-Computer. Code und Daten benutzen denselben Speicher. Wenn man 10 Bytes mehr Code benutzt, um an anderer Stelle 1 Byte Daten zu sparen, das klingt nicht nur sinnlos, das IST es auch.
Guter Punkt. Man sollte immer auch die Größe des Programms im Hinterkopf haben wenn man überlegt ein paar Bytes in irgendwelchen Datenfeldern sparen zu wollen.
Das sage ich ja schon die ganze Zeit. DAS meine ich ja mit: Wenn man eine komplexe Packroutine baut, die am Ende größer ist als das, was man beim Packen spart, hat man nichts gewonnen, weil ja alles im gleichen Speicher liegt. Dann hat man nur eine Routine, die zusätzlich Rechenzeit braucht und keinen Nutzen bringt. Es ist eben hier so, daß der Code nicht "irgendwo anders draußen" ist und nicht berücksichtigt werden muß. Beim Abschätzen von Speicherplatz müssen wir "Von-Neumann-Coder" immer berücksichtigen, daß nicht nur DATEN, sondern auch CODE Platz braucht. In unserer besonderen Situation (Real Mode) kommt noch dazu: DATEN können wir, wenn's eng wird, "nach oben" (XMS) auslagern - Code aber nicht! Code kann im Real Mode nur im Heap ausgeführt werden. (Wobei XMS natürlich ebenfalls nur ein "Lagerplatz" ist für uns, weil wir den auch nur indirekt benutzen können.)
zatzen hat geschrieben:
DOSferatu hat geschrieben:Also naja: Man könnte irgendeinen Bytecode erzeugen, der dann interpretiert wird. Aber echte ASM-Opcodes wäre natürlich die Königsklasse. Nur sollte das Programm, das diese Daten/Code erzeugt natürlich keinen Mist bauen. Und es sollte "wissen", welche Register es zu benutzen hat - sonst könnte man das ja kaum irgendwo einbauen.
Das mit den Registern könnte man ja klären, angenommen diese erzeugten Codes sind "Unterprogramme" von z.B. den Blockanzeigeroutinen. Also müsste man dann im Code eine gewisse Menge an Speicher freihalten, wo man dann den speziellen Code reinkopiert und dann ausführt. Aber ob das dann wirklich Geschwindigkeitszuwachs bringt? Jedenfalls eine interessante Sache, so könnte man eben halbwegs intelligent optimierten Code erzeugen, der die Bilddaten direkt in sich trägt und formuliert anstatt sie von A nach B zu kopieren. Wie das genau geht, ob man vom Heap ins Codesegment kopieren kann, weiss ich nicht.
Klar kann man das. Wie im letzten Abschnitt erwähnt, ist das ja alles der gleiche Speicher. Und weil wir im Real Mode (nicht Protected Mode) sind, gibts hier keinen Speicherschutz/Segmentschutz. Das Codesegment ist nur dadurch definiert, wo wir CS hinlegen. Ginge es nicht, ins Codesegment zu kopieren, ginge ja auch kein selbstmodifizierender Code (was ich so oft mache, wenn ich keine Register mehr habe bzw für die Register anderweitig bessere Verwendung habe.)
zatzen hat geschrieben:
DOSferatu hat geschrieben:[hochskalieren]Naja, das ist ja nur nötig, weil keine CRTs mehr benutzt werden, die das alleine so darstellen wie es muß, sondern diese TFTs und so Zeug, wo jede niedrigere als die Maximalauflösung immer irgendwie scheiße aussieht und man deshalb da software- (oder mittlerweile auch hardware-) mäßig interpolieren muß, um die Abscheulichkeit etwas abzumildern.
Ja, einfaches Interpolieren ist das eine, mit KI hochskalieren etwas anderes.https://youtu.be/HUwi6HnkH9I
Hab's mir mal angesehen... Naja. Es macht es nur hochauflösender, aber nicht "besser". Erklärung ist ganz einfach: Jemand, der 320x200 Grafik macht, läßt natürlich zu kleine Details weg, die vielleicht nur noch ein Pixelmatsch wären. Wenn mand das hochskaliert, wirkt dann immer alles leer und steril - weil natürlich keine Details dazukommen, sondern nur Algorithmen Pixeltreppen glätten und die besseren auch Rundungen erkennen.
Also ja: Interessante Technik - verfälscht aber den Grundeindruck.
Von einigen Lucas-Adventures soll es inzwischen aber echte reemasterte Versionen geben, die WIRKLICH dem Begriff "remastert" entsprechen: D.h. die benutzen "das Master". Gemeint ist: Die Zeichnungen (Hintergründe), die damals in 320x200 eingescannt/runtergerechnet wurden, wurden NEU eingescannt, mit höhrer Auflösung. Das ist dann natürlich etwas anderes als ein "Hochskalieren" eines bereits detailreduzierten Pixelbilds.
zatzen hat geschrieben:Davon kann man halten was man will, aber auf eine Weise interessant ist es schon. Wobei ich sagen muss dass ich ein Fan von 320x200 (oder x240) Pixelgrafik bin und nicht das Bedürfnis nach "remaster" Editionen habe.
Ja, wie gesagt: Es gibt inzwischen ECHTE Remaster-Versionen, die wirklich ausgehend vom Originalmaterial neu gemacht wurden, NICHT von den Pixelbildern "hochgezogen".
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ach, ich hab nichts gegen Actionspiele.
Ich auch nicht. Nur einige von Nintendo waren mir irgendwie zu stressig. Commander Keen auf den PC ist zum Beispiel sehr gemütlich.
Ja, ich z.B. kam nie mit Sonic the Hedgehog klar. Die Figur ist quasi nicht mehr steuerbar, sobald sie rennt. Man knallt ständig irgendwo gegen und verliert Ringe und Leben.
zatzen hat geschrieben:TheGame3:
Da stehe ich erstmal natürlich nur wie ein dummer User davor und verstehe nicht viel. Da könnte ich mich auch erst richtig reinknien wenn es ernst wird, ich also damit wirklich ein Spiel machen wollte.
Naja, der Kram hat eine zweisprachige "Online-Hilfe". Man kann an jedem beliebigen Zeitpunkt F1 drücken und die Hilfsseite(n) geht auf. Natürlich muß man erstmal trotzdem das allgemeine Bedienkonzept des ganzen Dings durchschauen. Das habe ich zwar auch erklärt (auf den ersten Hilfsseiten), aber ich weiß nicht, wie gut das jemand außer mir (der ja weiß, was gemeint ist) versteht.
zatzen hat geschrieben:Auf jeden Fall ein in seinem Umfang beeindruckendes Programm,
Naja, das Geheimnis ist, daß das komplette Menüsystem in einem Skript gebaut ist und jeweils einzeln aus dem Overlay nachgeladen wird, wenn es gebraucht wird. Aber das ist alles noch Pascal und hat auch gewisse Schwächen. Ich habe inzwischen ein ähnlich funktionierendes NEUES skriptbasiertes Menüsystem gebaut, nur daß das neue in 100% Assembler gemacht ist. Das wird aber nicht in TheGame3 benutzt - das Programm ist ja auch schon wieder einige Jahre alt.
zatzen hat geschrieben:aber das einzige was ich bisher konnte (auch klar, ohne jegliche Grafikdaten) war "PlayLevel" - wiederum beindruckend das scheinbar beliebig große Level, scrollend in alle vier Himmelsrichtungen.
Naja, das hat nichts mit TheGame3 zu tun. Das liegt an meiner ARCADE01-Unit, die da auch mit eingebaut ist. Die Levels (entweder nur 1 Ebene oder die Routine, die bis zu 4 Ebenen kann) sind direkt mit "Wraparound" vorgesehen, d.h. wenn eine Ebene an ihrem Ende angekommen ist, fängt sie wieder von vorn an (sowohl horizontal als auch vertikal). Vor allem für Hintergrund-Ebenen ist das ganz brauchbar, wenn man diese nur zur "Stimmung" haben will, damit es schöner ist und einen Himmel oder Skyline oder sowas machen will. Die Geschwindigkeit, bzwmit. das Geschwindigkeits-Verhältnis, mit dem sich die Ebenen zueinander bewegen, ist frei wählbar.
zatzen hat geschrieben:Bei meinem ollen Kotzman II war der Level-Editor sehr simpel, über wenige Shortcuts bedienbar und nur auf die Levels zugeschnitten. Es war nur die (auch sehr simple) Spielengine, alles hardgecodet, schnell um eine Editierfunktion ergänzt, die dann im Spiel nicht mehr drin war.
In Xpyderz ist ein Level-Editor direkt eingebaut. (Xpyderz hat ja auch so ein Skript-Menüsystem.) Sieht man nicht in der Pre-Pre-Release (da ist das auskommentiert). Vielleicht sollte ich mal die Xpyderz-Version MIT Level-Editor Online stellen, dann können "Fans" mal Levels bauen und dann kann man vielleicht mal eine Vollversion von Xpyderz rausbringen. Andererseits ist es auch eine steinkalte Schande, daß Xpyderz keinen Sound hat...

Andererseits ist die letzte Version 12 Jahre alt. Vielleicht sollte ich mal, mit meinen neuen Engines ein neues Xpyderz machen, das gleich von Anfang an die ganzen neuen Dinge benutzt: Das 100%-ASM Menüsystem, neue Darstellungsroutinen und natürlich auch ISM und so. Aber Xpyderz war damals eigentlich nur ein Experiment meinerseits. Das Spielprinzip ist ja nicht gerade der Brüller.
zatzen hat geschrieben:Kryptisches "Hello World":
Also bevor ich mir den Code näher ansehe, müsste ich da erstmal sehr viele Zeilenumbrüche einfügen... Und wenn ich da einfach mal die ersten Befehle lese, wird deutlich dass man sich wohl Notizen über die Registerinhalte machen muss um das ganze verfolgen zu können.
Ja, das ist ja Absicht, es SOLLTE ja etwas schwerer erkennbar sein, was das macht. Falls es dazu Fragen gibt, kann ich das gern erklären.
zatzen hat geschrieben:Interessant finde ich aber auch das Resultat, wenn ich das richtig interpretiere ist das im standard Textmodus gehalten mit modifiziertem Zeichensatz, und man merkt es zuerst eigentlich gar nicht.
Es ist NICHT modifizierter Zeichensatz. Ich "modifiziere" nur 1 Zeichen - das Zeichen 221 - und zwar nur für den Fall, daß jemand schändlicherweise NICHT den Zeichensatz Codepage 437 benutzt, wo das Zeichen das senkrechte Halbzeichen ist.

Der Rest der "Magie" funktioniert, indem man die Anzahl Zeilen, die die Zeichen haben können, in der Grafikkarte auf 2 Zeilen stellt. Der Textmode ist ja quasi 720x400 Pixel - und so erhält man quasi einen 160x200 "Pseudo-Grafik" Mode:
Es sind 80 Zeichen, die aber aus zwei Hälften bestehen: Vordergrund- und Hintergrundfarbe sind jeweils der linke und rechte Pixel. Und man sieht nur die ersten 2 Zeilen des Zeichens, dann kommt die nächste Zeichenzeile.

Am Ende, nach der Ausgabe, wird ja die Anzahl Zeichenzeilen wieder in einer Schleife hochgezählt, dadurch werden dann die ersten 25 "Pixelzeilen" wieder auf das ganze Bild hochgestreckt.

Der Rest ist eigentlich nichts weiter als "Pixelroutinen", die an diesen komischen "Pseudomodus" 160x200x16 angepaßt sind. Und dann Linien-Zieh-Routinen, die diese Pixelroutinen benutzen und noch etwas "Magic", um diesen Schachbrettboden hinzubekommen, mit Schatten und aus Linien gemachten Buchstaben.

Mit DOSbox kann man recht gut sehen, was das Ding macht: Wenn man die Zyklen auf einen sehr geringen Wert stellt, sieht man, wie der das ganze Bild aufbaut. Das ist NICHT irgendeine Grafik, die der da irgendwo hin"kopiert". Der "malt" das Bild wirklich.

Und da denk ich immer: Die heutigen Hoch-/Skriptsprachen-Trottel, die für alles nur immer ihre riesigen Frameworks benutzen und noch nie mal Binär-Arithmetik gemacht haben, ob die wohl selber (und in ASM) so 'ne Linienziehroutine bauen könnten...

Das Ding zeichnet übrigens auch jedesmal, wenn man es aufruft, die Buchstaben etwas anders - andere Farben, leicht anders gezerrte Form. Weil nämlich auch ein (16-bit)-Pseudozufallsgenerator drin ist. Kannst ja mal raten, woher der seinen Anfangswert (seinen "Seed") bekommt. Ist ja nicht schwer.

Soviel erstmal dazu.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

DOSferatu hat geschrieben:[parsen]Ich kenne keine .NIF Dateien. Wenn Du da etwas konkreter wirst oder mir ein Beispiel schickst, kann ich mal sehen, ob ich da was machen kann - aber versprich Dir da mal nicht zu viel. BINÄRE Files zu analysieren ist natürlich etwas aufwendiger als textbasiertes Zeug (wie z.B. Sourcecodes). Bei Binärfiles muß man oft mal "raten" und kann natürlich nicht voraussehen, ob es da bestimmte Optionen gibt, die nur in dem Beispielfile nicht vorkommen, aber theoretisch vorkommen könnten.
(22. September):
Es geht eher allgemein ums Parsen, konkret müsste ich bald mal an XMLs ran. Ich werd das schon hinkriegen, aber es könnte ja sein dass Du da gute Tipps hast wo ich so spontan einfach nicht drauf komme.
Dazu hier mal ein Ausschnitt aus einer Renoise Tracker XML, wie dort eine Patternspur codiert ist:

Code: Alles auswählen

      <Pattern>  
        <NumberOfLines>32</NumberOfLines>
        <Tracks>
          <PatternTrack type="PatternTrack">
            <SelectedPresetName>Init</SelectedPresetName>
            <SelectedPresetLibrary>Bundled Content</SelectedPresetLibrary>
            <SelectedPresetIsModified>true</SelectedPresetIsModified>
            <Lines>
              <Line index="0">
                <NoteColumns>
                  <NoteColumn/>
                  <NoteColumn/>
                  <NoteColumn>
                    <Note>C-4</Note>
                    <Instrument>00</Instrument>
                  </NoteColumn>
                </NoteColumns>
                <EffectColumns>
                  <EffectColumn>
                    <Value>37</Value>
                    <Number>0A</Number>
                  </EffectColumn>
                  <EffectColumn>
                    <Number>0V</Number>
                  </EffectColumn>
                  <EffectColumn>
                    <Number>0D</Number>
                  </EffectColumn>
                  <EffectColumn>
                    <Number>0G</Number>
                  </EffectColumn>
                </EffectColumns>
              </Line>

[...]

            </Lines>
            <AliasPatternIndex>-1</AliasPatternIndex>
            <ColorEnabled>false</ColorEnabled>
            <Color>0,0,0</Color>
          </PatternTrack>

[...]

        </Tracks>
      </Pattern>
Es geht natürlich jetzt nicht darum, was da konkret passiert, sondern einfach nur um ein Beispiel, wie die Daten in XML aussehen, die man auch binär halten könnte.
Ich denke mir, ich werde diese XML Daten parsen in binäre Datenfelder hinein, dann modifizieren um anschliessend wieder XML zu generieren. Oder wie würdest Du das machen?

(20. Oktober):
Ich habe zwischenzeitlich meine Tools schon geschrieben, also wieder meine Zeit sinnvoll im Unreal-Life verbracht. Das XML habe ich vielleicht etwas unelegant geparst, modifiziert und wieder generiert, aber es funktioniert. Trotzdem, falls Du da eine quasi optimale Vorgehensweise hast, lass es mich wissen, evtl. muss ich nämlich noch mehr Tools schreiben oder das vorhandene erweitern.
DOSferatu hat geschrieben:
zatzen hat geschrieben:Aber natürlich können wir uns einig darüber sein, dass tatsächlich Windows bzw. die Prozessorentwickler die Übeltäter sind, weil sie keine 16 Bit mehr unterstützen. Mutmaßlich hat das aber auch technische bzw. Performance-Gründe, und nicht nur verkaufspolitische.
ICH denke, es sind NUR verkaufspolitische Gründe und künstliche Obsolität.
286er: (fast) 100% abwärtskompatibel zu 8086+186
386er: (fast) 100% abwärtskompatibel zu 8086+186+286
486er: (fast) 100% abwärtskompatibel zu 8086+186+286+386
Pentium... usw...
Jahrelang haben die es hinbekommen. Aber HEUTE, wo Prozessoren im GHz-Bereich agieren und schon 8 Kerne haben usw., soll das nicht mehr möglich sein?
Man wollte wohl einen Schnitt machen. Ja, so isses eben, man will neues verkaufen, oder denkt sich einfach, wozu etwas weiter unterstützen wonach 99% der Leute nicht mehr krähen. In der Medienwelt läuft das ja auch so, aber da scheinen die Leute es ja gerne zu haben, mit der Zeit zu gehen. Ich bevorzuge im Zweifelsfall auch digitale Medien einer VHS-Kassette. Und ähnlich wie die bis vor einigen Jahren weit verbreitete DVD von der BlueRay verdrängt wird, wird die CD vom Streaming verdrängt (das gefällt mir überhaupt nicht) - wie wohl alle physischen Medienträger überhaupt eigentlich. Wenn es etwas neues gibt werden alte Formate fallengelassen. Die Vinyl-Schallplatte ist eine Ausnahme und scheint mittlerweile beliebter als die CD zu sein, obwohl sie wissenschaftlich gesehen von letzterer komplett in den Sack gesteckt wird was Klangtreue (und Komfort) angeht. Will heissen: Überspielt man eine Schallplatte mit guten Wandlern auf eine CD klingt die genauso wie die Schallplatte. Macht man von einer CD eine Schallplatte klingt es NICHT genauso. Und kaum habe ich mich damit abgefunden, dass die neueren Computer keine Diskettenlaufwerke mehr haben, sind auch schon CD/DVD Laufwerke wieder aus der Mode. Objektiv ja auch irgendwie verständlich, angesichts von mickrigen 512 GB fassenden USB-Sticks welche die Datenmenge einer DVD innerhalb einer handvoll Sekunden transferieren.
Aber es war "damals" schon irgendwie immer ein gutes Gefühl, seine Daten auf ROM-Datenträgern archivieren zu können, dann ist man vor irrtümlichem Löschen oder sonstigem sicher. Wenn da nicht einige dabeigewesen wären, die über die Jahre unlesbar geworden sind, wäre es perfekt gewesen. Jammerschade also dieses wahrscheinliche Aussterben der CD, von den technischen Eckwerten her ist sie auch heute noch völlig ausreichend, man könnte sie höchstens mittels Blu-Ray Technologie auf Münzgröße schrumpfen, das wäre doch mal was, die Mikro-Disc (µD).
DOSferatu hat geschrieben:Als ich auf Pascal kam, gab es erstmal eine MENGE, was ich komisch fand. Aber ich mochte schon auf Anhieb, daß es compiliert und war froh, von diesem Interpreter-Scheiß weg zu sein und seitdem will ich nichts mehr mit Interpreter-Zeug zu tun haben.
QuickBASIC hatte einen Compiler. Allerdings separat - die Testläufe machte ein Interpreter wenn ich nicht irre. Und der konnte meistens mehr Speicher fassen als der Compiler, deswegen war das oft ärgerlich wenn man sein Programm am Ende zurechtstutzen musste. Hatte ich schonmal erwähnt. Weil ich nur QBasic kannte, bei dem in der IDE die "SUBs" separat angezeigt wurden, fand ich erst unübersichlich dass man bei Pascal alle Routinen auf einmal sieht.


Einrücken etc.:
Das Programmieren macht mir ein kleines bisschen mehr Spaß wenn der Code auch eine gewisse leserliche Form hat. Aber alles einzeilig ist es bei mir auch nicht. Auch gerade bei meinen Blockdingensroutinen mache ich in Assembler viel Gebrauch von mehreren Befehlen pro Zeile. Und zuletzt habe ich auch gefallen dran gefunden alles etwas kompakter zu gestalten, gerade bei Routinen die nicht sonderlich extravagant sind und wo man im Verlauf nichts mehr dran ändern muss.

DOSferatu hat geschrieben:Ach, ich bin das $ so gewöhnt. Außerdem seh ich dann viel deutlicher, daß es Hex ist, weil das Zeichen nirgendwo sonst vorkommt:
bach = Variable Namens bach
0bach = Hex-Wert für dezimal 2988
Da ist mir $bac lieber.
Ich habe wohl verinnerlicht, dass Variablen nicht mit Ziffern beginnen dürfen. Ich hatte bisher keine Probleme. Aber jeder wie er will eben. :)
Aber tatsächlich gibt auch Freepascal bei einem RTE die Adresse mit einem $ vorne aus.

DOSferatu hat geschrieben:Und in C kann man keine Prozedures/Functions ineinander verschachteln. Das ist total arm!
Ich hab das bisher in Pascal auch nur sehr selten gemacht, ich dachte dabei immer das gäbe zu viel Stack-Beanspruchung.
DOSferatu hat geschrieben:Wenn man eine komplexe Packroutine baut, die am Ende größer ist als das, was man beim Packen spart, hat man nichts gewonnen, weil ja alles im gleichen Speicher liegt.
Es ist wahrscheinlich nichts neues wenn ich sage, dass die vielleicht 2K mehr Code (wobei, da ist ja auch noch ne Tabelle dabei, mit 512 Byte aber kaum nennenswert), bei ZSM sich lohnen, sobald man eben mehr als 2K komprimierte Samples hat. Ähnlich denke ich über ZVID2: Ich möchte da vielleicht um die 300K (gepackt) an Grafik reinklatschen, die Routinen werden sicherlich auch ziemlich klein sein. Übrigens an dieser Stelle nochmal das brenzlige Thema Hintergrundgrafik-Kompression bei der Blocksache: Wenn mein primäres Ziel nicht das Erreichen von möglichst hoher Performance ist, sondern möglichst viel Grafik im Speicher halten im Vordergrund steht: Dann ist es meines Erachtens sogar höchst sinnvoll, diese Kombination gepackter Hintergrund und nur teilweise restaurieren, denn: Wenn ich die Hintergründe faktisch definitiv packen will, dann schlägt der Performanceboost durch nur teilweises Restaurieren umso stärker zu Buche, im Vergleich dazu als wenn ich jedes mal ein Vollbild entpacken würde.
DOSferatu hat geschrieben:[vom Heap ins Codesegment kopieren]Klar kann man das. Wie im letzten Abschnitt erwähnt, ist das ja alles der gleiche Speicher. Und weil wir im Real Mode (nicht Protected Mode) sind, gibts hier keinen Speicherschutz/Segmentschutz. Das Codesegment ist nur dadurch definiert, wo wir CS hinlegen. Ginge es nicht, ins Codesegment zu kopieren, ginge ja auch kein selbstmodifizierender Code (was ich so oft mache, wenn ich keine Register mehr habe bzw für die Register anderweitig bessere Verwendung habe.)
Das wäre ja dann selbmodifizierend, im großen Stil sozusagen. Ein kleiner Performance-Haken könnte dann sein, dass das Kopieren den Codes auch eine kleine Weile dauert.

DOSferatu hat geschrieben:
zatzen hat geschrieben:[KI upscaling]Davon kann man halten was man will, aber auf eine Weise interessant ist es schon. Wobei ich sagen muss dass ich ein Fan von 320x200 (oder x240) Pixelgrafik bin und nicht das Bedürfnis nach "remaster" Editionen habe.
Ja, wie gesagt: Es gibt inzwischen ECHTE Remaster-Versionen, die wirklich ausgehend vom Originalmaterial neu gemacht wurden, NICHT von den Pixelbildern "hochgezogen".
Aber auch hier bin ich so geprägt, dass 320x200 zu einem stimmungsvollen Spielerlebnis beitragen kann. Die Hintergrundgrafiken von Monkey Island II sind kunstvoll mit Filzstift gemalt, durch die niedrige Grafikauflösung werden sie etwas pixelig-unschärfer und wirken dadurch paradoxerweise fotorealistischer. Wenn diese nun hochauflösend zu sehen sind sehen sie wieder mehr nach Zeichnungen aus. Bei der Special Edition wurden die Grafiken scheinbar auch nochmals stilisiert, weiter weg vom Original, das hat auf mich wieder zu sehr den Effekt "Hintergründe nur gemalt, Figuren wie aus Knete gemacht". - Oder vielmehr, alles wie aus Holz geschnitzt und angemalt. Das mag ja vielen gefallen, Monkey Island II als Holzpuppentheater, aber ich bevorzuge die pixelige Version die Raum zur Interpretation lässt und somit letztlich realistischer wirkt.

DOSferatu hat geschrieben:Vielleicht sollte ich mal, mit meinen neuen Engines ein neues Xpyderz machen, das gleich von Anfang an die ganzen neuen Dinge benutzt: Das 100%-ASM Menüsystem, neue Darstellungsroutinen und natürlich auch ISM und so. Aber Xpyderz war damals eigentlich nur ein Experiment meinerseits. Das Spielprinzip ist ja nicht gerade der Brüller.
4-Wege Scrolling hätte mich zumindest als Kind begeistert, eine große Welt erkunden und so. Deswegen hab ich ja auch Zelda gespielt. Oder Cauldron II auf dem C64 fand ich auch toll. Mein Einstieg in die Computerspielewelt war im Prinzip Super Mario Bros. und da konnte man ja immer nur von links nach rechts laufen, aber niemals zurück.

Hello World:
DOSferatu hat geschrieben:Der Rest der "Magie" funktioniert, indem man die Anzahl Zeilen, die die Zeichen haben können, in der Grafikkarte auf 2 Zeilen stellt. Der Textmode ist ja quasi 720x400 Pixel
War mir gar nicht so bewusst, diese Auflösung. Also hat ein Buchstabenblock komische 9x16 Pixel. Es ist schon ne Weile her dass ich mal nen eigenen Zeichensatz gemacht habe... Trotzdem messe ich die Breite Deiner Pixel gleichmäßig mit jeweils 4 "echten" Pixeln. Irgendwo stimmt da was in meinem Verständnis nicht. Vielleicht ist der Textmodus ja doch nur 640x400, die Ascii-Zeichen sind jedenfalls 8x16, gerade nochmal nachgesehen.
DOSferatu hat geschrieben: - und so erhält man quasi einen 160x200 "Pseudo-Grafik" Mode:
Es sind 80 Zeichen, die aber aus zwei Hälften bestehen: Vordergrund- und Hintergrundfarbe sind jeweils der linke und rechte Pixel. Und man sieht nur die ersten 2 Zeilen des Zeichens, dann kommt die nächste Zeichenzeile.
Auch sehr interessant. Hätte dieser Modus gewisse Vorteile gegenüber einem EGA 160x200, oder waren Spiele wie die ersten von Sierra in Wirklichkeit vielleicht sogar in diesem Modus gehalten? Aber ich sehe gerade, dass die Grafik selbst dort zwar auf 160x200 beschränkt ist, die Textausgaben aber in 320x200 aufgelöst sind. Dann ist es wohl insgesamt 320x200 Grafikmodus, und man wollte bei der Grafik einfach sparen. Oder einfach ein komischer Modus der zwar 320x200 Text aber nur 160x200 Grafik kann?
DOSferatu hat geschrieben:Mit DOSbox kann man recht gut sehen, was das Ding macht: Wenn man die Zyklen auf einen sehr geringen Wert stellt, sieht man, wie der das ganze Bild aufbaut. Das ist NICHT irgendeine Grafik, die der da irgendwo hin"kopiert". Der "malt" das Bild wirklich.

Und da denk ich immer: Die heutigen Hoch-/Skriptsprachen-Trottel, die für alles nur immer ihre riesigen Frameworks benutzen und noch nie mal Binär-Arithmetik gemacht haben, ob die wohl selber (und in ASM) so 'ne Linienziehroutine bauen könnten...
Jeder hat eben sein Spezialgebiet, und wer objektorientiert an künstlicher Intelligenz bastelt wird nicht viel mit Linienalgorithmen zu tun haben. Wobei eine allgemeine Programmiererfahrung auf jedem Gebiet sicherlich nicht schädlich ist.
DOSferatu hat geschrieben:[Zufallsgenerator]Kannst ja mal raten, woher der seinen Anfangswert (seinen "Seed") bekommt. Ist ja nicht schwer.
Timer? Ist ja die übliche Methode.
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: Eigenes Videoformat

Beitrag von zatzen »

Ich habe mich mal wieder mit ZVID2 beschäftigt. Für andere Mitleser: Ein Grafikformat das die Grafiken in 4x4 Pixel Blöcke unterteilt und dann jeweils nur mit so viel Bit speichert wie nötig, entsprechend der Anzahl der verschiedenen Farben innerhalb eines 4x4 Blocks. Das ganze soll dann in Echtzeit entpackt werden.

Naja und ich rolle das ganze nochmal mehr oder weniger neu auf und habe jetzt schnell gemerkt, dass ich die Assembler-Darstellroutinen zuerst schreiben muss, denn das Format muss sich nach der Darstellungsroutine ausrichten und nicht umgekehrt.

Jetzt habe ich noch eine Frage zum Thema Speicher an Nulloffset-Adresse anlegen. Du (DOSferatu) sagtest mir, ich muss das so machen (nachdem ich mit getmem 16 Byte mehr reserviert habe):

Code: Alles auswählen

inc(segment, succ(offset shr 4)); offset := 0;
Zum einen habe ich da eine Verständnisfrage, zum anderen wäre es in meinem Fall vorteilhaft wenn man das Segment immer um genau 1 erhöhen könnte, unabhängig von Inhalt des Offsets, also:
Wenn ich mit getmem genau 16 Bytes mehr reserviere und nicht mehr, dann funktioniert das ganze nur, wenn offset maximal 15 ist - andernfalls würde der reservierte Speicher nicht ausreichen. succ(offset shr 4) ergibt immer 1, egal ob offset 0 ist oder größer, solange < 16. Also müsste eigentlich auch inc(segment); offset := 0 korrekt sein, zumindest ist es im Ergebnis identisch mit inc(segment, succ(offset shr 4)); solange offset < 16 ist.

Nebenbei noch, welche Register könnte ich noch "missbrauchen" in einer Routine ohne Stackrahmen (d.h. ohne Variablenübergabe sondern einfach "procedure blabla; assembler ...")? BP ist mir geläufig, die ?X Register sind alle belegt, wie auch DS, ES, FS, GS.
Evtl. SP? Wobei mir das abenteuerlich scheint, den Stack-Pointer auf dem Stack zu sichern ist wohl keine gute Idee, allemal wäre ein sichern im Speicher (DS verändere ich nicht) denkbar, aber auch da sehe ich Konflikte wenn Interrupt-Routinen vorkommen.
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: Eigenes Videoformat

Beitrag von zatzen »

Ich habe beim Programmieren wieder was gelernt.
Wahrscheinlich ist Dir der Sachverhalt schon bewusst, leider haben wir es aber nie thematisiert.
Ich habe meinen ASM Code mal kompiliert und wieder disassembliert. Dabei habe ich mich gewundert, warum bestimmte Sprunginstruktionen nicht so durchkommen, wie ich sie programmiert habe. Kurz gesagt:
Der Pascal-Compiler hat aus "jnz @loop" soetwas gemacht wie

Code: Alles auswählen

jz @skip; jmp @loop; @skip:
Das handelt einem dann je nach Umständen unnötige Sprünge und Takte ein. Den Grund habe ich auch schnell begriffen - im 286er Befehlssatz gibt es nur 8 Bit bedingte Sprünge (short relative), und das Sprungziel war > 128 Byte entfernt. Der Pascal-Compiler würde die gleichen Workarounds möglicherweise auch für JCXZ basteln, aber hier existiert keine Inversion des Befehls, weshalb das nicht möglich ist.
Ein eleganter Lösungsansatz wäre gewesen, entsprechend Inline einen 16 Bit Jump zu definieren. Leider kann man aber nicht einfach schreiben "dw 0850fh; dw offset @loop - offset @jump - 4", obwohl das für den Compiler eigentlich machbar wäre.
Also fiel mir vorerst nur selbstmodifizierender Code ein:

Code: Alles auswählen

mov ax, 0850fh; mov cs:[offset @jump], ax
{... zwischendrin kommt ein Sprung vor (Cache wird gelöscht) ...}
@jump: nop; jmp @loop
Aber vielleicht gibt es ja doch noch eine Möglichkeit, sich die zwei modifizierenden Befehle (und die Notwendigkeit den Cache zu leeren) zu sparen. Bei Dingen wie meinem blockweise Hintergrund-Restaurieren wären sonst nämlich massig Selbstmodifikationen nötig.

JCXZ bekommt in diesem Zusammenhang eine besondere Nützlichkeit: Man kann damit testen, ob die Modifizierung eines bedingten Sprungs nötig ist, indem man diesen beim Programmieren probeweise durch JCXZ ersetzt.

Ich habe nochmal in diesem Thread gestöbert und bin auf Deine Erklärung gestoßen dass man auch Variablen (und wie ich festgestellt habe auch Register) mittels "dw" (oder db, dd) in den Code einlassen kann. Mir ist nur etwas unklar, wie der Compiler das handhabt: Den Wert einer Variablen kann er beim kompilieren nicht wissen, also kann es eigentlich nur der Offset im Datensegment sein. Demnach müsste er bei Registern auch nicht deren Inhalt speichern, sondern den Wert als Offset interpretieren.
Aber beim Kompilieren ist ja der Wert wiederum unklar. Rätselhaft. Gerade ins Kompilat geguckt: Bei Registern kommt einfach 0 raus, jedenfalls bei AX. Wozu kann man dann überhaupt Register in den Code bringen?
Jedenfalls könnte man obiges Problem auch auf diese Weise lösen (wie wir sehen werden tatsächlich NICHT), es wäre aber umständlicher und es bräuchte je nachdem für jeden Fall eine extra globale Word-Variable:

Code: Alles auswählen

mov ax, offset @loop; sub ax, offset @jump + 4; mov jumpamount, ax {vorbereitend irgendwo oben im Code}
dw 0850fh; dw jumpamount {später im Code in einem Teil die oft aufgerufen bzw. angesprungen wird}
Und HALT! Genau das wird so nicht funktionieren, weil ja nicht der Inhalt von "jumpamount" in den Code einfliesst sondern der Offset... Naja ich lass das mal stehen, hab immerhin nochmal etwas draus gelernt.

Übrigens, noch eine kleine Sache: Ich habe jetzt bei Segment-Overrides in Zusammenhang mit BP keine Probleme gehabt. Mein Fehler war, dass ich bisher z.B. "db 65h; mov ds:[bp], ..." geschrieben habe. Wenn ich nur "db 65h; mov [bp], ..." schreibe funktioniert das mit dem Override tadellos.

Und noch eine Analyse (ja, ich habe den Text hier mittlerweile schon mehrfach editiert):
Du sagtest mir mal, dass ich einfach Glück gehabt hätte wenn ich in einer Assembler-Prozedur RET verwende und mir nicht überlege ob RETF vielleicht richtig wäre. Nun, ich selber kann es ja auch schlecht einschätzen, zumal der Compiler nicht soetwas wie "CALLF" kennt mit dem man das selbst festlegen könnte. Dafür scheint er aber selbständig die richtigen Entscheidungen über RETF/RETN zu treffen, wenn man einfach nur RET schreibt: In meinem Code steht einfach nur RET, aber der Compiler hat ein $CB draus gemacht, also ein RETF. Schreibe ich dagegen explizit RETN geht das so auch ins Kompilat ein mit $C3.

Für meine Routine mit den Modifikationen habe ich jetzt eine passable Lösung, beim ersten Durchlauf wird ganz nach unten gesprungen, wo die Modifikationen stattfinden, dabei wird auch der Sprung oben umgewandelt in einen sinnvollen ersten Befehl der Routine, und unten steigt die Routine dann eben vor dem Modifikationspart mit RET aus. D.h. man muss die Routine einmalig aufrufen, damit sie sich verwandelt. Das Prinzip hast Du mir vorgemacht vor nicht langer Zeit, nur etwas komplizierter. Danke dafür!
Ich habe nur gerade gemerkt: Man muss vor dem Programmstart immer dran denken, das Ding neu zu kompilieren, sonst legt die Routine (weil sie noch modifiziert im Speicher liegt) einfach los und will Grafik anzeigen, aber man hat keine Parameter angegeben oder ist nicht noch nicht im Grafikmodus...
Okay bessere Idee: Nach dem Modifizieren wird direkt wieder an den Anfang der Routine gesprungen, die diesmal modifiziert ist. So passiert die Modifikation einfach automatisch und einmalig beim ersten Aufruf der Routine, man muss sie nicht extra nur für den Modifikationszweck einmal aufrufen.
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: Eigenes Videoformat

Beitrag von DOSferatu »

OK, dann auch hier noch eine Antwort:
zatzen hat geschrieben: Di 20. Okt 2020, 20:23
DOSferatu hat geschrieben:[parsen]Ich kenne keine .NIF Dateien. Wenn Du da etwas konkreter wirst oder mir ein Beispiel schickst,[...]
(22. September):
Es geht eher allgemein ums Parsen, konkret müsste ich bald mal an XMLs ran. Ich werd das schon hinkriegen, aber es könnte ja sein dass Du da gute Tipps hast wo ich so spontan einfach nicht drauf komme.
Dazu hier mal ein Ausschnitt aus einer Renoise Tracker XML, wie dort eine Patternspur codiert ist:

Code: Alles auswählen

      <Pattern>  
        <NumberOfLines>32</NumberOfLines>
        <Tracks>
          <PatternTrack type="PatternTrack">
            <SelectedPresetName>Init</SelectedPresetName>
            <SelectedPresetLibrary>Bundled Content</SelectedPresetLibrary>
            <SelectedPresetIsModified>true</SelectedPresetIsModified>
            <Lines>
              <Line index="0">
                <NoteColumns>
                  <NoteColumn/>
                  <NoteColumn/>
                  <NoteColumn>
                    <Note>C-4</Note>
                    <Instrument>00</Instrument>
                  </NoteColumn>
                </NoteColumns>
                <EffectColumns>
                  <EffectColumn>
                    <Value>37</Value>
                    <Number>0A</Number>
                  </EffectColumn>
                  <EffectColumn>
                    <Number>0V</Number>
                  </EffectColumn>
                  <EffectColumn>
                    <Number>0D</Number>
                  </EffectColumn>
                  <EffectColumn>
                    <Number>0G</Number>
                  </EffectColumn>
                </EffectColumns>
              </Line>

[...]

            </Lines>
            <AliasPatternIndex>-1</AliasPatternIndex>
            <ColorEnabled>false</ColorEnabled>
            <Color>0,0,0</Color>
          </PatternTrack>

[...]

        </Tracks>
      </Pattern>
Achduschande.
Ja, das ist eben XML. Textbasiert, prinzipiell leicht zu parsen, ABER:
Wie Du ja selbst schon siehst, arbeitet das ganze Ding verschachtelt. Bei so neueren Formaten ("wir haben ja genug Speicher und Rechenzeit"...) wird dann leider nicht definiert, WIE TIEF maximal geschachtelt werden darf - erwarte hier also alles oder nichts. Das ganze Ding sieht aus wie die umständlichst-mögliche und platzverschwendendste Möglichkeit, Musikdaten zu speichern. Stumpfsinn in absoluter Vollendung. Umständlichern wäre eigentlich nur noch, wenn dieses Zeug dann auch noch in eine Grafik oder ein PDF eingebettet wäre...

Was kann ich da groß für Tips geben? Man sieht ja, wie die Daten aussehen. Also immer auf < bzw. </ parsen und die Namen aller "Elemente" bzw. "Sub-Elemente" irgendwo in einer Tabelle haben. Sortierte Tabelle wäre gut, weil schnellere Suche (mit der Hälfte-Hälfte-Hälfte-Methode) möglich. Außerdem, was ICH immer mache: Alles interne UPCASE speichern (oder eben alles downcase) und beim Laden auch gleich alles, bevor es verglichen wird erstmal UPCASE (oder downcase) machen. Auf diese Art braucht man nicht Groß-/Kleinschreibung extra checken. Beim SPEICHERN sollte man dann natürlich die entsprechende Groß-/Kleinschreibung wieder so machen, damit das Leseprogramm von Renoise das abkann. (Weiß nicht, wie intelligent die das programmiert haben.)

Ach ja: Und bitte NICHT auf Zeilenenden und dieses Einrücken verlassen oder das als Möglichkeit betrachten, die Dinge zu erleichtern. Diese Leerzeichen und Zeilenenden und Tags oder ähnliches außerhalb der < > zählen nur zur Textformatierung, haben aber keinen Inhalt für HTML oder XML. Prinzipiell mußt Du also davon ausgehen, daß ein HTML oder XML auch komplett in einer einzigen Zeile geschrieben wurde, ohne Leerzeichen oder Zeilenenden. Also kann man nicht erwarten, daß man irgend etwas davon automatisch wie einen max. 255 Zeichen langen Pascal-String einlesen kann.

Bei von einem Programm erzeugten XML kann man zwar davon ausgehen, daß jedes "geöffnete" Item auch wieder "geschlossen" wird - aber man sollte evtl auch Erkennungsroutinen einbauen, die defekte Strukturen erkennen können und bei einfachen Fehlern diese selbst intern korrigieren und bei schweren Fehlern sich nicht in irgendwelchen Verschachtelungen totlaufen, sondern entsprechend aussteigen.

Ja, das klingt alles Mist - deshalb bin ich ja auch so ein "Riesen-Fan" von diesem Unsinn. (Mal abgesehen davon, wieviel Zeichen/Platz verbraucht werden, um einen Wert zu kennzeichnen, der vielleicht als Daten 1 Byte groß ist.)

Merke:
Beim LADEN fremder Daten immer so tolerant wie möglich sein - also alles erlauben, auch Zeug, was leicht "neben dem Format" liegt oder defekt ist. Solange es damit lesbar ist, auf plausible Daten anpassen. Falls nicht möglich, Fehler erkennen und ausgeben - keinesfalls darauf verlassen, daß textbasierte Formate immer richtig formatiert sind.

Beim SPEICHERN von Daten immer so stringent wie möglich sein - also möglichst genau an die Spezifikationen halten, weil man nicht weiß, wie tolerant andere Programme sind).
zatzen hat geschrieben:Es geht natürlich jetzt nicht darum, was da konkret passiert, sondern einfach nur um ein Beispiel, wie die Daten in XML aussehen, die man auch binär halten könnte.
Ich denke mir, ich werde diese XML Daten parsen in binäre Datenfelder hinein, dann modifizieren um anschliessend wieder XML zu generieren. Oder wie würdest Du das machen?
Genau so.
zatzen hat geschrieben:(20. Oktober):
Ich habe zwischenzeitlich meine Tools schon geschrieben, also wieder meine Zeit sinnvoll im Unreal-Life verbracht. Das XML habe ich vielleicht etwas unelegant geparst, modifiziert und wieder generiert, aber es funktioniert. Trotzdem, falls Du da eine quasi optimale Vorgehensweise hast, lass es mich wissen, evtl. muss ich nämlich noch mehr Tools schreiben oder das vorhandene erweitern.
Ich gehe natürlich davon aus, daß Du Dir zum Parsen des XML-Zeugs eine Unit gebaut hast.
zatzen hat geschrieben:[CPU-(64bit)-Abwärtskompatibilität]
Man wollte wohl einen Schnitt machen. Ja, so isses eben, man will neues verkaufen, oder denkt sich einfach, wozu etwas weiter unterstützen wonach 99% der Leute nicht mehr krähen.
Aber es waren meiner Meinung nach zu 0% technische Gründe. Die CPU hätte es immer noch als "Sandbox"-artige Emulation machen können - so fett und überkandidelt, wie die heute sind und dann keine 30 Jahre alten Standards erfüllen können - kann mir keiner erzählen. "Schnitt machen" ist dann immer so ein schönes Totschlag-Argument. So als würden sie den Bedarf ALLER Leute der Welt kennen. Meiner Meinung nach ist es nichts anderes als künstliche Veralterung, um die Leute zu zwingen, diesen neuen Dreck zu benutzen: Dreck, über den Leute kontrolliert werden können, weil sie selbst keine vollen Zugriffe mehr auf ihre eigenen Daten oder ihr System bekommen. Dreck, mit denen man Leuten vorschreiben kann, was sie mit ihren Computern machen dürfen und was nicht - weil das OS denen bestimmte Dinge, die zwar technisch möglich wären, einfach nicht mehr erlaubt.

Es geht einfach darum, willfährige "User" zu züchten - das was Apple schon seit Jahrzehnten macht, will man bei allen anderen jetzt auch durchsetzen. Und das ist nur ein weiterer Schritt in diese Richtung. Und es ist sehr leicht, Laien ohne Maschinen-Verständnis quasi JEDE Schweinerei als technische Notwendigkeit unterzujubeln. Und weil es so leicht ist, wird es auch gemacht.
zatzen hat geschrieben:In der Medienwelt läuft das ja auch so, aber da scheinen die Leute es ja gerne zu haben, mit der Zeit zu gehen.
"Mit der Zeit gehen" ist ein ähnliches Totschlag-Argument. Man stellt damit Leute, die sich für die Technik HINTER der Technik interessieren, als stumpfhirnige Höhlenmenschen dar. Indem man Leute, die bestimmte Dinge wollen / erhalten wollen, diskreditiert, erweckt man den Eindruck: "Dieser Bedarf muß nicht berücksichtigt werden, weil alle die das wollen, dumme Verlierer sind."

Beeinflußbare Leute wollen lieber zur hippen Clique gehören, als dafür, daß sie sich für etwas MEHR interessieren als das, was "Medienwelt" meint, was man "zu wollen hat", als dumme Hinterwäldler hingestellt zu werden. Das ist also alles Ergebnis von Marketing. Den meisten Leuten kann man bekanntermaßen jeden Mist einreden - wird auch seit Jahrzehnten gemacht. So "Werbefachleute" studieren diesen Kram schließlich, als wäre es eine Wissenschaft. Es gibt also Myriaden von Leuten, die in einer Berufsgruppe arbeiten, die den ganzen Tag nichts anderes machen, als (leider sehr erfolgreich) daran zu arbeiten, Leute zu überzeugen, was sie gefälligst "wollen sollen".
zatzen hat geschrieben:Die Vinyl-Schallplatte ist eine Ausnahme und scheint mittlerweile beliebter als die CD zu sein, obwohl sie wissenschaftlich gesehen von letzterer komplett in den Sack gesteckt wird was Klangtreue (und Komfort) angeht. Will heissen: Überspielt man eine Schallplatte mit guten Wandlern auf eine CD klingt die genauso wie die Schallplatte. Macht man von einer CD eine Schallplatte klingt es NICHT genauso.
Naja, und das ist dann eben wieder die berühmte Ausnahme:
Überall, wo es NICHT den EIGENEN Interessenbereich berührt, ist es egal, wenn Interessenbereiche ANDERER Leute beschnitten werden. Berührt es den eigenen Interessenbereich, ist es wichtig und tragisch.

Man könnte dem entgegenhalten, daß eine Schallplatte analog ist, d.h. JEDER denkbare Amplitudenwert möglich ist, bzw. nur noch davon anhängt, wieviele Atome breit die Spurrille einer Schallplatte maximal sein kann - während bei einer CD die Anzahl möglicher Amplitudenwerte auf eine diskrete Anzahl von 65536 begrenzt ist. Ich sage nicht, daß es nicht ausreicht oder mein Gehör so fein wäre, da noch "Treppen" zu hören. Ich sage nur, daß es auch Argumente für Schallplatte und gegen CD gibt.

Der Vorteil digitaler Daten (CD) ist ihre Robustheit. Weil es nur 0 und 1 gibt und NICHT "ein bißchen 1" oder "ein bißchen weniger 0", kann immer eindeutig zu 0 oder 1 zugeordnet werden - und entsprechende Fehlerkorrekturprotokolle tun ihr übriges, um die Daten auf einer CD selbst bei teilweiser Beschädigung noch zu 100% reproduzierbar zu machen.

Hat also alles Vor- und Nachteile und obwohl ich selbst keine Schallplatten habe, habe ich Verständnis dafür, wenn sie jemand den CDs vorzieht.

Denn ich erwarte ja schließlich auch Verständnis dafür (ohne daß es jemand genauso machen muß!), daß mir für Sound oft 8-Bit-Mono 11-16 kHz Daten mehr als ausreichen und ich nicht der Meinung bin, alles in aufgeblasenem 16-Bit-Stereo 44 kHz haben zu müssen - wogegen andere Leute der Meinung sind, daß selbst 384 kHz, 32 Bit immer noch "gerade mal gut genug" sind, um irgendwelche Sounddaten zu speichern.

(Das gleiche erlebe ich mit Grafik: Nein, 16777216 Farben (256 Phasen pro Rot/Grün/Blau-Phase) sind plötzlich ZU WENIG! - Nein, es müssen MINDESTENS 1024 Phasen (30-Bit) Farben sein! 16,7 Mio Farben reichen nicht, diese Leute können über 1 Milliarde verschiedener Farben wahrnehmen und wenn's weniger ist, sehen sie die Abstufungen zwischen den Farben... Daß die INDUSTRIE, die immer wieder was neues herstellen muß, um ihre Existenz zu rechtfertigen, mit solchem Kram kommt, ist mir klar und wundert mich nicht. Aber ich kenne leider Privatpersonen, die diesen Quatsch auch glauben...)

Zu VHS:
VHS hatte den Vorteil, daß es total idiotensicher war, eine Aufnahme zu machen, zu löschen, zu überschreiben, usw.: Einfach Play/Record/FastForward/Rewind/Stop... simpel wie ein Kassettenrekorder! Kein Auskennen mit dem DVD-Format, kein "Finishen" notwendig, damit die abspielbar wird. Tja - aber: Man (also die Film-/Musikindustrie) WILL ja auch gar nicht, daß irgendwer Aufnahmen von irgend etwas machen kann. Und wenn man es genügend erschwert, führt das dazu, daß es irgendwann auch sonst keiner mehr will.
zatzen hat geschrieben:Und kaum habe ich mich damit abgefunden, dass die neueren Computer keine Diskettenlaufwerke mehr haben, sind auch schon CD/DVD Laufwerke wieder aus der Mode.
Disketten: Selbes Ding. Einfach schreiben, löschen, überschreiben. So eine CD/DVD hingegen, die muß man brennen. Braucht ein spezielles Laufwerk UND spezielle Programme.

Die USB-Sticks sind wieder etwas "einfacher" als Wechselmedium: Die kann man endlich wieder wie Disketten/Festplatten beschreiben usw. Allerdings ist USB ein viel zu komplexes Format. Es wird zu viel Hardware und Software benötigt, um es irgendwo zu benutzen. Schon die offiziellen Specs vom ursprünglichsten USB sind ca. 1200 Seiten lang.

Gute Dinge / gute Ideen erkennt man immer daran, daß sie einfach sind.
Und: Bei allem (Hardware, Software, Programmiersprachen, andere Dinge) was man benutzen soll/will, sollte man immer daran denken, es wie bei HUMOR (bzw einem Witz) zu behandeln: Wenn man es erklären muß, ist es Mist.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Als ich auf Pascal kam, gab es erstmal eine MENGE, was ich komisch fand. Aber ich mochte schon auf Anhieb, daß es compiliert und war froh, von diesem Interpreter-Scheiß weg zu sein und seitdem will ich nichts mehr mit Interpreter-Zeug zu tun haben.
QuickBASIC hatte einen Compiler. Allerdings separat - die Testläufe machte ein Interpreter wenn ich nicht irre. Und der konnte meistens mehr Speicher fassen als der Compiler, deswegen war das oft ärgerlich wenn man sein Programm am Ende zurechtstutzen musste. Hatte ich schonmal erwähnt. Weil ich nur QBasic kannte, bei dem in der IDE die "SUBs" separat angezeigt wurden, fand ich erst unübersichlich dass man bei Pascal alle Routinen auf einmal sieht.
Ja, ich kenne noch jemand anders, der damals mit BASIC angefangen hat, jetzt aber immer noch in BASIC (Visual BASIC 6 und PowerBASIC) unterwegs ist und neueren. Aber der kennt das Obengesagte zu diesen alten PC-BASICs auch noch: Daß es interpretiert ODER compiliert sein konnte, der interpretierte (und langsamere) Kram aber mehr Speicher abkonnte...

Naja, selbst das VB6 erzeugt, nach dem, was der so erzählt, nur so 'ne Art Bytecode, der dann interpretiert wird... MIR wär das zu blöde - aber muß jeder selbst wissen. Ich sag mal so: Mein GameSys2 erzeugt auch Bytecode, der dann von einer (ASM-)VM interpretiert wird - aber das Ding ist ja nicht für ganze Programme gedacht, sondern nur zum Steuern von Figuren. Ich käme nicht auf die Idee, damit auch Grafiken zu berechnen/erzeugen.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ach, ich bin das $ so gewöhnt. Außerdem seh ich dann viel deutlicher, daß es Hex ist, weil das Zeichen nirgendwo sonst vorkommt:

bach = Variable Namens bach
0bach = Hex-Wert für dezimal 2988
Da ist mir $bac lieber.
Ich habe wohl verinnerlicht, dass Variablen nicht mit Ziffern beginnen dürfen. Ich hatte bisher keine Probleme. Aber jeder wie er will eben. :)
Aber tatsächlich gibt auch Freepascal bei einem RTE die Adresse mit einem $ vorne aus.
Naja, das ist die Notation, die Pascal von sich aus als primäre benutzt. Das mit dem h wird sekundär supportet für Leute, die's interessiert. Mich hats immer genervt, daß ich "verschieden lange" Hexzahlen haben muß: Damit Hexzahlen, die mit A bis F beginnen, als Zahl erkannt werden, immer diese 0 davor:
45FCh - 5 Zeichen
0FC45h - 6 Zeichen
Das sieht einfach scheiße aus. (Dann muß man wohl ALLE Hexzahlen "nullen", wenn man die untereinander schreibt oder so...) Aber muß eben jeder selbst wissen.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Und in C kann man keine Prozedures/Functions ineinander verschachteln. Das ist total arm!
Ich hab das bisher in Pascal auch nur sehr selten gemacht, ich dachte dabei immer das gäbe zu viel Stack-Beanspruchung.
Eigentlich ist das Gegenteil der Fall. Wenn man eine Procedure in eine andere schreibt, kann man die Variablen der "oberen" Procedure in der unteren mitverwenden, ohne sie nochmal extra übergeben zu müssen:

Code: Alles auswählen

  procedure Alpha(Eins,Zwei,Drei:word);
  var Vier,Fuenf,Sechs:integer;
    procedure Beta(Sieben,Acht,Neun:byte);
    var Zehn,Elf,Zwoelf:longint;
    begin
    {Hier bin ich in Procedure Beta -
     diese kennt jetzt ihre eigenen Variablen Sieben,Acht,Neun,Zehn,Elf,Zwoelf
     und zusätzlich kennt sie auch:
     Eins,Zwei,Drei,Vier,Fuenf,Sechs - ohne daß man sie übergeben mußte!}
    end;
  begin
  {hier bin ich in Procedure Alpha}
  Beta(120,243,77);{hier rufe ich Beta auf}
  end;
D.h. eigentlich braucht man so "Procedures in Procedures" (man kann auch Functions und Procedures oder umgekehrt benutzen und auch mehrfach verschachteln) gar keine Werte übergeben, wenn sie sich aus der darüberliegenden Procedure ergeben. Also: Klar, wenn Alpha nun Beta aufruft, landet die Rücksprungadresse, sowie die Variablen von Beta aufm Stack - aber das würden sie auch, wenn Beta "außen" (außerhalb von Alpha) stehen würden. ABER: Wenn Beta "außen" wäre, würde sie die Variablen von Alpha NICHT kennen und müßte diese alle mit übergeben bekommen, bräuchte also MEHR Stack.

Das heißt: Mit Proceduren in Proceduren braucht man nicht mehr Stack (sondern maximal gleich viel), sondern kann stattdessen damit sogar Stack sparen.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Wenn man eine komplexe Packroutine baut, die am Ende größer ist als das, was man beim Packen spart, hat man nichts gewonnen, weil ja alles im gleichen Speicher liegt.
Es ist wahrscheinlich nichts neues wenn ich sage, dass die vielleicht 2K mehr Code (wobei, da ist ja auch noch ne Tabelle dabei, mit 512 Byte aber kaum nennenswert), bei ZSM sich lohnen, sobald man eben mehr als 2K komprimierte Samples hat. Ähnlich denke ich über ZVID2: Ich möchte da vielleicht um die 300K (gepackt) an Grafik reinklatschen, die Routinen werden sicherlich auch ziemlich klein sein.
Ich meinte es nur als allgemeine Aussage, NICHT konkret auf ZSM oder ZVID/ZVID2 bezogen. Es ist einfach so, daß ICH, wenn ich so Dinge baue, schon manchmal extrem kompliziertes Zeug zum Packen gebaut habe, das dann derart viel Rechenzeit UND Code brauchte, daß es die paar Bytes DATEN Einsparung nicht mehr gerechtfertigt haben - und dann habe ich den Kram auch mal wieder ausgebaut.

ABER: Es hängt natürlich IMMER vom konkreten Anwendungsfall ab: Man kann z.B. eine riesige unrolled Loop bauen, die vielleicht 3 kB groß ist und denkt: Achdumeinegüte, 3 kByte! - Aber wenn man damit z.B. die Performance einer Grafikausgabe verdoppelt, verdreifacht oder gar verzehnfacht - was den Unterschied machen kann, ob ein Spiel 3 FPS oder 30 FPS hat - dann sind die 3 kB zusätzlicher Speicher vielleicht DOCH gerechtfertigt.

Aber wenn eine Routine zum PACKEN da ist (und der EINZIGE Grund, warum man Daten packt, ist, benötigten Speicherplatz zu reduzieren - es gibt keinen anderen!), dann sollte diese Routine auch WIRKLICH Speicherplatzverbrauch reduzieren! Wenn die Daten von 10 kB auf 3 kB gepackt werden (7 kB gespart), die Packroutine selbst aber 20 kB groß wäre, hätte man statt 10 kB dann 23 kB Speicherplatzverbrauch UND zusätzlichen Rechenzeitverbrauch (für Entpacken) und hätte quasi gar nichts gewonnen. Wenn das Entpacken (wie bei RAR/ZIP usw. irgendwo extern vorher passiert, ist es ja egal, wie groß die Routine ist. Wenn das Entpacken aber quasi "live" passiert und live im gleichen Speicher liegt wie die Daten (d.h. die Routine nach dem Entpacken nicht weg wäre), dann sollte es eben nicht mehr belegen, als der Packalgorithmus spart. Das ist alles, was ich damit sagen will.

Mitunter kann eine primitive, aber dafür schnelle und kleine Entpackroutine mehr helfen als eine komplexe, aber dafür große und langsame.

Die Engine von GameSys2 belegt z.B. ca. 20 kByte Speicherplatz - hinzu kommt noch der Bytecode, den sie ausführt, um Figuren zu steuern. Da könnte man sagen: Ist ja Speicherverschwendung gegenüber direktem Coden. Stimmt ja auch! -Nur dient GameSys2 NICHT dazu, Daten zu packen, sondern dazu, Daten auswechselbar zu machen und dazu, Dinge, die sowieso in Spielen gebraucht werden (wie z.B. Figur-Figur- bzw. Figur-Hintergrund-Kollisionen oder auch dynamische Speicherverwaltung von Figurendaten) gleich zur Verfügung zu stellen, so daß man es nicht für jedes Spiel (oder am Ende sogar für jede Figur!) einzeln neu coden zu müssen.

Das Gleiche kann man auch bei Sound sagen: Natürlich wäre es auch möglich, GAR KEIN Soundformat zu haben, sondern einfach nur ein paar Samples in den Speicher zu legen und direkt im Code einzelne Aufrufe einer Subroutine zu machen, die (gepitchte) Samples zum "Sound-Datenstrom" mixt - immer, wenn man meint, daß das nächste Sample gebraucht wird und KOMPLETT SO die Begleitmusik eines Spiels zu machen. - Möglich? Ja. Sinnvoll? Wohl kaum.
zatzen hat geschrieben:Übrigens an dieser Stelle nochmal das brenzlige Thema Hintergrundgrafik-Kompression bei der Blocksache:
Wieso ist das Thema "brenzlig"?
zatzen hat geschrieben:Wenn mein primäres Ziel nicht das Erreichen von möglichst hoher Performance ist, sondern möglichst viel Grafik im Speicher halten im Vordergrund steht: Dann ist es meines Erachtens sogar höchst sinnvoll, diese Kombination gepackter Hintergrund und nur teilweise restaurieren, denn: Wenn ich die Hintergründe faktisch definitiv packen will, dann schlägt der Performanceboost durch nur teilweises Restaurieren umso stärker zu Buche, im Vergleich dazu als wenn ich jedes mal ein Vollbild entpacken würde.
Ja, wie gesagt: Es ist immer ein guter Ansatz - gerade, wenn man im Realmode/VM86 und Heap (<640kB) codet, sich ein wenig zu überlegen, was man eigentlich braucht und was nicht - und selbstverständlich gibt es hier keine "allgemeingültige" Aussage, sondern hängt immer davon ab, was für eine Art Programm/Spiel man machen will. Und daß man Grafikdaten bei fixen Hintergründen sinnvollerweise komplett anders anordnen/speichern würde als z.B. bei scrollenden Hintergründen, steht ja wohl außer Frage.
zatzen hat geschrieben:
DOSferatu hat geschrieben:[vom Heap ins Codesegment kopieren]Klar kann man das. Wie im letzten Abschnitt erwähnt, ist das ja alles der gleiche Speicher. Und weil wir im Real Mode (nicht Protected Mode) sind, gibts hier keinen Speicherschutz/Segmentschutz. Das Codesegment ist nur dadurch definiert, wo wir CS hinlegen. Ginge es nicht, ins Codesegment zu kopieren, ginge ja auch kein selbstmodifizierender Code (was ich so oft mache, wenn ich keine Register mehr habe bzw für die Register anderweitig bessere Verwendung habe.)
Das wäre ja dann selbmodifizierend, im großen Stil sozusagen. Ein kleiner Performance-Haken könnte dann sein, dass das Kopieren den Codes auch eine kleine Weile dauert.
Wie bereits sehr oft erwähnt: Es ist IMMER ein Trade-Off zwischen Speicher und Performance. Alles gleichzeitig geht nicht. Einerseits das beste 3D-Spiel der Welt bauen, andererseits soll es nur 4 kByte groß sein - sowas wird z.B. nicht funktionieren. Wo man MEINT, daß einem selnstmodifizierender Code Vorteile bringt, kann man es ja einsetzen. Es stattdessen nur deswegen einzusetzen, weil "das schonmal jemand anders gemacht hat" oder weil es in einem völlig anderen Projekt mal einen Vorteil gebracht hat, ist selbstredend sinnlos. Also, wenn man das macht, sollte man auch wissen, warum. Und wenn man es vermeiden kann (z.B. weil man eigentlich auch noch Register übrig hätte), sollte man es auch vermeiden.

Selbstmodifizierender Code ist keinesfalls ein "Allheilmittel" und kann (auch performancemäßig) arg nach hinten losgehen.
zatzen hat geschrieben:
DOSferatu hat geschrieben:
zatzen hat geschrieben:[KI upscaling]Davon kann man halten was man will, aber auf eine Weise interessant ist es schon. Wobei ich sagen muss dass ich ein Fan von 320x200 (oder x240) Pixelgrafik bin und nicht das Bedürfnis nach "remaster" Editionen habe.
Ja, wie gesagt: Es gibt inzwischen ECHTE Remaster-Versionen, die wirklich ausgehend vom Originalmaterial neu gemacht wurden, NICHT von den Pixelbildern "hochgezogen".
Aber auch hier bin ich so geprägt, dass 320x200 zu einem stimmungsvollen Spielerlebnis beitragen kann. Die Hintergrundgrafiken von Monkey Island II sind kunstvoll mit Filzstift gemalt, durch die niedrige Grafikauflösung werden sie etwas pixelig-unschärfer und wirken dadurch paradoxerweise fotorealistischer. Wenn diese nun hochauflösend zu sehen sind sehen sie wieder mehr nach Zeichnungen aus. Bei der Special Edition wurden die Grafiken scheinbar auch nochmals stilisiert, weiter weg vom Original, das hat auf mich wieder zu sehr den Effekt "Hintergründe nur gemalt, Figuren wie aus Knete gemacht". - Oder vielmehr, alles wie aus Holz geschnitzt und angemalt. Das mag ja vielen gefallen, Monkey Island II als Holzpuppentheater, aber ich bevorzuge die pixelige Version die Raum zur Interpretation lässt und somit letztlich realistischer wirkt.
Geht mir genauso. Vieles so "nachträglich aufpolierte" Zeug sieht im Nachhinein nicht besser aus, sondern nur irgendwie "künstlicher" und verliert dadurch etwas an Reiz. Das ist teilweise auch bei digital remasterten und nachberechneten Filmen so: Manche der ("analogen") Effekte wurden damals gemacht mit dem Wissen, daß Nebel und Auflösung verbergen würden, wie es wirklich gemacht wurde und damit dem Effekt seine Wirkung verliehen hat. Werden solche Filme nachträglich digital "nachgebessert", wird das, was (zur Realisierung des Effekts) eigentlich verborgen bleiben sollte, wieder klarer gemacht und die alten Effekte wirken billig und albern. Muß man nicht wirklich haben - verstehen aber auch sehr viele Leute leider nicht.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Vielleicht sollte ich mal, mit meinen neuen Engines ein neues Xpyderz machen, das gleich von Anfang an die ganzen neuen Dinge benutzt: Das 100%-ASM Menüsystem, neue Darstellungsroutinen und natürlich auch ISM und so. Aber Xpyderz war damals eigentlich nur ein Experiment meinerseits. Das Spielprinzip ist ja nicht gerade der Brüller.
4-Wege Scrolling hätte mich zumindest als Kind begeistert, eine große Welt erkunden und so. Deswegen hab ich ja auch Zelda gespielt. Oder Cauldron II auf dem C64 fand ich auch toll. Mein Einstieg in die Computerspielewelt war im Prinzip Super Mario Bros. und da konnte man ja immer nur von links nach rechts laufen, aber niemals zurück.
Ja, "vorwärts immer, rückwärts nimmer"[TM].
Naja, ich gehe davon aus, daß unter anderem auch DAS (dieses 1-Richtung-Scrolling) bei Super Mario dazu beigetragen hat, diesen intern sehr effizient gespeicherten Levelaufbau zu schaffen, der es ermöglicht hat, in dem sehr kleinen Speicher so wahnsinnig viel Inhalt unterzubringen. Wie der Macher da die Levels gespeichert hat, ist 'ne Wissenschaft für sich - das ist nicht einfach ungepackt angeordnetes Blockraster.
zatzen hat geschrieben:Hello World:
DOSferatu hat geschrieben:Der Rest der "Magie" funktioniert, indem man die Anzahl Zeilen, die die Zeichen haben können, in der Grafikkarte auf 2 Zeilen stellt. Der Textmode ist ja quasi 720x400 Pixel
War mir gar nicht so bewusst, diese Auflösung. Also hat ein Buchstabenblock komische 9x16 Pixel. Es ist schon ne Weile her dass ich mal nen eigenen Zeichensatz gemacht habe... Trotzdem messe ich die Breite Deiner Pixel gleichmäßig mit jeweils 4 "echten" Pixeln. Irgendwo stimmt da was in meinem Verständnis nicht. Vielleicht ist der Textmodus ja doch nur 640x400, die Ascii-Zeichen sind jedenfalls 8x16, gerade nochmal nachgesehen.
Ja, die Zeichen sind 8x16, aber die Grafikkarte stellt sie standardmäßig (bei Herc, CGA und bei VGA, nur nicht bei EGA) mit 9 Spalten dar, um die Lesbarkeit zu erhöhen. Das ist hardwaremäßig in die Grafikkarte eingebaut. Die 9. Spalte ist dabei entweder "leer" (Hintergrundfarbe) ODER ene Kopie der 8. Spalte (letzteres nur für Zeichen 192 bis 223 möglich und abschaltbar). Zeichen 192 bis 223 sind die "Grafikzeichen - damit so Linien nicht "unterbrochen" aussehen. Das ist der Grund, wieso man die "Rähmchen" beim 437er Zeichensatz, die nach links gehen, VOR 192 gelegt hat - weil die diese kopierte 9. Spalte nicht brauchen. Man kann die 9. Spalte übrigens auch abschalten, dann stehen die Zeichen wieder enger nebeneinander (werden also 8x16 dargestellt und die Auflösung ist 640x400).

Anmerkung dazu: DOSbox supportet die 9. Spalte standardmäßig nicht - wahrscheinlich aus Performancegründen. (DOSbox macht ja alles als Grafik, stellt also auch den Textmode als Grafik dar.) Es gibt aber einen alten "Hack" von DOSbox 0.72, bei dem die Leute (simulierten) Netzwerkkartensupport eingebaut haben und diese Version supportet im Fenstermode auch die 9. Textmode-Spalte

Eigentlich dachte ich, dieses Wissen über den Textmode und so weiter wäre inzwischen Grundwissen von so DOS-Freaks. Also, ja: Datentechnisch sind es 640x400 - aber die Grafikkarte stellt 720x400 Pixel dar und der 9. Pixel wird von der Grafikkarte selbst generiert (siehe oben). Diese Möglichkeit der 720er Breite ist auch der Grund, wieso der Grafikmodus 360x200 bzw. 360x240 noch zu den "normalen" zählt (die auf jeder VGA funktionieren). Weil das Timing für die 720er sowieso schon eingebaut ist.
zatzen hat geschrieben:
DOSferatu hat geschrieben: - und so erhält man quasi einen 160x200 "Pseudo-Grafik" Mode:
Es sind 80 Zeichen, die aber aus zwei Hälften bestehen: Vordergrund- und Hintergrundfarbe sind jeweils der linke und rechte Pixel. Und man sieht nur die ersten 2 Zeilen des Zeichens, dann kommt die nächste Zeichenzeile.
Auch sehr interessant. Hätte dieser Modus gewisse Vorteile gegenüber einem EGA 160x200, oder waren Spiele wie die ersten von Sierra in Wirklichkeit vielleicht sogar in diesem Modus gehalten?
Vorteil gegenüber EGA: In EGA Grafikmodus sind die Pixel in Planes aufgeteilt. D.h. EGA sind eigentlich 4 monochrome Planes, die übereinander liegen und die 4 HINTEREINANDER (also auf die 4 Planes verteilten!) Bits ergeben jeweils eine der 16 Farben. Kannst Dir ja vorstellen, was für'n Spaß das ist, die Farbe EINES EGA-Pixels zu ändern.

Diesen Trick mit den Textmode hat man aber wohl eher bei CGA angewendet:
CGA hatte 16farbigen Textmode und nur 4-farbigen Grafikmode. Außerdem waren die Zeichen des Textmode nicht veränderlich. ABER: Standard CGA hatte nur 16kB Speicher oder glaub später 32kB - damit gehen aber keine 160x200, sondern nur 160x100. Wieso? Naja, 160x200 sind 32000 - aber man braucht für 2 "Pixel" ja trotzdem 2 Bytes, auch wenn eins der Bytes immer das gleiche "Zeichen" ist, also braucht man 64000 Bytes - das war bei CGA nicht eingebaut.
zatzen hat geschrieben:Aber ich sehe gerade, dass die Grafik selbst dort zwar auf 160x200 beschränkt ist, die Textausgaben aber in 320x200 aufgelöst sind. Dann ist es wohl insgesamt 320x200 Grafikmodus, und man wollte bei der Grafik einfach sparen. Oder einfach ein komischer Modus der zwar 320x200 Text aber nur 160x200 Grafik kann?
Kann ich nicht sagen, kommt darauf an, wie es die Programmierer gemacht haben. Wenn es nur 16 Farben sind, wird es kaum der berühmte Mode-X (oder Mode-Y) sein. Wegen seiner komischen Anordnung hat ModeXY die Eigenschaft, auch mehrere gleichfarbige Pixel gleichzeitig (d.h. mit einem einzigen Zugriff) setzen zu können, weil man bis zu 4 nebeneinanderliegende Pixel "chainen" kann. Das gilt aber nur für den Zugriff, d.h. Darstellung ist trotzdem einzeln. D.h. man kann das Chaining auch immer an- und ausschalten und z.B. für eine Grafik das Chaining anschalten, damit man nur halb so viele Zugriffe braucht und für Text wieder aus, damit man die Pixel einzeln ansprechen kann.

Man sieht das auch gut, wenn man Xpyderz die LowRes-Einstellung benutzt: Da wird nur das Level LowRes gemacht, aber die Sprites und Anzeigen sind weiterhin HiRes - d.h. Auflösung bleibt 320x200, aber im LowRes-Chaining (immer 2 nebeneinanderliegende Pixel gleichzeitig) werden mit einem Zugriff 2 Pixel gleichzeitig gesetzt, um Rechenzeit zu sparen.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Mit DOSbox kann man recht gut sehen, was das Ding macht: Wenn man die Zyklen auf einen sehr geringen Wert stellt, sieht man, wie der das ganze Bild aufbaut. Das ist NICHT irgendeine Grafik, die der da irgendwo hin"kopiert". Der "malt" das Bild wirklich.

Und da denk ich immer: Die heutigen Hoch-/Skriptsprachen-Trottel, die für alles nur immer ihre riesigen Frameworks benutzen und noch nie mal Binär-Arithmetik gemacht haben, ob die wohl selber (und in ASM) so 'ne Linienziehroutine bauen könnten...
Jeder hat eben sein Spezialgebiet, und wer objektorientiert an künstlicher Intelligenz bastelt wird nicht viel mit Linienalgorithmen zu tun haben. Wobei eine allgemeine Programmiererfahrung auf jedem Gebiet sicherlich nicht schädlich ist.
Ja, aber wer Frameworks anderer Leute benutzt, ohne zu wissen, wie sie funktionieren, könnte sie auch nicht nachbauen und wäre immer darauf angewiesen, und davon abhängig, daß einem andere Leute erst irgendwelches Grundlagenzeug zusammenbauen, bevor man selbst überhaupt anfangen könnte, zu programmieren. Und GERADE wenn man darauf angewiesen ist, daß es Leute gibt, die sich mit Binärarithmetik und systemischer Programmierung auskennen, sollte man nicht so herablassend über diese urteilen (wie es viele dieser Skripter leider gerne tun, so als wären Systemprogrammierer dumme Höhlenmenschen und sie wären diesen gegenüber so erhaben) weil man ohne die nämlich garnix wäre.
zatzen hat geschrieben:
DOSferatu hat geschrieben:[Zufallsgenerator]Kannst ja mal raten, woher der seinen Anfangswert (seinen "Seed") bekommt. Ist ja nicht schwer.
Timer? Ist ja die übliche Methode.
Ja, Timer natürlich.

Hier gleich zum nächsten Posting:
zatzen hat geschrieben:Ich habe mich mal wieder mit ZVID2 beschäftigt. Für andere Mitleser: Ein Grafikformat das die Grafiken in 4x4 Pixel Blöcke unterteilt und dann jeweils nur mit so viel Bit speichert wie nötig, entsprechend der Anzahl der verschiedenen Farben innerhalb eines 4x4 Blocks. Das ganze soll dann in Echtzeit entpackt werden.

Naja und ich rolle das ganze nochmal mehr oder weniger neu auf und habe jetzt schnell gemerkt, dass ich die Assembler-Darstellroutinen zuerst schreiben muss, denn das Format muss sich nach der Darstellungsroutine ausrichten und nicht umgekehrt.
Naja, das kann man so oder so machen. Um irgendwelche Routinen zu schreiben, braucht man ja erst einmal eine Idee, was sie machen sollen, und da kann es nicht schaden, dabei eine Art Format im Hinterkopf zu haben. Wenn aber das Format nicht irgendwelchen allgemeinen Formaten angepaßt sein muß, kann es dabei nicht schaden, es direkt auf die systemischen Möglichkeiten zuzuschneiden, um es sich selbst - und der Maschine - "leichter zu machen".
zatzen hat geschrieben:Jetzt habe ich noch eine Frage zum Thema Speicher an Nulloffset-Adresse anlegen. Du (DOSferatu) sagtest mir, ich muss das so machen (nachdem ich mit getmem 16 Byte mehr reserviert habe):

Code: Alles auswählen

inc(segment, succ(offset shr 4)); offset := 0;
Zum einen habe ich da eine Verständnisfrage, zum anderen wäre es in meinem Fall vorteilhaft wenn man das Segment immer um genau 1 erhöhen könnte, unabhängig von Inhalt des Offsets, also:
Wenn ich mit getmem genau 16 Bytes mehr reserviere und nicht mehr, dann funktioniert das ganze nur, wenn offset maximal 15 ist - andernfalls würde der reservierte Speicher nicht ausreichen. succ(offset shr 4) ergibt immer 1, egal ob offset 0 ist oder größer, solange < 16. Also müsste eigentlich auch inc(segment); offset := 0 korrekt sein, zumindest ist es im Ergebnis identisch mit inc(segment, succ(offset shr 4)); solange offset < 16 ist.
Ja, WENN Getmem immer einen Offset <16 zurückgeben würde, wäre das so! Hier geht es ja darum, genau den Speicher zu "treffen", den man reserviert hat UND einen Offset von 0 zu haben. Das Ganze kann dadurch erreicht werden, weil man weiß, daß die Segmente sich "überlappen": Alle 16 Bytes fängt ein Segment an. Die Speicheradresse
$1234:$5678
ist also die gleiche wie die Adresse:
$1234+$567:$0008 (also $179B:$0008)
weil beides der physikalischen Adresse $000179B8 entspricht.

Und weil ich nicht WEIß, bzw. mich nicht darauf verlassen kann, in welcher Anordnung mir GetMem einen Pointer zurückliefert, "normalisiere" ich diesen Pointer, indem ich den den *16-Teil des Offsets zum Segmentanteil "rübernehme" und mir nur ein Offset zwischen 0 und 15 bleibt. Genauer geht es eben nicht, weil Segmente immer nur an jeder 16. Speicheradresse anfangen. Wenn ich dann nun einen Segmentanfang mit Offset 0 haben will, muß ich eben ein paar Bytes verschwenden. Ich kann aber nicht einfach den Offset, falls er <>0 ist, "weglassen", denn dann wäre die damit beschriebene Speicherstelle ja VOR dem Pointer - also muß ich das Segment um 1 erhöhen (was die physikalische Speicherstelle um 16 erhöht) und DANN kann ich den Offset =0 setzen, weil er dann mit Sicherheit innerhalb des reservierten Speichers liegt.

Und: Bevor ich jetzt analysiere, ob und in welchem Format mir GetMem da Speicher liefert (was im Endeffekt wahrscheinlich sowieso irgend einen INT-Zugriff macht) und evtl. sogar davon abhängt, ob es in DOSbox oder Windows XP oder echtem DOS gemacht wird, mache ich doch lieber diesen sehr einfachen obengenannten Trick und kann damit sicher sein, daß ich einen Pointer mit Null-Offset habe - egal, in welcher Anordnung ich den von Getmem erhalte. Denn bei GetMem ist nur definiert, daß es einen Pointer bestimmter Größe an einer bestimmten Stelle reserviert, aber nicht, wie der den Pointer zurückgibt.

Bei Zugriff auf einen Pointer macht die CPU intern immer (Segment SHL 4)+Offset, um die physikalische Speicheradresse zu bekommen - ob der "addierte Teil" mehr im Offset oder mehr im Segment liegt, um auf dieselbe Adresse zuzugreifen, ist der CPU egal. Der Programmierer jedoch kann das entscheiden und findet einen Offset 0 vielleicht praktischer, um bei Register-indizierten Zugriffen auf ein Segment nicht immer noch einen zusätzlichen Rest dazuaddieren zu müssen.
zatzen hat geschrieben:Nebenbei noch, welche Register könnte ich noch "missbrauchen" in einer Routine ohne Stackrahmen (d.h. ohne Variablenübergabe sondern einfach "procedure blabla; assembler ...")? BP ist mir geläufig, die ?X Register sind alle belegt, wie auch DS, ES, FS, GS.
Evtl. SP? Wobei mir das abenteuerlich scheint, den Stack-Pointer auf dem Stack zu sichern ist wohl keine gute Idee, allemal wäre ein sichern im Speicher (DS verändere ich nicht) denkbar, aber auch da sehe ich Konflikte wenn Interrupt-Routinen vorkommen.
Also, rein technisch gesehen hat SP die gleichen Eigenschaften wie z.B. DX - man kann auch damit rechnen, shiften, usw. ABER: SP ist, wie der Name SP schon vermuten läßt, der StackPointer und zeigt an, wo der Stack gerade als nächstes beschrieben/gelesen wird - sowohl bei jedem Unterprogrammaufruf, als auch bei PUSH/POP von Werten oder Registern und selbstverständlich auch von JEDEM Interrupt!

Wenn man also wirklich SP anderweitig nutzen will, muß JEDER Interrupt abgeschaltet (gesperrt) werden und dürfen zwischendurch weder Unterprogrammaufrufe (CALL), noch PUSH/POP/PUSHF/POPF Befehle erfolgen. Und ja, das geht - aber hier überwiegen eindeutig die Nachteile: Der Timer ist aus, die Tastatur wird nicht mehr automatisch abgefragt und, übrigens: IRQ5 (oder IRQ7 oder was auch immer man eingestellt hat) vom Soundblaster ist z.B. auch ein solcher Interrupt. Der käme dann natürlich auch nicht mehr.

Hardware-Interrupts sind ja so konzipiert, daß sie die Arbeit der CPU quasi jederzeit (nach Abarbeitung des aktuellen Opcodes) unterbrechen dürfen, ihr Zeug machen und dann die CPU so hinterlassen "als wäre nichts gewesen". Und das geht nur, weil sie die Flags und die aktuelle Adresse, wo die CPU gerade liest, auf den Stack legen (und innerhalb der Interruptroutine vielleicht auch noch andere Register, falls die Routine die verändert) und dazu muß natürlich ein funktionierender Stack mit korrektem SP da sein.

Und noch zum dritten Posting:
zatzen hat geschrieben:Ich habe beim Programmieren wieder was gelernt.
Wahrscheinlich ist Dir der Sachverhalt schon bewusst, leider haben wir es aber nie thematisiert.
Ich habe meinen ASM Code mal kompiliert und wieder disassembliert. Dabei habe ich mich gewundert, warum bestimmte Sprunginstruktionen nicht so durchkommen, wie ich sie programmiert habe. Kurz gesagt:
Der Pascal-Compiler hat aus "jnz @loop" soetwas gemacht wie

Code: Alles auswählen

jz @skip; jmp @loop; @skip:
Das handelt einem dann je nach Umständen unnötige Sprünge und Takte ein. Den Grund habe ich auch schnell begriffen - im 286er Befehlssatz gibt es nur 8 Bit bedingte Sprünge (short relative), und das Sprungziel war > 128 Byte entfernt. Der Pascal-Compiler würde die gleichen Workarounds möglicherweise auch für JCXZ basteln, aber hier existiert keine Inversion des Befehls, weshalb das nicht möglich ist.
Ja, das stimmt. Eine Umsetzung durch Parser in teilweisen (inline) 386er-Code kann gar nicht schnell genug kommen, oder? Ja, wie gesagt: Muß man sehen. Ich wollte es ja immer mal machen, aber irgendwie murks ich hier (falls ich überhaupt mal code) mit meinem eigenen Kram rum.
zatzen hat geschrieben:Ein eleganter Lösungsansatz wäre gewesen, entsprechend Inline einen 16 Bit Jump zu definieren. Leider kann man aber nicht einfach schreiben "dw 0850fh; dw offset @loop - offset @jump - 4", obwohl das für den Compiler eigentlich machbar wäre.
Ich weiß, irgendwie will der keine Offsets subtrahieren (hab sowas auch schonmal versucht). In so Inline-Code (also dem 386er-Assembler, den ich bauen würde) würde der dann auch die Befehle, die er nicht "ersetzt", trotzdem analysieren, um zu zählen, wieviele Bytes sie brauchen und diese dementsprechend addieren. So wüßte er jederzeit, wie die entsprechenden Offsets wären. Selbstredend geht das bei Vorwärtssprüngen nur mit 2-Pass-Assemblieren, (also 2 Durchgängen) das macht auch Pascal wohl so.
zatzen hat geschrieben:Also fiel mir vorerst nur selbstmodifizierender Code ein:

Code: Alles auswählen

mov ax, 0850fh; mov cs:[offset @jump], ax
{... zwischendrin kommt ein Sprung vor (Cache wird gelöscht) ...}
@jump: nop; jmp @loop
Aber vielleicht gibt es ja doch noch eine Möglichkeit, sich die zwei modifizierenden Befehle (und die Notwendigkeit den Cache zu leeren) zu sparen. Bei Dingen wie meinem blockweise Hintergrund-Restaurieren wären sonst nämlich massig Selbstmodifikationen nötig.
Momentan sehe ich da auch gerade keine andere Möglichkeit. Es ist nervig, ich weiß. OK, es GIBT natürlich eine andere Möglichkeit: Manuell alle Bytes zählen, die alle Befehle zwischen Sprung und Sprungadresse brauchen. Aber das ist ja wirklich Pain-in-the-Ass.

Und das Selbstmodifizieren müßte übrigens nur 1x gemacht werden, danach wäre der Code ja so wie er soll. D.h. die Routine hätte dann einen Init, der nur 1x ausgeführt werden muß. Aber das ist eben alles nur eine "Krücke" und für "den echten Shit" wäre ein 386er-Assembler, der so Sachen automatisch im Code abändert, echt mal hilfreich.
zatzen hat geschrieben:JCXZ bekommt in diesem Zusammenhang eine besondere Nützlichkeit: Man kann damit testen, ob die Modifizierung eines bedingten Sprungs nötig ist, indem man diesen beim Programmieren probeweise durch JCXZ ersetzt.
Stimmt.
zatzen hat geschrieben:Ich habe nochmal in diesem Thread gestöbert und bin auf Deine Erklärung gestoßen dass man auch Variablen (und wie ich festgestellt habe auch Register) mittels "dw" (oder db, dd) in den Code einlassen kann. Mir ist nur etwas unklar, wie der Compiler das handhabt: Den Wert einer Variablen kann er beim kompilieren nicht wissen, also kann es eigentlich nur der Offset im Datensegment sein.
Offset im Datensegment, wenn es eine Datensegment-Variable (global) ist. Die lokalen Variablen werden als relativer Offset gespeichert. Benutzung einer lokalen Variable (also entweder im Header oder mittels var innerhalb einer Procedure/Function angelegt) führt dazu, daß sie intern durch einen [BP+LokalerOffset] Zugriff ersetzt wird,

Register kann man nicht im Code als Offset einlassen - das ist totaler Blödsinn. Register liegen nicht im Speicher, sondern sind Bestandteil der CPU. Daß dw solche Dinge in SUBROUTINEN (teste es mal im Hauptprogramm, da geht es nicht!) machen kannst, bzw der Pascal-Assembler es zuläßt, liegt daran, daß Pascal es ermöglicht, Registerinhalte vom Hauptprogramm automatisch zu übertragen - z.B. für Procedures, mit Typ "Interrupt". Diese Offsets werden Dir also in Assembler nichts bringen, sondern geben lediglich Werte zurück, die aus Hochsprachen-Fragmenten übrig sind. Pascal legt diese Register also nur als eine Art Variablenname an, damit diese Bezeichner dann nicht ungültig sind.
zatzen hat geschrieben:Demnach müsste er bei Registern auch nicht deren Inhalt speichern, sondern den Wert als Offset interpretieren.
Definitiv nicht. Ob ein Registerwert von der CPU wie ein Offset zu irgend etwas wahrgenommen wird, liegt ausschließlich daran, wo es eingesetzt wird:

Code: Alles auswählen

mov AX,BX   {AX erhält den Wert von BX}
mov AX,[BX] {AX erhält den 16bit-Wert, der an Speicherstelle DS:BX steht}
Im zweiten Fall ist BX also ein Offset zum Segmentanfang (ohne Segmentangabe meist DS).

Ein Register hat also keinen "Offset", weil es nicht in irgend etwas "liegt", zu dem es einen Abstand haben kann. Es liegt in der CPU und hat eine Nummer.
zatzen hat geschrieben:Aber beim Kompilieren ist ja der Wert wiederum unklar. Rätselhaft. Gerade ins Kompilat geguckt: Bei Registern kommt einfach 0 raus, jedenfalls bei AX. Wozu kann man dann überhaupt Register in den Code bringen?
Ja, wie gesagt: Erklärung oben.
zatzen hat geschrieben:Jedenfalls könnte man obiges Problem auch auf diese Weise lösen (wie wir sehen werden tatsächlich NICHT), es wäre aber umständlicher und es bräuchte je nachdem für jeden Fall eine extra globale Word-Variable:

Code: Alles auswählen

mov ax, offset @loop; sub ax, offset @jump + 4; mov jumpamount, ax {vorbereitend irgendwo oben im Code}
dw 0850fh; dw jumpamount {später im Code in einem Teil die oft aufgerufen bzw. angesprungen wird}
Und HALT! Genau das wird so nicht funktionieren, weil ja nicht der Inhalt von "jumpamount" in den Code einfliesst sondern der Offset... Naja ich lass das mal stehen, hab immerhin nochmal etwas draus gelernt.
Ja, ist eben nicht so einfach. Gerade das mit den Sprung-Offsets (oder allgemein mit Offsets) muß man sehr aufpassen.
Erklärung dazu: bei "mov ax, offset @loop" wird der Offset von @loop zum Segmentanfang von CS zurückgegeben. Bei relativen Sprüngen wird aber der Offset (d.h. Abstand) zwischen dem Byte direkt nach dem Sprungbefehl und dem Byte, wohin gesprungen werden soll, gebraucht.
zatzen hat geschrieben:Übrigens, noch eine kleine Sache: Ich habe jetzt bei Segment-Overrides in Zusammenhang mit BP keine Probleme gehabt. Mein Fehler war, dass ich bisher z.B. "db 65h; mov ds:[bp], ..." geschrieben habe. Wenn ich nur "db 65h; mov [bp], ..." schreibe funktioniert das mit dem Override tadellos.
Ja, aus zwei Gründen. Erstens: Im Falle BP (oder irgendwas in Kombination mit BP muß es SS sein, nicht DS.
Und zweitens: Ja, und zwar weil er, wenn Du BP benutzt, er normalerweise GAR KEIN Segment hinschreibt (dann aber von der CPU automatisch SS benutzt wird) und dadurch funktioniert dieser getrickste der Segment Override. Würdest Du aber "db 65h; mov [bp+Beispiel];" schreiben und Beispiel wäre eine globale Variable, wüßte der Assembler: Aha, die liegt an DS, also muß ich DS davorschreiben, weil ja BP normalerweise SS hat (wenn man nix davorschreibt). Und DANN würde das db 65h nur einen WEITEREN Segment Override DAVOR machen und im Speicher hätte man dann quasi sowas:
mov GS:DS:[bp+Beispiel] - und damit würde das GS: ignoriert werden, weil danach noch das DS kommt.

Der Trick, DS oder SS davorzuschreiben, wenn man die $64/$65 Segment-Overrides benutzen will, ist, daß man dan ursprünglichen Befehl dazu "zwingt", GAR KEIN Segment davorzuschreiben, indem man im Quelltext genau das Segment davorschreibt, das er sowieso bei diesem Zugriff (der in den eckigen Klammern steht) benutzen würde - denn dann läßt der Pascal-Assembler es nämlich aus Rationalisierungsgründen weg und somit greift das mit dem Byte getrickste $64 oder $65. Und man kann natürlich Glück haben, daß er auch kein Segment davorschreibt, wenn man selbst keins schreibt - aber dann muß man aufpassen, was in den eckigen Klammern steht, sonst kann man böse in die Falle gehen!

Andersherum geht es auch: Wenn man z.B. einen Befehl hat, der zwecks modifiziertem Code auf verschiedene Segmente zugreifen kann (sowas benutzt GameSys2 bei einigen Befehlen) und das "normale" Segment wäre DS, dann muß man den "Override" für DS als Byte (mit db) davorschreiben, weil z.B. ein einfaches "mov AX,DS:[BX]" (statt "mov AX,[BX]") NICHT dazu führt, daß er das $3E (für DS) davorsetzt, weil er ja weiß, daß das redundant ist.

Es ist also praktisch nur eine List, den Assembler dazu zu bringen, "sein" Segment-Override-Präfix wegzulassen, damit man selbst eins setzen kann, das dann beachtet wird.
zatzen hat geschrieben:Und noch eine Analyse (ja, ich habe den Text hier mittlerweile schon mehrfach editiert):
Du sagtest mir mal, dass ich einfach Glück gehabt hätte wenn ich in einer Assembler-Prozedur RET verwende und mir nicht überlege ob RETF vielleicht richtig wäre. Nun, ich selber kann es ja auch schlecht einschätzen, zumal der Compiler nicht soetwas wie "CALLF" kennt mit dem man das selbst festlegen könnte.
Stimmt, CALLF gibts nicht. Der Befehl dafür heißt:

Code: Alles auswählen

call far ptr NameDerRoutine
zatzen hat geschrieben:Dafür scheint er aber selbständig die richtigen Entscheidungen über RETF/RETN zu treffen, wenn man einfach nur RET schreibt: In meinem Code steht einfach nur RET, aber der Compiler hat ein $CB draus gemacht, also ein RETF. Schreibe ich dagegen explizit RETN geht das so auch ins Kompilat ein mit $C3.
Natürlich ist der Compiler nicht total doof. Es ist so: Greift man auf eine Routine innerhalb des gleichen "Codeblocks" zu (also etwas, das zwischen asm und end; liegt), weiß er, daß da NEAR ausreicht, macht NEAR calls und bei RET setzt er IMMER NEAR - niemals FAR!

Greift man auf eine Routine außerhalb des Codeblocks zu (also z.B. irgendeine Pascal-Routine) wird er sie so aufrufen, wie sie definiert ist: Ist im Header $F definiert ODER hinter der Routine steht ;FAR;, wird er sie mit einem FAR CALL aufrufen (weil er weiß, daß die Routine auch ein FAR RET haben wird), anderenfalls hängt es davon ab, wie das Programm strukturiert ist. Routinen innerhalb von Units sind glaub immer FAR, weil die ja von jeder Stelle aus aufrufbar sein müssen, auch wenn man mal nicht im gleichen Codesegment ist. Das hängt damit zusammen, daß eine Unit ja schon vorcompiliert ist. Routinen im gleichen (Haupt-)Programm können je nach Compilerschalter {$F-}/{$F+} entweder NEAR oder FAR sein.

Der Compiler wird sie natürlich so aufrufen, wie sie definiert ist. Prinzipiell könnte man JEDE Routine, die im GLEICHEN Codeblock ist mit NEAR oder FAR aufrufen. Das Problem ist dann nicht der Aufruf, sondern der Rücksprung! Wenn man eine FAR-Routine mit NEAR-Call aufruft (wenn sie im gleichen Codeblock ist), geht der Aufruf zwar noch glatt - aber beim Rücksprung (wenn sie FAR ist, hat sie einen FAR RET (bzw RETF) im Code stehen) will sie einen Offset UND ein Segment vom Stack holen, der NEAR-Call hat aber nur einen Offset auf den Stack gelegt und sie holt 2 Bytes mehr vom Stack als sie dürfte. Schon haben wir den Salat!

Andersherum; Wenn man eine NEAR-Routine mit einem FAR-Call aufruft, legt der CALL zuerst das aktuelle Segment (CS) auf den Stack und dann den aktuellen Offset (also IP) und springt dann zur Routine. Das geht auch gut. Aber eine NEAR-Routine hat auch nur einen NEAR-RET und der holt NUR den Offset wieder zurück, das vorher gespeicherte CS bleibt auf dem Stack und ist dort "zu viel": Wenn irgend etwas anderes jetzt sein gespeichertes Zeug vom Stack zurückholt, holt es erst stattdessen zuerst den Inhalt von CS, ohne es zu wissen - und das ist dann sicher nicht das, was es haben wollte.

(Anm.: Um eine FAR-Routine mit NEAR aufzurufen, müßte man nach dem Aufruf noch add SP,2 machen - oder irgendwie anderweitig das überflüssige Word vom Stack holen.)

Daß man eine FAR-Routine, die in einem ANDEREN Codeblock (Segment) liegt, natürlich NICHT mit einem NEAR-Call aufgerufen bekommt, dürfte klar sein: Um auf Code in einem anderen Segment zu springen, muß man dieses andere Segment ja angeben - und ein NEAR Call gibt ja nur den Offset an (für das aktuelle Segment).

Und ob man RET oder RETN schreibt, wenn man weiß, daß es ein NEAR-Rücksprung wird, ist egal - der Assembler macht aus beidem den gleichen Code. Aber erstens hab ich's ganz gerne, daß ich auch SEHE, daß das ein NEAR RET wird und zweitens gehe ich damit sicher, daß der Compiler/Assembler keine Scheiße baut.

An dieser Stelle auch mal ein lustiger Trick, wie man eine FAR-Routine (z.B. im Hauptprogramm oder einer Unit) in Assembler starten kann, wenn man nur ihren Pointer hat (z.B. in EAX):

Code: Alles auswählen

db $66;push AX; RETF
Ja, es belästigt kurz den Stack, aber jede andere Möglichkeit, die ich mir vorstellen kann, ist komplizierter.

Wozu könnte man das brauchen?

Code: Alles auswählen

procedure @Koche;begin end;
procedure @Backe;begin end;
[...]
const TU:array[0..5]of Pointer=(@Koche,@Backe,@Putze,@Wasche,@Singe,@Tanze);
[...]
asm
{in BX steht Nummer der Routine}
shl BX,2;
push ES;db $66;push word ptr [TU+BX];RETF;pop ES;
end;
Achja, und falls den Routinen noch Variablen übergeben werden sollen, muß man die vorher pushen...
Nur eine der vielen komischen Ideen, die ich so habe.
Mein schicker Skriptinterpreter, mit dem ich z.B. (unter anderem) so eine GUI steuern könnte, kann externe Routinen aufrufen und denen Daten aus diesem Skript (oder von woanders hergeholt) übergeben.

Einen ähnlichen Trick benutze ich z.B. auch öfter mal, wenn ich eine Routine aufrufe und nach Ende soll die an verschiedenen Stellen weitermachen, die ich schon vor dem Aufruf weiß: Da pushe ich den offset der Stelle, wohin sie danach soll, auf den Stack und rufe die Routine nicht mit CALL sondern nur mit JMP auf. Die Routine hat aber am Ende ein RET und wird sich daher vom Stack holen, wohin sie "zurück" soll. Das kann man auch benutzen, um eine Routine wahlweise per CALL oder JMP aufzurufen. Denn manchmal ist es so, daß bestimmte Dinge im Programm öfter gebraucht werden, aber jedesmal müßte man dann die Routine mit der gegenteiligen Bedingung überspringen und einen CALL machen. Da kann man doch auch das machen:

Code: Alles auswählen

push CS:offset @dahinter;jc @routine;add sp,2;@dahinter:
Was ist der Witz daran? Naja, wenn Carry gesetzt ist, springt er zur Routine, diese wird beim Beenden "@dahinter" vom Stack holen und dort landen. Wenn Carry gelöscht ist, pusht er nur einen Wert aufn Stack, macht den Sprung nicht und addiert danach den Stack um 2 (was den Wert wieder wegmacht).
Die eigentlich normale Alternative wäre:

Code: Alles auswählen

jnc @dahinter;call @routine;@dahinter:
Ja, da ist ein Befehl weniger drin - ABER: egal, ob Carry gesetzt ist oder nicht, wird hier gesprungen. Und ja, das ist natürlich nur dann sinnvoll, wenn nicht zusätzlich dieser lustige 286er "ich kann nur 8-Bit-Offsets" Kram passiert.

Auch schonmal erwähnt:
In GameSys2 habe ich viele "Befehle" drin, die einen Rückgabewert haben. Der Befehl selbst wird immer per CALL aufgerufen. Die Routine, die den Rückgabewert wieder zurückschreibt, wird auch per CALL aufgerufen.

Somit wäre das so gewesen:

Code: Alles auswählen

@SchreibeErgebniswert:
{tue, was es muß, um Ergebnis zu schreiben}
RET{a}
{...}
@NaechstenBefehlAufrufen:
call @Befehl42;
{...}
@Befehl42:
{tue, was Befehl 42 so tut}
call @SchreibeErgebniswert;
RET{b}
Klingt plausibel und wie ordentliche Programmierung - ABER:
Wenn das ausgeführt wird, wird nach SchreibeErgebniswert mit RET{a} zurückgesprungen - und wohin? Genau vor RET{b}. D.h. ein RET, nur um danach ein weiteres RET zu machen - das ist doch Schwachsinn... - also hab ich an allen Stellen, wo das so war, das gemacht:

Code: Alles auswählen

@SchreibeErgebniswert:
{tue, was es muß, um Ergebnis zu schreiben}
RET
{...}
@NaechstenBefehlAufrufen:
call @Befehl42;

{...}
@Befehl42:
{tue, was Befehl 42 so tut}
push CS:offset @NaechstenBefehlAufrufen; jmp @SchreibeErgebniswert;
Schon hab ich einen CALL durch ein JMP ersetzt und ein RET eingespart und von der Sache her macht das Programm das gleiche.
zatzen hat geschrieben:Für meine Routine mit den Modifikationen habe ich jetzt eine passable Lösung, beim ersten Durchlauf wird ganz nach unten gesprungen, wo die Modifikationen stattfinden, dabei wird auch der Sprung oben umgewandelt in einen sinnvollen ersten Befehl der Routine, und unten steigt die Routine dann eben vor dem Modifikationspart mit RET aus. D.h. man muss die Routine einmalig aufrufen, damit sie sich verwandelt. Das Prinzip hast Du mir vorgemacht vor nicht langer Zeit, nur etwas komplizierter. Danke dafür!
Ja, ich erinnere mich: Das war bei dem Beispiel, wie man DS zurückkriegen kann.
zatzen hat geschrieben:Ich habe nur gerade gemerkt: Man muss vor dem Programmstart immer dran denken, das Ding neu zu kompilieren, sonst legt die Routine (weil sie noch modifiziert im Speicher liegt) einfach los und will Grafik anzeigen, aber man hat keine Parameter angegeben oder ist nicht noch nicht im Grafikmodus...
Naja, im Gegensatz zu Dir compiliere ich nach jeder Änderung sowieso neu.
zatzen hat geschrieben:Okay bessere Idee: Nach dem Modifizieren wird direkt wieder an den Anfang der Routine gesprungen, die diesmal modifiziert ist. So passiert die Modifikation einfach automatisch und einmalig beim ersten Aufruf der Routine, man muss sie nicht extra nur für den Modifikationszweck einmal aufrufen.
Genau. Dann braucht sie beim allerersten Aufruf eben ein paar Mikrosekunden länger für die Modifikationen, aber macht trotzdem, was sie machen soll.

So, dann ist das auch mal beantwortet.
Die letzten Einträge zu Deinem "Trackermodul-Engine-Thread" (den Du ja durch einen neuen ersetzt hast für die neue Engine) werde ich aber auch noch beantworten.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Fröhlichen XMS gehabt zu haben!

Mittlerweile habe ich rausgefunden dass heutige 64 Bit CPUs wohl durchaus immer noch 16 Bit Code und Real Mode ausführen können. Nur mit BIOS-Funktionen kann es ein Problem geben, und ansonsten sind die Betriebssysteme wie Windows einfach daran schuld, dass man keinen 16 Bit Code in denen ausführen kann.
DOSferatu hat geschrieben:
zatzen hat geschrieben:In der Medienwelt läuft das ja auch so, aber da scheinen die Leute es ja gerne zu haben, mit der Zeit zu gehen.
"Mit der Zeit gehen" ist ein ähnliches Totschlag-Argument. Man stellt damit Leute, die sich für die Technik HINTER der Technik interessieren, als stumpfhirnige Höhlenmenschen dar. Indem man Leute, die bestimmte Dinge wollen / erhalten wollen, diskreditiert, erweckt man den Eindruck: "Dieser Bedarf muß nicht berücksichtigt werden, weil alle die das wollen, dumme Verlierer sind."
In Bezug auf Computer kann ich da nicht eine eindeutige Meinung finden. Während ich im Spielebereich nicht unbedingt das neueste brauche, begrüße ich dennoch im Video- oder Tonbereich die Kapazitäten die sich in den letzten 20 Jahren entwickelt haben. Ein "Aldi-PC" kann heute alles, wofür man in den 80ern noch schrankgroße spezielle Hardware brauchte. Gewissermaßen hatte das auch Vorteile, weil sich nur etablierte Studios solches Equipment leisten konnten und da dann auch nur Profis an den Knöpfen drehen durften.

DOSferatu hat geschrieben:Beeinflußbare Leute wollen lieber zur hippen Clique gehören, als dafür, daß sie sich für etwas MEHR interessieren als das, was "Medienwelt" meint, was man "zu wollen hat", als dumme Hinterwäldler hingestellt zu werden. Das ist also alles Ergebnis von Marketing. Den meisten Leuten kann man bekanntermaßen jeden Mist einreden - wird auch seit Jahrzehnten gemacht. So "Werbefachleute" studieren diesen Kram schließlich, als wäre es eine Wissenschaft. Es gibt also Myriaden von Leuten, die in einer Berufsgruppe arbeiten, die den ganzen Tag nichts anderes machen, als (leider sehr erfolgreich) daran zu arbeiten, Leute zu überzeugen, was sie gefälligst "wollen sollen".
Ja, das wirkt sich mitunter sozusagen schadhaft vor allem dort aus, wo die Leute keine Ahnung von den Dingen haben, weil sich dann nicht unbedingt das beste oder vernünftigste durchsetzt, sondern einfach die Produkte von dem Hersteller der am meisten in Marketing investiert. Öfters wird aber auch einfach nochmal zusätzlich das beworben, was sich auch einfach so bewährt hat.

DOSferatu hat geschrieben:Man könnte dem entgegenhalten, daß eine Schallplatte analog ist, d.h. JEDER denkbare Amplitudenwert möglich ist, bzw. nur noch davon anhängt, wieviele Atome breit die Spurrille einer Schallplatte maximal sein kann - während bei einer CD die Anzahl möglicher Amplitudenwerte auf eine diskrete Anzahl von 65536 begrenzt ist. Ich sage nicht, daß es nicht ausreicht oder mein Gehör so fein wäre, da noch "Treppen" zu hören. Ich sage nur, daß es auch Argumente für Schallplatte und gegen CD gibt.
Ein Argument wäre z.B., dass man der Schallplatte mit einer simplen mechanischen Anordnung wie einem Drehteller und einem Stück Papier schon krächzige Klänge entlocken kann, während man bei der CD komplizierte Elektronik braucht.
Über die Auflösung der Schallplatte habe ich in Jugendjahren auch mal philosophiert. Ob man vielleicht anhand der Atomgrößen die äquivalente digitale Bitbreite einer Schallplatte errechnen kann. Und, schön dass wir es nochmal ansprechen, jetzt komme ich zu folgendem: Ich bin zwar kein Chemiker, aber zur Annäherung könnte man davon ausgehen, dass ein PVC-Molekül in der Größenordnung von 10^(-9) Metern liegt. Entspricht 10^(-6) Millimetern. Bei den lautesten Schallplatten beträgt die Rillenauslenkung ca. 0,1 mm, dann haben wir dort eine Auflösung von 10^5 Molekülen, also hunderttausend. Das kommt dann in etwa mit 16-17 Bit hin. Demnach wäre die Frage die ich damals hatte beantwortet: Auf molekularer Ebene hat die Schallplatte etwa die gleiche Auflösung wie die CD. Mittlerweile weiss ich aber dass in der Praxis noch ein anderer wichtiger Faktor zu berücksichtigen ist: Der Rauschabstand. Die effektive Auflösung wird praktisch gesehen "unten" durch das Rauschen begrenzt, "oben" von Verzerrung. Von den 16 Bit wären also realistisch gesehen etwa 6 nur damit beschäftigt, Rauschen zu speichern (Schallplatten haben einen Rauschabstand von etwa 60 dB). Ähnliches (etwa 10 dB mehr) gilt für analoge Mastertapes, von denen eine Schallplatte hergestellt wird bzw. wurde. Die CD mit einem tatsächlich nutzbaren und auch nach der D/A Wandlung realisierbaren Rauschabstand von 96 dB (grobe Formel: Bits * 6) steckt sowohl professionelle Studiobänder als auch die Schallplatte in den Sack. Das bestreiten viele Analog-Freaks natürlich.
Ich habe nichts gegen die Schallplatte und ihren Charme, den charakteristischen Klang... Im Gegenteil. Ich bin ja auch damit aufgewachsen und war immer fasziniert. Aber wenn dann die CD vom Markt verschwindet weil behauptet wird, sie würde den Klang kompromittieren und die Schallplatte nicht... Dann ist das einfach Tatsachenverdrehung. Treppen gibt es nach der D/A Wandlung übrigens nicht mehr, die werden interpoliert bzw. laufen durch einen Tiefpass (der auch vor der Digitalisierung stattfindet), so dass man tatsächlich am Ende genau das gleiche analoge(!) Signal hat wie vor der Digitalisierung, gute Wandler vorausgesetzt. Eben nur 20 kHz bandbegrenzt. Aber die Schallplatte kann auch nicht wirklich mehr Höhen als die CD, eher im Gegenteil enstehen dort früher Verfälschungen, die der Analog-Fan aber als angenehm empfindet. Und natürlich, bei der Bearbeitung von Musik oder irgendwelchen Aufnahmen setzt man auch oft gezielt harmonische Verzerrung (meist im Hochtonbereich) ein, um dem Klang mehr Präsenz zu geben. Man kann also Dinge wie Verzerrung kontrolliert für kreative Zwecke nutzen, aber diese sollten nicht unkontrolliert beim Endmedium auftreten. Der Punkt ist also: Digitale Tonträger als Endmedium sind neutral, analoge nicht. Deswegen würde ich im Zweifelsfall meine Produktionen immer digital archivieren und nicht analog. Wer analog gewöhnt ist interpretiert allerdings oft das Fehlen von Verfälschungen als Verfälschung.
Die "Abtastrate" einer Schallplatte ist molekular gesehen natürlich tatsächlich nahezu "unendlich", wird aber stark begrenzt durch die Trägheit der Abtastnadel (bzw. schon des Schneidstichels bei der Herstellung) und dadurch, dass der Winkel der Rille nicht zu steil werden darf. Nicht vergessen darf man auch die "Schneidekennlinie" - grob gesagt wird der Bassbereich bei der Aufnahme um 20 dB abgesenkt, der Höhenbereich um 20 dB angehoben, das wird beim Abspielen wieder ausgeglichen. Dadurch müsste man genau genommen auch die "Bit-Auflösung" über die Frequenz anders werten. Das ist eine kleine Wissenschaft für sich und ich hatte gerade damit zu tun weil ich eine Schallplatte digitalisieren wollte aber mein Vorverstärker kaputt war, ich hab es dann als Notbehelf digital entzerrt...
DOSferatu hat geschrieben:Denn ich erwarte ja schließlich auch Verständnis dafür (ohne daß es jemand genauso machen muß!), daß mir für Sound oft 8-Bit-Mono 11-16 kHz Daten mehr als ausreichen und ich nicht der Meinung bin, alles in aufgeblasenem 16-Bit-Stereo 44 kHz haben zu müssen - wogegen andere Leute der Meinung sind, daß selbst 384 kHz, 32 Bit immer noch "gerade mal gut genug" sind, um irgendwelche Sounddaten zu speichern.
Das eine ist Geschmackssache / Genügsamkeit, das andere meiner Meinung nach eher sowas wie Aberglaube. Es gibt natürlich eine Sache wo man auch bei 44 kHz aufpassen muss, nämlich wenn man beim Bearbeiten von Musik bestimmte Effekte anwendet, die Obertöne erzeugen. Da können exorbitante Samplerates sinnvoll sein, um Spiegelfrequenzen zu vermeiden. Das lässt sich besser aber auch durch internes Oversampling und Filtern innerhalb der Effektalgorithmen vermeiden. Bei 32 Bit fahre ich mit, solange ich etwas bearbeite. Ich möchte ja am Ende wirkliche 16 Bit haben und nicht durch irgendwelche Bearbeitungen schon im Vorfeld grobe Rundungsfehler haben. 32 Bit als Endmedium halte ich für unsinnig.
DOSferatu hat geschrieben:(Das gleiche erlebe ich mit Grafik: Nein, 16777216 Farben (256 Phasen pro Rot/Grün/Blau-Phase) sind plötzlich ZU WENIG! - Nein, es müssen MINDESTENS 1024 Phasen (30-Bit) Farben sein! 16,7 Mio Farben reichen nicht, diese Leute können über 1 Milliarde verschiedener Farben wahrnehmen und wenn's weniger ist, sehen sie die Abstufungen zwischen den Farben... Daß die INDUSTRIE, die immer wieder was neues herstellen muß, um ihre Existenz zu rechtfertigen, mit solchem Kram kommt, ist mir klar und wundert mich nicht. Aber ich kenne leider Privatpersonen, die diesen Quatsch auch glauben...)
Ja, kann ich nachvollziehen, und im Zusammhang mit Grafik für DOS-Spiele erst recht. Ich muss aber auch sagen dass es wiederum für den Moment der Bearbeitung sinnvoll sein kann, wenn man mehr als 256 Helligkeitswerte hat, z.B. bei einem Dia-Scan, der mit einem sehr guten Gerät gemacht worden ist, aber viel zu dunkel ist und die Werte z.B. unterhalb 16 bleiben würden bei 8 Bit. Hatten wir glaub ich schonmal thematisiert. Ich habe beim Bearbeiten von Kontrast und dergleichen auch ein ungutes Gefühl wenn die Farbwerte nur 8 Bit haben. Während der Bearbeitung halte ich eine quasi beliebig hohe Auflösung für eigentlich durchaus sinnvoll. Aber als "gemastertes" Endprodukt, wo die Farbwerte optimal "ausgesteuert" sind, sind 8 Bit völlig ausreichend. Ähnlich wie beim CD-Endformat. Unsere Ohren sind offenbar nur etwas empfindlicher was Dynamik und Rauschen angeht als die Augen, bei 8 Bit Sound kann man Quantisierungsartefakte bzw. Rauschen hören. Ein Nachvollziehbares Beispiel wäre ein tiefer Sinuston, der im Verlauf noch tiefer wird, da kann man deutlich so ein "schmieriges Wirbeln" hören, das korreliert und auch seine Frequenz ändert.
DOSferatu hat geschrieben:VHS hatte den Vorteil, daß es total idiotensicher war, eine Aufnahme zu machen, zu löschen, zu überschreiben, usw.: Einfach Play/Record/FastForward/Rewind/Stop... simpel wie ein Kassettenrekorder! Kein Auskennen mit dem DVD-Format, kein "Finishen" notwendig, damit die abspielbar wird.
Ja, solche Medien haben den Vorteil dass man an jeder Stelle ein- und aussteigen kann mit der Aufnahme. Digitale Bandmedien wie Digital 8 haben das Prinzip ja sogar noch fortgeführt. Es hat wohl auch einfach mit dem Unterschied Band vs. Scheibe zu tun. Immerhin konnte man mit der MiniDisc ähnlich hantieren wie mit einer Kassette, allerdings musste der TOC immer upgedatet werden.
DOSferatu hat geschrieben:
zatzen hat geschrieben:Und kaum habe ich mich damit abgefunden, dass die neueren Computer keine Diskettenlaufwerke mehr haben, sind auch schon CD/DVD Laufwerke wieder aus der Mode.
Disketten: Selbes Ding. Einfach schreiben, löschen, überschreiben. So eine CD/DVD hingegen, die muß man brennen. Braucht ein spezielles Laufwerk UND spezielle Programme.
Es gab da ja noch diese Iomega Zip Disketten. Eigentlich nicht schlecht, aber war zu wenig verbreitet. Ich hatte das mal.
DOSferatu hat geschrieben:Die USB-Sticks sind wieder etwas "einfacher" als Wechselmedium: Die kann man endlich wieder wie Disketten/Festplatten beschreiben usw. Allerdings ist USB ein viel zu komplexes Format. Es wird zu viel Hardware und Software benötigt, um es irgendwo zu benutzen. Schon die offiziellen Specs vom ursprünglichsten USB sind ca. 1200 Seiten lang.
Möglicherweise hängt damit auch zusammen, dass die meisten Endgeräte nur FAT32 Sticks lesen können. Naja vielleicht nicht wirklich, aber immerhin scheint Hardware heute so billig und kompakt herstellbar zu sein dass man USB auch in den einfachsten und kleinsten Geräten implementieren kann. Es gab z.B. vor 10 Jahren schon solche Mini-MP3-Player als billiges Massenprodukt, insgesamt gerade mal vielleicht 4x5x1 cm groß, nur wenige Knöpfe drauf, keine Anzeige, aber per USB an den Computer anschliessbar um Dateien zu schreiben/löschen.
DOSferatu hat geschrieben:Gute Dinge / gute Ideen erkennt man immer daran, daß sie einfach sind.
Und: Bei allem (Hardware, Software, Programmiersprachen, andere Dinge) was man benutzen soll/will, sollte man immer daran denken, es wie bei HUMOR (bzw einem Witz) zu behandeln: Wenn man es erklären muß, ist es Mist.
An dieser Stelle fällt mir gerade ein, dass mir mittlerweile Assembler vom Prinzip her übersichtlicher vorkommt als Pascal. Einfach wegen den Instruktionen: Soetwas wie MOV ist kurz und macht direkt etwas, während ein BEGIN ... END nur eine Klammer ist und dafür länger. Klar, um eine Bedingung oder Schleife in Assembler zu machen muss man auch mehr Aufwand treiben. Aber ansonsten ermutigt Assembler zum von der Hochsprachenlehre verteufelten Programmierstil, dass man z.B. auch mal ein GOTO verwendet, während es ja eigentlich gilt, soetwas zu vermeiden und immer alles "elegant" durch Klammerungen zu lösen, die komplett durchlaufen werden müssen. Es ist einfach lehrreich zu verstehen wie so ein Programm kompiliert wirklich aussieht und tatsächlich nur ablaufen kann.
DOSferatu hat geschrieben:Naja, selbst das VB6 erzeugt, nach dem, was der so erzählt, nur so 'ne Art Bytecode, der dann interpretiert wird... MIR wär das zu blöde - aber muß jeder selbst wissen. Ich sag mal so: Mein GameSys2 erzeugt auch Bytecode, der dann von einer (ASM-)VM interpretiert wird - aber das Ding ist ja nicht für ganze Programme gedacht, sondern nur zum Steuern von Figuren. Ich käme nicht auf die Idee, damit auch Grafiken zu berechnen/erzeugen.
Mir ist in Windows oft untergekommen, dass Software, auch offizielle/professionelle, eine Visual Basic Runtime - DLL benötigt. Das fällt dann wohl unter den Begriff "Framework".
DOSferatu hat geschrieben:[$ vs. h]Mich hats immer genervt, daß ich "verschieden lange" Hexzahlen haben muß: Damit Hexzahlen, die mit A bis F beginnen, als Zahl erkannt werden, immer diese 0 davor:
45FCh - 5 Zeichen
0FC45h - 6 Zeichen
Das sieht einfach scheiße aus. (Dann muß man wohl ALLE Hexzahlen "nullen", wenn man die untereinander schreibt oder so...) Aber muß eben jeder selbst wissen.
Ja, es ist sicher auch ein Stück Gewöhnung. $ hat schon was für sich, in der Tat. Allerdings kanm man im ASM Teil auch ...b verwenden für binäre Zahlen. Da passt dann h für Hex besser dazu...
DOSferatu hat geschrieben:
zatzen hat geschrieben:
DOSferatu hat geschrieben:Und in C kann man keine Prozedures/Functions ineinander verschachteln. Das ist total arm!
Ich hab das bisher in Pascal auch nur sehr selten gemacht, ich dachte dabei immer das gäbe zu viel Stack-Beanspruchung.
Eigentlich ist das Gegenteil der Fall. Wenn man eine Procedure in eine andere schreibt, kann man die Variablen der "oberen" Procedure in der unteren mitverwenden [...]
D.h. eigentlich braucht man so "Procedures in Procedures" (man kann auch Functions und Procedures oder umgekehrt benutzen und auch mehrfach verschachteln) gar keine Werte übergeben, wenn sie sich aus der darüberliegenden Procedure ergeben.
Also hat es eigentlich nur Vorteile. Ich hab mich bisher nicht so damit auseinandergesetzt und meine nur, man sollte nicht unbedingt größere Arrays in Prozeduren definieren sondern wenn, dann lieber global.

Zur Packroutinen-Thematik: Ja, natürlich macht soetwas nur Sinn wenn die Entpackroutine und die gepackten Daten zusammen immer noch kleiner sind als die ungepackten Daten. Grundsätzlich denke ich aber immer erst dann ans Packen, wenn es sich um zu erwartende gepackte gesamte Datenmengen jenseits von z.B. 64 KB handelt. Ansonsten soetwas wie Patterndaten, die redundant und voller Nullen sind, und sich ohne zu großen Aufwand und mit wenig Code entpacken lassen.
DOSferatu hat geschrieben:
zatzen hat geschrieben:Übrigens an dieser Stelle nochmal das brenzlige Thema Hintergrundgrafik-Kompression bei der Blocksache:
Wieso ist das Thema "brenzlig"?
Weil Du Dich eindeutig und vehement dagegen ausgesprochen hattest, da ich ursprünglich mit dem Dingen auch die Performance verbessern wollte...
DOSferatu hat geschrieben:Ja, wie gesagt: Es ist immer ein guter Ansatz - gerade, wenn man im Realmode/VM86 und Heap (<640kB) codet, sich ein wenig zu überlegen, was man eigentlich braucht und was nicht - und selbstverständlich gibt es hier keine "allgemeingültige" Aussage, sondern hängt immer davon ab, was für eine Art Programm/Spiel man machen will. Und daß man Grafikdaten bei fixen Hintergründen sinnvollerweise komplett anders anordnen/speichern würde als z.B. bei scrollenden Hintergründen, steht ja wohl außer Frage.
Ja, für ein Spiel mit der Gemütlichkeit eines Adventures (wenig bewegte Elemente und ca. 10 fps) wäre Kompression des Hintergrunds wohl gar keine so schlechte Idee, wenn es denn irgendeinen Grund dafür gibt, dass man es braucht. Ich dachte allerdings eher daran, dass die Hintergründe dann auch eher minimalistisch sein sollten, so würde dann ein kompletter 320x200 Screen in vielleicht 10 KB passen, und man könnte vielleicht 16 davon im Speicher halten für ein komplettes Level. Naja, es sind erstmal noch Hirngespinste. Solange ich keine Spielidee habe werde ich auf so einem Gebiet wohl erstmal nicht konkreter kreativ.
DOSferatu hat geschrieben:["Remaster"]Geht mir genauso. Vieles so "nachträglich aufpolierte" Zeug sieht im Nachhinein nicht besser aus, sondern nur irgendwie "künstlicher" und verliert dadurch etwas an Reiz. Das ist teilweise auch bei digital remasterten und nachberechneten Filmen so: Manche der ("analogen") Effekte wurden damals gemacht mit dem Wissen, daß Nebel und Auflösung verbergen würden, wie es wirklich gemacht wurde und damit dem Effekt seine Wirkung verliehen hat. Werden solche Filme nachträglich digital "nachgebessert", wird das, was (zur Realisierung des Effekts) eigentlich verborgen bleiben sollte, wieder klarer gemacht und die alten Effekte wirken billig und albern. Muß man nicht wirklich haben - verstehen aber auch sehr viele Leute leider nicht.
Mir ist schonmal aufgefallen, dass CGI im Kino glaubwürdiger rüberkommt. Wahrscheinlich weil es sozusagen durch das Nadelöhr von 24 Bildern pro Sekunde und der Projektion und der dabei entstehenden Unschärfe läuft, oder je nachdem, auf Zelluloid ein wenig Klarheit verliert. Der Trend geht wohl hin zu 4K digital und 48 Bildern/sek., aber so genau interessiert mich das Filmbusiness nicht.
DOSferatu hat geschrieben:Naja, ich gehe davon aus, daß unter anderem auch DAS (dieses 1-Richtung-Scrolling) bei Super Mario dazu beigetragen hat, diesen intern sehr effizient gespeicherten Levelaufbau zu schaffen, der es ermöglicht hat, in dem sehr kleinen Speicher so wahnsinnig viel Inhalt unterzubringen. Wie der Macher da die Levels gespeichert hat, ist 'ne Wissenschaft für sich - das ist nicht einfach ungepackt angeordnetes Blockraster.
Man könnte als einfachstes annehmen, seriell, ein Zähler wieviele Einheiten pro nächster X-Einheit kommen, dann jeweils die Y-Koordinate dazu und der Blocktyp. Aber das wäre wohl noch zu ineffizient.
DOSferatu hat geschrieben:[Textmode und 9 Spalten]Eigentlich dachte ich, dieses Wissen über den Textmode und so weiter wäre inzwischen Grundwissen von so DOS-Freaks. Also, ja: Datentechnisch sind es 640x400 - aber die Grafikkarte stellt 720x400 Pixel dar und der 9. Pixel wird von der Grafikkarte selbst generiert (siehe oben). Diese Möglichkeit der 720er Breite ist auch der Grund, wieso der Grafikmodus 360x200 bzw. 360x240 noch zu den "normalen" zählt (die auf jeder VGA funktionieren). Weil das Timing für die 720er sowieso schon eingebaut ist.
Sehr interessante Hintergrundinformationen. Naja, da sieht man wohl, ich bin einfach kein Dos-"Freak". Mein Wissen beschränkt sich immer nur auf das, womit ich mich schon auseinandergesetzt habe weil ich etwas bestimmtes programmieren wollte.
DOSferatu hat geschrieben:Wegen seiner komischen Anordnung hat ModeXY die Eigenschaft, auch mehrere gleichfarbige Pixel gleichzeitig (d.h. mit einem einzigen Zugriff) setzen zu können, weil man bis zu 4 nebeneinanderliegende Pixel "chainen" kann. Das gilt aber nur für den Zugriff, d.h. Darstellung ist trotzdem einzeln. D.h. man kann das Chaining auch immer an- und ausschalten und z.B. für eine Grafik das Chaining anschalten, damit man nur halb so viele Zugriffe braucht und für Text wieder aus, damit man die Pixel einzeln ansprechen kann.
Dazu fällt mir ein, dass ich neulich mal drauf geachtet habe, beim Protracker 3.61 auf dem Amiga, da werden irgendwie auch mehrere verschiedene Auflösungen verwendet, und ich meine damit nicht nur die ganz Große Schrift der Patterndarstellung. Aber das ist wohl ein ganz anderes Terrain als Dos. Vielleicht liegt einfach eine komische hohe Auflösung zu Grunde wie 640x240, und teilweise werden Pixel gedoppelt.

GetMem bzw. Pointer Offset nullen: Okay, danke, jetzt habe ich es begriffen. Irgendwie hatte ich eine Denkblockade.

DOSferatu hat geschrieben:[Nutzung vom Register SP]Wenn man also wirklich SP anderweitig nutzen will, muß JEDER Interrupt abgeschaltet (gesperrt) werden und dürfen zwischendurch weder Unterprogrammaufrufe (CALL), noch PUSH/POP/PUSHF/POPF Befehle erfolgen. Und ja, das geht - aber hier überwiegen eindeutig die Nachteile[...]
Ja, leuchtet ein... Keine gute Idee...
DOSferatu hat geschrieben:[Pascal ASM kennt nur short bedingte Jumps]Ja, das stimmt. Eine Umsetzung durch Parser in teilweisen (inline) 386er-Code kann gar nicht schnell genug kommen, oder? Ja, wie gesagt: Muß man sehen. Ich wollte es ja immer mal machen, aber irgendwie murks ich hier (falls ich überhaupt mal code) mit meinem eigenen Kram rum.
Ich habe mich zwar langsam mehr oder weniger dran gewöhnt, parallel einen Assembler/Disassembler laufen zu haben aus dem ich dann die Bytecodes abtippe, aber das wäre natürlich eine tolle Sache. Mich würde interessieren, wie Du gedacht hast, das konkret zu realisieren. Müsste man die aktuelle Programmdatei in Pascal schliessen, dann Dein Programm drüberlaufen lassen (das vielleicht vorher ein Backup erstellt) und dann wieder öffnen? An dieser Stelle fällt mir noch ein, ich habe gesehen dass man bei DosBox ein Debug-Fenster öffnen kann, welches u.a. den Inhalt von Registern anzeigt. Das könnte allgemein hilfreich werden. Ich sehe, Pascal kann das auch, aber nur 16 Bit Register logischerweise. Immerhin kann man das Programm in Einzelschritten durchgehen. Wieder was gelernt. Mein Halbwissen entwickelt sich...


Vielen Dank für Deine Erklärungen zum Thema BP und Segment Override, und zum Thema CALL / RET FAR bzw. NEAR. Werde ich mir alles mal wie ein Nachschlagewerk zurechtlegen.

DOSferatu hat geschrieben:

Code: Alles auswählen

push CS:offset @dahinter;jc @routine;add sp,2;@dahinter:
Das geht schon stark in die Richtung was mir beim ZSM-Player (und beim MOD-kompatiblen Nachfolger) bei der 4 Bit Delta-Routine helfen könnte. Bei Schleifen muss ich immer prüfen ob das Sample-Ende erreicht wurde. Vielleicht fällt mir noch was besseres ein, aber bisher habe ich:

Code: Alles auswählen

  @delta7even: inc si; jz @reset7; @reset7ret:
    db 64h; dd 09c8ah; @d7add: { db 64h; mov bl, [si+xxxx] }
    dd 01b8c8b67h; dw offset deltatab; dw 0
    { = mov cx, ds:[ebx + ebx + offset deltatab] }
    add dh, ch
  @delta6odd: add dh, cl
  @delta5even: inc si; jz @reset5; @reset5ret: db 64h; dd 09c8ah; @d5add:
    dd 01b8c8b67h; dw offset deltatab; dw 0; add dh, ch
  @delta4odd: add dh, cl
{...}
SI habe ich modifiziert dass es am Sample-Ende nicht der Datenlänge entspricht, sondern null, und für die Indizierung muss dann ein entsprechender Wert abgezogen werden (wird bei den @d?add's reinmodifiziert).
Die Code an den Sprungzielen @reset? setzt SI auf seinen Startwert zurück und setzt die Phase in DH auf den Loopstart-Wert, dann wird zu reset?ret zurückgesprungen. Es wird verhältnismäßig selten angesprungen, Geschwindigkeit ist da eher nebensächlich. Es enthält jeweils nur ein paar Befehle, muss aber mehrfach existieren wegen dem Rücksprung. Ich hatte schon überlegt stattdessen einen CALL zu verwenden, aber dann müsste ich jedes mal drüberspringen. Deine Methode hätte so wie Du sie beschrieben hast für meine Anwendung hier auch noch einen Haken: VOR dem Sprung muss für den Rücksprung gepusht werden.

DOSferatu hat geschrieben:Die letzten Einträge zu Deinem "Trackermodul-Engine-Thread" (den Du ja durch einen neuen ersetzt hast für die neue Engine) werde ich aber auch noch beantworten.
Das Ding ist nicht unbedingt tot. Ich werde wohl vorerst noch eine 1.0 Version vom ZSM-Player posten, mit den Optimierungen drin die ich zuletzt gemacht habe. Weitere Details dazu kann ich dann im Thread davon erläutern. Eins kann ich vorab sagen: Ich habe mehr Zwischenstufen für die Wahl der Mischfrequenz eingebaut, und sie geht runter bis 8 kHz, also auch für Lo-Fi Fans das richtige dabei.
Aber ich habe eben gemerkt dass ich mittlerweile mehr Durchblick habe beim Programmieren, und das ermutigt mich, meine selbstauferlegten Beschränkungen abzulegen.
Und solange ich keine faszinierende Idee für ein Spiel habe ist es für mich sinnvoller, meine Zeit erstmal in soetwas wie einen Player zu stecken, der 100% kompatibel zu einem Format ist, das sich in riesiger Zahl als gewisser Standard etabliert hat. Ich tu einfach mal so als gäbe es nicht schon tausende Player für alle denkbaren Systeme, und Freaks die so einen Player in C++ innerhalb einer Stunde runterschreiben. Der Weg ist das Ziel und das Lernen macht Spaß. Den Protracker-Standard möglichst 100% zu unterstützen ist schon eine Herausforderung, für mich jedenfalls zum jetzigen Zeitpunkt. Wie sinnvoll oder kreativ das ganze ist, "nur" einen weiteren von hunderten zu schreiben... Sagen wir mal so, ZSM ist auf eine Weise noch weniger kreativ, nämlich wie MOD aber effektmäßig total abgespeckt. Eine ähnliche, teils sogar effektivere Patternkompression (da Redundanzen ausnutzend) sowie die üblichen 4 Bit Delta Samples (aber diesmal optional auch 8 Bit parallel dabei) wird das neue Format auch haben und hat somit die Vorteile von ZSM ohne die ganzen Abstriche. Konversionsausgangpunkt wäre diesmal dasd MOD-Format, dazu würde ich mich bei eigener Musik dann z.B. dem OpenMPT bedienen um MOD-Konforme Module zu machen, oder ein DMF ein MOD umwandeln, und dann eben wieder ein "mein" neues Format.
Ich hatte schonmal mit der Dekodier-Routine für die Patterns begonnen, dabei wollte ich ein paar MULs umgehen - letztlich habe ich das durch LEA Operationen gelöst die ich modifizierend in den Code kopiere, je nachdem welche feste Größe in dem jeweiligen Modul als Faktor benötigt wird an einer Stelle die mehrfach durchlaufen wird. Ist vielleicht jetzt nicht ganz klar beschrieben, jedenfalls ist es toll was man auf Maschinen-Ebene so machen kann. Ähnlich könnte ich das auch mit der Berechnung der Lautstärke machen. Eine 32,5 KB Tabelle (65 Stufen * 256 WORDs) ist mir zu groß, aber ein Array an LEA Befehlen (und evtl Shifts etc.) wäre denkbar, vielleicht 65 * 16 Bytes, also gut 1 KB.

Was immer noch bleibt ist das Problem dass ich mit dem Soundpuffer time. Das ist nicht nur deswegen schlecht weil Leistungspotential verschenkt wird, sondern ganz einfach auch deshalb, weil man dadurch keine Framerates von z.B. 70 fps realisieren kann, selbst wenn die Grafikausgabe so simpel ist dass das eigentlich überhaupt kein Problem wäre. Ich kapiere trotz aller Deiner bisherigen Erklärungen nicht, wie ich Bild und Ton unabhängig voneinander laufen lasse. Vielleicht liegt es an der Weise wie ich den Soundpuffer fülle: Das ist so angelegt, dass dafür praktisch eine übergeordnete bzw. zusammenfassende Routine ausgeführt wird solange, bis der Puffer komplett gefüllt ist. In dieser Zeit kann die CPU nichts anderes machen. Das würde bei einer unabhängigen Bildframerate dazu führen, dass es ständig hakelt, da die Pufferfüllung ja einige Zeit beansprucht und somit immer längere Unterbrechungen entstehen. Ich könnte mir höchstens soetwas vorstellen, dass die Grafikberechnung und -darstellung per Interrupt reinkommt. Aber so ganz ideal ist das wohl auch nicht. Wie machst Du das denn bei ISM? Deine 486 POWER - Demo hat ja bewiesen, dass sich ISM und hohe (bzw. variable) Framerate trotz größerem Soundpuffer realisieren lassen. Ich suche mir nochmal Deine Erklärung zu dem Thema im Forum raus, aber grundsätzlich bleibt mein Verständnisproblem bestehen, wie es sein kann, dass die Grafik flüssig läuft, obwohl der Soundpuffer zwischendrin immer punktuell komplett gefüllt wird. Diese Sache ist im Zusammenhang mit meiner neuen Sound-Engine von Bedeutung, schliesslich soll sie letztlich universell einsetzbar sein und nicht nur dann, wenn man das Timing per Sound-Interrupt realisiert.
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: Eigenes Videoformat

Beitrag von zatzen »

Noch etwas zu der Sache mit dem Soundpuffer der "im Hintergrund" gefüllt wird: Bei 486POWER fällt mir auf, dass es zwar insgesamt flüssig läuft, aber zumindest bei größerem Puffer und hoher Samplingrate in regelmäßigen Zeitabständen etwas hakelt (Parameter 8 44 - bei 16 44 gibts die Dumbass Meldung bei 30000 Dosbox Cycles). Liegt da vielleicht der Hund begraben? Dass es also einfach so ist, dass die Soundpuffer-Füllroutine eben zeitweise das Programm komplett anhält?

Guten Rutsch!
mov ax, 13h
int 10h

while vorne_frei do vor;
Antworten