Autor Thema: Assembler-Programm will nicht...  (Gelesen 4214 mal)

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« am: 14. February 2007, 20:51 »
Hallo,

ich versuche ein Programm (NASM als Assembler) zu schreiben, welches eigentlich ganz simpel ist:
-> Einen 64 KB-Puffer bereitstellen
-> darin 64 KB einer Datei lesen
-> diese Datei an LPT1 senden (direkt über die Ports).

Hintergrund: An LPT1 (Basisport 0x378) hängt ein DAC, mit dem ich Audiodaten an einen Verstärker senden kann. Timing spielt (noch) keine Rolle, das kann ich mit einer Schleife vorerst verzögern.

Mein Problem: In dem Moment, wo ich den int 21h aufrufe, bricht mein gesamtes Programm zusammen. Den Code zum Berechnen der aktuellen Programmgröße habe ich aus dem Netz.

Vielleicht kann mir jemand helfen; ich poste einfach mal den gesamten Code.

;------------------------------------
;- PLAYER                           -
;- Play RAW audio files on DAC/LPT1 -
;------------------------------------
org 100h

; set program's memory size to 64K (limit of "tiny" COM files) - CS=DS=ES=SS
        mov bx,ss
        mov ax,es
        sub bx,ax
        mov ax,sp
        add ax,14h
        shr ax,4
        add bx,ax
        mov ah,04ah
        int 21h

; get memory (64K)
        mov ah,48h
        mov bx,1000h    ; 4096 * 16 Bytes = 64 KBytes
        int 21h
        push ax

; open the file
        mov ah,3Dh
        mov al,0        ; read only
        mov dx,FILENAME ; ds:dx = ASCIIZ filename
        int 21h
        jc err_fopen
        mov [FILEHANDLE],ax

pop ds                  ; new DS => DS:BX addresses file buffer

; main program loop
;===================
beginmain:
        ; read (next) 64K of file
        mov ah,3Fh
        mov bx,[cs:FILEHANDLE]
        mov cx, 0ffffh
        mov dx,0
        int 21h
        jc err_fread

mov ah,09h
mov dx,msg_debug
int 21h

        mov bx,0                ; begin at address 0 of buffer
        mov dx,[BASE_ADDR]      ; dx = BASE_ADDR

        play:
                mov al,[ds:bx]
                out dx,al
                inc bx
                loop play

endmain:
;===================
; end main loop

; close the file
        mov ah,3Eh
        mov bx,[FILEHANDLE]
        int 21h
        jc err_fclose

; deallocate the memory
        mov ah,49h
        int 21h
        jc err_memclose

jmp end

; write error messages
err_fopen:
        mov ah,09h
        mov dx,msg_err_fopen
        int 21h
        jmp end
       
err_fclose:
        mov ah,09h
        mov dx,msg_err_fclose
        int 21h
        jmp end

err_fread:
        push cs
        pop ds
        mov ah,09h
        mov dx,msg_err_fread
        int 21h
        jmp endmain     ; end the program

err_memresize:
        mov ah,09h
        mov dx,msg_err_memresize
        int 21h
        jmp end

err_memclose:
        mov ah,09h
        mov dx,msg_err_memclose
        int 21h
        jmp end

err_memget:
        mov ah,09h
        mov dx,msg_err_memget
        int 21h
        jmp end

; exit program
end:
        mov ah,4Ch
        mov al,00h
        int 21h

msg_debug db "DEBUG",13,10,36
msg_err_fopen db "Could not open the file!",13,10,36
msg_err_fclose db "Could not close the file!",13,10,36
msg_err_fread db "Could not read the file!",13,10,36
msg_err_memresize db "Could not resize program's memory!",13,10,36
msg_err_memget db "Could not allocate enough memory (64K)!",13,10,36
msg_err_memclose db "Could not deallocate the memory!",13,10,36


FILENAME db "QUAAK.AU",0
FILEHANDLE dw 0
BASE_ADDR dw 0378h
HAVE_NEXTBLOCK db 0

Warum ich nicht in einem Segment bleiben möchte? Wenn ich mit 48h Speicher beschaffe, ist der unabhängig vom Programm und ich kann darauf dann eine Interrupt-Routine draufschmeißen. Außerdem kann ich so den Puffer maximieren.
Achja: Bitte keine 32-Bit-Addressierungen verwenden, das Gerät ist ein 286er.

Gruß,
ein völlig korrupter
Sebastian

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 18. February 2007, 18:25 »
Kann mir keiner helfen?
OS ist DOS 6.22 ...

