Forum: PC-Programmierung Wer hat Erfahrung mit Devicetreibern in DOS (gar mit BCC) ?


von Hans M. (gast4711)


Lesenswert?

Versuche einen Devicetreiber mit BCC unter DOS zu schreiben.
Dabei soll z.B. der Serielle Interrupt 12 durch meine
Interrupt-Funktion ersetzt werden.
In einem RS232-Testprogramm klappt das schon einwandfrei, doch im
SYS-File leider nicht.
Beim rs232.com-File geht’s z.B. nur mit #pragma  option –k (also
Standard-Stack),
beim rs232.sys nur mit “ option –k- “ (sonst stürzt alles nach Auslösen
des ersten Interrupts ab).
Meine Interruptroutine sieht wie folgt aus:

typedef  void  DUMMY;

DUMMY far new_in(DUMMY)
{
  asm cli;
  asm pop  di; // da BCC schon bei jeder void-Fkt push di/si macht
  asm pop  si;
  asm push ax;
  asm push bx;
  asm push cx;
  asm push dx;
  asm push es;
  asm push ds;
  asm push si;
  asm push di;
  asm push bp;
  asm mov bp,cs;  // statt ursprünglichem asm mov bp,cs:DGROUP@
  asm mov ds,bp;
  asm mov bp,sp;

... Hier Code "Zeichen auf Schnitstelle ausgeben" einfügen

  asm pop bp;
  asm pop di;
  asm pop si;
  asm pop ds;
  asm pop es;
  asm pop dx;
  asm pop cx;
  asm pop bx;
  asm pop ax;
  asm sti;
  asm iret;
}

Vielleicht findet sich ja jemand, der (im Idealfall sogar mit BCC.EXE)
schon einmal eine Interruptroutine mit einem selbstgebauten
Deviece-Treiber ersetzt hat oder jemanden kennt, der jemanden kennt,
der sich mit so etwas auskennt.

