1987-BHV-02 S.26-31

Die Grundintelligenz der HD 64180-Coprozessorkarte

Arnulf Sopp

Der HD 64180 kann seine erweiterten Fähigkeiten dem Computer nur auf Anforderung des Z80 zur Verfügung stellen. Er muß daher per Software in die Lage versetzt werden, eine solche Anforderung zur Kenntnis zu nehmen und zu interpretieren. Auf Helmut Bernhardts HD-Board ist ein EPROM vorgesehen, das dieses Programm enthält. Sein Inhalt ist im Listing 1 wiedergegeben.

Der HD liest nicht nur nach Reset, sondern auch nach einem TRAP-Interrupt an der Stelle logisch 0000h. Darauf wird hier nicht weiter eingegangen, weil Gerald Schröder darüber bereits schrieb. In diesem Falle verzweigt der HD in eine Fehlerbehandlungsroutine, die den unbekannten Befehl imitiert und dann zum alten Programm zurückkehrt. Diese Routine habe ich fast wörtlich von Geralds Programm übernommen, das in der 17. Ausgabe des Clubinfos abgedruckt ist. Deshalb sei hier nur auf den geringen Unterschied zu Geralds Routine hingewiesen: Die Interrupts müssen nicht mit DI maskiert werden, weil sich die Speicherkonfiguration während der Trap-Behandlung nicht ändert. Ein INT dürfte getrost eintreten. In diesem EPROM entfällt natürlich auch das Banking, das für das Genie 2s im Info beschrieben wurde. Ein TRAP kann im EPROM nicht auftreten, weil keine Illegals verwendet sind. Die Routine liegt daher nur zur Bearbeitung aus dem RAM bereit.

Wurde 0000h nach einem Reset gelesen, finden einige Initialisierungen statt. Für den leistungsfähigen HD-Befehl OTIMR (gib ein Datum aus (HL) auf Port (C) aus, erhöhe C und HL, erniedrige B, wiederhole dies, bis B=0 ist) wird HL als Zeiger auf einige Initialisierungsdaten (Label initab) aufgesetzt. Diese Daten konfigurieren im wesentlichen die MMU (Memory Management Unit) des HD, um aus dem Adreßraum von insgesamt 512 kB folgende "Fenster" der CPU zugänglich zu machen:

Common 0:nicht definiert Bank-Area:0.0000-0.EFFFh Common 1:7.F000-7.FFFFh

In dieser Tabelle von Daten sind weitere Informationen für den HD enthalten, die aber für das Verständnis des Folgenden nicht von Bedeutung sind. Soweit sie sich nicht aus den Kommentaren des Listings erklären, möchte ich auf das Datenbuch verweisen.

Die CPU hat nun insgesamt 64 kB Speicher verfügbar, deren physikalische Lage oben dargestellt ist. Nach dem Befehl OTIMR wird HL nun auf das letzte Byte einer zweiten Tabelle gestellt (tabend). Sie enthält Daten für den DMAC (Direct Memory Access Controller). Er kann unabhängig von der Konfiguration der MMU Daten aus jedem beliebigen Teil des Gesamtspeichers in jeden beliebigen anderen transferieren. Die Werte vom Label srclog an besagen in diesem Falle, daß die Daten ab dma0 bis zum Label prt in den physikalischen Speicherbereich ab 7.F000h übertragen werden sollen. Die beiden Bytes davor in der Tabelle bestimmen die Art des DMA-Zugriffs und führen schließlich zum Start des DMA.

Die Routine zur Programmierung des DMA-Controllers (ab Label dma0) ist so gestaltet, daß der Programmierer nicht weiter über die einigermaßen komplizierten Port-Ausgaben bescheidwissen muß. Analog zum Befehl LDIR lädt er lediglich HL mit der Quelladresse, DE mit der Zieladresse (beide innerhalb je eines beliebigen physikalischen 64-kB-Blocks) und BC als Bytezähler. Zusätzlich muß nur noch im Akku angegeben werden, in welchem physikalischen 64-kB-Block des Gesamtspeichers von 512 kB Quelle und Ziel zu suchen sind. Die Bits 0-2 tragen diese Information für die Quelle, Bit 4-6 für das Ziel. Ich habe diese Bits bewußt ausgewählt, weil dann der Akku Quelle und Ziel sozusagen im Klartext, nämlich in je einer Hex-Ziffer enthält. Je nach dem, welche Vorarbeit bei der Programmierung des DMAC schon geleistet ist, kann auch an späterer Stelle eingesprungen werden.

Der EPROM-Inhalt von srclog bis prt-1 ist nun ins obere RAM übertragen. Er enthält auch eine Interrupt-Vektortabelle für den Fall des IM 2. Der Z80 kann nämlich mit einer geringfügigen Hardware-Änderung mit der Ansprache des Ports FBh einen Interrupt am HD-Pin INT2 auslösen, wodurch der HD die Buskontrolle übernimmt. Ebenso ist es denkbar, daß der HD im Interrupt-Betrieb für den Z80 mit irgendwelcher Peripherie kommuniziert. Das EPROM ist auf diese Fälle vorbereitet. Da die Vektoren von Fall zu Fall programmiert werden müssen, sind sie im EPROM alle zu FFFFh gesetzt, so daß sie entweder umgebrannt oder später soft aufgesetzt werden können.

Nur der Vektor für einen Interrupt auf den Pin INT2 des HD ist auf intz80 gesetzt, weil er anstelle des Polling die Busübergabe signalisieren könnte. Die geschähe nun freilich asynchron. Ein Job könnte gerade in Arbeit sein. Daher gibt es das Flag rdyflag, aus dem der HD ablesen kann, ob er die neue Aufgabe bereits übernehmen kann. Wenn nicht, fährt er erst mit dem alten Job fort. Wenn er später wieder bei waitz80 landet und pollt, findet er sofort RAM und tut nun verspätet, was von ihm verlangt wird. Im ausschließlichen Polling-Betrieb merkt der HD nichts von der Z80-Ansprache bevor er nach waitz80 zurückkehrt.

Schließlich wird noch der gesamte Inhalt des EPROMs nach 6.0000-7.FFFF übertragen, damit der HD später auch vom Z80-RAM aus bequemen Zugriff auf Routinen hat, die im EPROM niedergelegt sind. Ohne diese Kopie ginge das nicht, weil das Z80-RAM und das EPROM niemals gleichzeitig erreichbar sind. Solche Routinen können nachträglich ins EPROM gebrannt werden. Ihre Funktion muß nichts mit dem HD zu tun haben.

Da ab physikalisch 7.F000h bereits Common 1 definiert ist, sind die per DMA nach oben geladenen Programmteile sofort verfügbar. Deshalb können bereits die Wertetabelle und das DMA-Unterprogramm im oberen RAM benutzt werden, um das EPROM nach oben zu kopieren.

Damit hat sich das EPROM nun selber überflüssig gemacht. Dennoch bleibt es eingeblendet. Der HD liest nun fortgesetzt an der Speicherstelle 0000h und prüft, ob dort der Opcode 00h für NOP steht. Wenn ja, handelt es sich an dieser Adresse immer noch um sein EPROM. Wenn ein beliebiger anderer Code gelesen wird, liegt dort unten nun der bisherige Z80-Speicher vor. Dies ist das Signal, daß der Z80 soeben die Kontrolle über seinen Systembus an den HD abgab. Kein Betriebssystem wird bei 0000h ausgerechnet mit einem NOP beginnen, so daß Zweifel über EPROM oder Z80-Memory nicht bestehen können. Und wenn doch, sollte der User ohnehin zuvor für den HD einen JP (Opcode C3h) zu einer Trap-Routine dort gepatcht haben. Auf Computer, die in ihrem unteren Adreßraum ein nicht schreibbares ROM haben, wird an späterer Stelle eingegangen.

Die Adresse 0000h für das Polling resultiert aus der Forderung, daß das Board mit seinem EPROM möglichst in jedem Z80-Rechner ohne Änderungen benutzbar sein sollte. In den TRS-80-kompatiblen Computern mit NEWDOS o. ä. gäbe es viele Möglichkeiten, auf eine andere Adresse auszuweichen. Jedoch schon unter CP/M fangen die Restriktionen an. Ab 0000h beansprucht CP/M den Speicher für sich. Dort steht ein JP-Befehl für den Warmstart mit CTRL-C. Unter CP/M muß deshalb eine Trap-Routine prüfen, ob es sich tatsächlich um einen TRAP handelt und gegf. den überschriebenen CP/M-Sprung nachholen. Dies gilt für viele Computer analog. Nur solche, die NEWDOS/G-DOS/TRSDOS fahren und ein Boot-EPROM besitzen, können sicher sein, daß 0000h nur bei einem TRAP angesprungen wird.

Die beiden Mikroprozessoren müssen einander mitteilen können, was anliegt. Eine Möglichkeit bestünde darin, daß der HD bei einer Ansprache durch den Z80 immer an dieselbe vereinbarte Speicherstelle springt und einfach tut, was da steht. Für diejenigen unter uns, die auch noch im Tiefschlaf vom Hacken träumen, wäre dies zweifellos die flexibelste Lösung. Ein bequemerer Weg, der sogar unter BASIC beschritten werden kann, wurde hier gewählt: An der Stelle 0003h für TRS-80 usw.: kommt später) wird der Stackpointer abgelegt. Alles Weitere, was zur Kommunikation der beiden Mikroprozessoren erforderlich ist, muß nun über den Stack abgewickelt werden. Der Stackpoin-ter des HD wird dazu aus 0003/4h geladen.

