如何用汇编语言修复“os.asm:113:错误:TIMES值-138为负数”

fsd*_*fff 3 assembly nasm osdev bootloader x86-16

我正在用汇编语言开发一个操作系统。\n在某个时间我从 NASM 收到此错误:

\n\n
\n

os.asm:113: 错误: TIMES 值 -138 为负数

\n
\n\n

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

\n\n

这是代码:

\n\n
BITS 16\n\nstart:\n    mov ax, 07C0h       ; Set up 4K stack space after this bootloader\n    add ax, 288     ; (4096 + 512) / 16 bytes per paragraph\n    mov ss, ax\n    mov sp, 4096\n    mov ax, 07C0h       ; Set data segment to where we're loaded\n    mov ds, ax\n    call cls\n    MOV AH, 06h    ; Scroll up function\n    XOR AL, AL     ; Clear entire screen\n    XOR CX, CX     ; Upper left corner CH=row, CL=column\n    MOV DX, 184FH  ; lower right corner DH=row, DL=column \n    MOV BH, 1Eh    ; YellowOnBlue\n    INT 10H\n    mov si, text_string ; Put string position into SI\n    call print_string   ; Call our string-printing routine\npush bx ;push registers\npush cx\npush dx\nmov ah,0h\nint 16h\n       cmp al, '1'\n       je reboot\n       cmp al, '2'\n       je shutdown\n       cmp al, '3'\n       je about\n       cmp al, '4'\n       je message\n       cmp al, '5'\n       je shutdown\n       cmp al, '6'\n       je credits\n\n       jmp $            ; Jump here - infinite loop!\n\n\n    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\n    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\n    message_str db '|Message|',10,13,'Hello, World!',13,10,'Press any key to go back!',0\n    cr_str db '|Credits|',13,10,'Copyright \xc2\xa9 2018 Alex~s Software',13,10,'Main Programer: Alex',13,10,'Graphichs: What graphics?',13,10,'Idea:  nobody :)',0\n\nreboot:\nmov ax, 0\nint 19h\n\nshutdown:\nmov ax, 0x1000\nmov ax, ss\nmov sp, 0xf000\nmov ax, 0x5307\nmov bx, 0x0001\nmov cx, 0x0003\nint 0x15\n\ncredits:\ncall cls\nmov si, cr_str  ; Put string position into SI\ncall print_string   ; Call our string-printing routine\npush bx ;push registers\npush cx\npush dx\nmov ah,0h\nint 16h\nje start\n\nmessage:\ncall cls\nmov si, message_str ; Put string position into SI\ncall print_string   ; Call our string-printing routine\npush bx ;push registers\npush cx\npush dx\nmov ah,0h\nint 16h\nje start\n\ncls:\n  pusha\n  mov ah, 0x00\n  mov al, 0x03  ; text mode 80x25 16 colours\n  int 0x10\n  popa\n  ret\n\nabout:\ncall cls\nmov si, about_string    ; Put string position into SI\ncall print_string   ; Call our string-printing routine\npush bx ;push registers\npush cx\npush dx\nmov ah,0h\nint 16h \nje start\n\nprint_string:           ; Routine: output string in SI to screen\n    mov ah, 0Eh     ; int 10h 'print char' function\n\n.repeat:\n    lodsb           ; Get character from string\n    cmp al, 0\n    je .done        ; If char is zero, end of string\n    int 10h         ; Otherwise, print it\n    jmp .repeat\n\n.done:\n    ret     \n\ntimes   512 - ($ - $$)    db  0\nsignature       dw      0xaa55\n
Run Code Online (Sandbox Code Playgroud)\n\n

为什么时代价值是负数?为什么其他人没有得到同样的错误?(或者像那样)

\n\n

我用这个:

\n\n
\n

NASM 版本 2.14

\n\n

Oracle VM VirtualBox 版本 6.0.0_RC1

\n\n

rawwrite dd 适用于 Windows 版本 0.5。

\n
\n\n

对于编译:

