68000汇编实战01-编程基础

文章目录

  • 简介
    • 产生背景
    • 应用领域
  • 语言学习
    • EASy68K
      • 帮助文档
      • IDE使用
    • 编程语言
      • comments
      • labels
        • 开始标签
        • 指令标签
        • 位置标签
      • opcode 操作码
        • 常用操作码
          • 数据传送
          • 算术运算
          • 逻辑运算
          • 控制流
            • 分支跳转
            • 地址跳转
            • 子程序跳转
          • 位操作
          • 比较
          • 堆栈操作
        • IO操作码
        • 其他操作码
      • directives 指令
        • DC指令
        • EQU 指令
        • SET 指令
        • DS 指令
        • 其他指令
      • 寄存器
        • 程序计数器(PC)
        • 状态寄存器(SR)
          • 状态寄存器的结构
          • 状态寄存器的作用
          • 示例
        • 数据寄存器(D)
          • D 寄存器的特点
          • D 寄存器的使用场景
          • 示例
        • 地址寄存器(A)
          • A 寄存器的特点
          • A 寄存器的使用场景
          • 示例
        • 堆栈寄存器(SS)
          • 堆栈操作
          • std函数模拟
      • 案例(9*9乘法表)

简介

68000 汇编语言是为 Motorola 68000 微处理器设计的低级编程语言。68000 微处理器于 1979 年推出,因其强大的性能和灵活的架构而广泛应用于多种计算机系统和嵌入式设备中。以下是对 68000 汇编语言的背景、应用领域以及学习它的好处的详细介绍。

产生背景

  1. 技术进步

    • 68000 微处理器是 16 位架构,具有 32 位的地址总线,能够寻址高达 4GB 的内存。这使得它在当时的微处理器中具有较高的性能和灵活性。
    • 其设计采用了复杂指令集计算(CISC)架构,支持多种寻址模式和丰富的指令集。
  2. 市场需求

    • 1970 年代末和1980年代初,个人计算机和嵌入式系统的需求迅速增长。68000 微处理器因其性能和成本效益被广泛采用。
    • 许多知名的计算机系统(如 Apple Macintosh、Atari ST 和 Sega Genesis)都使用了 68000 处理器。

应用领域

  1. 个人计算机

    • 68000 微处理器被用于早期的个人计算机,如 Apple Macintosh 和 Atari ST。这些系统的操作系统和应用程序通常使用 68000 汇编语言进行开发。
  2. 嵌入式系统

    • 68000 处理器也被广泛应用于嵌入式系统,如工业控制、汽车电子和消费电子产品。
  3. 游戏机

    • Sega Genesis 和其他游戏机使用 68000 处理器,许多经典游戏都是用 68000 汇编语言编写的,学习后可以做一些hackrom的实战。
  4. 实时系统

    • 由于其高效的性能,68000 处理器在实时系统中也得到了应用,如医疗设备和航空航天系统。

语言学习

EASy68K

EASy68K 是一个 68000 结构化汇编语言集成开发环境(IDE)。EASy68K 允许您在 Windows PC 或 Wine 上编辑、汇编和运行 68000 程序。无需额外的硬件。EASy68K 是一个开源项目,根据 GNU 通用公共使用许可分发。
使用easy68k方便我们学习整套68000的编程和调试,学习这件基础知识,对我们hackrom或者逆向的基础。
下载地址:http://www.easy68k.com/files/SetupEASy68K.exe
安装完成后的目录结构
在这里插入图片描述

帮助文档

helm.chm提供了完整的编程和调试工具以及68k语言的学习入门资料,可以直接从该文档入手。
在这里插入图片描述

IDE使用

打开EDIT68K.exe,菜单file->new x68 source file 。
在source里面实现一个功能,打印helloworld,并从空值台输入一个字符串并打印。

关于指令,标签,寄存器其他相关的内容请移步后续章节。

源代码

*-----------------------------------------------------------
* Title      :
* Written by :
* Date       :
* Description:
*-----------------------------------------------------------ORG    $1000 ;告诉编译器代码从1000位置开始,不指定默认从0开始
START:                  ; first instruction of program* 将text字符串地址写给A1lea text,A1* 将14号task print 给d0,并执行,14号任务自动获取A1地址的数据并打印move #14,D0trap #15* 执行2号任务,从输入流获取输入,自动写入到A1move #2,D0trap #15* 打印A1地址内容move #14,D0trap #15* Put program code here
*-----------------------------------------------------------
*HELLO:这是一个标签,标识字符串数据的起始位置。
*DC.B:这是一个伪指令,表示“定义常量(Define Constant)”,后面的 .B 表示定义的是字节(Byte)数据。
*'Hello World':这是一个字符串常量,表示字符数组。每个字符占用一个字节。
*$D:这是一个十六进制常量,表示一个字节的值。$D 的十进制值是 13,通常表示回车符(Carriage Return)。
*$A:这是一个十六进制常量,表示一个字节的值。$A 的十进制值是 10,通常表示换行符(Line Feed)。
*0:这是一个字节的值,表示字符串的结束符(null terminator),在 C 语言中常用来标识字符串的结束。
*-----------------------------------------------------------    
text dc.b  'helloworld',0SIMHALT             ; halt simulator* Put variables and constants hereEND    START        ; last line of source

