Assembly “如何修复”;操作系统asm:113:error:TIMES值-138为负;汇编语言

Assembly “如何修复”;操作系统asm:113:error:TIMES值-138为负;汇编语言,assembly,nasm,x86-16,bootloader,osdev,Assembly,Nasm,X86 16,Bootloader,Osdev,我正在用汇编语言开发一个操作系统。 在某个特定的时间,我从NASM得到这个错误: os.asm:113:error:TIMES值-138为负 我想把这个项目进行到底。只有那样的错误让我失望 代码如下: BITS 16 start: mov ax, 07C0h ; Set up 4K stack space after this bootloader add ax, 288 ; (4096 + 512) / 16 bytes per paragraph

我正在用汇编语言开发一个操作系统。 在某个特定的时间,我从NASM得到这个错误:

os.asm:113:error:TIMES值-138为负

我想把这个项目进行到底。只有那样的错误让我失望

代码如下:

BITS 16

start:
    mov ax, 07C0h       ; Set up 4K stack space after this bootloader
    add ax, 288     ; (4096 + 512) / 16 bytes per paragraph
    mov ss, ax
    mov sp, 4096
    mov ax, 07C0h       ; Set data segment to where we're loaded
    mov ds, ax
    call cls
    MOV AH, 06h    ; Scroll up function
    XOR AL, AL     ; Clear entire screen
    XOR CX, CX     ; Upper left corner CH=row, CL=column
    MOV DX, 184FH  ; lower right corner DH=row, DL=column 
    MOV BH, 1Eh    ; YellowOnBlue
    INT 10H
    mov si, text_string ; Put string position into SI
    call print_string   ; Call our string-printing routine
push bx ;push registers
push cx
push dx
mov ah,0h
int 16h
       cmp al, '1'
       je reboot
       cmp al, '2'
       je shutdown
       cmp al, '3'
       je about
       cmp al, '4'
       je message
       cmp al, '5'
       je shutdown
       cmp al, '6'
       je credits

       jmp $            ; Jump here - infinite loop!


    text_string db '|Main Menu| |Smile OS V1.4|',13,10,'1) Reboot',13,10,'2) Shutdown',13,10,'3) About',13,10,'4) Message',13,10,'5) System Halt',13,10,'6) Credits',0
    about_string db '|About|',13,10,'Smile OS is a console based operating system in assembly language. 8 hours of intense work done by Alex~s Software. Many errors but solved and very successful.',13,10,'Press any key to go back!',0
    message_str db '|Message|',10,13,'Hello, World!',13,10,'Press any key to go back!',0
    cr_str db '|Credits|',13,10,'Copyright © 2018 Alex~s Software',13,10,'Main Programer: Alex',13,10,'Graphichs: What graphics?',13,10,'Idea:  nobody :)',0

reboot:
mov ax, 0
int 19h

shutdown:
mov ax, 0x1000
mov ax, ss
mov sp, 0xf000
mov ax, 0x5307
mov bx, 0x0001
mov cx, 0x0003
int 0x15

credits:
call cls
mov si, cr_str  ; Put string position into SI
call print_string   ; Call our string-printing routine
push bx ;push registers
push cx
push dx
mov ah,0h
int 16h
je start

message:
call cls
mov si, message_str ; Put string position into SI
call print_string   ; Call our string-printing routine
push bx ;push registers
push cx
push dx
mov ah,0h
int 16h
je start

cls:
  pusha
  mov ah, 0x00
  mov al, 0x03  ; text mode 80x25 16 colours
  int 0x10
  popa
  ret

about:
call cls
mov si, about_string    ; Put string position into SI
call print_string   ; Call our string-printing routine
push bx ;push registers
push cx
push dx
mov ah,0h
int 16h 
je start

print_string:           ; Routine: output string in SI to screen
    mov ah, 0Eh     ; int 10h 'print char' function

.repeat:
    lodsb           ; Get character from string
    cmp al, 0
    je .done        ; If char is zero, end of string
    int 10h         ; Otherwise, print it
    jmp .repeat

.done:
    ret     

times   512 - ($ - $$)    db  0
signature       dw      0xaa55
为什么时间价值是负的?Hhy其他人没有得到相同的错误?(或诸如此类)

我用这个:

NASM版本2.14

