Framerate für Spiel einstellen

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

Framerate für Spiel einstellen

Beitrag von zatzen »

Nachdem ich es eingesehen habe dass es sinnlos ist
ein Spiel anhand des Soundkartenpuffers zu timen,
das gäbe dann vielleicht sowas wie 20fps und wäre
dann sehr ruckelig, brauche ich Alternativen.
Lieb wäre mir dennoch eine feste Framerate, z.B. 50 fps
Vielleicht den Timer umlenken?
Hätte da noch jemand Empfehlungen?
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: Framerate für Spiel einstellen

Beitrag von DOSferatu »

Ja, ich habe eine: Lenk den Timer um.
wobo
DOS-Guru
Beiträge: 614
Registriert: So 17. Okt 2010, 14:40

Re: Framerate für Spiel einstellen

Beitrag von wobo »

zatzen hat geschrieben:Nachdem ich es eingesehen habe dass es sinnlos ist
ein Spiel anhand des Soundkartenpuffers zu timen,
das gäbe dann vielleicht sowas wie 20fps und wäre
dann sehr ruckelig, brauche ich Alternativen.
Lieb wäre mir dennoch eine feste Framerate, z.B. 50 fps
Vielleicht den Timer umlenken?
Hätte da noch jemand Empfehlungen?

Bei Mods (4chn) müßte es eigentlich gehen. Die Ticks werden ja im Standardfall (125 bpm) ja alle 0,02 sec aufgerufen, was 50 Hz entspricht.

Im Regelfall machen die meisten das Timing aber immer über den Timer. Daneben kann man noch das Timing an den Vertical Retrace der VGA orientieren, was im Regelfall je nach Videomode 60 oder70 Hz ergibt. Drittens kann man das Timing noch über Int $15 machen und sich nach einer gewählten Zeit an millisekunden ein Flag setzen lassen. Daneben gibt es bestimmt noch ein paar andere Timing-Methoden. Das Timing mittels Timer ist aber wohl das gängiste und flexibelste.
TomCat
MemMaker-Benutzer
Beiträge: 87
Registriert: Do 1. Dez 2011, 17:16

Re: Framerate für Spiel einstellen

Beitrag von TomCat »

Hi,

das beste ist du liest den Performance-Counter aus. Dann kannst du auch keine Bewegungen abhaengig der vergangenen Zeit berechnen. Das einzig blöde ist dass es den Performance-Counter erst seit dem ur-Pentium gibt. Welche CPU haste denn drin?

TomCat
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Framerate für Spiel einstellen

Beitrag von DOSferatu »

Würde ich auf keinen Fall empfehlen.
Erstens limitiert man das Spiel dann zu sehr auf die spezielle Hardware (beim PC kein No-Go) und macht sich vor allem davon abhängig.
Man will ja selbst bestimmen, wie schnell das Spiel läuft, bzw wieviele "Steps" pro Sekunde das Spiel macht - selbst wenn man weniger Framerate bei der Grafikausgabe hat.
Das Problem, was heutzutage leider oft vorkommt, ist, daß unterhalb von bestimmter Systemleistung einfach nur kackenfrech gesagt wird: Geht nicht - anstatt den Benutzer entscheiden zu lassen, ob er mit eingeschränkten Features trotzdem spielen will...
Aber OK, ich schweife ab.
wobo
DOS-Guru
Beiträge: 614
Registriert: So 17. Okt 2010, 14:40

Re: Framerate für Spiel einstellen

Beitrag von wobo »

TomCat hat geschrieben:Hi,

das beste ist du liest den Performance-Counter aus. Dann kannst du auch keine Bewegungen abhaengig der vergangenen Zeit berechnen. Das einzig blöde ist dass es den Performance-Counter erst seit dem ur-Pentium gibt. Welche CPU haste denn drin?

TomCat
Wie geht denn das mit dem Performance-Counter. Da ich nur auf 386sx und 486sx programmiere, hat das für mich keine praktische Relevanz. Aber ein kurzes source-Beispiel würde mich schon interessieren oder ein Link auf irgendeine Erklärung hierzu. Beim Googeln habe ich jetzt nämlich nichts gefunden.
freecrac
DOS-Guru
Beiträge: 861
Registriert: Mi 21. Apr 2010, 11:44
Wohnort: Hamburg Horn

Re: Framerate für Spiel einstellen

Beitrag von freecrac »

