1

私はアセンブラ (16 ビット DOS の TASM) を学習しており、0Ah DOS サービスを使用してテキストをスタックに直接読み込もうとしています。emu8086ではかなりうまく機能しますが、実際のTASMで実行すると、ユーザー入力はありません(入力がまったくなく、スキップINT 21hしているように見えます)。

これが私がそれを使用している方法です:

PROC _readNum USES BP AX BX CX DX SI
    PUSH BP
    MOV BP, SP
    SUB SP, 7

    MOV AH, 0Ah ;Buffered input
    MOV [BP-7], 5 ;Max number of characters to read (4 + 1 for enter)
    LEA DX, [BP-7]
    INT 21h ;This interrupt seems to be doing nothing at all
    ...

何が問題になる可能性がありますか? スタックを間違った方法で参照していますか? 前もって感謝します。

念のため、完全なコードを次に示します。

ascii_offset EQU 30h
.model small
.stack 100h
.data
    ; add your data here!
    outStrA DB "Input A: $"
    outStrB DB "Input B: $"
    resultStr DB "Result of $"
    plusStr DB "+$"
    equalsStr DB " is $"
    eol DB 13, 10, "$"

    cnt DB 10
    rcnt DB 0
    buf DB 11 dup("$")

PRINT MACRO op1
    MOV AH, 09h
    LEA DX, op1
    INT 21h
ENDM

PRINTLN MACRO op1
    PRINT op1   
    PRINT eol
ENDM

PRINTEOL MACRO
    PRINT eol
ENDM

.code

PROC _printNum USES BP AX BX DX SI
    PUSH BP
    MOV BP, SP
    SUB SP, 6 ;Max number to print is 5 + $ sign    

    MOV AX, [BP+2]
    MOV DX, 0h ;Is required to divide a double pair
    MOV BH, 0h
    MOV BL, 10 ;Divisor

    LEA SI, [BP-1] ;Our string stored in memory (from end)
    MOV byte ptr [SI], "$" ;End of str
    _printNum_loop:
    DIV BX ;Result is in AX:DX
    ADD DL, ascii_offset ;Convert to ASCII
    DEC SI
    MOV [SI], DL    
    MOV DX, 0h ;Reset DX to divide again
    CMP AX, 0h ;If AX is 0
    JNE _printNum_loop

    PRINT [SI]

    MOV SP, BP
    POP BP
    RET 2
ENDP

PROC _readNum USES BP AX BX CX DX SI
    PUSH BP
    MOV BP, SP
    SUB SP, 7

    MOV AH, 0Ah ;Output to screen
    MOV [BP-7], 5 ;Max number of characters to read (4 + 1 for enter)
    LEA DX, [BP-7]
    INT 21h 

    MOV AX, 0h  ;Result
    MOV BX, 0h  ;Temporary result
    MOV CX, 0h  ;Loop counter   
    MOV CL, [BP-6] ;Loop counter
    LEA SI, [BP-5] ;Starting position to read number
    _readNum_strloop:
        MOV DX, 10 ; ;Will multiply AX by DX
        MUL DX ; AX = AX * DX
        MOV BL, [SI]        
        SUB BL, 30h
        ADD AX, BX
        INC SI  
    LOOP _readNum_strloop

    MOV SP, BP
    POP BP
    RET 0
ENDP

start:
; set segment registers:
    MOV AX, @data
    MOV DS, AX
    MOV ES, AX

    PUSH 0ABCDh
    JMP _printNum

    MOV AX, 4c00h ; exit to operating system.
    INT 21h    

END start ; set entry point and stop the assembler.
4

3 に答える 3

2

いくつかのバグがあるかもしれません。これをコンパイルできますか?

PROC _printNum USES BP AX BX DX SI
    PUSH BP
    MOV BP, SP
    SUB SP, 6  ; Max number to print is 5 + $ sign    

ここでは、この手順の最後で以前にアドレス指定またはソースとしてsp使用していなくても変更するため、この手順は不要です。spmov sp,bp

    MOV AX, [BP+2]

これはバグではありません、ただ疑問に思っています...ax今はそうあるべき0xABCDです、これは正しいですか?

    MOV DX, 0h  ; Is required to divide a double pair
    MOV BH, 0h
    MOV BL, 10  ; Divisor

    LEA SI, [BP-1]          ; Our string stored in memory (from end)
    MOV byte ptr [SI], "$"  ; End of str