Hier gibt es folgende Vereinbarung: Zuunterst im Stack kann AF gePOPt werden. Im Akku steht dann ein Signalbyte, das dem HD verrät, welche von fünf möglichen Anforderungen der Z80 an ihn stellt (was in welcher Reihenfolge dazu auf den Stack gelegt werden muß, ist in den Listings reichlich kommentiert und teilweise tabellarisch zusammengefaßt):

  1. Die endgültige Übergabe des Systembus an den HD. Wenn der Akku =00h ist, will sich der Z80 endgültig verabschieden. Der HD übernimmt nun seine Arbeit bis zum nächsten Reset oder Ausschalten. In diesem Falle wird in der nächsthöheren Ebene auf dem Stack eine Adresse erwartet, wo der HD ein paar Bytes zum Umbanken des Common- 1 -Bereiches ablegen kann. Bankt er nämlich noch oben im alten Common 1 dieses einfach weg, findet er an der entsprechenden Adresse im Bereich 0.xxxxh sonst etwas, bloß nicht viel Gescheites. Eine Ebene höher im Stack steht schließlich die Adresse, an der der HD seine Arbeit beginnen kann. Das wäre für NEWDOS/G-DOS z. B. im einfachsten Fall 402Dh, der DOS-Ready-Einsprung. Falls zusätzlich Registerinhalte aus dem Stack übergeben werden sollen, beginnt das Programm für den HD eben mit ein paar POPs.
  2. Erledigung eines vorübergehenden Jobs für den Z80. Irgendein Akku-Inhalt zwischen 03h und FEh signalisiert dem HD, daß jetzt eine Einzelaufgabe für den Z80 übernommen werden soll, wonach der HD auf eine erneute Ansprache des Z80 zu warten hat. Nun erwartet der HD in der nächsten StackEbene die Adresse, wo er ins Job-Programm einspringen soll. Um nicht auch noch Informationen über die Anfangs- und Endadresse des zu bearbeitenden Programms übergeben zu müssen, wird kurzerhand der ganze Speicherinhalt von 0000-EFFFh per DMA nach oben geholt. Die Bank-Area, bisher bei 0.0000- 0.EFFFh, wird nach 7.0000-7.EFFFh verlegt. Das Z80-Programm wird nun als nterprogramm behandelt (es muß mit RET enden). Wenn es abgearbeitet ist, kehrt der HD wieder in die Warteschleife waitz80 zurück. Es besteht die Vereinbarung, daß ein solches Programm Rechenergebnisse nur im Bereich ab F800h ablegen darf, und zwar höchstens 2 kB (bis logisch FFFFh). Weiterhin ist klar, daß es ab F000h, wo der HD waltet, nichts mehr verloren hat. Es darf aber nahezu beliebig herumbanken, denn an der Rückkehradresse waitz80 werden die lebenswichtigen Systemkonfigurationen restauriert.
  3. Die Abholung von Rechenergebnissen. Wenn ein solcher vorübergehender Job Ergebnisse ab F800h niedergelegt hat, könnte ein weiterer Job so programmiert sein, daß er sie von dort per DMA in den unteren Speicher transferiert. Das wäre für diese simple Aufgabe aber zu aufwendig. Deshalb gibt es die dritte Möglichkeit, daß der Akku genau =01h ist. In diesem Falle "weiß" der HD ohne weitere programmierte Absprache, daß er jetzt 2 kB Daten ab 7.F800 in einen Speicherbereich übertragen soll, der ihm eine Ebene höher im Stack mitgeteilt wird.
    Größere Datenmengen, etwa eine Graphik von 32 kB Umfang oder sehr lange Tabellen, können gleichwohl vom HD errechnet werden. Sie müssen allerdings dann wirklich in einem gesonderten Job-Programm abgeholt werden. Ihr Puffer muß weit genug unterhalb logisch F000h oder jedenfalls bei entsprechender Umsicht unterhalb physikalisch 7.F000h beginnen.
  4. Die Kopie des Speichers 0.0000-0.FFFF nach 1.0000-3.FFFF. Das EPROM bietet die Möglichkeit, das untere Viertel der 41256-RAMs in die drei oberen Viertel zu kopieren. Zu diesem Zweck wird der Akku mit FFh geladen und auf den Stack gelegt. Weitere Informationen für den HD sind nicht erforderlich. Nun liegen vier identische Speicherbereiche vor, zwischen denen hin- und hergeschaltet werden kann, weil in der Nachbarbank sofort die passende Fortsetzung gefunden wird. Lediglich dem Stack ist dabei Aufmerksamkeit zu widmen, falls er nicht in einem Common-Bereich liegt und für alle Speicherviertel gültig ist. Das Unterprogramm clone kann von überall her aufgerufen werden, um beispielsweise das Konzept von vier Computern in einem Gehäuse mit der DMA-Fähigkeit des HD komfortabler zu verwirklichen.
  5. Daß der Stack vom HD mit POPs und RETs gelesen wird, ist für den Z80 völlig belanglos. Sein Stackpointer geht bei der Busübergabe mit der ganzen CPU in den Ruhestand. Wenn der HD die Buskontrolle zurückgibt, zeigt der SP des Z80 an dieselbe Stelle wie vor dem OUT (FBh),A. Der User muß lediglich daran denken, den vorausgegangenen PUSHes nun die entsprechenden POPs folgen zu lassen, damit z. B. eine RET-Adresse wiedergefunden werden kann. Daß der Stack nicht ausgerechnet im Bereich ab F000h liegen darf, versteht sich. Dort hat der HD sein privates RAM, aus dem er höchstens POPpen könnte, was er selber gePUSHt hätte. Unter BASIC muß daher beispielsweise das Himem begrenzt werden.

    Bei der endgültigen Übergabe der Buskontrolle an den HD ist es so, als wäre im Rechner nie ein Z80 eingebaut gewesen. Man braucht sich um ihn nicht mehr zu kümmern. Allerdings darf der HD dann auch niemals einen Output auf den Port FBh geben, weil er damit die Kontrolle an den Z80 zurückgäbe. Prinzipiell ist das natürlich auch möglich, erfordert aber besondere Umsicht. Da der Z80 genau da weiterarbeiten würde, wo er selber mit einem OUT (FBh),A die Kontrolle abgab, sollte man an der Stelle dahinter z. B. einen Jump nach DOS-Ready oder ähnlich unkritischen Code unterbringen. Es muß auch beachtet werden, daß sich der HD zuvor wieder Common 1 in den oberen Speicher legt, damit er sich dort in sein Schneckenhaus zurückziehen kann.

    In dem unter 2. genannten Fall muß der Z80 selbstredend ein Programm übergeben, das sozusagen mit dem Gehirn des HD denkt. Wenn Common 0, die Bank-Area oder Common 1 dabei manipuliert wird, kann einiges schiefgehen. Der Programmierer kann mit der erforderlichen Vorsicht gleichwohl ohne weiteres über den EPROM-Inhalt und den gesamten Speicher ab 4.0000h verfügen sowie über die Annehmlichkeiten des DMAC, der seriellen Schnittstellen des HD usw. Es ist sogar möglich, nach diesem Muster einen BASIC-Job zu übergeben (wer wollte ernsthaft eine Wurzel in Maschinensprache ziehen?). Da der gesamte Speicher von 0.0005-0.EFFF nach oben verholt wird, darf selbstverständlich auch BASIC/CMD oder auch nur Level 2 dort am Werk sein.



    Es dürfen auch Illegals im Programm enthalten sein, denn Geralds Trap-Routine liegt bereit. Dieser Fall ist jedoch nicht vorgesehen. Dazu müßte an der Stelle logisch 0000h ein JP zur Trap-Routine gesetzt werden. Es wäre aber nicht besonders klug, weil die Umschreibung eines Illegals mit zwei, drei Befehlen um Größenordnungen schneller geht als ein Trap.

    Im Falle der Übergabe von Rechenergebnissen muß von der Z80-Seite aus nichts weiter bedacht werden. Es versteht sich lediglich, daß von der auf dem Stack abgelegten Adresse an wirklich mindestens 2 kB RAM frei sein müssen. In beiden letztgenannten Fällen knipst der HD im unteren Adreßraum mit OUT (FBh),A sein EPROM wieder an, sobald er das zu bearbeitende Programm nach oben oder die Resultate eines Programms nach unten verfrachtet hat. Der Z80 hat im unteren RAM nun wieder das Sagen.

    In allen Fällen muß der Programmierer wissen, daß der HD noch längst nicht auf die Hardware eingestellt ist, in der er die Arbeit übernehmen soll. Bei den Tandy-Kompatiblen ist z. B. der Befehl für den Interrupt-Modus IM 1 ins HD-Programm zu schreiben. Da bei der Busübernahme DI durchgeführt wurde, müssen die Interrupts auch bei Bedarf gesondert wieder enabled werden. Überhaupt kann der Z80 in keinerlei Hinsicht von sich auf andere schließen (Registerinhalte, INT-Behandlung), so daß vor der Busübergabe auf der Z80-Seite sehr sorgfältig an alles Wesentliche gedacht werden muß.

    Um das EPROM im Interesse einer allgemeinen Verwendbarkeit auch nicht auf das Genie 3s mit Helmuts Megabyte-Banker maßzuschneidern, darf der Z80 den HD 64180 nur ansprechen, wenn er gerde im unteren Viertel der 256er RAM-Chips arbeitet. Das ist der "Computer Nr. 0", wie wir ihn in dem Sonderheft "Dein G3s, die 4 unbekannten Wesen" genannt haben. Bei Computern mit 64 kB RAM entfällt diese Bedingung.

    Dies sind die Möglichkeiten, die der HD sich mit seinem EPROM selber schafft. Wenn er jedoch endgültig oder vorübergehend die Kontrolle über den Systembus übernimmt, muß ihm der Z80 zuvor Quartier gemacht haben. Wegen der möglicherweise auftretenden TRAP-Interrupts sollte an 0000h in eine Error-Trap-Routine verzweigt werden. Wo die Trap-Routine dann steht, ist gleichgültig. Es muß auch nicht die komplette Routine sein, wie sie bereits im EPROM programmiert ist. Da sie nach dem DMA bei den Initialisierungen nach 7.F100 übertragen wurde, wo sie noch immer steht, braucht der Z80-Beitrag zum TRAP nur aus dem entsprechenden Banking und einem Sprung nach dort oben zu bestehen.

    Die älteren TRS-80-kompatiblen Rechner lesen von 0000-2FFFh in einem ROM und haben erst ab 4000h freies RAM. Wenn diese Computer einen Banker eingebaut haben, der im unteren Adreßraum RAM einblenden kann, gilt alles bisher Gesagte unverändert. Im RAM muß dann eine Kopie des ROMs vorliegen, die an 0000h aber den Trap-Sprung enthält. Aber ohne Banker entfällt die Möglichkeit, unter der Regie des HD Programme mit Illegals zu fahren, da wegen der nicht änderbaren RST-00h-Routine ein TRAP nicht möglich ist. Solche Programme würden zu einem Boot führen. Am Anfang des Listings muß dann auch spbuff auf eine RAM-Adresse gesetzt werden, z. B. 4200h (DOS-Sektorpuffer).

    Im Folgenden soll nun ein Beispiel gegeben werden, wie der Z80 lästige Pflichten loswerden kann, um sich mittlerweile wichtigeren Dingen zu widmen. Das Beispiel besteht hier nur aus dem Löschen des Bildschirms. Eine läppische Sache, die diese digitale Orgie mit einem zweiten Mikroprozessor gar nicht lohnt. Es geht jedoch nur darum, zu zeigen, wie beide CPUs miteinander kommunizieren können. Das Programm ist im Listing 2 wiedergegeben. Der Z80 übergibt im Stack zuerst die Einsprungsadresse des Programms namens job, dann das Signalbyte, aus dem der HD entnehmen soll, daß es nur eine vorübergehende Aufgabe ist. Schließlich wird der Stackpointer an 0003/4h abgelegt.

    Jetzt hat der Z80 die Hände frei, um Denkwürdiges zu leisten, während der HD inzwischen 2 kB Pufferraum mit Leerzeichen vollmalt. Wenn der Z80 der Meinung ist, es sei nun an der Zeit, den Bildschirm wirklich zu löschen, holt er sich die 2 kB Blanks vom HD ab. Das geschieht analog zur obigen Prozedur: Auf den Stack kommt zunächst die Pufferadresse, hier der Bildschirm, dann das Signalbyte 01h, damit der HD seinen Puffer hinunterkopiert. Es wäre wesentlich simpler gegangen. Das Job-Programm hätte selbständig per DMA auf den Bildschirm zugreifen können, um ihn um Größenordnungen schneller als gewohnt zu löschen. Wie der User mit den vielfältigen Möglichkeiten jongliert, die ihm das EPROM offenläßt, bleibt ihm schließlich selbst überlassen.

    Als weiteres Beispiel wird gern. Listing 3 die Buskontrolle endgültig an den HD 64180 übergeben. Das Signalbyte am unteren Ende des Stack ist 00h. Darüber liegt die Adresse, wo der HD sein kleines Banking-Programm switch ablegen soll. Darüber schließlich findet der HD den Einsprung in seine künftigen Pflichten. Dort wird zunächst aus ihm sozusagen ein Z80 gemacht, damit er mit G-DOS klarkommt. Anwenderprogramme, die er später bearbeiten wird, können mit dem ganzen Spektrum seiner Leistungsfähigkeit arbeiten.

    Es gibt eine fünfte Möglichkeit, wie sich der Z80 des HD bedienen kann. Sie unterscheidet sich von den bisher Genannten so grundlegend, daß sie erst jetzt besprochen werden soll. Nachdem die Methode Stack-orientierter Kommunikation vorn Leser verdaut wurde, ist nun hoffentlich nicht mehr mit Konfusion zu rechnen.
  6. Der Vektor-Task. Bisher mußte das Byte (SP+0) als LSB eines gePUSHten 16-Bit-Worts nicht beachtet werden. Es beginnt, eine Rolle zu spielen, wenn der Inhalt von (SP+1) (gePUSHtes MSB) 02h beträgt. Dies ist das Signalbyte, um eine beliebige Routine zu bearbeiten, deren Adresse man nicht einmal kennen muß. Das Register C enthält nach POP BC die laufende Nummer der gewünschten Routine, von denen es 128 geben darf. Wird beispielsweise gewünscht, daß das Programm Nr. 9 (ab 0 gezählt) läuft, dann wird ein 16Bit-Register vor der Busübergabe mit 0209h geladen und gePUSHt. 02h ist das Signalbyte für diese Art der Ansprache des HD durch den Z80.

