Funktionsweisen der Bildschirmdarstellungen beim ZX81

Ein Tutorial von Wilf Rigter
Stand 7. September 1996


INDEX

  1. Einführung
  2. Grundlagen
  3. Die SLOW-MODE Betriebsart
  4. Die FAST-MODE Betriebsart
  5. ZX81 Video Hardware
  6. ZX81 Zeichen Video Hardware
  7. Pseudo Hires Hardware
  8. Echtes Hires Hardware
  9. Zeitliche Abläufe der Zeichendarstellung beim ZX81
  10. Zeitliche Abläufe bei Pseudo Hires
  11. Zeitliche Abläufe bei echtem Hires
  12. Maschinencoderoutinen im SLOW-MODE
  13. Maschinencoderoutinen im FAST-MODE
  14. Maschinencoderoutinen bei echtem Hires
  15. Maschinencoderoutinen bei Pseudo Hires

1. Einführung

Als innovative technische Ideen kombiniert mit einem wirtschaftlichen Design und einer Möglichkeit am Markt gegeben waren, begannen einige interessante Dinge zu passieren. Im Jahre 1980 war "Sinclair" der Allgemeinheit noch kein gängiger Begriff und vielleicht eher für seine Digitaluhr und seinen Taschenrechner, denn für seinen ZX80 bekannt. Doch Sinclair entschied, daß die Zeit für eine erschwingliche, einfach zu benutzende und in Massen hergestellte Version des ZX80 gekommen sei, die auch mit Gleitkommaarithmetik und einer flackerfreien Anzeige ausgestattet sein sollte.

Der ZX81 wurde geboren und, wie man sagt, "der Rest ist Geschichte".

Eine Schlüsselstellung in dem preiswerten Entwurf des ZX81 nahm das Video-System ein. Es war nicht nur preiswert herzustellen, sondern es stellte sich heraus, daß es Möglichkeiten bot, die über die ursprünglichen Ziele der Entwickler hinausgingen.

2. Grundlagen der ZX81 Bildschirmdarstellung

Das Standard-Bildschirmbild besteht aus 24 Zeilen zu je 32 Zeichen. Jedes Zeichen hat eine Höhe von 8 Scan-Zeilen und eine Breite von 8 Pixeln. Die darzustellenden Zeichen befinden sich in einem Speicherbereich, der DFILE (display file) genannt wird. Der Zeichensatz von 128 darstellbaren Zeichen besteht aus 64 normalen Zeichen (weiss auf schwarz), die (ausschließlich!) Großbuchstaben, Ziffern, Sonderzeichen und Grafikzeichen beinhalten, und deren invertierten Gegenstücken (schwarz auf weiss). Die ZX81-Zeichencodes CHR$ 0-63, CHR$ 118 und CHR$ 128-191 entsprechen nicht dem ASCII-Standard. Eine Reihe von Bildschirmcodes wird zudem für Keywords, Funktionen und Befehle benutzt, doch diese werden immer vor dem Schreiben nach DFILE aus einzelnen darstellbaren Zeichen zusammengesetzt.

DFILE beginnt mit der Sinclair-Entsprechung eines Zeilenrücksprungs CR (CHR$ 118), gefolgt von bis zu 32 CHR$ Codes; dies wiederholt sich 24 mal und endet mit einem CHR$ 118. CHR$ 118 ist der Op-Code für den HALT-Befehl der Z80-CPU aus Gründen, die später erläutert werden.

Alle anderen Zeichencodes sind unzulässig und verursachen generell einen Systemabsturz, wenn sie nach DFILE geladne werden. Das kleine DFILE wird bei ZX81-Systemen verwendet, die mit nur 1 oder 2 KB RAM ausgestattet sind, um den Platzbedarf des Bildschirmspeichers zu minimieren. Bei einem leerem Schirm besteht DFILE lediglich aus 25 CHR$ 118 Codes. Jede Zeile wird erweitert, wenn in diese Zeile Zeichen geschrieben werden. Bei einer Ausstattung von 4 KB RAM und mehr wird DFILE mit dem vollständig ausgeklappten Format von 24 Zeilen zu je 32 Zeichen mit CHR$ 00 (space) und 25 CHR$ 118 Zeilenabschlußzeichen initialisiert.

Die Bildschirmzeichen gelangen nicht direkt zur Darstellung, sondern dienen vielmehr als Adreßzeiger auf eine Zeichengeneratortabelle im ROM. Diese Tabelle wird adressiert mittels einer Kombination des in DFILE gespeicherten Codes und der ZX81-Hardware. Das darin enthaltene Byte wird in das Video-Schieberegister geladen. Bit 7 des Zeichen-Codes wird von der Video-Hardware dazu benutzt, die Pixel ggfs. zu invertieren, während sie aus dem Schieberegister geschoben werden. Die Darstellung auf dem Bildschirm wird durch einen seriellen Datenstrom von Pixeln des Videoschieberegisters erzeugt, der den Elektronenstrahl der Bildröhre des Fernsehers an- und ausschaltet während er die fluoreszierende Schicht im Innern der Bildröhre abtastet.

Ein vollständig ausgeklapptes DFILE mit 24 Zeilen zu je 32 Zeichen pro Zeile und 8 Bytes Zeichengeneratordaten pro Zeichen besteht aus 6144 Bytes oder 49152 Pixel pro Schirm.

3. Die SLOW-MODE Betriebsart

Bei der SLOW-MODE Betriebsart wechselt die CPU ständig zwischen dem Bilderzeugungs- und dem Anwenderprogramm. Rund 80% der CPU-Zeit wird für Bilderzeugung und Tastaturabfrage verwandt und lediglich rund 20% der CPU-Zeit stehen für die Ausführung des Anwenderprogramms zur Verfügung.

Tatsächlich wird die CPU-Zeit pro Fernsehhalbbild in vier Task-Blöcke geteilt, wie dies in Tabelle 1 dargestellt ist. Zwischen den einzelnen Tasks wird durch einen NMI-Generator umgeschaltet (NMI=non maskable interupt, nicht sperrbarer Interrupt). Die dabei ausgeführte NMI-Behandlungsroutine steuert den Taskwechsel zwischen dem asynchronem Anwendungsprogramm zu den Echtzeit-Bilderzeugungsroutinen.

1. VSYNC, Zählen der Halbbilder und TastaturabfrageNMI aus
2. Leere Zeilen / AnwenderprogrammNMI an
3. VIDEO DISPLAY RoutineNMI aus
4. Leere Zeilen / AnwenderprogrammNMI an
Tabelle 1: SLOW-MODE CPU-Task Tabelle

