Blinkeling, die optische Klingel

Bei der Ausgabe des ASCII-Zeichens BELL meldet sich der Monitor mit einem Pling; das Warnzeichen lässt sich aber auch optisch durch ein kurzes Invertieren des Bildschirms darstellen, wie es beispielsweise auf Terminals geschieht. Das hier vorgestellte OptoBell erlaubt dies auf dem ST.

OptoBell ist in Turbo-C mit einem Assembler-Teil geschrieben. Sie müssen Listing 1 und Listing 2 übersetzen, was mit der Projektdatei in Listing 3 automatisch geschieht. Das kleine Steuerungs-Accessory in Listing 4 wird von der Projektdatei in Listing 5 erzeugt. Beide Programme verwenden gemeinsame Definitionen aus der Header-Datei in Listing 6.

Es entsteht dabei OPTOBELL.PRG, das den eigentlichen Programmteil enthält und in den AUTO Ordner kopiert wird. OPTOBELL.ACC ist das Steuer-Accessory, das Sie auf Ihre Boot-Partition kopieren sollten.

Nach dem Hochfahren werden alle BELL-Zeichen in ein Aufblinken des Bildschirms übersetzt. Durch Anwählen des Accessories läßt sich dieses Verhalten steuern. Es erscheint eine Alert-Box, bei der die momentane Einstellung als Default vorgegeben ist (Bild 1).

Die Auswahlmöglichkeiten sind „Blink“ für das Aufblinken des Bildschirms, „Blink+ Bell“ für die zusätzliche Ausgabe des Klingelzeichens und schließlich „Bell“ für den Normalzustand.

Programmierung

Der residente Teil von OptoBell hängt sich in den BIOS-Vektor ein und prüft bei allen Aufrufen, ob das BELL-Zeichen auf dem Bildschirm ausgegeben werden soll. Dies ist genau dann der Fall, wenn als Operationscode 3 (für die BCo-nOut-Funktion) und als Parameter 2 und 7 übergeben werden. 2 bezeichnet das Konsolengerät, also den Bildschirm, und 7 ist der ASCII-Code von BELL.

Die Alert-Box zum Einstellen von OptoBell

Die Tests im Trap-Handler prüfen, ob die Parameter auf dem Supervisor- oder dem User-Stack zu finden sind, und verrechnen das zusätzlich abgelegte Wort bei Prozessoren ab 68020. Soll ein Klingeln ausgegeben werden, ruft der Händler die Routine blinkf) aus dem C-Teil auf und beendet danach den Trap-Aufruf. Ansonsten wird in den normalen Trap-Handler weitergesprungen. Ist die Einstellung „Blink+Bell“ gewählt, wird der Trap zusätzlich weitergeführt, wodurch das Klingelzeichen ertönt.

Zur Einbindung in den Vektor wird die XBRA-Kennung „RTOB“ verwendet. Die Installation im C-Teil (main()) beschränkt sich auf das Ändern des Vektors, die Ausgabe einer Meldung und eine residente Beendigung des Programms mit PTermRes.

Hinzu kommt die Ablage eines Cookies mit der gleichen Kennung, dessen Inhalt auf einen Integer-Wert zeigt, der das Verhalten von OptoBell steuert. Darüber ist einstellbar, ob OptoBell blinken, blinken und klingeln oder - wie im Normalzustand - nur klingeln soll. Durch den Cookie kann der residente Teil von „außen“, also über ein Accessory, gesteuert werden.

blink() ist für das Aufblinken des Bildschirms zuständig. Dessen Ausmaße werden dazu über die Line-A-Variablen ermittelt. Nach dem Abschalten von Clipping und Maus wird durch ein vro_cpyfm der Bildschirm invertiert.

Damit der Effekt überhaupt sichtbar ist, muß vor einem nochmaligen Invertieren - das den ursprünglichen Zustand wieder herstellt - etwas gewartet werden. Die Turbo-C-Routine delay ist dafür nicht geeignet, weil sie selber einen BIOS-Aufruf macht. Da das BIOS aber nicht wiedereintrittsfähig ist, führt dies zu einem Absturz. Daher wird das Warten direkt mit Hilfe des 200-Hz-Zählers implementiert. Die kleine for-Schleife stellt den aktuellen Inhalt fest und wartet dann, bis er den Wert aus aktuell plus Wartezeit erreicht hat. Darauf folgt das zweite Invertieren und das Einschalten der Maus.

