Der ST wird handgreiflich: Drucker-Port als Beispielanwendung! Teil 2

Nachdem im ersten Teil des Artikels die Hardware des Userports vorgestellt wurde, soll nun gezeigt werden, wie eine konkrete Anwendung umgesetzt und in das Betriebssystem eingebunden wird. Dabei dient eine zweite parallele Druckerschnittstelle als Beispiel. Ein zentraler Punkt der Treiber-Software ist die Interrupt-Programmierung des 6532.

Im Unterschied zum Original-Drucker-Port wird diese Schnittstelle von einem Pufferspeicher unterstützt. Dadurch kann die Ausgabe der Daten im Hintergrund erfolgen, nachdem sie in den Pufferspeicher übertragen wurden. Ein willkommener Nebeneffekt besteht darin, daß Ausdrucke auch möglich sind, wenn Eprommer oder GALprommer am ST-Drucker-Port residieren. Ein Untertauchen im üblichen Kabelsalat entfällt.

Die Software wird einmal resident gestartet und bindet dabei die Schnittstelle vollständig ins BIOS ein. Die Größe des Pufferspeichers und die Nummer, unter der das BIOS die Schnittstelle anspricht, werden entsprechend der Datei SPOOLER.CNF bestimmt. Die Zeicheneingabe über diesen Port ist nicht vorbereitet und kann bei Bedarf leicht ergänzt werden.

Hardware - nur die Kabel löten!

Es ist wenig zusätzliche Hardware nötig. Die acht Leitungen des Ports B werden -als Datenleitungen - mit den entsprechenden Pins der Anschlußbuchse verbunden. Das Strobe-Signal stellt der Ausgang PA6 zur Verfügung. Wegen der Interrupt-Möglichkeiten wird PA7 als Busy-Eingang vorgesehen (s. Ergänzungen zum ersten Teil). Dieser Ausgang ist in Druckern als Open-Collector-Treiber ausgelegt und muß deshalb rechnerseitig über einen 4,7kΩ-Widerstand mit +5V verbunden werden (s. Schaltbild). Der Anschluß erfolgt direkt am Userport oder an BS2 des Busmonitors.

So sollte die Druckerschnittstelle verdrahtet werden.

Die Software - gut sortiert ist halb gewonnen!

Bei der Einbindung der Schnittstelle ist es besonders wichtig, daß für die Applikationen kein Unterschied zur Standardschnittstelle auftritt. Der Einsprung in die neuen Routinen sollte deshalb auf einer möglichst maschinennahen Ebene erfolgen. Optimal scheint hier der BIOS-Trap. Alle Aufrufe der normalen Druckerschnittstelle durchlaufen diesen Trap, um über bcostat und bconout Daten an den Drucker zu übertragen.

Der Trap kann nach dem XBRA-Protokoll leicht auf die eigenen Treiber umgelenkt werden. Die Übersichtsgrafik verdeutlicht die weitere Gliederung des Treiberprogramms.

Der BIOS-Teil greift lediglich auf Routinen zu, die den Pufferstatus ermitteln oder ein Byte an den Puffer schicken. Der Puffer tritt also für die Applikation und damit auch für den Anwender nicht in Erscheinung.

Die Ausgabe aus dem Puffer an den Drucker erfolgt interruptgesteuert, wobei Druckerstatus und Pufferzustand das Verhalten dieser Routine beeinflussen.

Auch hier erfolgt noch kein direkter Zugriff auf die Register des 6532. Zur Ermittlung des Pufferzustands wird lediglich das entsprechende Unterprogramm aufgerufen. Es bildet zusammen mit der eigentlichen Ausgabe und der Initialisierung den einzigen hardwareabhängigen Teil des Treibers.

Dieses Vorgehen der funktionalen Aufgliederung bietet bei der Programmierung diverse Vorteile. So legt man sich automatisch ein Programmkonzept zurecht, anhand dessen man Schritt für Schritt die Implementation vornimmt.

