Assembly “如何修复”;操作系统asm:113:error:TIMES值-138为负;汇编语言
我正在用汇编语言开发一个操作系统。 在某个特定的时间,我从NASM得到这个错误: os.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
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