从0开始的x86汇编 3 在屏幕上显示

在屏幕上显示

显卡与显存

显卡的存储器简称显存,用显存中的数据来控制像素
对于字符有专门的字符显示器,对于图片则是操作像素
在这里插入图片描述

基础知识

mov指令
  • mov a, b => mov a <- b
  • mov 段寄存器, 通用寄存器
    • mov ds, ax
    • mov ds, bx
    • mov ds, cx
  • mov 段寄存器, 内存地址
    • mov es, [0x6C]
mov [0x01], [0x02] X

mov ax, [0x02]
mov [0x01], ax
mov ip, 0xf000 X

mov ds, ax
mov es, [0x3002]

显示文本

0xb800是文本模式显示缓冲区的地址

字符编码

在这里插入图片描述

在这里插入图片描述

    mov ax, 0xb800
    mov ds, ax

    mov byte [0x00], 'A'
    mov byte [0x01], 0x04       ;黑底红字无闪烁

    mov byte [0x02], 's'
    mov byte [0x03], 0x04

    mov byte [0x04], 's'
    mov byte [0x05], 0x04

    mov byte [0x06], 'e'
    mov byte [0x07], 0x04

    mov byte [0x08], 'm'
    mov byte [0x09], 0x04

    mov byte [0x0a], 'b'
    mov byte [0x0b], 0x04

    mov byte [0x0c], 'l'
    mov byte [0x0d], 0x04

    mov byte [0x0e], 'y'
    mov byte [0x0f], 0x04

    mov byte [0x10], '.'
    mov byte [0x11], 0x04

nasm main.asm -l main.lst
     1                                  ;   org 0x07c00
     2                                  ;   mov ax,cs
     3                                  ;   mov ds,ax
     4                                  ;   mov es,ax
     5                                  ;   mov ax,Message
     6                                  ;   mov bp,ax
     7                                  ;   mov cx, 13
     8                                  ;   mov ax,0x1301
     9                                  ;   mov bx,0x0002
    10                                  ;   mov dh,0
    11                                  ;   mov dl,0
    12                                  ;   int 0x10
    13                                  ;   jmp $
    14                                  ;Message:
    15                                  ;   db "Hello, world!"
    16                                  ;   times 510-($-$$) db 0
    17                                  ;   dw  0xaa55
    18                                  
    19 00000000 B80008                      mov ax, 0xb800
    20 00000003 8ED8                        mov ds, ax
    21                                  
    22 00000005 C606000041                  mov byte [0x00], 'A'
    23 0000000A C606010004                  mov byte [0x01], 0x04       ;黑底红字无闪烁
    24                                  
    25 0000000F C606020073                  mov byte [0x02], 's'
    26 00000014 C606030004                  mov byte [0x03], 0x04
    27                                  
    28 00000019 C606040073                  mov byte [0x04], 's'
    29 0000001E C606050004                  mov byte [0x05], 0x04
    30                                  
    31 00000023 C606060065                  mov byte [0x06], 'e'
    32 00000028 C606070004                  mov byte [0x07], 0x04
    33                                  
    34 0000002D C60608006D                  mov byte [0x08], 'm'
    35 00000032 C606090004                  mov byte [0x09], 0x04
    36                                  
    37 00000037 C6060A0062                  mov byte [0x0a], 'b'
    38 0000003C C6060B0004                  mov byte [0x0b], 0x04
    39                                  
    40 00000041 C6060C006C                  mov byte [0x0c], 'l'
    41 00000046 C6060D0004                  mov byte [0x0d], 0x04
    42                                  
    43 0000004B C6060E0079                  mov byte [0x0e], 'y'
    44 00000050 C6060F0004                  mov byte [0x0f], 0x04
    45                                  
    46 00000055 C60610002E                  mov byte [0x10], '.'
    47 0000005A C606110004                  mov byte [0x11], 0x04

完善主引导程序