Sind die Schnittstellen zwischen den einzelnen Ebenen, also die Funktionsaufrufe und deren Parameter, festgelegt, ist es egal, in welcher Reihenfolge die einzelnen Teile implementiert werden. Genauso kann das Projekt auf mehrere Programmierer aufgespalten werden.

Als Zugabe erhält man noch die Möglichkeit, durch geringe Änderung einzelner Teile das Programm an geänderte Anforderungen anzupassen. Änderungen der registerabhängigen Programmteile, das heißt: der Interrupt-Generierung und der Funktionen GET_STATE und PUT_BYTE, ermöglichen die Bedienung anderer Hardware, wie z.B. die der Standardschnittstelle. Will man keinen Puffer installieren, ruft man aus der BIOS-Ebene heraus direkt die Routinen auf, die die Register bedienen. Kurz gesagt, das Programm wird zu vertretbar hohem Aufwand wartbar.

Die Software installiert sich voll im BIOS

Die parallele Druckerschnittstelle!

Die parallele Druckerschnittstelle dient der parallelen Übertragung von Daten vom Rechner zum Drucker. Das Grundprinzip der Schnittstelle soll hier kurz Umrissen werden.

Im Atari ST ist nur der notwendigste Teil dieser Schnittstelle verwirklicht. Dies hat zur Folge, daß die Kommunikation über nur zwölf Leitungen erfolgt. Wie schon erwähnt, werden die Daten byteweise parallel übertragen. Dazu werden acht Datenleitungen benötigt. Die eigentliche Übertragung der Druckdaten wird synchronisiert, d.h. Sender und Empfänger stimmen Zeitpunkt und Geschwindigkeit der Übertragung aufeinander ab. Diese Abstimmung wird mit Hilfe der zwei Signalleitungen -STROBE und BUSY realisiert. Dabei ist -STROBE ein Signal vom Rechner zum Drucker und BUSY eines vom Drucker zum Rechner.

Das Timing der Steuersignale.

Will der Rechner ein Byte an den Drucker schicken, muß er zunächst warten, bis der Drucker Daten empfangen kann. Dies wird ihm vom Drucker durch OV auf der BUSY-Leitung gemeldet. Er darf nun neue Daten auf den Bus legen und wartet, bis sich die Signale stabilisiert haben. Mit Hilfe eines kurzen Impulses auf der -STROBE-Leitung fordert er den Drucker auf, diese Daten zu verarbeiten. Damit die Daten vom Drucker übernommen werden können, müssen sie auch noch nach dem Strobe-Impuls stabil anliegen. Die Mindestzeiten für einen NEC P6 Plus sind im Signalplan angegeben.

Als Bestätigung der Datenübernahme aktiviert der Drucker die BUSY-Leitung solange, bis er erneut bereit ist, Daten zu verarbeiten.

Das Programm

Den Rahmen des Programms bildet der Teil SPOOL_AUT.C. Er liest die Datei SPOOLER.CNF ein, um das globale Setup entsprechend einzustellen. Das Format der Datei ist simpel. leitet Kommentare ein, nach 'R’ wird die Größe des Pufferspeichers dezimal in KByte angegeben und ‘N 2’ bestimmt die neue Schnittstelle als BIOS-Gerät Null.

Durch Aufruf von B_INIT wird der Speicher für den Puffer reserviert und gleichzeitig die Ringpufferstruktur angelegt. Nach dem Aufruf von INIT_RES und INIT_PORT terminiert das Programm, ohne jedoch den Speicher freizugeben.

INIT_RES lenkt den BIOS-Trap auf die eigene Routine NEW 13 um. Sie wird automatisch bei jedem BIOS-Aufruf durchlaufen. Um feststellen zu können, ob eine der Druckerfunktionen aufgerufen werden soll, kontrolliert sie die auf dem Stack abgelegten Parameter. Dazu wird die Adresse des ersten Parameters bestimmt und dieser dann mit den relevanten Funktionsnummern verglichen. Bei Übereinstimmung erfolgt ein Sprung zur entsprechenden, neuen Routine. Handelt es sich um eine andere Funktion, wird der Aufruf unverändert an den alten BIOS-Trap weitergereicht.