_printNum_loop:
    DIV BX                  ; Result is in AX:DX

後の結果はにdiv bxありませんax:dxax商があり、dx余りがあります。

次にここに:

PROC _readNum USES BP AX BX CX DX SI
    PUSH BP
    MOV BP, SP
    SUB SP, 7

なぜこれsub sp,7spまず、 2に合わせておく必要があります。次に、この手順の最後で、spアドレス指定またはソースとして使用しない場合に、ここで変更するのはなぜですか。mov sp,bp

MOV AH, 0Ah    ; Output to screen
MOV [BP-7], 5  ; Max number of characters to read (4 + 1 for enter)

ここで、オペランドのサイズを定義する必要があります。mov byte [bp-7],5またはmov word [bp-7],5。またはptr、TASMで必要な場合は、わかりません:mov byte ptr [bp-7],5またはmov word ptr [bp-7],5

LEA DX, [BP-7]
INT 21h

これは意味がありません。DOS INT 21h によると、 DOS機能コードのドキュメントmov ah, 0x0a; int 21はバッファリングされた入力でありds:dx、入力バッファを指す必要があります。おそらくあなたのコメント「画面への出力」は間違っていますか?

MOV AX, 0h     ; Result
MOV BX, 0h     ; Temporary result
MOV CX, 0h     ; Loop counter   
MOV CL, [BP-6] ; Loop counter
LEA SI, [BP-5] ; Starting position to read number
_readNum_strloop:
    MOV DX, 10 ; Will multiply AX by DX
    MUL DX ; AX = AX * DX
    MOV BL, [SI]        
    SUB BL, 30h
    ADD AX, BX
    INC SI  
LOOP _readNum_strloop

このループでは、オーバーフローのリスクがあると思います。のような16ビットレジスタに適合する最大数axは2^16-1==65535です。

于 2012-09-19T13:52:52.780 に答える
1

すべてのコードを調べたわけではありませんが、1つの問題は、データとスタックセグメントが異なることです。つまり、プログラムはds!=で始まりssます。

したがってLEA DX, [BP-7]、DOS入力関数に正しいアドレスを提供することはありません。これは、DOS入力関数が予期されているにds:dxもかかわらず、バッファが!=ss:dxにあるためです。次の呼び出しは、一部のメモリを上書きして、ハングまたはクラッシュを引き起こす可能性があります。dsssint 21h

適切なアドレスを使用する必要があります。

更新:いくつかの詳細。

これがあなたのプログラムのわずかに変更されたバージョンです(質問のコードはTASM 3.2ではうまく組み立てられませんでした):

UPDATE2:変更されたすべての行に。で始まるコメントが含まれていることを忘れました;;;;

ascii_offset EQU 30h
.model small, C ;;;; added ", C"
.stack 100h
.data
    ; add your data here!
    outStrA DB "Input A: $"
    outStrB DB "Input B: $"
    resultStr DB "Result of $"
    plusStr DB "+$"
    equalsStr DB " is $"
    eol DB 13, 10, "$"

    cnt DB 10
    rcnt DB 0
    buf DB 11 dup("$")

PRINT MACRO op1
    MOV AH, 09h
    LEA DX, op1
    INT 21h
ENDM

PRINTLN MACRO op1
    PRINT op1   
    PRINT eol
ENDM

PRINTEOL MACRO
    PRINT eol
ENDM

.code

_printNum PROC USES BP AX BX DX SI ;;;; reordered PROC and _printNum
    PUSH BP
    MOV BP, SP
    SUB SP, 6 ;Max number to print is 5 + $ sign    

    MOV AX, [BP+2]
    MOV DX, 0h ;Is required to divide a double pair
    MOV BH, 0h
    MOV BL, 10 ;Divisor

    LEA SI, [BP-1] ;Our string stored in memory (from end)
    MOV byte ptr [SI], "$" ;End of str
    _printNum_loop:
    DIV BX ;Result is in AX:DX
    ADD DL, ascii_offset ;Convert to ASCII
    DEC SI
    MOV [SI], DL    
    MOV DX, 0h ;Reset DX to divide again
    CMP AX, 0h ;If AX is 0
    JNE _printNum_loop

    PRINT [SI]

    MOV SP, BP
    POP BP
    RET 2