点击工具栏运行按钮(如果由错误会有提示,根据情况修正)
在这里插入图片描述
会弹出一个确认框
在这里插入图片描述
点击execute
在这里插入图片描述
绿色圈圈点击变成红色可下断点,F9运行,F8 stepover,F7 stepinto,点击运行可调试。
在这里插入图片描述

在view可打开内存窗口,栈窗口等
在这里插入图片描述

编程语言

汇编语言程序由以下部分组成:

  • labels 标签 - 用户创建的名称,用于标记程序中的位置。
  • opcode 操作码 - 微处理器可以执行的特定指令,比如ADD,MOVE等。
  • operands 操作数 - 某些指令所需的附加数据,比如#1表示10进制立即数1,$1表示16进制的1。
  • directives 指令 - 发给汇编器的命令,比如ORG $1000,告诉编译器,代码的开始位置,代码段不占用空间,类似于c语言的宏,编译阶段使用。
  • macros 宏 - 用户创建的源代码集合,可以在编写程序时轻松重用。
  • comments 注释 - 用户创建的文本字符串,用于记录程序。
  • 寄存器:汇编语言编程需要与微处理器进行直接交互。68000 微处理器包含八个数据寄存器 D0 到 D7。数据寄存器是通用的,可以视为 8 位、16 位或 32 位的整数变量。还有八个地址寄存器 A0 到 A7,地址寄存器的长度为 32 位。它们通常用于引用变量。状态寄存器(SR)包含状态标志,用于指示比较的结果。

以下是一个例子
在这里插入图片描述

comments

在 Motorola 68000(68k)汇编语言中,注释用于帮助程序员理解代码的功能和逻辑。68k 汇编语言的注释格式如下:(*或者;开头的为注释)

* Date    
TRAP #15  ;将3任务执行,自动打印D1的内容

labels

标签用于通过名称标识程序中的位置或内存位置。需要位置的指令或指令可以使用标签来指示该位置。标签通常在行的第一列开始,必须以空格、制表符或冒号结束。如果使用冒号,它不会成为标签的一部分。如果标签没有在第一列开始,则必须以冒号结束。标签的前 32 个字符是有效的。标签有两种类型:全局标签和局部标签。

全局标签可以在程序的任何地方被引用。因此,全局标签必须是唯一的名称。全局标签应以字母开头,后面可以跟字母、数字或下划线。局部标签可以在程序中重复使用。局部标签必须以点 ‘.’ 开头,后面可以跟字母、数字或下划线。全局标签定义了局部标签的边界。当定义局部标签时,只有在遇到下一个全局标签之前,才能从局部标签上方或下方的代码中引用它。汇编器通过将局部标签名称附加到前面的全局标签并用冒号 ‘:’ 替换点来创建局部标签的唯一名称。结果名称的前 32 个字符是有效的。

开始标签

标签可以用来指定程序的起始位置。如果标签 START 指定了程序的起始位置,那么 END 指令的写法如下:

START:                    Start of programcodeEND    START
指令标签

标签常常放在某个指令前用来表示,定义变量,标签指向存储数据的首地址。
DC - DC 指令指示汇编器将后续的值放入当前内存位置。该指令有三种形式:DC.B 用于字节数据,DC.W 用于字(16 位)数据,DC.L 用于长(32 位)数据。定义常量指令不应与 C++ 中声明常量混淆。
例如

       ORG    $1000              start of the data region 
depart DC.B   'depart.wav',0     stores as a NULL terminated string in consecutive bytes DC.L   $01234567          the value $01234567 is stored as a long wordDC.W   1,2                two words are stored as $0001 and $0002DC.L   1,2                two long words are stored as $00000001 and $00000002 

depart 就是一个label是这块内存区域的首地址。

内存结果

00001000  64 65 70 61 72 74 2E 77 61 76 00 
0000100C  01234567 
00001010  0001 0002 
00001014  00000001 00000002

其他关于指令标签的用法参考,也可以到指令章节:
在这里插入图片描述

位置标签

可以定义一些位置标签,当进行特殊操作时,可以通过控制流opcode跳转到位置标签
实现一个从0,end_index的循环打印

    ORG    $1000
START:                  ; first instruction of program* Put program code here
* 实现一个从0,end_index的循环打印move #1,D1
t:move #3,D0TRAP #15  ;将3任务执行,自动打印D1的内容add.b #1,d1  ;让d1+1CMP #end_index,d1 ;比较d1和end_index的值BNE t  ;如果不相等继续跳转到t label执行SIMHALT             ; halt simulator* Put variables and constants here
end_index equ 10END    START        ; last line of source

opcode 操作码

在 68K 汇编语言中,操作码(opcode)是指令的核心部分,定义了要执行的操作。以下是一些常用的 68K 操作码及其功能:

常用操作码