von DOSi (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe sowas vor viiiiielen Jahren mal als TSR gschrieben, vielleicht
kannst Du damit was anfangen?

von Wolfram (Gast)


Lesenswert?

schau mal in PCintern 3.0 das ist ein 1000 seiten dickes Buch, da
findest du Beispiele für zeichenorientierte Devicetreiber.
Wenn du Zeichen auf der seriellen Schnittstelle mitloggen willst,
kannst du das aber mit einem TSR wesentlich schneller erreichen.

von Hans M. (gast4711)


Lesenswert?

@Wolfram: Habe hier PCintern 2.0, aber bei den Devicetreibern scheint
der Interrupt glaube ich nicht wirklich ersetzt zu werden.

@DOSi: Kann zwar praktisch kein Assembler, muß mich aber wohl doch da
mal einarbeiten. Der Treiber soll aber später noch etwas mehr können
und ist als Device-Treiber leider von den existierenden
Anwenderprogrammen vorgeschrieben (ein TSR-DOS-Programm geht also
leider nicht).

Das Hauptproblem ist wohl, daß man beim Initialisieren zwar die
Interruptfunktion mit new_int(); aufrufen kann und sie dann auch ein
Zeichen ausgibt, aber sie eben wohl nicht resident mit dem
Interruptvektor erreichbar ist (auch wenn's als .com-Datei ja klapt)

Dabei brauche ich den alten Vector nicht einmal sichern, weil der neue
vollständig ist und den alten für immer ersetzt.
Den seriellen Int.-Vector 12 ersetze ich übrigens durch mein selber
gebasteltes:
  int_vector = (unsigned int far *) MK_FP(0,0);
  asm cli;
  int_vector[24]=(unsigned int)(FP_OFF(new_int));
  int_vector[25]=(unsigned int) _DS;
  asm sti;
was soweit im COM-File ganz gut klappt.
Vielleicht lädt DOS den Treiber einfach an eine Stelle, die dem Linker
nicht bekannt ist? (Kenne mich auch mit far-Zeigern noch nicht so
wirklich aus...)
Schaue mir auf jeden Fall das INTSER.ASM mal genau an und sehe was ich
wie davon in C übernehmen kann (oder ob ich doch alles in ASM machen
muß). Danke auf jeden Fall schon einmal für das Programm!

von Wolfram (Gast)


Lesenswert?

poste mal den ganzen Code deines Com Programmes und den deines
Gerätetreibers.
IntVector 12 ist das eine PS/2 Mouse?
COM1/COM2 haben normalerweise die Int 4 und 3
wofuer soll der Devicetreiber eigentlich sein?
in deiner Interruptroutine benutzt du den Stack des Programmes
in Assembler kann man sowas machen, solange man den Stack nicht stark
belastet. Wenn du in deiner Interruptroutine C Code benutzt dann würde
ich den Stack des Gerätetreibers benutzen.(SS:SP). Wie hast du die
Interruptfunktion aufgerufen? (call new_int() oder pushf;call new_int)

von Hans M. (gast4711)


Lesenswert?

Unten erst einmal das Programm zum Test der seriellen Schnittstelle,
aber es tut ja hier eigentlich nichts zur Sache, welcher Interrupt
ersetzt wird.
Einzige Bedingung ist, daß der Device-Treiber zeichenorientiert ist
(und der Interrupt resident bleibt und mit dem Devicetreiberkern sowohl
von der Interruptroutine her als auch von den Anwenderprogrammen Daten
austauscht (schade auch, daß es hier offenbar keine PM’s gibt).

Wie ich den Stack in ein C-Programm einbinden kann weiß ich leider
nicht.
Wie gesagt stürzt das .COM-Programm mit “ option –k- “ ab und der
.sys-Treiber gerade mit “ option –k “ (‘Standard Stack frame ein‘ , was
immer das bedeutet; muß mir wohl mal das Assemlerlisting dazu
durcharbeiten).

Hier das com-Programm, auch wenn es mit dem Treiber-Problem ja wenig zu
tun hat und ja auch läuft ( compiliert mit BCC -mt -S rs232.c und dann
tasm rs232.asm und dann tlink /t /v
c0t.obj+rs232.obj,rs232.com,,graphics.lib emu.lib maths.lib cs.lib )

// RS232-Test-Programm
// This Programm sends out an 'X' to RS232 when a character comes in
via RS232
// (And also sends an 'X' when you press a key for testing via
RS232)
#pragma  inline
#pragma  option -k //#pragma  option -k- // TSR standard stack frame
#include <dos.h>
#include <conio.h>
typedef void DUMMY;
DUMMY   far  new_int(DUMMY);
//////////////////////////////////////////////////////////////////////vo 
id
main()
{
  unsigned int far *int_vector = (int far *) 0x00000000;
  unsigned int temp;
  unsigned int key;
  // initializing RS232 to 115kBaud:
  inport(0x3F8);                    // clear pending interrupts
  inport(0x3FD);
  inport(0x3FE);
  outport(0x3FA,0x06);
  outportb(0x3F9,0x00);             // disable interrupt
  outportb(0x3FA,0x00);             // disabble fifo
  temp = inportb(0x3FB);            // set Baudrate
  outportb(0x3FB,(temp | 0x80));    // set Baudrate
  outportb(0x3F8,0x01);             // set Baudrate
  outportb(0x3F9,0x00);             // set Baudrate
  outportb(0x3FB,temp);             // set Baudrate
  outportb(0x3FB,0x0b);             // 8bit, no parity, one stop bit
  outportb(0x3FC,0x08);             // int. enable, RTS disable
  outportb(0x21, (inportb(0x21) & 0xE7));
  outportb(0x20,0x20);
  outportb(0x3F9,0x01);             // enable receive ready interrupt
  outportb(0x3FB,(inportb(0x3FB) & 0xC7) |  0x38);
  outportb(0x3F9,0x01);             //disable tx interrupt
  //replacing int 12 Vector:
  int_vector = (unsigned int far *) MK_FP(0,0);  // far
  asm cli;
  int_vector[24]=(unsigned int)(FP_OFF(new_int));
  int_vector[25]=(unsigned int)_DS;
  asm sti;

  key=0;
  while(key !=27){                  // quit with "escape"
    if(kbhit()){
      key = getch();
      outportb(0x3F9,0x02);         // enable tx empty interrupt
    }
  }
}
//////////////////////////////////////////////////////////////////////DU 
MMY
far new_int(DUMMY)
{
  asm pop  di;
  asm pop  si;
  asm pop  bp; // because bcc made push bp;   mov  bp,sp
  asm  push  ax;
  asm  push  bx;
  asm  push  cx;
  asm  push  dx;
  asm  push  es;
  asm  push  ds;
  asm  push  si;
  asm  push  di;
  asm  push  bp; //  asm  mov  bp,cs:DGROUP@
  asm  mov  bp,cs;
  asm  mov  ds,bp;
  asm  mov  bp,sp;
  outportb(0x20,0x20);
  outportb(0x3F8,'X');
  outportb(0x3F9,0x01);   // enable receive ready interrupt
  inportb (0x3F8);
  asm  pop  bp;
  asm  pop  di;
  asm  pop  si;
  asm  pop  ds;
  asm  pop  es;
  asm  pop  dx;
  asm  pop  cx;
  asm  pop  bx;
  asm  pop  ax;
  asm iret;
}
//////////////////////////////////////////////////////////////////////

von Wolfram (Gast)


Lesenswert?

In der Interruptroutine solltest du noch die Flags sichern.(pushf/popf)
deine ersetzung des Intvektors ist etwas compilerspezifisch?
was macht MK_FP,FP_OFF?
//replacing int 12 Vector:
  int_vector = (unsigned int far *) MK_FP(0,0);  // far
  asm cli;
  int_vector[24]=(unsigned int)(FP_OFF(new_int));
  int_vector[25]=(unsigned int)_DS;
  asm sti;

soweit ich das verstehe benutzt du das datensegment (DS) um die
segmentadresse deiner Interruptroutine anzugeben,diese liegt aber im
Codesegment!(CS) bei einem COMProgramm sind diese segmente gleich ,ich
bin mir aber nicht sicher ob dies auch fuer einen Devicetreiber gilt.
der offset der interruptroutine ist auf jeden fall auf cs bezogen.
vom Compilermodell solltest du entweder ein gemeinsames
Code/Datensegment wählen oder Code/Datensegment jeweils maximal 64K.
ersteres wird wahrscheinlich unproblematischer.
was soll in deiner Interruptroutine eigentlich das nochmalige
aktivieren des receiveinterrupts?
Du restaurierst den Interruptvektor in deinem COMprogramm beim beenden
nicht wieder. Das heisst die Interruptroutine bleibt existent im
Speicher obwohl der Speicher nicht mehr dem Programm gehört und damit
jederzeit überschrieben werden kann. Damit dürfte es irgendwann zu
einem Absturz kommen.

Einen Gerätetreiber in C habe ich auch nicht als quellcode(ist auch
nicht in PCIntern3.0) wenn du keinen findest und nicht weiterkommst
wäre noch eine Möglichkeit einen alten Dosgerätetreiber über 15K zu
nehmen(der ist garantiert in C oder so geschrieben) und diesen
zu disassemblieren. effektiv brauchst du nur die Strategie und
Interruptroutine des Treibers um zu sehen was alles gesichert wird und
wie der Stackframe für den inneren C Teil aufgebaut wird.
Einen einfachen Gerätetreiber hats du in assembler aber wahrscheinlich
schneller geschrieben.
Wozu soll das ganze eigentlich dienen, dass du dir die mühe machst noch
einen Dostreiber zu schreiben?

von Bartli (Gast)


Lesenswert?

> In der Interruptroutine solltest du noch die Flags
sichern.(pushf/popf)

Wozu denn das ? Wird doch beim x86 automatisch gemacht wenn ein
Interrupt ausgelöst wird.

von Bartli (Gast)


Lesenswert?

>Wozu denn das ? Wird doch beim x86 automatisch gemacht wenn ein
>Interrupt ausgelöst wird.

Will sagen, wenn ein Interrupt ausgelöst wird, werden die Flags, und
CS:IP gesichert, IRET stellt die flags wieder her und springt zur
Rückkehradresse.

von Hans M. (gast4711)


Lesenswert?

Zunächst: Einen Gerätetreiber in C (den einzigen, den ich je fand) gibt
es unter http://ada2.unipv.it/biblio/trickyc/18c.htm
(zwar auch ein zeichenorientierter, aber leider offenbar ohne
Interruptersetzen :( ).

Da ich beim Compiler mit –mt Tiny als Modell wähle, dürfte sowieso
CS=DS sein (Änderung macht im Programm auch keinen Unterschied).

Das Progrämmchen oben ist übrigens kein TSR sondern war nur mal so zum
Test gedacht, also kann es nach Beendigung ruhig überschrieben werden.

Komisch ist, daß wenn ich mit option -k in rs232.com ein void far
interrupt newinterrupt(void);
deklariere, der Interrupt wie folgt aussieht (Erzeuge mit BCC –S ein
Assemblerlisting):
  push  ax
  push  bx
  push  cx
  push  dx
  push  es
  push  ds
  push  si
  push  di
  push  bp
  mov  bp,cs:DGROUP@
  mov  ds,bp
  mov  bp,sp
...eigentliches Interruptprogramm...
  pop  bp
  pop  di
  pop  si
  pop  ds
  pop  es
  pop  dx
  pop  cx
  pop  bx
  pop  ax
  iret
Compiliere ich dagegen in meinem Devicetreiber rs232.sys ohne
Standardstack, so erhalte ich
Den Linkererror “Error: Undefined symbol DGROUP@ in module rs232.c“ und
als Listing
  push  ax
  push  bx
  push  cx
  push  dx
  push  es
  push  ds
  push  si
  push  di
  push  bp
  mov  bp,DGROUP
  mov  ds,bp
... eigentliches Interruptprogramm...
  pop  bp
  pop  di
  pop  si
  pop  ds
  pop  es
  pop  dx
  pop  cx
  pop  bx
  pop  ax
  iret


Aber grundsätzlich stelle ich mir als Halblaie die Frage, ob die
Adresse meiner Interruptfunktion new_int() im Treiber überhaupt als die
durch FP_OFF(new_int) : _CS ermittelte Adresse auch beim Laden des
Treibers beim Booten an diese Adresse in den Speicher gelegt wird (und
falls nicht, wie ich die wahre Adresse erhalte).

von Hans M. (gast4711)


Lesenswert?

Ich dreh' hier noch am Rad. Warum klappt das nicht. Jetzt bekomme ich
sogar (was vorher nicht ging) ein scheinbar korrektes debug-Listing:
Wenn ich nämlich nach Installation des .SYS-Treibers debug d 0:30
starte,
erhalte ich folgende Interruptadrsse:
0000:0030  AD 00 02 12 6F EF 00 F0-9A 00 9E 09 65 04 usw.
wenn ich also dann u 1202:00AD eingebe, erhalte ich:
 1202:00AD 56            PUSH    SI
 1202:00AE 57            PUSH    DI
 1202:00AF FA            CLI
 1202:00B0 5F            POP     DI
 1202:00B1 5E            POP     SI
 1202:00B2 50            PUSH    AX
 1202:00B3 53            PUSH    BX
 1202:00B4 51            PUSH    CX
 1202:00B5 52            PUSH    DX
 1202:00B6 06            PUSH    ES
 1202:00B7 1E            PUSH    DS
 1202:00B8 56            PUSH    SI
 1202:00B9 57            PUSH    DI
 1202:00BA 55            PUSH    BP
 1202:00BB 8CCD          MOV     BP,CS
 1202:00BD 8EDD          MOV     DS,BP
 1202:00BF 8BEC          MOV     BP,SP
 1202:00C1 BA2000        MOV     DX,0020
 1202:00C4 B020          MOV     AL,20
 1202:00C6 EE            OUT     DX,AL
 1202:00C7 BAF803        MOV     DX,03F8
 1202:00CA B058          MOV     AL,58
 1202:00CC EE            OUT     DX,AL usw. ,
was doch durchaus schon so wie mein Interrupt aussieht!?
Da kann doch eigentlich (fast) nichts mehr verkehrt sein?

von Wolfram (Gast)


Lesenswert?

Es kommt nicht drauf an das du sauber in den Interrupt
reinkommst,sondern das du auch sauber wieder rauskommst
(gleiche Stackhöhe richtige rücksprungadresse)
installiere deinen Gerätetreiber und starte im Dos debug
ruf den Interrupt auf
und trace diesen Interrupt. achte auf stack ob cs und ds gleich sind
und ob du wieder sauber am ende des Interrupts bei deinem
Interruptaufruf landest.(die Stackhöhe muss dem entsprechen die sie vor
dem aufruf hatte und alle registerwerte muessen den vorhergehenden
registerwerten entsprechen)
wenn ich dein assemblerlisting betrachte fällt mir auf das cs nicht
gesichert wird aber im Interrupt verändert wird!
du solltest im mindesten alle register sichern  die im interrupt
verändert werden.
Wenn es dich tröstet: es ist der richtige interrupt
outportb(0x20,0x20); -> MOV     DX,0020
 1202:00C4 B020          MOV     AL,20
 1202:00C6 EE            OUT     DX,AL

Es wäre hilfreich, wenn du sagen würdest was du im Interrupt machen
willst, wahrscheinlich ist das mit ein paar zeilen assembler erledigt
und wenn du mit debug zurechtkommst, sollte dies auch keine Hürde mehr
darstellen.

von Hans M. (gast4711)


Lesenswert?

Hallo Wolfram, was der Interrupt machen soll sieht man im .com-File
weiter oben: Einfach bei eingehenden Zeichen ein ‘X‘ ausgeben.

Wenn ich bei der Initialisierungsroutine im Treiber einmal asm int 12;
schreibe, wird er ja auch aufgerufen und ein ‘X‘ ausgegeben.
Aber wenn dann im Betrieb ein Zeichen über die Schnittstelle kommt,
passiert im .sys nichts; im .com wird dagegen ‘X‘ korrekt
zurückgegeben.
Der Treiber wird also dann nicht einmal korrekt ausgeführt, bevor er
danach möglicherweise falsch verlassen wird.

Würde daher erst einmal den Eintritt des Interrupt in Frage stellen
(und zu erreichen versuchen), zumal ein falsches Verlassen meist durch
eine Windows-Messagebox mit unbekanntem Fehler angezeigt wird.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Windows-Messagebox und DOS-Devicetreiber? Was eigenartiges hast Du da
vor?

von Wolfram (Gast)


Lesenswert?

Also ich dachte du arbeitest unter reinem Dos! Das erklärt warum du
keine Probleme mit einem instabilen System nach dem Beenden deines
COM-Programmes hast.
Wenn du sowas unter windows macht dann wird sich deine Änderung des
Interruptvektors auf die jeweilige Dosbox auswirken und auch nur auf
sie. Auf jedenfall würde ich damit rechnen das die Windowsgerätetreiber
in jedem Fall Vorrang haben.
Du hast folgende Möglichkeiten:
1. Du benutzt reines Dos ohne das ein Windows dazwischen pfuscht
Allerdings frage ich mich schon warum im Jahre 2005 dies noch sein
muss
2. Schreiben eines Windowsgerätetreibers hier können durchaus auch
RealMode teile enthalten sein
3. Schreiben eines Programmes mit einem Hook
Das ist am ehesten vergleichbar mit einem Umgebogenen Interrupt
4.Portmonitor benutzen z.B. von Sysinternals
z.B. Wenn du eigentlich nur die Schnittstelle Debuggen willst bezüglich
Traffic

Welche dieser Möglichkeiten die beste ist, kann man nur sagen wenn du
beschreiben würdest, WAS du erreichen willst und warum es deiner
meinung nach ein Dosgerätetreiber sein muss.

CU Wolfram

von Hans M. (gast4711)


Lesenswert?

Es gibt für Windows und zeichenorientierte Device-driver ein sehr gutes
DOS-Simulations-Tool.

Das wäre ja auch verdammt zeitaufwendig, jedesmal den PC neu zu booten
und unter DOS zu programmieren...

Das Betriebssystem DOS schreiben übrigens die existierenden Programme
und sogar eine Art Norm für diesen Fall vor (also nichts anderes zu
machen).
Die Devicesimulation läuft in der DOS-BOX wie ganz normales DOS.
Natürlich muß ich in der gleichen DOS-Box vor Beendigung derselben auch
das debug starten
(klar, daß der Interrupt nicht so bleibt, wenn Windows die Box
schließt!).
Die Schnittstelle beobachte ich mit einem zweiten PC (alter Laptop mit
term90; reicht völlig)

Noch einmal zusammengefaßt, was ich vorhabe bzw. wo das eigentliche,
‘einzige‘ Problem ist:

Ich habe einen Devicetreiber (nennen wir ihn z.B. COMDRV.SYS) in C
geschrieben, der den Interrupt 12 (COM1)
Durch eine Routine ersetzt, die z.B. bei einem eingehenden Zeichen via
COM1 ein ‘X‘ zurückgibt.
Aus dem Treiber in der Initialisierungsroutine nach der Initialisierung
mit “asm int 12“ aufgerufen gibt die neue,Interruptfunktion er auch
tatsächlich ein ‘X‘ aus (soweit so gut). Doch wenn anschließend über
die RS232 ein Zeichen hereinkommt (die Baudrate u.a. stimmen natürlich)
passiert nichts.

Mit BCC –S erhalte ich für die Interruptfunktion wie gesagt folgendes
Assemblerlisting:

;  DUMMY far new_int(DUMMY)
  assume  cs:_TEXT
_new_int  proc  far
  push  si
  push  di
  cli
  pop      di
  pop      si
  push    ax
  push    bx
  push    cx
  push    dx
  push    es
  push    ds
  push    si
  push    di
  push    bp
  mov    bp,cs
  mov    ds,bp
  mov    bp,sp
;// ab hier die eigentliche Interruptfunktion (soweit OK)
  mov  dx,32
  mov  al,32
  out  dx,al
  mov  dx,1016
  mov  al,154
  out  dx,al
  mov  dx,1017
  mov  al,1
  out  dx,al
  mov  dx,1016
  in  al,dx
;// Ende der eigentlichen Interruptfunktion
  pop    bp
  pop    di
  pop    si
  pop    ds
  pop    es
  pop    dx
  pop    cx
  pop    bx
  pop    ax
  sti
  iret
  pop  di
  pop  si
  ret
_new_int  endp

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Solltest Du mit "DOS-Box" die DOS-VDM aktuellerer Windows-Versionen
meinen, so solltest Du Dir darüber klar sein, daß Dein
DOS-Devicetreiber darunter niemals mit der echten Hardware zu tun
bekommt, sondern immer nur mit einer mehr oder weniger brauchbar
virtualisierten Hardware.
Windows gaukelt DOS-Programmen die Standard-PC-Hardware (Serielle
Schnittstellen, Interrupt-Controller, Timer, DMA-Controller etc.) vor,
und das Verhalten dieser virtualisierten Geräte muss nicht zwingend
exakt den physikalischen Gegebenheiten unter "nacktem" DOS
entsprechen, es ist bloß recht genau angenähert.
Gerade bei den seriellen Schnittstellen gibt es größere Abweichungen,
daher funktionieren erstaunlich viele DOS-Programme, die die seriellen
Schnittstellen befummeln, nicht vernünftig unter Windows.
Das mag auch an der vergurkten Programmierung von Interrupt-Treibern
für DOS liegen, die sich manche Programmierer so einfallen lassen.

Ein (echter) Hardwareinterrupt der seriellen Schnittstelle wird
zunächst vom windows-eigenen Devicetreiber behandelt, dann wird dieser
Interrupt der DOS-VDM übergeben, die wiederum ihrer Hardwareemulation
mitteilt, daß sie einen simulierten Interrupt auslösen soll. Die
wiederum teil der simulierten seriellen Schnittstelle und dem
simulierten Interruptcontroller entsprechendes mit ...
Du wirst erahnen können, was das für ein Aufriss ist und wieviel
Rechenzeit das ganze kostet.

Auch unter einem Hardwarevirtualisierer wie VMWare oder VirtualPC sieht
das so aus, auch wenn dort die Hardwareemulation, die das DOS-Programm
zu sehen bekommt, sich deutlich genauer an der reellen Hardware
orientiert. Auch hier ist die gesamte Hardware virtualisiert, und das
DOS-Programm bekommt niemals die echte Hardware zu "sehen".

Vom Zeitverhalten her entspricht daher weder die DOS-VDM noch VMWare
etc. dem echter Hardware. Damit sind bestimmte timingkritische
Operationen weder in einer VDM noch unter VMWare möglich.

Wenn jetzt jemand auf die Idee kommt, sich das Leben mit
Windows-Devicetreibern à la "giveio.sys" zu vereinfachen, dann sollte
er das tunlichst sein lassen. Hardware, die Interrupts auslösen kann
(und dazu gehören die seriellen Schnittstellen nunmal), sollte man
damit auf gar keinen Fall manipulieren. Bestenfalls funktioniert es
einfach nicht (Interrupts kommen gar nicht erst in der DOS-VDM an),
schlimmstenfalls geht die Angelegenheit mächtig in die blaue Hose (Blue
Screen).

Letzlich bleibt noch die Frage, warum in drei Teufels Namen jemand sich
im Jahre 2005 hinsetzt und DOS-Devicetreiber programmiert ...

von Hans M. (gast4711)


Lesenswert?

Will sicher nicht undankbar sein, auch wenn bei so einem speziellen
Posting nach meiner Erfahrung ja nicht immer direkt ein Vorschlag zum
eigentlichen Problem (hier Interrupt) kommt. Trotzdem danke für die
Antworten.

Zu den letzten beiden Bemerkungen:
1. Die Devicetreiberemulation (ein ganz simples Programm namens
device.com) klappt durchaus schon fehlerfrei (hab’s eben
sicherheitshalber aber auch noch einmal unter reinem DOS probiert)

Und

2. Zur Farge warum .sys unter DOS habe ich weiter oben schon erwähnt
daß das wegen bestehender Programme von Fremdherstellern und normierter
Vorschriften leider nun einmal sein muß (sorry, liegt leider wirklich
nicht in meiner Macht, das zu ändern).

Bleibt das Problem, warum die Interruptfunktion beim treiberinternen
Aufruf klapppt und beim externen COM1-Event nichts passiert...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> Bleibt das Problem, warum die Interruptfunktion beim
> treiberinternen Aufruf klapppt und beim externen
> COM1-Event nichts passiert...


Weil a) das "COM1-Event" gar nicht ausgelöst wird, es b) wegen
fehlerhafter Programmierung des Interruptcontrollers nicht
weitergeleitet wird oder c) es in den Tiefen der DOS-VDM verschluckt
wird?

