MASM32-SDK (Dateizugriffe bei Windows Konsolapplikationen):
Testerei mit lineout$ und Dateizugriff über Zwischenpuffer

Bei den beiden auf der vorhergehenden Webseite beschriebenen Übungsprogrammen erfolgt die Speicherung für jeden eingetippte Textzeile einzeln, d.h. jede Zeile erzeugt genau einen Schreibzugriff auf die Ausgabedatei.

Besser wäre es, mehrere Zeilen in einem Zwischenspeicher zu sammeln und sie dann mit einem einzigen Schreibzugriff in die Datei zu speichern: Dies Verfahren entlastet den Computer.

Speziell dies Verfahren wird von dem Makro lineout$ unterstützt.

lineout$ und writeline

lineout$ - Kopiere Textzeile von einem Anwenderbereich in einen Pufferspeicher aus MASM32 Library Reference / Macro Categories / Memory Line IO Macros /lineout$
mov spos, lineout$(source,buffer,spos,op_crlf)

Kopiert eine Textzeile aus einem Anwenderbereich in einen Pufferspeicher. Bietet die Möglichkeit, dabei CR LF am Zeilenende zu ergänzen.

Parameter:
1. source - der Anwenderbereich soll eine mit 0 abgeschlossene Zeichenkette enthalten
2. buffer- der Zielpufferspeicher, in den die Zeichenkette kopiert werden soll
3. spos - dies Variable enthält beim Aufruf die Position im Zielpufferspeicher, ab den der String zu kopieren ist. Der erste Eintrag in einem Zielpuffer beginnt auf Position 0
4. op_crlf gibt an, ob ein Zeilenwechsel aus CR und LF am Zeilenende hinzugefügt werden soll: 0=ja, 1=nein

Rückgabewert: Zurückgegeben wird die nächste freie Schreibposition im Zielpufferspeicher. Außerdem steht in ECX steht die Anzahl der zum Zielpufferspeicher geschriebene Bytes.
Hinweise: Der Zielpufferspeicher muss groß genug sein, um alle in ihn zu schreibende Bytes aufnehmen zu können.

Fast die gleiche Funktion wird unter MASM32 Library Reference / In Memory Text Read and Write / writeline angeboten. Dort ist als Aufrufmuster angegeben:

writeline proc source:DWORD,buffer:DWORD,spos:DWORD,flag:DWORD

Rückgabewerte:
EAX enthält den neuen Positionszeiger „spos”. ECX enthält die die Anzahl der zum Zielpufferspeicher geschriebene Bytes.

Das Quellprogramm zu writeline steht unter masm32/m32lib/writeline.asm. Die Makrodefinition zu lineout$ ist in masm32/macros/macros.asm zu finden. Ihr Herz besteht aus diesem Aufruf

invoke writeline,reparg(source),buffer,spos,op_crlf

In der Originalbeschreibung wird nicht deutlich geschrieben, dass die erste mögliche Einschreibposition in den Zielpufferspeicher den Wert Null hat.

Anbei ein rasch geschriebenes Testprogramm, mit dem sich diese Eigenschaft erforschen lässt:

comment * ------------------------------------
Modifiziert aus masm32\examples\exampl06\getini\getini.asm

Testprogramm zum Pruefen der Adressierung bei lineout$
Assemblieren als Konsolprogramm
-------------------------------------------- *

      .486                      ; create 32 bit code
      .model flat, stdcall      ; 32 bit memory model
      option casemap :none      ; case sensitive

.XLIST
    include \masm32\include\windows.inc
    include \masm32\macros\macros.asm

	include \masm32\include\user32.inc
    include \masm32\include\kernel32.inc
    include \masm32\include\masm32.inc
	include \masm32\include\msvcrt.inc
	include \masm32\include\debug.inc           ;fuer debug

    includelib \masm32\lib\user32.lib
    includelib \masm32\lib\kernel32.lib
    includelib \masm32\lib\masm32.lib
	includelib \masm32\lib\msvcrt.lib
	includelib \masm32\lib\debug.lib            ;fuer debug
.LIST

Main PROTO              ;erforderlich weil Main eine Prozedur ist

DBGWIN_DEBUG_ON = 1 ;0 or 1. 1 if you want to include debug info into the program
DBGWIN_EXT_INFO = 1 ;0 or 1. 0 if you don't want to include extra debug info into the program