注意大部分操作码都可以添加结尾.W表示字(2个字节16位).L表示双字(4个字节32位),.B(1个字节8位)

数据传送
- `MOVE`:将数据从一个位置移动到另一个位置。- 例:`MOVE.W D0, D1`(将 D0 的值移动到 D1)
- `MOVEA`:将地址从一个位置移动到另一个位置。- 例:`MOVEA.L A0, A1`(将 A0 的地址移动到 A1)
算术运算
- `ADD`:将两个操作数相加。- 例:`ADD.W D0, D1`(将 D0 的值加到 D1)
- `SUB`:从一个操作数中减去另一个操作数。- 例:`SUB.W D1, D0`(从 D0 中减去 D1)
- `MULS`:有符号乘法。- 例:`MULS D0, D1`(将 D0 和 D1 相乘,结果存储在 D1)
- `DIVS`:有符号除法。- 例:`DIVS D0, D1`(将 D1 除以 D0,结果存储在 D1)
逻辑运算
- `AND`:按位与运算。- 例:`AND.W D0, D1`(D1 与 D0 按位与)
- `OR`:按位或运算。- 例:`OR.W D0, D1`(D1 与 D0 按位或)
- `EOR`:按位异或运算。- 例:`EOR.W D0, D1`(D1 与 D0 按位异或)
- `NOT`:按位取反。- 例:`NOT.W D0`(D0 的值取反)
控制流
常用如下:
- `BRA`:无条件跳转。- 例:`BRA label`(跳转到指定标签)
- `BEQ`:如果相等则跳转。- 例:`BEQ label`(如果零标志位被设置,则跳转)
- `BNE`:如果不相等则跳转。- 例:`BNE label`(如果零标志位未设置,则跳转)
- `JSR`:跳转到子程序。- 例:`JSR subroutine`(跳转到子程序并保存返回地址)
- `RTS`:从子程序返回。- 例:`RTS`(返回到调用子程序的地址)
分支跳转

该指令将在程序中引发分支,如果某些标志被设置。共有十五种检查标志的方法。每种方法都有一个由两个字母组成的符号,用于替换 “cc” 在 “Bcc” 中。

  • BCC:分支如果进位标志清除 - 当 C 标志为 0 时分支。
  • BCS:分支如果进位标志设置 - 当 C 标志为 1 时分支。
  • BEQ:分支如果相等 - 当 Z 标志为 1 时分支。
  • BNE:分支如果不相等 - 当 Z 标志为 0 时分支。
  • BGE:分支如果大于或等于 - 当 N 和 V 相等时分支。
  • BGT:分支如果大于 - 当 N 和 V 相等且 Z=0 时分支。
  • BHI:分支如果高于 - 当 C 和 Z 都为 0 时分支。
  • BLE:分支如果小于或等于 - 当 Z=1 或 N 和 V 不同时分支。
  • BLS:分支如果小于或相同 - 当 C=1 或 Z=1 时分支。
  • BLT:分支如果小于 - 当 N 和 V 不同时分支。
  • BMI:分支如果负 - 当 N=1 时分支。
  • BPL:分支如果正 - 当 N=0 时分支。
  • BVC:分支如果溢出标志清除 - 当 V=0 时分支。
  • BVS:分支如果溢出标志设置 - 当 V=1 时分支。
  • BRA:无条件分支 - 始终分支。

上面这些opcode根据标志触发跳转,只能跳转到label,注意进入label后会往下执行,和函数调用不一样,函数调用会返回,继续执行之前代码的下一行,这个不会,是直接跳转过去不回来了。

例子:

    ORG    $1000
START:                  ; first instruction of program* Put program code here
input:move.b #4,d0TRAP #15CMP #0,d1BNE input ;如果不等于0跳转到input标签,继续让输入数字BEQ exit  ;如果等于0直接退出labelexit:SIMHALT             ; halt simulator* Put variables and constants hereEND    START        ; last line of source
地址跳转

JMP(跳转)用于将程序控制转移到一个有效地址。它实际上相当于 MOVE.L xxx, PC,因为它将程序计数器更改为一个有效地址(计算得出)。

注意JMP是无条件跳转,相对于B开头的跳转,他也支持 JMP label的语法,同时他也支持直接JMP 地址的跳转。

    ORG    $1000
START:                  ; first instruction of program* Put program code here
input:move.b #4,d0TRAP #15CMP #0,d1BNE  input ;如果不等于0跳转到input标签,继续让输入数字BEQ exit  ;如果等于0直接退出labelexit:LEA quit,a0 ;=0跳转到这里后,将quit的地址给到a0,JMP直接跳转到地址,相当于:move.l a0,PC(这是伪代码)JMP (a0) ;如果想跳转到a0的下一个地址,可以1(a0) 或者n(a0),当然也可以直接JMP quitquit:SIMHALT             ; halt simulator* Put variables and constants hereEND    START        ; last line of source
子程序跳转

JSR/BSR(跳转到子例程)与 JMP(无条件跳转)类似,但在跳转之前,JSR 会将跳转指令后面的地址压入栈中,这样可以通过 RTS(返回子例程)指令返回,也就相当于调用函数,函数执行完了,执行代码的下一行。