Hast Du das ganze mal unter "reinem" DOS ausprobiert?

von Wolfram (Gast)


Lesenswert?

Ich kann mich Rufus nur anschließen
Teste unter reinem Dos!
Wenn du wissen willst ob der interrupt im SYS-Treiber überhaupt
ausgelöst wird schreibe in die Interruptroutine einen "int 3" aufruf
und starte auf dem Computer wo der Gerätetreiber läuft einen älteren
Debugger. Dieser biegt den Int 3 auf sich um. Du kannst dir genauso mit
dem Debugger die Interruptroutine des Devicetreibers suchen und da einen
Breakpoint setzen (das ist das gleiche) Sende dann vom Laptop
Terminalprogramm ein Zeichen. Wenn Interruptcontroller und Serielle
Schnittstelle richtig programmiert sind solltest du dich im Debugger an
der Stelle wieder finden. Überprüfe was passiert wenn du aus der
Interruptroutine rausgehst.
Wenn du nicht in der Interruptroutine landest ist eine der 3 von Rufus
geschilderten Möglichkeiten eingetreten oder nach dem Laden deines
Devicetreibers macht nach irgendein anderer Devicetreiber oder Programm
etwas mit der seriellen Schnittstelle/Interruptcontroller zur
Initialisierung und killt dir irgendwas. Kontrolliere die gesamten
Portregister.
Zur Not noch folgender Versuch mach das was dein COM Programm tut
in einer Funktion des Devicetreibers. Rufe diese Funktion über einen
IOCTL Aufruf auf und teste danach nochmals ob es nicht funktioniert.
Wofür ist das ganze eigentlich
Schule/Studium/Praktikum oder Arbeit?
Das ganze ist ein ziemliches Stochern im Trüben wenn du so ein
Geheimnis um den Quellcode deines Gerätetreibers machst und einem nur
Stücken vorwirfst. Wer weiss was im Rest für Fehler gemacht werden.
Es steht dir auch die Möglichkeit mit Windowsgerätetreiber mit
Realmodeanteilen offen, dann könntest du dir sicher sein dass kein
Windows dazwischenfunkt.

