Aus irgendeinem Grund hat es sich mein Programm anders überlegt und funktioniert jetzt, dabei bin ich der Meinung, nichts gemacht zu haben. Falls es jemanden interessiert ist hier nochmal der Quelltext des funktionierenden Programms. Es ist zwar sehr speziell an diese eine Aufgabe angepasst, aber mit ausreichend Kommentaren versehen, so dass man es mit ein paar Veränderungen auch für andere Aufgaben umschreiben könnte.
Code:
; ------------------------------
; Kommunikation mit einem Multimeter
; an der seriellen Schnittstelle
;
; Verwendetes Verfahren: POLLING
; Der Empfangspuffer wird auf
; empfangene Zeichen geprüft und
; gegebenenfalls ausgelesen.
;
; ------------------------------
segment stapel stack
resb 80h ; 128 Byte für den Stack
; ------------------------------
; Variablen
; ------------------------------
segment daten
zeichen: resb 1 ; Zum Zwischenspeichern eines empfangenen Bytes
datei_name: db 'messwert.dat'
datei_handle: resb 1
messwert: times 56 db 'x'
counter: db 00h ; Zähler
com1 dw 03F8h ; Adresse des COM1-Ports / I/O-Register / DLL
com11 dw 03F9h ; Interrupt-Enable-Register (IER) / DLH
com12 dw 03FAh ; Interrupt-ID-Register (IIR)
com13 dw 03FBh ; Line-Control-Register
com14 dw 03FCh ; Modem-Control-Register
com15 dw 03FDh ; Line-Status-Register
com16 dw 03FEh ; Modem Status Register
segment code
; ------------------------------
; Hauptprogramm
; ------------------------------
..start:
; Das Segment "daten" als Datensegment festlegen
; (da liegen unsere Variablen)
mov ax, daten
mov ds, ax
call initialisieren
call d_senden
call timer_start
schleife_empfang:
; Wird wiederholt, solange noch keine
; 14 Bytes empfangen worden sind
call timer_check
ja ende_schleife ; Wenn mehr als 5 Sekunden nichts empfangen
call zeichen_empfangen_check ; Zeichen im Empfangsregister?
jne schleife_empfang ; Wenn nein, zurück zum Anfang ...
; sonst (wenn Zeichen im Empfangsregister sind)
call zeichen_empfangen
call zeichen_in_string_speichern
call zeichen_ausgeben
call timer_start
call counter_up_and_check
jb schleife_empfang
ende_schleife:
call string_in_datei_schreiben
; Beenden
mov ah, 4Ch
int 21h
; Ende des Programmes
; ------------------------------
; Unterprogramme
; ------------------------------
; ---------------
initialisieren:
; ---------------
; Empfangsregister auslesen (muss für Initialisierung leer sein)
call zeichen_empfangen
; Interrupts ausschalten
mov dx, [com11]
mov al, 00h
out dx, al
mov dx, [com14] ; Modem Control
mov al, 00000011b
; PC betriebsbereit >> .......1
; PC will senden (nicht empfangsbereit) >> ......1.
; Interrupts aus >> ....0...
out dx, al
; DLAB einschalten
mov dx, [com13]
mov al, 10000000b
; DLAB >> 1.......
out dx, al
; Einstellen der Baudrate: 115200/Baudrate(=9600) = 12
; Low Byte = 12
mov dx, [com1]
mov al, 00001100b
out dx, al
; High-Byte = 0
mov dx, [com11]
mov al, 00h
out dx, al
; DLAB ausschalten
mov dx, [com13]
mov al, 00h
out dx, al
; Line Control setzen
mov dx, [com13]
mov al, 00000110b
; Kodierung (7 Bit) >> ......10
; 2 Stopbits >> .....1..
; keine Parität >> ..000...
out dx, al
ret
; ---------------
d_senden:
; ---------------
; Sendet "D" und setzt PC auf empfangsbereit
; PC will ein Zeichen senden
mov dx, [com14]
mov al, 00000011b
out dx, al
; Zeichen ins Senderegister
mov dx, [com1]
mov al, 44h; "D" in ASCII
out dx, al
mov dx, [com15]; Line Status
d_senden_warten_bis_senderegister_leer:
nop
in al, dx
and al, 01100000b
cmp al, 01100000b
jne d_senden_warten_bis_senderegister_leer
; PC auf empfangsbereit setzen
mov dx, [com14]
mov al, 00000001b
out dx, al
ret
; ---------------
zeichen_empfangen_check:
; ---------------
mov dx, [com15] ; Line Status
in al, dx
and al, 00000001b
cmp al, 00000001b
; Empfangsregister enthält Daten >> .......1
ret
; ---------------
zeichen_empfangen:
; ---------------
mov dx, [com1]
in al, dx
mov [zeichen], al
ret
; ---------------
zeichen_ausgeben:
; ---------------
mov ah, 02h
mov dl, [zeichen]
int 21h
ret
; ---------------
zeichen_in_string_speichern:
; ---------------
mov bx, messwert
mov ax, [counter]
add bx, ax
; Zeichen
mov al, [zeichen]
; Zeichen in String speichern
mov byte [ds:bx], al
ret
; ---------------
string_in_datei_schreiben:
; ---------------
; Datei erstellen
mov ah, 3Ch ; Erstellen
lea dx, [datei_name]
int 21h
; Datei öffnen
mov ah, 3Dh ; Öffnen
mov al, 01h ; Zugriffsmodus, nur schreiben
lea dx, [datei_name]
int 21h
mov [datei_handle], ax
; Datei beschreiben
mov ah, 40h ; Schreiben
mov bx, [datei_handle]
mov cx, 38h ; 56 Zeichen (eine Messung)
mov dx, messwert ; Offset von messwert
; Datei schließen
mov ah, 3Eh ; Schließen
mov bx, [datei_handle]
int 21h
ret
; ---------------
timer_start:
; ---------------
; Timer wird auf 0 gesetzt
; 18,2 Erhöhungen pro Sekunde
mov ah, 01h
mov cx, 0000h
mov dx, 0000h
int 1Ah ; Zugriff auf Systemuhr
ret
; ---------------
timer_check:
; ---------------
mov ah, 00h
int 1Ah
; Gibt Timerstand in DX aus
cmp dx, 005Bh ; 5 Sekunden
ret
; ---------------
counter_up_and_check:
; ---------------
; Überprüft, ob schon 14 Bytes empfangen wurden
mov ah, [counter]
inc ah
mov [counter], ah
cmp ah, 0Eh ; 14 Bytes empfangen?
ret
Jetzt versuche ich noch, das Ganze interruptgesteuert ablaufen zu lassen, das wäre natürlich etwas eleganter. Um das Ganze zu testen habe ich erstmal versucht, bei Tastatureingabe bzw. dem entsprechenden Interrupt eine bestimmte Aktion auszuführen, aber irgendwie funktioniert es nicht. Weiß vielleicht jemand, woran es liegen könnte? Ich habe inzwischen rausgefunden, dass offensichtlich irgendwas schiefläuft, während ich die Speicheradresse meiner Interrupt-Service-Routine in die Interrupt-Vektor-Tabelle schreibe. Es funktioniert aber auch nicht, wenn ich die vorgefertigten Funktionen dafür benutze, das habe ich schon ausprobiert.
Code:
; ------------------------------
; Interrupt-Test
;
; Bei Tastatureingabe wird eine
; eigene Interruptroutine vor der
; Standard-ISR aufgerufen
; ------------------------------
segment daten
alter_iv: resb 4 ; 4 Byte für alten Interrupt-Vektor
segment code
..start:
mov ax, daten
mov ds, ax
call iv_ersetzen
call timer_start
; 5 Sekunden warten
; Zeit zum Ausprobieren
schleife:
nop
nop
nop
call timer_check
jb schleife
; Beenden
mov ah, 4Ch
int 21h
; ---------------
iv_ersetzen:
; ---------------
; Alten IV speichern
xor ax, ax
mov es, ax
mov bx, 0024h
mov ax, word [es:bx]
mov word [alter_iv], ax
mov ax, word [es:bx+2]
mov word [alter_iv+2], ax
; Neuen IV in Tabelle eintragen
xor ax, ax
mov es, ax
mov bx, 0024h
cli
mov word [es:bx], cs
lea ax, [neue_isr]
mov word [es:bx+2], ax
sti
ret
; ---------------
neue_isr:
; ---------------
pushf
pusha
push es
; Eigene ISR
in al, 60h ; Eingegebenes Zeichen (Scancode)
mov bx, 0B800h
mov es, bx ; ES auf den Bildschirmspeicher
mov bx, 0000h ; Erstes Zeichen, oben links vermutlich
mov byte [es:bx], al
pop es
popa
; Alte ISR
call far dword [alter_iv]
iret
; ---------------
timer_start:
; ---------------
; Timer wird auf 0 gesetzt
; 18,2 Erhöhungen pro Sekunde
mov ah, 01h
mov cx, 0000h
mov dx, 0000h
int 1Ah ; Zugriff auf Systemuhr
ret
; ---------------
timer_check:
; ---------------
mov ah, 00h
int 1Ah
; Gibt Timerstand in DX aus
cmp dx, 005Bh ; 5 Sekunden
ret