带有%的组装在顶部 - 打印输出意外结果:仅AN; s&quot

发布于 2025-01-26 19:09:46 字数 935 浏览 5 评论 0原文

我对组装编程的新手相对较新,并且想知道为什么我的代码不打印预期的字符串。完成后,该项目应该是引导加载程序。我正在使用命令nasm -f bin boot.asm -o boot.bin进行编译。汇编过程中没有错误。

boot.asm

bits 16
org 0x7C00

%include "print.asm"
%include "text.asm"

boot:
        mov si, boot_string_00
        call print
        mov si, boot_string_01
        call print

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

print.asm

print:
        mov ah, 0x0E

.print_loop:
        lodsb
        or al, al
        je .print_done
        int 0x10
        jmp .print_loop

.print_done:
        cli
        ret

text.asm

boot_string_00: db "Placeholder OS Title v0.0.1", 0
boot_string_01: db "Loading Operating system", 0

预期输出:

占位符os os os v0.0.1.1加载操作系统

实际输出:

s

另外,我想知道如何在汇编中实现新线,以便可以在字符串中使用'\ n'。

I'm relatively new to assembly programming and was wondering why my code does not print the expected strings. This project is supposed to be a bootloader when finished. I am compiling using the command nasm -f bin boot.asm -o boot.bin. There are no errors during compilation.

boot.asm

bits 16
org 0x7C00

%include "print.asm"
%include "text.asm"

boot:
        mov si, boot_string_00
        call print
        mov si, boot_string_01
        call print

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

print.asm

print:
        mov ah, 0x0E

.print_loop:
        lodsb
        or al, al
        je .print_done
        int 0x10
        jmp .print_loop

.print_done:
        cli
        ret

text.asm

boot_string_00: db "Placeholder OS Title v0.0.1", 0
boot_string_01: db "Loading Operating system", 0

Expected Output:

PlaceHolder OS Title v0.0.1Loading Operating System

Actual Output:

S

Also, I was wondering how i could implement newlines in assembly so that i could just use '\n' in my strings.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

妥活 2025-02-02 19:09:46

您在引导加载程序顶部包含了东西,它将首先执行。而是在不在执行主要路径的情况下包括额外功能,仅通过呼叫来达到。


这应该有效,将%include指令放置在安全地放置额外功能或数据的地方,就像您将它们全部写入一个文件一样。

boot.asm:

[bits 16]
[org 0x7c00]

boot:
  xor ax, ax
  mov ds, ax        ; set up DS to make sure it matches our ORG

  mov si, boot_string_00
  call println

  mov si, boot_string_01
  call println

finish:       ; fall into a hlt loop to save power when we're done
  hlt
  jmp finish
 

%include "printf.asm"      ; not reachable except by call to labels in this file
%include "text.S"


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

printf.asm:

print:
        mov ah, 0x0E      ; call number for int 0x10 screen output

print_loop:
        lodsb
        test al, al
        je print_done
        int 0x10
        jmp print_loop

print_done:
        ret
           
println:
  call print
  mov si, line_end
  call print
  ret

text.s:

boot_string_00: db "Placeholder OS Title v0.0.1", 0
boot_string_01: db "Loading Operating system", 0
line_end:       db 0xD, 0xA, 0

You included stuff at the top of your bootloader, where it will executes first. Instead include extra functions where they aren't in the main path of execution and are only reached by call.


This should work, placing the %include directives where it's safe to put extra function or data, just like if you were writing them all in one file.

boot.asm:

[bits 16]
[org 0x7c00]

boot:
  xor ax, ax
  mov ds, ax        ; set up DS to make sure it matches our ORG

  mov si, boot_string_00
  call println

  mov si, boot_string_01
  call println

finish:       ; fall into a hlt loop to save power when we're done
  hlt
  jmp finish
 

%include "printf.asm"      ; not reachable except by call to labels in this file
%include "text.S"


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

printf.asm:

print:
        mov ah, 0x0E      ; call number for int 0x10 screen output

print_loop:
        lodsb
        test al, al
        je print_done
        int 0x10
        jmp print_loop

print_done:
        ret
           
println:
  call print
  mov si, line_end
  call print
  ret

text.S:

boot_string_00: db "Placeholder OS Title v0.0.1", 0
boot_string_01: db "Loading Operating system", 0
line_end:       db 0xD, 0xA, 0
私藏温柔 2025-02-02 19:09:46

包括%的指令时,请不要在顶部

,包括“ print.asm” ,nasm将插入文件 print.asm 的内容写了线。对于%的%,也是如此。因此,扩展的源文本变为:

bits 16
org 0x7C00

print:                                               \ %include "print.asm"
        mov ah, 0x0E                                 |
                                                     |
.print_loop:                                         |
        lodsb                                        |
        or al, al                                    |
        je .print_done                               |
        int 0x10                                     |
        jmp .print_loop                              |
                                                     |
.print_done:                                         |
        cli                                          |
        ret                                          /
