现在,我们来看看在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
破坏描述:此处没有