『楼 主』:
我写的djgpp的简单教程![原创]
使用 LLM 解释/回答一下
献给初学者的DJGPP教程
by xy_god
homepage:http://www.xygod.pc5.org
_____________________________________________________________________________________
自从用上LINUX后就用够了WINDOWS,但是好多开发还脱离不开DOS,真是没办法!
早就听说FREEDOS和MSDOS完全兼容,而且遵循的是GPL协议,所以下定决心脱离WINDOWS
,装了个FREEDOS!开发工具当然不能再用TURBO C啊等等原先商业化的东西了,用就用
个纯GNU的平台!好,就把TURBO C换成DJGPP吧,还有MASM和TASM,全部给我扔掉 ,换
成NASM!编辑器呢?用着EDIT根本就不爽,换成了DOS版的VIM,也是GNU工具!不好意思
,中文平台还是得用UCDOS,因为似乎还没有GPL协议的中文化DOS平台呢,还好UCDOS是免
费的了,不会让我的系统中存在D版软件了,我也没有做贼的感觉了!^-^
OK,这一切都搞定了,可是面对DJGPP和NASM突然发觉自己什么都不会了,以前用
TURBO C和MASM写代码的时候调用中断啊,交互编程啊,现在都不知道怎么做了,靠,和
当初刚刚接触LINUX怎么一样啊!只好到http://delorie.com/djgpp去看了,全他妈的E
文的,硬着头皮看吧!总算找到了一些用户指南,读懂了,基本上可以解决以上问题了!
想一想,那么多和自己一样的家伙们肯定也是一样的头疼,所以写出来和大家一起分享,
并非是翻译的,因为我没有耐心去翻译那些洋话连篇的东西,还是根据我的理解来写一份
教程吧,不过大牛们就不必看了!
另外,本教程假设您对turbo c或者DOS下的其他C/C++有一定了解并且作过一些中断
调用,中断驻留,显存映射操作以及C/C++和ASM混合编程!
OK,废话少说,本教程分为如下四个部分:
1 如何调用中断;
2 如何往VIDEO RAM中写入数据;
3 如何写中断驻留程序;
4 如何调用NASM写的汇编函数;
5 相关协议以及免责声明及其他
_____________________________________________________________________________________
1 如何调用中断:
在DJGPP中调用实模式中断和在TURBO C中使用INT86调用中断在形式上是很类似的,看
下面的例子(来自http://delorie.com/djgpp):
#include
void main(void){
__dpmi_regs r;
r.x.ax = 0x13;
r.d.ebx = 0x10000;
r.h.cl = 4;
r.h.dh = 5;
__dpmi_int(0x10, &r);
}
__dpmi_regs是一个结构,其中包含所有的80386用到的寄存器,如果你要使用8位寄存器
可以这样 __dpmi_regs.h.xx(xx可以是ah, al, bh, bl等寄存器),如果你要使用16位寄存器
可以这样__dpmi_regs.x.xx(即用x替换掉h即可),如果要使用32位寄存器,把h用d替 换就可
以了!
上面的代码就是调用int10中断,把显示模式设置为0x13(320*200 256)。
当中断执行完毕时,传给__dpmi_int的__dpmi_regs类型参数中将包含寄存器的新值,你
可以通过检测这些新值来分析中断的执行情况!注意,这些中断调用都是实模式下的!
_____________________________________________________________________________________
2 如何往VIDEO RAM中写入数据:
往VIDEO RAM中写入数据有两种方法,这两种方法个有优缺点,您需要根据情况选择使用!
第一种方法:
下面是代码:
#include
void main(void){
char *screen;
__dpmi_regs r;
r.x.ax = 0x13;
r.d.ebx = 0x10000;
r.h.cl = 4;
r.h.dh = 5;
__dpmi_int(0x10, &r);/*设置为0x13显示模式*/
if (__djgpp_nearptr_enable() == 0) return 0; /* 有可能发生 */
screen = 0xa0000 + __djgpp_conventional_base;
screen = 4;/*将屏幕左上角的一个点设置为红色*/
__djgpp_nearptr_disable();
}
这个例子在屏幕的左上角画一个红色的点。这种方法有下面两种缺点:
在调用一些特定的DPMI过程后,你必须重新计算video ram的近指针值!
在windows NT下不能用(至少在XP下是不能用的,但是在纯DOS下没问题)!
因此,一般并不推荐使用这种方法!
下面是第二种方法:
#include
#include
int main(){
int x=0,y=0;
char * screen;
__dpmi_regs r;
r.x.ax = 0x13;
__dpmi_int(0x10, &r);
_farsetsel(_dos_ds);
_farnspokeb(0xA0000, 4);
return 1;
}
这个例子在屏幕的左上角画一个红色的点。它也有一个缺点,那就是比第一种方法要稍
微慢一点。但是它比较安全,据说能够在NT下使用(但是我在XP上验证的效果很糟糕),而
且不必重新计算VIDEO RAM的指针,在Allegro库中就是使用的这种方法,所以它的速度也是
比较不错的,所以推荐使用这种方法!
_____________________________________________________________________________________
3 如何写中断驻留程序:
这儿有个程序,汉化自DJGPP的用户指南,不多说了,看程序,里面的注释已经很清楚了!
#include
#include
#include
#include
#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void *)&x,(long)sizeof(x));
#define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x,(long)sizeof(x));
#define TIMER 8/*时钟中断8每18.2 ms产生一次*/
int counter = 0;/*计数器初始值*/
void TickHandler(void){/*新的时钟中断处理程序*/
counter++;
}
int main(void)
{
/*含有中断地址(selector<img src="images/smilies/face-surprise.png" align="absmiddle" border="0">ffset)信息的结构体*/
_go32_dpmi_seginfo OldISR, NewISR;
printf("将要把新的中断处理程序连结到旧的中断处理程序上..\n"<img src="images/smilies/face-wink.png" align="absmiddle" border="0">;
getkey();
/*锁住函数和变量*/
LOCK_FUNCTION(TickHandler);
LOCK_VARIABLE(counter);
/*把旧的中断地址写入OldISR结构变量中*/
_go32_dpmi_get_protected_mode_interrupt_vector(TIMER, &OldISR);
/*把NewISR指向函数TickHandler的地址*/
NewISR.pm_offset = (int)TickHandler;
NewISR.pm_selector = _go32_my_cs();
/*把NewISR指向的地址连接到中断8的地址上*/
_go32_dpmi_chain_protected_mode_interrupt_vector(TIMER,&NewISR);
while (!kbhit())
printf("%d\n",counter);
printf("正在恢复原有的时钟中断。。。。。。\n"<img src="images/smilies/face-wink.png" align="absmiddle" border="0">;
/*恢复原有的时钟中断*/
_go32_dpmi_set_protected_mode_interrupt_vector(TIMER, &OldISR);
return 0;
}
_____________________________________________________________________________________
4 如何调用NASM写的汇编函数:
下面这个例子是我自己写的,共有三个文件:
将下面的代码存为asm.asm:
; ---------------------------------------------------------------------------
; 原型声明: unsigned int AddFour(unsigned int x);
; Returns: x + 4
; ---------------------------------------------------------------------------
x_AddFour equ 8
_AddFour__FUi:
push ebp
mov ebp, esp
mov eax,
add eax, 4
mov esp, ebp
pop ebp
ret
将下面的代码存为c.c:
#include
extern unsigned int AddFour(unsigned int);
int main(void)
{
printf("AddFour(4) = %i\n", AddFour(4));
return 0;
}
将下面的代码存为maker.bat:
@echo off
nasm -f coff asm.asm
gcc -o c.exe c.c asm.o
c.exe
最后输入maker,如果成功将输出:AddFour(4) =8
不成功的原因可能是你的NASM也许版本太老,也或者不是32位的NASM(非32位NASM
不认识coff目标文件格式),也或者是其他不可想象的原因。
_____________________________________________________________________________________
5 相关协议以及免责声明及其他
本文挡是本人参考http://delorie.com/djgpp上的某些文挡写成的,相当一部分直接来自
于那些文挡,所以本人只保留汉化的版权,但是您可以任意转载而且可以任意修改!
由于本人真正使用DJGPP的时间并不长,也没有时间仔细研究,所以并不能保证本 文相关
内容绝对正确,如果给您带来某些灾难性后果,本人概不负责!
如果您有问题,请通过邮件和我联系,我的邮箱是:xy_god@163.com
也可以到我的主页留言,我的主页是:http://www.xygod.pc5.org
最后,祝您愉快!^-^
Tutorial for Beginners on DJGPP
by xy_god
homepage:http://www.xygod.pc5.org
_____________________________________________________________________________________
Since I started using LINUX, I've had enough of WINDOWS, but many developments still can't do without DOS. It's really helpless! I've long heard that FREEDOS is completely compatible with MSDOS and follows the GPL license, so I made up my mind to get rid of WINDOWS and installed FREEDOS! The development tools can't use the original commercial things like TURBO C anymore. I'll use a pure GNU platform! Okay, replace TURBO C with DJGPP, and also replace MASM and TASM with NASM! What about the editor? Using EDIT is really unpleasant, so I replaced it with the DOS version of VIM, which is also a GNU tool! I'm sorry, but the Chinese platform still has to use UCDOS because it seems there's no GPL-licensed Chinese DOS platform yet. Fortunately, UCDOS is free, so there won't be pirated software in my system, and I don't have the feeling of being a thief! ^-^
OK, all this is done, but facing DJGPP and NASM, I suddenly feel I know nothing. When I wrote code with TURBO C and MASM, calling interrupts, interactive programming, etc., now I don't know how to do it anymore. Oh, it's just like when I first came into contact with LINUX! I had to go to http://delorie.com/djgpp to look, it's all in English. I forced myself to read it! Finally, I found some user guides and understood them, basically able to solve the above problems! Think about it, so many people like me must also be having the same headache, so I wrote this to share with everyone. It's not a translation because I don't have the patience to translate those wordy things. I'll write a tutorial according to my understanding. But big shots don't need to read it!
Also, this tutorial assumes that you have certain knowledge of turbo c or other C/C++ under DOS, and have done some interrupt calls, interrupt resident programs, video memory mapping operations, and mixed programming of C/C++ and ASM!
OK, enough nonsense. This tutorial is divided into the following four parts:
1 How to call interrupts;
2 How to write data to VIDEO RAM;
3 How to write an interrupt resident program;
4 How to call an assembly function written by NASM;
5 Related licenses, disclaimers, and others
_____________________________________________________________________________________
1 How to call interrupts:
Calling real-mode interrupts in DJGPP is very similar in form to using the INT86 call interrupts in TURBO C. Look at the following example (from http://delorie.com/djgpp):
#include
void main(void){
__dpmi_regs r;
r.x.ax = 0x13;
r.d.ebx = 0x10000;
r.h.cl = 4;
r.h.dh = 5;
__dpmi_int(0x10, &r);
}
__dpmi_regs is a structure containing all the registers used by 80386. If you want to use 8-bit registers, you can do it like this __dpmi_regs.h.xx (xx can be ah, al, bh, bl, etc. registers). If you want to use 16-bit registers, you can do it like this __dpmi_regs.x.xx (replace h with x). If you want to use 32-bit registers, replace h with d!
The above code is to call int10 interrupt and set the display mode to 0x13 (320*200 256).
When the interrupt is executed, the __dpmi_regs type parameter passed to __dpmi_int will contain the new values of the registers. You can detect these new values to analyze the execution of the interrupt! Note that these interrupt calls are all in real mode!
_____________________________________________________________________________________
2 How to write data to VIDEO RAM:
There are two methods to write data to VIDEO RAM. These two methods each have advantages and disadvantages. You need to choose according to the situation!
The first method:
The following is the code:
#include
void main(void){
char *screen;
__dpmi_regs r;
r.x.ax = 0x13;
r.d.ebx = 0x10000;
r.h.cl = 4;
r.h.dh = 5;
__dpmi_int(0x10, &r);/*Set to 0x13 display mode*/
if (__djgpp_nearptr_enable() == 0) return 0; /* It may happen */
screen = 0xa0000 + __djgpp_conventional_base;
screen = 4;/*Set a point in the upper left corner of the screen to red*/
__djgpp_nearptr_disable();
}
This example draws a red point in the upper left corner of the screen. This method has the following two disadvantages:
After calling some specific DPMI procedures, you must recalculate the near pointer value of video ram!
It can't be used under windows NT (at least it can't be used under XP, but it's okay under pure DOS)!
Therefore, this method is generally not recommended!
The following is the second method:
#include
#include
int main(){
int x=0,y=0;
char * screen;
__dpmi_regs r;
r.x.ax = 0x13;
__dpmi_int(0x10, &r);
_farsetsel(_dos_ds);
_farnspokeb(0xA0000, 4);
return 1;
}
This example draws a red point in the upper left corner of the screen. It also has a disadvantage that it's a little slower than the first method. But it's safer. It's said that it can be used under NT (but my verification under XP is very bad), and it doesn't need to recalculate the VIDEO RAM pointer. In the Allegro library, this method is used, so its speed is also relatively good, so this method is recommended!
_____________________________________________________________________________________
3 How to write an interrupt resident program:
Here's a program, translated from the DJGPP user guide. Not much to say, look at the program, the comments are very clear!
#include
#include
#include
#include
#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void *)&x,(long)sizeof(x));
#define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x,(long)sizeof(x));
#define TIMER 8/*The clock interrupt 8 occurs every 18.2 ms*/
int counter = 0;/*Initial value of the counter*/
void TickHandler(void){/*New clock interrupt handler*/
counter++;
}
int main(void)
{
/*Structure containing interrupt address (selector:offset) information*/
_go32_dpmi_seginfo OldISR, NewISR;
printf("Going to link the new interrupt handler to the old interrupt handler..\n");
getkey();
/*Lock functions and variables*/
LOCK_FUNCTION(TickHandler);
LOCK_VARIABLE(counter);
/*Write the old interrupt address into the OldISR structure variable*/
_go32_dpmi_get_protected_mode_interrupt_vector(TIMER, &OldISR);
/*NewISR points to the address of function TickHandler*/
NewISR.pm_offset = (int)TickHandler;
NewISR.pm_selector = _go32_my_cs();
/*Link the address pointed to by NewISR to the address of interrupt 8*/
_go32_dpmi_chain_protected_mode_interrupt_vector(TIMER,&NewISR);
while (!kbhit())
printf("%d\n",counter);
printf("Restoring the original clock interrupt.。。。。。\n");
/*Restore the original clock interrupt*/
_go32_dpmi_set_protected_mode_interrupt_vector(TIMER, &OldISR);
return 0;
}
_____________________________________________________________________________________
4 How to call an assembly function written by NASM:
The following example is written by myself. There are three files in total:
Save the following code as asm.asm:
; ---------------------------------------------------------------------------
; Prototype declaration: unsigned int AddFour(unsigned int x);
; Returns: x + 4
; ---------------------------------------------------------------------------
x_AddFour equ 8
_AddFour__FUi:
push ebp
mov ebp, esp
mov eax,
add eax, 4
mov esp, ebp
pop ebp
ret
Save the following code as c.c:
#include
extern unsigned int AddFour(unsigned int);
int main(void)
{
printf("AddFour(4) = %i\n", AddFour(4));
return 0;
}
Save the following code as maker.bat:
@echo off
nasm -f coff asm.asm
gcc -o c.exe c.c asm.o
c.exe
Finally, enter maker. If it's successful, it will output: AddFour(4) =8. The reason for failure may be that your NASM may be too old, or it's not 32-bit NASM (non-32-bit NASM doesn't recognize the coff object file format), or other unimaginable reasons.
_____________________________________________________________________________________
5 Related licenses, disclaimers, and others
This document is written by me referring to some documents on http://delorie.com/djgpp. A considerable part is directly from those documents, so I only retain the copyright of the Chinese translation, but you can freely reprint and modify it!
Since I really haven't used DJGPP for a long time and haven't had time to study carefully, I can't guarantee that the relevant content in this document is absolutely correct. If it causes some disastrous consequences to you, I won't be responsible!
If you have any questions, please contact me by email. My email is: xy_god@163.com
You can also leave a message on my homepage. My homepage is: http://www.xygod.pc5.org
Finally, I wish you a happy time! ^-^
|