ENDP

_readNum PROC USES BP AX BX CX DX SI ;;;; reordered PROC and _readNum
    PUSH BP
    MOV BP, SP
    SUB SP, 7

    MOV AH, 0Ah ;Output to screen
    MOV byte ptr [BP-7], 5 ;Max number of characters to read (4 + 1 for enter) ;;;; added "byte ptr"
    LEA DX, [BP-7]
    INT 21h 

    MOV AX, 0h  ;Result
    MOV BX, 0h  ;Temporary result
    MOV CX, 0h  ;Loop counter   
    MOV CL, [BP-6] ;Loop counter
    LEA SI, [BP-5] ;Starting position to read number
    _readNum_strloop:
        MOV DX, 10 ; ;Will multiply AX by DX
        MUL DX ; AX = AX * DX
        MOV BL, [SI]        
        SUB BL, 30h
        ADD AX, BX
        INC SI  
    LOOP _readNum_strloop

    MOV SP, BP
    POP BP
    RET 0
ENDP

start:
; set segment registers:
    MOV AX, @data
    MOV DS, AX
    MOV ES, AX

    PUSH 0ABCDh
    JMP _printNum

    MOV AX, 4c00h ; exit to operating system.
    INT 21h    

END start ; set entry point and stop the assembler.

tasm stkkbinp.asmエラーや警告なしで(TASM 3.2を使用して)組み立てられました。

tlink /m /s stkkbinpエラーや警告なしでリンクされています(TLINK 3.01を使用) 。

リンカによって生成されるマップファイルは次のとおりです。

 Start  Stop   Length Name               Class

 00000H 00088H 00089H _TEXT              CODE
 00090H 000C5H 00036H _DATA              DATA
 000D0H 001CFH 00100H STACK              STACK


Detailed map of segments

 0000:0000 0089 C=CODE   S=_TEXT          G=(none)  M=STKKBINP.ASM ACBP=48
 0009:0000 0036 C=DATA   S=_DATA          G=DGROUP  M=STKKBINP.ASM ACBP=48
 0009:0040 0100 C=STACK  S=STACK          G=DGROUP  M=STKKBINP.ASM ACBP=74

  Address         Publics by Name


  Address         Publics by Value


Program entry point at 0000:0070

次に、TDでプログラムを開き、最初の3つの命令を実行します。

ここに画像の説明を入力してください

ds等しくない画面ではっきりと見ることができますss。それらは4つ離れており、最初に.data0x4cafでセグメント化し、次に0x4cb3.stackで進みます。セグメント値に関するこの4の差は、4 * 0x10=0x40バイトに相当します。

上記のマップファイルを見ると、実際、これらのセグメント間の距離であることがわかります。

 0009:0000 0036 C=DATA   S=_DATA          G=DGROUP  M=STKKBINP.ASM ACBP=48
 0009:0040 0100 C=STACK  S=STACK          G=DGROUP  M=STKKBINP.ASM ACBP=74

マップファイルが両方(9)で同じセグメントを示している理由を説明することはできませんが、リンクされた実行可能ファイルではそれらが異なります。しかし、それが入力が正しく機能しない理由です。

于 2012-09-19T14:06:39.463 に答える
1

アレクセイ・フルンゼの答えを詳しく説明するには:

コードは SMALL モデルを指定しています。このディレクティブは、アセンブラに、とりわけそれを想定するように指示しますが、DS = SSそれを強制しません (できません)。

DOS が EXE をロードするとき、SS:SP をファイル ヘッダーにある場所に設定します。DS をPSPの先頭に設定するので、コマンドライン引数などを取得できます。

DOS バッファー読み取り機能は、バッファーが DS:DX にあることを想定しています。BP-7 (バッファの開始) で DX を適切にロードしましたが、関数呼び出しを行う前に DS ← SP も設定する必要があります。あなたはそれを行うことができます

MOV AX,SS
MOV DS,AX

また

PUSH SS
POP DS

MOV DS,SS命令はありません。

于 2012-09-19T15:06:44.197 に答える