Wo dieses Programm zu suchen ist, steht in einer Tabelle. Die Zahl im Register C wird zunächst verdoppelt, weil jeder Vektor (Zeiger) eine Zwei-Byte-Adresse enthält. Nachdem das MSB auf 00h gesetzt ist, steht nun in BC der Summand zur Basisadresse einer Vektortabelle. BC plus Basis ist dann der Zeiger auf die Adresse, an der das gewünschte Programm steht. Das ist dasselbe Prinzip, das bei Interrupts des Modus IM 2 angewendet wird. Ein solches Programm ist in seiner Flexibilität etwa der endgültigen Busübergabe vergleichbar, kehrt aber immer nach waitz80 zurück. Listing 7 zeigt ein Anwendungsbeispiel.

Mit Listing 4 soll gezeigt werden, wie die Trap-Routine des EPROMs vom Z80-RAM aus aufgerufen werden kann. Das Programm wurde mit dem Genie 3s erstellt, dürfte aber wohl auf allen TRS-80-kompatiblen Computern laufen, die kein ROM an den unteren Adressen haben. Andernfalls muß nach einem anderen Speicherabschnitt für die Routine trap Ausschau gehalten werden. Er darf auf keinen Fall die logische Adresse F000h überschreiten.

Die Routine liest die MMU-Register BBR und CBAR aus und puffert ihren Inhalt, damit er später wieder restauriert werden kann. Dann wird Common 1 wie bei Reset definiert (ab 7.F000h), damit die Trap-Routine unter der logischen Adresse F100h gefunden wird. Da (z. B. unter BASIC) der Stack im Himem liegen könnte, darf bei der Bankerei nichts gePUSHt werden; alles wird über Speicherladebefehle abgewickelt.

Dieses Programm stellt die Voraussetzung, daß nur im User-RAM ein TRAP auftreten wird. Das DOS, das auch in seiner eigenen Bank werkelt, benutzt keine Illegals, so daß von dort nichts zu befürchten ist. Der Anwender muß sich eben damit abfinden, daß er bei der Arbeit in einer anderen Bank nur die dokumentierten Befehle verwenden darf. Die ziemlich lendenlahme TRAP-Prozedur legt ohnehin nahe, Illegals durch andere Befehle zu ersetzen.

Es fällt auch auf, daß das TRAP-Bit nicht gelesen wird. Das Programm setzt einfach voraus, daß ein RST 00h nur bei Trap auftritt, denn bei Reset wird ja im Boot-EPROM (neuerdings auch der Coprozessorkarte) gelesen. Auch bei ROM-Computern mit nachträglich eingebautem Banker ist das so. Ein Reset-Taster existiert dort nicht, und nach dem Einschalten ist sowieso das ROM selektiert. Der angebliche RESET dieser Maschinen ist in Wirklichkeit ein NMI und springt bei 0066h ein. Dort steht nach wie vor der alte Code.

Aber unter CP/M wäre wegen des RST 00h bei Warmstart mit CTRL-C die Prüfung auf TRAP unumgänglich. CP/M stellt ebenfalls die Bedingung, unbedingt die Interrupts zu maskieren, weil es im Himem arbeitet, wo nach dem Umschalten nun eine andere Bank vorliegt. Die oben beschriebene Akrobatik zum Ermitteln des EI/DI-Zustandes braucht dabei aber nicht stattzufinden. Es geht auch so, wie im Listing 5 beschrieben. Der intern belegte Port 34h enthält nämlich nicht nur die Bits UFO und TRAP (Bits 6 und 7), sondern auch ITEO-2 (Bits 0, 1, 2). Wenn diese Bits 0 lauten, werden Interrupts an den korrespondierenden INT-Eingängen der CPU ignoriert. Der Input von 34h kann also an den Ausgang geladen, dann manipuliert und chließlich am Ausgang wieder restauriert werden.

Der HD 64180 bietet die Möglichkeit, eine programmierbare Anzahl von WAIT-Zyklen für Speicher- und I/O-Operationen getrennt zu generieren. Man möchte natürlich seine Maschine so schnell wie möglich arbeiten lassen, also so wenige WAITs einfügen, daß das System noch eben zuverlässig arbeitet. Im Listing 6 wird gezeigt, wie man diese Grenze herausfinden kann. Ein Job-Programm verändert in der EPROM-Tabelle initab das Byte für den Port 32h. Es bestimmt die Anzahl der WAITs, und zwar für den Speicher in den Bits 6-7 und für Portansprachen in den Bits 4-5. Nach der Bearbeitung des Jobs wird die Buskontrolle endgültig an den HD 64180 übergeben. Wenn das System mit diesem Programm abstürzt oder eine I/O-Operation nicht das erwartete Ergebnis bringt, war der Akku zu sparsam beladen.

Durch wiederholten Aufruf dieses Programms mit variiertem Wert für den Akku wird experimentell bestimmt, wie schnell höchstens mit der eigenen Hardware gearbeitet werden kann. Der so gefundene Wert sollte dann immer bei der ersten Ansprache des HD nach einem Reset gern. dem Muster in Listing 6 an den Anfang der Tabelle initab gesetzt werden. Mit korrekt geladenem Akku kann dies auch als Programm zur endgültigen Busübergabe genommen werden. Abgesehen vom Signalbyte für den HD und den Adressen, die im Stack übergeben werden, braucht dem Stack dabei keine weitere Aufmerksamkeit gewidmet zu werden, weil der Stackpointer beim Sprung nach DOS-Ready neu geladen wird.

Alle sieben Listings enthalten bereits die Mnemonics einiger zusätzlicher Befehle, die der HD 64180 kennt. Da ZEUS sie jedoch noch nicht kennt, stehen sie wie Kommentare hinter einem Semikolon. Um ZEUS begreiflich zu machen, was auf die Diskette assembliert werden soll, sind die Hex-Entsprechungen dieser Befehle darunter immer als DBs programmiert.