start:
    mov ax, 0x800
    mov ds, ax

    mov byte [0x00], 'A'
    mov byte [0x01], 0x04       ;黑底红字无闪烁

    mov byte [0x02], 's'
    mov byte [0x03], 0x04

    mov byte [0x04], 's'
    mov byte [0x05], 0x04

    mov byte [0x06], 'e'
    mov byte [0x07], 0x04

    mov byte [0x08], 'm'
    mov byte [0x09], 0x04

    mov byte [0x0a], 'b'
    mov byte [0x0b], 0x04

    mov byte [0x0c], 'l'
    mov byte [0x0d], 0x04

    mov byte [0x0e], 'y'
    mov byte [0x0f], 0x04

    mov byte [0x10], '.'
    mov byte [0x11], 0x04

current:
    times 510-(current-start) db 0
    db 0x55, 0xaa               ; DB作为汇编语言中的伪操作命令,它用来定义操作数占用的字节数

段间直接绝对远跳转指令

jmp 0x0000:0x7c00

start:
    mov ax, 0xb800
    mov ds, ax

    mov byte [0x00], 'A'
    mov byte [0x01], 0x0c       ;黑底红字无闪烁

    mov byte [0x02], 's'
    mov byte [0x03], 0x04

    mov byte [0x04], 's'
    mov byte [0x05], 0x04

    mov byte [0x06], 'e'
    mov byte [0x07], 0x04

    mov byte [0x08], 'm'
    mov byte [0x09], 0x04

    mov byte [0x0a], 'b'
    mov byte [0x0b], 0x04

    mov byte [0x0c], 'l'
    mov byte [0x0d], 0x04

    mov byte [0x0e], 'y'
    mov byte [0x0f], 0x04

    mov byte [0x10], '.'
    mov byte [0x11], 0x04

    jmp 0x0000:0x7c00
current:
    times 510-(current-start) db 0
    db 0x55, 0xaa               ; DB作为汇编语言中的伪操作命令,它用来定义操作数占用的字节数

bochs -f ./bochsrc 
# 进入配置好的环境
6
# 在0x7c00处打断点
b 0x7c00
# 继续执行
c
# 反汇编20条指令
u/20
# 逐步观察
s/n
s/n
s/n
s/n
s/n
s/n

使用标号计算JMP指令跳转的偏移地址

again:
    jmp 0x0000:0x7c00+again

段内间接绝对近跳转指令

不改变cs的内容,只改变ip的内容

    mov bx, 0x7c00
again:
    jmp bx

相对偏移量的短跳转和近跳转

  • [128~-127] 短跳转
jmp (short) start
  • [215~-215+1] 近跳转
jmp (near) start

显示数字

数字1 -> 0000 0001 (0x01)
字符1 -> 0011 0001 (0x31)

数字+0x30 == 对应字符

除法指令 div

  • 如果在指令中指定的是8位寄存器或者8位操作数的内存地址,则意味这被除数在AX(16位)中
  • 相除后,商寄存器AL中,余数在寄存器AH中
div bh
div byte [0x2002]
  • 如果在指令中指定的是16位寄存器或者16位操作数的内存地址,则意味这被除数是32位的,低16位放在AX(16位)中,高16位放在DX(16位)中
  • 相除后,商寄存器AX中,余数在寄存器DX中
dic bx
dic word [0x2002]
  • 如果在指令中指定的是32位寄存器或者32位操作数的内存地址,则意味这被除数是64位的,低32位放在EAX(32位)中,高32位放在EDX(32位)中
  • 相除后,商寄存器EAX中,余数在寄存器EDX中
div ebx
div dword [0x2002]
  • 如果在指令中指定的是64位寄存器或者64位操作数的内存地址,则意味这被除数是128位的,低64位放在RAX(64位)中,高64位放在RDX(64位)中
  • 相除后,商寄存器RAX中,余数在寄存器RDX中
div Rbx
div qword [0x2002]

试用

start:
  ; 计算378除以37的结果
  mov ax, 378
  mov bl, 37
  div bl                        ;AL: 商 AH: 余数

  mov ax, 0xb800
  mov ds, ax
current:
  times 510-(current-start) db 0
  db 0x55, 0xaa

XOR

65535 / 10