von Hans M. (gast4711)


Lesenswert?

Hab' doch direkt oben geschrieben, daß der Fehler ohne device.com bei
reinem Dos auch auftritt (im übrigen funktioniert device.com wirklich
bei allen zeichenorientierten Treibern, warum immer dieses Mißtrauen?)

Debuggen will ich gar nicht, zumal ich mich da überhaupt nicht auskenne
(und das bei Devicetreibern beim Start ja auch nicht so einfach möglich
ist, da ja in dem Zustand beim Booten nicht einmal DOS komplett geladen
ist).
Debug.com hatte ich nur mal so gestartet um zu sehen, ob der Interrupt
auch wirklich ersetzt wurde (im Speicher liegt).

Bei den Portregistern liegt es ganz sicher nicht (s.u.), zumal es nicht
um die serielle Schnittstelle geht, sondern wie man bei einem
Device-Treiber in C überhaupt einen Interrupt ersetzt. Könnte da auch
jeden anderen Interrupt ersetzen. Das würde im Moment noch genau so
wenig laufen.

Den Quellcode würde ich dabei ungern gleich komplett veröffentlichen,
sondern einzelnen wie Wolfram zuschicken wollen, aber das geht ja hier
glaube ich leider nicht.

Auf jeden Fall liegt es totsicher, erwiesenermaßen nicht an Windows
oder falschen UART-Registern. Das Ganze hat wie ich von anderer Stelle
gerade erfahre auch vermutlich viel mit Stack und Compileroptionen zu
tun.

