我正在尝试用 C 语言对实模式 MS-DOS 进行编程。使用一些关于游戏编程的旧书作为起点。书中的源代码是为 Microsoft C 编写的,但我试图让它在 OpenWatcom v2 下编译。当我尝试访问指向 VGA 视频内存开头的指针时,我很早就遇到了问题。
#include <stdio.h>
#include <dos.h>
void Set_Video_Mode(int mode) {
union REGS inregs, outregs;
inregs.h.ah = 0;
inregs.h.al = (unsigned char) mode;
int86(0x10, &inregs, &outregs);
}
int main(void)
{
Set_Video_Mode(0x13);
//the following line throws an error, without it the code compiles and runs
char far *video_buffer = (char far *)0xA0000000L;
while (!kbhit()) { };
Set_Video_Mode(0x03);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是远指针赋值引发以下错误:
VGA.C(33):错误!E1077:缺少 '}'
VGA.C(33):警告!W107:缺少函数“main”的返回值
VGA.C(36):错误!E1099: 语句必须位于函数内部。可能的原因:缺少{
这有点令人困惑,似乎宏定义出了问题,或者是什么......
当我使用相同的编译器尝试维基百科关于远指针的文章中的代码时:
#include …Run Code Online (Sandbox Code Playgroud) 我一直在阅读并使用8086指令集,它说CMP(比较)可以设置进位标志.我知道比较会减去两个操作数,但我想知道是否有人可以在这种情况下提供一个例子.
我只是无法掌握添加数字和负数的想法会设置进位标志.我已经阅读了借旗,但我只需要一个例子来澄清我对比较指令的理解.
另外,据我所知,如果3 - 5 = -2会设置负标志...何时进位?
我理解RCR将从右到左旋转位的术语,从进位中取出位,同时ROR将位从右向左旋转,从右侧取位,但这是它们之间的唯一区别吗?如果是这样,两条指令似乎都做同样的工作.请帮忙.谢谢
为什么DS和ES寄存器的初始化必须由程序员手动完成?
例如:
MOV AX, DTSEG
MOV DS, AX
Run Code Online (Sandbox Code Playgroud)
另一方面,CS和SS寄存器由操作系统(in MS-DOS)初始化.为什么会这样?
我的问题是我编写了一个代码,该代码应该将结果输出到连接到并行端口的一组LED中.当我运行代码时它几乎没有做任何事情.我的导师告诉我,代码运行得太快,我的眼睛看不到发生了什么.
我发现有几种方法可以延迟时间,我试图循环NOP,但我认为我无法确定发生了什么.有没有更好的方法?
我在这里有一部分代码,我必须添加一个时间延迟:
org 100h
mov ax, 0
mov dx, 378
out dx, ax
mov ax, 1
; 1st
mov cx, 1ah
start1st:
mov ax, 1
left:
out dx, ax
; --------------------------------> how to loop?
mov bx, 2
mul bx
cmp ax, 80h
jl left
dec cx
cmp cx,0
jg start1st
; end 1st
Run Code Online (Sandbox Code Playgroud) 在学习了英特尔8080结构后,我现在正在尝试学习英特尔8086以及这里的程序如何布局.就目前而言,即使查看基本示例也是非常令人生畏的,更糟糕的是,我无法区分两种编写8086代码的方法,我偶然发现了.也就是说,有时候我会看到:
.model small
.stack 100h
.code
start:
mov dl, ‘a’ ; store ascii code of ‘a’ in dl
mov ah, 2h ; ms-dos character output function
int 21h ; displays character in dl register
mov ax, 4c00h ; return to ms-dos
int 21h
end start
Run Code Online (Sandbox Code Playgroud)
虽然我也发现:
Progr segment
assume cs:Progr, ds:dataSeg, ss:stackSeg
start: mov ax,dataSeg
mov ds,ax
mov ax,stackSeg
mov ss,ax
mov sp,offset top
mov ah,4ch
mov al,0
int 21h
Progr ends
dataSeg segment
dataSeg ends
stackSeg segment
dw 100h …Run Code Online (Sandbox Code Playgroud) 我是学习OS开发的新手.从我读过的书中,它说启动加载器会将第一个MBR复制到0x7c00,然后从实模式开始.
并且,示例从16位汇编代码开始.但是,当我查看今天的linux内核时,arch/x86/boot有'header.S'和'boot.h',但实际代码是在main.c中实现的.
这似乎对"不编写汇编"很有用.但是,这是如何在Linux中专门完成的?我可以粗略地想象可能有特殊gcc选项和链接策略,但我看不到细节.
我无法让MASM接受写成的远程调用指令call 0f000h:1260h,可能是因为这个问题带来的问题.
我没有乱用神秘的MASM指令,而是决定使用DB手动将其编码到我的程序中,如下所示:
pushf ;IRET will be executed, push flags.
db 9ah,60h,12h,0,0f0h ;call location f000:1260.
;Location pointed to by int 1c (System timer tick)
;BIOS defaults it to a dummy IRET
Run Code Online (Sandbox Code Playgroud)
在使用DEBUG.COM跟踪程序时,我注意到在执行调用指令后出现"DB FE".但是,执行时不会发生这种情况int 1ch.跳转到位置f000:1260的这两种方法有什么区别?
我假设DEBUG没有将0xfe(以及后面的字节)识别为有效的操作码.我倾倒了位置f000:1260以查看哪些字节存在.
确实存在字节0xfe以及其他一些字节.我知道0xcf本身就是IRET的操作码(这是我期望找到的所有),那么这些其他字节是什么?
这是int 1ch位于0000:0070 的IVT条目.
UPDATE
正如Michael Petch在他的回答中所说,奇怪的字节构成了DOSBox的回调机制.我很想知道如果我试图在我的主程序中执行这个回调会发生什么.
执行:
xor ah, ah ;select set video mode function
mov al, 13h ;320x200 256 colors
db 0feh,38h,18h,00h ;set video mode DOSBox callback.
;Nothing pushed to stack.
Run Code Online (Sandbox Code Playgroud)
似乎与执行完全相同:
xor ah, ah …Run Code Online (Sandbox Code Playgroud) 我在互联网的某个地方读到,在跳到0x7c00之前,BIOS加载到启动设备的"驱动器号"%dl.但这个"驱动器号码"是什么?连接到计算机的每台设备都由BIOS分配了一个号码?如果是这样,我怎么知道分配给哪个设备的号码?
读取我在%dl设置了位0x80和0x70时发现的GRUB源代码,它用0x80覆盖整个寄存器.这是为什么?这是代码:
jmp 3f /* grub-setup may overwrite this jump */
testb $0x80, %dl
jz 2f
3:
/* Ignore %dl different from 0-0x0f and 0x80-0x8f. */
testb $0x70, %dl
jz 1f
2:
movb $0x80, %dl
1:
Run Code Online (Sandbox Code Playgroud)
顺便说说.网络上的PC启动过程是否有详细的资源?特别是在将控件提供给引导加载程序之前BIOS的作用以及用于与之通信的标准代码(如"驱动器数").我希望编写自己的引导加载程序,我发现的所有内容都过于模糊,技术性不足以告知我的引导加载程序开始运行时计算机的确切状态.
我读到该MOV指令不能为其两个操作数都有内存位置.
喜欢:MOV [0012H], [0016H]不允许.
为什么这样?
其他指令可以为其两个操作数设置内存位置吗?