start:
  ; 计算65535除以10的结果
  mov dx, 0x0
  mov ax, 0xffff
  mov bx, 10
  div bx                        ;AL: 商 AH: 余数

  mov ax, 0xb800
  mov ds, ax
current:
  times 510-(current-start) db 0
  db 0x55, 0xaa

Please choose one: [6]
00000000000i[      ] installing x module as the Bochs GUI
00000000000i[      ] using log file bochs.out
Next at t=0
(0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b          ; ea5be000f0
<bochs:1> b 0x7c00
<bochs:2> c
(0) Breakpoint 1, 0x0000000000007c00 in ?? ()
Next at t=17178869
(0) [0x000000007c00] 0000:7c00 (unk. ctxt): mov dx, 0x0000            ; ba0000
<bochs:3> s
Next at t=17178870
(0) [0x000000007c03] 0000:7c03 (unk. ctxt): mov ax, 0xffff            ; b8ffff
<bochs:4> r
CPU0:
rax: 00000000_0000aa55
rbx: 00000000_00000000
rcx: 00000000_00090000
rdx: 00000000_00000000
eflags 0x00000082: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf af pf cf
<bochs:5> s
Next at t=17178871
(0) [0x000000007c06] 0000:7c06 (unk. ctxt): mov bx, 0x000a            ; bb0a00
<bochs:6> r
CPU0:
rax: 00000000_0000ffff
rbx: 00000000_00000000
rcx: 00000000_00090000
rdx: 00000000_00000000
eflags 0x00000082: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf af pf cf
<bochs:7> s
Next at t=17178872
(0) [0x000000007c09] 0000:7c09 (unk. ctxt): div ax, bx                ; f7f3
<bochs:8> r
CPU0:
rax: 00000000_0000ffff
rbx: 00000000_0000000a
rcx: 00000000_00090000
rdx: 00000000_00000000
eflags 0x00000082: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf af pf cf
<bochs:9> s
Next at t=17178873
(0) [0x000000007c0b] 0000:7c0b (unk. ctxt): mov ax, 0xb800            ; b800b8
<bochs:10> r
CPU0:
rax: 00000000_00001999
rbx: 00000000_0000000a
rcx: 00000000_00090000
rdx: 00000000_00000005
eflags 0x00000082: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf af pf cf

同样的作用

XOR == 亦或

  • 1 xor 1 => 0
  • 1 xor 0 => 1
  • 0 xor 1 => 1
  • 0 xor 0 => 0

xor r/m, r/m/imm 结果放在前面

xor dx, dx

add指令

add 加法指令

add dl, 0x30

临时存放计算结果

可以使用db指令 效果类似数组

start:
  mov cx, 0
  mov ds, cx

  ; 计算65535除以10的结果
  mov ax, 0xffff
  xor dx, dx                    ; 初始化被除数

  mov bx, 10                    ; 初始化除数

  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+0], dl

  xor dx, dx
  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+1], dl

  xor dx, dx
  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+2], dl

  xor dx, dx
  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+3], dl

  xor dx, dx
  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+4], dl

buffer:
  times 5 db 0              ; 临时数组

current:
  times 510-(current-start) db 0
  db 0x55, 0xaa

es寄存器 与 段超越前缀的使用

es寄存器可以辅助程序员在两个段内操作程序,不需要来回设定、恢复ds

start:
  mov cx, 0
  mov ds, cx

  ; 计算65535除以10的结果
  mov ax, 0xffff
  xor dx, dx                    ; 初始化被除数

  mov bx, 10                    ; 初始化除数

  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+0], dl

  xor dx, dx
  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+1], dl

  xor dx, dx
  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+2], dl

  xor dx, dx
  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+3], dl

  xor dx, dx
  div bx                        ; AX: 商 DX: 余数
  add dl, 0x30                  ; 将数字转换为数字字符
  mov [0x7c00+buffer+4], dl


  ; 转化到文本模式的显存段
  mov cx, 0xb800
  mov es, cx                    ; es extra segment reg

  mov al, [0x7c00+buffer+4]
  mov [es:0x00], al             ; `es:` 段超越前缀
  mov byte [es:0x01], 0x2f

  mov al, [0x7c00+buffer+3]
  mov [es:0x02], al
  mov byte [es:0x03], 0x2f

  mov al, [0x7c00+buffer+2]
  mov [es:0x04], al
  mov byte [es:0x05], 0x2f

  mov al, [0x7c00+buffer+1]
  mov [es:0x06], al
  mov byte [es:0x07], 0x2f

  mov al, [0x7c00+buffer+0]
  mov [es:0x08], al
  mov byte [es:0x09], 0x2f