boot_string_00: db "Placeholder OS Title v0.0.1", 0  \ %include "text.asm"
boot_string_01: db "Loading Operating system", 0     /

boot:
        mov si, boot_string_00
        call print
        mov si, boot_string_01
        call print

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

现在,当Bios在地址0x7C00上完成加载Bootsector代码时,它将控件转移到内存中的相同地址。 CPU遇到的第一个指令将是MOV AH,0x0e,因此我们的打印循环开始了。问题是,我们尚未设置通向消息的指针。目的是要使代码开始在 boot 标签上执行,并且包含在错误的标签上。
一个快速解决方案是将JMP启动指令作为org 0x7c00指令下方的第一个指令。但是,为什么当我们也可以浪费2或3个字节时,最好将包含的内容放在其余的代码下方呢?这将是首选的解决方案:

bits 16
org 0x7C00

boot:
        mov si, boot_string_00
        call print
        mov si, boot_string_01
        call print

%include "print.asm"
%include "text.asm"

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

让我们再次扩展包含,并验证问题是否解决。

bits 16
org 0x7C00

boot:
        mov si, boot_string_00
        call print
        mov si, boot_string_01
        call print

print:                                               \ %include "print.asm"
        mov ah, 0x0E                                 |
                                                     |
.print_loop:                                         |
        lodsb                                        |
        or al, al                                    |
        je .print_done                               |
        int 0x10                                     |
        jmp .print_loop                              |
                                                     |
.print_done:                                         |
        cli                                          |
        ret                                          /
boot_string_00: db "Placeholder OS Title v0.0.1", 0  \ %include "text.asm"
boot_string_01: db "Loading Operating system", 0     /

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

两条消息都可以打印正常,但是,正如您所看到的,一旦第二个呼叫print返回,代码将在 print 例程中掉落并开始打印一个空消息(因为si寄存器指向第一个由times指令插入的零字节)。
一个更糟糕的问题是,因为这(第三次),ret指令在堆栈上没有明智的返回地址,所以计算机会崩溃,但是以危险的方式崩溃会去!

解决方案是防止在 print 子例程中掉落的代码。因为我们的程序无事可做,所以我们将插入一个停止循环,以便CPU不会在紧密的循环中浪费宝贵的周期。首选的方法是CLI HLT JMP $ -2

bits 16
org 0x7C00

boot:
    mov si, boot_string_00
    call print
    mov si, boot_string_01
    call print

    cli
    hlt
    jmp $-2

%include "print.asm"
%include "text.asm"

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

在当前 print 例程中有改进的空间

  • 。屏幕处于图形模式时所需的GraphicsColor。
  • 在此代码中使用cli指令是没有意义的。
  • 测试al为零注册,将更好地使用test al,al
  • 这样的简单循环切勿使用2个分支说明(迭代时)。
  • 此代码取决于正确的ds段寄存器。确保根据org 0x7c00指令,确保“呼叫代码” xor ax,ax mov ds,ax
print:
    mov     bx, 0x0007   ; DisplayPage BH=0, GraphicsColor BL=7
    jmp     .begin       ; This is what makes 'empty messages' OK
.print:
    mov     ah, 0x0E     ; BIOS.Teletype
    int     0x10
.begin:
    lodsb
    test    al, al
    jnz     .print
    ret

回答其他问题将进一步改善代码

另外,我想知道如何在汇编中实现新线,以便我可以在字符串中使用'\ n'。

在NASM中,您可以以3种不同的方式定义字符串英非常数字。使用单引号',使用双引号标记”或使用Backticks `

只有在backticks中,您才能在文本中包含逃生序列为了插入newlines \ r \ n,您的 text.asm 需要成为

boot_string_00: db `Placeholder OS Title v0.0.1\r\n`, 0
boot_string_01: db `Loading Operating system\r\n`, 0

 占位符OS标题V0.0.1
加载操作系统
 

现在,新线已嵌入到消息中,您可以考虑简化代码并输出2行消息,总共节省了7个字节:

boot_string: db `Placeholder OS Title v0.0.1\r\nLoading Operating system\r\n`, 0

为什么\ r \ r \ r \ n序列,而不是简单的\ n

引用Wikipedia文章中有关逃生序列的报价:

\ n会产生一个字节,尽管该平台可能会使用多个字节来表示新线,例如DOS/Windows CR-LF序列,0x0d 0x0a。当将字节写入文件或控制台时,从0x0a到0x0d 0x0a的翻译会发生,并且在读取文本文件时完成了反转换。

\ n(newline)逃脱序列仅插入lineFeed byte(10),但由于这是引导加载程序代码,而不是DOS/Windows,BIOS将同时需要Carriagare Byte(13)和LineFeed Byte(10)为了执行 emote 的开头。这就是为什么我们需要编写\ r \ n

Includes don't go at the top

When using a directive like %include "print.asm", NASM will insert the contents of the file print.asm right where you have written the line. The same is true for %include "text.asm". The expanded source text thus becomes:

bits 16
org 0x7C00

print:                                               \ %include "print.asm"
        mov ah, 0x0E                                 |
                                                     |
