开发第四天+第五天读书笔记

news2024/9/20 15:32:55

首先用C语言实现内存写入:

光是成功的让画面黑屏是不够的,还是要往画面上画点什么。首先修改naskfunc.nas。写成这样:

; naskfunc
; TAB=4

[FORMAT "WCOFF"]                ; 创建对象文件的模式
[INSTRSET "i486p"]              ; 想使用486的命令的记述
[BITS 32]                       ; 制作32位模式用的机器语言
[FILE "naskfunc.nas"]           ; 源文件名称信息

        GLOBAL  _io_hlt,_write_mem8

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

_io_hlt:                        ; void io_hlt(void)
        HLT
        RET                     ; 这个相当于C语言中的return,意思是"函数的处理到此结束,返回吧”

_write_mem8:                    ; void write_mem8(int addr,int data)
        MOV     ECX,[ESP+4]     ; [ESP+4]中存放的是地址,将其读入ECX
        MOV     AL,[ESP+8]      ; [ESP+8]中存放的是数据,将其读入AL
        MOV     [ECX],AL
        RET

按照作者的说法,只要往VRAM里面写点什么就可以了。翻一下书的前面可以知道这个代表了图像缓冲区的开始地址。

不仅如此,还要改变bootpack.c:

void io_hlt(void);
void write_mem8(int addr,int data);

void HariMain(void)
{
    int i;  //变量声明: 是一个32位整数

    for(i=0xa0000;i<=0xaffff;i++)
    {
        write_mem8(i,15);   //MOV BYTE [i],15
    }

    for(;;)io_hlt();
}

运行起来后出现了一片白:
在这里插入图片描述
只是显示出一篇白色并不能让人满意,那就尝试显示一下条纹吧。办法便是修改一下bootpack.c:

void io_hlt(void);
void write_mem8(int addr,int data);

void HariMain(void)
{
    int i;  //变量声明: 是一个32位整数

    for(i=0xa0000;i<=0xaffff;i++)
    {
        write_mem8(i,i&0x0f);   //MOV BYTE [i],15
    }

    for(;;)io_hlt();
}

虽然没用到,但还是说一下异或运算“A XOR B":
对于某一位,A和B该位的值如果不相同,那么异或运算的结果该位为1,否则就为0

作者认为针对下面这三种语句:

char *p;	//用于BYTE类地址
short *p;	//用于WORD类地址
int *p;		//用于DWORD类地址

在以上三个语句中,指针变量p都是4字节,在汇编语言中,地址是用4字节的变量来指定的,所以也是4字节。
再次改变bootpack.c:

void io_hlt(void);

void HariMain(void)
{
    int i;  //变量声明: 是一个32位整数
    char *p; //变量p,用于BYTE型地址

    for(i=0xa0000;i<=0xaffff;i++)
    {
        p=(char*)i;         //代入地址
        *p=i&0x0f;   //MOV BYTE [i],15

        //这可以代替write_mem8(i,i&0x0f)
    }

    for(;;)io_hlt();
}

运行后能出现彩色条纹:
在这里插入图片描述

作者在书中讲解了C语言的指针,以下面两个语句为例:

p=(char*)i;
*p=i & 0x0f;

转化成汇编语言,假设p相当于ECX,写出来就是:

MOV	ECX,i
MOV	BYTE [ECX],(i & 0x0f)

这两个汇编语句是不一样的,一个是给ECX寄存器赋值,而它的下一句则是给ECX号的内存地址赋值。存储他们的半导体也不一样,一个在CPU里,一个在内存芯片里。
如果把上面的两个C语言语句颠倒过来,也就是写成这样:

*p=i&0x0f
p=(char*)i;

如果照汇编语言来看,这相当于是:

MOV BYTE [ECX],(i&0x0f)
MOV	ECX,i

在做第一个MOV的时候,ECX的值不确定,这会导致i&0x0f的结果写入内存的某个未知地址中。

顺带说句,作者说将p[i]写成i[p]也是可以的,试了下,惊了