again:
  jmp again

buffer:
  times 5 db 0              ; 临时数组

current:
  times 510-(current-start) db 0
  db 0x55, 0xaa

显示标号的汇编地址

  mov ax,0xb800                 ;指向文本模式的显示缓冲区
  mov es,ax

  ;以下显示字符串"Label offset:"
  mov byte [es:0x00],'L'
  mov byte [es:0x01],0x07
  mov byte [es:0x02],'a'
  mov byte [es:0x03],0x07
  mov byte [es:0x04],'b'
  mov byte [es:0x05],0x07
  mov byte [es:0x06],'e'
  mov byte [es:0x07],0x07
  mov byte [es:0x08],'l'
  mov byte [es:0x09],0x07
  mov byte [es:0x0a],' '
  mov byte [es:0x0b],0x07
  mov byte [es:0x0c],'o'
  mov byte [es:0x0d],0x07
  mov byte [es:0x0e],'f'
  mov byte [es:0x0f],0x07
  mov byte [es:0x10],'f'
  mov byte [es:0x11],0x07
  mov byte [es:0x12],'s'
  mov byte [es:0x13],0x07
  mov byte [es:0x14],'e'
  mov byte [es:0x15],0x07
  mov byte [es:0x16],'t'
  mov byte [es:0x17],0x07
  mov byte [es:0x18],':'
  mov byte [es:0x19],0x07

  mov ax,number                 ;取得标号number的汇编地址
  mov bx,10

  ;设置数据段的基地址
  mov cx,cs
  mov ds,cx

  ;求个位上的数字
  mov dx,0
  div bx
  mov [0x7c00+number+0x00],dl   ;保存个位上的数字

  ;求十位上的数字
  xor dx,dx
  div bx
  mov [0x7c00+number+0x01],dl   ;保存十位上的数字

  ;求百位上的数字
  xor dx,dx
  div bx
  mov [0x7c00+number+0x02],dl   ;保存百位上的数字

  ;求千位上的数字
  xor dx,dx
  div bx
  mov [0x7c00+number+0x03],dl   ;保存千位上的数字

  ;求万位上的数字
  xor dx,dx
  div bx
  mov [0x7c00+number+0x04],dl   ;保存万位上的数字

  ;以下用十进制显示标号的偏移地址
  mov al,[0x7c00+number+0x04]
  add al,0x30
  mov [es:0x1a],al
  mov byte [es:0x1b],0x04

  mov al,[0x7c00+number+0x03]
  add al,0x30
  mov [es:0x1c],al
  mov byte [es:0x1d],0x04

  mov al,[0x7c00+number+0x02]
  add al,0x30
  mov [es:0x1e],al
  mov byte [es:0x1f],0x04

  mov al,[0x7c00+number+0x01]
  add al,0x30
  mov [es:0x20],al
  mov byte [es:0x21],0x04

  mov al,[0x7c00+number+0x00]
  add al,0x30
  mov [es:0x22],al
  mov byte [es:0x23],0x04

  mov byte [es:0x24],'D'
  mov byte [es:0x25],0x07

infi:
  jmp near infi                 ;无限循环

number:
  db 0,0,0,0,0

  times 203 db 0
  db 0x55,0xaa

