|
GOTOmsdos
铂金会员
       C++启程者
积分 5154
发帖 1827
注册 2003-7-18
状态 离线
|
『楼 主』:
试了TC2, 好像不支持 内联汇编?
使用 LLM 解释/回答一下
试了TC2, 好像不支持 内联汇编?
是不是呢?
Tried TC2, it seems that inline assembly is not supported? Is that the case?
|
|
2006-6-24 03:46 |
|
|
070
高级用户
    苏醒的沉睡者
积分 659
发帖 217
注册 2003-2-15 来自 福建
状态 离线
|
『第 2 楼』:
使用 LLM 解释/回答一下
google搜的,关键字 TC 内联汇编
按照TC2.0的帮助系统所以说的,在TC2.0下是可以用汇编的,方法是使用asm关键字:其格式是:
asm opcode <operands> <;newline>,如同别的注释一样,<>之间的表示可选的;例如:
main()
{
char *c=hello,world $;
asm mov ah,9;asm mov dx,c;asm int 33;
printf(You sucessed! );
}
或者是:
main()
{
char *c=hello,world $;
asm mov ah,9
asm mov dx,c
asm int 33
printf(You sucessed!);
}
两种格式其实是一种.如果你用的是第一种的样式,记住:
每一句汇编语句都要以asm开头,如果一行内有多个句子,
那么千万不要忘记在两个句子之间的这个semicolon(分号),
但是最后一句汇编后面(如果后面没有其它的语句)的分号可有可无,象第一个例子中的
asm int 33;后面的分号就可以不要,因为它的后面没有其它
的语句了.但如果是这样:
asm mov ah,9; asm mov dx,c;asm int 33; printf(You sucessed!);
那么asm int 33;后面的分号便还是留下好,以免出现编译错误!
在这一点上颇象C语言.
还有一种格式是
asm{ assembly language statement},这种格式应该被普遍的欢迎.
它们的例子如下(其中的语句排列格式与上面两种相同):
asm{
mov ax,var1
add ax,var2
......
}
但是要注意这种格式TC2.0是不支持的!
只有后来的TC++3.0及后来的IDE支持!
工具的使用:
一旦你的C源文件里包括了这些好东西,则必须用TCC.EXE的COMMAND-LINE来编译,具体的命令参数TCC.EXE已经提供,这里不复阐述了.最简单的是:TCC C源文件名(使用这个方法,TCC会自动调用TASM.EXE和TLINK.EXE,并且能够使TLINK.EXE正确的找到需要的.obj和.lib文件,如果你单步编译的话,可能会碰到很多的问题,主要是TLINK.EXE它自己并不会去找.obj和.lib文件,你自己可以建一个.bat文件,如果要指定.lib文件的目录的话可以用/L参数,在文章的后面有一个例子).但大家要注意了,看一下你的TC目录下面到底是否有TASM.EXE文件,并在 TURBOC.CFG(这个文件包括TCC.EXE运行期参数,这里面所有参数在运很期都将被自动TCC.EXE使用,例如:-IH:TCINCLUDE
-LH:TCLIB)文件中设置好一些参数,并确认TASM.EXE的版本号要2.0以上,以及是否能够向下兼容.但是在大多数的情况下TC的目录是没有TASM.EXE的,或是版本不正常.
如果你有TASM.EXE文件并且TURBOC.CFG文件也已经写好了,但是还要注意一个
问题:运行TCC.EXE时要在独立的DOS SHELL下面(不要害怕,这不是一个新东西,我的意思
是,不在诸如TC下的DOS SHELL下面运行,我曾经败在这个问题下,当我发现时直想揍电脑
一顿,还好没有,不然就没有这篇文件了.)
还有一句重要的话:TC2.0支持大部分8086指令(当然用法有一些约定,不过现在我并不打算
进行详细说明,因为那是一件很繁杂的事,以后有时间或许会写出来----如果大家需要的话).
如果说上面我所说的那些约定很繁杂的话,那么下面的方法该是多么简单啊!
让我们使用Borland为TC2.0内建的变量来进行伪汇编.
或许你还不知道在TC2.0中还有一些内建的pseudo寄存器(可以看作是register 型的变量,但是它们比register型的变量好用的多)
_AX,_AH,_AL,
_BX,_BH,_BL,
_CX,_CH,_CL,
_DX,_DH,_DL,
_DI,_SI,_SP,
_CS,_DS,_ES,_SS
注意这些寄存器的size,_AX,_BX,_CX,_DX,_CS,_DS,_ES,_SS,_SI,_DI,_SP等都是16位的寄存器相当于C语言的unsigned int类型,其余的都是8位的寄存器(相当于unsigned char)(TC怎么可能支持32位的寄存呢,所以EAX等是不能用的,FS,GS和IP寄存器都是无效的),还有就是在传递参数的时候千万不要忘记使用强制类型转换.
中断调用指令是:__int__(interrupt_#)(注意int的前辍和后辍都是两个underscores)
For example:
#include<dos.h>
unsigned int _stklen=0x200;
unsigned int _heaplen=0;
main()
{
_DX=(unsigned int)Hello,world. $;
_AX=0x900;
__int__(0x21);
}
dos.h它是包含__int__()内建中断调用语句的头文件,因此是不可
缺少的._stklen和_heaplen是定义运行期堆栈和堆大小的两个内部
引用变量(这是个我自己想的名词,意指如果这两个变量在源文件中
显式的声明了,那么编译程序会自会引用来构造编译时期的信息以产生
用户希望的目标文件,如果不显式的声明则编译程序自动确定).
这两个变量也有一些约定,如果_stklen不显式声明,_heaplen赋值为零
都表示栈和堆都是defult的.
最后在TC2.0中还有一个没有说明的标志位寄存器flags,它也是内建
pseudo寄存器是:_FLAGS,是一个16位寄存器.这些内建的寄存器都可以进行
运算,但是要注意它们所代表的类型(必要时进行类型转换);
看起来这是不是一种好的办法啊(而且使用这种方法只要用个一个dos.h头文件就好,
不需要用TCC编译,可以直接在TC20的IDE下编译).
TC2.0中也提供了一些简单好用的函数来实现对DOS功能的调用如:
int86(...),int86x(...)(但是这些方法实际仍然要调用函数,所以不如使用
伪寄存器,又因为要牵涉到union REGS结构的内存分配所以系统的开销是增大了,
而使用伪寄存器是最简洁的),端口通信函数如:inportb(...),inport(...),
outportb(...),outport(...),指针转换函数:FP_OFF,FP_SEG,MK_FP,这些函数在
帮助系统中都有,有用时大家可以查阅.
tlinkbat.bat的例子:
rem The lib environment variable is the directory of the .obj and .lib file
set lib=h: clib
rem 这下面的句子中的c0s(C 零S)是一个.OBJ文件,是一个C程序的STARTUP文件
tlink %lib%c0s %1,%1,%1,/L%lib%emu.lib %lib%maths.lib %lib%cs.lib
set lib=
(使用时可将以rem开头的句子删除)
___________________________________________________
一些约定:
我们先说一下在TC20下写汇编(内联汇编--自己起的名字,大家可以想叫什么叫什么)时的编译器的编译原则:
1.所有在main()函数外的的汇编语言的语句都作为数据声明语句处理,也即在编译器编译时会将它放在数据段中,如:
asm string1 db Hello,,,world!,0ah,0xd,$
main()
{
asm mov dx,offset string1
asm mov ah,9
asm int 33
asm mov dx,offset string2
asm int 33
}
asm string2 db the string can be declared after the main() function!$
象这些样子在main()外面的汇编语言的数据定义语句(事实上不管是什么汇编语句,
只要是在main()之外,包括这个句子:asm mov ax,0x4c00),在编译后都放在数据段中,而C语言的数据声明语句仍按C的规则!
2.所有在main()函内的汇编语言的语句在编译后都放在代码段中,包括这个句子:
asm string2 db the string can be declared after the main() function!$
3.不要在以asm 开头的语句中使用C语言的关键字,这会导致编译阶段的错误
那么,根据这三条大家会得到什么样的结论呢?(先闭上眼想一想,你可能会由此变的
很赞赏自己,是的你应该这样相信自己是对的!)
让我们一起看一下这个结论:
1.根据编译原则1得到:不可以在main()外面写汇编命令语句(不要笑,正是与C语言相同才值得注意!),在任何地方都不要进行任何的段定义和宏定义(这是因为编译后的形式决定的,也即:在TC20下所有的汇编格式的语句只能是,直接性的数据定义和语句指令)!
2根据编译原则2得到:不可以在main()之内使用汇编的语句进行数据定义(同样不要笑,
大多数人在第一次在TC20下写汇编都会有这样的错误的)
3.如同类强制类型这样的事是不可以在以asm开头的汇编语句中使用的
好了,天即朗,气瞬清!这样一说,一个大体的框架就出来了!只要遵守这个原则写,就可避免很多莫名其妙的错误出现!
通俗的说:
汇编语句的数据定义放在main()外面,指令放在main()里面.
如果你没有更好的文档,那么记住我的这些话!
一些细节的问题:
在以asm开头的内联汇编语句中是不支持C的转义字符的,但是用C语言声明一个字符数组(含有转义字符的),然后用int 33 ah=9这功能时输出这个字符串时,其中的转义字符是有效的(这主要是因为编译后其内部表示形式不同造成的,自己想想会有答案的).
内联汇编支持C的一些如数值表示,字符串声明格式等,
如:一个十六进制的数据可以用两种方式表示:0xa 和0ah,字符串可以是这样:
Hello,world!$(如同C)也可以这样Hello,world!$(用汇编自己的方?.
象C一样你同样要注意赋值的类型,而且要比C更严格(汇编从来不自己动手做
如同类型转换啊这样事),所以一切的事完全要你自己做好!而且你不要企图以C的形式
做这件事,如这样的格式 asm mov dx,(unsigned)a(a是一个这样的东西,
char a=hello,world!;),而且这样句子也会导致错误:asm mov dx,word ptr a(逻辑错误),不过这不是在编译时的错误,而是运行期的错误(具体的原因自己想一想,象word label这样的东西的运算作用和会导致的后果),你可以这样用一个句子做中间人如int i=(unsigned)a;asm mov dx,i(也千万不要用asm mov dx,(unsigned)a 这样的句子.但是,告诉大家一个好消息,你可以用指针指向一个字符串,然后你会惊讶你竟然可以这样:
char *p=hello,world;asm mov dx,p,然后用int 33 ah=9的功能输出这个字符串而不会有错误(这也表现出指针的特点,它是一个二字节的(TC20下)变量,含有的是一个地址,这与其指向的变量的类型是毫无关系的).
内汇汇编语句不支持->这个运算符.还有标号的问题,在最后的例子中你会年看到一些特别之处!
上面所说的只是很细小并微少的一些事(也是很常遇到的),尚有很多的细节要说,但由于本人时间有限不能一一列举,如C的结构在内联汇编的应用等大家可以按照其运行机理去想想一下用法;另外,由于这只是一件学习的事,所以还是大家自己学(找一下有关文档,当然现在已经没有什么比较完整的了),情况会好的多, 我在对内联汇编的学习过程中领会到了不少的东西,例如编译原理方面的知识,以及如何做会使代码更高效,占空间最少等的方法.最后向大家推荐一种方法,在利用TCC的-S开关可以生成C源文件的汇编代码
(或许很多的人都用过)是很好的学习材料!祝大家学有所成!
Cstarter
02-11-17
/* 由于个人的时间和能力有限,难免有错误和不详细的地方,请大家见谅!
My Email:wxe85@sina.com Cstarter1985@hotmail.com QQ:170594633 */
一些例子:
下面这个例子是对沈美明 温冬婵的
<<IBM-PC 汇编语言程序设计>>清华版第十一章程序的改写
可直接在命令行上键入 tcc filename 就可以,当然你要有TASM.EXE
/*
asm mus_frep dw 330,294,262,294,3 dup(330)
asm dw 3 dup(294),330,392,392
asm dw 330,294,262,294,4 dup(330)
asm dw 294,294,330,294,262,-1
asm mus_time dw 6 dup(25),50
asm dw 2 dup (25,25,50)
asm dw 12 dup(25),100
*/
asm mus_frep dw 330,392,330,294,330,392,330,294,330
asm dw 330,392,330,294,262,294,330,392,294
asm dw 262,262,220,196,196,220,262,294,330,262
asm dw -1
asm mus_time dw 3 dup (50),25,25,50,25,25,100
asm dw 2 dup (50,50,25,25),100
asm dw 3 dup (50,25,25),100
main()
{
asm jmp start
/*设置发声的频率,这一段在沈美明 温冬婵的
<<IBM-PC 汇编语言程序设计>>清华版第十一章有详细的说明 */
sound:
asm mov al,0b6h
asm out 43h,al
asm mov dx,12h
asm mov ax,533h*896
asm div di
asm out 42h, al
asm mov al,ah
/* 这个延时是用来防止两次IO操作的最后一次操作的错误,
因为CPU比总线的俣瓤旌芏?所以 要延时等待第一次操作完成后再进行第二次操作*/
asm mov cx,1000
delay:
asm loop delay
asm out 42h,al
asm in al,61h
asm mov ah,al
asm or al,3
asm out 61h,al
/* 使用中断15H功能86H延时CX:DX=微秒数*/
asm mov ax,2710h
asm mul bx
asm mov cx,dx
asm mov dx,ax
asm mov ah,86h
asm int 15h /*可用__int__(0x15);代替*/
asm mov al,ah
asm out 61h,al
asm jmp add_count
/*------------------*/
start:
asm mov si,offset mus_frep
asm lea bp,mus_time
frep:
asm mov di,
asm cmp di,-1
asm je end_mus
asm mov bx,
asm jmp sound
add_count: /*标号不能用汇编语言写*/
asm add si,2
asm add bp,2
asm jmp frep
end_mus:;
}
对于上面的程序大家可用伪寄存器的方法写一个,要容易的多!
/*一个发声程序!(引自<<PC技术内幕>>电力版--这个版不好,不如清华版的)*/
#includedos.h
main()
{
static union REGS ourregs;
outportb(0x43,0xb6);
outportb(0x42,0xee);
outportb(0x42,0);
outportb(0x61,(inportb(0x61)|0x03));
ourregs.h.ah=0x86;
ourregs.x.cx=0x001e;
ourregs.x.dx=0x8480;
int86(0x15,&ourregs,&ourregs);
outportb(0x61,(inportb(0x61)&0xfc));
}
Google search, keyword: TC inline assembly
According to the help system of TC2.0, it is said that in TC2.0, assembly language can be used. The method is to use the asm keyword: its format is asm opcode <operands> <;newline>, just like other comments, the content between <> is optional; for example:
main()
{
char *c="hello,world" $;
asm mov ah,9;asm mov dx,c;asm int 33;
printf("You sucessed! ");
}
Or:
main()
{
char *c="hello,world" $;
asm mov ah,9
asm mov dx,c
asm int 33
printf("You sucessed!");
}
The two formats are actually the same. If you use the first style, remember:
Each assembly statement must start with asm. If there are multiple sentences in one line, then never forget the semicolon (;) between the two sentences. But the semicolon after the last assembly statement (if there are no other statements after it) can be optional. For example, the semicolon after asm int 33; in the first example can be omitted because there are no other statements after it. But if it is like this:
asm mov ah,9; asm mov dx,c;asm int 33; printf("You sucessed!");
Then the semicolon after asm int 33; is still better to leave to avoid compilation errors! This is quite similar to the C language in this regard.
There is also a format asm{ assembly language statement}, this format should be generally welcomed.
Their examples are as follows (the statement arrangement format is the same as the above two):
asm{
mov ax,var1
add ax,var2
......
}
But note that this format is not supported by TC2.0!
Only later TC++3.0 and later IDEs support it!
Tool usage:
Once your C source file includes these good things, you must compile with the COMMAND-LINE of TCC.EXE. The specific command parameters are already provided by TCC.EXE, and I won't elaborate here. The simplest is: TCC C source file name (using this method, TCC will automatically call TASM.EXE and TLINK.EXE, and can make TLINK.EXE correctly find the required .obj and .lib files. If you compile step by step, you may encounter many problems. The main thing is that TLINK.EXE itself will not look for .obj and .lib files. You can build a .bat file by yourself. If you want to specify the directory of .lib files, you can use the /L parameter. There is an example later in the article). But everyone should pay attention, check whether there is a TASM.EXE file in your TC directory, and set some parameters in the TURBOC.CFG (this file includes the runtime parameters of TCC.EXE, all parameters in it will be automatically used by TCC.EXE during runtime, such as: -IH:TCINCLUDE -LH:TCLIB) file, and confirm that the version number of TASM.EXE is above 2.0, and whether it is downward compatible. But in most cases, there is no TASM.EXE in the TC directory, or the version is abnormal.
If you have the TASM.EXE file and the TURBOC.CFG file is also written, but you also need to pay attention to one problem: run TCC.EXE under an independent DOS SHELL (don't be afraid, this is not a new thing, I mean, don't run under the DOS SHELL in TC, for example. I once lost here. When I found it, I just wanted to beat the computer. Fortunately, I didn't, otherwise there would be no this article.)
There is also an important sentence: TC2.0 supports most 8086 instructions (of course, there are some conventions in usage, but I don't plan to explain them in detail now, because it is a very cumbersome thing. Maybe I will write it later - if you need it).
If the conventions I mentioned above are very cumbersome, then the following method is so simple!
Let's use the built-in variables of Borland for TC2.0 to perform pseudo-assembly.
Maybe you don't know that there are some built-in pseudo-registers in TC2.0 (can be regarded as register-type variables, but they are much more useful than register-type variables)
_AX, _AH, _AL,
_BX, _BH, _BL,
_CX, _CH, _CL,
_DX, _DH, _DL,
_DI, _SI, _SP,
_CS, _DS, _ES, _SS
Note the size of these registers. _AX, _BX, _CX, _DX, _CS, _DS, _ES, _SS, _SI, _DI, _SP, etc. are all 16-bit registers equivalent to the unsigned int type in C language. The rest are all 8-bit registers (equivalent to unsigned char) (How can TC support 32-bit registers, so EAX, etc. cannot be used. FS, GS and IP registers are all invalid). Also, when passing parameters, don't forget to use cast.
The interrupt call instruction is: __int__(interrupt_#) (note that both the prefix and suffix of int are two underscores)
For example:
#include<dos.h>
unsigned int _stklen=0x200;
unsigned int _heaplen=0;
main()
{
_DX=(unsigned int)"Hello,world. " $;
_AX=0x900;
__int__(0x21);
}
dos.h is the header file that contains the __int__() built-in interrupt call statement, so it is indispensable. _stklen and _heaplen are two internal reference variables (this is a term I thought of, meaning that if these two variables are explicitly declared in the source file, the compiler will refer to them to construct the information during compilation to generate the target file the user wants. If they are not explicitly declared, the compiler will automatically determine). There are also some conventions for these two variables. If _stklen is not explicitly declared and _heaplen is assigned zero, it means that the stack and heap are default.
Finally, there is an undocumented flag register flags in TC2.0, which is also a built-in pseudo-register: _FLAGS, which is a 16-bit register. These built-in registers can all be operated, but pay attention to the types they represent (perform type conversion when necessary);
Does this seem like a good way? (And using this method, as long as you use the dos.h header file, you don't need to compile with TCC, you can directly compile under the IDE of TC20).
TC2.0 also provides some simple and easy-to-use functions to implement calls to DOS functions such as: int86(...), int86x(...) (but these methods actually still call functions, so it is not as good as using pseudo-registers. Also, because it involves the memory allocation of the union REGS structure, the system overhead is increased, and using pseudo-registers is the most concise). Port communication functions such as: inportb(...), inport(...), outportb(...), outport(...), pointer conversion functions: FP_OFF, FP_SEG, MK_FP. These functions are all in the help system. You can refer to them when useful.
Example of tlinkbat.bat:
rem The lib environment variable is the directory of the .obj and .lib file
set lib=h: clib
rem The following sentence's c0s (C zero S) is an .OBJ file, which is a STARTUP file of a C program
tlink %lib%c0s %1,%1,%1,/L%lib%emu.lib %lib%maths.lib %lib%cs.lib
set lib=
(You can delete the sentences starting with rem when using)
___________________________________________________
Some conventions:
Let's first talk about the compilation principle of the compiler when writing assembly language (inline assembly - my own name, you can call it whatever you want) under TC20:
1. All assembly language statements outside the main() function are treated as data declaration statements, that is, they will be placed in the data segment during compiler compilation. For example:
asm string1 db "Hello,,,world!",0ah,0xd,$
main()
{
asm mov dx,offset string1
asm mov ah,9
asm int 33
asm mov dx,offset string2
asm int 33
}
asm string2 db "the string can be declared after the main() function!$"
Like this, the assembly language data definition statements outside main() (in fact, no matter what assembly statements, as long as they are outside main(), including this sentence: asm mov ax,0x4c00), are placed in the data segment after compilation, and the C language data declaration statements are still in accordance with C rules!
2. All assembly language statements inside the main() function are placed in the code segment after compilation, including this sentence:
asm string2 db "the string can be declared after the main() function!$"
3. Do not use C language keywords in assembly statements starting with asm, which will cause errors in the compilation stage
Then, what conclusion will you get according to these three items? (Close your eyes and think for a while, you may become very appreciative of yourself, yes, you should believe yourself is right!)
Let's take a look at this conclusion together:
1. According to compilation principle 1, it is concluded that: you cannot write assembly command statements outside main() (don't laugh, it is the same as C language, which is worth noting!), do not perform any segment definitions and macro definitions anywhere (this is because the compiled form determines it, that is, in TC20, all assembly format statements can only be direct data definitions and statement instructions)!
2 According to compilation principle 2, it is concluded that: you cannot use assembly statements to perform data definition inside main() (also don't laugh, most people will have such errors when writing assembly for the first time under TC20)
3. Things like class cast are not allowed in assembly statements starting with asm
Okay, the sky is bright and the air is clear! With this said, a general framework comes out! As long as you follow this principle, many inexplicable errors can be avoided!
In plain language:
The data definition of assembly statements is placed outside main(), and the instructions are placed inside main().
If you don't have better documentation, then remember my words!
Some details:
Escape characters of C are not supported in inline assembly statements starting with asm. But when a character array declared in C language (containing escape characters) is used, and the int 33 ah=9 function is used to output this string, the escape characters are valid (this is mainly because the internal representation forms are different after compilation, you can think about it yourself to get the answer).
Inline assembly supports some C-like things such as numerical representation, string declaration format, etc.
For example: a hexadecimal data can be represented in two ways: 0xa and 0ah, and the string can be like this: "Hello,world!$" (like C) or "Hello,world!$" (in the assembly's own way?.
Like C, you also need to pay attention to the type of assignment, and it is stricter than C (assembly never does things like type conversion by itself)
So everything is completely up to you to do well! And you don't try to do this in the form of C. For example, the format asm mov dx,(unsigned)a (a is such a thing, char a="hello,world!";), and this sentence will also cause an error: asm mov dx,word ptr a (logical error), but this is not a compilation error, but a runtime error (think about the specific reason yourself, like the operation effect and consequences of word label, etc.). You can use a sentence as an intermediary, such as int i=(unsigned)a; asm mov dx,i (also never use asm mov dx,(unsigned)a such a sentence. But, tell everyone a good news, you can use a pointer to point to a string, and then you will be surprised that you can do this: char *p="hello,world"; asm mov dx,p, and then use the int 33 ah=9 function to output this string without errors (this also shows the characteristics of the pointer, it is a two-byte (under TC20) variable, which contains an address, which has nothing to do with the type of the variable it points to).
Inline assembly statements do not support the -> operator. There is also the problem of labels. You will see some special features in the final example!
The above are just some small and minor things (also very common), and there are still many details to say, but due to my limited time, I can't list them one by one. For example, the application of C structures in inline assembly, you can think about the usage according to its operation mechanism; in addition, since this is just a learning matter, it is still better for everyone to learn by themselves (find some relevant documents, of course, there are not many complete ones now), the situation will be much better. I have learned a lot in the process of learning inline assembly, such as knowledge in compilation principles, and methods of how to make the code more efficient and occupy the least space, etc. Finally, I recommend a method to everyone. Using the -S switch of TCC can generate the assembly code of the C source file (maybe many people have used it) which is a very good learning material! I wish everyone success in learning!
Cstarter
02-11-17
/* Due to personal time and ability limitations, there are inevitable errors and details. Please forgive me!
My Email:wxe85@sina.com Cstarter1985@hotmail.com QQ:170594633 */
Some examples:
The following example is a rewrite of the program in Chapter 11 of "IBM-PC Assembly Language Programming" by Shen Meiming and Wen Dongchan, Tsinghua Edition. You can directly type tcc filename on the command line, of course, you need to have TASM.EXE
/*
asm mus_frep dw 330,294,262,294,3 dup(330)
asm dw 3 dup(294),330,392,392
asm dw 330,294,262,294,4 dup(330)
asm dw 294,294,330,294,262,-1
asm mus_time dw 6 dup(25),50
asm dw 2 dup (25,25,50)
asm dw 12 dup(25),100
*/
asm mus_frep dw 330,392,330,294,330,392,330,294,330
asm dw 330,392,330,294,262,294,330,392,294
asm dw 262,262,220,196,196,220,262,294,330,262
asm dw -1
asm mus_time dw 3 dup (50),25,25,50,25,25,100
asm dw 2 dup (50,50,25,25),100
asm dw 3 dup (50,25,25),100
main()
{
asm jmp start
/*Setting the frequency of sound generation, this section is detailed in Chapter 11 of "IBM-PC Assembly Language Programming" by Shen Meiming and Wen Dongchan, Tsinghua Edition */
sound:
asm mov al,0b6h
asm out 43h,al
asm mov dx,12h
asm mov ax,533h*896
asm div di
asm out 42h, al
asm mov al,ah
/* This delay is to prevent errors in the last operation of two IO operations, because the CPU is faster than the bus, so you need to delay and wait for the first operation to complete before performing the second operation */
asm mov cx,1000
delay:
asm loop delay
asm out 42h,al
asm in al,61h
asm mov ah,al
asm or al,3
asm out 61h,al
/* Using interrupt 15H function 86H to delay CX:DX=microseconds */
asm mov ax,2710h
asm mul bx
asm mov cx,dx
asm mov dx,ax
asm mov ah,86h
asm int 15h /* Can be replaced by __int__(0x15); */
asm mov al,ah
asm out 61h,al
asm jmp add_count
/*------------------*/
start:
asm mov si,offset mus_frep
asm lea bp,mus_time
frep:
asm mov di,
asm cmp di,-1
asm je end_mus
asm mov bx,
asm jmp sound
add_count: /* Labels cannot be written in assembly language */
asm add si,2
asm add bp,2
asm jmp frep
end_mus:;
}
For the above program, you can write one using the pseudo-register method, which is much easier!
/*A sound generation program! (quoted from "Inside PC Technology" Power Edition - this edition is not as good as Tsinghua Edition)*/
#includedos.h
main()
{
static union REGS ourregs;
outportb(0x43,0xb6);
outportb(0x42,0xee);
outportb(0x42,0);
outportb(0x61,(inportb(0x61)|0x03));
ourregs.h.ah=0x86;
ourregs.x.cx=0x001e;
ourregs.x.dx=0x8480;
int86(0x15,&ourregs,&ourregs);
outportb(0x61,(inportb(0x61)&0xfc));
}
|

好久没碰Dos,手都生了,赶紧回来练练.嘿嘿 |
|
2006-6-24 21:05 |
|
|
GOTOmsdos
铂金会员
       C++启程者
积分 5154
发帖 1827
注册 2003-7-18
状态 离线
|
『第 3 楼』:
使用 LLM 解释/回答一下
main()
{
asm mov dx,offset string1
asm mov ah,9
asm int 33
asm mov dx,offset string2
asm int 33
}
TC2, 他说:“in-line assembly not allowed in funciton main”
main()
{
asm mov dx,offset string1
asm mov ah,9
asm int 33
asm mov dx,offset string2
asm int 33
}
TC2, he said: "in-line assembly not allowed in function main"
|
|
2006-6-25 02:13 |
|
|
Scott0902
中级用户
  
积分 466
发帖 237
注册 2005-10-12
状态 离线
|
『第 4 楼』:
使用 LLM 解释/回答一下
TC2不支持,BC一定支持
TC2 is not supported, and BC must be supported
|
|
2006-6-26 23:44 |
|
|
zhgwbzhd
高级用户
   
积分 506
发帖 187
注册 2005-12-4
状态 离线
|
『第 5 楼』:
使用 LLM 解释/回答一下
支持的,我都用它弄过东西,我常使用tc2和bc2
Supported, I've used it to do things. I often use tc2 and bc2
|
|
2006-6-27 08:14 |
|
|
cnch
中级用户
  
积分 326
发帖 70
注册 2003-1-10
状态 离线
|
『第 6 楼』:
使用 LLM 解释/回答一下
TC2支持汇编内嵌,但在编译时要加参数
TC2 supports inline assembly, but you need to add parameters when compiling
|
|
2006-6-30 12:07 |
|
|