Jeder Task kann im Einzelnen wie folgt beschrieben werden:

  1. Während des Zeilensprungs (VSYNC aktiv) bei dem keine Daten zur Darstellung gelangen, führt die CPU eine VSYNC-Routine aus, die eine definierte Zeit zur Ausführung benötigt. Hierbei wird der Halbbildzähler erhöht und acht Zeilen der Tastaturmatrix zusammen mit dem 50/60Hz-Betriebsartsbit gelesen. Der ULA-Tastaturport wird bei jedem I/O-Lesezugriff adressiert, bei dem A0 "low" ist (z.B. $FE). Hierbei wird auch der 3-bit-Zeilenzähler (LCNCTR) der ULA zurückgesetzt. Nachdem sämtliche Tastaturdaten verarbeitet wurden (400uS später), führt die CPU ein OUT FF, A aus (jeder OUT-Befehl würde dasselbe bewirken), der den Video-Ausgang der ULA wieder zum Normalzustand (weißer Hintergrund mit horizontalen SYNC-Pulsen) zurücksetzt und den LCNTR-Resetzt verhindert.

    Am Ende der VSYNC-Routine wird die Anzahl leerer Zeilen bis zum Beginn des Ausgabebereichs des nächsten Halbbildes mittels der Systemvariablen MARGIN (50/60Hz) bestimmt. Anschließend wird der NMI-Generator eingeschaltet und die CPU-Registerbank auf den Registersatz des Anwendungsprogramms geschaltet.

  2. Wenn die CPU das Anwendungsprogramm ausführt, wird sie hierbei alle 64uS durch den NMI-Generator unterbrochen, wobei gleichzeitig die ULA einen HSYNC-Puls erzeugt. Die NMI-Routine erhöht einen "Leere-Zeilen-Zähler" in A' und kehrt zum Anwendungsprogramm zurück, wenn hierfür noch die vorgesehene Zeit ausreicht. Wenn der Zähler leerer Zeilen nach dem Erhöhen Null wurde, schaltet die NMI-Routine den NMI-Generator aus und schaltet über einen Zeiger im IX-Registerpaar auf die Video-Display-Routine um.

  3. Die Video-Display-Routine setzt den Zeiger auf DFILE, initialisiert den Zeilen- und Spaltenzähler, schaltet Interrupts frei (EI) und springt mittels JP (HL) an den Anfang von DFILE + 32k. Bis auf das N/L-Zeichen, das das Ende der Zeile markiert, wird jedes Zeichen in DFILE als NOP interpretiert.

    Bei Zeilenende wird der Zeilen- und Spaltenzähler aktualisiert und nach Verlassen der Interruptbehandlungsroutine die verbleibenden Zeilen ausgeführt. Nach 192 Zeilen endet die Video-Display-Routine mit dem Einschalten des NMI-Generators und dem Zurückschalten der CPU auf das Ausführen des Anwendungsprogramms.

  4. Wie zuvor zählt die NMI-Routine im Bereich des oberen nicht sichtbaren Bildes die leeren Zeilen. Am Ende der unteren leeren Zeilen wiederholt sich der ganze Ablauf durch das Zurückschalten der NMI-Behandlungsroutine auf die VSYNC-Routine.

4. Die FAST-MODE Betriebsart

In der ZX80-kompatibeln FAST-MODE-Betriebsart führt die CPU entweder die Videoroutine aus oder irgend ein anderes Programm, aber niemals beide, was das bekannte Flackern der Anzeige bewirkt, das entsteht, wenn die CPU zwischen diesen Tasks wechselt. Wenn das Anwendungsprogramm ausgeführt wird, arbeitet es mit 100% der CPU-Zeit. Nur wenn das Anwendungsprogramm geSTOPt wird (im Befehlseingabemodus), während auf Tastatureingaben während des INPUT-Befehls gewartet wird, oder wenn der Befehl PAUSE verarbeitet wird, wird das Bildschirmbild aufgebaut.

Die Video-Hardware wird wie im SLOW-MODE aktiviert, außer daß NMI immer deaktiviert ist. Zusätzlich werden die leeren Zeilen am oberen und unteren Bildschirmrand ebenfalls softwaremässig erzeugt, was das ZX81-ROM 100% kompatibel zur ZX80-Hardware macht.

5. ZX81 Video Hardware

Die Videohardware des ZX81 besteht aus der Z80 CPU, dem ROM, dem RAM und des größten Teils des ZX81 Sinclair Logic Chip, der gewöhnlich "ULA" genannt wird. Eine übersicht mit allen relevanten Verbindungen einschließlich der puffernden Widerstände R ist im Bild 2 dargestellt.

Aus Gründen der Vereinfachung wird nur die 2KB-Version dargestellt. Die ULA enthält einen 6,5 MHz-Quarzoszillator und Frequenzteiler, der HSYNC-Pulse am Videoausgang und NMI-Pulse am NMI-Ausgang erzeugt. Die HSYNC- und NMI-Ausgänge können mit den folgenden I/O-Befehlen kontrolliert werden:

1. OUT FD,A schaltet den NMI-Generator aus
2. OUT FE,A schaltet den NMI-Generator ein
3. IN A,FE schaltet den HSYNC-Generator aus (nur wenn NMI aus ist)
4. OUT FF,A schaltet den HSYNC-Generator ein

 

Die Spannung am Videoausgang der ULA wechselt zwischen drei Zuständen. Normalerweise ist er bei +5V für weisse, leere Zeilen. Zeichenmuster werden als schwarze Pixel dargestellt, wobei die Spannung +2,5V beträgt. Bei den horizontalen HSYNC-Pulsen und den breiten VSYNC-Pulsen beträgt die Spannung 0V, wie dies im Bild 1 dargestellt ist. Diese Spannungen werden mittels eines Spannungsteilers auf +1V, +0,5V und 0V (UK/US) reduziert und dem Eingang des UHF-Modulators zugeführt, der aus dem Videosignal ein für den Fernseher auswertbares Antennensignal erzeugt.

 

 white _  _____    _____    _________//__________//____             ____//_____
 black _   |   |__|     |__|   |             |         |<--400us-->|          |
 sync  _   |<-------64us------>|<---64us---->|         |_____//____|          |
               display line         blank        blank   vert sync     blank

Bild 1: Video-Spannungen

Die HSYNC-Pulse sind 5 uS lang mit 64 uS Abstand zwischen ihnen. Die VSYNC-Pulse sind 400 uS lang mit 16,6 ms oder 20 ms Abstand zwischen ihnen. VSYNC dient dazu, den Vertikaloszillator des Fernsehers zu synchronisieren und den Bildaufbau am oberen Bildschirmbereich zu starten. Dies geschieht wenn IN A, FE (zum Abfragen der Tastatur) den Videoausgang auf den SYNC-Level drückt. 400 uS später wird der SYNC-Level verlassen um die Erzeugung von HSYNC-Pulsen alle 64 uS zu ermöglichen. Die HSYNC-Pulse werden fortan wieder unabhängig von der CPU bis zum nächsten VSYNC erzeugt.

6. ZX81 Character Video Hardware

Die ZX81 Hardware zur Zeichendarstellung besteht aus der Z80 CPU, dem ROM, dem RAM und dem größten Teil des ZX81 Sinclair Logic Chip, der gewöhnlich "ULA" genannt wird. Das Blockschaltbild ist in Bild 2 mit allen relevanten Verbindungen einschließlich den Pufferwiderständen R dargestellt. Aus Gründen der Vereinfachung ist lediglich die 2 KB-Version dargestellt.

              ULA               ROM        Z80        2K RAM
        ________________       _____       _____       _____ 
VIDEO<-| VSHFTREG <-DATA|-----|DATA |-----|DATA |--R--|DATA |  
       |  LINECTR ->A0-2|-----|A0-2 |--R--|A0-2 |-----|A0-2 |
       | CHRLATCH ->A3-8|-----|A3-8 |--R--|A3-8 |-----|A3-8 |
       |                |     |A9-12|-----|A9-13|-----|A9-11|
       |           ROMCS|-----|CE   |     |  INT|-----|A6   |
       |                |     |_____|     |     |     |     |
       |           RAMCS|-----------------|-----|-----|CE/OE|
       |             A14|-----------------|A14  |     |_____|
       |             A15|-----------------|A15  |        
       |              WR|-----------------|WR   | 
       |              RD|-----------------|RD   |
       |              M1|-----------------|M1   |
       |            MREQ|-----------------|MREQ |
       |            IORQ|-----------------|IORQ | 
       |             NMI|-----------------|NMI  |   
       |            HALT|-----------------|HALT |
       |________________|                 |_____| 

Bild 2: Schaltung zur Zeichendarstellung

7. Pseudo Hires Video Hardware