\n\n
nasm os.asm -f bin -o os.bin  \ndd if=/dev/zero of=os.img bs=1024 count=1440   \ndd if=os.bin of=os.img\n
Run Code Online (Sandbox Code Playgroud)\n

Mic*_*tch 5

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

\n\n
\n\n

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

\n\n
\n

如果文件 os.bin超过 512 字节,那么您将需要使用 BIOS 手动将更多磁盘扇区读取到内存中。可以使用INT 13h/AH=2h来完成从软盘的磁盘读取。

\n
\n\n

没有提供一种机制(示例),该机制使用 NASM 和 INT 13h/AH=2h 在引导加载程序之后将更多磁盘扇区(又名 stage2)读取到物理地址 0x07E00 处的内存中。代码被注释了,但它实际上做了:

\n\n
    \n
  • 启动代码正确设置段寄存器并使用DL寄存器中 BIOS 传递的引导驱动器。这在我的Stackoverflow General Bootloader Tips中进行了讨论
  • \n
  • 该堆栈位于引导加载程序下方的 0x0000:0x7c00 处。将数据读入 0x7c00 到 0x7dff 之外的内存时,设置自己的堆栈非常重要,因为您不知道 BIOS 在哪里设置默认堆栈 ( SS:SP )。
  • \n
  • 其本身表现为一张带有 BIOS 参数块的 1.44MB 软盘,使其与真实硬件上启动的 USB 软盘驱动器仿真兼容
  • \n
  • Stage2 使用从 0x07e00 开始的 INT 13h/AH=2h 一次读取一个扇区。它支持错误重试。
  • \n
  • 一旦 Stage2 完成加载内核,引导加载程序会将控制权转移到位于 0x0000:0x7E00 ( stage2_start)的 stage2 代码
  • \n
  • Stage2 可以包含您希望运行的代码。您将有 32.5KiB 的空间来测试代码,而不受单个引导扇区(512 字节)的限制。
  • \n
  • Stage2 的磁盘扇区紧跟在磁盘映像中的引导扇区之后。
  • \n
  • 您的 Stage2(内核)代码进入stage2.asm. stage2.asm被组装到stage2.binos.asm包含二进制文件stage2.bin,以便可以确定 stage2 的大小,以便引导加载程序将其加载到内存中。
  • \n
  • stage2.asm必须使用ORG 0x7e00,因为上面的过程会将此代码加载到 0x7e00,因此 ORG(原点)必须设置为匹配。
  • \n
  • 该引导加载程序会将寄存器 DL 中的原始引导驱动器号(由 BIOS 传递)传递给 stage2 中运行的代码。
  • \n
  • 该文件stage2info.inc定义了常量来确定 stage2 的原点是什么,以及在将控制转移到 FAR JMP 时应使用什么段和偏移量。该文件的默认版本假定通过 0x0000:0x7e00 访问 stage2。该文件的替代版本2可用于将其变为 0x07e0:0x0000。后一个版本允许您的代码占用完整的 64kb 段。
  • \n
\n\n
\n\n

代码:

\n\n

bpb.公司

\n\n
    jmp boot_start\n    TIMES 3-($-$$) DB 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.\n\nbpb_disk_info:\n    ; Dos 4.0 EBPB 1.44MB floppy\n    OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses\n    bytesPerSector:    dw    512\n    sectPerCluster:    db    1\n    reservedSectors:   dw    1\n    numFAT:            db    2\n    numRootDirEntries: dw    224\n    numSectors:        dw    2880\n    mediaType:         db    0xf0\n    numFATsectors:     dw    9\n    sectorsPerTrack:   dw    18\n    numHeads:          dw    2\n    numHiddenSectors:  dd    0\n    numSectorsHuge:    dd    0\n    driveNum:          db    0\n    reserved:          db    0\n    signature:         db    0x29\n    volumeID:          dd    0x2d7e5a1a\n    volumeLabel:       db    "NO NAME    "\n    fileSysType:       db    "FAT12   "\n
Run Code Online (Sandbox Code Playgroud)\n\n

stage2info.inc

