ICS-09

  • 本节介绍栈的基本结构及其三种用途:
    • 1)中断驱动I/O;
    • 2)一种算数运算机制;
    • 3)二进制补码与ASCII字符串之间的转换算法

栈的基本结构

  • 栈是一种ADT,且特点为LIFO

内存中的实现

  • 常见方式为一段连续内存空间和一个寄存器(栈指针)组成。栈指针,是一个寄存器,它的内容是一个地址值,始终指向栈的顶部(最近被压入的元素)。
  • 压入:每压入一个值到栈中,栈指针先递减,然后将数值存入它的地址位置。
    • PUSH ADD R6,R6,#-1
    •  STR R0,R6,#0
      
    • R6为栈指针,将R0内容存入R6对应内容的值
  • 弹出:每弹出一个元素,需要先通过栈指针提供的地址读取该数值,然后递增栈指针。
    • POP LDR R0,R6,#0
    • ADD R6,R6,#1
      
    • R6为栈指针,取出R6对应内容的值存入R0
  • 下溢出:栈内元素不足以弹出。常用方法是,子程序将执行成功或失败的信息记录在某个寄存器中。R5为0标识成功,否则失败
POP     LD R1,EMPTY
        ADD R2,R6,R1
        BRz Failure
        LDR R0,R6,#0
        ADD R6,R6,#1
        AND R5,R5,#0
        RET
Failure AND R5,R5,#0
        ADD R5,R5,#1
        RET
EMPTY .FILL xC000   ; EMPTY <-- -x4000
  • 上溢出:占空间已全部被占用。同POP程序一样,记录程序执行状态,并返回调用程序再处理。
PUSH    LD R1,MAX
        ADD R2,R6,R1
        BRz Failure
        ADD R6,R6,#-1
        STR R0,R6,#0
        AND R5,R5,#0
        RET
Failure AND R5,R5,#0
        ADD R5,R5,#1
        RET
MAX .FILL xC005     ; MAX <-- -x3FFB
  • 小结:通过子程序POP和PUSH,我们可以将5个内存位置x3FFF~x3FFB,设计为一个栈。如果压入程序,只需,将数据存入R0,执行JSR PUSH,如果弹出程序,只需JSR POP。如果希望改变栈大小,修改BASE和MAX的值即可
  • 最终代码如下
POP             ST R2,Save2
                ST R1,Save1
                LD R1,BASE
                ADD R1,R1,#-1
                ADD R2,R6,R1
                BRz fail_exit
                LDR R0,R6,#0
                ADD R6,R6,#1
                BRnzp success_exit
PUSH            ST R2,Save2
                ST R1,Save1
                LD R1,MAX
                ADD R2,R6,R1
                BRz fail_exit
                ADD R6,R6,#-1
                STR R0,R6,#0
success_exitLD  R1,Save1
                LD R2,Save2
                AND R5,R5,#0
                RET
fail_exit       LD R1,Save1
                LD R2,Save2
                AND R5,R5,#0
                ADD R5,R5,#1
                RET
BASE .FILL xC001
MAX .FILL xC005
Save1 .FILL x0000
Save2 .FILL x0000

中断驱动I/O(第二部分)

  • I/O数据传输的管理能力,传输机制
    • 中断服务程序的启动
    • 中断服务程序的执行
    • 中断服务程序的返回

启动和执行

  • 当一个I/O设备的优先级高于当前执行程序时,他在发出INT信号时将启动中断。从处理器角度,每执行完一条指令之后,都将检查是否存在INT。如果没有,则继续下一条指令的执行,如果检测到INT,则暂停下一条指令的读取
  • 此时,开始中断执行的准备工作,1)保存当前执行程序的状态; 2)装载中断服务程序的工作状态,开始中断请求的服务。