Biehler Productions2

  • Beiträge: 47
    • Profil anzeigen
    • http://biehlos.biehler-josef.de
Gespeichert
« Antwort #2 am: 18. February 2007, 19:11 »

Mein Problem: In dem Moment, wo ich den int 21h aufrufe, bricht mein gesamtes Programm zusammen. Den Code zum Berechnen der aktuellen Programmgröße habe ich aus dem Netz.


Bei welchem Int 21H Aufruf bricht das programm ab?
Bei dem Stück Code, der den Speicherblock vergrößern soll?
Was soll das überhaupt bringen?
Was hast du davon, den Speicherblock, wo dein Programm drinne ist, auf 64KB zu erweitern?

Steht grad aufm Schlauch  :oops:


; set program's memory size to 64K (limit of "tiny" COM files) - CS=DS=ES=SS
        mov bx,ss
        mov ax,es
        sub bx,ax
        mov ax,sp
        add ax,14h
        shr ax,4
        add bx,ax
        mov ah,04ah
        int 21h


Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 18. February 2007, 23:27 »
Nabend,

Das Programm stürzt in dem Augenblick ab, wo ich den int 21h / 3Fh (Datei lesen) aufrufe. Ich vergrößere nicht meinen Codeblock, sondern ich verkleinere ihn von ~550 KB auf 64 KB. Das hängt damit zusammen, dass DOS meinem Programm sämtliche freien Blöcke zuweist. Wenn ich den Codeblock nicht verkleinere, schlägt int 21h / 48h (Speicher reservieren) fehl.
Später möchte ich die Schleife beginmain/endmain in ein TSR bzw. eine Interrupt-Routine verlagern, um eine Audio-Dekodierung benutzen zu können (z.Zt. sind es PCM-Rohdaten mit 4 kHz) und dazu brauche ich am besten "exakt ein Segment" Pufferspeicher.

Alle vorherigen int 21h - Aufrufe gelingen (zumindest ist CF=0). Hoffe, es hilft dir.

Gruß,
Svenska

Biehler Productions2

  • Beiträge: 47
    • Profil anzeigen
    • http://biehlos.biehler-josef.de
Gespeichert
« Antwort #4 am: 19. February 2007, 11:45 »
Also scheinbar haut das mit dem Speicher anfordern net ganz hin.
Keine Ahnung wieso.

Ich habs unter XP getestet, da hat mir das System den Fehler gebracht, dass auf das COM3 Gerät zugegriffen werden will (Bei der 3FH Funktion), aber das nicht geht.

Ich hab dann einfach die beiden FUnktionen zum verkleinern des Speichers und zum Anfordern eines Speicherblockes gelöscht.

Dann einfach auf die aktuelle Segmentadresse die Größe des eigentlichen Programmes addiert und das dann als neue Segmentadreses genommen.
So konnte ich dann eine Datei einlesen.

mov ax, ds
add ax, 1024
mov ds, ax

        ; read (next) 64K of file
        mov ah,3Fh
        mov bx,[cs:FILEHANDLE]
        mov cx, 0ffffh
        mov dx,0
        int 21h
        jc err_fread


Wenn deinem programm von DOS sowieso schon der ganze Speicher zugeordnet wurde, isses ja sowieso egal, dann wärs doch sinnlos (?) , extra den Block zu verkleinern und dann nochmal einen neuen anzufordern.
« Letzte Änderung: 19. February 2007, 11:59 von Biehler Productions2 »

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 19. February 2007, 15:30 »
COM3 ?
Auf den Gedanken, das unter Windows mal auszuführen, bin ich garnicht gekommen :oops:

Njain, es ist schon sinnlos, den Speicherblock zu verkleinern und dann einen neuen anzufordern, aber um das Timing exakt hinzukriegen, muss ich den Timer abfangen und timing-exakt die Daten an den Parallelport schicken. Dazu dachte ich im Prinzip zwei "Threads", den Producer (Puffer-Füller) und den Consumer (Interrupt-Handler). Und da ist die Adressierung über ein Segment (an bekannter Adresse) ganz praktisch.

Später könnte man den Producer ebenfalls in den Timer o.ä. hineinverlagern und so einen Hintergrundplayer/TSR für DOS basteln (GLX kann das ja), aber so weit denke ich vorerst nicht.

Du hast als Segmentadresse für ds einfach ds=cs+1024*16 genommen, also fängt bei dir der Puffer genau 16k hinter dem Programm an?

