|
|
dESµTerm |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Serial.CPP
// ---------- Version 13.10.2002
// Borland IDE C++ 3.1 oder 5.02
// Sprache C und Inlineassembler
// Model: SMALL
// Char: unsigned
// bearbeitet: BBS WiLu, D.Schwarzer
// IRQ-Treiber für die serielle Schnittstelle bei
// IBM-kompatiblen PC-Rechnern mit XON/XOFF Empfangs Flow
// Control
// -der Treiber sendet Zeichen über die Benutzerfunktion
// serout(). diese Funktion wartet, bis das letzte Zeichen
// gesendet wurde und gibt dann das aktuelle Zeichen aus.
// -der Treiber benutzt einen Empfangs-IRQ und legt alle
// empfangenen Zeichen in einem Buffer ab. Der Buffer kann
// durch die Funktionen ..
// is_serin() - liegt ein Zeichen im Buffer vor?
// getserin() - Zeichen holen
// .. gelesen werden.
// -Ist das XON/XOFF Übertragungsprotokoll eingeschaltet,
// sendet der Empfänger vor einem Bufferüberlauf XOFF
// zum Sender um diesen zur Einstellung der Sendung zu
// bewegen. Geschieht dies nicht füllt sich der Buffer
// wieder von vorne (RingBuffer)und es gehen möglicherweise
// Zeichen verloren. Die Wirkung von XOFF
// wird durch Senden von XON wieder aufgeboben.
// -Die Übertragungsparameter können in globalen Variablen
// gesetzt werden und durch die Funktion initSerial()
// gesetzt werden. Werden keine Werte in die Parameter-
// variablen eingetragen, benutzt der Treiber beim Aufruf
// von initSerial die Vorgabewerte ..
// 19200 Baud, 8Daten, kein Parity, 1Stopbit, XON/XOFF aus
// -Mit der Funktion restSerial() kann der gesetzte
// IRQ-Handler beim Verlassen dieses Programms wieder
// entfernt werden. Diese Funktion sollte auch vor einem
// zweiten Aufruf von initSerial() benutzt werden.
// -Es können beliebige Baudraten zwischen 2 bis 115.200
// Baud angegeben werden. Der Treiber berechnet die ihm
// mögliche Baudrate.
// -Die globale Strukturvariable ss enthält die vom Treiber
// tatsächlich eingestellten Werte. Diese Struktur kann
// für Anzeigen verwendet werden.
// (c) 1993 Schwarzer, BBS Winsen, 21423 Winsen/L
// ----------------------------------------------------- //
// COMx: serielle Schnittstelle COM 1-2 (3,4)
// BAUDRATE: Bits/Sekunde zwischen 2 und 115.200
// PARITY: 1 -ungerade, 2 -keine, 3 -ungerade
// DATENBITS: Zahl der Datenbits. Möglich 5, 6, 7 oder 8
// STOPBITS: Zahl der Stopbits. möglich 1 oder 2
// FLOW: 1 = XON/XOFF beim Empfang ein
// Voreinstellung
#define COMx 1 // COM 1
#define BAUDRATE 19200 // 19200 Bits/Sekunde
#define PARITY 2 // keine Paritätsprüfung
#define STOPBITS 1 // 1 Stopbit
#define DATENBITS 8 // 8 Datenbits
#define FLOW 0 // XON/XOFF Protokoll aus
// Die nachfolgenden Variablen enthalten die zur Laufzeit
// gültigen Werte der seriellen Schnittstelle. Sie können
// von einem externen Programm gesetzt werden, dann muss
// initSerial() aufgerufen werden.
int COM = COMx; // Schnittstellennummer
long baudrate = BAUDRATE; // Baudrate
int databit = DATENBITS; // Anzahl der Datenbits
int stopbit = STOPBITS; // Anzahl der Stopbits
int parityb = PARITY; // Art der Paritätsprüfung
int flow = FLOW; // XON/XOFF beim Empfang
// ----------------------------------------------------- //
// Diese Rückgabestruktur enthält die zur Laufzeit
// bestehenden Einstellungen der Schnittstelle. Externe
// Programme können diese Angaben zur Anzeige benutzen
struct Param_SS {
unsigned COM; // COM-nr Benutzter IBM Eingang
long baud; // am 8250 eingestellte Baudrate
int datab; // Anzahl Datenbits
int stopb; // Anzahl Stopbits
int parity; // KennNr. der Parität
char paritystrg[10]; // Bezeichnung der Parität
unsigned INTnr; // Benutzter INT -Vektor
unsigned INTadr; // Adresse auf die INTnr zeigt
unsigned IRQnr; // Benutzter IRQ
char methodestrg[10]; // Methode Polling/Interrupting
char abfragestrg[25]; // direkt oder über BIOS INT14h
} ss;
void initSerial (void); // ser Schnittst. aktivieren
void restSerial (void); // ser Schnittst. deaktivieren
void clrsbuf (void); // Empfangsbuffer löschen
void serout (int c); // warten dann Zeichen senden
int is_serin (void); // Flagge: Zeichen im Buffer ?
int getserin (void); // Funktion: Zeichen holen
// ------------------------------------------------------ //
#include <bios.h>
#include <dos.h>
#include <conio.h>
#include <string.h>
#define XON 0x11 // XON (CtrlQ) Sender soll senden
#define XOFF 0x13 // XOFF(CtrlS) soll ruhen
// Typenbezeichnung der Schnittstellenbausteine:
// SAB 8250 oder SAB16C450 = NS16450 = UMC82450 oder NS16550
// Taktfrequenz der Bausteine 1,8432 MHz
// max. Baudrate bei direkter Programmierung 115200 Baud
// Anzahl der mit diesem Programm ansteuerbaren seriellen
// Schnittstellen.
const int commax = 4;
// Basisadressen der Schnittstellenbausteine für die
// seriellen DOS-Schnittstellen COM 1..4 in IBM kompatiblen
// Rechnern.
const unsigned com[5] = { 0, 0x3F8, 0x2F8, 0x3E8, 0x2E8 };
// Registernamen (relative Adressen) des Schnittstellen-
// bausteins der seriellen Schnittstelle.
// (absolute Adresse = com[x] + Registername)
enum { IOR, IER, IIR, LCR, MCR, LSR, MSR };
// Interruptvektoren der Hardware-IRQ 4,3,10,11 die den
// Schnittstellen COM 1-4 zugeordnet sind. Die IRQ 10 u.
// 11 sind nur bei AT-Rechnern vorhanden Die meisten
// SS-Karten unterstützen deswegen nur die IRQ 4 und 3.Die
// Schnittstellen COM 3-4 werden von DOS erst ab
// Version 4.01 unterstützt.
// COM = x 1 2 3 4
// ---------------------------------------------
// int IRQnr[5] = { 0, 4, 3, 10, 11 };
// int INTnr[5] = { 0, 0x0C, 0x0B, 0x72, 0x73 };
int IRQnr[5] = { 0, 4, 3, 4, 3 };
int INTnr[5] = { 0, 0x0C, 0x0B, 0x0C, 0x0B };
// absolute Adressen des gewählten 8250 Schnittstellen-
// bausteins initSerial() setzt diese Adressen
int near R_IOR = 0x3F8+IOR;
int near R_IER = 0x3F8+IER;
int near R_IIR = 0x3F8+IIR;
int near R_LCR = 0x3F8+LCR;
int near R_MCR = 0x3F8+MCR;
int near R_LSR = 0x3F8+LSR;
int near R_MSR = 0x3F8+MSR;
// EmpfangsBuffer Hilfsvariable
// ----------------------------
int near SerBufFul = 0; // Flagge: 1 -EmpfangsBuffer
// voll
char near sibuf[0x1000]; // RingBuffer für die
// eingehenden Zeichen der
// ser. Schnittstelle
unsigned near widx = 0; // Zeiger auf Schreibstelle
// im Buffer
unsigned near ridx = 0; // Zeiger auf Lesestelle im
// Buffer
const int BUFFMASK = 0x0FFF; // Umlauf_Maske für die
// Indexwerte
// Lese/Schreib Hilfsfunktionen
// -----------------------------
int near is_V24in (void); // Zeichen empfangen ?
int near getV24in (void); // Zeichen holen
int near is_V24out (void); // bereit zum Senden ?
void near getV24out (int c); // Zeichen c senden
// Initialisations Hilfsfunktionen
// -------------------------------
void near initirq (int comnr, int irqnr);
void near restirq (int comnr, int irqnr);
int near getss (int comnr, long* baud, int* parity,
int* datab, int* stopb);
int near initss (int comnr, long baud, char parity,
int databit, int stopbit);
void near getParam_SS (void);
// Definition des IRQ-Handlers
// ---------------------------
#ifdef __cplusplus
#define __CPPARGS ...
#else
#define __CPPARGS
#endif
void interrupt (*oldVector)(__CPPARGS); // alter IRQ Vektor
void interrupt IBMCOMx (__CPPARGS); // IRQ Handler
void near _ChrToBuf (void); // IRQ Zeichen zum
// EmpfangsBuffer
/**********************************************************/
// Funktion löscht den Empfangsbuffer und setzt die Buffer-
// Voll-Flagge auf Buffer leer. XON (Ctrl-Q) wird gesendet,
// um dem Sender zu signalisieren, dass der Empfänger bereit
// ist.
void clrsbuf (void)
{
widx=ridx;
if(SerBufFul){
SerBufFul=0;
if(flow)serout(XON);
}
}
// FlaggenFkt: gibt 1 zurück, wenn noch Zeichen im
// EmpfangsBuffer sind, sonst wird 0 zurückgegeben.
int is_serin (void)
{
if(widx !=ridx) return(1); // es sind noch Zeichen im
// Buffer
if(SerBufFul){ // keine Zeichen, aber Buffer
// voll
if(flow)serout(XON); // geflaggt. Buffer leer
SerBufFul=0; // flaggen und Sender zum
// Senden auffordern
}
return (0);
}
// gibt ein Zeichen aus dem EmpfangsBuffer zurück. Die
// Rückgabe von 0 kann bedeuten, daß kein Zeichen im Buffer
// war, aber auch, daß sich im Buffer das Zeichen NULL
// befand. Deswegen sollte vor Aufruf dieser Funktion mit
// is_serin() geprüft werden, ob ein Zeichen im Buffer
// vorhanden ist.
int getserin (void)
{
if (widx != ridx) { // wenn Buffer Zeichen enthält..
return (sibuf[ridx++ & BUFFMASK]); // .. Zeichen holen
} // und zurückgeben
if (widx==ridx && SerBufFul){ // wenn Buffer leer
// u.Bufferüberlauf
if(flow)serout(XON); // geflaggt: Flagge
// auf Buffer leer
SerBufFul=0; // Sender mit XON
// freigeben
}
return(0); // Rückgabe wenn Buffer leer
}
// Wartet, bis die serielle Schnittstelle zur Ausgabe eines
// Zeichens bereit ist und gibt das Zeichen aus.
void serout (int c) { while (!is_V24out()); getV24out(c);}
// *******************************************************//
//
// Hilfsfunktionen: Schreiben /Lesen der seriellen
// Schnittstelle
// Test ob die serielle Schnittstelle zum Senden bereit ist
// Ein Zeichen kann mit getV24out gesendet werden.
// RÜCKGABE: 0 nicht bereit 1 Schnittstelle zum Senden
// bereit
int near is_V24out (void)
{
asm { mov DX,R_LSR // Status TxD direkt lesen
in AL,DX
and AL,00100000B
mov AH,0 }
return (_AX);
}
// Ein Zeichen über die serielle Schnittstelle senden. Ob
// die Schnittstelle zum Senden bereits ist kann durch
// is_V24out getestet werden.
void near getV24out (int c)
{
asm { mov AX, c // TxD direkt schreiben
mov DX, R_IOR
out DX, AL }
}
// Interrupthandler für die serielle Schnittstellen in IBM
// PC-kompatiblen Rechnern. Die empfangenen Zeichen werden
// in einem Buffer abgelegt, der mit getserin() gelesen
// werden kann.
// Eingabestatus der seriellen Schnittstelle abfragen. Das
// Zeichen kann mit getV24in() gelesen werden.
// RÜCKGABEN: 0 kein Zeichen 1 Zeichen steht bereit.
int near is_V24in (void)
{
asm { mov DX, R_LSR // Status RxD direkt lesen
in AL, DX
and AL, 00000001B
mov AH, 0 }
return (_AX);
}
// Ein Zeichen von der seriellen Schnittstelle lesen. Ob
// ein Zeichen vorliegt, kann mit is_V24in() geprüft werden
int near getV24in (void)
{
asm { mov DX, R_IOR // RxD direkt lesen
in AL, DX
mov AH, 0 }
return (_AX);
}
// Hole ein Zeichen der seriellen Schnittstelle und lege es
// im Buffer ab. Wenn der Buffer für die serielle
// Schnittstelle fast voll ist, wird ein Ctrl-S zum Sender
// geschickt und die Flagge 'SerBufFul' auf 1 gesetzt.
// Diese Funktion wird vom Interrupthandler aufgerufen.
void near _ChrToBuf (void)
{
char ch;
if(is_V24in ()){
ch = getV24in(); // Zeichen holen und zum
// Buffer
if(((((widx&BUFFMASK)-(ridx&BUFFMASK))&BUFFMASK) > 0xF00)
&& !SerBufFul){
if(flow)serout(XOFF); // wenn Buffer voll wird, Sender
SerBufFul = 1; // stoppen und Buffer-Voll flaggen
}
sibuf[widx++ & BUFFMASK] = ch; // Zeichen im Buffer
} // ablegen
}
// Interrupthandler für IRQ an der seriellen Schnittstelle
void interrupt IBMCOMx (__CPPARGS)
{
asm { push BP
pushf
push AX
push BX
push CX // Register auf dem Stack sichern
push DX
push DI
push SI
push ES
push DS
sti // weitere IRQ am 80x86 freigeben
mov DX, R_IIR
in AL, DX
test AL, 004h // IIR, Bit 2 -Receive Data available
jz EOI } // irgend ein anderer IRQ
_ChrToBuf(); // Zeichen holen und zum Buffer
EOI:
asm {
mov AL,020H // COM1: 064H, COM2: 063H ?
out 020H,AL // IRQ am Interruptcontroller löschen
pop DS
pop ES
pop SI // gesicherte Register vom Stack
pop DI // holen
pop DX
pop CX
pop BX
pop AX
popf
pop BP }
}
// Funktionen für Init und Rücksetzen der ser.Schnittstelle
int near initss (int comnr, long baud, char parity,
int databit, int stopbit)
// initialisiert die seriellen Schnittstellen COM 1-4 bei
// IBM- Rechnern
// comnr: Nummer d.zu initialisierenden Schnittstelle 1-4
// baud: Übertragungsrate beliebig zwischen 2 und 115.200
// parity: 1 -ungerade, 2 -keine, 3 -ungerade
// databit: Zahl der Datenbits. möglich 5, 6, 7 oder 8
// stopbit: Zahl der Stopbits. möglich 1 oder 2
{
int dat, stop, par, portval;
unsigned char blo, bhi;
if (comnr <= 0 || comnr > commax) return (0);
dat = databit-5;
if (dat < 0 || dat > 5) dat = 3; // Datenbits 5, 6, 7, 8
stop = (0x04 & (stopbit*2)); // Stopbits 1, 2
switch (parity) {
case 3: par = 0x18; break; // even (gerade)
case 1: par = 0x08; break; // odd (ungerade)
default: par = 0x00; break; } // keine
// Teilerrate für angegebene Baudrate berechnen
blo = (unsigned char)(115200L / baud);
bhi = (unsigned char)( 450L / baud);
// Wert von LCR lesen -->
portval = inportb (com[comnr] +LCR);
// Baudraten_Übertragung init
outportb (com[comnr]+LCR, portval| 0x80);
// Teilerrate Baud low /high
outportb (com[comnr]+0, blo);
outportb (com[comnr]+1, bhi);
// Kontrollbyte übertragen
outportb (com[comnr]+LCR, dat|stop|par);
inportb (com[comnr]);
return(1);
}
// ermittelt die Einstellungen der Schnittstellen COM 1-4
// und gibt diese an die referenzierten Variablen zurück.
// Konnten gültige Werte gelesen werden gibt die Funktion
// 1 zurück sonst 0
// parity kann die Werte 1 = ungerade, 2 = keine, 3 gerade
// Parität annehmen.
// Das Programm benötigt als externe Angaben die Tabelle
// der Basisadessen der Schnittstellenbausteine 'com[]' und
// die relativen Adressen der Bausteinregister LCR, IOR ...
int near getss (int comnr, long *baud, int *parity,
int *datab, int *stopb)
{
int portval, frame;
portval = inportb (com[comnr]+LCR); // Baudrate ermitteln
outportb (com[comnr]+LCR, portval | 0x80);
*baud = inportb (com[comnr]+IOR) +
inportb(com[comnr]+IER)*256;
if ((*baud>2304) || (*baud<0)) return (0);
*baud = 115200L / *baud;
outportb (com[comnr]+LCR, portval);
frame = inportb(com[comnr]+LCR); // Kontrollbyte holen
*parity = frame & 0x18;
*parity = *parity >> 3;
if (!(*parity)) *parity =2; // keine Par.prüfung
// wenn Par = 0 od.2
*datab = 5 + (frame & 0x03); // Datenbits
*stopb = 1 + ((frame & 0x04) >> 2); // Stopbits
return(1);
}
// ermittelt die tatsächlichen Einstellungen der
// Schnittstelle und legt diese in der globlaen Struktur
// ss ab.
void near getParam_SS (void)
{
ss.COM = COM;
getss (COM, &ss.baud, &ss.parity, &ss.datab, &ss.stopb);
switch (ss.parity) {
case 1: strncpy(ss.paritystrg, "ungerade",10); break;
case 2: strncpy(ss.paritystrg, "keine ",10); break;
case 3: strncpy(ss.paritystrg, "gerade ",10); break; }
ss.INTnr = INTnr[COM];
ss.INTadr= com[COM];
ss.IRQnr = IRQnr[COM];
}
// für die serielle Schnittstelle COMx wird einer der
// IRQs 3 oder 4 (siehe Tabelle IRQnr) freigegeben. Es wird
// der Interrupthandler IBMCOMx installiert. Seine Adresse
// wird in den Sofwareinterrupt eingetragen, der dem
// IRQx zugeordnet ist. (siehe Tabelle INTnr)
void near initirq (int comnr, int irqnr)
{
oldVector = getvect (INTnr[comnr]); // alten Vektor sichern
setvect (INTnr[COM], IBMCOMx); // neuen setzen
asm { cli } // IRQ's an CPU sperren
switch (irqnr) {
case 3: {asm {in AL, 021h // IRQ-Controller ..
and AL, 0F7h // IRQ 3 freigeben
out 021h, AL }
break;}
case 4: {asm {in AL, 021h // IRQ-Controller
and AL, 0EFh // IRQ 4 freigeben
out 021h, AL }
break;}
};
outportb (com[comnr]+MCR, 0x0B); // set DTR, RTS, OUT2(IRQ)
outportb (com[comnr]+IER, 0x01); // IRQ on RxD ready frei
asm { sti } // IRQs an CPU zulassen
}
// schaltet die IRQ-Quellen am Schnittstellencontroller und
// am IRQ-Controller ab. Entfernt den Interrupthandler
// dieses Progamms und setzt den alten Handler ein. Die
// Baudrate, Stopbits und die Parität bleiben erhalten.
void near restirq (int comnr, int irqnr)
{
asm { cli } // IRQ an CPU abschalten
outportb (com[comnr]+MCR, 0x03); // res OUT2(IRQ)
outportb (com[comnr]+IER, 0x00); // IRQ on RxD ready
// sperren
switch (irqnr) {
case 3: {asm {in AL, 021h // IRQ-Controller ..
or AL, 008h // IRQ 3 sperren
out 021h, AL }
break;}
case 4: {asm {in AL, 021h // IRQ-Controller
or AL, 010h // IRQ 4 sperren
out 021h, AL }
break;}
};
setvect (INTnr[COM], oldVector); // alten IRQ-Handler
// setzen
asm { sti } // IRQ an CPU zulassen
}
// ********************************************************
// *serielle Schnittstelle initialisieren und zurücksetzen
// ********************************************************
// Setzt die serielle Schnittstelle auf die Werte zurück,
// die vor dem Start dieses Programms existierten.
void restSerial (void) { restirq(COM, IRQnr[COM]); }
// initialisiert die gewünschte serielle Schnittstelle COMx
// mit den Vorgabewerten.
void initSerial (void)
{
R_IOR = com[COM]+IOR; // absolute Adressen der
R_IER = com[COM]+IER; // Register des ausgewählten
R_IIR = com[COM]+IIR;
R_LCR = com[COM]+LCR; // Schnittstellenbausteins
R_MCR = com[COM]+MCR; //8250 für COMx
R_LSR = com[COM]+LSR;
R_MSR = com[COM]+MSR;
strncpy (ss.methodestrg, "Interrupt",10);
strncpy (ss.abfragestrg, "direkte Abfrage des 8250",25);
initss (COM, baudrate, parityb, databit, stopbit);
initirq (COM, IRQnr[COM]);
while (is_V24in ()){ // Empfangsbuffer leeren
getV24in ();
}
widx = ridx = 0; // Bufferzeiger auf 0
SerBufFul = 0; // Empfangsuffer leer
getParam_SS (); // Einstellungen in einer
} // Struktur festhalten
// ********************************************************
// * dESµTerm DOS Terminal Demoprogramm für SC12IPC@CHIP
// ********************************************************
// main()
// DemoTerminal: wartet auf Zeichen vom Sender und übergibt
// dorthin Tastatureingaben. ESC bricht das Programm ab.
// Hier könnte noch ein ON/XOFF Handshake für Sendevorgänge
// eingebaut werden, was bei Tastendruckübergaben allerding
// relativ unnötig ist. Im Programm werden die Zeichen
// XON/XOFF ausgefiltert
#include <stdio.h>
#include <bios.h>
#define ALTx 0x2D00 // ASCII-Code von [ALT-X]
#define XON 0x11 // XON (CtrlQ) Sender soll senden
#define XOFF 0x13 // XOFF(CtrlS) soll ruhen
void main (void)
{
unsigned c;
clrscr(); // löscht Bildschirm
printf(" dESyTerm\n"
" DOS-Terminalprogramm"
" .. Ende mit ALT-X\n\n");
initSerial(); // Treiber aktivieren
clrsbuf(); // Buffer löschen
while (1){
if(is_serin()){ // Zeichen vorhanden
c = getserin(); // Zeichen holen
if(c!=XON && c!=XOFF)
printf("%c",c); // Zeichen ausgeben
}
if(bioskey(1)){ // Taste vorhanden
c= bioskey(0); // Taste holen
if(c==ALTx) break; // ALT-X Programmende
serout(c); // Taste senden
}
}
restSerial(); // Treiber deaktivieren
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|