Bauteilliste

1 20pol. Buchsenleiste
1 11 pol. Kabel
1 25pol. Sub-D-Buchse
1 4,7 kΩ Widerstand

Sowohl NBCOSTAT als auch NBCONOUT prüfen den Aufruf auf die im Setup eingestellte Gerätenummer. Bei erneuter Übereinstimmung liefert NBCOSTAT den Wert von B_STAT, und NBCONOUT gibt ein Zeichen an den Puffer aus. Ist der Puffer nicht voll, genügt dazu der Aufruf von B_PUT_BYTE. Andernfalls muß zunächst gewartet werden, bis ein Byte aus dem Puffer entfernt wurde. Dies geschieht - gesteuert durch einen Timerinterrupt - nach einiger Zeit automatisch, es sei denn, der Drucker ist nicht bereit.

Nach der Initialisierung des Port-Bausteins installiert INIT_PORT die notwendige Routine. Da der Interruptlevel 3 von diversen Programmen gesperrt wird, war es leider nötig, hierfür das Level 5 zu wählen. Dies erfordert eine kleine Änderung der Userport-Schaltung (s. Ergänzungen zum ersten Teil). Im Anschluß an die Installation wird der Timerinterrupt freigegeben.

Tritt ein solcher Interrupt auf, wird versucht, mit B_SEND_BYTE ein Zeichen auszugeben. Der Rückgabewert von B_SEND_BYTE beschreibt den Zustand des Puffers. Dadurch kann bei leerem Puffer der Timer mit dem hohen Wert W_TIME geladen werden, um weniger Rechenzeit auf unnötige Interrupts zu verschwenden. Befinden sich Zeichen im Puffer, wird der Timer mit dem niedrigen Wert TIME geladen. Auf diese Weise verringert sich die Zeit zwischen zwei Interrupts, und die Ausgabe an den Drucker wird beschleunigt.

Um zu verhindern, daß der Rechner bei B_SEND_BYTE in einer Schleife auf z.B. einen Drucker ohne Papier wartet, wird vor dem Aufruf von PUT_BYTE der Druckerstatus über GET_STATE getestet. Ist der Drucker auch nach MAXCOUNT Versuchen nicht empfangsbereit, wird die aktuelle Ausgabe abgebrochen und beim nächsten Interrupt erneut versucht.

Optional kann man bei älteren Druckern, die etwas langsamer arbeiten, die Busy-Leitung ebenfalls per Interrupt überwachen. Dazu wird der Flanken-Interrupt in INIT_PORT und NEW_INT bei jedem Laden des Timers mit MOVE.B #$00,PA7N_T freigegeben und in NEW_INT vor dem Aufruf von B_SEND_BYTE mit MOVE.B READI_F,DUM gelöscht.

Nimmt nun der Drucker das Busy-Signal weg, ist also empfangsbereit, tritt eine negative Flanke auf. Sie erzeugt einen zusätzlichen Interrupt, der sofort die Ausgabe des nächsten Zeichens veranlaßt.

Weitere Beschleunigungen lassen sich über die Werte W_TIME und TIME erreichen. Sie sind stark vom Druckertyp abhängig. Die Puffergröße muß ebenfalls an die jeweiligen Anforderungen angepaßt werden. Gute Werte ermittelt man schon nach wenigen Versuchen.

Beim Compilieren der Dateien ist es wichtig, daß weder CDECL-CALLING noch PASCAL-CALLING aktiv sind. Beim Aufruf von C-Routinen aus dem Assembler und umgekehrt wird die Parameterübergabe entsprechend der TC-Konventionen vorgenommen.

