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



Aus: ST-Computer 02 / 1992, Seite 126

Links

Copyright-Bestimmungen: siehe Über diese Seite