Die ZX81 Hardware zur Erzeugung der Pseudo-Hochauflösenden Grafik besteht aus der Z80 CPU, dem ROM, dem RAM und dem größten Teil des ZX81 Sinclair Logic Chip, der gewöhnlich "ULA" genannt wird. Das Blockschaltbild ist in Bild 3 mit allen relevanten Verbindungen einschließlich den Pufferwiderständen R dargestellt. Aus Gründen der Vereinfachung ist lediglich die 2 KB-Version dargestellt.

              ULA               ROM        Z80        2K RAM
        ________________       _____       _____       _____ 
VIDEO<-| VSHFTREG <-DATA|-----|DATA |-----|DATA |--R--|DATA |  
       | *LINECTR ->A0-2|-----|A0-2 |--R--|A0-2 |-----|A0-2 |
       | CHRLATCH ->A3-8|-----|A3-8 |--R--|A3-8 |-----|A3-8 |
       |                |     |A9-12|-----|A9-13|-----|A9-11|
       |           ROMCS|-----|CE   |     | *INT|-----|A6   |
       |                |     |_____|     |     |     |     |
       |           RAMCS|-----------------|-----|-----|CE/OE|
       |             A14|-----------------|A14  |     |_____|
       |             A15|-----------------|A15  |        
       |              WR|-----------------|WR   | 
       |              RD|-----------------|RD   |
       |              M1|-----------------|M1   |
       |            MREQ|-----------------|MREQ |
       |            IORQ|-----------------|IORQ | 
       |             NMI|-----------------|NMI  |   
       |            HALT|-----------------|HALT |
       |________________|                 |_____| 

Bild 3: Schaltung zur Erzeugung von Pseudo hochauflösender Grafik

Der einzige Unterschied zwischen Pseudo Hires und der Zeichendarstellung ist der *ULA LCNTR und die Nutzung des INT-Eingangs. Die Routinen zur Erzeugung von pseudo-hochauflösender Grafik nutzen den INT-Eingang nicht, und der LCNTR der ULA wird bei jeder horizontalen Linie auf Null zurückgesetzt. Die Ausnahme macht die Software XTRICATOR, die INT benutzt und auf zwei Arten das I-Register verwendet: erstens als Teil des RST-Vektors im INT-Mode 2, wenn die CPU am Ende einer horizontalen Linie unterbrochen wird und zweitens beim Refresh als Zeiger auf den Zeichengenerator im ROM.

8. Tatsächlich hochauflösende Grafikhardware

Die für tatsächlich für die Erzeugung von hochauflösender Grafik benötigte Hardware besteht aus der Z80 CPU, dem RAM, dem Videoschieberegister und der Schaltung innerhalb der ULA zur Erzeugung der SYNC-Pulse. In Bild 4 wird dies mit allen relevanten Verbindungen dargestellt. Wieder wird aus Gründen der Vereinfachung die 2 KB-Version gezeigt, doch für größere SRAM-Versionen trifft dies ebenso zu. Wenn ein 16K RAMPACK benutzt wird, müssen an diesem kleine Änderungen an der Hardware vorgenommen werden, damit während des Refresh-Zyklus Videodaten ausgegeben werden können, was für diese Art der Erzeugung hochauflösender Grafik benötigt wird. Woraus diese änderungen an der Hardware im Einzelnen bestehen, wird später besprochen werden.

              ULA               ROM         Z80       2K RAM
        ________________       _____       _____       _____ 
VIDEO<-| VSHFTREG <-DATA|-----|DATA |-----|DATA |--R--|DATA |
       |                |     |A0-12|-----|A0-15|-----|A0-10|
       |           ROMCS|-----|CE   |     |     |     |     |
       |                |     |_____|     |     |     |     |
       |           RAMCE|-----------------|-----|-----|CE/OE|
       |             A14|-----------------|A14  |     |_____|
       |             A15|-----------------|A15  |
       |              WR|-----------------|WR   |
       |              RD|-----------------|RD   |
       |              M1|-----------------|M1   |
       |            MREQ|-----------------|MREQ |
       |            IORQ|-----------------|IORQ |
       |             NMI|-----------------|NMI  |
       |            HALT|-----------------|HALT |
       |________________|                 |_____|

Bild 5: Schaltung zur Erzeugung tatsächlich hochauflösender Grafik

Mit der Ausnahme der Software WRX1K, die auf einem 1K ZX81 eine kleine hochauflösende Grafik erzeugt, benötigen alle Hires-Programme einen 6 KB-großen Grafikspeicher HFILE. Ausreichend RAM kann durch änderungen eines Standard-16K RAMPACK mit ein paar Dioden und einem Widerstand zur Verfügung gestellt werden.

Das RAMPACK muß verändert werden, damit während des Refreshzyklus Videodaten ausgegeben werden können. Dies geschieht, indem die RD- und RFSH-Leitungen am Steckverbinder unterbrochen werden und lediglich zwei 1N34A Germaniumdioden und ein 4,7k-Pullup-Widerstand eingefügt werden. Alle Änderungen geschehen auf eigenes Risiko!

                            +5V
                             |
                           [4.7K]
                     1N34A   |
connector   RD _______|/|____|_____________ RD OF RAMPACK
                      |\|    |
                             |
connector RFSH _______|/|____|     +5V_____ RFSH of RAMPACK
                      |\|
                     1N34A
Bild 6: änderungen am RAMPACK für tatsächlich hochauflösende Grafik

9. Zeitabläufe bei der ZX81 Zeichendarstellung

Die gesamte Sinclair ZX81 Bilderzeugungshardware wie in Bild 2 dargestellt wird benötigt, um ein Standardbild bestehend aus 24 Zeilen zu je 32 Zeichen darzustellen. Die Darstellung von Zeichen beginnt nach der letzten leeren Zeile am oberen Bildschirmrand, wenn die Videoroutine nach DFILE + 32K springt. Die Hardware in der ZX81 wird aktiviert, wenn irgendein Op-Code über 32K ausgeführt wird (A15 high und M1 low) und das Datenbit 6 "low" ist. Die Videodaten werden in diesen vereinfachten Schritten verarbeitet:

  1. Die ULA lädt den Zeichencode in ein Adreßregister innerhalb der ULA
  2. Die ULA drückt die Datenleitungen auf "low"
  3. Die CPU interpretiert die gedrückten Datenleitungen als NOP
  4. Die ULA erzeugt einen Teil der Adresse auf den Zeichengenerator und die CPU erzeugt den Zeichengeneratorzeiger mit dem I-Register
  5. Die Videodaten werden in's Videoschieberegister der ULA geladen

Man könnte sagen, daß das DFILE zeichenweise mit NOPs ausgeführt wird: für jedes Zeichen steht ein NOP. Jeder NOP-Befehl braucht zur Ausführung vier Taktzyklen der CPU bei 3.25 MHz und damit so lange wie 8 Pixel bei 6.5 MHz am Videoschieberegister der ULA.

           <--------CHARACTER 1-----------><--------CHARACTER 2----------->
   T STATE <--T1--><--T2--><--T3--><--T4--><--T1--><--T2--><--T3--><--T4-->
     (ref)  ___    1___2   3___     ___5    ___     ___     ___     ___
 CPU CLOCK |   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|
           _ _______________ _______________ _______________ _______________
    A0-A15 _X_____PC________X___I+CHR+ULA___X______PC_______X___I+CHR+ULA___X
                ________  NOP  __________       ________  NOP  __________
      DATA >---|__CHR___|_____|_ROM DATA_|-----|__CHR___|_____|_ROM DATA_|--

Bild 7: Zeitabläufe bei der Zeichendarstellung