程序状态
  • 程序运行所涉及资源的快照。包括该程序的内存空间和通用寄存器的内容,以及两个重要的寄存器:PC和PSR寄存器(处理器状态寄存器)。
    • PSR[15]代表的是该程序的运行模式,特权模式或非特权模式。
      • 特权模式指能够访问一般程序不可访问的资源
    • PSR[10:8]代表正在执行程序的优先级别(priority level),PL0~PL7
    • PSR[2:0]代表条件码,PSR[2]=N,PSR[1]=Z,PSR[0]=P

被中断程序的状态保存
  • 中断启动的第一个任务就是保存正在运行程序的状态,以使得I/O设备服务完成后,程序可以继续运行。
    • 保存PC:可获取中断返回后的下一条待执行指令
    • 保存PSR:条件码后续程序可能依赖它;优先级提供被中断程序与其他程序相比的迫切程度;特权级别表明了被中断程序可以访问的资源范围
  • LC-3将这些内容保存在一个特殊的栈空间中,称为"超级用户栈",该栈空间进攻特权模式下的程序使用。所有程序都通过R6(栈指针)访问栈空间,超级用户栈也不例外。因此,内部寄存器Saved.SSP和Save.USP分别用于保护两个栈指针的内容。
  • 即在中断服务程序开始之前,R6已装入超级用户栈指针的内容。而PC寄存器和被中断程序的PSR寄存器内容将被压入超级用户栈

中断服务程序的状态装入
  • 大多数处理器采用"矢量中断"的工作方式。在中断情况下,I/O设备在发出中断时,将向处理器传递一个8-bit的矢量值。
  • 在多个设备同时请求中断处理的情况下,优先级最高被选中并传递给处理器,称为INTV。
  • INTV拓展为16-bit的地址,对应"中断矢量表"的某个表项地址。中断矢量表的内存位置是x0100~x01FF,每个表项包含一个中断服务程序的起始地址。
  • 处理器将扩展后中断矢量INTV的地址内容装入PC
  • PSR寄存器装入过程:PSR[2:0]内容初始化为0;PSR[15]设置为0,特权模式;PSR[10:8]设置为中断请求者(设备)的优先级别。
  • 至此,完成中断服务的启动阶段,之后可以运行中断服务程序。

中断服务
  • 此时,PC包含的是中断服务程序的起始地址,所以下一个开始执行的就是中断服务程序,即I/O设备的请求开始被服务。

中断返回

  • 中断服务程序的最后,是中断返回指令RTI。当处理器遇到RTI指令时,意味着I/O设备的请求已经完成。
    • RTI(1000):将PSR和PC的内容弹出超级用户栈,然后将它们填入处理器中正确的位置。

基于栈的算术运算

  • “栈式机"或"零地址机器”:硬件默认时将栈顶部的两个元素当做源操作数,并将它们弹出,送给ALU,之后的相加结果又压回栈顶。 (ADD)
  • 在栈式机中,程序员只需告诉计算机执行ADD,而微结构(microarchitecture)将负责所有的具体操作
  • 加法运算
OpADD       JSR POP       ;first operand
            ADD R5,R5,#0  ;successful?
            BRp Exit      ;if unsuccessful
            ADD R1,R0,#0  ;make room for next
            JSR POP       ;next operand
            ADD R5,R5,#0  ;successful?
            BRp Restore1  ;Resotre pointer
            ADD R0,R0,R1  ;Add
            JSR RangeCheck;chenk result
            BRp Restore2  ;Restore pointer
            JSR PUSH      ;push on the stack
            RET           ;next task
Restore2    ADD R6,R6,#-1
Restore1    ADD R6,R6,#-1
Exit        RET
RangeCheck  LD R5,Neg999
            ADD R4,R0,R5
            BRp BadRange
            LD R5,Pos999
            ADD R4,R0,R5
            BRn BadRange
            AND R5,R5,#0
            RET
BadRange    ST R7,Save
            LEA R0,RangeErrorMsg
            TRAP x22
            LD R7,Save
            AND R5,R5,#0
            ADD R5,R5,#1
            RET
Neg999      .FILL #-999
Pos999      .FILL #999
Save        .FILL x0000
RangeErrorMsg .FILL x000A
              .STRINGZ "Number is illegal"
  • 乘法运算