.data
wpos       dword    0
buffer     db       260 dup(0)
lpbuffer   equ      $ - buffer

zeile      db       "AAAABBBB",0        ;8 Zeichen

.code
start:
      invoke Main
      inkey
      invoke ExitProcess,0

;-------------------------------
Main PROC

    LOCAL pMem  :DWORD
    LOCAL flen  :DWORD
    LOCAL rpos  :DWORD
; ----------------------

   DumpMem offset wpos, 32, "wpos + buffer vor lineout$"

   mov wpos, lineout$(ADDR zeile,ADDR buffer,wpos,0)

   DumpMem offset wpos, 32, "wpos + buffer nach lineout$"

   ret
Main ENDP
end start

MASM32-SDK, Testergebis zu lineout$ Das Testergebnis ist eindeutig: Die erste Schreibposition ist 0 und die nächste mögliche Einschreibposition beginnt hinter dem angehängten Zeilenwechsel (0D 0A).

Bevor die Zeile vom Anwenderbereich in den Zielpufferspeicher kopiert wird, muss sichergestellt sein, dass der freie Platz im Zielpufferspeicher
  • alle zu kopierenden Bytes,
  • einschließlich des ggf. angehängten Zeilenwechsels (2 Bytes) und
  • des immer angehängten neuen Stringendes (1 Byte Inhalt 0)
aufnehmen kann.

Der noch freie Platz im Zielpufferspeicher errechnet sich aus dessen Gesamtlänge minus der nächsten Einschreibposition. Bei der Speicherung von Sätzen die den Zeilenwechsel bereits enthalten, wäre dies die Gesamtlänge minus 3.

Quellprogramm Version 3 (lineout$ und fprintc, Zeileneingabe mit StdIn)
Einige Hinweise zum Ablauf des Programmes stehen unterhalb des Quellprogrammtextes.
comment * ---------------------------
Teile der Dateizugriffe nach masm32/examples\exampl07/ppfileio.asm

                    Build as a CONSOLE mode application

Testprogramm fuer lineout$ und fprintc
    Es werden mehrere Zeilen ueber die Konsole eingetippt und in die
    gewuenschte beim Programmaufruf angegebene Datei gespeichert

    Falls keine Datei angegeben wurde, erfolgt die Speicherung
    nach c:\temp\versuch.txt

---------------------------------------- *

    .486                       ; create 32 bit code
    .model flat, stdcall       ; 32 bit memory model
    option casemap :none       ; case sensitive

.XLIST

    include \masm32\include\windows.inc
    include \masm32\macros\macros.asm

    include \masm32\include\user32.inc
    include \masm32\include\kernel32.inc
    include \masm32\include\masm32.inc
    include \masm32\include\msvcrt.inc
    include \masm32\include\debug.inc           ;fuer debug

    includelib \masm32\lib\user32.lib
    includelib \masm32\lib\kernel32.lib
    includelib \masm32\lib\masm32.lib
    includelib \masm32\lib\msvcrt.lib
    includelib \masm32\lib\debug.lib            ;fuer debug
.LIST

Main    PROTO              ;erforderlich weil Main eine Prozedur ist

DBGWIN_DEBUG_ON = 1 ;0 or 1. 1 if you want to include debug info into the program
DBGWIN_EXT_INFO = 1 ;0 or 1. 0 if you don't want to include extra debug info into the program
; Konstanten
; ------------------------------------------------
bel     equ     7       ;Klingel
bs      equ     8       ;Backspace
ht      equ     9       ;Horizontaler Tabulator
lf      equ     0ah     ;Linefeed
vt      equ     0bh     ;Vertikaler Tabulator
ff      equ     0ch     ;Formfeed
cr      equ     0dh     ;Carriage Return
Ae      equ     8eh     ;A DOS-ASCII-codiert
Oe      equ     99h     ;O
Ue      equ     9ah     ;U
ae      equ     84h     ;a
oe      equ     94h     ;o
ue      equ     81h     ;u
sz      equ     0e1h    ;?
ctrlZ	equ		1ah		;CTRL Z

.data
lpos       dword    0    ;Stringlaenge im Low-byte, Position von ctrlZ im High-byte von cx
savech     db       0    ;sichert ob ctrlZ