接下来继续做操作系统的画面,要使用16种颜色,然后,修改bootpack.c:

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflegs(void);
void io_store_eflegs(int eflegs);

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

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

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();
}

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,   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;                                 
    //C语言中的static char语句只能用于数据,相当于汇编语言中的DB指令
}

void set_palette(int start, int end, unsigned char *rgb)
{
    int i,eflags;
    eflags=io_load_eflegs();                // 记录中断许可标志的值
    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;
}

这个C语言的声明:

char a[3];

其中的a以汇编语言来讲就是标志符,标志符的值就意味着地址。还准备了“RESB 3"(从地址a开始空出3个字节)。也就是上述声明相当于:

a:
	RESB	3

在nask中RESB的内容能保证是0,但 C语言中不能保证里面是否有垃圾数据。

char型的变量有3种模式,分别是signed型、unsigned型和未指定型。其中signed型用于处理-128——127的整数,unsigned型能够处理0——255的整数。未指定型是指没有特别指定时,可由编译器决定是unsigned还是signed。

0xff会被误解成-1.

既然CPU与设备相连,那么就有向这些设备发送信号,或者从这些设备取得信号的指令。像设备发送电信号的是OUT指令,从设备获取电信号的是IN指令。在这两种指令中为了区分不同的设备,也要使用设备号码((英文中称为port)。

指令CLI:将中断标志置为0;
指令STI:将中断标志置为1;

CPU遇到中断请求时,如果中断标志为1则立即处理中断请求,如果中断标志为0则忽略中断请求。

书中介绍了EFLAGS这一特别的寄存器。这是由名为FLAGS的16位寄存器扩展而来的32位寄存器。FLAGS是存储进位标志和中断标志等标志的寄存器。对于中断标志,只能读入EFLAGS,再检查第9位是0还是1。顺便说一下,进位标志是EFLAGS的第0位

能够用来读写寄存器EFLAGS的,只有PUSHFD和POPFD指令。前者的意思是将标志位的值按双字长压入栈。后者的意思是按照双字长将标志位从栈弹出。

那么这样一来,

PUSHFD	POP	EAX

指的是首先将EFLAGS压入栈,再将弹出的值压入EAX。
实质上是取代了下前面这句:

MOV	EAX,EFLAGS

另一方面,

PUSH EAX POPFD

相当于:

MOV EFLAGS,EAX

。。。。。。
经过一系列修改后,呈现出了这个样子:
在这里插入图片描述

好,进入Day 5部分。

如何实现对字符的显示是一个问题,作者认为字符可以用8*16的长方形像素点阵来表示。8位是一个字节,而一个字符是16个字节。如下图所示:

在这里插入图片描述
修改了bootpack.c之后,效果如下:
在这里插入图片描述
显示汉字的事情放后面,我们先要能显示字母和数字,照作者所说沿用OSASK的字体。

在C语言中,像这种在源程序以外准备的数据,都要加上extern属性,这样编译器就能够知道他们是外部数据,并在编译时做出相应调整,比如:

extern char hankakul.txt

所谓的字符串是指按顺序排列在内存里,末尾加上0x00而组成的字符编码

关于操作系统开发,作者还说:

能不能显示变量值,对于操作系统的开发影响很大,这是因为程序运行与想象中不一致时,将可以变量的值显示出来是最好的方法。就想Windows的调试器不能对Linux的程序进行调试一样,Windows的调试器也不能对我们的自制操作系统进行调试。

自制操作系统中不能随便用printf函数,但是sprintf函数可以使用,因为sprintf不是按指定格式输出,只是将输出内容作为字符串写在内存中,它能够不使用操作系统的任何功能,只对内存进行操作,所以可以应用于所有操作系统。

关于sprintf函数的格式记号及其含义:

%d: 单纯的十进制数
%5d: 5位十进制数,如果是123,则在前面加上两个空格,变成“ 123”强行达到5位
%05d: 5位十进制数,如果是123,则在前面加上两个0,变成“00123”强行达到5位
%x: 单纯的十六进制数,字母部分用小写
%X: 单纯的十六进制数,字母部分用大写
%5x: 5位十六进制数,如果是456(十进制),则在前面加上两个空格变成“ 1c8",强行达到5位。还有%5X的形式。
%05x: 5位十六进制数,如果是456(十进制),则在前面加上两个0变成“001c8",强行达到5位。还有%05X的形式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/189102.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux进程间通信(基于管道通信)

目录 一、进程间通信 1.1 通信目的 1.2 通信发展 二、管道 2.1 管道的概念和分类 2.2 匿名管道 2.2.1 匿名管道&#xff08;基于父子血缘关系) 2.2.2 匿名管道单向性 2.2.3 匿名管道是内存级别文件 2.2.4 匿名管道指令实现 2.2.5 代码实现匿名管道&#xff08;pipe()…

一文讲清楚shell 中的变量是怎么回事

目录 变量和引用 一&#xff0c; 什么是变量 二&#xff0c; 根据数据类型分类 三&#xff0c; 根据作用域分类 1、环境变量 2、普通变量 四&#xff0c; 变量的定义 1、变量定义示例&#xff1a;变量名变量值 2、位置参数和预定义变量 五&#xff0c; shell中的引用 六&…

【GPLT 二阶题目集】L2-022 重排链表

输入格式&#xff1a; 每个输入包含1个测试用例。每个测试用例第1行给出第1个结点的地址和结点总个数&#xff0c;即正整数N (≤10^5 )。结点的地址是5位非负整数&#xff0c;NULL地址用−1表示。 接下来有N行&#xff0c;每行格式为&#xff1a; Address Data Next 其中Addres…

深拷贝和浅拷贝对比

JavaScript存储引用数据&#xff08;对象&#xff09;都是存地址的&#xff0c;存放在堆内存中的对象&#xff0c;在栈内存中存的是一个指针&#xff0c;这个指针指向堆内存一个位置。再从堆内存中取得所需的数据。 深拷贝&#xff1a;对数据进行拷贝之后&#xff0c;修改拷贝之…

Python:从协议到抽象基类

本章话题是接口&#xff1a;鸭子类型代表特征动态协议&#xff1b; 使接口更明确、能验证实现是否副了规定的抽象基类ABC(Abstact Base Class).Python语言诞生15年后&#xff0c;Python2.6中才引入了抽象基类&#xff0c;抽象基类。对于java、C#类似的语言&#xff0c;会觉得鸭…

DevSecOps敏捷安全技术金字塔V3.0正式发布

2022年12月28日&#xff0c;由悬镜安全主办&#xff0c;3S-Lab软件供应链安全实验室、Linux基金会OpenChain社区、ISC、OpenSCA社区联合协办的第二届全球DevSecOps敏捷安全大会&#xff08;DSO 2022&#xff09;已通过全球直播的形式圆满举行。本届大会以“共生敏捷进化”为主题…

HTB_Markup_xml注入读ssh私钥进程注入

文章目录信息收集xml注入ssh 私钥连接提权信息收集 使用如下参数可以探测具体版本&#xff0c;只使用-sV -v无此效果 nmap -sC -A -Pn 10.129.95.192是个登录页面 弱口令&#xff0c;只有admin-password成功登录 几个页面&#xff0c;只有order.php页面可以与后端交互并传递x…

【Kubernetes 企业项目实战】06、基于 Jenkins+K8s 构建 DevOps 自动化运维管理平台(中)

目录 一、基于 Jenkinsk8sGitDocker Hub 等技术链构建企业级 DevOps 容器云平台 1.1 安装 Jenkins 1.1.1 安装 nfs 服务 1.1.2 在 kubernetes 中部署 jenkins 1.2 配置 Jenkins ​1.2.1 获取管理员密码 1.2.2 安装插件 1.2.3 创建第一个管理员用户 1.3 测试 jenkins 的…

VMware Workstation中安装Kali 2022

VMware Workstation中安装Kali 2022 前言 开工了&#xff0c;笔记本中的相关工具该更新了&#xff0c;今天记录一下。 首先记录的是在VMware Workstation中安装kali&#xff0c;这个过程比较简单。 我只是想扩充一下自己的博客&#xff0c;另外可以给入门人员一个参考。 下载…

react的JSX语法

1.jsx基本使用 1 createElement() 的问题 繁琐不简洁。不直观&#xff0c;无法一眼看出所描述的结构。不优雅&#xff0c;用户体验不爽。 2 JSX 简介 JSX 是 JavaScript XML 的简写&#xff0c;表示在 JavaScript 代码中写 XML&#xff08;HTML&#xff09; 格式的代码。 优…

通信原理笔记—增量调制(∆M)

目录 概述&#xff1a; 简单增量调制(∆M)原理&#xff1a; 编码器与解码器 简单△M的过载问题&#xff1a; 增量总和(∆-Σ)调制 数字压扩自适应增量调制&#xff1a; 不同编码调制方式的误码性能分析&#xff1a; 概述&#xff1a; 最简单的DPCM是增量调制&#xff0c…

layui框架学习(4:导航)

layui官网教程采用html中的无序列表和定义列表来实现导航样式&#xff08;文章最后还有个关于导航所用元素的补充说明&#xff09;&#xff0c;主要包括水平导航、垂直/侧边导航&#xff0c;同时还支持用span和a元素实现面包屑导航样式。导航功能需要加载element模块&#xff0…

实验二:Linux主机漏洞利⽤攻击实践

&#xff08;一&#xff09;实验简介 实验所属系列&#xff1a;windows主机漏洞利用攻击实践 实验对象&#xff1a;本科/专科信息安全专业 相关课程&#xff1a;渗透测试 实验时数&#xff08;学分&#xff09;&#xff1a;2学时 实验类别&#xff1a;实践类 &#xff08;二&a…

如何集中式管理多个客户端节点传输任务-镭速

在一些生产制造企业或it部门&#xff0c;它们的生产机器设备每天都会有大量的生产数据&#xff0c;并且需要人为地对这些数据进行迁移或者归档备份到其他存储。这样重复性的操作&#xff0c;无疑大大提高了人工成本&#xff0c;而且运作起来的效率也不高。 镭速服务器集中式任务…

MySQL优化方案

一、MySQL 的优化方案有哪些&#xff1f;MySQL 数据库常见的优化手段分为三个层面&#xff1a;SQL 和索引优化、数据库结构优化、系统硬件优化等&#xff0c;每个大的方向中又包含多个小的优化点。1.SQL 和索引优化通过优化 SQL 语句以及索引来提高 MySQL 数据库的运行效率① 使…

【UE5】动画系统

title: 【UE5】动画系统 date: 2023-01-31 19:50:47 tags: UE5 categories: 学习笔记 password: abstract: message: 最近接触的项目涉及到动捕和动画&#xff0c;以前接触的范围主要是GamePlay以及C和蓝图的交互&#xff0c;很少接触动画&#xff0c;借此机会学习一下UE5的动…

【Jmeter】报错解决:JedisException: Could not return the broken resource to the pool

一、报错详情 (1)报错场景 使用 Jmeter 插件 Redis Data Set 配置连接 Redis 数据池时,运行出现报错 (2)报错日志

Python对liunx中mysql数据库进行单表查询 10个案例带你了解

关于Python连接liunx中mysql数据库的方式在这一篇文章 这里写目录标题1.在Liunx服务器中的mysql新建一个表2.插入数据3.连接liunx中的mysql数据库1、查询1946班的成绩信息2&#xff0c;查询1944班&#xff0c;语文成绩大于60小于90的成绩信息3&#xff0c;查询学生表中1到6行的…

QTabWidget 美化 qss

1. tab&#xff0c; tab-bar&#xff0c; pane属性分布 2. 使用qss美化时&#xff0c;tab标签上和pane中都能美化成功&#xff0c;但tab最右侧的tab-bar却始终没有成功。 /*设置控件的背景*/ QTabWidget {background-color:rgb(4,138,224); } /*设置控件下面板的背景颜色*/ QT…

C++11 常用的新特性

本篇介绍C11标准对比之前C标准的新特性&#xff0c;C11为C语言在2011年发布的版本&#xff0c;它改进幅度很大&#xff0c;影响至今。如加入auto 关键字、nullptr、移动语义&#xff08;move semantics&#xff09;、委托构造函数&#xff08;delegating constructors&#xff…