Wenn ich übrigens bei INPUT, IOCTL_IN, OUTPUT, OPEN oder CLOSE asm int
12 einfüge, läuft die Interruptfunktion jeweils einwandfrei und gibt
‘X’e aus. Nur eben bei eingehenden Zeichen per COM1 passiert
unerklärlicherweise nichts.
Also liegt sie vielleicht doch an falscher Stelle im Speicher, was nach
obigem debug aber auch wieder nicht sein kann!?

von Wolfram (Gast)


Lesenswert?

Es ist definitiv kein Misstrauen, deine Informationspolitik bringt nur
einige Probleme mit sich. Ich versuche sie mal darzustellen.
1. das ganze läuft unter windows (sehr wichtige Info)
ein Windowsgerätetreiber (ist er installiert oder nicht?) kann
jederzeit einstellungen an den Portregistern ändern wie Abschalten der
interrupts die dieses Gerät auslöst ganz davon abgesehen das er dies
auch am Interruptcontroller tun könnte also besten mischt sich da
überhaupt kein WindowsTreiber ein
2. gehört Int12 exklusiv der Karte? sonst reagierst du auf Interrupts
die eventuell gar nicht der Karte gehören beziehungsweise reagiert
möglicherweise ein anderer (windows-)Treiber
3. Ist es eine normale serielle Schnittstelle oder was ist das
(16450/16550 etc.?)
4. Das es an Stack und Compileroptionen liegt ist sehr naheliegend
deswegen kam schon in mehreren Postings die Empfehlung in den Interrupt
reinzutracen in ihm die Register zu überprüfen besonders die
segmentregister (CS=DS?) und besonders beim rausgehen auf die gleichen
werte wie beim reingehen zu achten ->darauf kam bei dir bis jetzt keine
Reaktion ,das was du tust ist ein Indiz das es wahrscheinlich korrekt
ist
5. Die Interruptnummer die du angibst ist recht ungewöhnlich für eine
normale serielle Schnittstelle laut deinem Programmlisting
meinst du int 12 dezimal, bist du dir da sicher? Nicht das ein ganz
anderer Interrupt ausgelöst wird.
Die optionen die du für BCC 3.1 angibst sind relativ nichtssagend
Einen so alten Dos C Compiler benutzt man schon eine ganze weile nicht

