当前位置: 首页 > news >正文

【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR、流水线及伪指令

文章目录

  • 指令格式(重点)
    • 1. 立即数
    • 2. 寄存器位移
  • 一、数据传送指令
    • 1. MOV指令
    • 2. MVN指令
    • 3. LDR指令
  • 二、数据计算指令
    • 1. ADD指令
    • 1. SUB指令
    • 1. MUL指令
  • 三、位运算指令
    • 1. AND指令
    • 2. ORR指令
    • 3. EOR指令
    • 4. BIC指令
  • 四、比较指令
  • 五、跳转指令
    • 1. B/BL指令
    • 2. ldr指令
    • 练习
  • 六、内存访问指令
    • 1. 单内存访问指令
      • 练习
    • 2. 多内存访问指令
      • 示例
    • 3. 栈操作指令
      • 示例
  • 七、CPSR/SPSR操作指令
      • 练习
  • 八、ARM指令流水线分析及伪指令
    • 1. 最佳流水线
    • 2. 内存访问指令流水线
    • 3. 分支流水线
    • 4. ARM伪指令、汇编与C混合编程、Volatile关键字
      • (1)LDR R0,=0x12345678分析
      • (2)LDR R0,=Label 分析
      • (3)LDR R0,Label
      • (4)ADR R0,Label分析
      • (5)如何判别代码在实际内存中运行的地址?


指令格式(重点)

在这里插入图片描述

1. 立即数

一个常数,该常数必须对应8位位图,即一个8位的常数通过,循环右移偶数位得到该数,该数
数为合法立即数。

在指令中表示方法:#数字,例如:#100

快速判定是否是合法立即数:

  • 首先将这个数转换为32bit的16进制形式,例如218=0xDA=0x000000DA
  • 除零外,仅有一位数为合法立即数
  • 除零外,仅有二位数,并且相邻(包括首尾,如0x1000000A)的为合法立即数。
  • 除零外,仅有三位数,并且相邻(包括中间有0相间,例如0x10800000,包括首尾相邻
    如:0x14000003),这三位数中,最高位取值仅能为1、2、3,最低位取值仅能为4、8、C
    中间位0x0~0xF。
    这种组合的为合法立即数。

2. 寄存器位移

将寄存器值读取之后,进行移位运算后,作为操作数2参与运算。支持的移位方式如下:

  • LSL(Logical shift Left)逻辑右移
  • LSR(Logical shift Right)逻辑左移
  • ASR(Arithmetic shift Right)算术右移
r0,lsr #4 表示r0 >>4
r0,lsr r1 表示r0 >>r1
#3,LsL #4 错误,只能是寄存器移位,不能是立即数移位

一、数据传送指令

1. MOV指令

格式:mov 目标寄存器,操作数2
功能:将操作数2的值赋值给目标寄存器

在这里插入图片描述

2. MVN指令

格式:mvn 目标寄存器,操作数2
功能:将操作2取反的值给目标寄存器

在这里插入图片描述

3. LDR指令

格式: LDR 目标寄存器,= 数据
功能: 完成任意的数据传送到目标寄存器
注意: 数据前面不能加#,因为此时数据不按立即数来处理

在这里插入图片描述

二、数据计算指令

1. ADD指令

格式: add 目标寄存器,操作数1操作数2
功能: 将操作数1加上操作数2的结果给目标寄存器
在这里插入图片描述

1. SUB指令

格式: sub 目标寄存器,操作数1操作数2
功能: 将操作数1减去操作数2的结果给目标寄存器

在这里插入图片描述

1. MUL指令

格式: mul 目标寄存器,操作1操作2
功能: 将操作数1乘以操作数2的结果存放在目标寄存器

注意:操作数1操作2必须都是寄存器,并且操作1的寄存器编号不能和目标寄存器一样

在这里插入图片描述

三、位运算指令

1. AND指令

格式: and 目标寄存器,操作数1操作数2
功能: 将操作数1按位与操作数2的结果存放在目标寄存器
在这里插入图片描述

2. ORR指令

格式: orr 目标寄存器,操作数1操作数2
功能: 将操作1按位或操作2的结果存放在目标寄存器

3. EOR指令

格式: eor 目标寄存器,操作1操作2
功能: 将操作数1按位异或操作数2的结果存放在目标寄存器

在这里插入图片描述

4. BIC指令

格式: bic 目标寄存器,操作1操作2
功能: 将操作数1按位与操作数2取反的结果存放在目标寄存器
目标寄存器 = 操作数1 & ~操作数2

在这里插入图片描述

四、比较指令

