Как исправить «os.asm: 113: ошибка: значение TIMES -138 отрицательно» на языке ассемблера - PullRequest
0 голосов
/ 19 декабря 2018

Я разрабатываю операционную систему на ассемблере.В определенное время я получаю эту ошибку от NASM:

os.asm: 113: ошибка: значение 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

Почему значение Times отрицательно?Ххх другие не получают такую ​​же ошибку?(Или вот так)

Я использую это:

NASM версия 2.14

Oracle VM VirtualBox версия 6.0.0_RC1

rawwrite dd для windowsверсия 0.5.

Для компиляции:

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

Ответы [ 3 ]

0 голосов
/ 19 декабря 2018

Это отрицательно, потому что 510 - code_size отрицательно.Ваш код слишком велик, чтобы поместиться в один сектор как MBR.

Я закомментировал строку дополнения и собрал ваш файл.Полученный двоичный файл имеет длину 652 байта (включая 2 байта после заполнения).512 - 650 = -138.

Либо код вашей игры в гольф, так что она делает то же самое с меньшим количеством байтов кода ( Советы по игре в гольф с машинным кодом x86 / x64 )или разбейте его на загрузочный сектор, который загружает остальную часть кода с диска после загрузки с вызовами BIOS.

Со всеми этими длинными строками, вероятно, здесь не так много места для сохранения 140 байтов.Конечно, есть место для значительной экономии, например, mov ax, 07C0h / add ax, 288 глупо против mov ax, 07C0h + 288, поэтому вы можете легко сохранить 3 байта.

См. Включите загрузчик для загрузки второго сектораUSB и Как загрузить ядро ​​или использовать больше места в собственном загрузчике?

Общие советы по разработке загрузчика Майкла Петча ( Загрузчик не прыгаетк коду ядра ) может быть полезно, если вы хотите возиться с устаревшими компонентами BIOS.

Другой вариант - написать загрузчик UEFI вместо устаревшего BIOS, поэтому ваш код начинается с 32 или 64-битовый режим.И что более важно, «приложение» EFI может иметь любой разумный размер, поэтому микропрограммное обеспечение может загружать весь ваш код сразу, вместо того, чтобы вам приходилось писать код, который загружает остальную часть себя.


Кроме того,вы неправильно использовали 512 - size, что не оставляет места для подписи MBR 2 байта в конце. Использование 510 - ($ - $$)

См. Сборка Почему, когда я изменяю код, результат становится не загружаемым для получения дополнительной информации об этом.

0 голосов
/ 20 декабря 2018

TL; DR : Ваш код и данные слишком велики и конфликтуют с загрузочной подписью в последних 2 байтах файла.Приведенный ниже код представляет собой загрузчик гибких дисков, который читает второй этап (ваше ядро) и передает ему управление.Предоставленный BPB для 1.44MiB дискеты.В отличие от загрузчика, stage2 будет загружен по физическому адресу 0x07e00 (сразу после загрузчика в памяти).Это позволяет вашему коду иметь размер до 32,5 КБ.Ваш второй этап может читать больше секторов, если это необходимо.Этот код был разработан таким образом, чтобы другие могли использовать его в качестве шаблона для чтения второго этапа и передачи ему контроля.


На этот вопрос уже был дан ответ в соответствии с вашим предыдущим Stackoverflow Question .Существует предупреждение о заполнении с использованием times 512 - ($ - $$) db 0x00, которое должно быть 510, а не 512. Ответ предупреждает о слишком большом количестве кода и данных (превышающем 512 байт), а также способ получить лучшие ошибки / предупреждения от NASM о размере.Примечание в моем другом ответе суммирует проблему размера следующим образом:

Если файл os.bin имеет размер больше 512 байт , то вам потребуется использовать BIOS для чтения дополнительных дисков.секторы в память вручную.Чтение диска с дискеты можно выполнить с помощью INT 13h / AH = 2h .

