Why does nasm mess up assembling when strings are next to subroutines rather than in the bottom?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
atomtables
Posts: 14
Joined: Fri Nov 08, 2024 5:08 pm
Libera.chat IRC: atomtables

Why does nasm mess up assembling when strings are next to subroutines rather than in the bottom?

Post by atomtables »

Hi. I modified my bootloader code to add a menu that allows a user to select what graphical interface they would like, since that would need to be changed using a bios interrupt. When doing this, I noticed that including the subroutine had large effects, including corruption(???) of other assembled code (not like the EIP reached strings, but rather that the assembler actually had an aneurysm and replaced random working bytes that had no relation to what got affected

The original code:

Code: Select all

jmp     $
mov     edi, 0x8000 ; basically a temp buffer to store
jmp     $
call    ata_lba_read; TODO: ssumption that we're booting off of 0x80
jmp     $

mov     si, [0x8010] ; amount of entries
mov     eax, [0x500+PART_START_SECTOR]    ; we need to get the lba
inc     eax; load next partition 
What it should assemble into (i removed the whole subroutine while debugging before):

Code: Select all

 => 0x61c:       jmp    0x61c
   0x61e:       mov    $0x8000,%edi
   0x623:       jmp    0x623
   0x625:       call   0xa9e
   0x62a:       jmp    0x62a
   0x62c:       mov    0x8010,%si
   0x633:       mov    0xb00,%eax
   0x638:       inc    %eax
When I included the subroutine in the middle:

Code: Select all

 => 0x61c:       jmp    0x61c
   0x61e:       mov    $0x8000,%edi
   0x623:       jmp    0x5dd
   0x625:       add    %cl,%ah
   0x627:       add    $0xfeeb0000,%eax
   0x62c:       mov    0x8010,%si
   0x633:       mov    0xc58,%eax
   0x638:       inc    %eax
   0x639:       mov    $0x1,%cl
   0x63b:       add    $0x200,%edi 
it's not just a shift or whatever, the physical bytes had changed. I think the eb fe had b8 replaced midway, because while testing (without the jmp $) I saw
0x61f: mov edi,0x8000
become
0x61f: mov edi,0xb800

which completely broke the bootloader since edi contained the destination buffer to load partition data in. When i found the fix (move the strings from right above the subroutine to the end of the block with all the other free space), it seemed to somehow work fine. Why is this? This makes no sense to me as there is a 512 byte reserve space after the subroutine that has no effect either.

bootloader main (boot.aex.asm)

Code: Select all

; booting from AEX so we should be at 0x500
; our segments are clobbered so let's fix that
mov     ax, 0x50
mov     ds, ax
mov     es, ax
mov     bx, 0x8000
mov     ss, bx
xor     sp, sp
jmp     0x50:next

next:
mov     [BOOT_DRIVE], dl
mov     [PART_START_SECTOR], edi ; receive this from the start program
cld

KERNEL_OFFSET equ 0x10000 ; The same one we used when linking the kernel

jmp     switch_to_32bit ; disable interrupts, load GDT,  etc. Finally jumps to 'BEGIN_PM'
jmp     $               ; Never executed

disk_error:
    ; mov     si, disk_error_msg
    ; Print the error message by moving it to the video memory
    mov     dl, ah
    mov     ah, 0x0E
    mov     al, 'E'
    int     0x10
done:
    jmp     $

; use the INT 0x15, eax= 0xE820 BIOS function to get a memory map
; note: initially di is 0, be sure to set it to a value so that the BIOS code will not be overwritten. 
;       The consequence of overwriting the BIOS code will lead to problems like getting stuck in `int 0x15`
; inputs: es:di -> destination buffer for 24 byte entries
; outputs: bp = entry count, trashes all registers except esi
[bits 16]
mmap_ent equ 0x2000             ; the number of entries will be stored at 0x2000
do_e820:
    xor di, di
    mov es, di
    mov di, 0x2004          ; Set di to 0x8004. Otherwise this code will get stuck in `int 0x15` after some entries are fetched 
	xor ebx, ebx		; ebx must be 0 to start
	xor bp, bp		; keep an entry count in bp
	mov edx, 0x534D4150	; Place "SMAP" into edx
	mov eax, 0x0000e820
	mov [es:di + 20], dword 1	; force a valid ACPI 3.X entry
	mov ecx, 24		; ask for 24 bytes
	int 0x15
	jc short .failed	; carry set on first call means "unsupported function"
	mov edx, 0x534D4150	; Some BIOSes apparently trash this register?
	cmp eax, edx		; on success, eax must have been reset to "SMAP"
	jne short .failed
	test ebx, ebx		; ebx = 0 implies list is only 1 entry long (worthless)
	je short .failed
	jmp short .jmpin
.e820lp:
	mov eax, 0x0000e820		; eax, ecx get trashed on every int 0x15 call
	mov [es:di + 20], dword 1	; force a valid ACPI 3.X entry
	mov ecx, 24		; ask for 24 bytes again
	int 0x15
	jc short .e820f		; carry set means "end of list already reached"
	mov edx, 0x0534D4150	; repair potentially trashed register
.jmpin:
	jcxz .skipent		; skip any 0 length entries
	cmp cl, 20		; got a 24 byte ACPI 3.X response?
	jbe short .notext
	test byte [es:di + 20], 1	; if so: is the "ignore this data" bit clear?
	je short .skipent
.notext:
	mov ecx, [es:di + 8]	; get lower uint32_t of memory region length
	or ecx, [es:di + 12]	; "or" it with upper uint32_t to test for zero
	jz .skipent		; if length uint64_t is 0, skip entry
	inc bp			; got a good entry: ++count, move to next storage spot
	add di, 24
.skipent:
	test ebx, ebx		; if ebx resets to 0, list is complete
	jne short .e820lp
.e820f:
	mov [es:mmap_ent], bp	; store the entry count
	clc			; there is "jc" on end of list to this point, so the carry must be cleared
	ret
.failed:
    mov [es:mmap_ent], bp	; store the entry count
	stc			; "function unsupported" error exit
	ret
[bits 16]
switch_to_32bit:
    ; before all that, get memory
    ; we want to get the memory map to store
    ; for the later kernel use.
    call   do_e820
    mov di, 0x50
    mov es, di
    ; jmp     $
    ; jc     disk_error
    ; jc     disk_error ; if carry is set, error occurred
    finished:
    ; if our drive isnt 0x80, then take precautionary measures
    mov     dl, [BOOT_DRIVE]
    ; cmp     dl, 0x80
    ; je      .continue
    ; otherwise, load via bios
    ; call    READ_BIOS
    ; call    READ_BIOS_KERNEL
    
    .continue:
    call    choose_graphics_mode
    call    enable_a20          ; 0. enable A20 line
    cli                         ; 1. disable interrupts
    nop                         ; 1.1. some CPUs require a delay after cli
    lgdt    [gdt_descriptor]    ; 2. load the GDT descriptor
    mov     eax, cr0
    or      eax, 0x1            ; 3. set 32-bit mode bit in cr0
    mov     cr0, eax
                                ; 4. far jump by using a different segment
    jmp     CODE_SEG:init_32bit+0x500

[bits 16]
strcmp:
    push    di
    push    si
    repe    cmpsb
    pop     si
    pop     di
    je      .match
    xor     ax, ax
    ret
.match:
    mov     ax, 1
    ret

; NEVER MIX THE BLOODY 16 AND 32
; I HATE LIFE DEBUGGED TS FOR 2 HOURS
[bits 32]
strcmp32:
    push    edi
    push    esi
    repe    cmpsb
    je      .match
    pop     esi
    pop     edi
    xor     ax, ax
    ret
.match:
    pop     esi
    pop     edi
    mov     ax, 1
    ret

[bits 32]
init_32bit:                 ; we are now using 32-bit instructions
                            ; 5. update the segment registers
    mov     word ax, DATA_SEG
    mov     ds, ax
    mov     ss, ax
    mov     es, ax
    mov     fs, ax
    mov     gs, ax

    mov     ebp, 0x10000    ; 6. update the stack right at the top of the free space
    mov     esp, ebp

    mov     eax, [0x500+PART_START_SECTOR]    ; we need to get the lba
    mov     cl,  0x1    ; this command only reliably reads one sector
    mov     edi, 0x8000 ; basically a temp buffer to store
    [bits 32]
    call    ata_lba_read; TODO: ssumption that we're booting off of 0x80

    mov     si, [0x8010] ; amount of entries
    mov     eax, [0x500+PART_START_SECTOR]    ; we need to get the lba
    inc     eax; load next partition
    mov     cl,  0x1    ; this command only reliably reads one sector
    add     edi, 512
    ; jmp     $
    call    ata_lba_read; TODO: ssumption that we're booting off of 0x80
    found:
    ; the first file entry is at 0x200
    mov     edi, 0x8201 ; 0x8200
    mov     word [0x7bfe], 0x8400 ; the next sector (next place we need to load at)
    mov     dword [0x7bfa], 1    ; the difference in sector
    check_name:
    cmp     si, 0   ; if the amount of files left is 0
    je      failed       ; hang (there is no bootable file)
    ; check the file name (match1 is the name)
    push    esi
    mov     esi, match1+0x500
    ; DI should be at the aligned file name
    mov     ecx, 7
    call    strcmp32  ; compare the file name
    ; jmp     $
    cmp     ax, 1
    pushf
    add     di, 8   ; 0x209
    popf
    pop     esi
    jne     tryagain; if it fails then just restart then and there
    push    esi
    ; now let's compare file extension
    mov     esi, match2+0x500
    mov     ecx, 3 ; stupid ahh code i hate programming
    call    strcmp32  ; compare the file extension
    cmp     eax, 1
    pop     esi
    jne     tryagain

    ; now that we've established a file, we subtract di and
    ; runexe
    add     edi, 3
    xor     edx, edx
    mov     eax, [edi + 4]  ; get the size of the file
    mov     esi, 512
    div     esi             ; divide by 512 to get amount of sectors it spans
    inc     eax             ; account for the last partial sector
    mov     ebx, eax        ; ebx now contains number of sectors (size)
    
    xor     edx, edx
    mov     eax, [edi]
    div     esi             ; divide by 512 to get amount of sectors it spans
    ; inc     eax             ; part_start_sector includes the vba, so ignore that
    add     eax, [0x500+PART_START_SECTOR]
                            ; we get the full dword of the partition area and add it
    ; eax contains LBA of the file start
    ; and edx contains the offset (% 512)
    xor     esi, esi
    mov     edi, 0x8000
    read:
        mov     cl, 1
        call    ata_lba_read
        mov     [0xb8000], 'D'
        cmp     ebx, esi
        jne     .continue
        jmp     .done
        .continue:
        inc     esi
        inc     eax
        add     edi, 512
        mov     [0xb8000], ' '
        jmp     read
        .done:
        mov     [0xb8002], 'D'
        jmp     $
        call    CODE_SEG:0x8000
        jmp     $
    tryagain:
    ; try again
    add     di, 12  ; 20 bytes later
    sub     si, 1   ; we went through one file
    mov     dx, [0x7bfe]
    cmp     di, dx  ; make sure we aren't at the end of the sector
    jge     getnewvalues ; if we are then we load new files
    jmp     check_name
    getnewvalues:
    xor     ah, ah
    mov     eax, [0x7bfa]; get the last increment in sectors
    add     al, 1       ; add one more to ah
    mov     dword [0x7bfa], eax; and put the increment back

    add     eax, [0x500+PART_START_SECTOR]; offset by the start
    mov     cl, 0x01    ; one sector as usual
    mov     edi, 0x8200 ; same sector
    call    ata_lba_read
    ; after this point, we only read one sector.
    ; hold onto the value of bx so we can read again later
    jmp     check_name
    failed:
    jmp     $

%include "src/bootloader_partition/boot.aex/graphicsmode.asm"
%include "src/bootloader_partition/boot.aex/a20.asm"
%include "src/bootloader_partition/boot.aex/gdt.asm"
%include "src/bootloader_partition/boot.aex/read.asm"

BOOT_DRIVE: db 0 
PART_START_SECTOR: dd 0
align 4
lba:
lbasize: db 0x10
lbaresv: db 0x00
lbamaxs: dw 0x0001
lbaoffs: dw 0x0000
lbasegs: dw 0x1000
lbalow4: dd 0x00000000
lbahigh: dd 0x00000000
align 4
lbaFIRSTPART:
db 0x10
db 0x00
dw 0x0001
dw 0x7c00
dw 0x0000
dd 0x00000000
dd 0x00000000
match1: db "LOADER", 0
match2: db "AEX", 0

os_identifier: db "AstatineOS dev build, bootloader", 0
os_callforaction: db "Choose an option for display graphics below", 0
os_textmode: db " Text mode (80x25) ", 0
os_bitmapmode: db " Display (320x200x256, 53x25) ", 0
currently_selected_graphics_mode: db 0;
a20.asm:

Code: Select all

; credit for this goes to the really really heplful people of osdev.org
[bits 16]
;	out:
;		ax - state (0 - disabled, 1 - enabled)
get_a20_state:
	pushf
	push si
	push di
	push ds
	push es
	cli

	mov ax, 0x0000					;	0x0000:0x0500(0x00000500) -> ds:si
	mov ds, ax
	mov si, 0x0500

	not ax							;	0xffff:0x0510(0x00100500) -> es:di
	mov es, ax
	mov di, 0x0510

	mov al, [ds:si]					;	save old values
	mov byte [.BufferBelowMB], al
	mov al, [es:di]
	mov byte [.BufferOverMB], al

	mov ah, 1						;	check byte [0x00100500] == byte [0x0500]
	mov byte [ds:si], 0
	mov byte [es:di], 1
	mov al, [ds:si]
	cmp al, [es:di]
	jne .exit
	dec ah
.exit:
	mov al, [.BufferBelowMB]
	mov [ds:si], al
	mov al, [.BufferOverMB]
	mov [es:di], al
	shr ax, 8
	sti
	pop es
	pop ds
	pop di
	pop si
	popf
	ret

	.BufferBelowMB:	db 0
	.BufferOverMB:	db 0

;	out:
;		ax - a20 support bits (bit #0 - supported on keyboard controller; bit #1 - supported with bit #1 of port 0x92)
;		cf - set on error
query_a20_support:
	push bx
	clc

	mov ax, 0x2403
	int 0x15
	jc .error

	test ah, ah
	jnz .error

	mov ax, bx
	pop bx
	ret
.error:
	stc
	pop bx
	ret

enable_a20_keyboard_controller:
	cli

	call .wait_io1
	mov al, 0xad
	out 0x64, al

	call .wait_io1
	mov al, 0xd0
	out 0x64, al

	call .wait_io2
	in al, 0x60
	push eax

	call .wait_io1
	mov al, 0xd1
	out 0x64, al

	call .wait_io1
	pop eax
	or al, 2
	out 0x60, al

	call .wait_io1
	mov al, 0xae
	out 0x64, al

	call .wait_io1
	sti
	ret
.wait_io1:
	in al, 0x64
	test al, 2
	jnz .wait_io1
	ret
.wait_io2:
	in al, 0x64
	test al, 1
	jz .wait_io2
	ret

;	out:
;		cf - set on error
enable_a20:
	clc									;	clear cf
	pusha
	mov bh, 0							;	clear bh

	call get_a20_state
	;jc .fast_gate

	test ax, ax
	jnz .done

	call query_a20_support
	mov bl, al
	test bl, 1							;	enable A20 using keyboard controller
	jnz .keybord_controller

	test bl, 2							;	enable A20 using fast A20 gate
	jnz .fast_gate
.bios_int:
	mov ax, 0x2401
	int 0x15
	jc .fast_gate
	test ah, ah
	jnz .failed
	call get_a20_state
	test ax, ax
	jnz .done
.fast_gate:
	in al, 0x92
	test al, 2
	jnz .done

	or al, 2
	and al, 0xfe
	out 0x92, al

	call get_a20_state
	test ax, ax
	jnz .done

	test bh, bh							;	test if there was an attempt using the keyboard controller
	jnz .failed
.keybord_controller:
	call enable_a20_keyboard_controller
	call get_a20_state
	test ax, ax
	jnz .done

	mov bh, 1							;	flag enable attempt with keyboard controller

	test bl, 2
	jnz .fast_gate
	jmp .failed
.failed:
	stc
.done:
	popa
	ret
gdt.asm:

Code: Select all

ALIGN 8
gdt_start:  ; don't remove the labels, they're needed to compute sizes and jumps
            ; the GDT starts with a null 8-byte
    dd  0x0 ; 4 byte
    dd  0x0 ; 4 byte

; GDT for kernel code segment. base = 0x00000000, length = 0xfffff
; for flags, refer to os-dev.pdf document, page 36
gdt_code:
    dw  0xffff    ; segment length, bits 0-15 (16-bit value)
    dw  0x0       ; segment base, bits 0-15 (16-bit value)
    db  0x0       ; segment base, bits 16-23 (8-bit value)
    db  10011010b ; flags (8 bits)
    db  11001111b ; flags (4 bits) + segment length, bits 16-19
    db  0x0       ; segment base, bits 24-31

; GDT for kernel data segment. base and length identical to code segment
; some flags changed, again, refer to os-dev.pdf
gdt_data:
    dw  0xffff
    dw  0x0
    db  0x0
    db  10010010b
    db  11001111b
    db  0x0

; Base = 0
; Limit = 0xFFFFF
; Access Byte = 0xFA
; Flags = 0xC
;gdt_user_code:
;    dw  0xffff
;    dw  0x0
;    db  0x0
;    db  10011010b
;    db  11001111b
;    db  0x0

; Base = 0
; Limit = 0xFFFFF
; Access Byte = 0xF2
; Flags = 0xC
;gdt_user_data:
;    dw  0xffff
;    dw  0x0
;    db  0x0
;    db  10010010b
;    db  11001111b
;    db  0x0

; gdt taask segment
;gdt_tss:
;    dw  0x67
;    dw  0x0
;    db  0x0
;    db  10001001b
;    db  0x0
;    db  0x0

gdt_end:

; GDT descriptor
gdt_descriptor:
    dw  gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
    dd  gdt_start+0x500 ; address (32 bit)

; define some constants for later use
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
graphicsmode.asm (with subroutine, removed strings):

Code: Select all

[bits 16]

; si: null terminated string
; return cx: length
[bits 16]
strlen:
    push    ax
    push    si
    xor     cx, cx
.loop:
    lodsb
    cmp     al, 0
    je      .end
    inc     cx
    jmp     .loop
.end:
    pop     si
    pop     ax
    ret

; si: null terminated string
; bl: color attribute
; dh: row
; dl: column
[bits 16]
draw_string:
    push    ax
    push    bx
    push    cx
    push    si
    push    bp
    ; we can just use bios write string
    ; but have to get length of string first
    call    strlen
    xor     bh, bh
    mov     ah, 0x13
    mov     al, 0x01
    mov     bp, si
    int     0x10
    .end:
    pop     bp
    pop     si
    pop     cx
    pop     bx
    pop     ax
    ret

[bits 16]
choose_graphics_mode:
    pusha
    ; clear the screen
    mov     ah, 0x06
    mov     al, 0x00
    mov     bh, 0x07
    mov     ch, 0x00
    mov     cl, 0x00
    mov     dh, 0x18
    mov     dl, 0x4F
    int     0x10
    ; print the first line
    mov     dh, 8
    mov     dl, 16
    mov     bl, 0x0f
    mov     si, os_identifier
    call    draw_string
    ; now we print the second line
    mov     dh, 9
    mov     dl, 16
    mov     bl, 0x07
    mov     si, os_callforaction
    call    draw_string
    ; now we can start drawing options
    .loop:
    ; draw the first option
    mov     dh, 11
    mov     dl, 16
    ; but now we check if its selected (0x70) or not (0x07)
    movzx   di, byte [currently_selected_graphics_mode]
    cmp     di, 0
    jne     .second_selected
    .first_selected:
    mov     bl, 0x70
    mov     si, os_textmode
    call    draw_string
    ; second string
    mov     dh, 12
    mov     dl, 16
    mov     bl, 0x07
    mov     si, os_bitmapmode
    call    draw_string
    jmp     .keypress
    .second_selected:
    mov     bl, 0x07
    mov     si, os_textmode
    call    draw_string
    ; second string
    mov     dh, 12
    mov     dl, 16
    mov     bl, 0x70
    mov     si, os_bitmapmode
    call    draw_string
    .keypress:
    mov     ah, 0x00
    int     0x16
    ; in ah is the scan code
    cmp     ah, 0x1C    ; enter
    je      .done
    cmp     ah, 0x48    ; arrow down
    je      .arrow_down
    cmp     ah, 0x50    ; arrow up
    je      .arrow_up
    jmp     .keypress
    .arrow_up:
    mov     dh, byte [currently_selected_graphics_mode]
    cmp     dh, 0
    je      .allow_arrow_up
    jmp     .keypress
    .allow_arrow_up:
    inc     byte [currently_selected_graphics_mode]
    jmp     .loop
    .arrow_down:
    mov     dh, byte [currently_selected_graphics_mode]
    cmp     dh, 1
    je      .allow_arrow_down
    jmp     .keypress
    .allow_arrow_down:
    dec     byte [currently_selected_graphics_mode]
    jmp     .loop
    .done:
    movzx   di, byte [currently_selected_graphics_mode]
    cmp     di, 1
    jne     .end
    .set_graphics:
    call    set_graphics_mode
    .end:
    mov     si, 0x1000
    mov     ax, di
    mov     byte [si], al
    popa
    ret

[bits 16]
set_graphics_mode:
    push   ax
    mov    ah, 0x00
    mov    al, 0x13
    int    0x10
    pop    ax
    ret

[bits 16]
get_vesa_info:
    clc
    mov     ax, 0x4F00          ; VBE function 00h - Return VBE Controller Information
    mov     di, buffer   ; ES:DI -> VBEInfoBlock
    int     0x10
    jne     .failed
    ret
    .failed:
        stc
        ret

buffer: resb 512

failed_vbe_info_str: db "Failed to get VBE info, continuing without graphics...", 0

; =====================================================================
; VBE Controller Information Block (512 bytes total)
; Used with INT 10h, AX = 4F00h
; =====================================================================

struc VBEInfoBlock
    .Signature:        resb 4      ; 'VESA' after call
    .Version:          resw 1      ; BCD: 0x0200 = VBE 2.0
    .OemStringPtr:     resd 1      ; Far pointer: seg:off
    .Capabilities:     resb 4
    .VideoModePtr:     resd 1      ; Far pointer to mode list
    .TotalMemory:      resw 1      ; In 64 KB blocks

    ; VBE 2.0+ additional fields
    .OemSoftwareRev:   resw 1
    .OemVendorNamePtr: resd 1
    .OemProductNamePtr resd 1
    .OemProductRevPtr: resd 1

    .Reserved:         resb 222    ; Must be zero
    .OemData:          resb 256    ; OEM data
endstruc

; =====================================================================
; VBE Mode Information Block (256 bytes total)
; Used with INT 10h, AX = 4F01h
; =====================================================================

struc VBEModeInfo
    .ModeAttributes:         resw 1
    .WinAAttributes:         resb 1
    .WinBAttributes:         resb 1
    .WinGranularity:         resw 1
    .WinSize:                resw 1
    .WinASegment:            resw 1
    .WinBSegment:            resw 1
    .WinFuncPtr:             resd 1
    .BytesPerScanLine:       resw 1

    ; VBE 1.2 fields
    .XResolution:            resw 1
    .YResolution:            resw 1
    .XCharSize:              resb 1
    .YCharSize:              resb 1
    .NumberOfPlanes:         resb 1
    .BitsPerPixel:           resb 1
    .NumberOfBanks:          resb 1
    .MemoryModel:            resb 1
    .BankSize:               resb 1
    .NumberOfImagePages:     resb 1
    .Reserved1:              resb 1

    ; Direct color fields (VBE 2.0)
    .RedMaskSize:            resb 1
    .RedFieldPosition:       resb 1
    .GreenMaskSize:          resb 1
    .GreenFieldPosition:     resb 1
    .BlueMaskSize:           resb 1
    .BlueFieldPosition:      resb 1
    .RsvdMaskSize:           resb 1
    .RsvdFieldPosition:      resb 1
    .DirectColorModeInfo:    resb 1

    ; Linear framebuffer fields
    .PhysBasePtr:            resd 1
    .OffScreenMemOffset:     resd 1
    .OffScreenMemSize:       resw 1

    .Reserved2:              resb 206
endstruc

read.asm (osdev's ata lba read):

Code: Select all

[bits 32]
;=============================================================================
; ATA read sectors (LBA mode)
;
; @param EAX Logical Block Address of sector
; @param CL  Number of sectors to read
; @param EDI The address of buffer to put data obtained from disk
;
; @return None
;=============================================================================
ata_lba_read:
    pushad
    pushfd

    and eax, 0x0FFFFFFF

    mov ebx, eax         ; Save LBA in RBX

    mov edx, 0x01F6      ; Port to send drive and bit 24 - 27 of LBA
    shr eax, 24          ; Get bit 24 - 27 in al
    or al, 11100000b     ; Set bit 6 in al for LBA mode
    out dx, al

    mov edx, 0x01F2      ; Port to send number of sectors
    mov al, 1           ; Get number of sectors from CL
    out dx, al

    mov edx, 0x1F3       ; Port to send bit 0 - 7 of LBA
    mov eax, ebx         ; Get LBA from EBX
    out dx, al

    mov edx, 0x1F4       ; Port to send bit 8 - 15 of LBA
    mov eax, ebx         ; Get LBA from EBX
    shr eax, 8           ; Get bit 8 - 15 in AL
    out dx, al


    mov edx, 0x1F5       ; Port to send bit 16 - 23 of LBA
    mov eax, ebx         ; Get LBA from EBX
    shr eax, 16          ; Get bit 16 - 23 in AL
    out dx, al

    mov edx, 0x1F7       ; Command port
    mov al, 0x20         ; Read with retry.
    out dx, al
.still_going:  
    in al, dx
    test al, 8           ; the sector buffer requires servicing.
    jz .still_going      ; until the sector buffer is ready.

    mov eax, 256         ; to read 256 words = 1 sector
    xor bx, bx
    mov bl, cl           ; read CL sectors
    mul bx
    mov ecx, eax         ; RCX is counter for INSW
    mov edx, 0x1F0       ; Data port, in and out
    rep insw             ; in to [RDI]

    popfd
    popad
    ret
Octocontrabass
Member
Member
Posts: 6250
Joined: Mon Mar 25, 2013 7:01 pm

Re: Why does nasm mess up assembling when strings are next to subroutines rather than in the bottom?

Post by Octocontrabass »

atomtables wrote: Sat Jun 27, 2026 11:20 amI think the eb fe had b8 replaced midway, because while testing (without the jmp $) I saw
0x61f: mov edi,0x8000
become
0x61f: mov edi,0xb800
If it changes while it's running, the problem is a bug in your code, not the assembler. Did you try disassembling the binary directly instead of using a debugger?
atomtables wrote: Sat Jun 27, 2026 11:20 am

Code: Select all

; booting from AEX so we should be at 0x500
; our segments are clobbered so let's fix that
mov     ax, 0x50
mov     ds, ax
mov     es, ax
mov     bx, 0x8000
mov     ss, bx
xor     sp, sp
jmp     0x50:next
Using nonzero segment registers in real mode is generally a bad idea. Mixing different values in different segment registers is a worse idea. Do you really need to make things this difficult for yourself? I wouldn't be surprised if the bug is that you're calculating an address relative to the wrong segment somewhere in your code. You can't make that mistake when every segment starts at zero.
Post Reply