格式: cmp 寄存器,操作数2
等于寄存器减去操作数2
功能: 将寄存器的值与操作2比较,比较的结果会自动影响CPSR的NZCV

在这里插入图片描述

答案

在这里插入图片描述

五、跳转指令

1. B/BL指令

格式: B/BL 标签
功能: 跳到一个指定的标签,BL 跳转之前,将跳转前的PC的值保存在LR,跳转范围+/- 32M
在这里插入图片描述

NZCV 标志位

标志位含义
N (Negative)结果为负数(Rn < Rm)
Z (Zero)结果为 0(Rn == Rm)
C (Carry)发生借位(无符号比较时 Rn < Rm)
V (Overflow)溢出(有符号计算超出范围)

比较指令 + B 条件跳转

指令条件说明
BEQ labelZ == 1相等(Rn == Rm)时跳转
BNE labelZ == 0不相等(Rn ≠ Rm)时跳转
BGT labelZ == 0 且 N == V大于(Rn > Rm,有符号)时跳转
BGE labelN == V大于等于(Rn ≥ Rm,有符号)时跳转
BLT labelN ≠ V小于(Rn < Rm,有符号)时跳转
BLE labelZ == 1 或 N ≠ V小于等于(Rn ≤ Rm,有符号)时跳转
BHI labelC == 1 且 Z == 0大于(Rn > Rm,无符号)时跳转
BHS labelC == 1大于等于(Rn ≥ Rm,无符号)时跳转
BLO labelC == 0小于(Rn < Rm,无符号)时跳转
BLS labelC == 0 或 Z == 1小于等于(Rn ≤ Rm,无符号)时跳转

2. ldr指令

格式: ldr pc,= 标签名
功能: 将PC指针指闻标签表示的地址
在这里插入图片描述

练习

在这里插入图片描述

答案

在这里插入图片描述

六、内存访问指令

1. 单内存访问指令

LDR 将内存中的值加载到寄存器(读内存)
STR 将寄存器的内容写入内存(写内存)

寄存器间接寻址:寄存器的值是一个地址

LDR ro,[r1 ]     //r0 = *r1
STR ro,[ r1 ] //*r1 = ro

基址变址寻址:将基地址寄存器加上指令中给出的偏移量,得到数据存放的地址

  • A. 前索引