OpMult      AND R3,R3,#0
            JSR POP
            ADD R5,R5,#0
            BRp Exit
            ADD R1,R0,#0
            JSR POP
            ADD R5,R5,#0
            BRp Restore1
            ADD R2,R0,#0
            BRzp PosMultiplier
            ADD R3,R3,#1
            NOT R2,R2
            ADD R2,R2,#1
PosMultiplier AND R0,R0,#0
              ADD R2,R2,#0
              BRz PushMult

MultLoop    ADD R0,R0,R1
            ADD R2,R2,#-1
            BRp MultLoop

            JSR RangeCheck
            ADD R5,R5,#0
            BRp Restore2

            ADD R3,R3,#0
            BRz PushMult
            NOT R0,R0
            ADD R0,R0,#1
PushMult    JSR PUSH
            RET
Restore2    ADD R6,R6,#-1
Restore1    ADD R6,R6,#-1
Exit        RET
  • 取反运算
OpNeg       JSR POP
            ADD R5,R5,#0
            BRp Exit
            NOT R0,R0
            ADD R0,R0,#1
            JSR PUSH
Exit        RET

数据类型转换

ASCII->二进制

  • 按ASCII字符串方式,将数组存放在LC-3内存中标识ASCIIBUFF起始的三个连续地址中,R1记录起始内存中的有效位数。
  • 依次处理每位数字,对于每位数字,只取其二进制值的最低4位,然后以该值索引对应的数值查找表(不同的数位有不同的表),每个表项的内容是10个数字对应的数值。然后,将每位数值累加入R0。
ASCIItoBinary   AND R0,R0,#0  ;result
                ADD R1,R1,#0  ;test digits
                BRz DoneAtoB  ;no digits

                LD R3,NegASCIIOffset ;-x0030
                LEA R2,ASCIIBUFF

                ADD R2,R2,R1
                ADD R2,R2,#-1

                LDR R4,R2,#0 ;R4<--ones digit
                ADD R4,R4,R3
                ADD R0,R0,R4

                ADD R1,R1,#-1
                BRz  DoneAtoB
                ADD R2,R2,#-1

                LDR R4,R2,#0 ;R4<--tens digit
                ADD R4,R4,R3
                LEA R5,LookUp10
                ADD R5,R5,R4; R5 pointer
                LDR R4,R5,#0
                ADD R0,R0,R4

                ADD R1,R1,#-1
                BRz DoneAtoB
                ADD R2,R2,#-1

                LDR R4,R2,#0 ;R4<--hudreds
                ADD R4,R4,R3
                LEA R5,LookUp100
                ADD R5,R5,R4; R5 pointer
                LDR R4,R5,#0
                ADD R0,R0,R4

DoneAtoB        RET
NegASCIIOffset  .FILL xFFD0
ASCIIBUFF       .BLKW 4
LookUp10        .FILL #0
                .FILL #10
                .FILL #20
                .FILL #30
                .FILL #40
                .FILL #50
                .FILL #60
                .FILL #70
                .FILL #80
                .FILL #90
LookUp100       .FILL #0
                .FILL #100
                .FILL #200
                .FILL #300
                .FILL #400
                .FILL #500
                .FILL #600
                .FILL #700
                .FILL #800
                .FILL #900         
                                    

二进制->ASCII

  • R0数值范围为-999~+999,存放带转换的补码整数,转换后的ASCII字符串存放在ASCIIBUFF起始的4个连续内存位置
BinarytoASCII   LEA R1,ASCIIBUFF
                ADD R0,R0,#0
                BRn NegSign
                LD R2,ASCIIplus
                STR R2,R1,#0
                BRnzp Begin100
NegSign         LD R2,ASCIIminus
                STR R2,R1,#0
                NOT R0,R0
                ADD R0,R0,#1

Begin100        LD R2,ASCIIoffset
                LD R3,Neg100
