Day5

Show text and initialize GDT/IDT

Using struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct BOOTINFO {
char cyls , leds , vmode , reserve ;
short scrnx , scrny ;
char * vram ;
};

void HariMain(){
char * vram ;
int xsize , ysize ;
struct BOOTINFO * binfo ;

init_palette();
binfo = (struct BOOTINFO *) (0x0ff0) ; // we store boot info at this address in boot stage using assembly and BIOS
xsize = binfo->scrnx ;
ysize = binfo->scrny ;
vram = binfo->vram ;

init_screen(xsize , ysize , vram);
}

Show text

In vga video mode, we use bitmap

bit map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
unsigned char fontA [16] ={
0b00000000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b00100100,
0b00100100,
0b00100100,
0b00100100,
0b01111110,
0b01000010,
0b01000010,
0b01000010,
0b11100111,
0b00000000,
0b00000000
} ;

void putfont8(char * vram , int xsize, int x , int y , char c , char * font ){
//c stand for color
int i ;
char * p , d ;
//16 bytes per character
for (i = 0 ; i < 16 ; i++){
p = vram + (y+i)*xsize + x ; //2D to 1D, (x,y) indicate the position
d = font[i] ; //get the byte

//check the byte bit by bit, if non-zero, fill it
if ((d & 0x80) != 0) { p[0] = c; }
if ((d & 0x40) != 0) { p[1] = c; }
if ((d & 0x20) != 0) { p[2] = c; }
if ((d & 0x10) != 0) { p[3] = c; }
if ((d & 0x08) != 0) { p[4] = c; }
if ((d & 0x04) != 0) { p[5] = c; }
if ((d & 0x02) != 0) { p[6] = c; }
if ((d & 0x01) != 0) { p[7] = c; }
}
}

A

It will be troublesome for us to make the font one by one, so we use a external font family and link it with out OS

hankaku.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# part of hankaku.txt

char 0x49
........
.*****..
...*....
...*....
...*....
...*....
...*....
...*....
...*....
...*....
...*....
...*....
...*....
.*****..
........
........

char 0x4a
........
...*****
.....*..
.....*..
.....*..
.....*..
.....*..
.....*..
.....*..
.....*..
.....*..
.....*..
*....*..
.*..*...
..**....
........
1
% makefont.exe hankaku.txt font.bin

Each character is represented by 16 bytes

fonts

Turn binary data to an linkable object file format

1
% bin2obj.exe font.bin font.obj hankaku

Usage

1
2
3
extern char hankaku [4096];

putfont8(binfo->vram , binfo->scrnx , 8 ,8 , COL8_FFFFFF , hankaku + 'A' * 16);

Show string

1
2
3
4
5
6
void putfonts8_asc(char * vram , int xsize , int x , int y , char c , unsigned char * s){
extern char hankaku [4096];
for (; *s != '\0' ; s++){
putfont8(vram ,xsize , x, y, c , hankaku+ (*s) * 16);
}
}

Use sprintf for debug purpose

As declared by the auther, this version is sprintf is OS independent

Show cursor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void init_mouse_cursor8(char *mouse, char bc)
/* prepare cursor buffer(16×16), write into mouse */
{
static char cursor[16][16] = {
"**************..",
"*OOOOOOOOOOO*...",
"*OOOOOOOOOO*....",
"*OOOOOOOOO*.....",
"*OOOOOOOO*......",
"*OOOOOOO*.......",
"*OOOOOOO*.......",
"*OOOOOOOO*......",
"*OOOO**OOO*.....",
"*OOO*..*OOO*....",
"*OO*....*OOO*...",
"*O*......*OOO*..",
"**........*OOO*.",
"*..........*OOO*",
"............*OO*",
".............***"
};
int x, y;
for (y = 0; y < 16; y++) {
for (x = 0; x < 16; x++) {
if (cursor[y][x] == '*') {
mouse[y * 16 + x] = COL8_000000;
}
if (cursor[y][x] == 'O') {
mouse[y * 16 + x] = COL8_FFFFFF;
}
if (cursor[y][x] == '.') {
mouse[y * 16 + x] = bc;
}
}
}
return;
}
//bc stand for background color