Die Invertierung des Bildschirm führt beispielsweise bei einem TT im Low-Modus zu einem rechten Farbenspiel. Damit der Effekt nicht zu aufdringlich wird, ist die Wartezeit von der Anzahl der Planes der momentanen Auflösung abhängig.

Für die VDI-Aufrufe wird ein festes Handle verwendet. Da das AES selber das BIOS benutzt (Warnton beim Klicken außerhalb einer Dialogbox), würde ein graf_handle-Aufruf einen Wiedereintritt in das AES bedeuten. Damit hat es allerdings - wie das BIOS - Probleme, die zu einem Absturz führen.

Das Accessory ist einfach zu programmieren. Anfangs wird nach dem Cookie gesucht und -ist dieser vorhanden - darüber die Adresse der Konfigurations-Variablen festgestellt. Die folgende Schleife erwartet ein Anwählen des Accessories und zeigt dann eine kleine Alert-Box an. In ihr können die drei genannten Einstellungen für OptoBell festgelegt werden.

/* OPTOBELL.C 
 *
 * Ersetzt die Ausgabe des BEL-Zeichens durch
 * ein kurzes Invertieren des Bildschirms.
 *
 * Geschrieben mit TC 2.0 und MAS
 *
 * 1991 by Robert Tolksdorf
 * (C) ST-Computer 
 */

/***********************************************
 * TOS-, VDI- und LINEA-Definitionen 
 */

#include <tos.h>
#include <vdi.h>
#include <linea.h>
#include <ext.h>

/***********************************************
 * Die Definitionen zur Kommunikation 
 */
#include "optobell.h"

/***********************************************
 * In OPTOBIOS.S definiert 
 */
extern void my_bios();
extern void *XB_BIOS;
#define NULL ( ( void * ) 0L )

/* MFDB für den Bildschirm, auf 0L initialisiert */
MFDB    screen = {NULL};
/* Quellen- und Zielkoordinaten                  */
int pxy[8];
/* Für den Zielwert des 200Hz-Zählers            */
unsigned long wait;

/* Der 200Hz-Zähler                              */
#define HZ200 *(long *) 0x04BA
/* Für andere Wartezeit HIER ändern!             */
#define WAIT 10
/* WAIT auf 50Hz-Wert anpassen                   */
#define _WAIT_ ((WAIT/5) & 0xFFFF)
/* Ein VDI-Handle, das es geben muß, da ein
   graf_handle im BIOS-Trap einen eigenen Stack 
   erfordern würde. */
#define VDI_HANDLE 1

void blink(void)
{
    /* Bildschirmausmaße anfordern */
    /* Clipping aus */
    vs_clip(VDI_HANDLE,0,pxy);
    /* Bildschirmausmaße auslesen und Rechteck bilden */
    pxy[2]=pxy[6]=Vdiesc->v_rez_hz-1; 
    pxy[3]=pxy[7]=Vdiesc->v_rez_vt-1;
    /* Maus aus         */ 
    v_hide_c(VDI_HANDLE);
    /* 1. Invertieren   */
    vro_cpyfm(VDI_HANDLE, DINVERT, pxy, &screen,&screen); 
    /* Warten           */
    for (wait=HZ200+_WAIT_/Linea->v_planes; wait>HZ200;); 
    /* 2. Invertieren   */
    vro_cpyfm(VDI_HANDLE, D_INVERT, pxy, &screan, &screen); 
    /* Maus ein         */
    v_show_c(VDI_HANDLE,1);
}

long super_stack;
long stackcorr;
int OptoConf = O_BLINK;

/* Cookie-Jar einrichten, Zeiger auf ersten Cookie abliefern */
COOKIE *install_cookie_jar(long n)
{
    COOKIE *cookie;

    cookie=Malloc(sizeof(COOKIE)*n);
    Super(0L);
    *(long *)0x5A0L=cookie;
    Super((void *) super_stack); 
    cookie->id=0L; 
    cookie->val=n; 
    return (cookie);
}

