LR

已经过了一周多了没动这个了,先发布文章吧,最近进入项目组,可能时间不大多,以后慢慢改。
或许可以近期将一些这里研究过的东西写些感受放上来

目标:
1.可以显示不同编码的汉字,可自行更换字体,更改字体大小
2. 不同方式显示,例如,横版,竖版,双屏等等
3.多种控制方式
4.良好的界面,足够的速度,文件大小回避等。
5.支持压缩文件

达成:
1.使用freetype2库,可以打印Unicode汉字,使用ttf字体,可更改字体大小
2.可以实现GBK的打印
3.简单排版
4.读取配置文件
5.显示速度
6.字型灰度表示
7.简单的自动排版
8.简单的按键控制
9.1bit的位图的显示
10.自动判断几种编码方式,Unicode,UnicodeBigEnd,GBK(兼容GB2312,兼容ANSI)。
11.文件列表,及文件选择
12.简单的键盘,笔操作实现

短期目标:
1.各种操作的结合行
2.文件列表的缓冲显示
3.书签
4.常用字符点阵缓存功能
5.支持压缩文件,至少要zip
6.看能否实现最大程度的编码范围,比如使用iconv库来实现


log:
2008
2月
16日:决定自己做一款阅读器,开始搜集资料
17:安装devkitpro以及palib,hellowold成功
18:尝试读取文本,读取文本成功,直接打印英文成功,中文失败
19:研究点阵字库打印
20:GBK字库打印失败,GB2312打印成功
21:使用各种方式尝试使用GBK字库(system.fon),失败。PS,这个fon字体是不是有问题啊。。。
22:放弃点阵字库,发现开源freetype库,简单研究,发现freetype for nds,还是先thanks一下 Robots w/lasers http://blog.davr.org/
23:简单查看文档,根据demo编制程序,编译失败
24:freetype2古怪的include编译器不支持,更改其为普通的include方式,编译成功,使用fat.h for nds(应该是有更改流方式的作用),字体读取成功。未有显示结果,研究palib bitmap处理,果然,是颜色未定义。汉字打印失败,英文成功。考虑编码问题,果然是编码问题,使用unicode编码文字替代原有文字,打印成功。开心,终于完成了一个目标。
25:荒废了一日,基本。无论怎么include,都提示各种输入方式的wchar方式未定义
26:继续尝试各种widechar方式,无奈没有一种可以成功。放弃,改为直接读取char,然后将char类型转换成wchar_t类型,基本成功。
27:发现windows下字体全是unicode,故想用windows的字体直接使用GBK编码文件是不可能的,唯一的方法是写出转换函数,将GBK(或者GB2312,GBK向下兼容)编码转换成unicode编码,也就是windows采用的codepage方式,nds估计没有codepage,自己编一个。从gbk编码表中提取了GB2312编码表,实现转换。
28:利用stlmap,实现gbk编码查找,速度的问题暂时不知道,等有空去nds上实际测试一下。测试效果,不如人意。速度慢,效果差。。。进行了简单的排版试验
29:字体调整中,实在是受不了那个难看的效果了,想到两种方法,一种将各种灰度表现出来,二,看能否转换成单色(这个bitmap可能会小)。采用第一种方法成功,r=g=b为各种灰度。速度问题,读字体的时候,是非常慢的。看来只有先将要显示的读出来的办法了,不然确实很慢。不过,有个关键在于,看书的时候,看完一页需要时间,这个时间用来缓存应该是足够的。搞定。发现一个libiconv可以转换编码,应该会有所帮助吧,但是我这周的主要工作就没了。。。。看见说simsun字体在小字号下使用嵌入式bitmap,1bit,我忘记考虑这个了,怪不得。应该要做进去,可以自动判断得到FTbitmap的类型。不过我找到一种load方式,FT_LOAD_NO_BITMAP,不使用嵌入的字体。算是一个偷懒的方法吧。对于字体,我简单将其居中,继续偷懒。。。
3.1:简单实现了自动排版。今天,从字体解脱吧。开始选择文件!实现了简单的按键控制,可以简单的翻页了,但是速度很不理想,主要是需要缓冲很长时间。需要开始考虑FT_CACHE的问题,也许会有所帮助。
3.2:搞定了1bit bitmap和gray bitmap之间的显示,都是抄的代码-_-!。实现了编码自动判断,这个有了就比较方便了,其实早就编好了,只是今天去将最后的判断代码编写一下if..else if..else... :-)调整灰度函数,现在显示效果还不错了。使用点阵字库显示文件列表,不过16点阵看起来有些大啊,找找12点阵的看
3.3:汉字常用的字符频率:最常用的140个字符占50%,常用的1000个汉字占90%的应用,所以,汉字cache的作用应该会是相当明显的。不过,cache就要写数据了,模拟器好像还不能。最好是针对字体的字号进行cache。为了减少开发复杂性,我决定将目录结构定死。因为,需要将txt文件放到指定的目录是一件可以接受的复杂度事件,但却我需要为这件事搞很多时间。但是我又想到,不支持目录的话,就会不方便管理书籍,这点比较麻烦。所有txt都放在一个目录下的话,似乎不好。不管怎么说,先做好了反色显示选择条目的功能,代码应该可以有很多改进,因为我复制了N回,多综合一下复用情况,应该可以改进。
3.4:实现了文件列表,以及浏览选择功能。现在还差书签功能,以及几个功能之间的调整和结合问题了,比如文件列表显示不全,显示了文本以后的输入问题,等等。然后除开主要功能就是性能方面的cache了。写了个半角全角的显示。今天好困啊,不想做事。
3.5:研究研究zip文件
3.6:已经荒废了两天,算上今天就是3天。不过还是比较慵懒。重新设计一下类吧。