void putblock8_8(char * vram , int vxsize , int pxsize , int pysize , int px0,int py0, char * buf , int bxszie ){
// vram : video memory
// vxsize : screen x resolution
// pxsize : picture x size
// pysize : picture y size
// (px0 , py0) : position
// buf : picture data
// bxsize : picture data x size, usually equals to pxsize
int x , y ;
for ( y = 0 ; y < pysize ; y++){
for (x = 0 ; x < pxsize ; x++){
vram[(py0+y) * vxszie + (px0+x)] = buf[y * bxsize + x];
}
}
}

GDT and IDT

Segments management

How to describe a segment ?

64 bits segment discriptor

  • Segment size
  • Start address of the segment
  • Accessibility of the segment

However, the segment base register is still 16 bit, we need a external data structure to store these infomation

The value in DS(segment register) is called segment selector, funtions like the index of an array(GDT)

For some historical reason, the lowest 3 bits of DS is unusable, so the size of GDT is (2**13) * 64 bits

GDT(Global segment Desciptor Table)

  • Stored in memory
  • The start address of GDT and size limitation is stored in GDTR (global discriptor table register)

IDT(Interrupt Descriptor Table)

  • Same with “interrupt vector table” in real mode
  • Stored in memory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//GDT entry
struct SEGMENT_DESCRIPTOR{
short limit_low;
short base_low ;
char base_mid ;
char access_right ;
char limit_high ;
char base_high ;

//segment base addr = catenate(base_high , base_mid , base_low)
//why divide 4 bytes address into 3 parts?
//For back compatibility of 80286

//segment size limit = catenate(limit_high & 0xffff , limit_low)
//why size limit is only 20 bits ?
//12 bits for access infomation
//So the biggest size is 1 MB ?
//If G_bit in accessInfo is turned on, the limitSize will be interpreted as number of pages
// max_seg_size = 1MB * 4KB(page size) = 4G

//segment access right
// limit_high & 0xffff0000 is called extended access right bits, used as xx00
// the first bit is G_bit
// the second bit is used to indicate protected mode or real mode(only for 80286, not for BIOS)
// access_right :
// 0x00 : not allocated
// 0x92 : OS only, rw_
// 0x9a : OS only, r_x
// 0xf2 : User seg , rw_
// 0xfa : User seg , r_x
// OS only (ring0) , User (ring3)
};

//IDT entry
struct GATE_DESCRIPTOR{
short offset_low ;
short selector ;
char dw_count ;
char access_right ;
short offset_high ;
};

//the struct format is required by CPU manufacturer, please read the manual

void init_gdtidt(void){
//arbitrarily pick two free address region in memory for GDT and IDT
struct SEGMENT_DESCRIPTOR * gdt = (struct SEGMENT_DESCRIPTOR*) 0x00270000 ;
struct GATE_DESCRIPTOR * idt = (struct GATE_DESCRIPTOR) 0x0026f800 ;

int i ;

//init GDT
for (i = 0 ; i < 8192 ; i++){
set_segmdesc(gdt + i , 0 , 0 , 0);
}
//set entry 1 and 2
set_segmdesc(gdt + 1 ,0xffffffff,0x00000000,0x4092);
//CPU access segment ,4G
set_segmdesc(gdt + 2 ,0x0007ffff,0x00280000,0x409a);
// bootpack access segment, 512 bytes

//load GDT address into GDTR
load_gdtr(0xffff,0x00270000);

//init IDT
for (i = 0 ; i < 256 ; i++){
set_gatedesc(idt + i , 0 , 0, 0);
}
load_idtr(0x7ff , 0x0026f800) ;
return ;
}

/*
limit : size upper bound
base : base address
ar : access right
*/

void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
if (limit > 0xfffff) {
ar |= 0x8000; /* G_bit = 1 */
limit /= 0x1000;
}
sd->limit_low = limit & 0xffff;
sd->base_low = base & 0xffff;
sd->base_mid = (base >> 16) & 0xff;
sd->access_right = ar & 0xff;
sd->limit_high = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);
sd->base_high = (base >> 24) & 0xff;
return;
}

void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{
gd->offset_low = offset & 0xffff;
gd->selector = selector;
gd->dw_count = (ar >> 8) & 0xff;
gd->access_right = ar & 0xff;
gd->offset_high = (offset >> 16) & 0xffff;
return;
}