buffer1    db       0               ;spaeter: switch Teil / oder Datenpuffer Eingabezeile
buffer1p1  db       260 dup(0)
lpbuffer1  equ      $ - buffer1

;buffer 2 ist fuer das Testen der Programmlogik auf 64 Bytes verkuertzt. Für die echte Nutzung
;wird man buffer 2 erheblich groeßer ansetzen. Eine typische Groeße waere 4096 Bytes.
wpos       dword    0
buffer2    db       64 dup(0)      ;buffer2 fuer Dateiname / oder Zielpufferspeicher
lpbuffer2  equ      $ - buffer2

;Defaultwerte
fname	db	'C:\temp\versuch.txt',0
lfname  equ $ - fname

hilfe db "lineout Testprogramm Konsoleingabe mit Abspeicherung in Datei",cr,lf
      db  "Aufruf: [/?|dateiname]",cr,lf
      db  "Wenn kein Dateiname angegeben ist, erfolgt die Speicherung in"
      db  " c:/temp/versuch.txt",cr,lf
      db  "Das Programm nimmt mehrzeilige Texteingaben entgegen und speichert sie in",cr,lf
      db  "einer Datei",cr,lf
crlf  db  cr,lf,0         ;Zeilenwechsel  ;ergaenzt hilfe

msg1  db  "Eingabe mehrere Zeilen. Eingabeende mit Ctrl Z ab der "
      db  "zweiten Eingabestelle einer Zeile",cr,lf,0
msg2  db  62,0

 .code
start:
    invoke Main
    inkey
    invoke ExitProcess,eax

;-------------------------------
Main PROC
    LOCAL hFile :DWORD              ;file handle
    LOCAL bwrt  :DWORD              ;variable for bytes written
    LOCAL cloc  :DWORD              ;current location variable
    LOCAL txt   :DWORD              ;text handle
    LOCAL flen  :DWORD              ;file length variable
    LOCAL hMem  :DWORD              ;allocated memory handle

    invoke  GetCL,1,ADDR buffer1    ;Parameter abfragen
    cmp     eax, TRUE
    je      @F
    invoke  MemCopy,ADDR fname,ADDR buffer2,lfname
    jmp	    param99                 ;kein Parameter angegeben

;1. Parameter: Switch oder Dateiname?
@@:
    mov     byte ptr al,buffer1
    cmp     al,'/'
    jne     @F                      ;O.K., Dateiname in buffer1

    mov     byte ptr al,buffer1p1
    cmp     al,'?'
    jne     @F                      ;Dateiname
    call    help                    ;/? in buffer1. Also Hilfstextanzege
    ret

@@:	mov     flen, len(ADDR buffer1)     ;Dateiname laut Parameter nach buffer2
    invoke	MemCopy,ADDR buffer1,ADDR buffer2,flen

param99:
   DumpMem offset lpos, 32, "lpos + byte + buffer1"
   DumpMem offset wpos, 32, "wlpos + buffer2"

   .if rv(exist,ADDR buffer2) != 0       ;if file already exists
        test fdelete(ADDR buffer2), eax  ;delete it
   .endif

; ----------------------------------
; create the file
; ----------------------------------
    mov    hFile, fcreate(ADDR buffer2) ;create the file
    xor    eax,eax
    mov    dword ptr wpos,eax           ;Ausgabezwischenspeicher leer setzen
    invoke StdOut,ADDR msg1             ;Anzeige Bedienungsanleitung

konsoleingabe:
    invoke StdOut,ADDR msg2	            ;Anzeige Prompt
    invoke StdIn,ADDR buffer1,lpbuffer1 ;lesen Eingabezeile

  DumpMem offset lpos, 32, "pos + buffer1 nach Taste"

    mov    edi,offset buffer1

lenstr: xor	ecx,ecx             ;laenge und ggf. position von ctrlZ des
;                                ueber edi adressierten strings nach ecx

@@: mov     al,[edi]
    or      al,al               ;Laenge +1
    inc     edi
    cmp     al,ctrlZ            ;Eingabeende angefordert?
    jne     @B

    dec     cl
    mov     ch,cl   	        ;Position von ctrlZ
    dec     edi
    xor     al,al
    mov     byte ptr [edi],al   ;Neues Stringende an Position von ctrlZ und fertig!