.print_loop:                                         |
        lodsb                                        |
        or al, al                                    |
        je .print_done                               |
        int 0x10                                     |
        jmp .print_loop                              |
                                                     |
.print_done:                                         |
        cli                                          |
        ret                                          /
boot_string_00: db "Placeholder OS Title v0.0.1", 0  \ %include "text.asm"
boot_string_01: db "Loading Operating system", 0     /

boot:
        mov si, boot_string_00
        call print
        mov si, boot_string_01
        call print

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

Now, when BIOS has finished loading your bootsector code at the address 0x7C00, it will transfer control to that same address in memory. The first instruction that the CPU encounters will be mov ah, 0x0E and so our printing loop starts. Problem is, we haven't yet setup the pointer to the message. The intend was for the code to start executing at the boot label, and the includes made that go wrong.
A quick solution would be to have a jmp boot instruction as the very first instruction beneath the org 0x7C00 directive. But why waste 2 or 3 bytes when we could just as well, and better, place the includes below the rest of the code? That's going to be the preferred solution:

bits 16
org 0x7C00

boot:
        mov si, boot_string_00
        call print
        mov si, boot_string_01
        call print

%include "print.asm"
%include "text.asm"

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

Let's expand the includes again and verify that the problem is resolved.

bits 16
org 0x7C00

boot:
        mov si, boot_string_00
        call print
        mov si, boot_string_01
        call print

print:                                               \ %include "print.asm"
        mov ah, 0x0E                                 |
                                                     |
.print_loop:                                         |
        lodsb                                        |
        or al, al                                    |
        je .print_done                               |
        int 0x10                                     |
        jmp .print_loop                              |
                                                     |
.print_done:                                         |
        cli                                          |
        ret                                          /
boot_string_00: db "Placeholder OS Title v0.0.1", 0  \ %include "text.asm"
boot_string_01: db "Loading Operating system", 0     /

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

Both messages print fine, but, as you can see, once the second call print returns, the code falls through in the print routine and starts printing an empty message (because the SI register is pointing at the first zero-byte inserted by the times directive).
A far worse problem is, that because this (third) time, the ret instruction has no sensible return address on the stack, the computer will crash, but in a dangerous way because there's no telling about where execution will go to!

The solution is to prevent the code falling through in the print subroutine. Because our program has nothing more to do, we will insert a halting loop so the CPU doesn't waste precious cycles in a tight loop. The preferred way is cli hlt jmp $-2.

bits 16
org 0x7C00

boot:
    mov si, boot_string_00
    call print
    mov si, boot_string_01
    call print

    cli
    hlt
    jmp $-2

%include "print.asm"
%include "text.asm"

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

There's room for improvement in the present print routine

  • The BIOS.Teletype function 0Eh expects the BH register to contain the desired DisplayPage and the BL register the desired GraphicsColor for when the screen is in a graphics mode.
  • There's no sense in having the cli instruction in this code.
  • Testing the AL register for zero, would better use test al, al.
  • A simple loop like this should never use 2 branch instructions (while iterating).
  • This code depends on a correct DS segment register. Make sure the calling code has xor ax, ax mov ds, ax in accordance with the org 0x7C00 directive.
print:
    mov     bx, 0x0007   ; DisplayPage BH=0, GraphicsColor BL=7
    jmp     .begin       ; This is what makes 'empty messages' OK
.print:
    mov     ah, 0x0E     ; BIOS.Teletype
    int     0x10
.begin:
    lodsb
    test    al, al
    jnz     .print
    ret

Answering the additional question will improve the code even further

Also, I was wondering how i could implement newlines in assembly so that i could just use '\n' in my strings.

In NASM you can define string litterals in 3 different ways. Using single quotation marks ', using double quotation marks ", or using backticks `.

Only with the backticks can you include escape sequences in the text. In order to insert newlines \r\n, your text.asm would need to become:

boot_string_00: db `Placeholder OS Title v0.0.1\r\n`, 0
boot_string_01: db `Loading Operating system\r\n`, 0

to produce

Placeholder OS Title v0.0.1
Loading Operating system

Now that the newlines are embedded in the messages, you could consider simplifying the code and output the 2-line message as a whole, for a total savings of 7 bytes:

boot_string: db `Placeholder OS Title v0.0.1\r\nLoading Operating system\r\n`, 0

Why the \r\n sequence, and not simply \n?

For reference a quote from the Wikipedia article about escape sequences:

\n produces one byte, despite the fact that the platform may use more than one byte to denote a newline, such as the DOS/Windows CR-LF sequence, 0x0D 0x0A. The translation from 0x0A to 0x0D 0x0A on DOS and Windows occurs when the byte is written out to a file or to the console, and the inverse translation is done when text files are read.

The \n (newline) escape sequence only inserts the linefeed byte (10), but since this is bootloader code, and not DOS/Windows, BIOS will require both the carriage return byte (13) and the linefeed byte (10) in order to perform a move to the beginning of the next line. That's why we need to write \r\n.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文