Im Einzelnen werden die Abläufe für jedes Byte Zeichendaten in Bild 7 gezeigt und wie folgt erläutert:

  1. Jeder Zeichencode (CHR$) in DFILE wird adressiert durch den PC (program counter) der Z80 CPU. Bei der steigenden Flanke T2 wird das Byte aus DFILE in die ULA geladen: bit 0-5 werden in ein 6-Bit-Adreßlatch geladen, wohingegen Bit 7 das Invertierungsflag setzt oder rücksetzt.
  2. Bei der fallenden Flanke T2 zwingt die ULA alle Datenleitungen auf low.
  3. Bei der steigenden Flanke T3 werden die auf null gedrückten Datenleitungen von der CPU als NOP-Befehl erkannt.
  4. Während T3/T4 führt die CPU den Refreshzyklus aus und die ROM-Adressen werden mittels des I-Registers auf A9-A15 erzeugt, dem 6-Bit-ULA- Zeichenregister auf A3-A8 und dem ULA Zeilenzähler modulo 8 auf A0-A2.
  5. Wenn Bit 7 im Zeichencode-Latch der ULA "high" ist, werden die Videodaten invertiert.
  6. Dies wiederholt sich so lange, bis ein HALT-Befehl geholt wird.
  7. Bei dem Op-Code für den HALT-Befehl ist das Datenbit 6 "high", weshalb dieser Befehl nicht durch ein NOP ersetzt wird, sondern tatsächlich zur Ausführung gelangt.
  8. Unabhänging vom CPU-Timing erzeugt die ULA einen HSYNC-Puls und erhöht den Zeilenzähler LCNTR.
  9. Die CPU führt weiterhin NOPs aus, wobei sie das R-Register erhöht und bei jeder ansteigenden Flanke T4 den INT-Eingang liest.
  10. Wenn A6, das mit dem INT-Eingang fest verdrahtet ist, während des Refreshzyklus auf "low" geht (Bit 6 des R-Registers = 0), führt der Z80 die Interruptbehandlungsroutine unterhalb 32K aus.
  11. Die CPU kehrt von der Interruptbehandlungsroutine zurück und nimmt die "Ausführung" von DFILE-Opcodes wieder auf.
  12. Diese Vorgänge wiederholen sich 192 mal. Dann kehrt die INT-Routine zur Haupt-Video-Routine zurück, schaltet den NMI-Generator ein und schaltet zurück zum Anwenderprogramm.

10. Zeitabläufe bei Pseudo Hires

Bis auf wenige Ausnahmen wird die gesamte ZX81 Zeichendarstellungshardware wie in Bild 2 gezeigt für die Erzeugung eines Standard-Bildes von 192 Zeilen mit 32 scheinbar hochauflösenden Mustern. Die Darstellung von Mustern beginnt nach der letzten leeren Zeile am oberen Bildschirmrand, wenn die Videoroutine nach dem 6K großen HFILE + 32K springt. Die Hardware in der ZX81 wird aktiviert, wenn irgendein Op-Code über 32K ausgeführt wird (A15 high und M1 low) und das Datenbit 6 "low" ist. Die Videodaten werden in diesen fünf Schritten verarbeitet:

The video data is loaded in five steps:

  1. Die ULA lädt den Zeichencode in ein Adreßregister innerhalb der ULA
  2. Die ULA drückt die Datenleitungen auf "low"
  3. Die CPU interpretiert die gedrückten Datenleitungen als NOP
  4. Die ULA erzeugt einen Teil der Adresse auf den Zeichengenerator und die CPU erzeugt den höherwertigen Adreßteil auf den Zeichengeneratorzeiger (MSB) mit dem I-Register
  5. Die Musterdatendaten der scheinbar hochauflösenden Grafik werden in's Videoschieberegister der ULA geladen

Jeder NOP-Befehl braucht zur Ausführung vier Taktzyklen der CPU bei 3.25 MHz und damit so lange wie 8 Pixel bei 6.5 MHz am Videoschieberegister der ULA.

           <--------CHARACTER 1-----------><--------CHARACTER 2----------->
   T STATE <--T1--><--T2--><--T3--><--T4--><--T1--><--T2--><--T3--><--T4-->
     (ref)  ___    1___2   3___     ___5    ___     ___     ___     ___
 CPU CLOCK |   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|
           _ _______________ _______________ _______________ _______________
    A0-A15 _X_____PC________X___I+CHR+ULA___X______PC_______X___I+CHR+ULA___X
                ________  NOP  __________       ________  NOP  __________
      DATA >---|__CHR___|_____|_ROM DATA_|-----|__CHR___|_____|_ROM DATA_|--

Bild 7: Zeitabläufe bei der Erzeugung scheinbar hochauflösender Grafik

Der genaue Ablauf von Operationen für jedes einzelne Zeichenbyte läuft wie folgt ab:

  1. Jeder Zeichencode (CHR$) in DFILE wird adressiert durch den PC (program counter) der Z80 CPU. Bei der steigenden Flanke T2 wird das Byte aus DFILE in die ULA geladen: bit 0-5 werden in ein 6-Bit-Adreßlatch geladen, wohingegen Bit 7 das Invertierungsflag setzt oder rücksetzt.
  2. Bei der fallenden Flanke T2 zwingt die ULA alle Datenleitungen auf low.
  3. Bei der steigenden Flanke T3 werden die auf null gedrückten Datenleitungen von der CPU als NOP-Befehl interpretiert.
  4. Während T3/T4 führt die CPU den Refreshzyklus aus und die ROM-Adressen werden mittels des I-Registers auf A9-A15 erzeugt, dem 6-Bit-ULA- Zeichenregister auf A3-A8 und Null auf den Adreßausgängen A0-A2.
  5. Bei der fallenden Flanke T4 werden die Videodaten in das Videoschieberegister geladen und 8 Pixel werden bei 6.5MHz herausgeschoben.
  6. Wenn Bit 7 im Zeichencode-Latch der ULA "high" ist, werden die Videodaten invertiert.
  7. Die CPU erhöht ihren PC und holt den nächten Op-Code.
  8. Dies wiederholt sich so lange, bis ein RET-Befehl die CPU zur Hires-Routine zurückkehren lässt.
  9. Bei dem Op-Code für den RET-Befehl ist das Datenbit 6 "high", weshalb dieser Befehl nicht durch ein NOP ersetzt wird, sondern tatsächlich zur Ausführung gelangt.
  10. Unabhänging vom CPU-Timing erzeugt die ULA einen HSYNC-Puls und erhöht den Zeilenzähler LCNTR, die Software setzt den LCNTR aber direkt auf Null zurück.
  11. Die CPU kehrt von der Hires-Routine zurück und beginnt wieder, die Zeichencodes aus DFILE "auszuführen".
  12. Diese Vorgänge wiederholen sich 192 mal. Dann endet die Hires-Routine mit dem Einschalten des NMI-Generators und dem Zurückschalten zum Anwenderprogramm.

11. Zeitabläufe bei echter hochauflösender Grafik

Obwohl ich die WRX hires core-Routinen als Beispiel nehmen werde, ist die Software zur Erzeugung echter hochauflösender Grafik, die unabhängig davon von anderen entwickelt wurde, dieser sehr ähnlich.

Die Darstellung von hochauflösender Grafik beginnt nach der letzten leeren Zeile am oberen Bildschirmrand, wenn die NMI-Behandlungsroutine mittels dem IX-Registerpaar in die HR-Videoroutine springt. HR setzt die Adreßzeiger im I- und R-Register auf das hochauflösende Grafikfile (HFILE), anschließend springt die HR-Routine zur gespiegelten LBUF-routine über 32K und lädt das R-Register, schließlich verzweigt es nach dem ersten NOP-Opcode.

Die ULA lädt das Videoschieberegister wenn irgendein Op-Code ausgeführt wird, bei dem A15 "high" und M1 "low" ist und das Datenbit 6 gleich null.