@@: mov	    savech,ch           ;sichert ob ctrlZ
    xor     ch,ch
    mov     lpos,ecx            ;Stringlaenge nun in lpos

;    fprint  hFile,offset buffer1    ;write line to file
;--- ab hier gepuffertes Schreiben
  mov     eax, lpbuffer2        ;lpbuffer2 ist die Gesamtlänge des Zielpufferspcichers
  sub     eax,3                 ;davon geht CR, LF und die 0 am Stringendes ab
  sub     eax,wpos              ;noch frei
  cmp     eax,lpos
  jg      @F                    ;Sprung, wenn noch ausreichend Platz ist

  fprintc hFile,offset buffer2   ;schreibt den belegten Teil des Zielpuffers in die Datei
  xor     eax,eax
  mov     dword ptr wpos,eax    ;nun ist der Zielpuffer wieder ganz leer
@@:                             ;es kann in den Zwischenpuffer geschrieben werden!
  mov     wpos, lineout$(ADDR buffer1,ADDR buffer2,wpos,0)

  DumpMem offset lpos, 32, "lpos + byte + buffer1"
  DumpMem offset wpos, 80, "wpos + buffer2"
;---- bis hier gepuffertes Schreiben
    mov    ah,savech
    or     ah,ah                ;fertig, wenn ah <> 0
    jz     konsoleingabe

    xor    eax,eax              ;Eingabeende? Ist Zwischenpuffer leer?
    cmp    dword ptr wpos,eax
    je     @F                   ;er ist leer wenn wpos  gleich 0

    lea    edi,buffer2          ;0 kennzeichnet Stringende
    add    edi,wpos
    xor    al,al
    mov    [edi],al
    fprintc  hFile,offset buffer2

@@:
    fclose hFile                ;Datei schliessen
    mov    eax,TRUE             ;Errorlevel O.K.
    ret
Main ENDP

; -----
; Unterprogramm Hilfstext ausgeben
; -----
help PROC
    print   offset  hilfe
    mov     eax,99               ;Errorlevel 99 und Schluss!
    ret
help ENDP

 end start
Einige Hinweise zum Ablauf des Quellprogramms Version 3 (lineout$ und fprintc, Zeileneingabe mit StdIn)
lpos dword 0und
lpos dword 0: Mit dem Positionszeiger lpos wird die Position im Eingabepuffer verwaltet. Mit wpos wird die nächste Schreibposition im Zielpufferspeicher verwaltet.

buffer2 db 64 dup(0): Nur für den Test, ob die Zielpufferverwaltung richtig funktioniert, ist der Zielpufferspeicher klein angelegt. Wenn die Teste hier keinen Fehler gefunden haben, wird man den Zielpufferspeicher größer anlegen. Ich habe ihn später auf 4096 Bytes vergrößert: buffer2 db 4096 dup(0)

fprint hFile,offset buffer1 wurde durch das vorgestellte Semikolon zum Kommentar. Die Befehle zwischen den Kommmentarzeilen ;--- ab hier gepuffertes Schreiben und ;--- bis hier gepuffertes Schreiben ersetzen den auskommentierten Schreibbefehl. Das gepufferte Schreiben schreibt nur dann in den Puffer buffer2, wenn noch ausreichend Platz vorhanden ist. Dies wird durch den Vergleich cmp eax,lpos festgestellt. Wenn der freie Platz in buffer2 zu klein ist, wird der Inhalt von buffer2 mit fprintc hFile,offset buffer2 zur Ausgabedatei entsorgt. Dabei wird ein Zeilenwechsel ergänzt.

cmp dword ptr wpos,eax: nachdem die Eingabeverarbeitung ein ctrl Z gefunden hatte und demzufolge die eingetippten Daten bis unmittelbar vor ctrl Z in den Zielpufferspeicher buffer2 abgelegt hatte, ist das Programm fast fertig. Für den Fall, dass noch Daten aus dem Eingabepuffer buffer1 in die Datei geschrieben werden müssen, wird einmalig fprintc hFile,offset buffer1 durchgeführt. Vorher wurde noch im Eingabepuffer eine Schlussnull am Ende der auszugebenden Daten ergänzt.

Letztes Upload: 24.03.2023 um 11:35:16 • Impressum und Datenschutzerklärung