BSR适合同一代码段里的label直接调用,是相对掉哟个,JSR适合指定一个绝对地址调用(比如JSR $5000) ,但是实际上两个可以互相替换,没啥区别。

    ORG    $1000
START:                  ; first instruction of program* Put program code here
input:JSR input_notion  ;JSR执行完后会自动执行下一行代码,B开头的跳过去就不回来了move.b #4,d0TRAP #15CMP #0,d1BNE  input ;如果不等于0跳转到input标签,继续让输入数字BEQ exit  ;如果等于0直接退出
input_notion: ;屏幕上输出提示语MOVE    #14,D0LEA        INPUT_STR,A1TRAP        #15RTS  ;注意返回了会运行调用这个函数的下一行
confirm_exit      *屏幕上输出确认提示语MOVE    #14,D0LEA        CONFIRM_STR,A1TRAP        #15RTS
exit:JSR confirm_exitmove.b #4,d0TRAP #15CMP #0,d1BEQ quitBNE input
quit:SIMHALT             ; halt simulator* Put variables and constants here
INPUT_STR: dc.b 'please input number(exit=0):',0
CONFIRM_STR: dc.b 'confirm exit(:exit=0,not=1):',0END    START        ; last line of source

效果
在这里插入图片描述

位操作
- `SHL`:左移。- 例:`SHL.W #1, D0`(D0 左移 1 位)
- `SHR`:右移。- 例:`SHR.W #1, D0`(D0 右移 1 位)
- `ROL`:循环左移。- 例:`ROL.W #1, D0`(D0 循环左移 1 位)
- `ROR`:循环右移。- 例:`ROR.W #1, D0`(D0 循环右移 1 位)
比较
- `CMP`:比较两个操作数。- 例:`CMP.W D0, D1`(比较 D0 和 D1 的值)
堆栈操作
- `PUSH`:将数据压入堆栈。- 例:`PUSH.W D0`(将 D0 的值压入堆栈)
- `POP`:从堆栈弹出数据。- 例:`POP.W D0`(从堆栈弹出值到 D0)
IO操作码

TRAP #15 被用于触发 I/O. 不同的io流任务存储在: D0.
参考chm:
在这里插入图片描述
常用的输入输出任务:

  • 14: 将A1地址对应的字符串输出 以0结尾结束。
  • 13:将A1地址对应的字符串输出 以0结尾结束,加上\r\n换行。
  • 2: 从控制台获取一个字符串回车后存储在A1地址中 0结尾。
  • 4:读取一个数字写入D1.L中。

例子

START   ORG     $1000   Program load address.move    #14,D0 ;设置14号任务打印A1地址字符串lea     text,A1  ;获text地址到A1trap    #15     ;激活任务SIMHALT        text    dc.b    'Hello World',0   ;0表示字符串结束END     START   End of source with start address specified.
其他操作码

关于更加详情的指令参考chm
在这里插入图片描述

directives 指令

指令是汇编器需要遵循的指令。它们占据源代码行中的第二个字段,与指令操作码占据的位置相同,但指令并不是 68000 操作码。 “DC” 和 “DCB” 是唯一会导致数据被添加到输出文件中的指令。指令还可以用于控制宏的汇编、条件汇编和结构化语法。

在以下描述中,选项项用方括号 [] 表示。用斜体显示的项应替换为适当的语法。

Usage:
[label] directive[.size] [data,data,...]^                 ^         ^\_________________\_________\_____ varies by directive
DC指令
  • 全称:Define Constant(定义常量)
  • 用途:用于定义并初始化数据常量。DC 指令可以用于定义一个或多个初始值,这些值会被存储在程序的输出文件中。
  • 内存分配DC 指令会在程序的内存中分配实际的存储空间,并将指定的值写入该空间。
  • 示例
    使用语法:
Usage:
[label] DC.size data,data,...

例子:

VALUE1 DC 10          ; 定义常量 VALUE1,值为 10
VALUE2 DC 20, 30      ; 定义常量 VALUE2,值为 20 和 30
  • 特性
    • 定义的值在程序运行时是不可更改的。
    • 实际在内存中占用空间。

注意下面的代码修改地址的值是非法的,常量无法修改

START:                  ; first instruction of programlea usercount,A0move.b 20,(A0) ;修改A0地址的常量这是非法的。
* Put program code hereSIMHALT             ; halt simulator* Put variables and constants hereORG    $1200
usercount    dc.b    10,20dc.w    23

在这里插入图片描述

EQU 指令
  • 全称:Equate(等于)

  • 用途:用于定义一个符号并将其与一个值关联。EQU 定义的值在整个程序中是不可更改的,通常用于定义常量或符号地址,类似于c语言的#define在预编译将对应引用的地方替换为值。

  • 内存分配EQU 不会在内存中分配实际的存储空间。它只是创建一个符号,所有使用该符号的地方都会被替换为其定义的值。

  • 示例

    MAX_SIZE EQU 100 ; 定义常量 MAX_SIZE,值为 100

  • 特性

    • 一旦定义,EQU 的值不能被修改。
    • 不占用内存空间,编译时进行替换
        ORG    $1000          ; 程序起始地址