\n\n
STAGE2_ABS_ADDR   equ 0x07e00    ; Physical address of stage2\n\n; Segment and Offset to use to transfer (FAR JMP) control to Stage2\n;     Segment:Offset = 0x0000:0x7e00\nSTAGE2_RUN_SEG   equ 0x0000\nSTAGE2_RUN_OFS   equ STAGE2_ABS_ADDR\n
Run Code Online (Sandbox Code Playgroud)\n\n

操作系统.asm

\n\n
%include "stage2info.inc"\n\nSTAGE2_LOAD_SEG  equ STAGE2_ABS_ADDR>>4\n                                ; Segment to start reading Stage2 into\n                                ;     right after bootloader\n\nSTAGE2_LBA_START equ 1          ; Logical Block Address(LBA) Stage2 starts on\n                                ;     LBA 1 = sector after boot sector\nSTAGE2_LBA_END   equ STAGE2_LBA_START + NUM_STAGE2_SECTORS\n                                ; Logical Block Address(LBA) Stage2 ends at\nDISK_RETRIES     equ 3          ; Number of times to retry on disk error\n\nbits 16\nORG 0x7c00\n\n; Include a BPB (1.44MB floppy with FAT12) to be more comaptible with USB floppy media\n%include "bpb.inc"\n\nboot_start:\n    xor ax, ax                  ; DS=SS=ES=0 for stage2 loading\n    mov ds, ax\n    mov ss, ax                  ; Stack at 0x0000:0x7c00\n    mov sp, 0x7c00\n    cld                         ; Set string instructions to use forward movement\n\n    ; Read Stage2 1 sector at a time until stage2 is completely loaded\nload_stage2:\n    mov [bootDevice], dl        ; Save boot drive\n    mov di, STAGE2_LOAD_SEG     ; DI = Current segment to read into\n    mov si, STAGE2_LBA_START    ; SI = LBA that stage2 starts at\n    jmp .chk_for_last_lba       ; Check to see if we are last sector in stage2\n\n.read_sector_loop:\n    mov bp, DISK_RETRIES        ; Set disk retry count\n\n    call lba_to_chs             ; Convert current LBA to CHS\n    mov es, di                  ; Set ES to current segment number to read into\n    xor bx, bx                  ; Offset zero in segment\n\n.retry:\n    mov ax, 0x0201              ; Call function 0x02 of int 13h (read sectors)\n                                ;     AL = 1 = Sectors to read\n    int 0x13                    ; BIOS Disk interrupt call\n    jc .disk_error              ; If CF set then disk error\n\n.success:\n    add di, 512>>4              ; Advance to next 512 byte segment (0x20*16=512)\n    inc si                      ; Next LBA\n\n.chk_for_last_lba:\n    cmp si, STAGE2_LBA_END      ; Have we reached the last stage2 sector?\n    jl .read_sector_loop        ;     If we haven\'t then read next sector\n\n.stage2_loaded:\n    mov ax, STAGE2_RUN_SEG      ; Set up the segments appropriate for Stage2 to run\n    mov ds, ax\n    mov es, ax\n\n    ; FAR JMP to the Stage2 entry point at physical address 0x07e00\n    jmp STAGE2_RUN_SEG:STAGE2_RUN_OFS\n\n.disk_error:\n    xor ah, ah                  ; Int13h/AH=0 is drive reset\n    int 0x13\n    dec bp                      ; Decrease retry count\n    jge .retry                  ; If retry count not exceeded then try again\n\nerror_end:\n    ; Unrecoverable error; print drive error; enter infinite loop\n    mov si, diskErrorMsg        ; Display disk error message\n    call print_string\n    cli\n.error_loop:\n    hlt\n    jmp .error_loop\n\n; Function: print_string\n;           Display a string to the console on display page 0\n;\n; Inputs:   SI = Offset of address to print\n; Clobbers: AX, BX, SI\n\nprint_string:\n    mov ah, 0x0e                ; BIOS tty Print\n    xor bx, bx                  ; Set display page to 0 (BL)\n    jmp .getch\n.repeat:\n    int 0x10                    ; print character\n.getch:\n    lodsb                       ; Get character from string\n    test al,al                  ; Have we reached end of string?\n    jnz .repeat                 ;     if not process next character\n.end:\n    ret\n\n;    Function: lba_to_chs\n; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).\n;              Works for all valid FAT12 compatible disk geometries.\n;\n;   Resources: http://www.ctyme.com/intr/rb-0607.htm\n;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion\n;              /sf/ask/3180442961/\n;              Sector    = (LBA mod SPT) + 1\n;              Head      = (LBA / SPT) mod HEADS\n;              Cylinder  = (LBA / SPT) / HEADS\n;\n;      Inputs: SI = LBA\n;     Outputs: DL = Boot Drive Number\n;              DH = Head\n;              CH = Cylinder (lower 8 bits of 10-bit cylinder)\n;              CL = Sector/Cylinder\n;                   Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL\n;                   Sector in lower 6 bits of CL\n;\n;       Notes: Output registers match expectation of Int 13h/AH=2 inputs\n;\nlba_to_chs:\n    push ax                     ; Preserve AX\n    mov ax, si                  ; Copy LBA to AX\n    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV\n    div word [sectorsPerTrack]  ; 32-bit by 16-bit DIV : LBA / SPT\n    mov cl, dl                  ; CL = S = LBA mod SPT\n    inc cl                      ; CL = S = (LBA mod SPT) + 1\n    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV\n    div word [numHeads]         ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS\n    mov dh, dl                  ; DH = H = (LBA / SPT) mod HEADS\n    mov dl, [bootDevice]        ; boot device, not necessary to set but convenient\n    mov ch, al                  ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS\n    shl ah, 6                   ; Store upper 2 bits of 10-bit Cylinder into\n    or  cl, ah                  ;     upper 2 bits of Sector (CL)\n    pop ax                      ; Restore scratch registers\n    ret\n\n; Uncomment these lines if not using a BPB (via bpb.inc)\n; numHeads:        dw 2         ; 1.44MB Floppy has 2 heads & 18 sector per track\n; sectorsPerTrack: dw 18\n\nbootDevice:      db 0x00\ndiskErrorMsg:    db "Unrecoverable disk error!", 0\n\n; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes\nTIMES 510-($-$$) db  0\ndw 0xaa55\n\n; Beginning of stage2. This is at 0x7E00 and will allow your stage2 to be 32.5KiB\n; before running into problems. DL will be set to the drive number originally\n; passed to us by the BIOS.\n\nNUM_STAGE2_SECTORS equ (stage2_end-stage2_start+511) / 512\n                                ; Number of 512 byte sectors stage2 uses.\n\nstage2_start:\n    ; Insert stage2 binary here. It is done this way since we\n    ; can determine the size(and number of sectors) to load since\n    ;     Size = stage2_end-stage2_start\n    incbin "stage2.bin"\n\n; End of stage2. Make sure this label is LAST in this file!\nstage2_end:\n
Run Code Online (Sandbox Code Playgroud)\n\n