Kein Programm wird jemals fertig. Der Inhalt dieses EPROMs wird sich noch ändern, besonders in den ersten Monaten der Erprobung in der Praxis. Das Listing 1 ist deshalb vielleicht (oder eher wahrscheinlich) zum Zeitpunkt seiner Veröffentlichung bereits überholt. Wer das EPROM für seine eigene Coprozessorkarte haben möchte, erhält die jeweils neueste Version. Ein aktualisiertes Listing wird nicht mitgeliefert. Sein Inhalt kann mit einem Job-Programm in den unteren Speicher kopiert und dort leicht disassembliert werden.

Von den möglichen 64 kB des EPROMs ist mit bisher gerade einem Kilobyte nur ein Bruchteil für die Grundintelligenz der Coprozessorkarte verbraucht. Viele nützliche Routinen, z. B. ein Spooler mit enormem Pufferplatz oder eine intelligente RAM-Floppy, finden hier noch Platz. Zu diesem Zweck sind bestimmte Adressen mit FFh-Bytes freigehalten worden. FFh kann bei einem bereits gebrannten EPROM nachgebrannt werden. So sind beispielsweise noch sämtliche Restart-Adressen frei (außer RST 00h, versteht sich). Auch Füllbytes, die nachfolgende Routinen oder Tabellen auf "gerade" Adressen schieben sollen, lauten FFh und können noch sinnvoll programmiert werden, ebenso die Tabelle der Vektor-Utilities. Dasselbe gilt für den NMI-Einsprung an 0066h, obwohl die Karte nicht für einen NMI im EPROM (sehr wohl im RAM) vorgesehen ist. Aber irgendein Hard-Freak unter euch könnte sich das nachrüsten wollen.


		00001 ;	Listing 1: EPROM-Inhalt der Coprozessorkarte
		00002
		00003	*****************************************************
		00004	Boot-EPROM für die Coprozessorkarte
		00005	mit dem HD 64180
		00006
		00007	für das Genie 3s und die meisten anderen Z80-Rechner
		00008
		00009	von Helmut Bernhardt
		00010
		00011	(C) 1987 Arnulf Sopp
		00012
		00013	die Trap-Routine entspricht weitestgehend der Vorlage
		00014	von Gerald Schröder	(C) 1987
		00015 ;******************************************************
		00016
		00017 ;Puffer für den Stackpointer des Z80 bei Übergabe der Buskontr. an den HD
		00018 ;die Adresse gilt für Computer mit einem Boot-EPROM und RAM ab 0000
0003		00019 spbuff	EQU	0003h		;für ROM-Computer: auf freie RAM-Adresse ändern
		00020
0000		00021		ORG	0000h		;physikalisch 0.0000
		00022
		00023 ;RST 00: nach Reset die MMU des HD 64180 initialisieren usw.
0000 00		00024		NOP			;wegen des Pollings an logisch 0000
0001 ED5E	00025		IM	2		;wegen mögl. Ansprache des Z80 über INT2
0003 214501	00026		LD	HL,initab+2	;Tabelle der internen Port-Initialwerte
0006 1833	00027		JR	gorst00		;dort weiter
		00028
		00029 ;weitere RST-Routinen können nachgerüstet werden
0008 FFFF	00030		DW	Offffh,Offffh,Offffh,Offffh	;Platzhalter für RST 08
0010 FFFF	00031		DW	Offffh,Offffh,Offffh,Offffh	;dto. RST 10
0018 FFFF	00032		DW	Offffh,Offffh,Offffh,Offffh	;dto. RST 18
0020 FFFF	00033		DW	Offffh,Offffh,Offffh,Offffh	;dto. RST 20
0028 FFFF	00034		DW	Offffh,Offffh,Offffh,Offffh	;dto. RST 28
0030 FFFF	00035		DW	Offffh,Offffh,Offffh,Offffh	;dto. RST 30
0038 FF		00036		DB	Offh,Offh,Offh			;dto. RST 38 (JP genügt)
		00037
		00038 ;Fortsetzung der RST 00-Routine (Reset oder Trag)
0038 01340C	00039 gorst00	LD	BC,0c34h		;12 Werte ab Port 34h
		00040 ;		OTIMR				;Initialwerte für interne
003E ED		00041		DB	0edh,93h		;Ports aufsetzen (s. o.)
		00042
		00043 ;Warteprogramm und EPROM-Inhalt per DMA ins Himem transferieren