Ich hoffe, der Bau des Userports wird Sie anregen, eigene Schaltungen zum Messen, Steuern und Regeln zu entwickeln. Bei Fragen oder Vorschlägen können Sie sich auch direkt mit mir in Verbindung setzen.

Stephan Neikes Grünstädterstr. 2 W-6529 Monsheim

Literaturhinweis:

[1] Abelson.Sussman; Structure and Interpretation of Computer Programms; 1985 MIT-Press

Ergänzungen zum ersten Teil!

Es hat sich herausgestellt, daß die Interrupt-Erzeugung durch kleine Änderungen so gestaltet werden kann, daß der 6532 Interrupts der Level 3,5 oder 7 generieren kann. Dazu führt man die Interrupt-Leitung des 6532 über ein Jumper-Feld an nun drei Eingänge des GALs (Pin 3, 13, 15) und an die Megaslot-Eingänge -INT3, -INT5 und -INT7 (s.Schaltbild). Das GAL muß entsprechend dem Listing Steuer5.3 neu programmiert werden. Diese Änderung ist leider auch für den Betrieb des Pufferspeichers nötig.

Es wurde nicht ausdrücklich erwähnt, daß die Treiber im Busmonitor die Signale zum Ausgang BS3 hin invertieren. Soll eine Peripherieschaltung für nichtinvertierte Ausgänge betriebenwerden, können dieTreiber gegen solche vom Typ 74LS07 ausgetauscht werden. In diesem Fall erfolgt jedoch die Anzeige des Buszustandes invertiert.


Projekt: Userport Gal: STEUER 5.1 Datum: 3.11.91 Funktion: Steuer und Interruptlogik %ID Steuer53 %TYP GAL16V8 %PINS NC -VMA -INT3 FC2 FC1 FC0 A1 A2 A3 NC -LDS -INT5 -INT7 --VPA -CS2 NC A0 -AS %LOGIC !-CS2 = A0 * !FC1 * FC0 * !-LDS * !-VMA; --VPA = A0 * !-LDS + !-INT3 * !-AS * FC2 * FC1 * FC0 * A1 * A2 * ! A3 + !-INT5 * !-AS * FC2 * FC1 * FC0 * A1 * !A2 * A3 + !-INT7 * !-AS * FC2 * FC1 * FC0 * A1 * A2 * A3; %END

Das neue Listing für die geänderte GAL-Programmierung


/************************************************ * Routinen zum Einhängen in die Bios-Ausgabe * * Interrupt Verfahren * * (c)1991 by MAXON-Computer * * Autor: S.Neikes * ************************************************ * Dateiname: SPOOL_AUT.C * ************************************************/ #include <stdlib.h> #include <string.h> #include <stdio.h> #include <tos.h> /************************************************ * Defines * ************************************************/ #define FIRST 0 #define INST "SPOOLER.CNF" #define MAXCHAR 80 #define _SYSBASE 0x4F2L /************************************************ * Typendefinition * ************************************************/ #include <types.h> /************************************************ * Prototypen * ************************************************/ #include <spool_pt.h> /************************************************ * Globale Variablen * ************************************************/ install setup; char buffer[MAXCHAR]; volatile mem_block block1; extern long strobe_t = 32; extern int _app; extern long _PgmSize; /************************************************ * Main * ************************************************/ int main (void) { extern _app; int wert =-1; if (test_acc()==TRUE) { printf(" Treiber für LPT2!\n”); printf(" läuft nicht als ACC!\n"); gets(buffer); } else { wert = (int)do_prg(); gets(buffer); Ptermres (_PgmSize,wert); } return(wert); } /************************************************ * test_acc * ************************************************/ boolean test_acc(void) { if (_app == 0) { return(TRUE); } else { return(FALSE); } } /************************************************ * do_prg * ************************************************/ boolean do_prg (void) { long ssp; init_data(); ssp = Super(0L); init_res(); /* BIOS-Vekt. verbiegen */ init_port(); /* INT-Vektor verbiegen */ Super((void*)ssp); return (TRUE); } /************************************************ * init_data * ************************************************/ boolean init_data (void) { boolean wert =TRUE; if (load_inst() != TRUE) { setup.resi=64; setup.norm=1; } wert = b_init(); return (wert); } /************************************************ * load_inst * ************************************************/ boolean load_inst (void) { FILE *datei; int zeichen; if ((datei = fopen(INST,"r")) != NULL) { rewind(datei); while ((zeichen=getc(datei)) != EOF) { switch (Zeichen) { case 0x20: fgets( buffer, MAXCHAR, datei); break; case 0x2A: fgets( buffer, MAXCHAR, datei); break; case 0x52: setup.resi=(unsigned long) atoi(fgets( buffer, MAXCHAR, datei)); break; case 0x4E: setup.norm= atoi(fgets( buffer, MAXCHAR, datei)); break; default: break; } } if(fclose(datei)==0) return(TRUE); else { printf("Fehler beim Schließen der Datei:\n"); printf("SPOOLER.CNF\n"); return(FALSE); } } else return(FALSE); } /************************************************ * reset_data * ************************************************/ void reset_data (void) { b_del(); } /************************************************ * lpt_devn * * liefert akt. Dev.Nr. von LPT2 * * zum Aufruf durch Assembler * ************************************************/ int lpt_devn (void) { if (setup.norm ==1) return(setup.alt); else return(0); }