Loop100         ADD R0,R0,R3
                BRn End100
                ADD R2,R2,#1
                BRnzp Loop100
End100          STR R2,R1,#1
                LD R3,Pos100
                ADD R0,R0,R3

                LD R2,ASCIIoffset
Begin10         LD R3,Neg10
Loop10          ADD R0,R0,R3
                BRn End10
                ADD R2,R2,#1
                BRnzp Loop10
End10           STR R2,R1,#2
                ADD R0,R0,#10
Begin1          LD R2,ASCIIoffset
                ADD R2,R2,R0
                STR R2,R1,#3
                RET
ASCIIplus       .FILL x002B
ASCIImius       .FILL x002D
ASCIIoffset     .FILL x0030
Neg100          .FILL xFF9C
Pos100          .FILL x0064
Neg10           .FILL xFFF6
<think>嗯,用户之前让我帮忙生成一个每天固定时间段的ICS文件,比如早上8点到11点40分,下午1点半到5点半。现在他们提到的是要把“前面C++后端学习的计划”生成ICS代码。我需要理解用户的需求。 首,用户之前可能已经有一个C++后端学习计划,可能包括不同的主题或任务,分布在不同的时间段。现在他们希望将这些计划转换成ICS格式,方便导到日历应用中,按时提醒学习。 我需要确认用户的具体计划结构。比如,是否有不同的学习模块,每个模块的时间安排是怎样的?是每天重复相同的安排,还是每周有不同的内容?比如,可能周一、周三、周五学习数据结构,周二、周四学习网络编程,或者类似的结构。 用户之前的例子是每天固定两个时间段,但这次可能更复杂,可能涉及不同的主题在不同时间段的安排。因此,可能需要创建多个VEVENT,每个对应不同的学习主题,并设置不同的RRULE,比如每周重复,或者特定星期几。 另外,用户可能没有明确说明时间安排的具体细节,比如开始日期、持续时间、是否有例外日期(如周末休息)等。这些都需要假设或询问用户,但根据之前的例子,可能需要假设一个开始日期,比如2024年9月1日,并默认每天重复,除非用户有其他要求。 还需要注意ICS文件的格式是否正确,比如时区设置、UID唯一性、DTSTAMP等字段。可能需要提供示例代码,并解释关键参数,方便用户修改。 另外,用户可能希望用Python自动生成,像之前的例子那样,或者直接给出ICS代码。需要根据用户的历史提问,判断他们可能更倾向于哪种方式。之前的回复中既有直接ICS代码,也有Python脚本,所以可能需要同时提供两种方法,或询问用户偏好,但根据当前问题,可能直接给出ICS示例更合适。 可能需要将C++后端学习计划分解为多个事件,每个事件对应一个学习主题,分布在不同的时间段。例如,上午学习一个主题,下午另一个主题,或者不同天有不同的安排。但用户可能希望每天固定时间学习不同的内容,或者每周循环。需要进一步假设,但如果没有更多信息,可能需要采用通用结构,例如每天重复相同的学习时间段,但不同事件名称,或者按周循环。 可能的例子是创建多个VEVENT,每个对应一个学习主题,使用RRULE设置重复频率,比如每周一、三、五学习主题A,周二、四学习主题B,周末休息。或者每天分为不同时段学习不同内容。 此外,需要确保每个VEVENT的UID唯一,DTSTAMP正确,时间格式正确,时区正确设置为Asia/Shanghai或其他用户所在时区。 总结步骤: 1. 假设用户的学习计划结构,比如每天分为不同时段学习不同主题,或每周不同天安排不同内容。 2. 根据假设的结构生成ICS代码示例,包含多个VEVENT,每个对应一个学习任务。 3. 提供关键参数说明,帮助用户修改日期、时间、重复规则等。 4. 可能提供Python脚本生成方法,方便自动化。 5. 注意事项部分提醒用户修改开始日期、时区、排除日期等。 需要确保回答清晰,结构合理,符合用户之前接受的格式,比如分章节、代码块、表格说明等。</think>根据您之前提供的C++后端学习计划,以下是生成ICS日历文件的完整解决方案: --- ### 一、ICS代码示例(含多科目循环) ```ics BEGIN:VCALENDAR VERSION:2.0 PRODID:-//C++ Learning Plan//CN BEGIN:VEVENT UID:cplusplus1@example.com DTSTAMP:20240720T000000Z DTSTART;TZID=Asia/Shanghai:20240901T080000 DTEND;TZID=Asia/Shanghai:20240901T113000 SUMMARY:C++核心语法(循环) RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR DESCRIPTION:学习指针/内存管理/模板编程 LOCATION:线上学习平台 END:VEVENT BEGIN:VEVENT UID:cplusplus2@example.com DTSTAMP:20240720T000000Z DTSTART;TZID=Asia/Shanghai:20240901T140000 DTEND;TZID=Asia/Shanghai:20240901T173000 SUMMARY:网络编程实践(循环) RRULE:FREQ=WEEKLY;BYDAY=TU,TH DESCRIPTION:Socket编程/协议实现 LOCATION:开发环境 END:VEVENT BEGIN:VEVENT UID:cplusplus3@example.com DTSTAMP:20240720T000000Z DTSTART;TZID=Asia/Shanghai:20240902T090000 DTEND;TZID=Asia/Shanghai:20240902T113000 SUMMARY:项目实战日 RRULE:FREQ=WEEKLY;BYDAY=SA DESCRIPTION:分布式系统开发实践 END:VEVENT END:VCALENDAR ``` --- ### 二、计划结构解析 ```mermaid gantt title C++学习日历结构 dateFormat YYYY-MM-DD section 常规学习 核心语法 :a1, 2024-09-01, 12w 网络编程 :a2, 2024-09-01, 12w section 项目实战 分布式系统 :crit, 2024-09-02, 12w ``` --- ### 三、Python动态生成(含注释) ```python from ics import Calendar, Event import pytz from datetime import datetime, timedelta def create_cpp_calendar(): tz = pytz.timezone("Asia/Shanghai") cal = Calendar() # 核心语法课程(周一、三、五) core_event = Event() core_event.uid = "cpp_core@learning" core_event.name = "C++核心语法" core_event.begin = tz.localize(datetime(2024,9,2,8,0)) # 首个周一 core_event.end = core_event.begin + timedelta(hours=3.5) core_event.rrule = "FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=36" # 三个月周期 core_event.description = "重点:智能指针/移动语义/模板元编程" # 网络编程课程(周二、四) network_event = Event() network_event.uid = "cpp_network@learning" network_event.name = "网络编程" network_event.begin = tz.localize(datetime(2024,9,3,14,0)) # 首个周二 network_event.end = network_event.begin + timedelta(hours=3.5) network_event.rrule = "FREQ=WEEKLY;BYDAY=TU,TH;COUNT=24" network_event.location = "实验环境:Ubuntu 22.04" cal.events.extend([core_event, network_event]) return cal # 生成ICS文件 calendar = create_cpp_calendar() with open("cpp_learning.ics", "w", encoding="utf-8") as f: f.write(calendar.serialize()) ``` --- ### 四、高级配置技巧 1. **弹性时间设置**:通过`RDATE`添加特殊加课 ```ics RDATE;TZID=Asia/Shanghai:20241007T080000 ``` 2. **进度跟踪**:添加进度标记 ```ics X-MICROSOFT-CDO-PERCENT-COMPLETE:50 ``` 3. **多设备同步**:建议添加提醒规则 ```ics BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION:课前提醒 END:VALARM ``` --- ### 五、使用建议 1. 用`VSCode`安装`ICS`插件可直接预览日历 2. 在手机日历中添加`WebDAV`账户实现自动同步 3. 每完成一个阶段后,可通过修改`RRULE`的`UNTIL`参数结束旧事件 生成文件后建议`Google Calendar`测试时间逻辑,确认无误后再同步到常用设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值