Jump to content

Check out our Community Blogs

Register and join over 40,000 other developers!

Recent Status Updates

View All Updates

- - - - -

NASM leaving the bootsector?

kernel assembly x86

This topic has been archived. This means that you cannot reply to this topic.
3 replies to this topic

#1 Poe


    CC Resident

  • Advanced Member
  • PipPipPipPip
  • 81 posts

Posted 16 October 2014 - 11:34 AM

I finally did it, after about a month and half of studying tutorials (none of which worked as a whole) but from studying bits of one tutorial and parts of another I was able to get a hanging boot, finally able to draw characters to the screen, and then get user input.


However now I have run into an error I knew was coming, to put it bluntly after booting and typing 10 characters qemu halts and has this to say in the BASH terminal:



dylan@debian:~/Documents/assembly/os$ qemu boot.img
qemu: fatal: Trying to execute code outside RAM or ROM at 0xf00f656a

EAX=00006e98 EBX=00006e98 ECX=f000656a EDX=0000fe20
ESI=00000001 EDI=00000040 EBP=00000040 ESP=00006e94
EIP=f000656a EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0040 00000400 0000ffff 00009300
CS =f000 000f0000 0000ffff 00009b00
SS =0000 00000000 0000ffff 00009300
DS =0000 00000000 0000ffff 00009300
FS =0000 00000000 0000ffff 00009300
GS =0000 00000000 0000ffff 00009300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT=     000fd3a8 00000037
IDT=     00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000020 CCD=00000020 CCO=ADDB    
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000

Basically, there is not enough memory to continue it seems something often spoken of I can't write the actual OS in the bootsector I need to move the bulk of the code out.  I however am not sure how to go about this.


Would I make an external object starting outside of the boot sector and then CALL it?

this is more of a theory question than anything.

"Portability is for those who can't write new programs" - Linus Torvalds

#2 dargueta


    I chown trolls.

  • Moderator
  • 4854 posts

Posted 16 October 2014 - 03:44 PM

I have my suspicions, but can I see your code? You might've forgotten a bits 16 directive at the top of your assembly file.


Some instructions can be encoded two different ways depending on whether they're assembled in 16- or 32-bit mode, and if you compiled 16-bit code as 32-bit or even 64-bit code, the 16-bit virtual 8086 is gonna see it, misinterpret it, and do something you didn't expect.


Sometimes things like this won't show up. You can write a 16-bit program and compile it as 32-bit and miraculously it won't have problems. But as soon as you use one of those instructions that're encoded differently... Boom.


Edit: You could also be trashing your own memory (particularly if you didn't set up the stack right) and that could cause a jump to who knows where.

Edited by dargueta, 16 October 2014 - 03:48 PM.

sudo rm -rf / && echo $'Sanitize your inputs!'

#3 Poe


    CC Resident

  • Advanced Member
  • PipPipPipPip
  • 81 posts

Posted 16 October 2014 - 07:47 PM

indeed you can, i thought i had posted it

[BITS 16]
[ORG 0x7C00]


	mov SI,Hello

	mov cx,0
	jmp $


	cmp cx,10
	add cx,1
	mov SI,Before



    mov ah, 0x0E    ; Teletype output mode
    mov bh, 0x00    ; Page number
    mov bl, 0x0A    ; Grey font
    int 0x10        ; Call video interrupt
    ret             ; Return to the calling procedure


    lodsb           ; Load a byte from SI to AL and increment SI
    cmp al, 0       ; Compare accumulator to 0
    je EXIT         ; If AL == 0, call the EXIT procedure
    call PRINTCHAR  ; Else, print the current character on the screen
    jmp PRINTSTR    ; Jump to PRINTSTR - print the rest of the string

   xor cl, cl
   mov ah, 0
   int 0x16   ; wait for keypress
   cmp al, 0x08    ; backspace pressed?
   je .backspace   ; yes, handle it
   cmp al, 0x0D  ; enter pressed?
   je .done      ; yes, we're done
   cmp cl, 0x3F  ; 63 chars inputted?
   je .loop      ; yes, only let in backspace and enter
   mov ah, 0x0E
   int 0x10      ; print out character
   stosb  ; put character in buffer
   inc cl
   jmp .loop

   cmp cl, 0	; beginning of string?
   je .loop	; yes, ignore the key
   dec di
   mov byte [di], 0	; delete character
   dec cl		; decrement counter as well
   mov ah, 0x0E
   mov al, 0x08
   int 10h		; backspace on the screen
   mov al, ' '
   int 10h		; blank character out
   mov al, 0x08
   int 10h		; backspace again
   jmp .loop	; go to the main loop


   mov al, 0	; null terminator
   mov ah, 0x0E
   mov al, 0x0D
   int 0x10
   mov al, 0x0A
   int 0x10		; newline


    ret             ; Just return
Hello DB 'Loading System...', 13, 10, 0
Input DB 'Test'
Before DB '#', 13, 10, 0
times 510 - ($ - $$) DB 0
dw 0xAA55

"Portability is for those who can't write new programs" - Linus Torvalds

#4 dargueta


    I chown trolls.

  • Moderator
  • 4854 posts

Posted 16 October 2014 - 09:52 PM

These may or may not be necessary for all systems (especially emulators) but they're good practices to follow.


The very first things you should do before anything else:


1) Set up your segment registers. You're not guaranteed that they're pointing in the right places. In fact, you'll notice in your debug dump that ES=0x40 (the BIOS data area) and DS=0. This is especially important if you're gonna be using any of the string operations (e.g. movsb, movsw, lodsb, stosb, etc.) like in PRINTSTR.


 (Doing a mov between segment registers is illegal, which is why I use ax.)

mov     ax, cs
mov     ds, ax
mov     es, ax
mov     ss, ax

2) Set up your stack. You have no idea where sp is pointing to and if your call stack gets deep enough, you could end up overwriting your own code. For this reason I set up the stack below the entry point. This very well could be your problem.

mov     sp, 0x7bf0

3) Don't assume that the general registers (AX, BX, CX, DX) are intact after a BIOS call, especially for ones that return values. Push any registers you need to keep before you call an interrupt, and pop them back off afterwards. Some BIOSes, especially buggy ones (I've encountered them) will trash registers they're not supposed to.


Now for some things specific to your code:


1) ENDSEC has return instruction, but in the second line of TERMINAL you jump to ENDSEC. This means the ret is actually for TERMINAL and so you'll return to start and hang. This might be what you want but it looks suspicious to me.



; Instead of using this to hang
jmp     $

; Use this - it halts the processor, saves power on laptops
; jmp $ just spinlocks it.

3) Before you use the string instructions, I recommend using a cld instruction (just once, at the beginning). This clears the direction flag so string instructions go forward in memory (to higher addresses), not backwards (to lower addresses).

sudo rm -rf / && echo $'Sanitize your inputs!'

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download