ZX81 Speed optimieren

ZX-Team Forum
Antworten
Benutzeravatar
PokeMon
User
Beiträge: 4706
Registriert: 31.08.2011, 23:41

ZX81 Speed optimieren

Beitrag von PokeMon » 25.05.2015, 19:39

Ich mache mal einen eigenen Thread für dieses Thema auf.
Generell bringt die meiste Änderung die "WHY WAIT?" Modifikation von WIlf Rigter, die hier gut beschrieben ist und für den eigenen Zeddy leicht nachbaubar ist. Damit wird die WAIT Schaltung (die nur für die Synchronisation der ersten Bildzeile notwendig ist) nicht mehr bei jedem NMI ausgeführt sondern nur noch beim letzten NMI. Dieser zeichnet sich dadurch aus, dass es der einzige NMI während eines HALT Zustands der CPU ist.

http://www.user.dccnet.com/wrigter/inde ... 81WAIT.htm

Wer noch ein kleines Quentchen mehr rauspressen möchte, ich habe einen Weg gefunden mittels einer kleinen Änderung der NMI Routine (die ggf. gepatcht werden kann, siehe dazu die letzten Kapitel im ZX-IDE Tutorial Thread) nochmals 2% Geschwindigkeit resp. 3 Taktzyklen rausholen kann. Und das ohne mehr Platzbedarf im ROM. Offenbar eine Sache, die Sinclair damals übersehen hat. 8)

Schauen wir mal kurz ins ROM:
http://www.wearmouth.demon.co.uk/zx81.htm

und genauer die NMI Routine an:

Code: Alles auswählen

;; NMI
L0066:  EX      AF,AF'          ; (4) switch in the NMI's copy of the 
        INC     A               ; (4) increment.
        JP      M,L006D         ; (10/10) jump, if minus, to NMI-RET as this is
        JR      Z,L006F         ; (12) forward to NMI-CONT
;; NMI-RET
L006D:  EX      AF,AF'          ; (4)  switch out the incremented line counter
        RET                     ; (10) return to User application for a while.
;; NMI-CONT
L006F:  EX      AF,AF'          ; (4) restore the main accumulator.
        PUSH    AF              ; (11) *             Save Main Registers
        PUSH    BC              ; (11) **
        PUSH    DE              ; (11) ***
        PUSH    HL              ; (11) ****
        LD      HL,($400C)      ; (16) fetch start of Display File from D_FILE
        SET     7,H             ; (8) point to upper 32K 'echo display file'
        HALT                    ; (1) HALT synchronizes with NMI.  
        OUT     ($FD),A         ; (11) Stop the NMI generator.
        JP      (IX)            ; (8) forward to L0281 (after top) or L028F
