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