现在,我们来看看在linux平台上面怎么对之前实现的功能进行改写了。当然这里先说启动区加载程序跟在win下面是一样的代码的,不同的就是C调用汇编程序代码和汇编调用C代码这里不同。以及剩下的就是一些makefile文件的不同。
我们重点放在C于汇编之间的调用,当然我们这里先不讲汇编是怎么调用C的,因为这一部分我也暂时还没看,我就先用米油给的一个entry.S来直接使用了,后面才对这一部分进行分析。
那我们来看看C调用汇编,
用gcc内嵌gas汇编的方法非常好,也非常的高效。
只需要用一个宏定义就行了,如要在c中调用汇编的hlt指令,只需要
#define io_halt() asm("hlt")
这样就可以把io_halt()当一个正常的函数用了,但是这是最容易的,有输入,输出参数的函数的调用规则要复杂一些。但是只是一个规则。那么我们可以很快的将之前的代码进行改写
/********************************************************************** main.c **********************************************************************/ #include<header.h> void bootmain(void) { // io_halt(); //有效 // clear_screen(40); //read //color_screen(15); //white // x86上,类似单片机编程的感觉,直接有指针访问Vram int i; unsigned char *p; int color; int x=320,y=200; init_palette(); //after init_palette,对于color不知道是如何控制了 p=(unsigned char*)0xa0000; boxfill8(p,320,110,20,20,250,150); //draw a window boxfill(170,0, 0 ,x-1,y-29); //task button boxfill(15,0, y-28,x-1,y-28); boxfill(27,0, y-27,x-1,y-27); boxfill(27,0, y-26,x-1,y-1); //left button boxfill(30, 3, y-24, 59, y-24); boxfill(30, 2, y-24, 2 , y-4); boxfill(20, 3, y-4, 59, y-4); boxfill(20, 59, y-23, 59, y-5); boxfill(0, 2, y-3, 59, y-3); boxfill(0, 60, y-24, 60, y-3); // //right button boxfill(110, x-47, y-24,x-4,y-24); boxfill(110, x-47, y-23,x-47,y-4); boxfill(110, x-47, y-3,x-4,y-3); boxfill(110, x-3, y-24,x-3,y-3); while(1); }
screen.c #include<header.h> void clear_screen(char color) //15:pure white { int i; for(i=0xa0000;i<0xaffff;i++) { write_mem8(i,color); //if we write 15 ,all pixels color will be white,15 mens pure white ,so the screen changes into white } } void color_screen(char color) //15:pure white { int i; color=color; for(i=0xa0000;i<0xaffff;i++) { write_mem8(i,i); //if we write 15 ,all pixels color will be white,15 mens pure white ,so the screen changes into white } } void init_palette(void) { //16种color,每个color三个字节。 static unsigned char table_rgb[16*3]= { 0x00,0x00,0x00, /*0:black*/ 0xff,0x00,0x00, /*1:light red*/ 0x00,0xff,0x00, /*2:light green*/ 0xff,0xff,0x00, /*3:light yellow*/ 0x00,0x00,0xff, /*4:light blue*/ 0xff,0x00,0xff, /*5:light purper*/ 0x00,0xff,0xff, /*6:light blue*/ 0xff,0xff,0xff, /*7:white*/ 0xc6,0xc6,0xc6, /*8:light gray*/ 0x84,0x00,0x00, /*9:dark red*/ 0x00,0x84,0x00, /*10:dark green*/ 0x84,0x84,0x00, /*11:dark yellow*/ 0x00,0x00,0x84, /*12:dark 青*/ 0x84,0x00,0x84, /*13:dark purper*/ 0x00,0x84,0x84, /*14:light blue*/ 0x84,0x84,0x84, /*15:dark gray*/ }; set_palette(0,15,table_rgb); return; } //设置调色板, 只用到了16个color,后面的都没有用到。 void set_palette(int start,int end, unsigned char *rgb) { int i,eflag; eflag=read_eflags(); //记录从前的cpsr值 io_cli(); // disable interrupt outb(0x03c8,start); for(i=start;i<=end;i++) { outb(0x03c9,rgb[0]); outb(0x03c9,rgb[1]); outb(0x03c9,rgb[2]); rgb=rgb+3; } write_eflags(eflag); //恢复从前的cpsr return; } void boxfill8(unsigned char *vram,int xsize,unsigned char color,int x0,int y0,int x1,int y1) { int x,y; for(y=y0;y<=y1;y++) { for(x=x0;x<=x1;x++) { vram[y*xsize+x]=color; } } } void boxfill(unsigned char color,int x0,int y0,int x1,int y1) { boxfill8((unsigned char *)0xa0000,320,color,x0,y0,x1,y1); }
head.h文件 #ifndef header #define header #include<x86.h> #define io_halt() asm("hlt") #define write_mem8(addr,data8) (*(volatile char *)(addr))=(char)data8 #define io_cli() asm("cli") #define io_sti() asm("sti") #define io_stihlt() (io_cli();io_halt;) extern void clear_screen(char color) ; //color=15 pure white color=40 red extern void color_screen(char color) ; extern void init_palette(void);//用现成的table_rgb来初始化调色板 extern void set_palette(int start,int end, unsigned char *rgb); extern void boxfill8(unsigned char *vram,int xsize,unsigned char color,int x0,int y0,int x1,int y1); extern void boxfill(unsigned char color,int x0,int y0,int x1,int y1); #endif
x86.h #ifndef JOS_INC_X86_H #define JOS_INC_X86_H #include <types.h> static __inline void breakpoint(void) __attribute__((always_inline)); static __inline uint8_t inb(int port) __attribute__((always_inline)); static __inline void insb(int port, void *addr, int cnt) __attribute__((always_inline)); static __inline uint16_t inw(int port) __attribute__((always_inline)); static __inline void insw(int port, void *addr, int cnt) __attribute__((always_inline)); static __inline uint32_t inl(int port) __attribute__((always_inline)); static __inline void insl(int port, void *addr, int cnt) __attribute__((always_inline)); static __inline void outb(int port, uint8_t data) __attribute__((always_inline)); static __inline void outsb(int port, const void *addr, int cnt) __attribute__((always_inline)); static __inline void outw(int port, uint16_t data) __attribute__((always_inline)); static __inline void outsw(int port, const void *addr, int cnt) __attribute__((always_inline)); static __inline void outsl(int port, const void *addr, int cnt) __attribute__((always_inline)); static __inline void outl(int port, uint32_t data) __attribute__((always_inline)); static __inline void invlpg(void *addr) __attribute__((always_inline)); static __inline void lidt(void *p) __attribute__((always_inline)); static __inline void lldt(uint16_t sel) __attribute__((always_inline)); static __inline void ltr(uint16_t sel) __attribute__((always_inline)); static __inline void lcr0(uint32_t val) __attribute__((always_inline)); static __inline uint32_t rcr0(void) __attribute__((always_inline)); static __inline uint32_t rcr2(void) __attribute__((always_inline)); static __inline void lcr3(uint32_t val) __attribute__((always_inline)); static __inline uint32_t rcr3(void) __attribute__((always_inline)); static __inline void lcr4(uint32_t val) __attribute__((always_inline)); static __inline uint32_t rcr4(void) __attribute__((always_inline)); static __inline void tlbflush(void) __attribute__((always_inline)); static __inline uint32_t read_eflags(void) __attribute__((always_inline)); static __inline void write_eflags(uint32_t eflags) __attribute__((always_inline)); static __inline uint32_t read_ebp(void) __attribute__((always_inline)); static __inline uint32_t read_esp(void) __attribute__((always_inline)); static __inline void cpuid(uint32_t info, uint32_t *eaxp, uint32_t *ebxp, uint32_t *ecxp, uint32_t *edxp); static __inline uint64_t read_tsc(void) __attribute__((always_inline)); static __inline void breakpoint(void) { __asm __volatile("int3"); } //int3会产生软件中断,这个软件中断通常是为调试代码而用的。可以在软件中断服务程序中打印出一些我们需要的信息。 //因为我们现在还没有搞明白ldt的内容,没有有效的中断服务程序,所以调用这个软件中断后,会产生reset的效果。 //////////////////////////////////////////////////////////////////////////////////////////////////////////// //in: read a port static __inline uint8_t inb(int port) { //read a byte from port uint8_t data; __asm __volatile("inb %w1,%0" : "=a" (data) : "d" (port)); return data; } static __inline uint16_t inw(int port) { //read word from port uint16_t data; __asm __volatile("inw %w1,%0" : "=a" (data) : "d" (port)); return data; } static __inline uint32_t inl(int port) { uint32_t data; __asm __volatile("inl %w1,%0" : "=a" (data) : "d" (port)); return data; } // out:write a data to a port static __inline void outb(int port, uint8_t data) { __asm __volatile("outb %0,%w1" : : "a" (data), "d" (port)); } static __inline void outw(int port, uint16_t data) { __asm __volatile("outw %0,%w1" : : "a" (data), "d" (port)); } static __inline void outl(int port, uint32_t data) { __asm __volatile("outl %0,%w1" : : "a" (data), "d" (port)); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static __inline void insb(int port, void *addr, int cnt) { __asm __volatile("cld\n\trepne\n\tinsb" : "=D" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "memory", "cc"); } static __inline void insw(int port, void *addr, int cnt) { __asm __volatile("cld\n\trepne\n\tinsw" : "=D" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "memory", "cc"); } static __inline void insl(int port, void *addr, int cnt) { __asm __volatile("cld\n\trepne\n\tinsl" : "=D" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "memory", "cc"); } static __inline void outsb(int port, const void *addr, int cnt) { __asm __volatile("cld\n\trepne\n\toutsb" : "=S" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "cc"); } static __inline void outsw(int port, const void *addr, int cnt) { __asm __volatile("cld\n\trepne\n\toutsw" : "=S" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "cc"); } static __inline void outsl(int port, const void *addr, int cnt) { __asm __volatile("cld\n\trepne\n\toutsl" : "=S" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "cc"); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// static __inline void invlpg(void *addr) { __asm __volatile("invlpg (%0)" : : "r" (addr) : "memory"); } static __inline void lidt(void *p) { __asm __volatile("lidt (%0)" : : "r" (p)); } static __inline void lldt(uint16_t sel) { __asm __volatile("lldt %0" : : "r" (sel)); } static __inline void ltr(uint16_t sel) { __asm __volatile("ltr %0" : : "r" (sel)); } static __inline void lcr0(uint32_t val) { __asm __volatile("movl %0,%%cr0" : : "r" (val)); } static __inline uint32_t rcr0(void) { uint32_t val; __asm __volatile("movl %%cr0,%0" : "=r" (val)); return val; } static __inline uint32_t rcr2(void) { uint32_t val; __asm __volatile("movl %%cr2,%0" : "=r" (val)); return val; } static __inline void lcr3(uint32_t val) { __asm __volatile("movl %0,%%cr3" : : "r" (val)); } static __inline uint32_t rcr3(void) { uint32_t val; __asm __volatile("movl %%cr3,%0" : "=r" (val)); return val; } static __inline void lcr4(uint32_t val) { __asm __volatile("movl %0,%%cr4" : : "r" (val)); } static __inline uint32_t rcr4(void) { uint32_t cr4;static __inline uint8_t inb(int port) { //read a byte from port uint8_t data; __asm __volatile("inb %w1,%0" : "=a" (data) : "d" (port)); return data; } __asm __volatile("movl %%cr4,%0" : "=r" (cr4)); return cr4; } static __inline void tlbflush(void) { uint32_t cr3; __asm __volatile("movl %%cr3,%0" : "=r" (cr3)); __asm __volatile("movl %0,%%cr3" : : "r" (cr3)); } static __inline uint32_t read_ebp(void) { uint32_t ebp; __asm __volatile("movl %%ebp,%0" : "=r" (ebp)); return ebp; } static __inline uint32_t read_esp(void) { uint32_t esp; __asm __volatile("movl %%esp,%0" : "=r" (esp)); return esp; } static __inline void cpuid(uint32_t info, uint32_t *eaxp, uint32_t *ebxp, uint32_t *ecxp, uint32_t *edxp) { uint32_t eax, ebx, ecx, edx; asm volatile("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (info)); if (eaxp) *eaxp = eax; if (ebxp) *ebxp = ebx; if (ecxp) *ecxp = ecx; if (edxp) *edxp = edx; } static __inline uint64_t read_tsc(void) { uint64_t tsc; __asm __volatile("rdtsc" : "=A" (tsc)); return tsc; } ////////////////////////////////////////////////////////////////////////////////////////////////// //read eflags and write_eflags static __inline uint32_t read_eflags(void) { uint32_t eflags; __asm __volatile("pushfl; popl %0" : "=r" (eflags)); return eflags; } static __inline void write_eflags(uint32_t eflags) { __asm __volatile("pushl %0; popfl" : : "r" (eflags)); } #endif /* !JOS_INC_X86_H */
我们这里重点讲讲gcc内嵌汇编的一些要点,AT&T我也不太熟悉,不过跟intel汇编是大同小异的。
asm volatile( "assembler template" : output : input : clobber);
上面是基本的格式:
其中assembler template为汇编指令部分,output是输出部分,input是输入部分,clobber表示被修改的部分,汇编指令中的数字和前缀%表示样板操作数,例如%0,%1等,用来依次指代后面的输出部分,输入部分等样板操作数。由于这些样板操作数使用了%,因此寄存器前面要加两个%。output,input分别是输出部分和输入部分,clobber是损坏部分。
gcc内嵌汇编限制符
a 对应的变量必须在EAX寄存器
b EBX
c ECX
d EDX
S ESI
D EDI
q EAX,EBX,ECX,EDX中的任何一个
r EAX,EBX,ECX,EDX,ESI,EDI中的任何一个
A EAX:EDX组合成一个64位操作数
m 操作数必须是内存中的变量
o 操作是内存变量,并且对操作数的寻址方式为基址加一个偏移量
V 操作数是内存变量,但是寻址方式位基址,没有偏移量
g 操作数可以是内存变量,立即数,EAX,EBX,ECX或者EDX
I 操作数是0-31的立即数(用于32位的移位操作)
J 操作数是0-63的立即数(用于64位的移位操作)
K 操作数必须是0xFF
L 操作数必须是0xFFFF
M 操作数是0,1,2,3
N 操作数可以是0-255中的任何一个数(用于in/out指令)
f 操作数是浮点寄存器
t 第一个浮点寄存器
u 第二个浮点寄存器
= 操作数是只写的(用于输出)
+ 操作数是可读可写的(用于输入输出)
& 表示在汇编代码前,对应的操作数会被输入部分修改
memory 用在损坏部分中,表示内存被修改了
static __inline uint8_t
inb(int port)
{
//read a byte from port
uint8_t data;
__asm __volatile("inb %w1,%0" : "=a" (data) : "d" (port));
return data;
}
inb 是intel x86的一条指令
%w1表示宽度为w的1号占位符
%0表示0号占位符
inb %w1,%0 意思是将%w1读到%0,
嵌入式汇编除指令外有三部分(可选的),依次为
输出:"=a" (_v),_v0对应0号占位符,=表示只写,a表示最终从%eax / %ax / %al传送给_v
输入:"Nd" (port),port对应1号占位符号,N表示 0-255 之间的立即数 d表示将port传送给%edx / %dx / %dl
破坏描述:此处没有
相关推荐
GAS汇编指令小集GAS汇编指令小集GAS汇编指令小集GAS汇编指令小集GAS汇编指令小集GAS汇编指令小集GAS汇编指令小集
《gas汇编器命令详解》 在软件开发领域,汇编语言是底层编程的重要工具,而gas(GNU Assembler)是GNU项目中的汇编器,用于将汇编语言代码转换成机器可执行的二进制代码。gas汇编器提供了丰富的命令集,使得程序员...
GNU GAS(GNU Assembler)是GNU Binutils工具集的一部分,它是用于编写机器语言程序的开源汇编器。GNU Binutils是一系列二进制工具的集合,包括汇编器、链接器、目标文件处理工具等,广泛应用于Linux和其他类UNIX...
《x86 Assembly Language Reference Manual》written by Sun Microsystems, Inc 英文的手册有兴趣就话下来看看!
【标题】:“面向VLIW处理器的GAS汇编器实现.pdf” 【描述】:这篇文档探讨了如何基于GAS(GNU Assembler)为VLIW(Very Long Instruction Word)处理器设计和实现汇编器。VLIW架构的DSP处理器提高了指令级并行度,...
GNU汇编器(gas)是一个广泛使用的汇编语言编译器,它是GNU项目的一部分,专门用于将汇编指令转换为机器码。GNU汇编器的权威手册详细介绍了该工具的使用方法、语法规则、伪指令以及命令行选项。 在Linux环境下,...
### Linux下的汇编——GAS和NASM的区别 在深入探讨GAS(GNU Assembler)与NASM(Netwide Assembler)之间的区别之前,我们首先需要了解这两种汇编器的基本概念及其在Linux环境下的应用。 #### 一、GAS与NASM简介 ...
《ARM标准汇编与GNU汇编》是一本深入解析ARM架构下汇编语言编程的教程,特别关注在GNU工具链环境中的应用。ARM汇编是针对ARM架构处理器的低级编程语言,而GNU汇编则是GNU开发工具集的一部分,用于在多种架构上编写...
与NASM不同,Gas主要使用AT&T汇编语法,这是Unix系统中常见的汇编语言风格。 Gas手册是学习和理解AT&T汇编语法的关键资源。它涵盖了基本的汇编指令、符号解析、宏定义、伪指令、段管理以及如何与链接器交互。通过...
转换gcc gas 汇编代码为 ios的gcc编译器 可以直接编译通过的 汇编代码 perl脚本
AT&T汇编语言是GCC(GNU Compiler Collection)和GAS(GNU Assembler)在Linux环境下进行底层编程时常用的汇编语法。与Intel汇编不同,AT&T汇编语法的结构和表达方式有其独特之处,这使得它在某些场景下更适合作为...
GNU汇编程序GAS用于将汇编语言源文件转换为目标文件,并可以通过LD命令进行连接。例如,汇编和连接的命令分别是"gcc -o executable sourcecode.S"。使用GCC时,S大写可以确保gcc能够识别汇编程序中的C预处理命令,如...
GNU汇编程序(GAS)是GNU项目中的一个重要组成部分,用于将汇编语言源文件转换成目标文件。GAS支持多种处理器架构,并且能够很好地与GCC集成。GAS的基本使用方法如下: ```sh as sourcecode.s -o objfile.o ``` ...
"AT&T汇编语言格式uploadby_weball.pdf"这个文档很可能包含了AT&T汇编的详细语法、指令集以及如何使用GAS(GNU Assembler)进行汇编的过程。学习这部分内容可以帮助读者理解和编写AT&T汇编代码,这对于深入理解Linux...
GCC 编译器可以对生成的二进制代码进行优化,而 gas 汇编器可以将汇编代码转换为目标代码,ld 链接器可以将目标代码链接成可执行文件。 在 Linux 平台上编写汇编代码需要注意以下几点: 1. 了解 Linux 的汇编语言...
3. **GAS(GNU Assembler)**:作为GNU工具链的一部分,GAS支持多种处理器架构,并且与GCC编译器配合良好。 4. **TASM(Turbo Assembler)**:由Borland公司开发,主要用于DOS环境下的x86编程。 5. **Visual ...
常见的汇编语言编译工具有NASM(Netwide Assembler)、MASM(Microsoft Macro Assembler)、GAS(GNU Assembler)等。这些工具不仅支持标准的汇编语法,还提供了丰富的扩展功能,如宏定义、条件编译等,以满足不同...