4、C语言与画面显示的练习

用C语言实现内存写入
这次想要再画面中加点东西,C语言中无直接写入指定内存地址的语句,所以在naskfunc.nas中添加一些东西

; naskfunc
; TAB=4

  [FORMAT "WCOFF"]              ; 制作目标文件的模式
  [INSTRSET "i486p"]            ; 用来告诉nask,这个程序是给486用的,这样EAX就会被解释成寄存器名。
  [BITS 32]                     ; 制作32位模式用的机器语言

  ; 制作目标文件的信息

 [FILE "naskfunc.nas"]          ; 源文件名信息

         ; 程序中包含的函数名
         GLOBAL    _io_hlt, _write_mem8      

  ; 以下是实际的函数

 [SECTION .text]                ; 目标文件中写了这些后再写程序

 _io_hlt:                       ; 为了与C语言函数廉洁,函数名前必须加‘_',void io_hlt(void);
         HLT                    ; 使CPU基本处于睡眠状态
         RET                    ; 相当于return

_write_mem8:                    ; void write_mem8(int addr, int data);
     MOV       ECX,[ESP+4]      ; [ESP+4]中存放的是地址,将其读入ECX。CX加上E代表32位寄存器
     MOV       AL,[ESP+8]       ; [ESP+8]中存放的数据,将其读入AL
     MOV       [ECX],AL
     RET

bootpack.c中导入了变量

//告诉C编译器,有一个函数在别的文件里

void io_hlt(void);   //是函数声明,意思是函数在别的文件中,自己找一下吧
void write_mem8(int addr, int data);

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
   int i;            //变量声明:i是一个32位整数

   for(i = 0xa0000; i<=0xaffff; i++)
   {
        write_mem8(i, 15);  //MOV BYTE [i], 15  向VRAM中全部写入15,及全部像素都是第15中颜色:白色
   }

  for (;;) 
   { 
        io_hlt();    //执行naskfunc.nas里的_io_hlt
   }
}

make run
这里写图片描述

条纹图案
修改bootpack.c

write_mem8(i, i & 0x0f);  //相当于写入00 01 02 ... 0F 00 01 02 ...

Make run
这里写图片描述

挑战指针
C语言虽不能直接写入指定内存地址,但指针可以,下面修改bootpack.c,代替汇编部分

//告诉C编译器,有一个函数在别的文件里

void io_hlt(void);   //是函数声明,意思是函数在别的文件中,自己找一下吧

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
       int i;            //变量声明:i是一个32位整数
       char *p;          //变量p,用于BYTE型地址

       for(i=0xa0000; i<=0xaffff; i++)
       {
            p = i;       //代入地址
            *p = i & 0x0f;   

            //以上两句也可以写成 *(p+i) = i & 0x0f
   }

       for (;;) 
       { 
            io_hlt();    //执行naskfunc.nas里的_io_hlt
       }
}

色号设定
接下来处理颜色问题。之前使用320*200的8位颜色模式,这次韩城RGB方式,用6位十六进制数,也就是用24位来指定颜色。
bootpack.c中添加代码:

//告诉C编译器,有一个函数在别的文件里

void io_hlt(void);   //是函数声明,意思是函数在别的文件中,自己找一下吧
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

//就算写在同一个源文件里,如果想在定义前使用,还必须先声明一下

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
    int i;            //变量声明:i是一个32位整数
    char *p;          //变量p,用于BYTE型地址

    init_palette();   //设定调色板

    p = (char *) 0xa0000;  //指定地址

    for(i=0; i<=0xffff; i++)
    {
        *(p+i) = i & 0x0f;
    }

    for (;;) 
    { 
        io_hlt();    //执行naskfunc.nas里的_io_hlt
    }

}

//设置调色板
void init_palette(void)
{
    /*声明了一个常数 table_rgb,相当于汇编中
         table_rgb:
              RESB 48

     static char 相当于汇编中的DB
        */
    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, 0xff, 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;
}