void main(void)
{

    char *mess1 = "\r\n\x1Bp OptoBell V 1.0 \x1Bq\r\n" \
                "1991 by Robert Tolksdorf\r\n" \
                "(C) ST-Computer\r\n"; 
    char *mess2 = "\r\n\x1Bp OptoBell V 1.0
                   \x1Bq\r\n" \
                   "Already installed\r\n";

    COOKIE *cookie, *cookieo; 
    int ncookie = 0;
    long jarsize;

    /* Systemvariablen lesen */ 
    super_stack=Super(0L); 
    cookie=cookieo= *(long *)0x5A0L; 
    stackcorr = (*(int *)0x59EL) ? 2 : 0 ;
    Super((void *) super_stack);
    /* Kein Cookie-Jar vorhanden -> neuen einrichten */ 
    if (!cookie)
    {
        cookie=install_cookie_jar(8L); 
        ncookie=0;
    }
else
    /* sonst durchsuchen */
    for (;((cookie->id) && (cookie->id!=OPTOID)); cookie++, ncookie++);
    /* cookie zeigt auf RTOB-Cookie oder Null Cookie */ 
    if (!cookie->id) /* OptoBell noch nicht installiert*/ 
    {
        /* Ist noch Platz ?? (nur, wenn Jar schon eingerichtet!) */
        if (cookie->val<=ncookie)
        {
            /* nein -> neuen einrichten, alten kopieren */ 
            cookie=install_cookie_jar(cookie->val+8L); 
            for (;cookieo->id!=0L;
                (*cookie++)=(*cookieo++)); 
            cookie->id=0L;
            cookie->val=cookieo->val+8L;
        }
        /* Cookie hinterlassen */

        jarsize=cookie->val; /* Groesse des Cookie-Jars merken */
        cookie->id=OPTOID; 
        cookie++->val=&OptoConf; 
        cookie->id=0L; 
        cookie->val=jarsize; 
        linea_init();
        /* Installieren */
        XB_BIOS=Setexc(45,my_bios);
        /* "Installiert"-Meldung */
        Cconws(mess1);
        /* Und resident im Speicher bleiben */
        Ptermres(_PgmSize,0);
    }
    else
        Cconws(mess2);
}

Listing 1: Der C-Teil von OptoBell

; OPTOBIOS - Der BIOS-Handler für OPTOBELL
;
; 1991 by Robert Tolksdorf
; (C) ST-Computer
:
EXPORT XB_BIOS, my_bios
IMPORT blink, OptoConf, stackcorr

        DC.L "XBRARTOB"
XB_BIOS: DC.L 0

my_bios:
        TST.W   OptoConf 
        BEQ     goto_stand

        MOVE.L  A0, a0_save ; A0 sichern 
        MOVE.L  USP,A0 
        BTST    #$5,(A7)
        BEQ     not_super

        LEA     6(A7),A0

not_super:
        ADD.L   stackcorr(PC),A0; 68020 beachten

        CMPI.L  #$30002,(A0)    ; BConOut(2,... ?
        BNE     cont_stand      ; Nein -> normal weiter
        CMPI.W  #$7,4(A0)       ; BConOut(2,7) ?
        BNE     cont_stand      ; Nein -> normal weiter

        JSR     blink ; Blinken lassen

        TST.W   OptoConf 
        BMI     cont_stand

        MOVE.L  a0_save(PC),A0  ; A0 restaurieren
        RTE                     ; und fertig

cont_stand:
        MOVE.L  a0_save(PC),A0  ; A0 restaurieren 
goto_stand:
        MOVE.L  XB_BIOS,-(A7)
        RTS

a0_save: DC.L 0 
        END

Listing 2: Der Assembler-Teil von OptoBell

; OPTOBELL.PRJ
optobell.prg 
.S[-S]

=                       ; list of modules follows...

TCSTART.O               ; startup code

OPTOBELL.C (OPTOBELL.H) ; das Hauptprogramm
OPTOBIOS.S              ; der Assembler-Teil

TCSTDLIB.LIB            ; Standard library
TCTOSLIB.LIB            ; TOS library
TCGEMLIB.LIB            ; AES and VDI library
TCLNALIB.LIB            ; LINE-A library
TCEXTLIB.LIB