Listing 1


/************************************************ * Routinen zum Verwaltung eines Spooler-Puffers* * (c) 1991 by MAXON-Computer * * Autor: S.Neikes 19.11.91 * ************************************************ * Dateiname: BUFF.C * * Datum: 20.11.91 * * Version: 0.10 * ************************************************/ #include <tos.h> #include <stdlib.h> #include «string.h> #include <stdio.h> #include <math.h> /************************************************ * Defines * ************************************************/ #define MAXCOUNT 5 /************************************************ *Typendefinition * ************************************************/ #include «types.h> /************************************************ * Prototypen * ************************************************/ #include <spool_pt.h> /************************************************ * Variable * ************************************************/ extern mem_block block1; extern install setup; /************************************************ * b_init * ************************************************/ boolean b_init (void) { boolean wert; ldiv_t erg; wert=TRUE; if((block1.Start = (char*)malloc((size_t)(setup.resi*1024L))==NULL) { printf("Nicht genug Speicher in 'b_init'!\n"); erg=ldiv((long)Malloc(-1L),1024L); setup.resi=erg.quot; block1.start=(char*)malloc(setup.resi*1024L); printf("Reserviert wurden %u Kbyte.\n",(int)setup.resi); } else { printf("Spooler entsprechend\n"); printf("der Datei 'Spooler.cnf'\n"); printf("installiert!"); } block1.write=0; block1.read=0; block1.end = setup.resi * 1024L; return(wert); } /************************************************ * b_del * ************************************************/ void b_del (void) { if (block1.start!=0) free(block1.start); } /************************************************ * b_stat * ************************************************/ long b_stat (void) { if ((block1.read==0) && (block1.write+1==block1.end)) return(OL); /* voll */ if ((block1.write+1==block1.read)) return(0L); /* voll */ return(-1L); /* beschreibbar */ } /************************************************ * b_put_byte * ************************************************/ void b_put_byte (char Zeichen) { if ((block1.write==block1.read) && (get_state()==(-1L))) put_byte(Zeichen); else { while (b_stat()==(0L)) /* voll? */ b_send_byte(); /* dann warten */ memcpy((block1.start+block1.write++),&zeichen,1); /* byte in Puffer */ if ((block1.write)==block1.end) /* Test auf Pufferende */ block1.write=0; } } /************************************************ * b_send_byte * ************************************************/ long b_send_byte (void) { int count; if (block1.read==block1.write) return(0); else { count=MAXCOUNT; while (count--!=0) if (get_state() ==-1L) { put_byte((int)*(block1.start+block1.read++)); if (block1.read==block1.end) block1.read=0; } return(-1); } }

