【问题标题】:Filling dynamically allocated memory freezes program execution填充动态分配的内存会冻结程序执行
【发布时间】:2021-01-16 19:34:01
【问题描述】:

我正在尝试使用 TASM 分配一些应该用作缓冲区的内存。

为此,我首先使用以下方法解除分配给可执行文件的所有内存:

    MOV     BX, SS 
    MOV     AX, ES  
    SUB     BX, AX  
    MOV     AX, SP 
    ADD     AX, 0fh
    SHR     AX, 4
    ADD     BX, AX
    MOV     AH, 4ah
    INT     21h

之后我尝试使用以下方法分配 64000 个字节:

    MOV     AH, 48h
    MOV     BX, 64000/16 
    INT     21h
    MOV     buffer, AX

这似乎完美无缺,因为执行后没有设置 CARRY 标志 指令 21h。

稍后我最终会尝试填充刚刚分配的内存,例如:

    MOV     BX, OFFSET BUFFER
    MOV     ES, BX

    XOR     DI,DI
   
    MOV     CX,64000/2
    MOV     AL,12
    MOV     AH,AL
    REP     STOSW

不幸的是,由于程序在REP STOSW 指令处冻结而失败。 现在我有点迷路了,因为我找不到明显错误的地方。那么为什么会这样呢?

这是我的完整来源:

.MODEL LARGE

.STACK 100h
.DATA? 
buffer      DW ?

.CODE
Main:
    MOV     BX, SS 
    MOV     AX, ES  
    SUB     BX, AX  
    MOV     AX, SP 
    ADD     AX, 0fh
    SHR     AX, 4
    ADD     BX, AX
    MOV     AH, 4ah
    INT     21h

    MOV     AH, 48h
    MOV     BX, 64000/16 
    INT     21h
    MOV     buffer, AX

    MOV     BX, OFFSET BUFFER
    MOV     ES, BX

    XOR     DI,DI
   
    MOV     CX,64000/2
    MOV     AL,12
    MOV     AH,AL
    REP     STOSW

    MOV     AH,4ch  
    INT     21h 
   
END Main      

【问题讨论】:

    标签: assembly dos x86-16 tasm


    【解决方案1】:
    MOV     BX, OFFSET BUFFER
    MOV     ES, BX
    

    DOS函数48h给你的信息是一个段落地址。

    上面的代码将偏移量加载到包含这个地址的变量中。您需要取消引用它:

    MOV     ES, buffer
    

    直接来自AX

    MOV     AH, 48h
    MOV     BX, 64000/16 
    INT     21h          ; -> AX CF
    
    JC      Fail         ; Never forget to check for failure
    
    MOV     buffer, AX
    MOV     ES, AX
    XOR     DI, DI
    MOV     CX, 64000/2
    MOV     AX, 0C0Ch
    CLD                  ; Clear direction flag at least once in your program
    REP     STOSW
    
    Fail:
    MOV     AX, 4C00h  
    INT     21h
    

    [编辑]

    .MODEL LARGE
    
    .STACK 100h
    .DATA? 
    buffer      DW ?
    
    .CODE
    

    通过上面的代码,你的程序内存的高端最终看起来像:

        .DATA?          .STACK           func 48h alloc
    ... <-- 16 bytes --><-- 256 bytes --><-- 64000 bytes --> ...
        94h,08h,0,0,..,0
        ^               ^                ^
        0883h           0884h            0894h    <== paragraph addresses
        <-- 272 bytes = 17 paragraphs -->
    

    buffer 是 BSS 中第一个(也是唯一一个)变量的名称 (.DATA?)。

    mov ax, offset bufferAX 加载为 0,因为 buffer 变量占据了 BSS 中的第一个字。
    mov ax, seg bufferAX 加载为 0883h,即段落地址BSS 在程序加载时分配。
    mov ax, buffer 加载 AX 0894h,buffer 变量的内容,这是 DOS 分配给 64000 字节分配的段落地址。

    有关段和段落的更多解释请阅读:What are Segments and how can they be addressed in 8086 mode?

    【讨论】:

    • 很抱歉回到这里,但我可以澄清一下 Roland。 Dos 48h 为我分配了一些内存(如果可用)并返回一个段落地址。现在我认为“段落”只是“段”的另一个词,不是吗?但是,如果我执行 MOV ES, buffer MOV AX, SEG buffer 之类的操作(以获取段地址)并最终比较 AX 和 ES 内的值,我会得到 894h 的 ES 和 883h 的段AX 中的地址。造成这种差异的原因是什么?
    • buffer 是 BSS 中的变量名称 (.DATA?)。在那里,该变量位于某个“OFFSET”处,而 BSS 本身位于某个“SEG”处。汇编器允许您通过mov ax, offset buffermov ax, seg buffer 检索这些值。 mov es, buffer 指令的作用是检索存储在变量中的值。所以与该变量在内存中的 WHERE 无关。
    • 是的,我完全知道这些指令和我自己的变量的作用——我不知道段和段落之间的区别。从我认为 int 21h 之后返回到 AX 寄存器的段落地址是段地址。显然这不是因为这两个值相差 17 个字节。那么那两个是什么? =)
    • 重读我的解释。这两个值必须不同(概率很高)。它们是完全不同的东西。为什么它们必须保持相同的值?
    • 段地址和段地址本质上是一回事。每个段 (64KB) 从内存中的一个段落地址开始。每个段落都是 16 字节的内存块。 1MB 内存中有 65536 个重度重叠的 64KB 段。
    猜你喜欢
    • 2011-07-06
    • 1970-01-01
    • 2020-03-05
    • 1970-01-01
    • 2013-10-15
    • 1970-01-01
    • 2011-05-03
    • 2020-01-18
    • 2016-04-10
    相关资源
    最近更新 更多