今天尝试将操作系统的开发移到linux平台过来,因为感觉在win下面做的很不爽。参与了米油的一些代码,总算成功了。那么下面开始今天的内容。
1,用C语言实现内存写入
往naskfunc.nas里添加的一个新的函数
_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
这段函数类似于C语言中的"write_mem8(0x1234,0x56);"语句,动作上相当于"MOV BYTE [0x1234], 0x56"。
在C语言中如果用到了write_mem8函数,就会跳转到_write_mem8。此时参数指定的数字就存放在内存里,分别是:
第一个数字的内存地址: [ESP + 4]
第二个数字的内存地址: [ESP + 8]
第三个数字的内存地址: [ESP + 12]
第四个数字的内存地址: [ESP + 16]
这里我们来看看栈的使用,当然我们这里不说栈的一些概念,就是先进先出,我们这里只说一些重点的东西。我们知道函数调用参数都是会被压入栈中的。
第一:这里为什么每次都是以4再进行增长?原因很简单,因为传进来的参数都是整形的,因为我们这里的整形占4个字节,所以按4增长。
第二:为什么我们这里MOV EXC,[ESP+4],这里为什么是4,而不是3,2或者是1,这里就要理解压栈的过程了,以后每次压栈时,SP都要依次减2。很明显,不
同于代码段,代码段在处理器上执行时,是由低地址端向高地址端推进的,而压栈操作则正好相反,是从高地址端向低地址端推进的。下面可以通过这个图来
了解它的工作原理。
第三:[ESP + 4]是不是等于EDS*16+(ESP+4)?? 不是,而是等于ESS*16+(ESP+4)
那么这段代码要被C调用,还需做一些操作才行,需要创建一个naskfunc.nas才行,内容如下
;naskfunc ;TAB = 4 [FORMAT "WCOFF"] ;制作目标文件的模式 [BITS 32] ;制作32位模式用的机械语言 ;制作目标文件的信息 [FILE "naskfunc.nas"] ;源文件名信息 GLOBAL _write_mem8 ;程序中包含的函数名 ;以下是实际的函数 [SECTION .text] ;目标文件中写了这些之后再写程序 _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
OK,完工,有了上面这个文件,就可以在C中轻松的调用汇编代码了。在后面,你会看到用gcc内嵌gas汇编更加的方便。
**如果与C语言联合使用的话,有的寄存器能自由使用,有的寄存器不能自由使用,能自由使用的只有EAX,ECX,EDX这三个,至于其他寄存器,只能使用其值,而不能改变其值。
因为这些寄存器在C语言编译后生成的机器语言中,用于记忆非常重要的值,因此这次我们只用EAX和ECX。
2,条纹图案
for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem8(i, i & 0x0f);
}
这里为什么能形成条纹图案呢?
其实很简单, 0xa0000 & 0x0000f = 0x00; 0xa0001 & 0x0000f = 0x01;...... 0xa000f & 0x0000f = 0x0f,后面重新循环,又接着从0x00开始到0x0f结束,这样就每隔
16个像素,色号就反复一次。这样就出现了条纹图案。
3,挑战指针:
(1)如果我们将write_mem8(i, i & 0x0f)改成 *i = i & 0x0f;那么make run之后就会出现 invalid type argument of 'unary *'错误
我们可以将*i = i & 0x0f用汇编的形式来代替,MOV [i], (i & 0x0f),用我们以前的知识来判断MOV [0x1234], 0x56是否正确,当然这是错误的,为什么呢?这是因为指定
内存事,不知道到底是BYTE,还是WORD,还是DWORD,只是在另一方也是寄存器的时候才能省略,其他情况都不能省略。那么对于MOV [i], (i & 0x0f)来说,同样不知到[i]是BYTE,还是WORD,还是DWORD。这也是这段不用指定BYTE的原因
_write_mem8: ;void write_mem8(int addr,int data);
MOVECX, [ESP+4] ;[ESP + 4]中存放的是地址,将其读入ECX
MOVAL,[ESP+8] ;[ESP + 8]中存放的是数据,将其读入AL
MOV[ECX],AL
RET
(2)
char *p /*用于BYTE类地址*/
short *p /*用于WORD类地址*/
int *p /*用于DWORD类地址*/
char i 是类似AL的1字节变量,short i 是类似AX的2字节变量,int i是类似EAX的4字节变量
而不管是char *p, short *p, int *p,变量p都是4个字节,这是因为p是用于记录地址的变量,在汇编语言中,地址就像ECX一样,用4个字节的寄存器来指定,所以也是4个字节。
4,bootpack.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 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 *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(); } } void init_palette(void) { static unsigned char table_rgb[16 * 3] = { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0xc6, 0xc6, 0x84, 0x00, 0x00, 0x00, 0x84, 0x00, 0x84, 0x84, 0x00, 0x00, 0x00, 0x84, 0x84, 0x00, 0x84, 0x00, 0x84, 0x84, 0x84, 0x84, 0x84 }; 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(); 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; } 我们只重点来看看set_palette函数,首先我们先来分析 void set_palette(int start, int end, unsigned char *rgb) { int i; 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; } return; }
我们只重点来看看set_palette函数,首先我们先来分析
void set_palette(int start, int end, unsigned char *rgb) { int i; 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; } return; }
我们前面说过,CPU的管脚与内存相连,如果仅仅是于内存相连,CPU就只能完成计算和存储的功能,但实际上,CPU还要对键盘的输入有响应,要通过网卡从网络取得信息,通过
声卡发送音乐数据,向软盘写入信息,这些都是设备,他们当然要连接到CPU上。
既然CPU与设备相连,那么就有向这些设备发送电信号,或者从这些设备取得信息的指令,向设备发送电信号的是OUT指令,从设备取得电气信号的是IN指令,正如位了区别不同的
内存要使用内存地址一样,在OUT指令和IN指令中,为了区别不同的设备,也要使用设备号码,设备号码在英文中称为port(端口)。port原意是“港口”,这里形象地将CPU与各个设备交换CPU与各个设备交换电信号的行为比作了船舶的出港和进港。
所以,我们执行OUT指令时,出港信号就要挥泪告别CPU了。在C语言中,没有于IN或OUT指令相当的语句,所以我们只好拿汇编语言来做了,这时候,汇编就显示出它的优势了。
调色版的访问步骤:
1,首先在一连串的访问中屏蔽中断(比如CLI)。
2,将想要设定的调色板写入0x03c8,接着着,按R,G,B的顺序写入0x03c9,如果还想要继续设定下一个调色板,则省略调色板号码,再按照RGB的顺序写入0x03c9就行了。
3,如果想要读出当前调色板的状态,首先要将调色板的号码写入0x03c7,再从0x03c9读取3次,读出的顺序就是RGB。如果想要继续读出下一个调色板,同样也是省略调色板号码
的设定,按RGB的顺序读出。
4,如果最初执行CLI,那么最后要执行STI
我们再来看这段代码剩余的部分:
void set_palette(int start, int end, unsigned char *rgb) { int i, eflags; eflags = io_load_eflags(); /*记录中断许可标志的值*/ io_cli(); /*将许可标志设置为0,禁止中断*/ io_out8(0x03c8, start); 已经说明的部分 io_store_eflags(eflags); /*恢复许可标志的值*/ return; }
首先是CLI和STI,所谓CLI,就是将中断标志设置位0的指令,STI是要将这个中断标志设置为1的指令。而标志,是指像以前曾出现过的进位标志一样的各种标志,也就是说CPU中有多种多样的标志。更改中断标志油什么好处呢?正如其名所示,它与CPU的中断处理有关系,当CPU遇到中断请求时,是立即处理中断请求(中断标志为1),还是忽略中断请求(中断标志为0),就由这个中断标志位来设定。
来了,剩下的我们来看看C调用汇编的代码
; naskfunc ; TAB=4 [FORMAT "WCOFF"] [INSTRSET "i486p"] [BITS 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: ; 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 POP EAX RET _io_store_eflags: ; void io_store_eflags(int eflags); MOV EAX,[ESP+4] PUSH EAX POPFD RET
我们重点来看看PUSHFD和POPFD指令: 因为汇编中没有MOV EAX,EFLAGS的指令,所以只能通过PUSHFD和POPFD来间接的实现这个功能 PUSHFD是"push flags double-word"的缩写,意思是将标志位的值按双字长压入栈。其实它所做的无非就是"PUSH EFLAGS".POPFD是"pop flags double-word"的缩写,意思是 案双字节长将标志位从栈弹出,它所做的就是"POP EFLAGS"。 也就是说,"PUSHFD POP EAX",是指首先将EFLAGS压入栈,再将弹出的值代入EAX。所以说它代替了"MOV EAX,EFLAGS"。另一方面,PUSH EAX POPFD正好与此相反,它相当于"MOV EFLAGS,EAX"。 最后一个就是绘制矩形的公式:像素坐标(x,y)对应的VRAM地址公式:0xa0000+x+y*320。后面再来看看linux平台上面的实现。
相关推荐
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
MMC整流器技术解析:基于Matlab的双闭环控制策略与环流抑制性能研究,Matlab下的MMC整流器技术文档:18个子模块,双闭环控制稳定直流电压,环流抑制与最近电平逼近调制,优化桥臂电流波形,高效并网运行。,MMC整流器(Matlab),技术文档 1.MMC工作在整流侧,子模块个数N=18,直流侧电压Udc=25.2kV,交流侧电压6.6kV 2.控制器采用双闭环控制,外环控制直流电压,采用PI调节器,电流内环采用PI+前馈解耦; 3.环流抑制采用PI控制,能够抑制环流二倍频分量; 4.采用最近电平逼近调制(NLM), 5.均压排序:电容电压排序采用冒泡排序,判断桥臂电流方向确定投入切除; 结果: 1.输出的直流电压能够稳定在25.2kV; 2.有功功率,无功功率稳态时波形稳定,有功功率为3.2MW,无功稳定在0Var; 3.网侧电压电流波形均为对称的三相电压和三相电流波形,网侧电流THD=1.47%<2%,符合并网要求; 4.环流抑制后桥臂电流的波形得到改善,桥臂电流THD由9.57%降至1.93%,环流波形也可以看到得到抑制; 5.电容电压能够稳定变化 ,工作点关键词:MMC
Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基于功率反馈的扰动观察法调整电压方向研究,Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基于功率反馈的扰动观察法调整电压方向研究,Boost二级升压光伏并网结构,Simulink建模,MPPT最大功率点追踪,扰动观察法采用功率反馈方式,若ΔP>0,说明电压调整的方向正确,可以继续按原方向进行“干扰”;若ΔP<0,说明电压调整的方向错误,需要对“干扰”的方向进行改变。 ,Boost升压;光伏并网结构;Simulink建模;MPPT最大功率点追踪;扰动观察法;功率反馈;电压调整方向。,光伏并网结构中Boost升压MPPT控制策略的Simulink建模与功率反馈扰动观察法
STM32F103C8T6 USB寄存器开发详解(12)-键盘设备
科技活动人员数专指直接从事科技活动以及专门从事科技活动管理和为科技活动提供直接服务的人员数量
Matlab Simulink仿真探究Flyback反激式开关电源性能表现与优化策略,Matlab Simulink仿真探究Flyback反激式开关电源的工作机制,Matlab Simulimk仿真,Flyback反激式开关电源仿真 ,Matlab; Simulink仿真; Flyback反激式; 开关电源仿真,Matlab Simulink在Flyback反激式开关电源仿真中的应用
基于Comsol的埋地电缆电磁加热计算模型:深度解析温度场与电磁场分布学习资料与服务,COMSOL埋地电缆电磁加热计算模型:温度场与电磁场分布的解析与学习资源,comsol 埋地电缆电磁加热计算模型,可以得到埋地电缆温度场及电磁场分布,提供学习资料和服务, ,comsol;埋地电缆电磁加热计算模型;温度场分布;电磁场分布;学习资料;服务,Comsol埋地电缆电磁加热模型:温度场与电磁场分布学习资料及服务
1、文件内容:ibus-table-chinese-yong-1.4.6-3.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/ibus-table-chinese-yong-1.4.6-3.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
基于51单片机protues仿真的汽车智能灯光控制系统设计(仿真图、源代码) 一、设计项目 根据本次设计的要求,设计出一款基于51单片机的自动切换远近光灯的设计。 技术条件与说明: 1. 设计硬件部分,中央处理器采用了STC89C51RC单片机; 2. 使用两个灯珠代表远近光灯,感光部分采用了光敏电阻,因为光敏电阻输出的是电压模拟信号,单片机不能直接处理模拟信号,所以经过ADC0832进行转化成数字信号; 3. 显示部分采用了LCD1602液晶,还增加按键部分电路,可以选择手自动切换远近光灯; 4. 用超声模块进行检测距离;
altermanager的企业微信告警服务
MyAgent测试版本在线下载
Comsol技术:可调BIC应用的二氧化钒VO2材料探索,Comsol模拟二氧化钒VO2的可调BIC特性研究,Comsol二氧化钒VO2可调BIC。 ,Comsol; 二氧化钒VO2; 可调BIC,Comsol二氧化钒VO2材料:可调BIC技术的关键应用
C++学生成绩管理系统源码
基于Matlab与Cplex的激励型需求响应模式:负荷转移与电价响应的差异化目标函数解析,基于Matlab与CPLEX的激励型需求响应负荷转移策略探索,激励型需求响应 matlab +cplex 激励型需求响应采用激励型需求响应方式对负荷进行转移,和电价响应模式不同,具体的目标函数如下 ,激励型需求响应; matlab + cplex; 负荷转移; 目标函数。,Matlab与Cplex结合的激励型需求响应模型及其负荷转移策略
scratch介绍(scratch说明).zip
内容概要:本文全面介绍了深度学习模型的概念、工作机制和发展历程,详细探讨了神经网络的构建和训练过程,包括反向传播算法和梯度下降方法。文中还列举了深度学习在图像识别、自然语言处理、医疗和金融等多个领域的应用实例,并讨论了当前面临的挑战,如数据依赖、计算资源需求、可解释性和对抗攻击等问题。最后,文章展望了未来的发展趋势,如与量子计算和区块链的融合,以及在更多领域的应用前景。 适合人群:对该领域有兴趣的技术人员、研究人员和学者,尤其适合那些希望深入了解深度学习原理和技术细节的读者。 使用场景及目标:①理解深度学习模型的基本原理和结构;②了解深度学习模型的具体应用案例;③掌握应对当前技术挑战的方向。 阅读建议:文章内容详尽丰富,读者应在阅读过程中注意理解各个关键技术的概念和原理,尤其是神经网络的构成及训练过程。同时也建议对比不同模型的特点及其在具体应用中的表现。
该文档提供了一个关于供应链管理系统开发的详细指南,重点介绍了项目安排、技术实现和框架搭建的相关内容。 文档分为以下几个关键部分: 项目安排:主要步骤包括搭建框架(1天),基础数据模块和权限管理(4天),以及应收应付和销售管理(5天)。 供应链概念:供应链系统的核心流程是通过采购商品放入仓库,并在销售时从仓库提取商品,涉及三个主要订单:采购订单、销售订单和调拨订单。 大数据的应用:介绍了数据挖掘、ETL(数据抽取)和BI(商业智能)在供应链管理中的应用。 技术实现:讲述了DAO(数据访问对象)的重用、服务层的重用、以及前端JS的继承机制、jQuery插件开发等技术细节。 系统框架搭建:包括Maven环境的配置、Web工程的创建、持久化类和映射文件的编写,以及Spring配置文件的实现。 DAO的需求和功能:供应链管理系统的各个模块都涉及分页查询、条件查询、删除、增加、修改操作等需求。 泛型的应用:通过示例说明了在Java语言中如何使用泛型来实现模块化和可扩展性。 文档非常技术导向,适合开发人员参考,用于构建供应链管理系统的架构和功能模块。
这份长达104页的手册由清华大学新闻与传播学院新媒体研究中心元宇宙文化实验室的余梦珑博士后及其团队精心编撰,内容详尽,覆盖了从基础概念、技术原理到实战案例的全方位指导。它不仅适合初学者快速了解DeepSeek的基本操作,也为有经验的用户提供了高级技巧和优化策略。
主题说明: 1、将mxtheme目录放置根目录 | 将mxpro目录放置template文件夹中 2、苹果cms后台-系统-网站参数配置-网站模板-选择mxpro 模板目录填写html 3、网站模板选择好之后一定要先访问前台,然后再进入后台设置 4、主题后台地址: MXTU MAX图图主题,/admin.php/admin/mxpro/mxproset admin.php改成你登录后台的xxx.php 5、首页幻灯片设置视频推荐9,自行后台设置 6、追剧周表在视频数据中,节目周期添加周一至周日自行添加,格式:一,二,三,四,五,六,日
运行GUI版本,可二开