Damit der Cursorbalken in einem Auswahlmenü beim Drücken der Bewegungstasten und bei unterschiedlich schnellen CPUs nahezu in der gleichen Bewegungsgeschwindigkeit sich bewegt habe ich eine kleines Programm(SPEED.EXE) geschrieben, welches eine Schleife mit 64 NOP-Befehlen eine Sekunde lang durchläuft und danach die Anzahl der Durchläufe in eine Datei(SPEED.tic) schreibt und dessen Anzahl dann als Verzögerungsfaktor verwendet wird. So konnte ich es verhindern das sich der Cursorbalken ggf. zu schnell bewegen läßt und die Menüpunkte sich dann nicht mehr sinnvoll auswählen lassen. Die Verzögerung habe ich damals auf einem 80386DX mit 40mhz eingestellt und das Menü läßt sich auch noch mit meinen Core2quad mit 2800mhz benutzen.

Die erzeugte Datei mit dem Namen "SPEED.tic" ist 4 Byte gross(little endian) und kann von jedem Programm eingeladen und als goben Anhaltspunkt für die Ausführungsgeschwindigkeit verwendet werden.

Code: Alles auswählen

.386P
.387
.CODE
 org 100h
                Cpu86    =   0        ; Wert als Index in der Takt-Tabelle
                Cpu286   =   4
                Cpu386   =   8
                Cpu486   =  12
                Cpu586   =  16

START:    mov      ax, @DATA
          mov      ds, ax             ; DS auf Daten-Bereich
          call GETCPU                 ; Prozessor-Analyse:    AX=CPU
          call GETSPEED               ; Zählschleife
          call FILE                   ; create a new file
;-------------------------------------
          mov      ah, 4Ch            ; Dos-Rücksprung, Programm-Ende
          int    21h
;────────────────────────────────────────────────────────────────────────────
;                         U N T E R - R O U T I N E N
;────────────────────────────────────────────────────────────────────────────
 org START + ((($-START)/16)*16) + 16
;---------------------------------------------------------------------------
GETCPU:   mov      ax, Cpu86
          xor      bx, bx
          push     bx                 ; Null auf Stack
          popf                        ; Null in Flagregister
          pushf
          pop      bx                 ; zurück nach bx
          and      bh, 0F0h
          cmp      bh, 0F0h           ; wenn gleich, dann 8086
          je  short CPUOK
;-------------------------------------
          mov      ax, Cpu286
          push     7000h              ; dasselbe mit 7000h
          popf
          pushf
          pop      bx
          and      bh, 70h
          jz  short CPUOK
;-------------------------------------
          mov      ax, Cpu386
          mov      edx, esp
          and      esp, 0FFFCh        ; durch vier teilbare Adr.
          pushfd
          pop      ebx
          mov      ecx, ebx
          btc      ebx, 18            ; Bit 18 umdrehen
          push     ebx
          popfd
          pushfd
          pop      ebx
          push     ecx                ; alte Flaggen zurück
          popfd
          mov      esp, edx           ; Stack zurück
          cmp      ecx, ebx           ; wenn gleich dann 386
          jz  short CPUOK
;-------------------------------------
          mov      ax, Cpu486
          btc      ecx, 21
          push     ecx
          popfd
          pushfd
          pop      ebx
          cmp      ebx, ecx           ; wenn ungleich, dann 486
          jnz short CPUOK
;-------------------------------------
          mov      ax, Cpu586         ; sonst Pentium
;----------------------------------------------------------------------------
CPUOK:    mov      bp, ax
          ret                         ; AX=Cpu
;────────────────────────────────────────────────────────────────────────────
 org START + ((($-START)/16)*16) + 16
;---------------------------------------------------------------------------
GETSPEED: cli                         ; Interrupt's verbieten
          xor      ax, ax
          mov      es, ax             ; ES auf Seg.: 0
;-------------------------------------
          mov      di, es:[20h]       ; Vector von INT 8 retten (Offset)
          mov      si, es:[22h]       ;                         (Segment)
;-------------------------------------
          mov      es:[20h], OFFSET NEUVEC ; Interrupt-Vector von INT 8
          mov      es:[22h], cs            ; auf neue Routine legen
;-------------------------------------
          mov      al, 36h            ; RUNDE auf 18,2 Hertz (Standart)
          out      43h, al
          xor      al, al
          out      40h, al            ; low
          out      40h, al            ; high
;-------------------------------------
          cmp      bp, 8              ;  32-Bit-CPU ?
          jb  short M16
          jmp  M32
;---------------------------------------------------------------------------
 org START + ((($-START)/16)*16) + 16