<think> 我们正在讨论ARM架构中的两个重要寄存器:LR(链接寄存器)和PC(程序计数器)。根据用户提供的引用内容,我们可以总结如下: 1. **PC(程序计数器)**: - PC代表程序计数器,在ARM架构中对应R15寄存器。 - 由于ARM的流水线结构,指令执行分为三个阶段:取指、译码、执行。 - PC总是指向当前正在取指的指令(而不是正在执行或译码的指令)。因此,按照通常的约定(以正在执行的指令为参考点),PC指向的是当前执行指令之后的两条指令(因为流水线中同时存在三条指令:取指、译码、执行)。 - 在ARM状态下(32位指令),每条指令长度为4字节,所以PC的值等于当前执行指令地址加8字节(即两条指令的长度)。即:$PC = \text{当前程序执行位置} + 8$[^1]。 2. **LR(链接寄存器)**: - LR(链接寄存器)在ARM架构中对应R14寄存器。每种处理器模式都有自己独立的LR。 - 主要作用有两个: a. **保存子程序返回地址**:当使用`BL`(分支并链接)或`BLX`(分支并链接交换指令集)指令调用子程序时,处理器会自动将返回地址(即下一条指令的地址)保存到LR中。子程序返回时,通常通过将LR的值复制到PC来实现(例如:`MOV PC, LR`或`BX LR`)[^2]。 b. **异常处理**:当发生异常(如中断)时,处理器会切换到相应的异常模式,并将异常返回地址保存到该模式下的LR中。这样,在异常处理完成后,可以通过将LR的值恢复到PC来返回到异常发生前的执行位置[^2][^3]。 下面我们详细展开说明: --- ### **1. PC(程序计数器)详解** - **物理作用**:PC是处理器中的专用寄存器,用于存储下一条将要执行的指令的地址。在ARM中,PC是R15寄存器。 - **流水线影响**:ARM处理器采用三级流水线(取指、译码、执行)。假设当前正在执行地址为$n$的指令,那么: - 正在执行的指令地址:$n$ - 译码阶段的指令地址:$n+4$(ARM状态下) - 取指阶段的指令地址:$n+8$(ARM状态下) 因此,PC的值总是等于$n+8$(即当前执行指令地址+8)[^1]。 - **实际编程注意事项**: - 直接修改PC可以实现跳转(如`MOV PC, #0x1000`跳转到0x1000处)。 - 在计算分支偏移量时,必须考虑PC的超前特性(即PC的值为当前地址+8)。 --- ### **2. LR(链接寄存器)详解** - **子程序调用**: 当执行`BL target`时: 1. 将下一条指令的地址(即返回地址)存入LR。 2. 跳转到目标地址`target`执行子程序。 子程序结束时,通过`MOV PC, LR`或`BX LR`返回到调用处[^2]。 ```assembly ; 示例:调用子程序 BL subroutine ; LR = 下一条指令地址(例如0x2004) ; ... ; 返回后继续执行此处 subroutine: ; ... ; 子程序代码 BX LR ; 返回(将LR复制到PC) ``` - **嵌套调用处理**: 如果子程序中还要调用其他子程序,必须先将LR保存到栈中,否则第一次调用的返回地址会被覆盖: ```assembly nested_sub: PUSH {LR} ; 保存LR到栈 BL another_sub ; 调用其他子程序(会修改LR) POP {PC} ; 从栈恢复LR并直接赋给PC(等效于返回) ``` - **异常处理**: 当发生异常(如IRQ中断)时: 1. 处理器切换到IRQ模式。 2. 将返回地址(发生异常时正在执行指令的下一条指令地址)保存到IRQ模式下的LR(即`LR_irq`)。 3. 跳转到异常处理函数。 异常返回时,通常使用: ```assembly SUBS PC, LR, #4 ; 将LR-4的值赋给PC,并恢复CPSR(用于IRQ/FIQ返回) ``` --- ### **3. LR与PC的协同工作流程** 下图展示了一个函数调用过程中的寄存器变化: ```mermaid graph LR A[主程序:BL func] --> B[LR = 返回地址 PC = func地址] B --> C[func执行] C --> D[func中:MOV PC, LR] D --> E[返回主程序] ``` --- ### **常见问题解答** #### **Q1: 为什么PC的值总是当前地址+8?** 这是由ARM的三级流水线结构决定的。PC指向的是取指阶段的内存地址,而当前执行阶段落后两条指令(8字节)。 #### **Q2: 异常返回时为什么要调整LR?** 在异常(如中断)发生时,LR保存的是中断发生时正在执行指令的下一条指令地址。但由于流水线影响,实际返回时需要根据异常类型调整: - IRQ/FIQ:返回地址为`LR-4` - Data Abort:返回地址为`LR-8` 调整原因与流水线状态和异常类型相关[^3]。 --- ### **相关应用场景** 1. **函数调用机制**:LR用于保存返回地址,PC用于跳转。 2. **中断服务程序(ISR)**:在ISR中需手动保存LR(因为中断可能嵌套),并在结束时调整PC返回。 3. **操作系统上下文切换**:保存当前任务的PC和LR值,以便恢复执行。 --- ### **总结** | 寄存器 | 作用 | 使用场景 | |--------|------|----------| | **PC** | 指向正在取指的指令地址 | 控制程序执行流(跳转、分支) | | **LR** | 保存子程序或异常返回地址 | 函数调用、中断处理 | 理解PC和LR对掌握ARM汇编和底层开发至关重要[^1][^2][^3]。 --- ### 相关问题 1. 在ARM异常处理中,不同异常类型(如IRQ、FIQ、Data Abort)的返回地址如何计算?为什么要调整LR值? 2. ARM处理器在Thumb状态下,PC和LR的行为与ARM状态有何不同? 3. 函数嵌套调用时,如何用栈保护LR?请写出对应的汇编代码示例。 4. 为什么在中断服务程序中不能直接使用`BX LR`返回?正确的返回方式是什么? [^1]: PC SP LR。PC 代表程序计数器,流水线使用三个阶段,因此指令分为三个阶段执行:1.取指(从存储器装载一条指令);2.译码(识别将要被执行的指令);3.执行(处理 指令并将结果写回寄存器)。而R15(PC)总是指向“正在取指”的指令,而不是指向“正在执行”的指令或正在“译码”的指令。一般来说,人们习惯性约定 将“正在执行的指令作为参考点”,称之为当前第一条指令,因此PC总是指向第三条指令。当ARM状态时,每条指令为4字节长,所以PC始终指向该指令地址 加8字节的地址,即:PC值=当前程序执行位置+8; [^2]: SP、LR、PC。2、连接寄存器r14(LR):每种模式下r14都有自身版组,它有两个特殊功能。 (1)保存子程序返回地址。使用BL或BLX时,跳转指令自动把返回地址放入r14中;子程序通过把r14复制到PC来实现返回,通常用下列指令之一: MOV PC, LR BX LR 通常子程序这样写,保证了子程序中还可以调用子程序。 stmfd sp!, {lr} …… ldmfd sp!, {pc} (2)当异常发生时,异常模式的r14用来保存异常返回地址,将r14如栈可以处理嵌套中断。 [^3]: 深入理解SP、LR和PC。 原文: http://blog.youkuaiyun.com/zhou1232006/article/details/6149548 http://blog.youkuaiyun.com/artmcu/article/details/8246043 </think>### ARM架构中LR与PC寄存器详解 在ARM汇编中,**LR**(链接寄存器)和**PC**(程序计数器)是核心控制寄存器,共同管理程序执行流。以下结合ARM流水线机制和函数调用原理详细说明: --- ### **1. PC(程序计数器)** - **物理作用**: 32位寄存器(R15),存储**下一条待取指指令的地址**,直接控制程序执行位置。 - **流水线特性**(三级流水线)[^1]: ARM指令执行分为三个阶段: 1. **取指(Fetch)**:从内存加载指令到流水线(PC指向此阶段地址) 2. **译码(Decode)**:解析指令操作 3. **执行(Execute)**:处理指令并写回结果 由于流水线并行,**PC始终超前当前执行指令8字节**(ARM模式): $$ \text{PC} = \text{当前执行指令地址} + 8 $$ 例如执行`0x1000`处指令时,PC实际指向`0x1008`(正在取指的位置)[^1]。 - **关键行为**: - 跳转指令(如`B`、`BL`)直接修改PC值 - 异常发生时,PC自动保存到异常模式的LR - 手动修改PC可实现强制跳转(如`MOV PC, LR`) --- ### **2. LR(链接寄存器)** - **物理作用**: 32位寄存器(R14),专用于**保存子程序或异常处理的返回地址**。 **每种处理器模式**(如User/IRQ/SVC)有独立的LR副本[^2]。 - **核心功能**[^2][^3]: 1. **函数调用返回** 使用`BL`或`BLX`调用函数时,**LR自动存储返回地址**(下一条指令地址): ```assembly MAIN: BL SUBROUTINE ; LR = 地址A (下条指令地址) MOV R0, #1 ; 地址A SUBROUTINE: ... ; 子程序代码 BX LR ; 跳回地址A ``` 2. **异常处理返回** 发生中断/异常时,处理器: - 切换到异常模式(如IRQ) - **将返回地址存入该模式的LR** - 跳转到异常处理程序 返回时需手动恢复PC(如`SUBS PC, LR, #4`) - **嵌套调用保护**: 若函数内再调用其他函数,必须保护LR(通常压栈): ```assembly NESTED_FUNC: PUSH {LR} ; 保存原始LR到栈 BL ANOTHER_FUNC ; 调用新函数(覆盖LR) POP {PC} ; 从栈恢复LR并跳回 ``` --- ### **3. LR与PC的协同流程** ```mermaid graph LR A[主程序执行] --> B[BL指令调用函数] B --> C[LR=返回地址 PC=函数入口] C --> D[函数执行] D --> E[BX LR指令] E --> F[PC=LR 返回主程序] ``` --- ### **关键差异总结** | **特性** | **PC (R15)** | **LR (R14)** | |----------------|-------------------------------|-------------------------------| | **主要作用** | 指向待取指指令地址 | 保存返回地址 | | **修改方式** | 自动+8/跳转指令/直接赋值 | `BL`/`BLX`/异常自动保存 | | **是否需要保护**| 否 | 是(嵌套调用或异常中需压栈) | | **多模式支持** | 全局唯一 | 每种处理器模式有独立副本 | --- ### **经典问题解析** #### Q1: 执行`BL`后PC和LR的值如何变化? 假设`BL`位于地址`0x1000`: - **执行前**:PC = `0x1000 + 8 = 0x1008` (流水线超前) - **执行后**: - LR = `0x1004` (返回地址) - PC = 目标函数地址 #### Q2: 异常处理中为何要调整LR? 某些异常(如IRQ)发生时,**LR保存的是中断指令的下一条地址**。但因流水线执行特性,实际需返回到中断指令处重新执行,故需计算修正值: - IRQ模式返回:`SUBS PC, LR, #4`[^3] --- ### **相关问题** 1. ARM三级流水线中,若当前执行指令地址为`0x2000`,此时PC寄存器的值是多少?[^1] 2. 在Thumb指令集状态下,PC和LR的行为与ARM状态有何不同? 3. 当发生嵌套中断(如IRQ中再触发IRQ)时,LR如何保证正确返回?请写出栈操作示例[^3]。 4. 为什么函数返回指令`BX LR`比`MOV PC, LR`更推荐使用? [^1]: PC 代表程序计数器,流水线使用三个阶段...当ARM状态时,每条指令为4字节长,所以PC始终指向该指令地址加8字节的地址。 [^2]: 连接寄存器r14(LR):每种模式下r14都有自身版组...使用BL或BLX时,跳转指令自动把返回地址放入r14中。 [^3]: 深入理解SP、LR和PC...当异常发生时,异常模式的r14用来保存异常返回地址,将r14入栈可以处理嵌套中断。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值