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.
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.
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 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.
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.
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
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