STR r0,[r1,#4] //*(r1 + 4)= r0
LDR r0,[r1,#4] //r0 =*(r1+ 4)
  • B. 后索引
STR r0,[r1],#4   //*r1=r0 &&r1=r1 + 4
LDR r0,[r1],#4   //r0=*r1 &&r1=r1 + 4
  • C. 自动索引
STR r0,[r1,#4]!    //*(r1+4)=r0&&r1=r1+4
LDR r0,[r1,#4]!    //r0=*(r1+4)&&r1 =r1+4

示范:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

练习

将1-10数据存放在基地址为0x4000,0000,将0x4000,0000起始地址的值拷贝到0x4000,0100

答案
在这里插入图片描述

将0x1234写到0x4000,0000,将0xabcd写到0x4000,0004,然后从这两个地址读取数据做案加,最终结果存放在r0

答案2
在这里插入图片描述

2. 多内存访问指令

LDM 将一块内存的数据,加载到多个寄存器中
STM 将多个寄存器的值,存储到一块内存

格式:

LDM{条件}{s}<MODE>基址寄存器{!},{Reglist}^
STM{条件}{s}<MODE>基址寄存器{!},{Reglist}^

mode说明
IA后增加地址
IB先增加地址
DA后减少地址
DB先减少地址

基址寄存器
用于放内存的起始地址

!
最后更新基址寄存器的值

Reglist

  • 多个寄存器,从小到大,中间用 , 隔开,如 {r0,r2,r3}{r0-r7,r10}
  • 寄存器号大的对应内存的高地址,寄存器号小的对应内存的低地址

^

  • 它存在,如果 Reglist 没有 pc 的时候,这个时候操作的寄存器是用户模式下的寄存器
  • LDM 指令中,有 PC 的时候,在数据传送的时候,会将 SPSR 的值拷贝到 CPSR,用于异常的返回

流程图:
在这里插入图片描述
在这里插入图片描述

示例

在这里插入图片描述

3. 栈操作指令

A. 进栈

stmfd sp!,{寄存器列表}

B. 出栈

Idmfd sp!,{寄存器列表}

注意
在对栈操作之前,必须先设置sp的值,进栈和出栈的方式一样,ATPCS标准规定满减栈

流程图:
在这里插入图片描述

堆栈指针指向最后压入的堆栈的有效数据项,称为满堆栈
堆栈指针指向下一个待压入数据的空位置,称为空堆栈

在这里插入图片描述

示例

在这里插入图片描述

七、CPSR/SPSR操作指令

A. 读操作

MRS Rn,CPSR/SPSR
将状态寄存器的值,读到通用寄存器中

B. 写操作

MSR CPSR/SPSR,Rn
将通用寄存器的值,写到状态寄存器

练习

A.写一段代码,将CPSR的第I(7)位清0,其他位不变(使能IRQ异常)
B.写一段代码,将CPSR的第I(7)位置1,其他位不变(禁用IRQ异常)

答案
在这里插入图片描述

八、ARM指令流水线分析及伪指令

在ARM核中,为增加处理器指令流的速度,ARM7系列使用3级流水线。允许多个操作同时处理,而非顺序执行。不同的ARM核,流水线的级数是不一样的,ARM核版本越高,流水线级数越多。对于软件工程师编程而言,统一按照三级流水线来分析就可以了。

PC指向正被取指的指令,而非正在执行的指令

在这里插入图片描述

1. 最佳流水线

在这里插入图片描述

该例中用5个时钟周期执行了5条指令,所有的操作都在寄存器中(单周期执行)
指令周期数(CPI)=1

2. 内存访问指令流水线

在这里插入图片描述

该例中,用6周期执行了4条指令,指令周期数(CPI)=1.5

3. 分支流水线

在这里插入图片描述

4. ARM伪指令、汇编与C混合编程、Volatile关键字

伪指令定义:
为了方便程序员使用,编译器设计的指令,这个指令ARM核无法直接识别,需要编译器对他翻译成ARM核所能识别的指令。

(1)LDR R0,=0x12345678分析

再次强调:PC指向正被取指的指令,而非正在执行的指令

如何看内存中的12345678
正在读取的LDR内存是0x0008 加上 PC所在的地址(因为LDR正在执行 所以pc等于0x0000000C预取的值)
也就是0x0008加上pc的值0x0000000C等于0x00000014

在这里插入图片描述

总结

编译器在编译的时候,将Idr r0,=0x12345678翻译成了ldr r0,[pc,#0x0008]这一条读内存的指令。根据PC的值加上偏移量算出0x12345678这个数据在内存的地址,然后使用Idr指令读取这个地址的数据。

(2)LDR R0,=Label 分析

1) 链接地址指定为0x0情况分析

0x00000018等于0x000C加上pc的值0x000C

注意 0x00000018的值是14 这是个值 是编译器算出来的一个值

在这里插入图片描述

在这里插入图片描述

2) 链接地址指定为0x2000情况分析

修改链接地址
在这里插入图片描述

再运行

label的地址也就是0x000c+pc的值0x0000200c=0x00002018

在这里插入图片描述

3) 总结

LDR r0,=Label指令表示将Label的值写入r0,Label的值由指定的代码段运行地址(-Ttext=地址值)来决定。

编译器做法:

  • 首先根据指定的代码段开始的地址,算出Label标签对应的地址值
  • 然后将这个表示的地址值存放在一个位置
  • 生成内存访问指令,根据pc +固定偏移量,找到标签对应值存放的位置

注意
当代码编译结束的时候,标签表示的地址值(根据指定的代码段地址)已经编译死存放在程序文件中了。

(3)LDR R0,Label

LDR R0,Label 表示读取Label表示的地址对应数据

不带=的时候 存的是标签里的内容

在这里插入图片描述

(4)ADR R0,Label分析

动态方式 根据pc的值+0x00000008

之前是静态的 在编译完的时候 label就已经确定值是什么了
这个是动态

举个例子:如果是用LDR我把这个代码放到A内存和B内存运行
这两块内存的值是一模一样的 因为在编译完的时候 label就已经确定值是什么了
如果是ADR A内存的0x0008 和B内存的0x0008 是不一样的
有点难理解

在这里插入图片描述

ADR R0,Label指令表示根据当前的PC的值 +/-偏移量,动态获取当前Label所表示的内存地址

(5)如何判别代码在实际内存中运行的地址?

ADR r0,_start 可以知道,因为他是根据pc的值,动态获取
LDR r0,=_start 无法知道,这条指令不论在哪里运行,r0的值都是固定(取决于指定的链接地址)

相关文章:

【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR、流水线及伪指令

文章目录 指令格式&#xff08;重点&#xff09;1. 立即数2. 寄存器位移 一、数据传送指令1. MOV指令2. MVN指令3. LDR指令 二、数据计算指令1. ADD指令1. SUB指令1. MUL指令 三、位运算指令1. AND指令2. ORR指令3. EOR指令4. BIC指令 四、比较指令五、跳转指令1. B/BL指令2. l…...

Jackson中@JsonTypeId的妙用与实例解析

在日常的Java开发中&#xff0c;Jackson库是处理JSON数据的常用工具。其中&#xff0c;JsonTypeId注解是一个非常实用的功能&#xff0c;它可以帮助我们更好地控制多态类型信息在序列化过程中的表现。今天&#xff0c;我们就来深入探讨一下JsonTypeId的用法&#xff0c;并通过具…...

Ubuntu 顶部状态栏 配置,gnu扩展程序

顶部状态栏 默认没有配置、隐藏的地方 安装使用Hide Top Bar 或Just Perfection等进行配置 1 安装 sudo apt install gnome-shell-extension-manager2 打开 安装的“扩展管理器” 3. 对顶部状态栏进行配置 使用Hide Top Bar 智能隐藏&#xff0c;或者使用Just Perfection 直…...

Java---入门基础篇(上)

前言 本片文章主要讲了刚学Java的一些基础内容,例如注释,标识符,数据类型和变量,运算符,还有逻辑控制等,记录的很详细,带你从简单的知识点再到练习题.如果学习了c语言的小伙伴会发现,这篇文章的内容和c语言大致相同. 而在下一篇文章里,我会讲解方法和数组的使用,也是Java中基础…...

Linux C++

一、引言 冯诺依曼架构是现代计算机系统的基础&#xff0c;它的提出为计算机的发展奠定了理论基础。在学习 C 和 Linux 系统时&#xff0c;理解冯诺依曼架构有助于我们更好地理解程序是如何在计算机中运行的&#xff0c;包括程序的存储、执行和资源管理。这对于编写高效、可靠的…...

gradio 合集

知识点 1&#xff1a;基本 Chatbot 创建 import gradio as gr 定义历史记录 history [gr.ChatMessage(role“assistant”, content“How can I help you?”), gr.ChatMessage(role“user”, content“What is the weather today?”)] 使用历史记录创建 Chatbot 组件 ch…...

996引擎 - NPC-动态创建NPC

996引擎 - NPC-动态创建NPC 创建脚本服务端脚本客户端脚本添加自定义音效添加音效文件修改配置参考资料有个小问题,创建NPC时没有控制朝向的参数。所以。。。自己考虑怎么找补吧。 多重影分身 创建脚本 服务端脚本 Mir200\Envir\Market_Def\test\test001-3.lua -- NPC八门名…...

论文阅读(十三):复杂表型关联的贝叶斯、基于系统的多层次分析:从解释到决策

1.论文链接&#xff1a;Bayesian, Systems-based, Multilevel Analysis of Associations for Complex Phenotypes: from Interpretation to Decision 摘要&#xff1a; 遗传关联研究&#xff08;GAS&#xff09;报告的结果相对稀缺&#xff0c;促使许多研究方向。尽管关联概念…...

代码随想录算法训练营第三十九天-动态规划-198. 打家劫舍

动规五部曲 dp[i]表示在下标为i的房间偷或不偷与前面所偷之和所能获得的最大价值递推公式&#xff1a;dp[i] std::max(dp[i - 2] nums[i], dp[i - 1])初始化&#xff1a;要给dp[0]与dp[1]来给定初始值&#xff0c;因为递推公式有-1与-2。dp[0] nums[0], dp[1] std::max(num…...

CF1098F Ж-function

【题意】 给你一个字符串 s s s&#xff0c;每次询问给你 l , r l, r l,r&#xff0c;让你输出 s s s l , r sss_{l,r} sssl,r​中 ∑ i 1 r − l 1 L C P ( s s i , s s 1 ) \sum_{i1}^{r-l1}LCP(ss_i,ss_1) ∑i1r−l1​LCP(ssi​,ss1​)。 【思路】 和前一道题一样&#…...

Python 函数魔法书:基础、范例、避坑、测验与项目实战

Python 函数魔法书&#xff1a;基础、范例、避坑、测验与项目实战 内容简介 本系列文章是为 Python3 学习者精心设计的一套全面、实用的学习指南&#xff0c;旨在帮助读者从基础入门到项目实战&#xff0c;全面提升编程能力。文章结构由 5 个版块组成&#xff0c;内容层层递进…...

vim交换文件的作用

1.数据恢复&#xff1a;因为vim异常的退出&#xff0c;使用交换文件可以恢复之前的修改内容。 2.防止多人同时编辑&#xff1a;vim检测到交换文件的存在,会给出提示&#xff0c;以避免一个文件同时被多人编辑。 &#xff08;vim交换文件的工作原理&#xff1a;vim交换文件的工作…...

[NOI1995] 石子合并

[NOI1995] 石子合并 题目描述 在一个圆形操场的四周摆放 N N N 堆石子&#xff0c;现要将石子有次序地合并成一堆&#xff0c;规定每次只能选相邻的 2 2 2 堆合并成新的一堆&#xff0c;并将新的一堆的石子数&#xff0c;记为该次合并的得分。 试设计出一个算法,计算出将 …...

真正的智能与那只蝴蝶

“蝴蝶效应”可以展开为对智能本质与大算力关系的追问&#xff0c;其中“蝴蝶”作为隐喻可能指向多重维度——从混沌理论的“蝴蝶效应”到庄子“物我两忘”的蝴蝶之梦。这种并置本身暗示了智能与宇宙秩序、认知边界之间的深刻张力。以下从三个层面展开分析&#xff1a;一、混沌…...

C++小病毒-1.0勒索(更新次数:2)

内容供学习使用,不得转卖,代码复制后请1小时内删除,此代码会危害计算机安全,谨慎操作 在C20环境下,并在虚拟机里运行此代码!&#xff0c;病毒带来后果自负! 使用时请删除在main()里的注释,并修改位置至C:\\(看我代码注释)//可以改成WIN Main() #include <iostream> #i…...

Node.js 的底层原理

Node.js 的底层原理 1. 事件驱动和非阻塞 I/O Node.js 基于 Chrome V8 引擎&#xff0c;使用 JavaScript 作为开发语言。它采用事件驱动和非阻塞 I/O 模型&#xff0c;使其轻量且高效。通过 libuv 库实现跨平台的异步 I/O&#xff0c;包括文件操作、网络请求等。 2. 单线程事…...

基于Django的豆瓣影视剧推荐系统的设计与实现

【Django】基于Django的豆瓣影视剧推荐系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统采用了Python作为后端开发语言&#xff0c;采用Django作为后端架构&#xff0c;结…...

P10638 BZOJ4355 Play with sequence Solution

Description 给定 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1​,a2​,⋯,an​)&#xff0c;有 m m m 个操作&#xff0c;分以下三种&#xff1a; assign ⁡ ( l , r , k ) \operatorname{assign}(l,r,k) assign(l,r,k)&#xff1a;对每个 i ∈ [ l , r ] i \…...

MySQL误删数据怎么办?

文章目录 1. 从备份恢复数据2. 通过二进制日志恢复数据3. 使用数据恢复工具4. 利用事务回滚恢复数据5. 预防误删数据的策略总结 在使用MySQL进行数据管理时&#xff0c;误删数据是一个常见且具有高风险的操作。无论是因为操作失误、系统故障&#xff0c;还是不小心执行了删除命…...

项目测试之MockMvc

文章目录 基础基础概念Mockxxx一般实现文件位置 实战MockMvc与Test注解不兼容RequestParams参数RequestBody参数 基础 基础概念 定义&#xff1a;是Spring框架提供的一种用于测试Spring MVC控制器的工具&#xff0c;它允许开发者在不启动完整的web服务器的情况下&#xff0c;…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

Linux-进程间的通信

1、IPC&#xff1a; Inter Process Communication&#xff08;进程间通信&#xff09;&#xff1a; 由于每个进程在操作系统中有独立的地址空间&#xff0c;它们不能像线程那样直接访问彼此的内存&#xff0c;所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...

网页端 js 读取发票里的二维码信息(图片和PDF格式)

起因 为了实现在报销流程中&#xff0c;发票不能重用的限制&#xff0c;发票上传后&#xff0c;希望能读出发票号&#xff0c;并记录发票号已用&#xff0c;下次不再可用于报销。 基于上面的需求&#xff0c;研究了OCR 的方式和读PDF的方式&#xff0c;实际是可行的&#xff…...

背包问题双雄:01 背包与完全背包详解(Java 实现)

一、背包问题概述 背包问题是动态规划领域的经典问题&#xff0c;其核心在于如何在有限容量的背包中选择物品&#xff0c;使得总价值最大化。根据物品选择规则的不同&#xff0c;主要分为两类&#xff1a; 01 背包&#xff1a;每件物品最多选 1 次&#xff08;选或不选&#…...

用鸿蒙HarmonyOS5实现国际象棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的国际象棋小游戏的完整实现代码&#xff0c;使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├── …...