Die Verarbeitung der Hires-Daten erfolgt in drei Schritten:

  1. Die CPU führt jede einzelne der 4 Taktzyklen dauernden NOP-Befehle aus.
  2. Während der Taktzyklen T3/T4 (Refresh) erscheinen die Inhalte der I- und R-Register auf den Adreßleitungen A0-15.
  3. Das Byte mit den Hires-Daten, das durch I und R adressiert wird, wird in das ULA-Schieberegister geladen.

LBUF besteht aus 32 NOPs, wobei jeder für die Ausführung vier Taktzyklen benötigt. Während des zweiten Teils der Ausführung des NOP-Opcodes adressieren das I- und das R-Register die Hires-Daten in HFILE, die die ULA in das Videoschieberegister lädt.

Die von der ULA erzeugte ROM-Adresse für den Zeichengenerator wird nicht benutzt, da ROMCS nicht aktiviert wird, da das I-Register (A8-15) auf einen Punkt im RAM-basierten HFILE zeigt.

           <---------HIRESBYTE1-----------><---------HIRESBYTE2----------->
   T STATE <--T1--><--T2--><--T3--><--T4--><--T1--><--T2--><--T3--><--T4-->
            ___     ___     ___     ___     ___     ___     ___     ___
 CPU CLOCK |   |___|   |___|   |___|   |___|   |___|   |___|   |___|   |___|
           _ _______________ _______________ _______________ _______________
    A0-A15 _X______PC_______X______I_+_R____X______PC_______X______I_+_R____X
                ____________    _________       ____________    _________
      DATA >---|_____NOP____|--|__HIRES__|-----|_____NOP____|--|__HIRES__|--

Bild 8: Die Zeitabläufe bei der Erzeugung tatsächlich hochauflösender Grafik

Die Einzelheiten der Verarbeitung jedes einzelnen Hires-Bytes lauten wie folgt:

  1. Der erste Op-Code der LBUF-Routine, LD R, A, wird ausgeführt.
  2. Die folgenden 32 NOPs in LBUF werden nacheinander ausgeführt.
  3. Bei der steigenden Flanke von T3 bei jedem Laden des Op-Codes, führt die CPU ein NOP aus.
  4. Während T3/T4 wird die Adresse aus dem R-Register auf A0-7 und dem I-Register auf A8-15 erzeugt.
  5. Bei der fallenden Flanke von T4 werden die Hires-Daten aus HFILE in das Videoschieberegister der ULA geladen und 8 Pixel bei 6.5MHz herausgeschoben.
  6. Die CPU erhöht den PC und das R-Register und holt das nächste NOP und somit das nächste Hires-Byte.
  7. Dieser Vorgang wiederholt sich 32 mal.
  8. Der letzte Op-Code von LBUF, JP (IX), wird ausgeführt um zur HR-Routine zurückzukehren.
  9. Die ULA erzeugt einen HSYNC-Puls.
  10. Die HLINE-Routine erhöht das I/R-Registerpaar um 32 und springt zurück zu der 32 NOP beinhaltenden LBUF-Routine über 32K.
  11. Dieser Vorgang wiederholt sich 192 mal.
  12. Die Hires-Video-Routine kann die Sinclair Zeichendarstellungsroutine aufrufen um die unteren Zeilen darzustellen und stellt die Registerinhalte usw. wieder her um zum Anwenderprogramm zurückkehren zu können.

Wie auch die anderen Hires-Routinen hält WRX die Sinclair Video-Logik durch das Laden eines neuen Videoroutinen-Vektors in das IX-Register außenvor.

12. ZX81 SLOW MODE VIDEO ROUTINES

Wie in Tabelle 1 dargestellt, betreibt die CPU Multitaskung zwischen den Videoroutinen und dem Anwenderprogramm in vier zeitlichen Abschnitten.

1. VSYNC-Intervall

0229 DISPLAY-1 Erniedrigt den Zeilenzähler
023E DISPLAY-2 Fragt Tastatur ab (zu Beginn des VSYNC)
0277 OUT FF,A Beendet den VSYNC-Puls
0292 DISPLAY-3 Sichert den Videovektor in IX (0281) und kehrt zum Anwenderprogramm zurück

2. Anwenderprogramm

0066 NMI Zählt leere Zeilen, kehrt zum Anwenderprogramm zurück oder verzweigt mittels JP (IX) nach 0281

3. Darstellung des DFILEs

0281 VIDEO-1 Richtet Darstellungsparameter ein, CALL 2B5 (RETURN VIA INT)
02B5 DISPLAY-5 Richtet Darstellungsparameter ein, gibt Interrupt frei, JP (DFILE)
XXXX (DFILE) Führt HALTs und erzwungene NOPs innerhalb des DFILEs aus
0038 INT Erniedrigt den Reihen-/Zeilenzähler und kehrt nach DFILE oder 028B zurück
0292 DISPLAY-3 Sichert den Videovektor(028F)

4. Anwenderprogramm

0066 NMI Zählt leere Zeilen, kehrt zum Anwenderprogramm zurück oder verzweigt mittels JP (IX) nach 028F
028F VIDEO-2 JP 229 zurück zum Frame-Zähler in Block 1

Die ZX81 Videoroutinen folgen mit voll dokumentierten Listings, die mehr Details enthalten als das Listing aus IAN LOGAN ZX81 DISASSEMBLY. Es muß wohl nicht weiter betont werden, daß ich Dr. LOGAN's Buch intensiv während meiner Nachforschungen im ZX81 Videocode verwendet habe.

Hinweis: Nur der für die Bilderzeugung relevante Code wird hier dargestellt.

0038               ;INT SERVICE ROUTINE
     DEC C         ;decrement the scan line counter in register C
     JP NZ 0045    ;go SCAN-LINE : repeats 8 times for each DFILE character row
     POP HL        ;point to the start of next DFILE row
     DEC B         ;decrement the ROW counter in register B
     RET Z         ;return to 028B
     SET 3,C       ;load scan line counter in register C with 08 scan lines

0041               ;WAIT-INT
     LD R,A        ;load value DD into register R
     EI            ;enable INT
     JP (HL)       ;execute the NOPs in DFILE

0045               ;SCAN-LINE
     POP DE        ;discard the return address
     RET Z         ;delay (never returns)
     JR 0041       ;got WAIT-INT
                   ;----------------------------------------------------------

0066               ;NMI SERVICE ROUTINE
                   ;Interupts application program every 64 usec (HSYNC)
     EX AF,AF'     ;retrieve blank line counter in AF'
     INC A         ;next blank line
     JP M 006D     ;RETURN via 006D if AF' = FF (to NMI-EXIT)
     JR Z 006F     ;JR NMI-CONT if last line
006D EX AF,AF'     ;save blank line counter
     RET           ;return to application or NMI-EXIT

006F               ;NMI-CONT
     EX AF,AF'     ;retrieve main register AF
     PUSH AF       ;now save the application program registers
     PUSH BC
     PUSH DE
     PUSH HL
     LD HL,(DFILE) ;needed only if IX=0281 and
     SET 7,H       ;if DFILE is executed
     HALT          ;1T state synchronization: this HALT is used with special
                   ;hardware connected to the CPU WAIT and HALT lines and is
                   ;released and synchronized on the falling of the NMI pulse

007A               ;NMI-EXIT
     OUT FD,A      ;turn off NMI generator
     JP (IX)       ;to VIDEO-1 or VIDEO-2
                   ;---------------------------------------------------------

0229               ;DISPLAY-1
     LD HL,(FRAMES);get the system variable FRAMES
     DEC HL        ;decrement each frame
     .....
     LD (FRAMES),HL;save the system variable FRAMES