Listing 3: Die Projektdatei für OptoBell

/* OPTOCONF.C 
 *
 * Accessory zum Konfigurieren von OptoBell
 *
 * Geschrieben mit TC 2.0 und MAS
 *
 * 1991 by Robert Tolksdorf
 * (C) ST-Computer 
 */

/***********************************************
 * TOS-, AES-Definitionen 
 */

#include <aes.h>
#include <tos.h>

/***********************************************
 * Die Definitionen zur Kommunikation 
 */
#include "optobell.h"

/* Texte für Menü und Alert */
#define MENUTITLE " OptoConv"
#define CONFALERT "[2][Welche OptoBell-Einstellung?]" \ "[Blink|Blink+Bell|Bell]"

long super_stack; 
int *OptoConf;

void main(void)
{
    COOKIE *cookie; 
    int applid,
        message[8], 
        defbtn;

    applid=appl_init();
    /* Systemvariablen lesen */ 
    super_stack=Super(0L); 
    cookie= *(long *)0x5A0L;
    Super((void *) super_stack);
    /* Kein Cookie-Jar vorhanden -> neuen einrichten */ 
    if (cookie)
    {
        /* durchsuchen */
        for (; ((cookie->id) && (cookie->id!=OPTOID)) ; cookie++);
        /* cookie zeigt auf RTOB-Cookie oder Null Cookie */ 
        if (cookie->id) /* OptoBell installiert */
        {
            OptoConf=cookie->val; 
            menu_register(applid, MENUTITLE);
            /* Hauptschleife, wartet auf Aufruf und stellt Konfigurations-Dialog dar */
            do
            {
                /* Aktuelle Einstellung als Default-Button */ 
                switch (*OptoConf)
                {
                    case O_BLINK: 
                        defbtn=1; 
                        break; 
                    case O_BLINKBELL: 
                        defbtn=2; 
                        break; 
                    case O_BELL: 
                        defbtn=3; 
                        break;
                }
                /* Auf ACOPEN-Mitteilung warten */ 
                evnt_mesag(message); 
                if (message[0]==AC_OPEN)
                    /* Kleinen Dialog mit Alert-Box durchführen */ 
                    switch (form_alert(defbtn,CONFALERT))
                    {
                        /* Button Blink */ 
                        case 1: *OptoConf=O_BLINK;
                            break;
                        /* Button Blink+Bell */
                        case 2: *OptoConf=O_BLINKBELL;
                            break;
                        /* Button Bell */ 
                        case 3: *OptoConf=O_BELL;
                            break;
                    }
            } while(1);
            /* Old Accessories never die -they just loop again */
        }
    }
    /* Kommt nur dann hierher, wenn kein Cookie-Jar oder kein OptoBell */
    form_alert(1,"[3][Ohne den residenten|"\
                "Teil in OPTOBELL.PRG|"\
                "kann OptoConv nicht|"\
                "arbeiten][Abbruch]");
    do
        evnt_mesag(message); 
    while(1);
}

Listing 4: Das Accessory OptoConf

; OPTOCONF.PRJ 

optoconf.acc

=               ; list of modules follows...

TCSTART.O       ; startup code

OPTOCONF.C (OPTOBELL.H)

TCSTDLIB.LIB    ; Standard library
TCTOSLIB.LIB    ; TOS library
TCGEMLIB.LIB    ; AES and VDI library

Listing 5: Die Projektdatei für OptoConf

/* OPTOBELL.H 
 *
 * Definitionen für OPTOBELL.C und OPTOCONF.C 
 */

/* Der Cookie-ID "RTOB"     */
#define OPTOID 0x52544F42L

/* Die Cookie-Struktur      */
typedef struct{
    long id, val;
} COOKIE;

/* Die OptoBell-Einstellungen */
#define O_BLINK 1
#define O_BLINKBELL -1 
#define O_BELL 0

Listing 6: Die Header-Datei OPTOBELL.H, die von beiden Programmen benutzt wird.


Robert Tolksdorf
Aus: ST-Computer 01 / 1992, Seite 84

Links

Copyright-Bestimmungen: siehe Über diese Seite