第8天 鼠标控制与32位模式切换
移动鼠标指针
harib05d
准备材料(windows环境)
- VMware Workstation
- Visual Studio Code
- 程序和源代码:https://pan.baidu.com/s/1bFGAYgnA0m9KB7tzFrMD5g
提取码:d210 - Makefile
- windows 批处理
开始实验
-
在tolset文件夹下创建子文件侠harib05d。
-
打开记事本,输入以下代码并另存为 !cons_nt.bat文件(windows 批处理),存放在harib05d文件夹中。
cmd.exe
ps:当然也可以直接使用 win + r 快捷键也可以,不过最好还是创建一个比较好。因为以后打开时不需要再麻烦了。
3. 打开vc code,输入以下代码并保存为 naskfunc.nas,存放在harib05d文件夹中。
; naskfunc
; TAB=4
[FORMAT "WCOFF"] ; 制作目标文件的模式
[INSTRSET "i486p"] ; 要使用486指令的语句
[BITS 32] ; 制作32位模式用的机器语言
[FILE "naskfunc.nas"] ; 原文件名信息
GLOBAL _io_hlt, _io_cli, _io_sti, _io_stihlt
GLOBAL _io_in8, _io_in16, _io_in32
GLOBAL _io_out8, _io_out16, _io_out32
GLOBAL _io_load_eflags, _io_store_eflags
GLOBAL _load_gdtr, _load_idtr
GLOBAL _asm_inthandler21, _asm_inthandler27, _asm_inthandler2c
EXTERN _inthandler21, _inthandler27, _inthandler2c
[SECTION .text]
_io_hlt: ; void io_hlt(void);
HLT
RET
_io_cli: ; void io_cli(void);
CLI
RET
_io_sti: ; void io_sti(void);
STI
RET
_io_stihlt: ; void io_stihlt(void);
STI
HLT
RET
_io_in8: ; int io_in8(int port);
MOV EDX,[ESP+4] ; port
MOV EAX,0
IN AL,DX
RET
_io_in16: ; int io_in16(int port);
MOV EDX,[ESP+4] ; port
MOV EAX,0
IN AX,DX
RET
_io_in32: ; int io_in32(int port);
MOV EDX,[ESP+4] ; port
IN EAX,DX
RET
_io_out8: ; void io_out8(int port, int data);
MOV EDX,[ESP+4] ; port
MOV AL,[ESP+8] ; data
OUT DX,AL
RET
_io_out16: ; void io_out16(int port, int data);
MOV EDX,[ESP+4] ; port
MOV EAX,[ESP+8] ; data
OUT DX,AX
RET
_io_out32: ; void io_out32(int port, int data);
MOV EDX,[ESP+4] ; port
MOV EAX,[ESP+8] ; data
OUT DX,EAX
RET
_io_load_eflags: ; int io_load_eflags(void);
PUSHFD ; 指 PUSH EFLAGS
POP EAX
RET
_io_store_eflags: ; void io_store_eflags(int eflags);
MOV EAX,[ESP+4]
PUSH EAX
POPFD ; 指 POP EFLAGS
RET
_load_gdtr: ; void load_gdtr(int limit, int addr);
MOV AX,[ESP+4] ; limit
MOV [ESP+6],AX
LGDT [ESP+6]
RET
_load_idtr: ; void load_idtr(int limit, int addr);
MOV AX,[ESP+4] ; limit
MOV [ESP+6],AX
LIDT [ESP+6]
RET
_asm_inthandler21:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler21
POP EAX
POPAD
POP DS
POP ES
IRETD
_asm_inthandler27:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler27
POP EAX
POPAD
POP DS
POP ES
IRETD
_asm_inthandler2c:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler2c
POP EAX
POPAD
POP DS
POP ES
IRETD
- 打开vc code,输入以下代码并保存为 int.c 存放在harib05d文件夹中。
/* 与中断有关 */
#include "bootpack.h"
#include <stdio.h>
void init_pic(void)
/* PIC的初始化 */
{
io_out8(PIC0_IMR, 0xff ); /* 禁止所有中断 */
io_out8(PIC1_IMR, 0xff ); /* 禁止所有中断 */
io_out8(PIC0_ICW1, 0x11 ); /* 边沿触发模式 (edge trigger mode) */
io_out8(PIC0_ICW2, 0x20 ); /* IRQ0-7 由 INT20-27 接收 */
io_out8(PIC0_ICW3, 1 << 2); /* PIC1 由 IRQ2 连接 */
io_out8(PIC0_ICW4, 0x01 ); /* 无缓冲区模式 */
io_out8(PIC1_ICW1, 0x11 ); /* 边沿触发模式 (edge trigger mode) */
io_out8(PIC1_ICW2, 0x28 ); /* IRQ8-15 由 INT28-2f 接收 */
io_out8(PIC1_ICW3, 2 ); /* PIC1 由 IRQ2 连接 */
io_out8(PIC1_ICW4, 0x01 ); /* 无缓冲区域 */
io_out8(PIC0_IMR, 0xfb ); /* 11111011 PIC1 以外全部禁止 */
io_out8(PIC1_IMR, 0xff ); /* 11111111 禁止所有中断 */
return;
}
#define PORT_KEYDAT 0x0060
struct FIFO8 keyfifo;
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* 通知PIC“IRQ-01”已经受理完毕 */
data = io_in8(PORT_KEYDAT);
fifo8_put(&keyfifo, data);
return;
}
struct FIFO8 mousefifo;
void inthandler2c(int *esp)
/* 来自PS/2键盘的中断 */
{
unsigned char data;
io_out8(PIC1_OCW2, 0x64); /* 通知PIC1 IRQ-12的受理已经完成 */
io_out8(PIC0_OCW2, 0x62); /* 通知PIC0 IRQ-02的受理已经完成 */
data = io_in8(PORT_KEYDAT);
fifo8_put(&mousefifo, data);
return;
}
void inthandler27(int *esp)
/* 防止来自PIC0的不完整中断的措施 */
/* 在Athlon64X2机器等中,由于芯片组的便利性,该中断在PIC初始化时仅发生一次 */
/* 此中断处理功能不执行中断 */
/* 您为什么不做任何事情? */
/* 由于此中断是由PIC初始化期间的电气噪声产生的,*/
/* 您不需要认真做任何事情。 */
{
io_out8(PIC0_OCW2, 0x67); /* 通知PIC IRQ-07已被接受(见7-1)*/
return;
}
- 打开vc code,输入以下代码并保存为 bootpack.c 存放在harib05d文件夹中。
/* bootpack 其他处理 */
#include "bootpack.h"
#include <stdio.h>
struct MOUSE_DEC {
unsigned char buf[3], phase;
int x, y, btn;
};
extern struct FIFO8 keyfifo, mousefifo;
void enable_mouse(struct MOUSE_DEC *mdec);
void init_keyboard(void);
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat);
void HariMain(void)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
char s[40], mcursor[256], keybuf[32], mousebuf[128];
int mx, my, i;
struct MOUSE_DEC mdec;
init_gdtidt();
init_pic();
io_sti(); /* 由于IDT / PIC的初始化已完成,因此请禁用CPU的中断。 */
fifo8_init(&keyfifo, 32, keybuf);
fifo8_init(&mousefifo, 128, mousebuf);
io_out8(PIC0_IMR, 0xf9); /* 允许PIC1和键盘(11111001) */
io_out8(PIC1_IMR, 0xef); /* 允许鼠标(11101111) */
init_keyboard();
init_palette();
init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);
mx = (binfo->scrnx - 16) / 2; /* 计算坐标在屏幕中心 */
my = (binfo->scrny - 28 - 16) / 2;
init_mouse_cursor8(mcursor, COL8_008484);
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
sprintf(s, "(%3d, %3d)", mx, my);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
enable_mouse(&mdec);
for (;;) {
io_cli();
if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
io_stihlt();
} else {
if (fifo8_status(&keyfifo) != 0) {
i = fifo8_get(&keyfifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
} else if (fifo8_status(&mousefifo) != 0) {
i = fifo8_get(&mousefifo);
io_sti();
if (mouse_decode(&mdec, i) != 0) {
/* 3字节都凑齐了,所以把它们显示出来 */
sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
if ((mdec.btn & 0x01) != 0) {
s[1] = 'L';
}
if ((mdec.btn & 0x02) != 0) {
s[3] = 'R';
}
if ((mdec.btn & 0x04) != 0) {
s[2] = 'C';
}
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
/* 鼠标指针的移动 */
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, mx, my, mx + 15, my + 15); /* 隐藏鼠标 */
mx += mdec.x;
my += mdec.y;
if (mx < 0) {
mx = 0;
}
if (my < 0) {
my = 0;
}
if (mx > binfo->scrnx - 16) {
mx = binfo->scrnx - 16;
}
if (my > binfo->scrny - 16) {
my = binfo->scrny - 16;
}
sprintf(s, "(%3d, %3d)", mx, my);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15); /* 鼠标坐标 */
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); /* 显示坐标 */
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16); /* 描画鼠标 */
}
}
}
}
}
#define PORT_KEYDAT 0x0060
#define PORT_KEYSTA 0x0064
#define PORT_KEYCMD 0x0064
#define KEYSTA_SEND_NOTREADY 0x02
#define KEYCMD_WRITE_MODE 0x60
#define KBC_MODE 0x47
void wait_KBC_sendready(void)
{
/* 等待键盘控制电路准备完毕 */
for (;;) {
if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) {
break;
}
}
return;
}
void init_keyboard(void)
{
/* 初始化键盘控制电路 */
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, KBC_MODE);
return;
}
#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4
void enable_mouse(struct MOUSE_DEC *mdec)
{
/* 激活鼠标 */
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
/* 顺利的话,ACK(0xfa)会被传送过来 */
mdec->phase = 0; /* 等待0xfa的阶段 */
return;
}
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{
if (mdec->phase == 0) {
/* 等待鼠标的0xfa的阶段 */
if (dat == 0xfa) {
mdec->phase = 1;
}
return 0;
}
if (mdec->phase == 1) {
/* 等待鼠标第一字节的阶段 */
if ((dat & 0xc8) == 0x08) {
/* 如果第一字节正确 */
mdec->buf[0] = dat;
mdec->phase = 2;
}
return 0;
}
if (mdec->phase == 2) {
/* 等待鼠标第二字节的阶段 */
mdec->buf[1] = dat;
mdec->phase = 3;
return 0;
}
if (mdec->phase == 3) {
/* 等待鼠标第三字节的阶段 */
mdec->buf[2] = dat;
mdec->phase = 1;
mdec->btn = mdec->buf[0] & 0x07;
mdec->x = mdec->buf[1];
mdec->y = mdec->buf[2];
if ((mdec->buf[0] & 0x10) != 0) {
mdec->x |= 0xffffff00;
}
if ((mdec->buf[0] & 0x20) != 0) {
mdec->y |= 0xffffff00;
}
mdec->y = - mdec->y; /* 鼠标y方向与画面符号相反 */
return 1;
}
return -1; /* 应该不可能到这里来 */
}
- 打开vc code,输入以下代码并保存为 bootpack.c 存放在harib05d文件夹中。
//* bootpack 其他处理 */
#include "bootpack.h"
#include <stdio.h>
extern struct FIFO8 keyfifo;
void HariMain(void)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
char s[40], mcursor[256], keybuf[32];
int mx, my, i;
init_gdtidt();
init_pic();
io_sti(); /* 由于IDT / PIC的初始化已完成,因此请禁用CPU的中断。 */
fifo8_init(&keyfifo, 32, keybuf);
io_out8(PIC0_IMR, 0xf9); /* 允许PIC1和键盘(11111001) */
io_out8(PIC1_IMR, 0xef); /* 允许鼠标(11101111) */
init_palette();
init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);
mx = (binfo->scrnx - 16) / 2; /* 计算坐标在屏幕中心 */
my = (binfo->scrny - 28 - 16) / 2;
init_mouse_cursor8(mcursor, COL8_008484);
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
sprintf(s, "(%d, %d)", mx, my);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
for (;;) {
io_cli();
if (fifo8_status(&keyfifo) == 0) {
io_stihlt();
} else {
i = fifo8_get(&keyfifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}
}
ps: 。
7. 打vc code,输入以下代码并保存为 bootpack.h,存放在harib05d文件夹中。
/* asmhead.nas */
struct BOOTINFO { /* 0xff0-0x0fff */
char cyls; /* 启动区读硬盘读到何处为止 */
char leds; /* 启动时键盘LED的状态 */
char vmode; /* 显卡模式为多少位彩色 */
char reserve;
short scrnx, scrny; /* 画面分辨率 */
char *vram;
};
#define ADR_BOOTINFO 0x00000ff0
/* naskfunc.nas */
void io_hlt(void);
void io_cli(void);
void io_sti(void);
void io_stihlt(void);
int io_in8(int port);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);
void asm_inthandler21(void);
void asm_inthandler27(void);
void asm_inthandler2c(void);
/* fifo.c */
struct FIFO8 {
unsigned char *buf;
int p, q, size, free, flags;
};
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf);
int fifo8_put(struct FIFO8 *fifo, unsigned char data);
int fifo8_get(struct FIFO8 *fifo);
int fifo8_status(struct FIFO8 *fifo);
/* graphic.c */
void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen8(char *vram, int x, int y);
void putfont8(char *vram, int xsize, int x, int y, char c, char *font);
void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s);
void init_mouse_cursor8(char *mouse, char bc);
void putblock8_8(char *vram, int vxsize, int pxsize,
int pysize, int px0, int py0, char *buf, int bxsize);
#define COL8_000000 0
#define COL8_FF0000 1
#define COL8_00FF00 2
#define COL8_FFFF00 3
#define COL8_0000FF 4
#define COL8_FF00FF 5
#define COL8_00FFFF 6
#define COL8_FFFFFF 7
#define COL8_C6C6C6 8
#define COL8_840000 9
#define COL8_008400 10
#define COL8_848400 11
#define COL8_000084 12
#define COL8_840084 13
#define COL8_008484 14
#define COL8_848484 15
/* dsctbl.c */
struct SEGMENT_DESCRIPTOR {
short limit_low, base_low;
char base_mid, access_right;
char limit_high, base_high;
};
struct GATE_DESCRIPTOR {
short offset_low, selector;
char dw_count, access_right;
short offset_high;
};
void init_gdtidt(void);
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar);
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar);
#define ADR_IDT 0x0026f800
#define LIMIT_IDT 0x000007ff
#define ADR_GDT 0x00270000
#define LIMIT_GDT 0x0000ffff
#define ADR_BOTPAK 0x00280000
#define LIMIT_BOTPAK 0x0007ffff
#define AR_DATA32_RW 0x4092
#define AR_CODE32_ER 0x409a
#define AR_INTGATE32 0x008e
/* int.c */
void init_pic(void);
void inthandler21(int *esp);
void inthandler27(int *esp);
void inthandler2c(int *esp);
#define PIC0_ICW1 0x0020
#define PIC0_OCW2 0x0020
#define PIC0_IMR 0x0021
#define PIC0_ICW2 0x0021
#define PIC0_ICW3 0x0021
#define PIC0_ICW4 0x0021
#define PIC1_ICW1 0x00a0
#define PIC1_OCW2 0x00a0
#define PIC1_IMR 0x00a1
#define PIC1_ICW2 0x00a1
#define PIC1_ICW3 0x00a1
#define PIC1_ICW4 0x00a1
- 打vc code,输入以下代码并保存为 graphic.c,存放在harib05d文件夹中。
/* u关于描画的处理 */
#include "bootpack.h"
void init_palette(void)
{
static unsigned char table_rgb[16 * 3] = {
0x00, 0x00, 0x00, /* 0 : 黑 */
0xff, 0x00, 0x00, /* 1 : 亮红 */
0x00, 0xff, 0x00, /* 2 : 亮绿 */
0xff, 0xff, 0x00, /* 3 : 亮黄 */
0x00, 0x00, 0xff, /* 4 :亮蓝 */
0xff, 0x00, 0xff, /* 5 : 亮紫 */
0x00, 0x00, 0xff, /* 6 : 浅亮蓝 */
0xff, 0xff, 0xff, /* 7 :白 */
0xc6, 0xc6, 0xc6, /* 8 : 亮灰 */
0x84, 0x00, 0x00, /* 9 : 暗红 */
0x00, 0x84, 0x00, /* 10 : 暗绿 */
0x84, 0x84, 0x00, /* 11 : 暗黄 */
0x00, 0x00, 0x84, /* 12 : 暗青 */
0x84, 0x00, 0x84, /* 13 : 暗紫 */
0x00, 0x84, 0x84, /* 14 : 浅暗蓝 */
0x84, 0x84, 0x84 /* 15 : 暗灰 */
};
set_palette(0, 15, table_rgb);
return;
/* C语言中的static char语句只能用于数据, 相当于汇编中的DB指令 */
}
void set_palette(int start, int end, unsigned char *rgb)
{
int i, eflags;
eflags = io_load_eflags(); /* 记录中断许可标志的值 */
io_cli(); /* 将中断许可标志置为0, 禁止中断 */
io_out8(0x03c8, start);
for (i = start; i <= end; i++) {
io_out8(0x03c9, rgb[0] / 4);
io_out8(0x03c9, rgb[1] / 4);
io_out8(0x03c9, rgb[2] / 4);
rgb += 3;
}
io_store_eflags(eflags); /* 复原中断许可标志 */
return;
}
void boxfill8(unsigned char *vram, int xsize, unsigned char c, 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] = c;
}
return;
}
void init_screen8(char *vram, int x, int y)
{
boxfill8(vram, x, COL8_008484, 0, 0, x - 1, y - 29);
boxfill8(vram, x, COL8_C6C6C6, 0, y - 28, x - 1, y - 28);
boxfill8(vram, x, COL8_FFFFFF, 0, y - 27, x - 1, y - 27);
boxfill8(vram, x, COL8_C6C6C6, 0, y - 26, x - 1, y - 1);
boxfill8(vram, x, COL8_FFFFFF, 3, y - 24, 59, y - 24);
boxfill8(vram, x, COL8_FFFFFF, 2, y - 24, 2, y - 4);
boxfill8(vram, x, COL8_848484, 3, y - 4, 59, y - 4);
boxfill8(vram, x, COL8_848484, 59, y - 23, 59, y - 5);
boxfill8(vram, x, COL8_000000, 2, y - 3, 59, y - 3);
boxfill8(vram, x, COL8_000000, 60, y - 24, 60, y - 3);
boxfill8(vram, x, COL8_848484, x - 47, y - 24, x - 4, y - 24);
boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y - 4);
boxfill8(vram, x, COL8_FFFFFF, x - 47, y - 3, x - 4, y - 3);
boxfill8(vram, x, COL8_FFFFFF, x - 3, y - 24, x - 3, y - 3);
return;
}
void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
int i;
char *p, d; /* date */
for (i = 0; i < 16; i++) {
p = vram + (y + i) * xsize + x;
d = font[i];
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; }
}
return;
}
void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
extern char hankaku[4096];
for (; *s != 0x00; s++) {
putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
x += 8;
}
return;
}
void init_mouse_cursor8(char *mouse, char bc)
/* 准备鼠标指标 (16 x 16) */
{
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;
}
void putblock8_8(char *vram, int vxsize, int pxsize,
int pysize, int px0, int py0, char *buf, int bxsize)
{
int x, y;
for (y = 0; y < pysize; y++) {
for (x = 0; x < pxsize; x++) {
vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x];
}
}
return;
}
- 打vc code,输入以下代码并保存为 asmhead.nas,存放在harib05d文件夹中。
; haribote-os boot asm
; TAB=4
BOTPAK EQU 0x00280000 ; bootpack装载处
DSKCAC EQU 0x00100000 ; 磁盘缓存的地方
DSKCAC0 EQU 0x00008000 ; 磁盘高速缓存的场所(实时模式)
; 有关BOOT_INFO
CYLS EQU 0x0ff0 ; 设定启动区
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 关于颜色数目的信息,颜色的位数
SCRNX EQU 0x0ff4 ; 分辨率的X(screen x)
SCRNY EQU 0x0ff6 ; 分辨率的Y(screen y)
VRAM EQU 0x0ff8 ; 图像缓冲区的开始地址
ORG 0xc200 ; 这个程序要被装载到内存的什么地方呢?
; 画面模式设定
MOV AL,0x13 ; VGA显卡,320x200x8位彩色
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ; 记录画面模式(C语言参照)
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
; 用BIOS取得键盘各种LED指示灯的状态
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
; 使PIC不授受一切中断
; 如果要初始PIC的话,要在AT兼容的规范中,
; 如果不把这家伙放在CLI面前,我偶尔会举起来
; 稍后进行PIC的初始化
MOV AL,0xff
OUT 0x21,AL
NOP ; 如果连续OUT命令的话,可能会有不太好的机型
OUT 0xa1,AL
CLI ; 甚至禁止CPU层面插队
; cpu从1 mb以上的内存,a20gate设定
CALL waitkbdout
MOV AL,0xd1
OUT 0x64,AL
CALL waitkbdout
MOV AL,0xdf ; enable A20
OUT 0x60,AL
CALL waitkbdout
; 保护模式过渡
[INSTRSET "i486p"] ; 用于记述想要使用的486命令
LGDT [GDTR0] ; 暂定GDT设定
MOV EAX,CR0
AND EAX,0x7fffffff ; 使bit31为0(为子禁止寻乎)
OR EAX,0x00000001 ; 使bit0为1(为子保护模式转移)
MOV CR0,EAX
JMP pipelineflush
pipelineflush:
MOV AX,1*8 ; 可读区段32bit
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV SS,AX
; bootpack的传送
MOV ESI,bootpack ; 传输源
MOV EDI,BOTPAK ; 传输目的地
MOV ECX,512*1024/4
CALL memcpy
; 顺便磁盘数据也向原来的位置传送
; 首先从引导扇区
MOV ESI,0x7c00 ; 传输源
MOV EDI,DSKCAC ; 传输目的地
MOV ECX,512/4
CALL memcpy
; 剩下的全部
MOV ESI,DSKCAC0+512 ; 传输源
MOV EDI,DSKCAC+512 ; 传输目的地
MOV ECX,0
MOV CL,BYTE [CYLS]
IMUL ECX,512*18*2/4 ; 从柱面数转换成字节数 /4
SUB ECX,512/4 ; 通过IPL减去
CALL memcpy
; 我们已经完成了需要使用asmhead 进行的所有操作
; 放到bootpack中
; 启动bootpack
MOV EBX,BOTPAK
MOV ECX,[EBX+16]
ADD ECX,3 ; ECX += 3;
SHR ECX,2 ; ECX /= 4;
JZ skip ; 无需转移
MOV ESI,[EBX+20] ; 传输源
ADD ESI,EBX
MOV EDI,[EBX+12] ; 传输目的地
CALL memcpy
skip:
MOV ESP,[EBX+12] ; 堆栈初始值
JMP DWORD 2*8:0x0000001b
waitkbdout:
IN AL,0x64
AND AL,0x02
JNZ waitkbdout ; 如果AND的结果不为0,请跳转至waitkbdou
RET
memcpy:
MOV EAX,[ESI]
ADD ESI,4
MOV [EDI],EAX
ADD EDI,4
SUB ECX,1
JNZ memcpy ; 如果减法不为0,则返回memcpy
RET
; memcpy也可以用字符串指令编写,除非你忘记了地址大小写前缀
ALIGNB 16
GDT0:
RESB 8 ; 空选择器
DW 0xffff,0x0000,0x9200,0x00cf ; 读/定段32bit
DW 0xffff,0x0000,0x9a28,0x0047 ; 可执行段32bit(用于bootpack)
DW 0
GDTR0:
DW 8*3-1
DD GDT0
ALIGNB 16
bootpack:
- 打开 VC code 创建 ipl10.nas 文件,输入以下代码,也存放的harib05d中。
; haribote-ipl
; TAB=4
CYLS EQU 10 ; 要读取到什么程度
ORG 0x7c00 ; 启动装载程序
; 以下记述用于标准FAT12格式软盘
JMP entry
DB 0x90
DB "HARIBOTE" ; 磁盘名称(可以是任意字符串)
DW 512 ; 每个扇区的大小(必须是512)
DB 1 ; 簇的大小(必须为一个扇区)
DW 1 ; FAT12的起始位置(一般从第一个扇区开始
DB 2 ; FAT的个数(必须为2)
DW 224 ; 根目录的大小(一般设成224项)
DW 2880 ; 该磁盘的大小(必须是2880扇区)
DB 0xf0 ; 该磁盘的种类(必须是0xf0
DW 9 ; FAt的长度(必须是9扇区)
DW 18 ; 一个磁道有几个扇区(必须是18)
DW 2 ; 磁头数(必须是2)
DD 0 ; 不使用分区,必须是0
DD 2880 ; 磁盘大小
DB 0,0,0x29 ; 意义不明固定
DD 0xffffffff ; (可能是)卷标号码
DB "HARIBOTEOS " ; 磁盘的名称(11字节)
DB "FAT12 " ; 磁盘格式名称(8字节)
RESB 18 ; 先空出18字节
; 程序主体
entry:
MOV AX,0 ; 初始化寄存器
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
; 读磁盘
MOV AX,0x0820
MOV ES,AX
MOV CH,0 ; 柱面 0
MOV DH,0 ; 磁头 0
MOV CL,2 ; 扇区 2
readloop:
MOV SI,0 ; 记录失败次数的寄存器
retry:
MOV AH,0x02 ; AH=0x02 : 读入磁盘
MOV AL,1 ; 1 个扇区
MOV BX,0
MOV DL,0x00 ; A 驱动器
INT 0x13 ; 调用磁盘BIOS
JNC next ; 没出错时跳转到next
ADD SI,1 ; SI 加 1
CMP SI,5 ; 比较 SI 与 5
JAE error ; SI >= 5 时,跳转到error
MOV AH,0x00
MOV DL,0x00 ; A 驱动器
INT 0x13 ; 重置驱动器
JMP retry
next:
MOV AX,ES ; 把内存地址后移0x200
ADD AX,0x0020
MOV ES,AX ; 因为没有 ADD ES,0x020 指令,所以这里稍微绕个弯
ADD CL,1 ; CL 加 1
CMP CL,18 ; 比较 CL 与 18
JBE readloop ; 如果 CL <= 18,则跳转至readloop
MOV CL,1
ADD DH,1
CMP DH,2
JB readloop ; 如果 DH < 2, 则跳转到readloop
MOV DH,0
ADD CH,1
CMP CH,CYLS
JB readloop ; 如果 CH < CYLS,则跳转至readloop
; 因为看完了实行haribote.sys
MOV [0x0ff0],CH ; IPL读到什么地方结束
JMP 0xc200
error:
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ; SI 加 1
CMP AL,0
JE fin
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用显卡BIOS
JMP putloop
fin:
HLT ; 让CPu停止,等待指令
JMP fin ; 无限循环
msg:
DB 0x0a, 0x0a ; 换行两次
DB "load error"
DB 0x0a ; 换行
DB 0
RESB 0x7dfe-$ ; 重复0x00一直到0x7dfe
DB 0x55, 0xaa
- 打开记事本,输入以下代码,另存为 make.bat, 同样放到harib05d中。
..\z_tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
- 创建 Makefile,并输入以下代码并存放到harib05d中。
OBJS_BOOTPACK = bootpack.obj naskfunc.obj hankaku.obj graphic.obj dsctbl.obj \
int.obj fifo.obj
TOOLPATH = ../z_tools/
INCPATH = ../z_tools/haribote/
MAKE = $(TOOLPATH)make.exe -r
NASK = $(TOOLPATH)nask.exe
CC1 = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet
GAS2NASK = $(TOOLPATH)gas2nask.exe -a
OBJ2BIM = $(TOOLPATH)obj2bim.exe
MAKEFONT = $(TOOLPATH)makefont.exe
BIN2OBJ = $(TOOLPATH)bin2obj.exe
BIM2HRB = $(TOOLPATH)bim2hrb.exe
RULEFILE = $(TOOLPATH)haribote/haribote.rul
EDIMG = $(TOOLPATH)edimg.exe
IMGTOL = $(TOOLPATH)imgtol.com
COPY = copy
DEL = del
# 默认
default :
$(MAKE) img
# 文件生成规则
ipl10.bin : ipl10.nas Makefile
$(NASK) ipl10.nas ipl10.bin ipl10.lst
asmhead.bin : asmhead.nas Makefile
$(NASK) asmhead.nas asmhead.bin asmhead.lst
hankaku.bin : hankaku.txt Makefile
$(MAKEFONT) hankaku.txt hankaku.bin
hankaku.obj : hankaku.bin Makefile
$(BIN2OBJ) hankaku.bin hankaku.obj _hankaku
bootpack.bim : $(OBJS_BOOTPACK) Makefile
$(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \
$(OBJS_BOOTPACK)
# 3MB+64KB=3136KB
bootpack.hrb : bootpack.bim Makefile
$(BIM2HRB) bootpack.bim bootpack.hrb 0
haribote.sys : asmhead.bin bootpack.hrb Makefile
copy /B asmhead.bin+bootpack.hrb haribote.sys
haribote.img : ipl10.bin haribote.sys Makefile
$(EDIMG) imgin:../z_tools/fdimg0at.tek \
wbinimg src:ipl10.bin len:512 from:0 to:0 \
copy from:haribote.sys to:@: \
imgout:haribote.img
# 一般规则
%.gas : %.c bootpack.h Makefile
$(CC1) -o $*.gas $*.c
%.nas : %.gas Makefile
$(GAS2NASK) $*.gas $*.nas
%.obj : %.nas Makefile
$(NASK) $*.nas $*.obj $*.lst
# 命令
img :
$(MAKE) haribote.img
run :
$(MAKE) img
$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin
$(MAKE) -C ../z_tools/qemu
install :
$(MAKE) img
$(IMGTOL) w a: haribote.img
clean :
-$(DEL) *.bin
-$(DEL) *.lst
-$(DEL) *.obj
-$(DEL) bootpack.map
-$(DEL) bootpack.bim
-$(DEL) bootpack.hrb
-$(DEL) haribote.sys
src_only :
$(MAKE) clean
-$(DEL) haribote.img
- 打开记事本,复制以下内容并保存为 hankaku.txt,且存放在harib05d中;ps:建议这一段代码直接复制不要看。
这段代码过多,无法贴出来,请在网盘资源复制,projects/05_day/harib02f中hankaku.txt
编译及运行
- 双击 !cons_nt.bat,并在打开的命令行中输入 make run
- VMware中运行结果,如下图
ps: 结果就是这样,因为本次实验的目的是移动鼠标指针 (harib05d),可以试着移动鼠标看看,到鼠标画面移动了,以后会有更精彩。敬请期待!
好的今天到这里harib05d就张结束了,请大家多多支持!!!
**