0040 219800	00044		LD	HL,tabend	;Ende der Wertetabelle für den DMAC
0043 CD8700	00045		CALL	dma2		;per DMA Himem-Programm hochtransferieren
0046 68		00046		LD	L,B		;HL (- 0000 (Anfang EPROM, H=00, BC=0000)
0047 50		00047		LD	D,B		;DE (- 0000 (LSW logische Zieladresse)
0048 58		00048		LD	E,8
0049 3E60	00049		LD	A,60h		;Quellber. 0.xxxx, Zielber. 6.xxxx (MSB)
0048 CD6900	00050		CALL	dma0		;EPROM-Inhalt nach 6.0000-6.FFFF übertr.
004E C33CF0	00051		JP	inidone+offs	;Warteroutine anspringen
		00052
		00053 ;Platzhalter bis zum NMI-Einsprung an 0066
0051 FFFF	00054		DW	Offffh,Offffh,Offffh,Offffh,Offffh,Offffh,Offffh,Offffh
0061 FF		00055		DB	Offh,Offh,Offh,Offh,Offh
		00056
		00057 ;Einsprung für NMI (nicht vorgesehen, kann aber nachgerüstet werden)
0066 FF		00058 nmi	DB	Offh,Offh,Offh	;3 Bytes, können mit JP überschr. werden
		00059
F000		00060 himem	EQU	0f000h		:Common 1, Arbeitsadresse des Programms
EF97		00061 offs	EQU	himem-$		;Offset zur Arbeitsadresse des Programms
		00062
		00063 ;der folgende Teil des EPROM-Inhalts arbeitet im Speicher ab 7.F000
		00064
		00065 ;Unterprogr. für DMA: HL = LSW Quelle, DE = LSW Ziel, BC = Bytezähler,
		00066 ;A Bit 0-2 = MSB der Quelle, A Bit 4-6 = MSB des Ziels
0069 222BF0	00067 dma0	LD	(srclog+offs),HL	;Quellbereich	LSW	x.HL
006C ED532EF0	00068		LD	(dstlog+offs),DE	;Zielbereich	LSW	x.DE
0070 ED4331F0	00069		LD	(dmacnt+offs),BC	;Bytezähler für DMA aufsetzen
0074 F5		00070		PUSH	AF			;phys. Quell- und Ziel-MSB retten
0075 E60F	00071		AND	0fh			;Quellbits maskieren
0077 322DF0	00072		LD	(srcphys+offs),A	;Quellbereich	phys.	A.HL
007A F1		00073		POP	AF			;MSB des Quell- und Zielblocks
0078 OF		00074		RRCA				;in die unteren Bits rotieren
007C OF		00075		RRCA
007D OF		00076		RRCA
007E OF		00077		RRCA
007F E60F	00078		AND	Ofh			;Zielbits maskieren
0081 3230F0	00079		LD	(dstphys+offs),A	;Zielbereich	phys.	A.DE
		00080 ;Einsprung in DMA, falls die Tabelle der Adressen vorbereitet ist 0084
0084 2132F0	00081 dma1	LD	HL,tabend+offs		;Beginn der DMA-Wertetabelle
		00082 ;Bank-unabhängiger Teil des Programms, kann von überallher gecallt werden
0087 012708	00083 dma2	LD	BC,0827h		;8 Werte ab Port 27 abwärts
		00084 ;		OTDMR				;DMAC aufsetzen
008A ED		00085		DB	0edh,9bh		;(s. o.)
008C 013102	00086		LD	BC,0231h		;2 Werte ab Port 31 abwärts
		00087 ;		OTDMR				;Rest aufsetzen u. DMA abschießen
008F ED		00088		DB	0edh,9bh		;(s. o.)
0091 C9		00089		RET
		00090
		00091 ;Init.-Werte des DMAC zur Kopie des Himem-Programms ins RAM ab 7.F000 0092
0092 41		00092		DB	41h	;30.	DSTAT:	Kanal 0, Zustand 'scharf'
0093 02		00093		DB	02h	;31,	DMODE:	Speicher zu Speicher, Burst-Modus
0094 6900	00094 srclog	DW	dma0	:20/1, SAROL/H: Quelladresse	LSW	dma0
0096 00		00095 srcphys	DB	00h	;22,	SAROB:	MSB	0.xxxx
0097 00F0	00096 dstlog	DW	himem	;23/4, DAROL/H: Zieladresse	LSW	F000
0099 07		00097 dstphys	DB	07h	;25,	DAROB:	MSB	7.xxxx
009A 0403	00098 dmacnt	DW prt-dma0	;26/7, BCROL/H: Bytezähler für die Programmlänge
0098		00099 tabend	EQU	S-1	;Ende der Tab.. womit der Zeiger HL geladen wird
		00100
		00101 ;nach der Initialis. auf Ansprache des Z80 warten, sie interpretieren
009C 00		00102 rdyflag	DB	00h		;Flag für noch nicht beendeten Job
009D 21DAF0	00103 waitz80	LD	HL,initab+offs	;Tabelle der internen Port-Initialwerte
00A0 01320E	00104		LD	BC,0e32h	;14 Werte ab Port 32h
		00105 ;		OTIMR			;Initialwerte für interne Ports aufsetzen
00A3 ED		00106		DB	0edh,93h	;(s. o.)
00A5 3EF3	00107 inidone	LD	A,inttab+offs/256	;MSB der Interrupt-Vektortabelle
00A7 ED47	00108		LD	I,A		;ins Interrupt-Vektorregister
00A9 D3FB	00109 copdone	OUT	(0fbh),A	;falls der HD noch die Buskontrolle hat
00AB 3100F1	00110		LD	SP,trap+offs	;Stack im freien Bereich
00AE 210000	00111		LD	HL,0000h	;Prüfadresse (im Z80-RAM unkritisch)
0081 AF		00112	XOR	A			;A (- 00, Flag für beendeten Job
0082 3233F0	00113		LD	(rdyflag+offs),A	;Bereitsch. des HD dort vermerken
0085 FB		00114		EI			;für evtl. Z80-Interrupt
0086 7E		00115 polloop	LD	A,(HL)		;Testbyte laden
0067 B7		00116		OR	A		;immer noch 00? (EPROM noch eingeblendet)
0088 28FC	00117		JR	Z,polloop	;falls noch EPROM
		00118
		00119 ;der Z80 gab soeben die Kontrolle an den HD ab (auch Entry für Z80-INT)
008A F3		00120 intz80 DI	;wegen unklaren Interrupt-Modus usw.
0088 2A33F0	00121		LD	HL,(rdyflag+offs)	;Flag für anhängigen Job
008E 7E		00122		LD	A,(HL)		;Flag laden
00BF 67		00123		OR	A		;ist z. Zt. noch ein Job in Arbeit?
0000 2802	00124		JR	Z,nojob		;falls nicht mehr
0002 ED4D	00125		RETI			;falls ja (nur bei INT vom Z80)
00C4 ED780300	00126 nojob	LD	SP,(spbuff)	;Stackpointer des Z80 übernehmen
		00127
		00128 ;vereinbarte Vorgaben für den Stack des Z80:
		00129 ;	(SP+0):	evtl. Wegweiser in die Utility-Vektortabelle
		00130 ;	(SP+1):	Signalbyte für die Art der übergebenen Aufgabe
		00131 ;
		00132 ;bei endgültiger Übernahme:
		00133 ;	(SP+2), (SP+3): Platz für den Befehlsstring zum Banking
		00134 ;	(SP+4), (SP+5): Startadresse (Entrypoint) für den HD 64180
		00135 :	evtl. darüber : Registerinhalte, die gePOPt werden können
		00136 ;
		00137 ;bei vorübergehendem Job:
		00138 ;	(SP+2), (SP+3): Startadresse (Entrypoint) für den HD 64180
		00139 ;
		00140 ;bei Übergabe von Rechenergebnissen:
		00141 ;	(SP+2), (SP+3): Pufferadresse im Z80-RAM
		00142 ;
		00143 ;bei Speicherkopie von 0.0000-0.FFFF nach 1.0000-3.FFFF im Z80-RAM
		00144 ;und bei Abruf einer programmierten Utility aus der Vektortabelle:
		00145 ;	keine weiteren Daten
		00146 ;
		00147 ;Vereinbarung für das Signalbyte (SP+0) (Register B nach POP BC)
		00148 ;	B:00:	endgültige Übernahme des Systembusses durch den HD 64180
		00149 ; B=01:	2 kB Ergebnisse eines früheren Jobs aus 7.F800 übergeben
		00150 ; B=02:	eine Utility aus der Vektortabelle aufrufen
		00151 ; B=FF:	0.0000-0.FFFF in die 64-kB-Blocks bis 3.FFFF kopieren
		00152 ; FF>8>02: einen sonstigen vorübergehenden Job vom Z80 übernehmen
		00153
0008 C1		00154		POP	BC		;unterste Stack-Ebene lesen
00C9 78		00155		LD	A,B		;Task-Signalbyte vom Z80-Stack
00CA 77		00156		LD	(HL),A		;Flag für anhängenden Job setzen
00CB 3C		00157		INC	A		;A=FF, das RAM 0.0000-0.FFFF kopieren?
00CC 2840	00158		JR	Z,copy		;falls ja
00CE 3D		00159		DEC	A		;A=00, Kontrolle endgültig übernehmen?
00CF 200F	00160		JR	NZ,job		;>00: Job übernehmen o. Ergebn. übergeben
		00161
		00162 ;der Z80 hat im Stack signalisiert, daß er die Kontrolle ganz abgibt
00D1 2173F0	00163		LD	HL,switch+offs	;Befehlsfolge zum Umschalten von Common 1
00D4 D1		00164		POP	DE		;Zieladr. des Umschaltbefehls a. d. Stack
00D5 D5		00165		PUSH	DE		;noch einmal auf den Stack als RET-Adr.
0006 010400	00166		LD	BC,0004h	;Länge der beiden Befehle
00D9 EDBO	00167		LDIR			;übertragen
0008 C9		00168		RET			;banken und Arbeit des Z80 übernehmen
		00169
		00170 switch
		00171 ;		OUT0	(38h),A		;A=00, Common 1 ab 0.0000 einstellen
00DC ED		00172		DB	0edh,39h,38h	;Hex-Entsprechung des Befehls
00DF C9		00173		RET			;an die vom Z80 vorgegebene Adr. springen
		00174
		00175 ;der Z80 hat im Stack signalisiert, daß er für den HD 64180 einen Job hat
		00176 ;er muß mit RET aufhören (wird wie ein Unterprogramm behandelt)
00E0 E1		00177 job	POP	HL		;Einsprungsadresse für den Job
00E1 3100F1	00178		LD	SP,trap+offs	;Stack wieder im eigenen RAM
00E4 E5		00179		PUSH	HL		;Entry des Jobs auf den eigenen Stack
00E5 2134F0	00180		LD	HL,waitz80+offs ;Rücksprungadresse
00E8 E3		00181		EX	(SP),HL		;als RET-Adresse a. d. Steck, HL <- Entry
00E9 E5		00182		PUSH	HL		;Programm-Entry retten
00EA 3D		00183		DEC	A		:A=01, Ergebnisse hinunterladen?
00E8 2816	00184		JR	Z,putres1	;falls ja
00ED 3D		00185		DEC	A		;A=02, eine Utility aus d. Vektortabelle?
00EE 2823	00186		JR	Z,vectask	;falls ja
00F0 210000	00187		LD	HL,0000h	;Quelladresse für DMA x.0000 (LSW)
00F3 54		00188		LD	D,H		;Zieladresse dto.
00F4 5D		00189		LD	E,L
00F5 0100F0	00190		LD	BC,himem	;Größe des zu übertrag. Speicherblocks
00F8 3E70	00191		LD	A,70h		;Quelle phys. 0.xxxx, Ziel 7.xxxx (MSB)
		00192 ;		OUT0	(39h),A		;Bank-Area ebenfalls ab 7.0000
00FA ED		00193		DB	0edh,39h,39h	;(s. 0.)
00FD CD00F0	00194 dmaresl	CALL	dma0+offs	;Speicher hoch- oder runterkopieren
0100 D3FB	00195 vectret	OUT	(0fbh),A	;dem Z80 die Buskontrolle zurückgeben
0102 C9		00196		RET			;evtl. Programm anspr., dann nach waitz80
		00197
		00198 ;der Z80 hat signalisiert, daß er 2 kB Resultate aus 7.F800 abholen will 0103 D1
		00199 putresl	POP	DE		;für Ergebnisse: DE = Zielbereich
0104 2100F8	00200		LD	HL,0f800h	;Puffer für Job-Ergebnisse (Quellbereich)
0107 010008	00201		LD	BC,0800h	;Länge des Puffers immer 2 kB
010A 3E07	00202		LD	A,07h		;Quellbereich 7.xxxx, Zielbereich 0.xxxx
010C 18EF	00203		JR	dmaresl		;Ergebnisse übertragen, zurück zu waitz80
		00204
		00205 ;der Z80 hat signalisiert, 0.0000-0.FFFF nach 1.0000-3.FFFF zu kopieren
010E CDBAF0	00206 COPY	CALL	clone+offs	;den Speicher kopieren
0111 1896	00207		JR	copdone		;zurück in die Warteschleife
		00208
		00209 ;der Z80 ruft einen Task auf, dessen Adresse in der Vektortabelle steht
0113 2197F0	00210 vectask	LD	HL,vectret+offs ;Rückkehradresse für diesen Fall
0116 E3		00211		EX	(SP),HL		;auf den Stack, Nonsense-Adresse löschen
0117 47		00212		LD	B,A		;8 als MSB des Vektorsummanden =00
0118 CB01	00213		RLC	C		;LSB verdoppeln wegen 2 Bytes pro Vektor
011A 2100F2	00214		LD	HL,vectab+offs	;Beginn der Vektortabelle
011D 09		00215		ADD	HL,BC		;Zeiger auf die Task-Adresse stellen
011E 7E		00216		LD	A,(HL)		;das LSB der Adresse laden
011E 23		00217		INC	HL		;auf das MSB stellen
0120 66		00218		LD	H,(HL)		;dieses laden
0121 6F		00219		LD	L,A		;HL - Adresse der Utility
0122 E9		00220		JP	(HL)		;sie anspringen
		00221
		00222 ;UP zur Speicherkopie, das auch unabhängig von A=FF gecallt werden kann
		00223 ;je nach gewünschter Blockgröße, Startbank, Zahl der Banks, Adressen usw.
 		00224 ;Einsprung mit unterschiedl. Registerinhalten an verschiedenen Stellen
0123 010000	00225 clone	LD	BC,0000h	;Bytezähler 0000 : 1.0000 (64 kB)
0126 60		00226		LD	H,B		;Quelladresse LSW 0000
0127 69		00227		LD	L,C
0128 50		00228		LD	D,8		;Zieladresse LSW 0000
0129 59		00229		LD	E,C
012A 3E10	00230		LD	A,10h		;Quelle phys. 0.0000, Ziel phys. 1.0000
0120 ED43CDF0	00231		LD	(cloncnt+offs),BC	;den Zähler aufsetzen
0130 0603	00232		LD	B,03h		;3 Banks zu je 64 kB
0132 C5		00233 coploop	PUSH	BC		;Schleifenzähler retten
0133 E5		00234		PUSH	HL		;dto. LSW Quelladresse
0134 F5		00235		PUSH	AF		;dto. MSB Quell- und Zieladresse
0135 010000	00236		LD	BC,0000h	;Bytezähler 0000 (:1.0000)
0136		00237 cloncnt	EQU	$-2		;kann auf andere Größe geändert werden
0138 CD00F0	00238		CALL	dma0+offs	;eine Bank kopieren
0138 F1		00239		POP	AF		;MSB Quell- und Zieladresse
013C C610	00240		ADD	A,10h		;der nächste phys. 64-kB-Block
013E E1		00241		POP	HL		;LSW Quelladresse
013F C1		00242		POP	BC		;Schleifenzähler
0140 10F0	00243		DJNZ	coploop		;bis 3 64-kB-Blocks kopiert sind
0142 C9		00244		RET
		00245
		00246 ;Initialwerte der internen Ports 32-3F (nach RESET erst ab 34 gelesen)
0143 F0		00247 initab	DB	0f0h		;32, DCNTL: 3 WAITs für Memory und I/O
0144 00		00248		DB	00h		;33, IL: interne INTs ab 00 in der Tabelle
0145 07		00249		DB	07h		;34, ITC: alle ext. Interrupts zugelassen
0146 FF		00250		DB	0ffh		;35: belanglos
0147 83		00251		DB	83h		;36, RCR: Refreshes möglichst zeitsparend
0148 FF		00252		DB	0ffh		;37: belanglos
0149 70		00253		DB	70h		;38, CBR: Common 1 ab 7.x000
014A 00		00254		DB	00h		;39, BBR: Bank-Area ab	0.x000
014B F0		00255		DB	0f0h		;3A, CBAR: Common 0 nicht definiert
		00256					;Bits 0-3: Bank-Area: 0.0000 - 0.EFFF
		00257					;Bits 4-7: Common 1 : 7.F000 - 7.FFFF
014C FF		00258		DB	0ffh		;3B:	belanglos
014D FF		00259		DB	0ffh		;3C:
014E FF		00260		DB	0ffh		;3D:
014F FF		00261		DB	0ffh		;3E:
0150 20		00262		DB	20h		;3F, ICR: interne Ports ab 00, IOSTOP-Modus
		00263
		00264 ;Füllbytes, um trap0 im Himem auf die "gerade' Adresse F100 zu bekommen
		00265 ;hier liegt auch der HD-eigene Stack (max. 12 Ebenen!)
0151	FFFF	00266		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0161	FFFF	00267		DW	0ffffh,0ffffh,0ffffh,0ffffh
		00268
		00269 ;Fehlerbehandlung bzw. Illegal-Simulation nach einem TRAP-Interrupt
0169 F5		00270 trap	PUSH	AF		;Akku und Flags retten
		00271 ;		IN0	A,(34h)		;TRAP-Bit und UF0-Bit einlesen
016A ED		00272		DB	0edh,38h,34h	;(s. o.)
016D E67F	00273		AND	7fh		;TRAP-Bit zurücksetzen
		00274 ;		OUT0	(34h),A		;TRAP löschen
016F ED		00275		DB	0edh,39h,34h	;(s. o.)
0172 3211F1	00276		LD	(ufo+offs),A	;UFO retten
0175 F1		00277		POP	AF		;Akku vom Stack holen
0176 E3		00278		EX	(SP),HL		;HL mit Trapadresse tauschen
0177 F5		00279		PUSH	AF		;Akku wieder retten
0178 C5		00280		PUSH	BC		;retten
0179 3E00	00281		LD	A,00h		;UFO (Dummy-Operand)
017A		00282 ufo	EQU	$-1		;je nach dem
0178 CB77	00283		BIT	6,A		;UF0-Bit gesetzt?
017D 2801	00284		JR	Z,nosub		;nein, Opcode direkt davor
017F 28		00285		DEC	HL		;sonst Zeiger -1
0180 2B		00286 nosub	DEC	HL		;Zeiger auf illegalen 0pcode
0181 7E		00287		LD	A,(HL)		;Opcode laden
0182 47		00288		LD	B,A		;und retten
0183 FECB	00289		CP	0cbh		;CBxx? (SLIA bzw. SLL)
0185 23		00290		INC	HL		;Zeiger weiterstellen
0186 7E		00291		LD	A,(HL)		;2. Byte laden
0187 200D	00292		JR	NZ,ddfd		;falls 0perand mit IX/IY
0189 D620	00293		SUB	20h		;Akku korrigieren
0186 3228F1	00294		LD	(cbxx+offs),A	;in den Programmtext laden
018E C1		00295		POP	BC		;Register restaurieren
018F F1		00296		POP	AF
0190 23		00297		INC	HL		;PC neu setzen
0191 E3		00298		EX	(SP),HL		;als RET-Adresse auf den Stack
0192 37		00299		SCF			;Cy - 1, Bit 0 bei SLIA/SLL immer 11
0193 CB10	00300		RL	B		;rotieren (8 ist Dummy-Operand)
0194		00301 cbxx	EOU	S-1		;0perand für Register B-A
0195 C9		00302		RET
		00303
0196 FECB	00304 ddfd	CP	0cbh		;ein ähnlicher Befehl für IX/IY?
0198 2827	00305		JR	Z,ddfdcb	;falls ja
019A 3252F1	00306		LD	(substit+offs),A	;nächsten 0pc. in d. Programmtext
019D FE26	00307		CP	26h		;LD HX/HY,const ?
019F 2807	00308		JR	Z,ldcon		;falls ja
01A1 FEZE	00309		CP	2eh		;LD LX/LY,const ?
01A3 2803	00310		JR	Z,ldcon		;falls ja
01A5 AF		00311		XOR	A		;keine Konstante: NOP
01A6 1802	00312		JR	gotrap		;dort weiter
01A8 23		00313 ldcon	INC	HL		:Zeiger auf die Konstante
01A9 7E		00314		LD	A,(HL)		;diese laden
01AA 3253F1	00315 gotrap	LD	(const+offs),A	;evtl. Konstante in den Programmtext
01AD 78		00316		LD	A,B		;DD/FD zurück
01AE 324FF1	00317		LD	(ddfd1+offs),A	;in den Programmtext setzen
0181 3255F1	00318		LD	(ddfd2+offs),A	;dort auch
0164 C1		00319		POP	BC		;Register restaurieren
0185 F1		00320		POP	AF
0186 23		00321		INC	HL		;PC neu setzen
0167 E3		00322		EX	(SP),HL		;als RET-Adresse auf den Stack
0188 DDE5	00323 ddfd1	PUSH	IX		;bzw. IY
018A E3		00324		EX	(SP),HL		;HL - IX/IY
0168 7F		00325 substit	LD	A,A		;Operation ausführen (hier Dummy)
016C 00		00326 const	DB	00h		;evtl. Konstante
01BD E3		00327		EX	(SP),HL		;neues IX/IY zurück
018E DDE1	00328 ddfd2	POP	IX		;bzw. IY
01C0 C9		00329		RET
		00330
01C1 78		00331 ddfdcb	LD	A,B		;DD/FD zurück
01C2 32A2F1	00332		LD	(ddfd3+offs),A	;in den Programmtext laden
0105 32A7F1	00333		LD	(ddfd4+offs),A	;dort auch
01C8 23		00334		INC	HL		;Zeiger auf den Offset
01C9 7E		00335		LD	A,(HL)		;.d" aus (IX/IY+d) laden
01CA 32A4F1	00336		LD	(dspl1+offs),A	;in den Programmtext laden
01CD 32A9F1	00337		LD	(dspl2+offs),A	;dort auch
0100 23		00338		INC	HL		;Zeiger auf Operation
0101 7E		00339		LD	A,(HL)		;Befehl laden
0102 E607	00340		AND	07h		;Bits 0-2 maskieren
01D4 3C		00341		INC	A		;+1
0105 47		00342		LD	B,A		;als Zähler
0106 3E3E	00343		LD	A,3eh		;0pcode 46-7E erzeugen
01D8 C608	00344 opclop1	ADD	A,08h		;(46/4E/56/5E/66/6E/7E
010A 10FC	00345		DJNZ	opclop1		;= LD B/C/D/E/H/L/A,(IX+d) )
010C 32A8F1	00346		LD	(load+offs),A	;in den Programmtext
01DF 7E		00347		LD	A,(HL)		;Befehl noch einmal laden
01E0 E6F8	00348		AND	0f8h		;Bits 0-2 löschen
01E2 0F		00349		RRCA			;und herausrotieren
01E3 0F		00350		RRCA
01E4 0F		00351		RRCA
01E5 3C		00352		INC	A		;+1
01E6 47		00353		LD	B,A		;als Zähler
01E7 3EFE	00354		LD	A,0feh		;(06/0E/16/1E/26/2E/3E
01E9 C608	00355 opclop2	ADD	A,08h		;= RLC/RRC/RL/RR/SLA/SRA/SRL/RES/SET ...)
01EB 48		00356		LD	C,B		;NOP erzeugen (für reta)
01EC FE36	00357		CP	36h		;SLIA/SLL ?
01EE 200B	00358		JR	NZ,noslia	;falls nein
01F0 0637	00359		LD	B,37h		;SCF erzeugen
01F2 7E		00360		LD	A,(HL)		;Befehl restaurieren
01F3 FE36	00361		CP	36h		;SLIA/SLL (IX/IY+d) ?
01F5 2002	00362		JR	NZ,nopro	;falls nein, kein Problem
01F7 0EC9	00363		LD	C,0c9h		;Opcode RET für reta
01F9 3E16	00364 nopro	LD	A,16h		;RL erzeugen (statt SLIA/SLL)
01F8 32A5F1	00365 noslia 	LD	(opera+offs),A	;in den Programmtext
01FE 78		00366		LD	A,B		;SCF oder NOP
01FF 32A1F1	00367		LD	(cflag+offs),A
0202 79		00368		LD	A,C		;RET oder N0P
0203 32A6F1	00369		LD	(reta+offs),A
0206 C1		00370		POP	BC		;Register restaurieren
0207 F1		00371		POP	AF
0208 23		00372		INC	HL		;PC korrigieren
0209 E3		00373		EX	(SP),HL		;als RET-Adresse auf den Stack
		00374
020A 00		00375 cflag	NOP			;oder SCF
0208 00		00376 ddfd3	NOP			;DD oder FD für IX/IY
020C C8		00377		DB	0cbh		;immer C8
020D 00		00378 dsp11	NOP			;Displacement d bei (IX/IY+d)
020E 00		00379 opera	NOP			;Operationsanweisung
020F 00		00380 reta	NOP			;RET bei SLIA/SLL
0210 00		00381 ddfd4	NOP			;DD/FD für IX/IY
0211 7F		00382 load	LD	A,A		;Ladebefehl (hier Dummy)
0212 00		00383 dsp12	NOP			;s. dspll
0213 C9		00384		RET			;Simulation des Befehls beendet
		00385
		00386 ;Füllbytes für die "gerade" Adresse F200 der Utility-Vektortabelle
		00387 ;mit freundlicher Empfehlung:
0214 28		00388		DM	'(R) #0000 - (C) 1987 by The HACKT0RY '
0239 48		00389		DM	'Helmut Bernhardt, Arnulf Sopp, Gerald Schröder'
0267 FFFF	00390		DW	0ffffh
		00391
		00392 ;bei F200 die Tabelle der Einsprungsadressen bei vektorisiertem Z80-Task
		00393 ;zunächst alle auf FF (können nachgebrannt oder im RAM programm. werden)
0269 FFFF	00394 vectab	DW	0ffffh,Offffh,Offffh,Offffh,Offffh,Offffh,Offffh,Offffh
0279 FFFF	00395		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0289 FFFF	00396		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0299 FFFF	00397		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
02A9 FFFF	00398		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0289 FFFF	00399		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
02C9 FFFF	00400		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0209 FFFF	00401		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
02E9 FFFF	00402		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
02F9 FFFF	00403		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0309 FFFF	00404		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0319 FFFF	00405		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0329 FFFF	00406		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0339 FFFF	00407		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0349 FFFF	00408		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
0359 FFFF	00409		DW	0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh,0ffffh
		00410
		00411 ;dahinter die Vektortabelle für Interrupts des Typs 2
		00412 :im EPR0M ist nur der Einsprung für einen INT durch den Z80 initialisiert
0369 FFFF	00413 inttab	DW	0ffffh	;für INTs auf den INT1-Pin (nicht init.)
0368 51F0	00414		DW	intz80+offs	;dto. INT2-Pin (INT durch den Z80)
		00415 ;weiter für die internen Interrupts (nicht initialisiert)
		00416 ;(nur bis hierher muß das EPROM gebrannt werden, solange keine weiteren
		00417 ;Interrupt-Vektoren aufgesetzt werden; es folgen vorerst nur FF-Bytes) 036D
FFFF		00418 prt	DW	0ffffh,0ffffh	;dto. PRT Kanal 0 und 1
0371 FFFF	00419		DW	0ffffh,0ffffh	;dto. DMA Kanal 0 und 1
0375 FFFF	00420		DW	0ffffh		;dto. CSI/0
0377 FFFF	00421		DW	0ffffh,0ffffh	;dto. ASCI Kanal 0 und 1
		00422 ;dasselbe für die übrigen externen Interrupts (nicht weiter gelistet)
0378		00423 end	EQU	S		;Hilfslabel für Programmende und -lange
		00424
		00425 ;Hilfslabels zur Kontrolle v. Unterprogramm- u. Tabellenadressen im Himem
F000		00426 dmaabs	EQU	dma0+offs	;Arbeitsadresse des Unterprogramms dma0
F028		00427 dmatabs	EQU	srclog+offs	;dto. Adressentabelle des DMAC
F0BA		00428 clonabs	EQU	clone+offs	;dto. Unterprogramm clone0
F00A		00429 initabs	EQU	initab+offs	;dto. Tabelle der int. Port-Initialwerte
F100		00430 trapabs	EQU	trap+offs	;dto. Trap-Routine
F200		00431 vectabs	EQU	vectab+offs	;dto. Utility-Vektortabelle
F300		00432 inttabs	EQU	inttab+offs	;dto. INT-Vektortabelle
		00433
0000		00434		END
00000 Fehler
cbxx	0194	cflag	020A	clonabs F0BA	cloncnt 0136	clone	0123	const	01BC
copdone	00A9	coploop 0132	copy	010E	ddfd	0196	ddfd1	0188	ddfd2	016E
ddfd3	0206	ddfd4	0210	ddfdcb	01C1	dma0	0069	dmal	0084	dma2	0087
dmaabs	F000	dmacnt	009A	dmaresl 00FD	dmatabs F026	dspl1	020D	dsp12	0212
dstlog	0097	dstphys 0099	end	0378	gorst00 0038	gotrap	01AA	himem	F000
inidone 00A5	initab	0143	initabs F0DA	inttab	0369	inttabs F300	intz80	00BA
job	00E0	ldcon	01A8	load	0211	nmi	0066	nojob	00C4	nopro	01F9
noslia	01FB	nosub	0180	offs	EF97	opclop1 0108	opclop2 01E9	opera	020E
polloop	0066	prt	036D	putresl 0103	rdyflag 009C	reta	020F	spbuff	0003
srclog	0094	srcphys 0096	substit 0168	switch	000C	tabend	0098	trag	0169
trapabs F100	ufo	017A	vectab	0269	vectabs F200	vectask 0113	vectret 0100
waitz80 009D


		00001 ;	Listing 2: Der Z80 gibt dem HD 64180 einen vorübergehenden Job
		00002
0003		00003 spbuff	EOU	0003h	;bei TRS-80 u. ä.: stattdessen RAM-Adresse wählen
		00004
5200		00005		ORG	5200h
		00006
5200 211E52	00007 start	LD	HL,job		;Zeiger auf das Programm für den HD
5203 E5		00008		PUSH	HL		;auf dem Steck ablegen
5204 7C		00009		LD	A,H		;A - irgendetwas zwischen 02 und FE
5205 F5		00010		PUSH	AF		;Signalbyte für 'bitte Job übernehmen!'
5206 ED730300	00011		LD	(spbuff),SP	;dem HD den Stackpointer mitteilen
520A D3F6	00012		OUT	(0fbh),A	;die Buskontrolle an den HD übergeben
		00013
		00014 ;jetzt ist der Z80 für einige Mikrosekunden im Schlummerzustand
		00015 ;der HD holt sich soeben den Job nach oben
		00016 ;danach:
		00017
520C F1		00018		POP	AF		;Stack wieder bereinigen
520D F1		00019		POP	AF		;(es waren zwei Ebenen)
		00020
		00021 ;		CALL	WICHTIG		;inzwischen etwas Wesentlicheres tun
		00022
520E 210038	00023		LD	HL,3800h	;Bildschiradresse (sichtbar ab 3C00)
5211 E5		00024		PUSH	HL		;als Pufferadresse für den HD
5212 3E01	00025		LD	A,01h		;Signalbyte 'bitte Ergebnisse abliefern!'
5214 F5		00026		PUSH	AF		:für den HD auf den Stack
5215 ED730300	00027		LD	(spbuff),SP	;wie oben
5219 D3FB	00028		OUT	(0fbh),A	;Buskontrolle wieder an den HD übergeben
		00029
		00030 ;wieder ist für einen Moment Funkstille, während der der HD die Codefolge
 		00031 ;20h, 20h. 20h, ... (Leerzeichen) in den Bildschirmspeicher lädt
		00032 :der Bildschirm ist jetzt gelöscht
		00033 ;danach:
		00034
5216 F1		00035		POP	AF		;dasselbe wie oben
521C F1		00036		POP	AF
521D C9		00037		RET			;oder was auch immer
		00038
521E 2100F3	00039 job	LD	HL,0f300h	;Datenpuffer für Rechenergebnisse
5221 1101F3	00040		LD	DE,0f301h	;die nächste Stelle
5224 01FF07	00041		LD	BC,07ffh	;Länge 2 k8
5227 3620	00042		LD	(HL),' '	;am Pufferanfang ein Leerzeichen ablegen
5229 EDB0	00043		LDIR			;den ganzen Puffer mit Blanks füllen
5226 C9		00044		RET			;in die Warteschleife zurückkehren
		00045
5200		00046		END	start
00000 Fehler

job  521E	spbuff 0003	start 5200

		00001 ;	Listing 3: Der Z80 gibt die Buskontrolle endgültig an den HD ab
		00002
0003		00003 spbuff	EOU	0003h		;TRS-80 u. ä.: stattdessen RAM-Adresse wählen!
		00004
4200		00005		ORG	4200h		;im Sektorpuffer ist Platz genug
		00006
4200 211042	00007 start	LD	HL,entry	;Ansprungsadresse für den HD 64180
4203 E5		00008		PUSH	HL		;für den HD auf den Stack
4204 211A42	00009		LD	HL,bank		;Platz für das Banking-Programm des HD
4207 E5		00010		PUSH	HL		;dto.
4208 AF		00011		XOR	A		;A - 00, Signal für endgültige Übernahme
4209 F5		00012		PUSH	AF		;dto.
420A ED730300	00013		LD	(spbuff),SP	;dem HD verraten, wo der Stack ist
420E D3FB	00014		OUT	(0fbh),A	;und den Bus endgültig übergeben
		00015
4210 ED56	00016 entry	IM	1		;für die Interrupt-Bearbeitung von G-D0S
4212 3E01	00017		LD	A,01h		;nur noch Interrupts am Pin INT0 zulassen
		00018 ;		OUT0	(34h),A		;Ausgabe auf den INT/TRAP-Port
4214 ED		00019		DB	0edh,39h,34h	;Hex-Entsprechung des Befehls
4217 C32D40	00020		JP	402dh		;DOS-Entry, dort steht z. B. auch EI
		00021
		00022 bank				;Platz für die Banking-Routine des HD
		00023
4200		00024		END	start
00000 Fehler
bank	421A	entry 4210	spbuff 0003	start 4200


		00001 ;	Listing 4: Einsprung in d. Trap-Routine beim Genie 3s unter G-D0S
		00002
0000		00003		ORG	0000h		;RST-00-Routine
0000 C30039	00004		JP	trap		;dort weitermachen
		00005
3900		00006		ORG	3900h		;hier ist z. B. freies RAM
3900 322739	00007 trap	LD	(accbuf1),A	;Akku retten (Stack unklar, kein PUSH!)
3903 ED5F	00008		LD	A,R		;Interrupt-Enable-Flipflop ins P/V-Flag
3905 F3		00009		DI			;Interrupts maskieren
3906 3EF3	00010		LD	A,0f3h		;auf Verdacht: DI
3908 E20D39	00011		JP	P0,intok	;falls die Interrupts disabled waren
3908 3EFB	00012		LD	A,0fbh		;sonst den Befehl EI laden
390D 324439	00013 intok	LD	(eidi),A	;und vor den RET-Befehl patchen
		00014 ;		IN0	A,(38h)		;den Inhalt des CBR lesen
3910 ED		00015		DB	0edh,38h,38h	;(Hex-Entsprechung des Befehls)
3913 323939	00016		LD	(cbrbuff),A	;und p,uffern
3916 3E70	00017		LD	A,70h		;Common 1 des HD ab 7.x000
		00018 ;		OUT0	(38h),a		;auf CBR ausgeben
3918 ED		00019		DB	0edh,39h,38h	;(s. o.)
		00020 ;		IN0	A,(3ah)		;den Inhalt des CBAR lesen
391B ED		00021		DB	0edh,38h,3ah	;(s. o.)
391E 323E39	00022		LD	(cbarbuf),A	;und p,uffern
3921 F6F0	00023		OR	0f0h		;Common 1 des HD ab 7.F000
		00024 ;		OUT0	(3ah),A		;MMU neu konfigurieren
3923 ED		00025		DB	0edh,39h,3ah	;(s. o.)
3926 3E00	00026		LD	A,00		;Akku restaurieren (00 ist Dummy)
3927		00027 accbufl	EQU	$-1		;dort ist der Akku-Inhalt gepuffert
3928 ED733339	00028		LD	(spbuff),SP	;Stackpointer retten
392C 3100F1	00029		LD	SP,0f100h	;Stack im Common-1-Bereich
392F CDD0F0	00030		CALL	0f100h  	;an 7F100 steht die Trap-Routine
3932 310000	00031		LD	SP,0000h	;den alten Stack restaur. (Dummy-Adresse)
3933 		00032 spbuff	EQU	$-2		;je nach dem
3935 324339	00033		LD	(accbuf2),A	;Akku und Flags retten (ohne Stack, s. o)
3938 3E00	00034		LD	A,00h		;alter Inhalt des CBR (00 ist Dummy)
3939		00035 cbrbuff	EQU	$-1		;je nach dem
		00036 ;		OUT0	(38h),A		;den alten Registerinhalt restaurieren
393A ED		00037		DB	0edh,39h,38h	;(s. o.)
393D 3E00	00038		LD	A,00h		;dasselbe für das CBAR
393E		00039 cbarbuf	EQU	1-1
		00040 ;		OUT0	(3ah),A
393F ED		00041		DB	0edh,39h,3ah
3942 3E00	00042		LD	A,00h		;Akku und Flags restaurieren (s. o.)
3943		00043 accbuf2	EQU	$-1
3944 FB		00044 eidi	EI	;oder DI, je nach altem Zustand
3945 C9		00045		RET	;das war's
		00046
0000		00047		END
00000 Fehler
accbufl 3927	accbuf2 3943	cbarbuf 393E	cbrbuff 3939	eidi	3944	intok	390D
spbuff 3933	trap	3900

		00001 ;	Listing 5: möglicher TRAP-Einsprung unter CP/M u. a.
		00002
0000		00003		ORG	0000h
0000 C30300	00004		JP	trap
		00005
		00006 ;wo trap im RAM liegen kann, muß je nach CP/M-Version entschieden werden
		00007 ;		ORG	irgendwo
		00008
		00009 trap
		00010 ;		IN0	A,(34h)		;INT/TRAP-Register lesen
0003 ED		00011		DB	0edh,38h,34h	;(Hex-Entsprechung des Befehls)
0006 CB7F	00012		BIT	7,A		;war das ein TRAP-Interrupt?
		00013 ;		JP	NZ,warmstart	;falls nein (Sprungadresse je nach dem)
0008 E67F	00014		AND	7fh		;TRAP-Bit löschen
000A 321300	00015		LD	(portbuf),A	;den Input von Port 34h puffern
000D AF		00016		XOR	A		;A <- 00, auch die ITE-Bits =0 (wie DI)
		00017 ;		OUT0	(34h),A		;TRAP resetten und Interrupts disablen
000E ED		00018		DB	0edh,39h,34h	;(s. o.)
		00019
		00020 ;hier folgt die Trap-Routine (oder CALL nach 7.F100), und so endet sie:
		00021
0011 F5		00022 exitrap	PUSH	AF		;Akku und Flags retten
0012 3E00	00023		LD	A,00h		;alter Inhalt von Port 34 (Dummy-Operand)
0013		00024 portbuf	EQU	$-1		;Operanden-Byte des Befehls = Port-Inhalt
		00025 ;		OUT0	(34h),A		;Inhalt des Ports restaurieren
0014 ED		00026		DB	0edh,39h,34h	;(s. o.)
0017 F1		00027		POP	AF		;Akku und Flags restaurieren
0018 C9		00028		RET
		00029
0000		00030	END
00000 Fehler
exitrap 0011	portbuf 0013	trap	0003


		00001 ;	Listing 6: Testprogramm für maximale Arbeitsgeschwindigkeit
		00002
0003		00003 spbuff 	EQU	0003h		;TRS-80 u. ä.: auf freie RAM-Adresse ändern!
		00004
4200		00005	ORG	4200h			;das paßt in den Sektorpuffer
		00006
4200 211042	00007 start	LD	HL,entry	:Ansprungadresse für den HD 64180
4203 E5		00008		PUSH	HL		;für den HD auf den Stack
4204 211F42	00009		LD	HL,bank		;Platz für das Banking-Programm des HD
4207 E5		00010		PUSH	HL		;dto.
4208 AF		00011		XOR	A		;A - 00. Signal für endgültige Übernahme
4209 F5		00012		PUSH	AF		;dto.
420A ED730300	00013		LD	(spbuff),SP	;dem HD verraten, wo der Stack jetzt ist
420E D3FB	00014		OUT	(Ofbh),A	;und den Bus endgültig übergeben
		00015
		00016 ;Einsprung bei der endgültigen Busübernahme durch den HD 64180
4210 ED56	00017 entry	IM	1		;für die Interrupt-Bearbeitung von G-DOS
4212 3E01	00018		LD	A,01h		;nur noch Interrupts am Pin INT0 zulassen
		00019 ;		0UT0	(34h),A		;Ausgabe auf den INT/TRAP-Port
4214 ED		00020		DB	0edh,39h,34h	;(Hex-Entsprechung des Befehls)
4217 3E00	00021		LD	A,00h		;oder anderer Wert zwischen Ox und Fx
		00022 ;		OUT0	(32h),A		;Zahl der WAIT-Zyklen auf DCNTL ausgeben
4219 ED		00023		DB	0edh,39h,32h	;(s. o.)
421C C32040	00024		JP	402dh		;DOS-Entry, dort steht z. B. auch EI
		00025
		00026 bank				;Platz für die Banking-Routine des HD
		00027
4200		00028		END	start
00000 Fehler
bank	421F	entry 4210	spbuff 0003	start 4200

		00001 ;	Listing 7: Der vektororientierte Ansprung eines Programms
		00002
		00003 ;Voraussetzung: Zuvor wurden von einem Job-Programm einige Routinen ir
		00004 ;gendwo ab 4.0000 abgelegt, um den Z80-Speicher davon zu entlasten.
		00005 ;Sie können auch im EPR0M fertig vorliegen, müssen dann aber im RAM mit
		00006 ;OUT (0FBh),A beginnen.
		00007 ;Ihre Entrypoints, an denen z. B. zuerst das evtl. notwendige Banking
		00008 ;stattfinden könnte, sind in der Tabelle vectab (7.F200) der Reihe nach
		00009 ;abgelegt worden.
		00010 ;Hier soll nun das Programm Nr. 4 etwas berechnen. Später holt das Progr.
 		00011 ;Nr. 2 die Ergebnisse in den Z80-Speicher, die nicht in den für Resultate
 		00012 ;reservierten Raum ab 7.F800 gepaßt hätten.
		00013
0003		00014 spbuff	EQU	0003h		;TRS-80 u. ä.: auf freie RAM-Adresse ändern!
		00015
5200		00016		ORG	5200h
5200 210402	00017 start	LD	HL,0204h	;Signalbyte 02, Programm Nr. 4 (ab Nr. 0)
5203 E5		00018		PUSH	HL		;auf den Stack für den HD 64180
5206 ED730300	00019		LD	(spbuff),SP	;Stackpointer für den HD ablegen
5208 D3FB	00020		OUT	(0fbh),A	;die Buskontrolle abgeben
		00021
		00022 ;Pause, bis der HD im Prg. Nr. 4 die Kontrolle über den Bus zurückgibt
		00023
		00024 ;		CALL	WICHTIG		;dann z. B. Bedeutendes vollbringen ...
		00025
520A D1		00026		POP	DE		;altes Signalbyte (Registerpaar egal)
520B 1E02	00027		LD	E,02h		;Programm Nr. 2 (MSB ist noch richtig)
520D E5		00028		PUSH	HL		;Anforderung für den HD auf den Stack
520E D3FB	00029		OUT	(0fbh),A	;wie oben (korrekter SP noch in 7FFE)
		00030
		00031 ;wieder ein paar Millisekunden Pause ..., dann geht es irgendwie weiter.
		00032
5200		00033		END	start
00000 Fehler
spbuff 0003	start 5200