您将要测试的所有代码放入文件中,stage2.asm该文件将包含在我的os.asm. 删除了开头和结尾不必要部分的代码版本是:

\n\n

阶段2.asm

\n\n
%include "stage2info.inc"\nORG STAGE2_RUN_OFS\n\nBITS 16\n\nstart:\n    ; Removed the segment and stack code\n    call cls\n    MOV AH, 06h    ; Scroll up function\n    XOR AL, AL     ; Clear entire screen\n    XOR CX, CX     ; Upper left corner CH=row, CL=column\n    MOV DX, 184FH  ; lower right corner DH=row, DL=column\n    MOV BH, 1Eh    ; YellowOnBlue\n    INT 10H\n    mov si, text_string ; Put string position into SI\n    call print_string   ; Call our string-printing routine\npush bx ;push registers\npush cx\npush dx\nmov ah,0h\nint 16h\n       cmp al, \'1\'\n       je reboot\n       cmp al, \'2\'\n       je shutdown\n       cmp al, \'3\'\n       je about\n       cmp al, \'4\'\n       je message\n       cmp al, \'5\'\n       je shutdown\n       cmp al, \'6\'\n       je credits\n\n       jmp $            ; Jump here - infinite loop!\n\n\n    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\n    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\n    message_str db \'|Message|\',10,13,\'Hello, World!\',13,10,\'Press any key to go back!\',0\n    cr_str db \'|Credits|\',13,10,\'Copyright \xc2\xa9 2018 Alex~s Software\',13,10,\'Main Programer: Alex\',13,10,\'Graphichs: What graphics?\',13,10,\'Idea:  nobody :)\',0\n\nreboot:\nmov ax, 0\nint 19h\n\nshutdown:\nmov ax, 0x1000\nmov ax, ss\nmov sp, 0xf000\nmov ax, 0x5307\nmov bx, 0x0001\nmov cx, 0x0003\nint 0x15\n\ncredits:\ncall cls\nmov si, cr_str  ; Put string position into SI\ncall print_string   ; Call our string-printing routine\npush bx ;push registers\npush cx\npush dx\nmov ah,0h\nint 16h\nje start\n\nmessage:\ncall cls\nmov si, message_str ; Put string position into SI\ncall print_string   ; Call our string-printing routine\npush bx ;push registers\npush cx\npush dx\nmov ah,0h\nint 16h\nje start\n\ncls:\n  pusha\n  mov ah, 0x00\n  mov al, 0x03  ; text mode 80x25 16 colours\n  int 0x10\n  popa\n  ret\n\nabout:\ncall cls\nmov si, about_string    ; Put string position into SI\ncall print_string   ; Call our string-printing routine\npush bx ;push registers\npush cx\npush dx\nmov ah,0h\nint 16h\nje start\n\nprint_string:           ; Routine: output string in SI to screen\n    mov ah, 0Eh     ; int 10h \'print char\' function\n\n.repeat:\n    lodsb           ; Get character from string\n    cmp al, 0\n    je .done        ; If char is zero, end of string\n    int 10h         ; Otherwise, print it\n    jmp .repeat\n\n.done:\n    ret\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,您可以使用以下命令组装并构建磁盘映像1