Listing 2


************************************************** * Routinen zum Einhängen in die Bios-Ausgabe * * Interrupt Verfahren * * (c)1991 by MAXON-Computer * * Autor: S.Neikes 29.11.91 * ************************************************** * Dateiname: BIOS_AUT.S * ************************************************** EXPORT init_res ; Initialisierung EXPORT exit_res ; Aushängen IMPORT lpt_devnr ; NR. der LPT2 Schnittst. IMPORT get_state ; Aus Spool_po.s IMPORT put_byte IMPORT b_put_byte ; Pufferroutinen IMPORT b_stat IMPORT b_send_byte TEXT ************************************************** * init_resm * ************************************************** SUPER init_res: move.w sr,d0 ; sr retten ori.w #$0700,sr ; Int-level auf 7 move.w #1,fakeflag ; 13 ansehen move.l $B4,old13 ; Trap13 umlenken move.l #new13,$B4 move.w d0,sr ; Int-Lev. rts USER ************************************************** * (void) exit_resm (void);" * ************************************************** SUPER exit_res: move.w sr,d0 ; sr retten or.w #$0700,sr ; Int-level auf 7 move.w #0,fakeflag ; alter Trap13 move.w d0,sr ; Int-level rts USER ************************************************** * Neue Trap #13-Routine (BIOS) * ************************************************** SUPER dc.b "XBRA" ; XBRA-Struktur dc.l "lpt2" ; Kennung old13: dc.l 0 ; Alter Vektor ; f. Trap #13 new13: tst.w fakeflag beq nixtun fake: move.l a7,a0 move.w (a0),d0 ; Status laden btst #13,d0 ; Supermode? bne supmod ; Ja! move.l usp,a0 ; Prg-Parameter subq.l #6,a0 ; holen... supmod: addq.l #6,a0 ; Entweder USP o. ; SSP nach S-Bit move.w (a0),d0 ; gewählte Fktnr. cmp.w #$01,d0 ; Bconstat? beq nbconstat ; Ja => fälschen cmp.w #$02,d0 ; Bconin? beq nbconin ; Ja => fälschen cmp.w #$03,d0 ; Bconout? beq nbconout ; Ja => fälschen cmp.w #$08,d0 ; Bcostat? beq nbcostat ; Ja => fälschen nixtun: move.l old13(pc),a0; normal weiter jmp (a0) ************************************************** * Neue bconout Routine (BIOS 3) * ************************************************** nbconout: movem.l d0-a7,-(SP) bsr lpt_devnr ; devnr in d0 move.w (a0)+,d1 ; => Funkt.Nr. cmp.w (a0)+,d0 ; => DEV_NR == ; devnr? beq tst_start ; das sind wir movem.l (SP)+,d0-a7 bra nixtun ; => dann BIOS tst_start: movem.l d0-a7,-(SP) tst_loop: bsr b_stat ; Puffer voll? bmi tst_end ; nein? bra tst_loop ; => warten tst_end: movem.l (SP)+,d0-a7 move.w (a0)+,d0 ; =>char nach d0 bsr b_put_byte ; Ausgabe Puffer movem.l (SP)+,d0-a7 ; fertig rte ************************************************* *Neue bconstat Routine (BIOS 1) * ************************************************* nbconstat: bra nixtun ; gibt's nicht ************************************************* *Neue bconin Routine (BIOS 2) * ************************************************* nbconin: bra nixtun ; gibt's nicht ************************************************* *Neue bcostat Routine (BIOS 8) * ************************************************* nbcostat: movem.l d1-a6,-(a7) bsr lpt_devnr ; devnr in d0 move.w (a0)+,d1 ; => Funkt.Nr. cmp.w (a0)+,d0 ; => DEV_NR == ; devnr? beq test_stat ; das sind wir movem.l (SP)+,d1-a6 bra nixtun ; nein? => BIOS test_stat: bsr b_stat ; Status in D0 movem.l (SP)+,d1-a6 ; fertig rte ********************************* * Variablenbereich (resident) * ********************************* DATA fakeflag: dc.w 0 END