Oracle VM VirtualBox版本6.0.0\u RC1

windows版本0.5的rawwrite dd

对于编译:

nasm os.asm -f bin -o os.bin  
dd if=/dev/zero of=os.img bs=1024 count=1440   
dd if=os.bin of=os.img
从您的产品线开始:

times 512 - ($ - $$) db 0
意味着用零填充512字节内存块的其余部分,很可能您已经超过了这个值(大约138字节)。您可能需要缩短代码(或使其中一些字符串稍微不那么冗长)以使其适合

我的建议是从关于字符串的
开始,这似乎是不必要的。删除(相当自私的)
“Alex的软件完成了8个小时的紧张工作。许多错误都已解决,而且非常成功。”
将是一个良好的开端,因为它将节省93个字节。此外,您还可以删除重复的
“按任意键返回!”
(带前导和尾随CRLF),但需要额外增加几个字节的代码

这可以通过以下方式实现:

about_string db '|About|',13,10,'Smile OS is a console based operating system in assembly language.'
any_key      db 13,10,'Press any key to go back!',0
message_str db '|Message|',10,13,'Hello, World!',0
然后,可以以完全相同的方式打印about字符串(因为
about_字符串
没有终止
0
,因此也将打印
任何_键
),但消息字符串将变为两步操作:

mov si, message_str     --> mov si, message_str
call print_string           call print_string
                            mov si, any_key
                            call print_string
这将节省大约20个字节,在138个字节中节省大约113个字节

除此之外,似乎还有一些小事情可以节省非常小的空间,例如转换:

mov ah, 0x00
mov al, 0x03
进入:

或者将键输入重构到函数中(这也将保持堆栈平衡,这是当前代码似乎无法做到的,尽管我并不确定这是否必要-文档似乎表明,
ax
是唯一受影响的寄存器,这意味着您可能会删除推送和弹出):


当然,如果您做了所有这些,但仍然无法低于阈值,那么就不需要将字符串放入启动代码区域。您可以像第一步一样轻松地将它们存储在引导代码加载的另一个区域。这样,您就可以从启动代码区域中删除所有字符串,节省大约460多个字节(为代码加载字符串扇区,可能需要加上20个字节),因此它的值远远低于阈值。

它是负值,因为
510-code\u size
是负值。您的代码太大,无法作为MBR放在一个扇区中

我注释掉了填充线,并整理了你的文件。生成的二进制文件长度为652字节(包括填充后的2个字节)
512-650=-138

要么对程序进行编码,使其以更少的代码字节()执行相同的操作,要么将其拆分为引导扇区,在使用BIOS调用引导后从磁盘加载其余代码

由于有这么长的字符串,这里可能没有太多的空间来保存140字节。当然还有很大的节省空间,例如
mov-ax,07C0h
/
add-ax,288
mov-ax,07C0h+288
相比是愚蠢的,所以有3个字节可以轻松节省

看到和

Michael Petch的一般引导加载程序开发技巧()对于您想处理遗留的BIOS内容应该很有帮助

您的另一个选择是编写UEFI引导加载程序,而不是传统BIOS,因此您的代码以32位或64位模式启动。更重要的是,EFI“应用程序”可以是任何合理的大小,因此固件可以一次加载所有代码,而无需编写加载其余部分的代码


此外,您还错误地使用了
512-size
,这将不会为MBR签名在末尾留下2个字节的空间使用
510-($-$$)


有关这方面的更多信息,请参阅。

TL;DR:您的代码和数据太大,并且与文件最后2个字节中的启动签名冲突。下面的代码是一个软盘引导程序,它读取第二阶段(内核)并将控制权转移给它。提供的BPB用于1.44MiB软盘。与引导加载程序不同,stage2将加载到物理地址0x07e00(就在内存中引导加载程序的后面)。这允许您的代码最大为32.5KB。如果需要,您的第二阶段可以读取更多扇区。此代码的设计目的是让其他人可以将其用作读取第二阶段并将控制权转移到该阶段的模板


这个问题实际上已经在你之前的报告中得到了回答。关于使用
乘以512-($-$$)db 0x00进行填充的警告需要为510而不是512。答案是警告代码和数据太多(超过512字节),以及从NASM获得关于大小的更好错误/警告的方法。我的另一个答案中的注释将尺寸问题总结为:

如果文件os.bin超过512字节,则需要使用BIOS将更多磁盘扇区手动读取到内存中。从软盘上读取磁盘的操作可以用

没有提供的是一种机制(示例),它使用NASM和INT 13h/AH=2h在物理地址0x07E00的引导加载程序之后立即将更多磁盘扇区(也称为stage2)读入内存。对代码进行了注释,但实际上:

  • 启动代码正确设置段寄存器,并使用BIOS在DL寄存器中传递的引导驱动器。这在我的报告中讨论
  • 堆栈位于引导加载程序下方0x0000:0x7c00处。在将数据读入0x7c00到0x7dff之外的内存时,设置自己的堆栈非常重要,因为您不知道BIOS设置在哪里
    mov ax, 0x0003
    
    get_kbd: push bx
             push cx
             push dx
             xor  ax,ax
             int  16h
             je   start
             pop  dx
             pop  cx
             pop  bx
             ret
    
        jmp boot_start
        TIMES 3-($-$$) DB 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.
    
    bpb_disk_info:
        ; Dos 4.0 EBPB 1.44MB floppy
        OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
        bytesPerSector:    dw    512
        sectPerCluster:    db    1
        reservedSectors:   dw    1
        numFAT:            db    2
        numRootDirEntries: dw    224
        numSectors:        dw    2880
        mediaType:         db    0xf0
        numFATsectors:     dw    9
        sectorsPerTrack:   dw    18
        numHeads:          dw    2
        numHiddenSectors:  dd    0
        numSectorsHuge:    dd    0
        driveNum:          db    0
        reserved:          db    0
        signature:         db    0x29
        volumeID:          dd    0x2d7e5a1a
        volumeLabel:       db    "NO NAME    "
        fileSysType:       db    "FAT12   "
    
    STAGE2_ABS_ADDR   equ 0x07e00    ; Physical address of stage2
    
    ; Segment and Offset to use to transfer (FAR JMP) control to Stage2
    ;     Segment:Offset = 0x0000:0x7e00
    STAGE2_RUN_SEG   equ 0x0000
    STAGE2_RUN_OFS   equ STAGE2_ABS_ADDR
    
    %include "stage2info.inc"
    
    STAGE2_LOAD_SEG  equ STAGE2_ABS_ADDR>>4
                                    ; Segment to start reading Stage2 into
                                    ;     right after bootloader
    
    STAGE2_LBA_START equ 1          ; Logical Block Address(LBA) Stage2 starts on
                                    ;     LBA 1 = sector after boot sector
    STAGE2_LBA_END   equ STAGE2_LBA_START + NUM_STAGE2_SECTORS
                                    ; Logical Block Address(LBA) Stage2 ends at
    DISK_RETRIES     equ 3          ; Number of times to retry on disk error
    
    bits 16
    ORG 0x7c00
    
    ; Include a BPB (1.44MB floppy with FAT12) to be more comaptible with USB floppy media
    %include "bpb.inc"
    
    boot_start:
        xor ax, ax                  ; DS=SS=ES=0 for stage2 loading
        mov ds, ax
        mov ss, ax                  ; Stack at 0x0000:0x7c00
        mov sp, 0x7c00
        cld                         ; Set string instructions to use forward movement
    
        ; Read Stage2 1 sector at a time until stage2 is completely loaded
    load_stage2:
        mov [bootDevice], dl        ; Save boot drive
        mov di, STAGE2_LOAD_SEG     ; DI = Current segment to read into
        mov si, STAGE2_LBA_START    ; SI = LBA that stage2 starts at
        jmp .chk_for_last_lba       ; Check to see if we are last sector in stage2
    
    .read_sector_loop:
        mov bp, DISK_RETRIES        ; Set disk retry count
    
        call lba_to_chs             ; Convert current LBA to CHS
        mov es, di                  ; Set ES to current segment number to read into
        xor bx, bx                  ; Offset zero in segment
    
    .retry:
        mov ax, 0x0201              ; Call function 0x02 of int 13h (read sectors)
                                    ;     AL = 1 = Sectors to read
        int 0x13                    ; BIOS Disk interrupt call
        jc .disk_error              ; If CF set then disk error
    
    .success:
        add di, 512>>4              ; Advance to next 512 byte segment (0x20*16=512)
        inc si                      ; Next LBA
    
    .chk_for_last_lba:
        cmp si, STAGE2_LBA_END      ; Have we reached the last stage2 sector?
        jl .read_sector_loop        ;     If we haven't then read next sector
    
    .stage2_loaded:
        mov ax, STAGE2_RUN_SEG      ; Set up the segments appropriate for Stage2 to run
        mov ds, ax
        mov es, ax
    
        ; FAR JMP to the Stage2 entry point at physical address 0x07e00
        jmp STAGE2_RUN_SEG:STAGE2_RUN_OFS
    
    .disk_error:
        xor ah, ah                  ; Int13h/AH=0 is drive reset
        int 0x13
        dec bp                      ; Decrease retry count
        jge .retry                  ; If retry count not exceeded then try again
    
    error_end:
        ; Unrecoverable error; print drive error; enter infinite loop
        mov si, diskErrorMsg        ; Display disk error message
        call print_string
        cli
    .error_loop:
        hlt
        jmp .error_loop
    
    ; Function: print_string
    ;           Display a string to the console on display page 0
    ;
    ; Inputs:   SI = Offset of address to print
    ; Clobbers: AX, BX, SI
    
    print_string:
        mov ah, 0x0e                ; BIOS tty Print
        xor bx, bx                  ; Set display page to 0 (BL)
        jmp .getch
    .repeat:
        int 0x10                    ; print character
    .getch:
        lodsb                       ; Get character from string
        test al,al                  ; Have we reached end of string?
        jnz .repeat                 ;     if not process next character
    .end:
        ret
    
    ;    Function: lba_to_chs
    ; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
    ;              Works for all valid FAT12 compatible disk geometries.
    ;
    ;   Resources: http://www.ctyme.com/intr/rb-0607.htm
    ;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
    ;              https://stackoverflow.com/q/45434899/3857942
    ;              Sector    = (LBA mod SPT) + 1
    ;              Head      = (LBA / SPT) mod HEADS
    ;              Cylinder  = (LBA / SPT) / HEADS
    ;
    ;      Inputs: SI = LBA
    ;     Outputs: DL = Boot Drive Number
    ;              DH = Head
    ;              CH = Cylinder (lower 8 bits of 10-bit cylinder)
    ;              CL = Sector/Cylinder
    ;                   Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
    ;                   Sector in lower 6 bits of CL
    ;
    ;       Notes: Output registers match expectation of Int 13h/AH=2 inputs
    ;
    lba_to_chs:
        push ax                     ; Preserve AX
        mov ax, si                  ; Copy LBA to AX
        xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
        div word [sectorsPerTrack]  ; 32-bit by 16-bit DIV : LBA / SPT
        mov cl, dl                  ; CL = S = LBA mod SPT
        inc cl                      ; CL = S = (LBA mod SPT) + 1
        xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
        div word [numHeads]         ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
        mov dh, dl                  ; DH = H = (LBA / SPT) mod HEADS
        mov dl, [bootDevice]        ; boot device, not necessary to set but convenient
        mov ch, al                  ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
        shl ah, 6                   ; Store upper 2 bits of 10-bit Cylinder into
        or  cl, ah                  ;     upper 2 bits of Sector (CL)
        pop ax                      ; Restore scratch registers
        ret
    
    ; Uncomment these lines if not using a BPB (via bpb.inc)
    ; numHeads:        dw 2         ; 1.44MB Floppy has 2 heads & 18 sector per track
    ; sectorsPerTrack: dw 18
    
    bootDevice:      db 0x00
    diskErrorMsg:    db "Unrecoverable disk error!", 0
    
    ; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
    TIMES 510-($-$$) db  0
    dw 0xaa55
    
    ; Beginning of stage2. This is at 0x7E00 and will allow your stage2 to be 32.5KiB
    ; before running into problems. DL will be set to the drive number originally
    ; passed to us by the BIOS.
    
    NUM_STAGE2_SECTORS equ (stage2_end-stage2_start+511) / 512
                                    ; Number of 512 byte sectors stage2 uses.
    
    stage2_start:
        ; Insert stage2 binary here. It is done this way since we
        ; can determine the size(and number of sectors) to load since
        ;     Size = stage2_end-stage2_start
        incbin "stage2.bin"
    
    ; End of stage2. Make sure this label is LAST in this file!
    stage2_end:
    
    %include "stage2info.inc"
    ORG STAGE2_RUN_OFS
    
    BITS 16
    
    start:
        ; Removed the segment and stack code
        call cls
        MOV AH, 06h    ; Scroll up function
        XOR AL, AL     ; Clear entire screen
        XOR CX, CX     ; Upper left corner CH=row, CL=column
        MOV DX, 184FH  ; lower right corner DH=row, DL=column
        MOV BH, 1Eh    ; YellowOnBlue
        INT 10H
        mov si, text_string ; Put string position into SI
        call print_string   ; Call our string-printing routine
    push bx ;push registers
    push cx
    push dx
    mov ah,0h
    int 16h
           cmp al, '1'
           je reboot
           cmp al, '2'
           je shutdown
           cmp al, '3'
           je about
           cmp al, '4'
           je message
           cmp al, '5'
           je shutdown
           cmp al, '6'
           je credits
    
           jmp $            ; Jump here - infinite loop!
    
    
        text_string db '|Main Menu| |Smile OS V1.4|',13,10,'1) Reboot',13,10,'2) Shutdown',13,10,'3) About',13,10,'4) Message',13,10,'5) System Halt',13,10,'6) Credits',0
        about_string db '|About|',13,10,'Smile OS is a console based operating system in assembly language. 8 hours of intense work done by Alex~s Software. Many errors but solved and very successful.',13,10,'Press any key to go back!',0
        message_str db '|Message|',10,13,'Hello, World!',13,10,'Press any key to go back!',0
        cr_str db '|Credits|',13,10,'Copyright © 2018 Alex~s Software',13,10,'Main Programer: Alex',13,10,'Graphichs: What graphics?',13,10,'Idea:  nobody :)',0
    
    reboot:
    mov ax, 0
    int 19h
    
    shutdown:
    mov ax, 0x1000
    mov ax, ss
    mov sp, 0xf000
    mov ax, 0x5307
    mov bx, 0x0001
    mov cx, 0x0003
    int 0x15
    
    credits:
    call cls
    mov si, cr_str  ; Put string position into SI
    call print_string   ; Call our string-printing routine
    push bx ;push registers
    push cx
    push dx
    mov ah,0h
    int 16h
    je start
    
    message:
    call cls
    mov si, message_str ; Put string position into SI
    call print_string   ; Call our string-printing routine
    push bx ;push registers
    push cx
    push dx
    mov ah,0h
    int 16h
    je start
    
    cls:
      pusha
      mov ah, 0x00
      mov al, 0x03  ; text mode 80x25 16 colours
      int 0x10
      popa
      ret
    
    about:
    call cls
    mov si, about_string    ; Put string position into SI
    call print_string   ; Call our string-printing routine
    push bx ;push registers
    push cx
    push dx
    mov ah,0h
    int 16h
    je start
    
    print_string:           ; Routine: output string in SI to screen
        mov ah, 0Eh     ; int 10h 'print char' function
    
    .repeat:
        lodsb           ; Get character from string
        cmp al, 0
        je .done        ; If char is zero, end of string
        int 10h         ; Otherwise, print it
        jmp .repeat
    
    .done:
        ret
    
    # Build stage2 (kernel) FIRST as os.asm will include stage2.bin
    nasm -f bin stage2.asm -o stage2.bin
    # Build and combine stage1 (boot sector) and stage2 (kernel)
    nasm -f bin os.asm -o os.bin
    
    # Build 1.44MB disk image
    dd if=/dev/zero of=disk.img bs=1024 count=1440
    dd if=os.bin of=disk.img conv=notrunc
    
    nasm os.asm -f bin -o os.bin  
    dd if=/dev/zero of=os.img bs=1024 count=1440   
    dd if=os.bin of=os.img
    
    STAGE2_ABS_ADDR   equ 0x07e00    ; Physical address of stage2
    
    ; Segment and Offset to use to transfer (FAR JMP) control to Stage2
    ;     Segment:Offset = 0x07e0:0x0000
    STAGE2_RUN_SEG   equ STAGE2_ABS_ADDR>>4
    STAGE2_RUN_OFS   equ 0x0000