Und eine Verständnisfrage: Das Programm beginnt also bei Adresse cs:0x0000 und der Stack bei cs:0xffff ? Bringt dein Ansatz damit nicht den Stack durcheinander (wenn der Stack dann benutzt wird) ?

Gruß,
Svenska

Biehler Productions2

  • Beiträge: 47
    • Profil anzeigen
    • http://biehlos.biehler-josef.de
Gespeichert
« Antwort #6 am: 19. February 2007, 16:29 »

Njain, es ist schon sinnlos, den Speicherblock zu verkleinern und dann einen neuen anzufordern, aber um das Timing exakt hinzukriegen, muss ich den Timer abfangen und timing-exakt die Daten an den Parallelport schicken. Dazu dachte ich im Prinzip zwei "Threads", den Producer (Puffer-Füller) und den Consumer (Interrupt-Handler). Und da ist die Adressierung über ein Segment (an bekannter Adresse) ganz praktisch.


Ist doch unter DOS egal?
Da haste ja keinen Speicherschutz, rein theoretisch könntest du überall hin schreiben, da meckert keiner  :-D


Zitat
Du hast als Segmentadresse für ds einfach ds=cs+1024*16 genommen, also fängt bei dir der Puffer genau 16k hinter dem Programm an?

Ja, wobei ich unnötig viel hinzuaddiert hab.
Dein programm ist ja keine 16KB groß^^

Es reicht auch, nur 40H hinzuzuaddieren.

mov ax, ds
add ax, 40H
mov ds, ax

Dann liegt der Datenbuffer genau 1024 Byte hinter dem Start von dem Programm.

Zitat
Und eine Verständnisfrage: Das Programm beginnt also bei Adresse cs:0x0000

Jo, also theoretisch schon, in der Praxis eher an CS:0x0100, weil vor nem .COM programm noch 256 Byte vom PSP sind.


Zitat
und der Stack bei cs:0xffff ? Bringt dein Ansatz damit nicht den Stack durcheinander (wenn der Stack dann benutzt wird) ?

Hm, also rein theoretisch könnte es so sein, dass der Stack bei CS:0xFFFF beginnt.
Das würde dann wohl den Stack durcheinander bringen.
Aber das müsste man umgehen können, wenn du als Segment für deinen Datenbuffer CS+1000H nimmst.
Dann dürfte der Buffer direkt 64KB hinter dem Start deines programmes liegen und dem Stack keine Probleme mehr bereiten.



Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 19. February 2007, 16:54 »
Ist doch unter DOS egal?
Da haste ja keinen Speicherschutz, rein theoretisch könntest du überall hin schreiben, da meckert keiner  :-D
Für mein Programm sicher nicht, aber wenn da noch andere Programme laufen, muss ich den Speicher wenigstens reservieren. :-)

Zitat
Zitat
Und eine Verständnisfrage: Das Programm beginnt also bei Adresse cs:0x0000
Jo, also theoretisch schon, in der Praxis eher an CS:0x0100, weil vor nem .COM programm noch 256 Byte vom PSP sind.
Ja gut, das hab ich unterschlagen; macht aber nix.

Zitat
Zitat
und der Stack bei cs:0xffff ? Bringt dein Ansatz damit nicht den Stack durcheinander (wenn der Stack dann benutzt wird) ?
Hm, also rein theoretisch könnte es so sein, dass der Stack bei CS:0xFFFF beginnt. Das würde dann wohl den Stack durcheinander bringen.
Genau darauf bezieht sich der Speicher-Freigabeschnipsel. Allerdings hatte ich auch anfangs die Reservierung für exakt 64 KB (größer kann eine .COM ja eigentlich nicht werden). Je 64K für Code&Daten ist auch zu verschmerzen für den Anwendungszweck.

Zitat
Aber das müsste man umgehen können, wenn du als Segment für deinen Datenbuffer CS+1000H nimmst.
Dann dürfte der Buffer direkt 64KB hinter dem Start deines programmes liegen und dem Stack keine Probleme mehr bereiten.
Und dann könnte ich meinen Bereich auf 128 KB = 8192 Paragraphen begrenzen und hätte somit keine Speicherverwaltungsprobleme mehr.

Schade, ich hatte gehofft, den Datenpuffer in die UMBs zu quetschen (sofern da noch genug frei ist). Auf dem 286er geht das ohnehin nicht (hab keinen UMB-Treiber für diesen Chipsatz gefunden), aber auf nem 386er (mit Hardware-MP3-Decoder am LPT) würde das noch ein bisschen Arbeit ersparen.

Gruß,
Svenska

 

Einloggen