一些补充

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用 x86 汇编语言在 Windows 系统上显示九九乘法表的示例程序: ``` .386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib .data prompt db "九九乘法表:", 0Ah, 0 row db 0Ah, 0 multiplier db " x ", 0 equal db " = ", 0 space db " ", 0 .data? buffer db 256 dup(?) .code start: ; 获取当前进程句柄 invoke GetModuleHandle, NULL mov hInstance, eax ; 加载主窗口图标 invoke LoadIcon, NULL, IDI_APPLICATION mov hIcon, eax ; 加载主窗口背景画刷 invoke GetStockObject, WHITE_BRUSH mov hBrush, eax ; 注册主窗口类 mov wc.cbSize, sizeof WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra, 0 mov wc.cbWndExtra, 0 push hInstance pop wc.hInstance mov wc.hIcon, hIcon mov wc.hCursor, LoadCursor, NULL, IDC_ARROW mov wc.hbrBackground, hBrush mov wc.lpszMenuName, NULL mov wc.lpszClassName, OFFSET szClassName mov wc.hIconSm, hIcon invoke RegisterClassEx, ADDR wc ; 创建主窗口 invoke CreateWindowEx, WS_EX_CLIENTEDGE, ADDR szClassName, ADDR szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, hInstance, NULL mov hWnd, eax ; 显示主窗口 invoke ShowWindow, hWnd, SW_SHOWDEFAULT invoke UpdateWindow, hWnd ; 进入消息循环 .loop: invoke GetMessage, ADDR msg, NULL, 0, 0 or eax, eax jz .exit invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg jmp .loop ; 退出程序 .exit: invoke ExitProcess, wParam WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM cmp uMsg, WM_DESTROY je .wmdestroy cmp uMsg, WM_PAINT je .wmpaint jmp .defwndproc ; 处理 WM_DESTROY 消息 .wmdestroy: invoke PostQuitMessage, 0 xor eax, eax ret ; 处理 WM_PAINT 消息 .wmpaint: ; 获取设备上下文句柄 invoke BeginPaint, hWnd, ADDR ps mov hdc, eax ; 设置字体 invoke GetStockObject, DEFAULT_GUI_FONT invoke SelectObject, hdc, eax ; 打印提示信息 invoke TextOut, hdc, 10, 10, ADDR prompt, 14 ; 初始化行数为1 mov ebx, 1 ; 循环1-9行 .loop1: ; 初始化列数为1 mov ecx, 1 ; 循环1-9列 .loop2: ; 计算乘积 mov eax, ebx mul ecx ; 打印行号 invoke wsprintf, ADDR buffer, "%d", ebx invoke TextOut, hdc, 10, ebx * 20 + 10, ADDR buffer, eax ; 打印 x 号 invoke TextOut, hdc, 30, ebx * 20 + 10, ADDR multiplier, 3 ; 打印列号 invoke wsprintf, ADDR buffer, "%d", ecx invoke TextOut, hdc, 50, ebx * 20 + 10, ADDR buffer, eax ; 打印等号 invoke TextOut, hdc, 70, ebx * 20 + 10, ADDR equal, 3 ; 打印乘积 invoke wsprintf, ADDR buffer, "%d", eax invoke TextOut, hdc, 90, ebx * 20 + 10, ADDR buffer, eax ; 打印空格 invoke TextOut, hdc, 130, ebx * 20 + 10, ADDR space, 1 ; 打印换行符 invoke TextOut, hdc, 140, ebx * 20 + 10, ADDR row, 2 ; 列数加1 inc ecx ; 判断是否循环9列 cmp ecx, 10 jne .loop2 ; 行数加1 inc ebx ; 判断是否循环9行 cmp ebx, 10 jne .loop1 ; 释放设备上下文句柄 invoke EndPaint, hWnd, ADDR ps xor eax, eax ret ; 默认消息处理程序 .defwndproc: invoke DefWindowProc, hWnd, uMsg, wParam, lParam xor eax, eax ret WndProc endp szClassName db "myWindowClass", 0 szTitle db "九九乘法表", 0 hInstance HINSTANCE ? hIcon HICON ? hBrush HBRUSH ? hWnd HWND ? msg MSG <> ps PAINTSTRUCT <> hdc HDC ? end start ``` 上述代码使用了 Windows API 进行屏幕输出,创建了一个简单的窗口来显示九九乘法表。可以使用 MASM 汇编器进行编译并在 Windows 操作系统上运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值