023E               ;DISPLAY-2
     CALL 02BB     ;read the keyboard and load MARGIN with blank lines
     .....         ;also starts the VSYNC pulse
0277 OUT FF,A      ;stops the VSYNC pulse
     LD HL,(DFILE) ;(FAST VIDEO only) - point HL to first HALT for blank lines
     SET 7,H       ;(FAST VIDEO only) - DFILE echo above 32K
027E CALL 0292

0281               ;VIDEO-1
                   ;this vector is saved in register IX at 0292
     LD A,R        ;delay
     LD BC,1901    ;set up INT parameters for first HALT at (DFILE)
     LD A,F5       ;set up R register for first HALT at (DFILE)
     CALL 2B5      ;continue setup for DFILE display and return via INT
028B               ;return here from last INT
     DEC HL        ;(FAST VIDEO only) - point HL to last HALT for blank lines
     CALL 292      ;save VIDEO vector in IX, calculate blank lines, POP regs

028F JP 0229       ;VIDEO-2
                   ;this vector is saved in register IX at 0292

0292               ;DISPLAY-3
     POP IX        ;IX=0281 or 028F to vestor to VIDEO-1 or VIDEO-2
     LD C,(IY+56)  ;load number of blank lines from MARGIN (1F in 60 Hz option)
     BIT 7,(IY+59) ;test FAST/SLOW bit
     JR Z,2A9      ;(FAST VIDEO)  branches to generate blank lines
     LD A,C        ;C=(MARGIN)=1F for 60 Hz
     NEG           ;
     INC A         ;
     EX AF,AF'     ;during NMI @ 0066 - AF' is incremented and tested for zero
     OUT (FE),A    ;turn on NMI generator
     POP HL        ;self explanatory
     POP DE
     POP BC
     POP AF
     RET           ;return to application program interupted every HSYNC by NMI
                   ;-----------------------------------------------------------

02B5               ;DISPLAY-5
     LD R,A        ;R increments with each opcode on A0 to A7 during RFSH
                   ;until A6 goes low which generates the INT signal.
     LD A,DD       ;Set the left margin of all other lines, load to R at 0041
     EI            ;Now that R is set up enable INT
     JP (HL)       ;"executes" the DFILE starting with HALT and waits for the
                   ;first INT to come to the rescue.
                   ;-----------------------------------------------------------

13. ZX81 FAST MODE VIDEO ROUTINES

Um die für die Ausführung des Anwenderprogramms benötigte Zeit zu verringern, benutzt der FAST MODE 100% der CPU-Zeit. Dennoch gibt es Zeiten, in denen das Anwenderprogramm nicht aktiv ist. Wenn das Programm geSTOPpt ist oder eine PAUSE macht oder auf INPUT von der Tastatur wartet, wird die Tastatur abgefragt und wenn keine Taste gedrückt ist, wird das Bild unabhängig von NMI-Pulsen erzeugt.

Tatsächlich wurden die ZX81 FAST MODE Videoroutinen so realisiert, daß sie kompatibel mit der ZX80 Hardware sind, so daß das ZX81-ROM als "Frischzellenkur" im ZX80 verwendet werden kann.

Da der ZX80 die leeren Zeilen mittels Software erzeugt, macht das ZX81-ROM dasselbe, wenn es im FAST MODE ist.

Die Schleife der Videoroutinen für den FAST MODE beginnen mit der FRAME/KBD/VSYNC Routine bei 229h:

   0229 DECREMENT FRAME COUNTER
   023D EXIT FAST VIDEO IF FRAMES=0 (END OF PAUSE)
   023E CHECK KEYBOARD
   0260 EXIT FAST VIDEO IF NEW KEY PRESSED
   0292 SAVE THE VIDEO POINTER IN IX (0281)
   029B JR Z 02A9 TO BLANK LINE ROUTINE
   02A9 GENERATE BLANK LINES
   02B3 JP (IX) TO 0281
   0281 GENERATE THE DFILE DISPLAY
   0292 SAVE VIDEO POINTER (028F)
   029B JR Z 02A9 TO BLANK LINE ROUTINE
   02A9 GENERATE BLANK LINES
   02B3 JP (IX) TO 028F)
   028F JP 229 BACK TO FRAME COUNTER

Da der Großteil der Routinen bereits im SLOW MODE VIDEO-Kapitel behandelt wurden, werden jetzt hier nur die Unterschiede beschrieben.

Since most of the routines were described in the SLOW MODE VIDEO chapter, only the differences are described here. Compare the way the SLOW mode enters this loop from end of blank line application program execution by saving the main registers of the program and restoring them at the end of 0292. By contrast, the FAST mode does not save any registers and branches out of the 0292 restore main register routine to literally generate the blank lines. This is done at 029B after testing the FAST flag and jumping to a less known routine called DISPLAY-4

02A9 ;DISPLAY-4
   LD A,FC        ; first R delay to INT
   LD B,01        ; one row
   CALL 02B5      ; display blank lines
   DEC HL         ; point back to HALT
   EX (SP),HL     ; delay 19T
   EX (SP),HL     ; delay 19T
   JP (IX)        ; IX = 0281 or 028F

The routine at 02A9 is called twice each frame to generate the top and bottom blank lines with HL pointing to either the first HALT at the start of DFILE or the last HALT at end of DFILE. Reg C holds the number of blank lines and reg B is set up for 1 row. After VSYNC the 31 top blank lines are generated by calling the diplay routine at 02B5 and excecuting the first HALT at the START of DFILE 31 times. After returning from the display routine HL points to the last HALT+1 and DEC HL is required point HL back to the last HALT of DFILE. After saving the return address in IX, the routine at 029A is reentered with HL pointing to the last HALT and generates the bottom 31 blank lines by excecuting the HALT at the END of DFILE.

 

14. TRUE HIRES VIDEO SOFTWARE

The true hires core routines are distinguished by the use of the I and R register pair as address pointers for the display file. The only other requirement is to execute 32 NOP instructions (or equal) per horizontal line and to update the I and R registers during HSYNC time. More blank lines can be used above and below the display for faster application execution. The listings are compatable source code for the ZXAS assembler both on the ZX81 and under XTender, the ZX81 emulator form CARLO DELHEZ.

Check current version of XTender for hires compatability.

These ASCII listings can be used to prepare a formatted 2 REM .l file with the ZXAS.COM program from Jack Raats.

WRX16 - 1984 wilf rigter

This is the hires core used in programs by FRED NACHBAUR and GREG HARDER. It creates a 256x192 high resolution display in a 6144 byte array starting at (HFILE), which can be poked directly from BASIC programs. START is used to start the hires display and STOP restores the SINCLAIR video. It has a characteristic signature with the I register value greater than 2000 hex. PART 1 calls LBUF 192 times, displaying 256x192 pixels, calculates blank lines, saves pointer to PART 2 in IX and returns to application code. PART 2 calls VSYNC etc, calculates blank lines, saves pointer to PART 2 in IX and returns to application code execution.

      ;ORIGIN = 16516 (hex 4084)

LBUF  ;Displays one line of 256 pixels
      ;------------------------------

      ;like DFILE, it is called above 32K to activate the ULA video
      ;hardware. The hires bytes may be inverted for special effects
      ;by setting bit 7 of the NOP codes . The hires data is loaded
      ;into the ULA video shift register during the refresh cycles of
      ;the 32 NOP opcodes when the I and R registers sequentially
      ;address 32 bytes of hires data in the 6144 byte HFILE

LBUF
      LD R,A     ;Now load R register
      00 00 00 00;32 bytes of 8 pixels
      00 00 00 00
      00 00 00 00
      00 00 00 00
      00 00 00 00
      00 00 00 00
      00 00 00 00
      00 00 00 00
      JP (IX)    ;Return to HR