mehr.
bitte schreibe dazu Programmiermodell /Segmentanordnung also auch was
sie bedeuten
6. Um das debuggen wirst du nicht herumkommen, das muesste ich auch tun
wenn ich sowas entwickeln muesste, ausserdem ist es der einzige Weg zu
ueberpruefen ob das ganze auch korrekt ablaeuft. Eigentlich muss ich
bei jedem Projekt einer gewissen Grösse mit dem Debugger überprüfen was
los ist. Manchmal auch bis in die Maschinenspracheebene.
 Du bist hier ganz einfach an einem Punkt wo du auch in C beachten
musst welche Segmentregister beteiligt sind und was in den
Portregistern ablaeuft.
Das dieses Wissen mit einem Assemblerhintergrund eher vorhanden ist, ja
das ist so.
7. Ob der entsprechende Interrupt ausgelöst wird, kann man am ehesten
mit der Int3 methode überprüfen, es sei denn du willst dich direkt an
die Hardwareinterruptleitung hängen und diese mit dem Oszi überwachen.
Wenn er nicht ausgelöst wird könnte es sein das du am falschen Int
hängst (dann wundert es mich aber das es mit dem COM Porggi
funktioniert) oder jemand anderes hat den Int zwischenzeitlich
umgeleitet oder abgeschaltet.
8. Ich verstehe nicht ganz warum du nicht sagst wofür das ganze ist.
Das irgendjemand deine Idee für einen Dosgerätetreiber klaut ist wohl
im Jahre 2005 äusserst unwahrscheinlich. Es dürfte wohl eher dazu
führen das auch andere Leute dir aus Nostalgiegründen helfen.
Ist übrigens auch mein Grund, ich habe ziemlich viel in x86 Assembler
geschrieben und den x86 sehr hardwarenah programmiert. Allerdings muss
man da sehr genau wissen auf WELCHE HARDWARE man zugreift.
Ein funktionstuechtiger zeichenorientierter Gerätetreiber in C der
einen Interrupt auf sich verbiegt wäre eine Grundlage auf der man
effektiv diskutieren könnte. Die eigentlichen Funtionen kannst du ja
leer lassen. Alles andere ist nur stochern im Nebel und das bringt
nichts.
Es gäbe da noch andere Interruptquellen an die du deine
Interruptroutine erstmal testweise dranhängen könntest
z.B. int 9h Keyboard bei jedem Tastendruck
oder int 1Ch 18,2mal pro sekunde
allerdings würde dies erfordern dass du innerhalb der Interruptroutine
in die alte verzweigst.