\n\n
# Build stage2 (kernel) FIRST as os.asm will include stage2.bin\nnasm -f bin stage2.asm -o stage2.bin\n# Build and combine stage1 (boot sector) and stage2 (kernel)\nnasm -f bin os.asm -o os.bin\n\n# Build 1.44MB disk image\ndd if=/dev/zero of=disk.img bs=1024 count=1440\ndd if=os.bin of=disk.img conv=notrunc\n
Run Code Online (Sandbox Code Playgroud)\n\n

以 开头的行#只是注释,不是命令。

\n\n
\n\n

截图

\n\n

主菜单显示为:

\n\n

在此输入图像描述

\n\n

信用屏幕显示为:

\n\n

在此输入图像描述

\n\n
\n\n

笔记:

\n\n

1您使用的这些命令包含错误:

\n\n
nasm os.asm -f bin -o os.bin  \ndd if=/dev/zero of=os.img bs=1024 count=1440   \ndd if=os.bin of=os.img\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后一行应确保1.44MB 磁盘映像在写入文件dd if=os.bin of=os.img conv=notrunc时不会被截断。os.bin如果您查看磁盘映像的大小,您可能会发现这不是预期的 1474560

\n\n
\n\n

2stage2info.inc使用 0x07e0:0x0000 而不是 0x0000:0x7e00 将控制权转移到 stage2 的替代文件:

\n\n
STAGE2_ABS_ADDR   equ 0x07e00    ; Physical address of stage2\n\n; Segment and Offset to use to transfer (FAR JMP) control to Stage2\n;     Segment:Offset = 0x07e0:0x0000\nSTAGE2_RUN_SEG   equ STAGE2_ABS_ADDR>>4\nSTAGE2_RUN_OFS   equ 0x0000\n
Run Code Online (Sandbox Code Playgroud)\n


归档时间:

查看次数:

2027 次

最近记录:

6 年,12 月 前