首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >NASM大会-印刷“你好世界”

NASM大会-印刷“你好世界”
EN

Stack Overflow用户
提问于 2022-10-05 15:28:05
回答 1查看 95关注 0票数 0

我已经创建了一个字符串并将其转换为一个数组。循环遍历每个索引并移动到al寄存器,以便它可以打印到vga。问题是,它打印字符串的大小没有问题,但字符是乱七八糟的。你能帮我找出密码里的问题吗?我们会非常感激的。

代码语言:javascript
复制
 org 0
 bits 16

 section .text
   global _start

  _start:

    
    mov si, msg
    loop:
    inc si
    mov ah, 0x0e
    mov al, [si]
    or al, al
    jz end
    mov bh, 0x00
    int 0x10
    jmp loop
    end:
    jmp .done

.done:
jmp $


msg  db  'Hello, world!',0xa
len  equ  $ - msg

TIMES 510 - ($ - $$) db 0
DW 0xAA55

引导加载程序代码

代码语言:javascript
复制
ORG 0x7c00
BITS  16

boot:
mov ah, 0x02
mov al, 0x01
mov ch, 0x00
mov cl, 0x02
mov dh, 0x00
mov dl, 0x00
mov bx, 0x1000
mov es, bx
int 0x13
jmp 0x1000:0x00

times 510 - ($ - $$) db 0
dw 0xAA55
EN

回答 1

Stack Overflow用户

发布于 2022-10-08 16:11:18

引导加载器

在处理内核代码之前,让我们看一下引导加载器,它将内核带到内存中。

您已经编写了一个非常简约的引导加载程序版本,它省略了很多常见的内容,比如设置段寄存器,但是由于它的减少性质,这并不是一个问题。

可能是一个问题,是您编写了mov dl, 0x00,硬编码一个0来选择第一个软盘作为您的引导磁盘。如果确实是这样的话,没有问题,但是最好只使用BIOS预装DL寄存器的任何值。这是存放引导加载程序和内核的磁盘的ID。

是一个问题,是将内核加载到分段地址0x1000:0x1000,然后跳转到分段地址0x1000:0x0000,比内核少4096字节。幸运的是,内核代码最终确实运行了,这要归功于这两个地址之间的内存--很可能是由零字节填充的,这些字节(二次二)转换为指令add [bx+si], al。因为您忽略了DS段寄存器的设置,所以我们不知道有什么不幸的字节被覆盖了这么多次。希望这不是一个重要的字节..。

代码语言:javascript
复制
mov bx, 0x1000
mov es, bx
xor bx, bx          <== You forgot to write this instruction!
int 0x13
jmp 0x1000:0x0000

是一个问题,是,当从磁盘加载扇区时,您忽略了遇到故障的可能性。至少,您应该检查BIOS.ReadSector函数02h报告的进位标志,如果设置了标记,则可以彻底中止。更复杂的方法也会重试有限的次数,比如3次。

代码语言:javascript
复制
ORG   0x7C00
BITS  16

; IN (dl)
mov dh, 0x00     ; DL is bootdrive
mov cx, 0x0002
mov bx, 0x1000
mov es, bx
xor bx, bx
mov ax, 0x0201   ; BIOS.ReadSector
int 0x13         ; -> AH CF
jc  ERR
jmp 0x1000:0x0000

ERR:
cli
hlt
jmp ERR

times 510 - ($ - $$) db 0
dw 0xAA55

内核

jmp 0x1000:0x0000指令将您带到内核的第一条指令之后,CS代码段寄存器保存的值为0x1000。其他的段寄存器都没有改变,而且由于您没有在引导加载器中设置它们中的任何一个,所以我们仍然不知道它们中包含了什么。但是,为了用mov al, [si]指令从msg上的消息中检索字节,我们需要DS数据段寄存器的正确值。根据ORG 0指令,正确的值是我们在CS中已经拥有的值。只需要两个1字节的指令:push cs pop ds.

关于内核代码还有更多的内容要说:

  • 打印循环在SI寄存器中对指针使用预增量.因此,字符串的第一个字符将不会显示。您可以通过mov si, msg - 1.
  • The打印循环处理一个零终止字符串来补偿这一点.你不需要准备那个等价物。您需要的是一个显式的零字节来终止字符串。您不应该依赖times生成的大量零字节。在未来的代码版本中,可能根本没有零字节!
  • 您(认为您)在字符串中包含了一个换行符(0xa)。对于BIOS.Teletype函数0Eh,这只是一个在屏幕上向下移动的行提要。要获得换行符,您需要同时包含回车(13)和linefeed (10).
  • There's --没有理由让内核代码的引导扇区签名字节位于偏移量510。根据您如何将这些代码放到磁盘上,可能需要将代码压缩到(倍数为512 ),所以保持times 512 - ($ - $$) db 0.

内核:

代码语言:javascript
复制
ORG   0
BITS  16

section .text
    global _start

_start:
    push cs
    pop  ds
    mov  si, msg
    mov  bx, 0x0007    ; DisplayPage=0, GraphicsColor=7 (White)
    jmp  BeginLoop
PrintLoop:
    mov  ah, 0x0E      ; BIOS.Teletype
    int  0x10
BeginLoop:
    mov  al, [si]
    inc  si
    test al, al
    jnz  PrintLoop


    cli
    hlt
    jmp  $-2

msg db   'Hello, world!', 13, 10, 0

TIMES 512 - ($ - $$) db 0
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73962758

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档