32 位寄存器可访问汇编时的 DOS 控制台数据内存访问

DOS Console data memory access when 32bit registers are accessible assemblery

我正在 TASM 汇编中进行一个项目,当我在汇编程序中使用 32 位寄存器(使用 .386)时,我无法将颜色输出到 DOS 控制台的特定单元格。

下面是我通常如何执行此操作的示例:

BX 单元格位置,
单元格的 AH 颜色/字符

mov ah , 01000000b ; Color Red to ah
mov ax,0b800h ; memory location of the console 
mov es,ax ; to es 
mov es:[bx] , ah ; mov data from ah to the cell of bx 

这行得通,但是当我在开始时执行 .386 使其成为 32 位时,此代码停止工作...有人知道解决问题的方法吗? 短的 : 代码需要将 Color 设置为 cell 。在没有 .386 的情况下工作并停止在 .386

下工作

代码段:

.386
.Model small
.data 
 ;all my data 
 .code 
  Start:
  ; all the code in there also the output code snippet I showed 
 end start 

Ped7g 修复: 将行 .386 放在 .code

Margaret Bloom 修复: 将 USE16 修饰符添加到 MODEL 指令

两者都在工作。感谢帮助

任务 32 位

如果您没有明确指定代码段的属性,使用 .386 指令会将默认操作数端设置为 32 位。

引用 TASM manual:

Note that you can specify the model modifier in two places, for compatibility with MASM 5.2. If you don't use a model specifier, Turbo Assembler as.sumes the NEARSTACK modifier, and USE32 (if the 80386 or 80486 processor is selected).

这并不意味着您可以使用 32 位寄存器1,而是 assembler 将发出 [=69= 中的指令] 方法。

x86 机器中的所有代码都有默认的操作数大小。
运行 在 32 位时是 32 位,当 运行 在 16 位时是 16 位(64 位有点复杂,它仍然是 32 位,但可以用REX.W 前缀)。

默认操作数大小决定了直接操作数的默认大小,即期望值的指令的操作码后面的字节数。

mov ax, 0b800hmov eax, 0b800h 等指令以 相同的 方式编码:使用操作码 B8.
在这个操作码之后,它跟在 16 位或 32 位的立即操作数之后。
具体大小取决于默认的操作数大小。

要访问 "other" 大小,即在 32 位代码中指定指令的 16 位版本,反之亦然,存在操作数大小覆盖前缀(值 66)。

戴上table

                    +-----------------------------------------+
                    |           Default operand size          | 
+-------------------+---------------------+-------------------+ 
|Instruction        |      16             |        32         |
+-------------------+---------------------+-------------------+
|                   |                     |                   |
|mov ax, 1234h      |     B8 34 12        |    66 B8 34 12    |  
|                   |                     |                   |
|mov eax, 12345678h |  66 B8 78 56 34 12  |   B8 78 56 34 12  |   
|                   |                     |                   |
+-------------------+---------------------+-------------------+

看看当 assembler 假定默认操作数大小为 32 位时,mov ax, 0b800h 是如何用 66 B8 编码的?
当以 16 位代码执行时,66 B8 被解码为具有 32 位立即数?

这搞砸了后续指令的解码。

assembled 作为 32 位代码作为 16 位代码执行时发布的片段导致:

00000000  B440              mov ah,0x40
00000002  66B800B88EC0      mov eax,0xc08eb800
00000008  26678827          mov [es:edi],ah

如何解决这个问题

明确地为每个代码段指定代码大小属性,或者使用 MODEL 指令设置默认值。
例如,如果您使用 SMALL 内存模型:

.MODEL USE16 SMALL 

1 就 CPU 而言,你总是如此,只是 TASM 拒绝 assemble 不符合所选处理器系列的指令.

在您可以使用任何可能的 x86 保护模式(无论是 16 位还是 32 位变体)之前,您必须 运行 一组特定的机器指令,这些指令将 CPU 从实模式传输到保护模式。这不是一件小事,因为您至少需要设置最少数量的系统表和系统寄存器,例如 CR0。它在英特尔软件开发人员手册 Volume 3 中有详细描述,并附有汇编程序示例。

请注意,在那之后处理器的行为实际上发生了一切变化:您作为汇编程序输入编写的指令将不再意味着它们的意思(因为默认操作数和地址大小可能已经改变),但最重要的是内存地址不再简单地指向物理内存,而是通过分段和分页进行转换。甚至您的控制台地址也必须重新计算以确保您指向正确的物理地址。请注意,良好的旧 DOS 中断将不再起作用,因为它们被编写为在实模式下工作并且不期望保护模式;它只会挂起或重新启动。

也许您应该阐明您要实现的目标以及为什么现有的实模式代码不能满足您的需要。如果你的目标是学习,那么我建议你自己熟悉什么是保护模式,如何将 CPU 传递给它,以及如何为它编写程序。我估计第一点会占用您 80% 的时间,第二点会占用 15%,而程序本身只会占用您 5% 或更少的时间。