Dies ist die Standard NMI Routine des ZX81. Im ersten Teil wird nur das AF`Register runtergezählt und bei "0" der zweite Teil der Routine ausgeführt, die die eigentliche Synchronisation durchführt mittels des HALT Befehls und anschließend entweder die Videodarstellung anspringt (sichtbarer Teil) oder den vertikalen Synchronimpuls erzeugt und dabei die Tastatur abfragt und den FRAME Counter erhöht.

Die NMI Routine wird zweimal ausgeführt, einmal für die oberen "Leerzeilen" überhalb des sichtbaren Bildes und einmal für den unteren Bereich (unterhalb des sichtbaren Bildes). Die NMI Routine wird je 56 mal ausgeführt bei PAL Norm (NTSC = 32) wobei das letzte Mal kein User Programm mehr ausgeführt wird sondern der Rechner im Wesentlichen auf den letzten NMI Puls wartet mit HALT, was der Synchronisation für die erste Zeile dient. Im Grunde genommen könnte man sich das sogar für den unteren Teil noch sparen, ist aber hardwareseitig etwas aufwändiger zu realisieren.

So ein Bild besteht im Grunde aus 312,5 Zeilen im Zeilensprungverfahren (bei 50 Halbbildern). Der ZX81 erzeugt jedoch nur 312 Zeilen, was in einer etwas flotteren Bildwiederholfrequenz (50.3 Hz statt 50 Hz) resultiert. Das liegt auch daran, dass die Horizontalfrequenz etwas höher ist und eine Bildzeile nur 63.7us statt 64us lang ist. Fernseher (insbesondere die alte Generation) waren da sehr tolerant was die Signale betrifft. Moderne Geräte sind da schon mal etwas pingelig.

Berechnungsformeln:
Taktfrequenz=3.25 MHz
Bildzeile=207 Clocktakte=307.7ns * 207=63.7us
Frame (Halbbild)=63.7us * 312=19.87 ms
Bildwiederholfrequenz=1/19.87ms=50.32Hz

Rechnen (oder Programme ausführen) macht die CPU nur in den 56 oberen und unteren (nicht sichtbaren) "Leerzeilen", also insgesamt 112. Davon zählen die beiden Zeilen für die Synchronisation jedoch nicht mit, also nur 110 Zeilen. Jetzt beträgt die Rechenzeit jedoch nicht wirklich 207 Takte pro Zeile sondern die Bearbeitung des NMI muss man hier noch abziehen. Im Grunde führt die CPU hier einen RST Befehl ähnlich RST $10 aus mit 11 Takten um zur NMI Routine zu gelangen, die Adresse ist dabei fest einprogrammiert auf $0066. Die Routine selbst dauert jedoch auch 32 Takte. Die Unterbrechung des Programms ist dabei pro NMI stolze 43 Takte, so dass von den 207 Takten nur 164 Takte für Programme übrig bleiben.

So die maximale Rechenzeit beträgt daher 50.3 Frames * 110 NMI Zeilen * 164 Takte = 907.412 Takte pro Sekunde, was in etwa einer Taktfrequenz von 0.91 MHz entspricht (der Rest ist Displayanzeige und Keyboardabfrage). Das gilt aber nur für den Fall, dass man die obige WAIT Schaltung verwendet also kein WAIT in einem normalen NMI stattfindet. Hierbei führt der ZX81 die NMI Routine erst mit Beendigung des NMI Pulses aus und wartet vorher. Wieviele Takte dabei verloren gehen, kann man nur grob schätzen weil der NMI nicht immer sofort ausgeführt wird sondern nur nach Beendigung eines laufenden Befehls, der wiederum zwischen 4 und 23 Takte lang sein kann. Der Puls ist etwa 5.5us lang, was 18 Takte entspricht. Geht man davon aus, dass hier nochmal im Durchschnitt etwa 16 Takte verloren gehen, kommt man nur auf 148 Takte resp. 818.000 Takte oder 0.82 MHz effektive Taktfrequenz.


So, ich nehme es mal vorweg, mit folgender Änderung der NMI Routine kann man weitere 3 Takte herausholen, was rechnerisch in etwa 2% Geschwindigkeitszuwachs betrifft:

Code: Alles auswählen

nmi_int:
        EX      AF,AF'
        INC     A
        JR      Z,.endnmi       // end nmi mode

.cont:  EX      AF,AF'          // return to user program
        RET

.endnmi:
        EX      AF,AF'
        PUSH    AF              // prepare stack for video routine
        PUSH    BC
        PUSH    DE
        PUSH    HL
        LD      HL,(DISPDATA)   // load start address of display
        SET     7,H
        HALT                    // sync display
        OUT     (IOADRNMIOFF),A // stop NMI finally
        JP      .final
.final:     JP      (IX)            // and go for main display or VSYNC
Wir lassen den JP M,L006D einfach mal weg, weil er programmtechnisch nicht notwendig ist und nur Timingzwecken dient. So wird weiterhin bei AF' Werten ungleich Null die reine Zählfunktion ausgeübt und bei AF'=0 die Sync Routine. In dieser Sync Routine wird ein weiteres Mal die NMI Routine ausgeführt und hier fehlen uns jetzt die zusätzlichen Takte aus dem JP M,xxxx Befehl, der nicht mehr ausgeführt wird aber 10 Takte verbrät. Diese werden nach dem HALT Befehl durch den eher unsinnig anmutenden JP .final wieder eingefügt, so dass das Timing hier wieder stimmt. Da der Befehl sich nur verschiebt, wird auch nicht mehr Platz im ROM benötigt, er ist einfach nur an einer anderen Stelle.

Die Routine funktioniert weiterhin ordnungsgemäß, nur dass der JR Z,xxx Befehl ausgeführt wird, der jedoch wirkungslos ist und die Routine kommt so 3 Takte früher (7 statt 10 Takte) zum Schluss.

Also - Geschwindigkeitsfanatiker, Programmiergerät rausholen und los gehts. :mrgreen:
Anbei noch ein Beispiel, was die Programmändeurng bei meinem ZXmore gebracht hat.

Vorher:
IMG_8477k.JPG
IMG_8477k.JPG (151.06 KiB) 1664 mal betrachtet
Nachher:
IMG_8480k.JPG
IMG_8480k.JPG (162.91 KiB) 1664 mal betrachtet
PS: Das hier rechnerisch ein kleinerer Zuwachs als 2% ausgegeben wird, hängt mit dem Umstand der höheren Taktfrequenz des ZXmore zusammen (6.5 MHz statt 3.25 MHz).

Hier eine Gegenüberstellung der gemessenen Werte:
3.25 MHz / Standard NMI Routine: 1702 Frames vs. 1.863 (109.5% oder 0.88 MHz)
3.25 MHz / optimierte NMI Routine: 1566 Frames vs. 1.863 (119% oder 0.95 MHz)
6.50 MHz / Standard NMI Routine: 787 Frames vs. 1.863 (236.7% oder 1.89 MHz)
6.50 MHz / optimierte NMI Routine: 780 Frames vs. 1.863 (238.8% oder 1.91 MHz)

Die Berechnung kommt hier allein schon durch die fehlenden WAIT Zyklen (was ich jetzt nicht wirklich abschaltbar gemacht habe) durcheinander sowie mit der höheren Frequenz von 6.5MHz. Letztlich sind 3 Takte bei 6.5 MHz nur 1.5 Takte bei 3.25 MHz daher kann das Programm hier nur einen Unterschied von 1% ausweisen. Ist aber tatsächlich bei Standard Taktfrequenz mehr, siehe Gegenüberstellung. :wink:

WHY WAIT Umbau und ROM Änderung bringen hier in Summe 20% Geschwindigkeitszuwachs.
Noch schlimmer haben es allerdings unsere amerikanischen NTSC Freunde, hier beträgt die effektive Geschwindigkeit nur 59.9 Hz * 62 Bildzeilen * 148 Takte = 0.55 MHz effektive Taktfrequenz.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Antworten