START:           ; 将立即数 10 移动到 D0 寄存器; 定义常量
MAX_COUNT EQU 2              ; 定义 MAX_COUNT 为 100
START_VALUE EQU 1             ; 定义 START_VALUE 为 10MOVE.B #10, D0ADD.B #MAX_COUNT, D0     ; 将 MAX_COUNT (100) 加到 D0SUB.B #START_VALUE, D0    ; 将 START_VALUE (10) 从 D0 中减去SIMHALT                  ; 停止模拟器ORG    $1200            ; 数据段起始地址END    START

在这里插入图片描述

SET 指令
  • 用途:用于定义一个符号并赋予一个初始值,但与 DC 不同的是,SET 定义的值是可更改的。SET 通常用于在程序运行时动态地改变值。

  • 示例

    COUNT SET 0 ; 定义符号 COUNT,初始值为 0 COUNT SET COUNT + 1 ; 重新定义 COUNT,值为 COUNT + 1

  • 内存分配SET 指令并不分配实际的存储空间来存储值,而是定义一个符号,允许在程序中动态地改变该符号的值。

DS 指令
  • 全称:Define Space(定义空间)

  • 用途:用于定义一块未初始化的内存空间。DS 指令只分配内存,但不初始化这些内存的值,随时可改。

  • 示例

    BUFFER DS 256 ; 定义一个大小为 256 字节的缓冲区

  • 内存分配DS 指令会在输出文件中分配指定大小的内存空间,但这些空间的初始值是未定义的(通常是随机值或零,具体取决于系统)。

定义一个100字节的空间,可以理解为数组,将MULT_TABLE数字第一个位置设置为:12

    ORG    $1000
START:                  ; first instruction of program* Put program code heremove.B #0,D0LEA MULT_TABLE, A0MOVE.B #12,(A0, D0)SIMHALT             ; halt simulatorORG    $1200
* Put variables and constants hereMULT_TABLE:        ; 乘法表的存储位置DS.B 10 * 10   ; 预留 10x10 的空间END    START        ; last line of source
其他指令

参考chm
在这里插入图片描述

寄存器

程序计数器(PC)

程序计数器(有时在不同的体系结构中也称为指令指针或指令地址寄存器)保存下一条将要执行的指令的内存地址。每当 CPU 执行一条指令时,PC 的值会自动更新,以指向下一条指令。
更新机制:在大多数情况下,PC 在指令执行后自动加一(或加上指令的长度),以指向下一条指令的地址。
编写一个简单程序 运行,默认会从start:的写一条语句开始,PC寄存器指向初始代码的地址(注意有效的代码时左侧绿色点点的,其他都是指令或者注释)
在这里插入图片描述
按下F8执行到下一条
在这里插入图片描述
我这里将usercount的地址指向A0 ,同时加了ORG $1200从1200这个地址写入。点击A0的地址可以查看内存:
在这里插入图片描述

状态寄存器(SR)

在 68k(Motorola 68000)架构中,状态寄存器(SR,Status Register)是一个重要的寄存器,用于存储处理器的状态信息和控制标志。状态寄存器的内容影响程序的执行流程,特别是在条件跳转和中断处理时。以下是对 68k 状态寄存器的详细介绍:

状态寄存器的结构

68k 的状态寄存器是一个 16 位的寄存器,包含多个标志位。主要的标志位包括:

  1. N(Negative):

    • 表示最近一次运算的结果是否为负数。
    • 如果结果的最高位(符号位)为 1,则 N 标志被设置。
  2. Z(Zero):

    • 表示最近一次运算的结果是否为零。
    • 如果结果为 0,则 Z 标志被设置。
  3. V(Overflow):

    • 表示最近一次运算是否发生了溢出。
    • 溢出通常发生在有符号数运算中,当结果超出可表示的范围时,V 标志被设置。
  4. C(Carry):

    • 表示最近一次运算是否产生了进位或借位。
    • 在加法运算中,如果产生了进位,C 标志被设置;在减法运算中,如果发生了借位,C 标志也会被设置。
  5. I(Interrupt Mask):

    • 这是一个 3 位的中断屏蔽位,控制中断的响应。
    • I0、I1 和 I2 位用于设置中断优先级,值越大,响应的中断优先级越低。
  6. T(Trace):

    • 这是一个单个位,用于启用或禁用跟踪模式。
    • 当 T 位被设置时,处理器将在每个指令执行后产生一个中断,适用于调试。
  7. S(Supervisor):

    • 这是一个单个位,指示当前处理器是否处于特权模式(超级用户模式)。
    • 当 S 位被设置时,处理器处于超级用户模式,允许执行特权指令。
状态寄存器的作用
  • 条件跳转: 状态寄存器中的标志位用于条件跳转指令(如 BEQBNE 等),根据运算结果的状态决定程序的执行路径。
  • 中断处理: 中断标志位控制中断的响应,允许或禁止特定级别的中断。
  • 运算结果的状态: 通过检查 N、Z、V 和 C 标志,程序可以根据运算结果的状态做出相应的处理。