;---------------------------------------------------------------------------
M16:      mov      bp, 1              ; Aktiv-Flag = IRQ-Ende wenn 0
          xor      cx, cx             ; Zähler low
          xor      dx, dx             ; Zähler high
          sti                         ; neuen Interrupt 8 starten
;-------------------------------------
S1:       and      bp, bp             ; Auf IRQ-Ende warten
          jnz S1
          mov      bp, 1              ; Für's Zählen IRQ Aktiv-Flag setzen
;-------------------------------------
S2:       inc      cx                 ; Hauptzählschleife
          jnz short S3
          inc      dx
;-------------------------------------
S3:       nop                         ; 64 Nop's
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
;-------------------------------------
          and      bp, bp             ; auf Timer-IRQ-Ende abfragen
          jnz S2                      ; weiter, wenn Timer-IRQ noch aktiv
;-------------------------------
          mov      RUNDE, cx          ; Zähler low retten
          mov      RUNDE+2, dx        ; Zähler high retten
;---------------------------------------------------------------------------
ANALYS:   cli                         ; Interrupts verbieten
          mov      es:[20h], di       ; alten Interrupt-Vector
          mov      es:[22h], si       ; von INT 8 wiederherstellen
          sti                         ; Interrupts erlauben
          ret
;────────────────────────────────────────────────────────────────────────────
 org START + ((($-START)/16)*16) + 16
;---------------------------------------------------------------------------
M32:      mov      bp, 1              ; Aktiv-Flag = IRQ-Ende wenn 0
          xor      ecx, ecx           ; Zähler
          sti                         ; neuen Interrupt 8 starten
;-------------------------------------
S4:       and      bp, bp             ; Auf IRQ-Ende warten
          jnz S4
;-------------------------------------
          mov      bp, 1              ; Für's Zählen IRQ Aktiv-Flag setzen
;-------------------------------------
S5:       inc      ecx                ; Hauptzählschleife
          nop                         ; 64 Nop's
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop
          nop

          nop
          nop
          nop
          nop
;-------------------------------------
          and      bp, bp             ; auf Timer-IRQ-Ende abfragen
          jnz S5                      ; weiter, wenn Timer-IRQ noch aktiv
;---------------------------------------------------------------------------
          mov     DWORD PTR[RUNDE], ecx ; Zähler retten
          jmp  ANALYS
;────────────────────────────────────────────────────────────────────────────
;               neue   I R Q - R o u t i n e   für  INT 8
;────────────────────────────────────────────────────────────────────────────
 org START + ((($-START)/16)*16) + 16
;---------------------------------------------------------------------------
NEUVEC:   mov      al, 20h            ; neue IRQ-Routine für INT 8
          dec      bp                 ; Aktiv-Flag löschen
          out      20h, al            ; =EOI
          iret
;────────────────────────────────────────────────────────────────────────────
 org START + ((($-START)/16)*16) + 16
;---------------------------------------------------------------------------
FILE:     mov      dx, OFFSET SPEEDNAM
          xor      cx, cx
          mov      ah, 3Ch              ; Datei erstellen
          int    21h
          mov      bx, ax
;---------------------------------------
          mov      dx, OFFSET RUNDE
          mov      cx, 4
          mov      ah, 40h              ; Datei beschreiben
          int    21h
;---------------------------------------
          mov      ah, 3Eh              ; Datei schließen
          int    21h
          ret
;────────────────────────────────────────────────────────────────────────────
;                          D A T E N - B E R E I C H
;────────────────────────────────────────────────────────────────────────────
 org START + ((($-START)/64)*64) + 64
;---------------------------------------------------------------------------
.DATA
 org 0
;---------------------------------------------------------------------------
RUNDE    DW 0, 0                        ; Anzahl der Schleifen-Durchläufe
SPEEDNAM DB "SPEED.tic", 0
;────────────────────────────────────────────────────────────────────────────
.STACK 20h
 end
http://www.alice-dsl.net/freecracmaps/Tool/MENU.ZIP

Dirk
TomCat
MemMaker-Benutzer
Beiträge: 87
Registriert: Do 1. Dez 2011, 17:16

Re: Framerate für Spiel einstellen

Beitrag von TomCat »

wobo hat geschrieben:
TomCat hat geschrieben:Hi,

das beste ist du liest den Performance-Counter aus. Dann kannst du auch keine Bewegungen abhaengig der vergangenen Zeit berechnen. Das einzig blöde ist dass es den Performance-Counter erst seit dem ur-Pentium gibt. Welche CPU haste denn drin?