void set_palette(int start, int end, unsigned char *rgb)
{
    int i, eflags;
    eflags = io_load_eflags();  /* 记录中断许可标志的值 */
    io_cli();               /* 将中断许可标志置为0,禁止中断。访问调色板,首先屏蔽中断,如

CLI */
    io_out8(0x03c8, start);         //将想要设定的调色板号码写入0x03c8
    for (i = start; i <= end; i++) {      //再将RGB顺序写入0x03c9
        io_out8(0x03c9, rgb[0] / 4);
        io_out8(0x03c9, rgb[1] / 4);
        io_out8(0x03c9, rgb[2] / 4);
        rgb += 3;
    }
    io_store_eflags(eflags);    /* 复原中断许可标志。如果最初执行了CLI,最后还要执行STI */
    return;
}

说明:
进位标志在FLAGS第0位,中断标志在第9位

naskfunc.nas

; naskfunc
; TAB=4

  [FORMAT "WCOFF"]              ; 制作目标文件的模式
  [INSTRSET "i486p"]            ; 用来告诉nask,这个程序是给486用的,这样EAX就会被解释成寄存器名。
  [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


  ; 以下是实际的函数

 [SECTION .text]                ; 目标文件中写了这些后再写程序

 _io_hlt:                       ; 为了与C语言函数廉洁,函数名前必须加‘_',void io_hlt(void);
         HLT                    ; 使CPU基本处于睡眠状态
         RET                    ; 相当于return

 _io_cli:           ; void io_cli(void);
        CLI         ; (clear interrupt flag)将中断标志置为0,CPU遇到中断请求时忽略。
        RET

 _io_sti:           ; void io_sti(void);
        STI     ; (set interrupt flag)将中断标志置为1,CPU遇到中断请求时立即处理。
        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       ; IN--CPU从设备取得电气信号
        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       ; OUT--CPU向设备发送电气信号
        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         ; 将标志位弹出的值赋给EAX
        RET

 _io_store_eflags:          ; void io_store_eflags(int eflags);
        MOV     EAX,[ESP+4]
        PUSH        EAX
        POPFD               ; POP EFLAGS,按双字长出栈,将EX中的值赋给EFLAGS
        RET

make run
这里写图片描述

绘制矩形
bootpack.c中的节选

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);

//声明常数,表示哪种色号对应哪种颜色
#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

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
    char *p;    /* p变量的地址 */

    init_palette(); /* 设置调色板*/

    p = (char *) 0xa0000; /* 将地址赋值进去 */

    boxfill8(p, 320, COL8_FF0000,  20,  20, 120, 120);  //绘制矩形
    boxfill8(p, 320, COL8_00FF00,  70,  50, 170, 150);
    boxfill8(p, 320, COL8_0000FF, 120,  80, 220, 180);

    for (;;) 
    {
        io_hlt();
    }

}

//画面上有320*200个像素,左上为(0,0),右下为(319,319)
//像素坐标(x, y)对应的VREAM地址为 0xa0000 + x + y * 320
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;
}

这里写图片描述

成果
只修改HariMain

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
    char *vram;
    int xsize, ysize;

    init_palette();
    vram = (char *) 0xa0000;
    xsize = 320;
    ysize = 200;

    boxfill8(vram, xsize, COL8_008484,  0,         0,          xsize -  1, ysize - 29);
    boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 28, xsize -  1, ysize - 28);
    boxfill8(vram, xsize, COL8_FFFFFF,  0,         ysize - 27, xsize -  1, ysize - 27);
    boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 26, xsize -  1, ysize -  1);

    boxfill8(vram, xsize, COL8_FFFFFF,  3,         ysize - 24, 59,         ysize - 24);
    boxfill8(vram, xsize, COL8_FFFFFF,  2,         ysize - 24,  2,         ysize -  4);
    boxfill8(vram, xsize, COL8_848484,  3,         ysize -  4, 59,         ysize -  4);
    boxfill8(vram, xsize, COL8_848484, 59,         ysize - 23, 59,         ysize -  5);
    boxfill8(vram, xsize, COL8_000000,  2,         ysize -  3, 59,         ysize -  3);
    boxfill8(vram, xsize, COL8_000000, 60,         ysize - 24, 60,         ysize -  3);

    boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize -  4, ysize - 24);
    boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize -  4);
    boxfill8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize -  3, xsize -  4, ysize -  3);
    boxfill8(vram, xsize, COL8_FFFFFF, xsize -  3, ysize - 24, xsize -  3, ysize -  3);

    for (;;) 
    {
        io_hlt();
    }

}

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值