示例

以下是一个简单的示例,展示如何使用状态寄存器的标志位:

    MOVE.L #5, D0          ; 将 5 加载到 D0MOVE.L #3, D1          ; 将 3 加载到 D1SUB.L D1, D0           ; D0 = D0 - D1,结果为 2; 检查 Z 标志BEQ zero_result        ; 如果 Z 标志为 1,跳转到 zero_result; 检查 N 标志BPL positive_result     ; 如果 N 标志为 0,跳转到 positive_resultzero_result:; 处理结果为零的情况; ...positive_result:; 处理结果为正的情况; ...
数据寄存器(D)

在 68000(68k)架构中,D 寄存器(数据寄存器)是用于存储数据和操作数的寄存器。68k 处理器有 8 个数据寄存器,分别为 D0 到 D7。

D 寄存器的特点
  1. 数量:

    • 68k 处理器有 8 个数据寄存器,编号为 D0 到 D7。
  2. 大小:

    • 每个 D 寄存器的大小为 32 位(4 字节),可以存储 32 位的整数或指针。
  3. 用途:

    • D 寄存器主要用于存储运算的操作数、结果以及临时数据。它们在算术运算、逻辑运算、数据传输等操作中被广泛使用。
  4. 寻址模式:

    • D 寄存器可以与多种寻址模式结合使用,支持直接寻址、间接寻址等方式,方便数据的访问和操作。
  5. 操作:

    • D 寄存器可以参与各种指令的操作,如加法、减法、位运算等。指令可以直接对 D 寄存器进行操作,也可以将 D 寄存器的值存储到内存中或从内存中加载数据。
D 寄存器的使用场景
  • 算术运算: D 寄存器用于存储参与运算的数值。
  • 数据传输: 在数据传输指令中,D 寄存器可以作为源或目标。
  • 函数参数: 在调用子程序时,D 寄存器常用于传递参数。
示例

以下是一个简单的汇编代码示例,展示如何使用 D 寄存器进行基本的算术运算:

    MOVE.L #10, D0        ; 将 10 加载到 D0 寄存器MOVE.L #5, D1         ; 将 5 加载到 D1 寄存器ADD.L D1, D0          ; D0 = D0 + D1,D0 现在为 15
地址寄存器(A)

68000(68k)架构中,A 寄存器(地址寄存器)是用于存储内存地址的寄存器。68k 处理器有 8 个地址寄存器,分别为 A0 到 A7。以下是对 A 寄存器的详细描述:

A 寄存器的特点
  1. 数量:

    • 68k 处理器有 8 个地址寄存器,编号为 A0 到 A7。
  2. 大小:

    • 每个 A 寄存器的大小为 32 位(4 字节),可以存储 32 位的内存地址。
  3. 用途:

    • A 寄存器主要用于存储内存地址,支持数据的加载和存储操作。它们在指令中用于指向数据或指令的内存位置。
  4. 寻址模式:

    • A 寄存器可以与多种寻址模式结合使用,包括直接寻址、间接寻址、基址寻址和相对寻址等。这使得程序能够灵活地访问内存中的数据。
  5. 堆栈指针:

    • A7 寄存器通常用作堆栈指针(SP),指向当前堆栈的顶部。堆栈用于存储函数调用的返回地址、局部变量等。
A 寄存器的使用场景
  • 内存访问: A 寄存器用于指向数据在内存中的位置,支持数据的读取和写入。
  • 函数调用: 在函数调用中,A 寄存器可以用于传递参数和返回地址。
  • 堆栈管理: A7 寄存器作为堆栈指针,管理函数调用的堆栈帧。
示例

以下是一个简单的汇编代码示例,展示如何使用 A 寄存器进行内存操作:

   LEA array, A0         ; 将数组的地址加载到 A0 寄存器MOVE.L (A0), D0       ; 从 A0 指向的地址加载数据到 D0 寄存器ADD.L #1, D0          ; D0 = D0 + 1MOVE.L D0, (A0)       ; 将 D0 的值存储回 A0 指向的地址
堆栈寄存器(SS)

在68k架构中,堆栈寄存器是用于管理程序运行时的堆栈的关键组件。68k系列处理器使用一个专用的寄存器来指向当前堆栈的顶部,这个寄存器被称为堆栈指针(Stack Pointer)。

在68k架构中,堆栈指针寄存器通常是 A7(地址寄存器7),它指向当前堆栈的顶部。
堆栈是一个后进先出(LIFO)的数据结构,用于存储临时数据,如函数调用的返回地址、局部变量和中断处理程序的上下文。

在这里插入图片描述

堆栈操作

我们来看下堆栈指针的移动和数据写入逻辑。
在68k汇编语言中,-(A7) 和 (A7)+ 分别用于表示压栈和出栈操作。
执行代码

move.l #10,-(a7)