HR    ;HIRES DISPLAY ROUTINE PART 1
      ;----------------------------
      LD B,04    ;load delay
HR0   DJNZ HR0   ;delay 56T states to synchronize with HSYNC pulses.
      LD HL,(HFILE);RAMTOP points to the first HFILE byte
      LD B,C0    ;48 horizontal lines
      LD IX,HR1  ;save the return vector in IX (for JP (IX) at end of LBUF)
      JR HR2     ;skip HR1 first time through the loop
HR1   LD DE,20   ;this value for 32 bytes or 256 pixels per line is
      ADD HL,DE  ;added to HL to point to the start of the next HLINE
      DEC B      ;repeat 48 times
      JP Z HR3   ;if this is the last line JP to HR3
HR2   LD A,H     ;the address in HL is then transferred to
      LD I,A     ;I register
      LD A,L     ;and during LBUF to the R register
      JP C084    ;jump to LBUF @ 4084 + 8000 to start the ULA
HR3   LD IX,HR4  ;save the video vector so that NMI returns to HR4
      JR HR5     ;now get blank lines and return to application code

HR4   ;HIRES DISPLAY ROUTINE PART 2
      ;----------------------------
      CALL 220   ;first PUSH registers then jump to VSYNC, etc
      LD IX,WRX16;save the video vector so that NMI returns to HR
HR5   LD A,(4028);33 or 19 blank lines in system variable MARGIN
      JP 29E     ;save blank lines, start NMI, POP registers and RETURN

      ;--------------------- end of listing ---------------------

The hires video is started and stopped by changing the vector address
in register IX which is used by NMI to JP (IX) to the video routine.
The following routines are synchronized with the display so that the
changeover in video mode occurs without display breakup.

STOP  ;STOP hires video and return to SINCLAIR video
      ;---------------------------------------------
      LD HL,0281 ;pointer to SINCLAIR video routine
      LD A,1E    ;SINCLAIR ROM pattern table MSB base address (1E00)
      LD I,A     ;pointer to I register
      JR SYNC

START ;Start the hires video
      ;---------------------
      LD HL,HR   ;pointer to the hires video routine

SYNC  ;used by START and STOP to smoothly change video mode
      ;----------------------------------------------------
      PUSH HL
      LD HL,4034 ;FRAMES counter
      LD A,HL    ;get old FRAMES
SYNC1 CP A,(HL)  ;compare to new FRAMES
      JR Z SYNC1 ;exit after a change is detected
      POP IX     ;SINCLAIR video routine


     ;-------------- END OF LISTING ---------------

GUUS-FLATER by ENNO BORGESTEEDE (1984)

This hires core uses a ingenious way to intercept the video vector. Instead of changing the value of the IX register, GUUS-FLATER intercepts at the beginning of the DFILE execution by changing the first 4 bytes including the HALT to DI and JP 409F which is the start of the hires routine. At the end of the hires screen the program simply returns to ROM routine at xxxx. It has a characteristic DFILE starting with the DI and JP 409F and the HFILE starts at (4004)

(400C)  DI           ;these bytes are loaded into DFILE
        JP 409F      ;to vector to the hires routine

409F    LD B,08      ;delay
40A1    DJNZ 40A1    ;delay
        LD A,R       ;delay
        LD B,C0      ;192 lines
        LD DE,20     ;32 bytes per line
        LD HL,(4004) ;hires file (HFILE) starts at RAMTOP
40AD    LD A,H       ;MSB address of HFILE
        LD I,A       ;load MSB of HFILE pointer
        LD A,L       ;LSB address of HFILE
40B3    JP C0B6      ;JUMP above 32K
40B6    LD R,A       ;load LSB of HFILE pointer
40B8    "COPYRIGHT 1984 ENNO BORGESTEEDE " ; same as 32 NOPs
40D8    JP 40DB      ;JUMP below 32K
40DB    ADD HL,DE    ;next hires line
40DC    DJNZ 40AD    ;next line repeats 192 times
40DE    LD A,1E      ;restore ROM pattern table pointer
40E0    LD I,A       ;load pointer
40E2    RET          ;join the SINCLAIR video in progress

HRG7 - in progress

WRX16K - 1996 wilf rigter <rigter@cafe.net>

The original WRX was written in 1984 but recent renewed interest has yielded newer more efficient versions. WRX16K 1996 is the most compact version of the WRX yet and will display a true bit mapped 256x192 hires screen. It was designed to work with the modified 16K RAMPACK or 16K SRAM and you must first lower RAMTOP with POKE 16389,96 then NEW before loading.

The hires mode can be started and stopped with the same routines shown in the WRX16 listing above. The simple START is used for starting the hires mode by changing video vector address in the IX register. Hires is stopped with the inline code segment called "BREAK" which returns synchronously to the Sinclair video mode when the space key is pressed. The HFILE is a 6K linear array starting at (4004) but is easily relocated.

Note that HFILE must start on a 32 byte boundary (2000,2020, etc).

ORG   16516       ;(hex 4084)

START LD IX,HR    ;simple start of the hres mode
      RET

LBUF LD R,A       ;load HFILE address LSB
      0 0 0 0     ;32 NOPs = 256 pixels
      0 0 0 0 
      0 0 0 0 
      0 0 0 0 
      0 0 0 0 
      0 0 0 0 
      0 0 0 0
      0 0 0 0
      RET NZ      ;always returns

HR    
      LD B,7      ;delay
HR0   DJNZ HR0    ;delay
      DEC B       ;reset Z flag
      LD HL,(4004);HFILE starts at RAMTOP or HSCRN (note below)
      LD DE,20    ;32 bytes per line
      LD B,C0     ;192 lines per hires screen
HR1   LD A,H      ;get HFILE address MSB
      LD I,A      ;load MSB into I register
      LD A,L      ;get HFILE address LSB
      CALL C089   ;CALL LBUF + 8000
      ADD HL,DE   ;next line
      DEC B       ;dec line counter
      JP NZ HR1   ;last line
HR2   
      CALL 292    ;return to application program
      CALL 220    ;extra register PUSH and VSYNC
BREAK             ;this code segment is optional
      CALL F46    ;check break key
      LD A,1E     ;restore pattern table pointer
      LD I,A
      JR NC STOP  ;skip the HR vector load if BREAK
      LD IX,HR    ;load the HR vector
STOP  JP 2A4      ;return to application program

HSCRN 2000        ;this is used with SRAM at 8K - 16K

;------------------ end of listing ------------------

Note: HFILE can be relocated to use SRAM between 8 to 16K by changing LD HL,(4004) to LD HL,(HSCRN).

WRX1K -1996 wilf rigter

This little true hires program is special because it runs on a 1K/2K ZX81.
It creates a miniature 64x48 high resolution display in a 384 byte array
starting at (RAMTOP), which can be poked directly from BASIC programs.
When START is called, it collapses the SINCLAIR DFILE and expands
the hires file above RAMTOP in order to efficiently utilize the 1K memory.
When STOP is called, it recovers the space above RAMTOP to make more RAM
avialable for DFILE.

      ;ORIGIN = 16516 (hex 4084)

LBUF  ;Dummy display file
      ;------------------

      ;like DFILE, it is called above 32K to activate the ULA video
      ;hardware but only bit 7 character code data is used. The hires
      ;data is loaded into the ULA video shift register during the refresh
      ;cycles of the 8 NOP opcodes when the I and R registers
      ;sequentially address 8 bytes of hires data in the 384 byte HFILE
      ;NOTE: in this special case of short (8 byte) video lines the delay
      ;opcodes (E3 and 40) have bit 6 high to suppress the video display
      ;at the start and end of each horizontal line

      E3 E3 E3 E3;Delay 76T states
      LD R,A     ;Now load R register