Listing 3


************************************************* * Basisroutinen Druckerport * * Interrupt Verfahren * * (c)1991 by MAXON-Computer * * Autor: S.Neikes 29.11.91 * ************************************************* * Dateiname: SPOOL_INT.S * ************************************************* ************************************************* * Globals * ************************************************* EXPORT init_port ; Ports und Interrupts ; initialisieren EXPORT reset_port ; 6532 entschärfen EXPORT get_state ; LPT 2 Status ; do = 0 => BUSY ; d0 = -1 => FREI EXPORT put_byte ; Byte ausgeben ; dabei Strobe Pulse ; mit Länge strobe_t EXPORT strobe_t ; 1 für Schleifenzahler IMPORT b_send_byte ************************************************* * Defines * ************************************************* INCLUDE "6532_S.h" INT EQU $74 ; Level 5 W_TIME EQU $30 ; *64*l/800sec ; = Zeit Puffer leer TIME EQU $04 ; *64*l/800sec ; = Zeit Puffer voll TEXT ************************************************* * void init_port (void) * ************************************************* SUPER init_port: move.w sr,d0 ; sr retten ori.w #$0700,sr ; Int-level auf 7 move.b #$ff,DDR3 ; Port B Ausgang move.b #$ff,ORA ; STOBE auf '1' move.b #$40,DDRA ; Port A Bit 6 ; (STROBE) Ausg. move.b #$00,PA7N_F ; pa7 irq aus move.b READT_F,dum ; timer irq aus move.b READI_F,dum ; irq löschen move.l INT,oldint ; Int-Vektor ; umlenken move.l #newint,INT set_timer: move.b #TIME,T0064_T; timer laden ; irq an andi.w #$F5FF,d0 ; Int-lev. 5 move.w d0,sr rts ************************************************* * Die Interrupt Routine * ************************************************* Xbra: dc.l 'XBRA' ; XBRA dc.l 'LPT2' ; lpt2 Interrupt oldint: dc.l 0 ; alter Vektor newint: movem.l d0-a6,-(sp) ; register retten bsr b_send_byte ; Ausgabe Buffer set_ints: or.l #$0000,d0 ; Zeichen weg? bne fast ; JA! move.b #W_TIME,T0064_T ; Nein! ; clr timer irq ; reload (slow) bra cont fast: move.b #TIME,T0064_T ; clr timer ; irq / reload cont: movem.l (sp)+,d0-a6 ; register zurück rte ; ende irq ************************************************* * void reset_port (void) * ************************************************* .even reset_port: move.b #$00,ORB ; Bits auf '0' move.b #$00,DDRB ; Port B Input move.b #$00,DDRA ; Port A Input move.b #$00,ORA ; Bits auf '0' move.b #$00,PA7N_F ; pa7 irq aus move.b READT_F,dum ; timer irq aus move.b READI_F,dum ; irq löschen rts ************************************************* * long get_state (void);" * ************************************************* .even get_state: move.b ORA,D0 ; Status in D0 and.b #$80,D0 ; Busy maskieren bne busy free: move.l #-1,D0 ; D0 =-1 => frei rts busy: clr.l D0 ; D0 = 0 => busy rts ************************************************* * void put_byte (int daten) * ************************************************* put_byte: movem.l d0-a6,-(sp) warten: jsr get_state ; warten cmp.l #-1,D0 ; bis Port bne warten ; frei ist movem.l (sp)+,d0-a6 move.b D0,ORB ; byte in PB move.b #$00,ORA ; STROBE setzen move.l strobe_t,D0 loop: dbeq D0,loop move.b #$40,ORA ; STROBE reset rts ************************************************* * Variablen Bereich * ************************************************* DATA strobe_t: dc.l $40 ; Zähler für Strobe- ; Warteschleife dum: dc.w $00 ; dummy_Var END