未执行前原始堆栈地址A7指向:01000000,没有任何数据
在这里插入图片描述
执行:move.l #10,-(a7)
在这里插入图片描述
执行:move.l #20,-(a7)
在这里插入图片描述
执行出栈:move.l (a7)+,d0
在这里插入图片描述

std函数模拟

我们知道c语言的std约定是:调用函数先压入执行代码的后一个位置,然后参数从右往左压入,在函数内部出栈从左(后入先出)往右获取参数,执行完成获取代码执行的位置,跳转。
我们来模拟这个过程:
假设函数:
public int add(int a,int b)
用98k模拟堆栈实现:

    ORG    $1000
START:                  ; first instruction of program* Put program code heremove.l #10,-(a7) #第二个参数压栈。move.l #20,-(a7) #第一个参数压栈。LEA *+12, A0  *计算下LEA占用4个字节,一直到move.l d0,d2是12个字节,*+12就是从PC当前位置+12个就是下一个执行代码的位置move.l a0,4(a7) *将下一个执行的地址压栈JMP addmove.l d0,d2SIMHALT             ; halt simulatoradd:move.l (a7)+,a0  ;地址出栈move.l (a7)+,d0 ;第一个参数出栈move.l (a7)+,d1 ;第二个参数出栈add.l d1,d0JMP (a0)* Put variables and constants hereEND    START        ; last line of source

案例(9*9乘法表)

*-----------------------------------------------------------
* Title      :
* Written by :
* Date       :
* Description:
*-----------------------------------------------------------ORG    $1000
START:                  ; first instruction of program* Put program code heremove.b #start_index,d2 ;行索引move.b #start_index,d3  ;列索引
row:jsr print_str_line ;到row的部分就添加一个换行,jsr调用子程序,子程序需要RTS返回add.b #1,d2 ;每运行一次+1move.b #start_index,d3cmp #end_index+1,d2 ;到达最后一行+1直接退出BEQ exitcol:add.b #1,d3move.b d2,d1jsr print_num ;打印行的数字lea tmp_str,a1move.b #'*',(a1) ;打印一个*jsr print_strmove.b d3,d1jsr print_num ;打印一个列的数字move.b #'=',(a1) jsr print_str ;打印一个=move.b #1,d4muls d2,d4muls d3,d4move.b d4,d1jsr print_num ;打印一个列的数字move.b #' ',(a1) jsr print_str ;打印一个空格cmp d3,d2BEQ rowBNE col
print_num:move.b #3,d0TRAP #15 RTS    
print_str:move.b #0,1(a1) ;打印字符的结尾move.b #14,d0TRAP #15 RTS   
print_str_line:move.b #0,(a1) ;打印字符的结尾move.b #13,d0TRAP #15 RTS       
exit:SIMHALT             ; halt simulator* Put variables and constants here
tmp_str ds.b 2
end_index equ 9
start_index equ 0END    START        ; last line of source

效果
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wsl2的Ubuntu18.04安装ros和anaconda

参考:超详细 WSL2 安装 ros 和 anaconda_wsl2安装anaconda-CSDN博客 一.安装ros 1. 更换系统源 输入 wget http://fishros.com/install -O fishros && . fishros 和上面的链接一样,依次输入5-2-1 2. 安装ros 输入 wget http://fishros.c…

如何为 ext2/ext3/ext4 文件系统的 /dev/centos/root 增加 800G 空间

如何为 ext2/ext3/ext4 文件系统的 /dev/centos/root 增加 800G 空间 一、引言二、检查当前磁盘和分区状态1. 使用 `df` 命令检查磁盘使用情况2. 使用 `lsblk` 命令查看分区结构3. 使用 `fdisk` 或 `parted` 命令查看详细的分区信息三、扩展逻辑卷(如果使用 LVM)1. 检查 LVM …

【Linux打怪升级记 | 报错02】-bash: 警告:setlocale: LC_TIME: 无法改变区域选项 (zh_CN.UTF-8)

🗺️博客地图 📍1、报错发现 📍2、原因分析 📍3、解决办法 📍4、测试结果 1、报错发现 装好了CentOS操作系统,使用ssh远程登陆CentOS,出现如下告警信息: bash: 警告:setlocale…

【数据结构】双向链表、单向循环链表、双向循环链表、栈、链栈

目录 一、双向链表 定义类和封装函数以及测试样例如下: 注意事项: 二、循环链表 单循环列表的类和函数封装如下: 注意事项: 三、双向循环链表 结点类和双循环链表的定义部分 函数封装之判空和尾插 双循环链表遍历 双循…

week 6 - SQL Select II