von Hans M. (gast4711)


Lesenswert?

Nochmal zu den einzelnen z.Teil so nicht ganz richtig dargestellten
Punkten:

1. Das ganze läuft unter DOS (nicht Windows!).

2. Int 12 ist der Interrupt von COM1 und funktioniert.
   (siehe mein testhalber baugleich geschriebenes RS232.COM)

3. Es ist die ganz normale COM1  .

4. Der interrupt ist 6 Postings weiter oben abgedruckt (Fehler tritt
sowohl bei int_vector[25]= _CS wie auch bei _DS auf).

5. Die serielle Schnittstelle COM1 hat den Interrupt 12 (dez)  =  0Ch !


Die Optionen zu BCC kann ich auch nicht näher erklären, sie werden als
Hilfetext ausgegeben, wenn man BCC.EXE eintippt. Den Compiler samt
Hilfedatei kann man sich unter dem Suchbegriff "Wolfenstein" als
"Free BCC" aus dem Netz downloaden und ich gehe schon davon aus, daß
jemand, der mir helfen kann, ihn einmal ausprobiert haben muß.

6. Das Debuggen in Echtzeit dürfte bei einem Devicetreiber fast
unmöglich aber auch nicht erforderlich sein (zumal DOS zur
Treiberladezeit noch gar nicht vollständig geladen ist).