Listing 4


/************************************************ * Typen des Projets Spooler * * (c)1991 by MAXON-Computer * * Autor: S.Neikes 20.11.91 * ************************************************* * Dateiname: TYPES.H * * Datum: 20.11.91 * * Version: 0.10 ************************************************* typedef enum { FALSE, TRUE } boolean; typedef struct { char *start; long write, read, end; int status; } mem_block; typedef struct { unsigned long resi; int maxi, norm, alt; } install;

Listing 5


/*********************************************** * Prototypen des Spooler Projektes * * (c)1991 by MAXON-Computer * * Autor: S.Neikes 1.12.91 * *********************************************** * Dateiname: SPOOL_PT.H * * Datum: 1.12.91 * * Version: 0.10 * ***********************************************/ /*********************************************** * Spooler.C * ***********************************************/ /************ Main *****************************/ int main (void); boolean test_acc (void); /*********** Do_prg ****************************/ boolean do_prg (void); boolean init_data (void); void reset_data (void); boolean load_inst (void); /*********** Hilfe *****************************/ int lpt_devn (void); /*********************************************** * Buff.C * ***********************************************/ boolean b_init (void); /* initialisiert block1 TRUE => erfolgreich FALSE=> Fehler */ void b_del (void); /* entfernt block1 */ long b_stat (void); /* Status des Puffers 0 => Puffer ist voll -1 => noch Platz */ void b_put_byte (char); /* Byte in block1 */ long b_send_byte (void); /* Byte aus block1 LPT2 0 => Puffer war leer -1 => Zeichen weg */ /*********************************************** * Spool_int.S * ***********************************************/ boolean init_port (void); void reset_port (void); int get_state (void); /* LPT 2 Status */ /* d0 = 0 => BUSY */ /* d0 = -1 => FREI */ void put_byte (int); /*********************************************** * Bios_aut.S * ***********************************************/ void init_res (void); void exit_res (void);

Listing 6


************************************************ * Include Datei 6532 Register * * (c)1991 by MAXON-Computer * * Autor: S.Neikes 29.11.91 * ************************************************ * Dateiname: 6532_S.H * ************************************************ ************************************ * Definition der Register des 6532 * ************************************ RAM EQU $FF0000 ORA EQU $FF0101 DDRA EQU $FF0103 ORB EQU $FF0105 DDRB EQU $FF0107 T0001_T EQU $FF0139 T0008_T EQU SFF013B T0064_T EQU $FF013D T1024_T EQU $FF013F T0001_F EQU $FF0129 T0008_F EQU $FF012B T0064_F EQU $FF012D T1024_F EQU $FF012F READT_T EQU $FF0119 READT_F EQU $FF0109 READI_F EQU $FF010B PA7N_F EQU $FF0109 PA7P_F EQU $FF010B PA7N_T EQU $FF010D PA7P_T EQU $FF010F

Listing 7


;************************************************ ;* Projektdatei * ;* zweite Druckerschnittstelle mit Puffer * ;* (c)1991 by MAXON-Computer * ;* Autor: S.Neikes 29.11.91 * ;************************************************ ;* Dateiname: SPOOLAUT.PRJ * ;************************************************ spoolaut.prg ; name of executable program .C[-A -C -K -J -Z -M] .S[-N] = ; list of modules follows... TCSTART.O ; startup code spoolaut.c (types.h,spool_pt.h) ; C-Teil buff.c (types.h,spool_pt.h) ; Pufferverwaltung spoolint.s (types.h,spool_pt.h) ; Assembler Teil (interrupt) bios_aut.s (types.h,spool_pt.h) ; Einklinken ins Bios (interrupt) TCSTDLIB.LIB ; standard library TCTOSLIB.LIB ; TOS library TCGEMLIB.LIB ; AES and VDI library ; »»»»»»»»»»»»««««««««««««

Listing 8



Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]