Overview 1. Joins 包括交叉连接(Cross)、内连接(Inner)、自然连接(Natural)、外连接(Outer) 2. ORDER BY to produce ordered output 3. 聚合函数(Aggregate Functio…

systemverilog约束中:=和:/的区别

“x dist { [100:102] : 1, 200 : 2, 300 : 5}” 意味着其值等于100或101或102或200或300其中之一, 其权重比例为1:1:1:2:5 “x dist { [100:102] :/ 1, 200 : 2, 300 : 5}” 意味着等于100,101,102或200,或300其…

[Python/网络安全] Git漏洞之Githack工具基本安装及使用详析

前言 本文仅分享Githack工具基本安装及使用相关知识,不承担任何法律责任。 Git是一个非常流行的开源分布式版本控制系统,它被广泛用于协同开发和代码管理。许多网站和应用程序都使用Git作为其代码管理系统,并将其部署到生产环境中以维护其代…

NFT Insider #157:The Sandbox 开启新一期 VoxEdit 比赛

市场数据 加密艺术及收藏品新闻 Artnames 项目上线,将用户姓名转化为个性化 NFT 艺术品 由知名数字艺术家 Arrotu 发起的生成艺术项目「Artnames」正式上线,利用区块链技术将用户姓名转化为独一无二的 NFT 艺术品。该项目于 11 月 14 日启动&#xff0…

计算机是如何工作的

1. 冯诺依曼体系 CPU 中央处理器: 进行算术运算和逻辑判断 存储器: 分为外存和内存, 用于存储数据(使用二进制方式存储) 输入设备: 用户给计算机发号施令的设备 输出设备: 计算机个用户汇报结果的设备 1)针对存储空间: 硬盘 > 内存 >> CPU …

简单好用的折线图绘制!

折线图的概念及作用: 折线图(Line Chart)是一种常见的图表类型,用于展示数据的变化趋势或时间序列数据。它通过一系列的数据点(通常表示为坐标系中的点)与这些点之间的线段相连,直观地展示变量…

【拥抱AI】Milvus 如何处理 TB 级别的大规模向量数据?

处理 TB 级别的大规模向量数据是 Milvus 的核心优势之一。Milvus 通过分布式架构、高效的索引算法和优化的数据管理策略来实现这一目标。下面将详细介绍 Milvus 如何处理 TB 级别向量数据的流程,包括插入代码示例、指令以及流程图。 1. 分布式架构 Milvus 使用分…

Scrapy管道设置和数据保存

1.1 介绍部分: 文字提到常用的Web框架有Django和Flask,接下来将学习一个全球范围内流行的爬虫框架Scrapy。 1.2 内容部分: Scrapy的概念、作用和工作流程 Scrapy的入门使用 Scrapy构造并发送请求 Scrapy模拟登陆 Scrapy管道的使用 Scrapy中…

k8s集群部署metrics-server

1、Metrics Server介绍 Metrics Server 是集群级别的资源利用率数据的聚合器。从 Kubelets收集资源指标,并通过 Metrics API 在 Kubernetes apiserver 中公开它们,以供 Horizontal Pod Autoscaler 和Vertical Pod Autoscaler 使用。 Metrics API 也可以…

什么是串联谐振

比如有一个由电阻、电容和电感的串联电路中,存在一个频率能使这个电路的电流最大,这个现象就叫谐振。 那么这个频率是多少呢? 交流电频率与电路固有频率一致时,它就能发生谐振,此时这个电路的电流是最大的 这个固有频…

(vue)启动项目报错The project seems to require pnpm but it‘s not installed

(vue)启动项目报错The project seems to require pnpm but it’s not installed 原因 该错误信息表明你的项目需要使用 pnpm 作为包管理工具,但系统中尚未安装 pnpm。 解决方法 【1】删除pnpm.lock 【2】npm install -g pnpm 之后再重新启动 yarn报错&#xff0…

Laravel8.5+微信小程序实现京东商城秒杀方案

一、商品秒杀涉及的知识点 鉴权策略封装掊口访问频次限制小程序设计页面防抖接口调用订单创建事务使用超卖防御 二、订单库存系统方案(3种) 下单减库存 优点是库存和订单的强一致性,商品不会卖超,但是可能导致恶意下单&#xff…

JVM:即时编译器,C2 Compiler,堆外内存排查

1,即时编译器 1.1,基本概念 常见的编译型语言如C,通常会把代码直接编译成CPU所能理解的机器码来运行。而Java为了实现“一次编译,处处运行”的特性,把编译的过程分成两部分,首先它会先由javac编译成通用的…

屏幕分辨率|尺寸|颜色深度指纹修改

一、前端通过window.screen接口获取屏幕分辨率 尺寸 颜色深度,横屏竖屏信息。 二、window.screen c接口实现: 1、third_party\blink\renderer\core\frame\screen.idl // https://drafts.csswg.org/cssom-view/#the-screen-interface[ExposedWindow ] …

【C#设计模式(15)——命令模式(Command Pattern)】

前言 命令模式的关键通过将请求封装成一个对象,使命令的发送者和接收者解耦。这种方式能更方便地添加新的命令,如执行命令的排队、延迟、撤销和重做等操作。 代码 #region 基础的命令模式 //命令(抽象类) public abstract class …

Linux基础项目包含(DNS,FTP,Samba)

今天我们来实现一个linux的基础项目,多做项目可以加深我们对每个服务的掌握程度,能更加的熟悉各个服务的使用,以及每个服务之间如何相互应用,我们可以有一个清晰的认知,那么接下来我们就开始项目的实操。 一、 项目背…