Не был представлен механизм (пример), который использует NASM и INT 13h /AH = 2h, чтобы прочитать больше секторов диска (он же stage2) в память сразу после загрузчика по физическому адресу 0x07E00.Код комментируется, но он эффективно выполняет:

  • Код запуска правильно устанавливает регистры сегментов и использует загрузочный диск, переданный BIOS в регистре DL .Это обсуждается в моих общих советах по загрузчику Stackoverflow
  • Стек находится под загрузчиком в 0x0000: 0x7c00.Установка собственного стека важна при чтении данных в память за пределами 0x7c00 до 0x7dff, поскольку вы не знаете, где BIOS устанавливает стек по умолчанию ( SS: SP ).
  • Представляет собой дискету объемом 1,44 МБ с блоком параметров BIOS, что делает ее совместимой с эмуляцией USB-дисковода для гибких дисков загрузка на реальном оборудовании.
  • Этап 2 читается как секторвремя, используя INT 13h / AH = 2h, начиная с 0x07e00.Он поддерживает повторные попытки при ошибках.
  • Как только Stage2 завершит загрузку ядра, загрузчик передает управление коду stage2 по адресу 0x0000: 0x7E00 (stage2_start)
  • Stage2 может содержать код, который вы хотитебежать.У вас будет 32,5 КБ места для тестирования кода, а не ограничения одного загрузочного сектора (512 байт).
  • Секторы диска Stage2 сразу же следуют за загрузочным сектором в образе диска.
  • Ваш код Stage2 (ядро) входит в stage2.asm.stage2.asm собирается в stage2.bin, а os.asm включает двоичный файл stage2.bin, так что размер stage2 может быть определен для целей загрузки его в память загрузчиком.
  • stage2.asm mustиспользуйте ORG 0x7e00, так как описанный выше процесс будет загружать этот код в 0x7e00, поэтому ORG (точка начала) должна быть настроена на совпадение.
  • Этот загрузчик пропустит исходный номер загрузочного диска (переданный BIOS)в регистре DL к коду, работающему в stage2.
  • Файл stage2info.inc определяет константы, чтобы определить, что является исходной точкой stage2, и какой сегмент и смещение следует использовать для JAR FAR при передаче управления ему,Версия этого файла по умолчанию предполагает, что доступ к stage2 осуществляется через 0x0000: 0x7e00.Альтернативная версия 2 файла может быть использована для получения 0x07e0: 0x0000.Последняя версия позволяет вашему коду занимать полный сегмент размером 64 КБ.

Код:

bpb.inc :

    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   "

stage2info.inc :

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

os.asm :

%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:

Вы размещаете весь код, который хотитетест в файле stage2.asm, который будет включен в мою версию os.asm.Версия вашего кода с удаленными ненужными частями в начале и конце:

stage2.asm

%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

Затем вы собираете и формируете образ диска с помощью этих команд 1 :

# 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

Строки, начинающиеся с #, являются просто комментариями и не являются командами.


Снимки экрана

Главное меню выглядит так:

enter image description here

Экран кредита отображается как:

enter image description here


Примечания:

1 Эти команды содержат ошибку:

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

Последняя строка должна быть dd if=os.bin of=os.img conv=notrunc, чтобы образ диска размером 1,44 МБ не усекался при записи в него файла os.bin.Если вы посмотрите на размер образа вашего диска, вы, вероятно, увидите, что не соответствует ожидаемому 1474560 .


2 Альтернативный файл stage2info.incиспользуйте 0x07e0: 0x0000 вместо 0x0000: 0x7e00 для передачи управления на stage2:

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
0 голосов
/ 19 декабря 2018

Поскольку ваша строка:

times 512 - ($ - $$) db 0

предназначена для того, чтобы заполнить оставшуюся часть 512-байтового фрагмента памяти нулями, вполне вероятно, что у вас уже превышено (напримерно 138 байт).Вам, вероятно, нужно просто сократить код (или сделать некоторые из этих строк менее подробными), чтобы он подходил.

Мой совет будет начинать с about_string, что выглядит как способ больше, чем необходимо.Удаление (довольно корыстного) " 8 hours of intense work done by Alex~s Software. Many errors but solved and very successful." было бы хорошим началом, поскольку это сэкономило бы 93 байта.Кроме того, за счет нескольких дополнительных байтов кода вы можете удалить дубликат "Press any key to go back!" (с начальным и конечным CR LF ).

Thisможет быть сделано с чем-то вроде:

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_string не имеет завершающего 0, следовательно, также будет печатать any_key), но сообщениестрока будет преобразована в двухэтапную операцию:

mov si, message_str     --> mov si, message_str
call print_string           call print_string
                            mov si, any_key
                            call print_string

Это сэкономит еще около 20 байтов, что даст вам экономию около 113 из 138 байтов.

Кроме этого, естьПохоже, есть несколько незначительных вещей, которые могут сэкономить очень мало места, например, преобразование:

mov ah, 0x00
mov al, 0x03

в:

mov ax, 0x0003

или рефакторинг клавишного ввода в функцию (это будеттакже сохраняйте баланс в стеке, чего, по-видимому, ваш текущий код не выполняет, хотя на самом деле я не уверен, что это необходимо - документация предполагает, что ax - единственный затронутый регистр, что означает, что вы можетепросто удалите толчки и треск):

get_kbd: push bx
         push cx
         push dx
         xor  ax,ax
         int  16h
         je   start
         pop  dx
         pop  cx
         pop  bx
         ret

Конечно, если вы все это сделаете, и вы все еще не сможете подняться ниже порога, нет ничего для , требующего Вы должны поместить строку в область кода загрузки.Вы можете так же легко сохранить их в другой области, в которую загружается загрузочный код, как первый шаг.Таким образом, вы убираете все строк из области кода загрузки, сохраняя около 460 нечетных байтов (добавляя, может быть, двадцать назад для кода для загрузки секторов строк) и, следовательно, получаете хорошо под порог.

...