LBYTE 00 00 00 00;8 bytes of 8 pixels
      00 00 00 00;
      40 40 40 40;Delay 20T states
      40         ;Delay 4T
      JP (IX)    ;Return to HR

START ;Makes room above RAMTOP and starts the hires routine
      ;----------------------------------------------------
      LD IX,HR    ;This is the start of the new video routine
      LD BC,180   ;384 bytes are required for a 64X48 display
      CALL EC5    ;is there enough room to lower ramtop?
      LD HL,(4004);get the old RAMTOP (1K/2K)
      CCF         ;calculate the new RAMTOP value by
      SBC HL,BC   ;Subtracting the HFILE length

STACK ;used by START and STOP to change RAMTOP without NEW

      OUT FD,A    ;Turn off the NMI generator during STACK move
      LD (4004),HL;Save RAMTOP value in the sytem variable
      DEC HL      ;point to the first byte below RAMTOP
      LD (HL),3E  ;and mark it with 3E
      DEC HL
      LD SP,HL    ;Load the STACK POINTER
      DEC HL
      DEC HL
      LD (4002),HL;Load the ERROR STACK POINTER
      OUT FE,A    ;Turn on NMI
      JP 676      ;resume BASIC program execution at NEXT LINE


HR    ;PART 1 calls LBUF 48 times, displaying 64x48 pixels, calculates blank
      ;lines, saves pointer to PART 2 in IX,and returns to application code
      ;PART 2 calls VSYNC etc, checks the BREAK key, calculates blank lines,
      ;saves pointer to PART 1 in IX and returns to application code.

      LD B,04    ;load delay
HR0   DJNZ HR0   ;delay 56T states to synchronize with HSYNC pulses.
      LD HL,(4004);RAMTOP points to the first HFILE byte
      LD B,30    ;48 horizontal lines
      LD IX,HR1  ;save the return vector in IX (for JP (IX) at end of LBUF)
      JR HR2     ;skip HR1 first time through the loop
HR1   LD DE,08   ;this value for 8 bytes or 64 pixels per line is
      ADD HL,DE  ;added to HL to point to the start of the next HLINE
      DEC B      ;repeat 48 times
      JP Z HR3   ;if this is the last line JP to HR3
HR2   LD A,H     ;the address in HL is then transferred to
      LD I,A     ;I register
      LD A,L     ;and during LBUF to the R register
      JP C084    ;jump to LBUF (4084 + 8000) to start the video
HR3   LD IX,HR4  ;save the video vector so that NMI returns to PART 2
      JR HR5     ;now get blank lines and return to application code

HR4   ;PART 2

      CALL 220   ;first PUSH registers then jump to VSYNC, etc
      CALL F46   ;test the BREAK key
      JR NC STOP ;and exit HR if key is down
      LD IX,HR   ;save the video vector so that NMI returns to PART 1
HR5   LD A,(4028);33 or 19 blank lines in system variable MARGIN
      ADD A,47   ;71 more blank lines for fast application code execution
      JP 29E     ;save blank lines, start NMI, POP registers and RETURN
STOP             ;EXIT hires video to restore RAMTOP and SINCLAIR video
      LD A,1E    ;SINCLAIR ROM pattern table MSB base address (1E00)
      LD I,A     ;pointer to I register
      LD HL,(4004);load the current RAMTOP
      LD DE,180  ;HFILE length
      ADD HL,DE  ;is added to the current RAMTOP
      LD IX,0281 ;SINCLAIR video routine
      JR STACK   ;exit HR via STACK to change RAMTOP


     ;------------------END OF LISTING----------------

15. PSEUDO HIRES CORE ROUTINES

Pseudo hires software uses CHR$ code + register I to address 1 of 64 pattern bytes in the ROM. The CHR$ codes are limited 0 to 63 and their inverse (128 to 191) and the value of I is choses to point to a block of pattern bytes in ROM which has the greatest randomness and least duplication of values. This method is called "pseudo" hires because fewer than 50% of the 256 patterns required for true hires are available for display. This results in incomplete or missing pixel patterns but for many application (games etc) this is not a problem. The advantage of this method is the fact that it runs on systems the standard 16K RAMPACK and is emulated by Xtender.
It has a characteristic 6336 byte DFILE consisting of 192 RET opcodes spaced at 33 byte intervals.

ROCK CRUSH by Steve McDonald

In progress

3DHIRES author unknown

In this example core routine the expanded hires "DFILE" starts at 6700 hex

START   LD A,04   ; select an "interesting" pattern table
        LD I,A    ; load into ROM pattern table pointer
        LD IX,42C4; load pseudo hires vector
        RET

42C4    LD HL,E6DF; HL is used as the hires "DFILE" pointer
        LD DE,0021; 32 CHR$ + RET = 33 bytes per line
        DI        ; INT not used
        LD C,FE   ; IO address to reset the ULA row counter
        LD B,16   ; delay
42CF    DJNZ 42CF ; delay
        LD B,C0   ; 192 lines of 33 bytes
42D3    IN A,(C)  ; apply reset to row counter
        OUT FF,A  ; release reset from row counter
        ADD HL,DE ; next "DFILE" line (E700,E721, etc)
        CALL 42EC ; "execute" the "DFILE" via JP (HL) at 42EC
        DEC B     ; decrement line counter
        JP NZ 42D3; last line? repeat 192 times.
        CALL 0292 ; restore main registers, return to application
        CALL 0220 ; extra register PUSH then VSYNC
        LD IX,42C4; hires routine vector
        JP 02A4   ; restore main registers, return to application
        JP (HL)   ; jump to the hires "DFILE" echo above 32K

XTRICATOR by Software Farm

This unusual pseudo hires core routine intercepts the video by setting INT mode 2 in which the interrupting device supplies part of the INT vector address. Since the idle data bus is FF and the I register is set to 40, the INT vector is 4000 when the A6 line interupts at the end of the horizontal line.

4083    LD HL,40A5 ;return vector
4086    PUSH HL    ;save vector
4087    LD HL,E500 ;hires file
408A    PUSH HL    ;save vector
408B    LD B,07    ;delay
408D    DJNZ 408D  ;delay
408F    LD A,1E    ;Sinclair ROM patterns
4091    LD I,A     ;load pattern table pointer
4093    LD DE,C201 ;D = 194 lines
4096    DEC E      ;set Z FLAG
4097    JP Z 409A  ;delay
409A    POP HL     ;HL = E500
409B    DEC D      ;decrement the line counter
409C    RET Z      ;after last line only : return to 40A5
409D    SET 0,E    ;E = 01
409F    JP 40A2    ;delay
40A2    LD A,00    ;delay
40A4    JP (HL)    ;jump to 6500 above 32K

40A5    LD A,04    ;"special" ROM pattern
40A7    LD I,A     ;load pattern table pointer
40A9    RET        ;return to xxxx

40AA    LD A,40    ;INT mode 2 MSB address
40AC    LD I,A     ;load the INT mode 2 vector
40AE    IM2        ;start INT mode 2
40B0    RET

40B1    LD A,1E    ;Sinclair ROM patterns
40B3    LD I,A     ;load pattern table pointer
40B5    IM1        ;restore INT mode 1
40B7    RET

40B8    LD HL,6500 ;load a call instruction
40BB    LD B,C1    ;the start of HFILE (E500)
408D    LD (HL),CD ;CALL 4096
408F    INC HL
40C0    LD (HL),96
40C2    INC HL
40C3    LD (HL),40
40C5    INC HL
40C6    LD A,20     ;generate a 6144 spaces starting 
40C8    LD (HL),00  ;at 6503
40CA    INC HL
40CB    DEC A
40CC    JR NZ 40C8
40CE    DJNZ 40C6 
40D0    RET