7. Sobald bei korrektem Device-Treiber der Interrupt ausgelöst wird,
erscheint das Echo auf meinem Laptop.
Mit asm int 12 läuft es ja auch. Und mit meinem COM-Programm (s.o.)
klappt ja auch der Interrupt.
Da verbiegt übrigens auch niemand etwas (weder Windows noch sonst ein
Task).

8. Es gibt schon eine wenn auch sehr kleine Gefahr, daß jemand die Idee
klaut, auch wenn der Markt klein ist. Habe einfach schon zu viel
Arbeitszeit in die Fehlersuche hineingesteckt, als daß ich der
Konkurrenz ein paar tausend Euro Entwicklungsarbeit schenken möchte (so
Preise berechnen die nämlich; sind ja nicht alle arbeitslos wie ich).

Auf Int9 umzusteigen ist zwar denkbar, aber mein Laptop und die
Interruptfunktion selber funktionieren ja gut, so daß ich keine neuen
Experimente brauche.

Aber es gibt Profis, die über so etwas alles nur lachen und das mal
eben (ohne Debugging) aus dem Ärmel schütteln (nur helfen die einem
meist nicht umsonst und beobachten wohl auch nicht so ein Forum.....).

von Hans M. (gast4711)


Lesenswert?

So Ihr lieben. Habe soeben von einem Profi die Lösung erfahren.
Will Euch diese hier auch nicht vorenthalten:
Man kann nämlich bei bcc mit dem Aufruf 'bcc +compile.cfg ...' eine
Optionsdatei wie z.B. 'compile.cfg' angeben (gaht etwas länglich auch
über die Kommandozeile aus dem Batch), in der man auch alle Segmente auf
ein Segment legen kann; etwa in der Art
-zCCOMMON
-zPCGROUP
-zACOMMON
-zRCOMMON
-zSCGROUP
usw.          -     Jetzt klappt alles!

Aber allen erst einmal Danke für das Bemühen und noch viel Erfolg bei
Euren eigenen Projekten!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.