TomCat
Wie geht denn das mit dem Performance-Counter. Da ich nur auf 386sx und 486sx programmiere, hat das für mich keine praktische Relevanz. Aber ein kurzes source-Beispiel würde mich schon interessieren oder ein Link auf irgendeine Erklärung hierzu. Beim Googeln habe ich jetzt nämlich nichts gefunden.

der ASM-Befehl lautet:
rdtsc

und liefert den Inhalt des Performance-Counters in EDX:EAX als 64-Bit Zahl sozusagen zurück. Er zählt mit der Taktfrequenz der CPU hoch. Allerdings wie ich schaon sagte gibts den erst seit dem Ur-Pentium und nicht bei 486er oder noch tiefer.
Also um auch in 286er 386 exakte zeiten zu messen und komfortabel benutzen zu koennen gibts ne andere sehr gute Methode.;-)
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Framerate für Spiel einstellen

Beitrag von DOSferatu »

Ich habe in Xpyderz irgendwann einen Workaround eingebaut. Normalerweise stelle ich ja den Ticker auf die gewünschte Frequenz und er tickt dann fröhlich vor sich hin. Problem ist, daß zB unter WinNT4 und Win2000 (also NT-Linie, betrifft aber NICHT WinXP, da haben sie es wohl gefixt) der Ticker nachträglich wieder irgendwie umgeändert... was weiß ich.
Daher messe ich die Durchläufe pro Sekunde, indem ich die BIOS-Uhr benutze. D.h. ich warte, bis die Sekunde umschaltet, dann zähle ich die Tickerdurchläufe, bis die nächste Sekunde umschaltet.
Und daraufhin passe ich den Tickerwert neu an, damit am Ende das richtige Ergebnis herauskommt.
Ich weiß, das ist totaler Voodoo, aber es funktioniert.
Und eigentlich hätte ich es nicht machen müssen, da Xpyderz ein DOS-Spiel ist und wenn es unter anderen OS nicht richtig funktioniert, ist es eigentlich nicht meine Schuld. Aber ich hatte es zufällig mal auf den besagten Windowsen getestet und es hat mich eben gestört, daß es nicht richtig funktionierte. Tschuldigung, daß ich so oft dieses dämliche Spiel erwähne, aber ich schätze eben, daß auch andere von meinen Erfahrungen profitieren können - so wie ich es von den Erfahrungen anderer ebenfalls tue.
freecrac
DOS-Guru
Beiträge: 861
Registriert: Mi 21. Apr 2010, 11:44
Wohnort: Hamburg Horn

Re: Framerate für Spiel einstellen

Beitrag von freecrac »

TomCat hat geschrieben:der ASM-Befehl lautet:
rdtsc

und liefert den Inhalt des Performance-Counters in EDX:EAX als 64-Bit Zahl sozusagen zurück. Er zählt mit der Taktfrequenz der CPU hoch. Allerdings wie ich schaon sagte gibts den erst seit dem Ur-Pentium und nicht bei 486er oder noch tiefer.
Also um auch in 286er 386 exakte zeiten zu messen und komfortabel benutzen zu koennen gibts ne andere sehr gute Methode.;-)
Dazu habe ich nun auch ein kleines Beispiel von Ralph Bauer gefunden:
http://dcla.rkhb.de/rdtsc.html

Weitere Hinweise:
http://en.wikipedia.org/wiki/Time_Stamp_Counter
Starting with the Pentium Pro, Intel processors have supported out-of-order execution, where instructions are not necessarily performed in the order they appear in the executable. This can cause RDTSC to be executed later than expected, producing a misleading cycle count.[3] This problem can be solved by executing a serializing instruction, such as CPUID, to force every preceding instruction to complete before allowing the program to continue, or by using the RDTSCP instruction, which is a serializing variant of the RDTSC instruction (starting from Core i7[4] and starting from AMD Athlon 64 X2 CPUs with AM2 Socket (Windsor & Brisbane)).
Neben dem TSC gibt es nun auch noch den Power Management Timer (PMTIMER) und den High Precision Event Timer(HPET) und auch (locale) Advanced Programmable Interrupt Controller(APIC).

Dirk
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Framerate für Spiel einstellen

Beitrag von DOSferatu »

Naja, wie gesagt - für so etwas Simples wie ein Spiel/Programm vernünftig zu timen würde ich nicht gleich einen Pentium als Mindestvoraussetzung ansetzen. Aber das ist natürlich nur, was ich machen bzw nicht machen würde - das wird natürlich jeder für sich selbst entscheiden.
Antworten