NASM - The Netwide Assembler

NASM Forum => Other Discussion => Topic started by: TightCoderEx on January 07, 2013, 12:23:19 AM

Title: Boot Loader Peculularity
Post by: TightCoderEx on January 07, 2013, 12:23:19 AM
In my effort to roll my own BIOS and OS  :-\ well at least the intention is there, I've come across a problem. Certain information is missing from the sites I visit, so what I'm doing is writing an informative phase to my boot loader. Initial prompt is as follows;

Loading BONES OS

Stack Pointer 0000:03E2

Not surprisingly BIOS stack is just below BPB (Bios Parameter Block) 0x400 to 0x501. This is the code that does this.

Code: [Select]
[bits 16]

BOOT_BASE equ 0x7c00

org BOOT_BASE ; Bootstrap base

; ============================================================================================

start mov     ax, ss
        mov     bx, sp
        xor     ax, ax
        mov     ss, ax
        mov     ds, ax
        mov     es, ax
        mov     sp, BOOT_BASE
        push    bx
        push    ax
        mov     si, SignOn
        call    ShowS
        pop     ax
        push    si
        mov     di, Stack
        call    I2A_16
        pop     si
        pop     ax
        push    si
        add     di, 5
        call    I2A_16
        mov     si, Disp
        call    ShowS

        ; Wait for operator input before carrying on
.Again xor ax,  ax
inc al
int 0x16
jz .Again

int 0x19

; --------------------------------------------------------------------------------------------
; Display NULL termianted string teletype mode

; ENTRY: SI = Pointer to string

; LEAVE: SI = Pointer to next string if applicable. If stings are positioned properly
;      in data area, this could be called successively without modifying SI.
; ____________________________________________________________________________________________

         ShowS mov ah, 14
  .Next lodsb ; Get character from string
  or al,  al
  jz .Done
  int 0x10 ; Display character
  jmp .Next
  .Done ret ; BX = Pointer to next possible string

; ============================================================================================
; Converts unsigned integer to NULL terminated ASCII string

; ENTRY: AX = 16 bit value to be converted
; DX = Pointer to destination buffer (must be at least 5 bytes in length)

; LEAVE: AX = Pointer to first character of conversion
; DX = Unchanged and points to ASCII string padded with blanks.
; --------------------------------------------------------------------------------------------

I2A_16 push di ; Preserve base pointer
        add     di, 3                   ; Bump to end of display area
push dx
mov dx, ax

; It might be a better idea to check flag before changing, but in this case I'm
; going to assume all routines change to auto increment before completing.

std ; Auto decrement

; Cycle until DX is null, but should it be zero at least one '0' will be written.

.Next mov al, dl ; Get next byte
and al, 15 ; Strip high nibble
or al, '0' ; Make ASCII
cmp al, 58 ; and determine if greater than '9'
jb $ + 4 ; Branch if between '0' & '9'

add al, 7 ; Makes character 'A' - 'F'

stosb ; Write ASCII character to buffer
shr dx, 4 ; Shift next nibble into AL
jnz .Next ; and continue if not zero

cld ; Set default direction
pop dx ; Restore original contents
mov ax, di ; Copy pointer
inc ax ; and bump so there are no leading spaces
pop di ; Pointer with leading spaces

; <> - <> - <> - <> - <> - <> - <> - <> - < > - <> - <> - <> - <> - <> - <> - <> - <> - <>- <>

  SignOn db 'Loading BONES OS', 13, 10, 13, 10, 0
  Disp          db      'Stack Pointer '
  Stack         db      '0000:0000', 13, 10, 0

times 510 - ($ - $$) db 90H

dw 0xAA55

My alternate version, which should have worked, because it does on my DOS box invoked from command prompt, DEBUG and SYMDEB, but didn't when booting. Code in part;

Code: [Select]
        mov     si, SignOn
        call    ShowS
        pop     ax
        push    si
        mov     di, Stack
        call    I2A_16
        pop     si
        pop     ax
        push    si
        add     di, 5
        call    I2A_16
        pop     si
        call    ShowS

As you can see, identical except SI is derived from stack. Hoping somebody has an idea before I have to code a trace mechanism in to follow what's happening  >:(
Title: Re: Boot Loader Peculularity
Post by: Frank Kotler on January 07, 2013, 03:24:41 AM
Code: [Select]
start mov     ax, ss
        mov     bx, sp
        xor     ax, ax

I don't doubt that ss was 0 before you put it there, but we don't really know this... If you're looking for some "standard" values for what BIOS hands us in any register but dl (boot drive), I don't think there is any...  I'm not sure I understand what the issue is, TightCoderEx. Feeling slow today...


Title: Re: Boot Loader Peculularity
Post by: TightCoderEx on January 07, 2013, 04:02:39 AM
ES @ start is zero & SS is 0x3E2, so that is resolved no problem.

ShowS is designed to take SI as a pointer and when done SI points to the next location after the terminator being NULL. So at the first invocation of ShowS SI = 7C65 and when it's done 7C7A.
Code: [Select]
   112 7C65 4C6F6164696E672042-       SignOn db 'Loading BONES OS', 13, 10, 13, 10, 0
   113 7C6E 4F4E4553204F530D0A-
   114 7C77 0D0A00             
   115 7C7A 537461636B20506F69-       Disp          db      'Stack Pointer '
   116 7C83 6E74657220         
   117 7C88 303030303A30303030-       Stack         db      '0000:0000', 13, 10, 0
   118 7C91 0D0A00             

At lines 20 & 21 I save ES:SP and at lines 26 & 27 exchange ES with pointer to Disp = 7C7A.  That value being zero is converted correctly and then @ 31 - 33 in essence 7C7A is exchanged with 03E2 and then converted so my display in previous posting is 0000:03E2.  by the time we get to 37, contents of stack should be 7C7A, but it's not.  The string @ SignOn is displayed again, so somehow the contents of stack got changed to 7C65.

Code: [Select]
    20 7C11 53                              push    bx
    21 7C12 50                              push    ax
    23 7C13 BE[6500]                        mov     si, SignOn
    24 7C16 E81F00                          call    ShowS
    26 7C19 58                              pop     ax
    27 7C1A 56                              push    si
    28 7C1B BF[8800]                        mov     di, Stack
    29 7C1E E82300                          call    I2A_16
    31 7C21 5E                              pop     si
    32 7C22 58                              pop     ax
    33 7C23 56                              push    si
    34 7C24 83C705                          add     di, 5
    35 7C27 E81A00                          call    I2A_16
    37 7C2A 5E                              pop     si
    38 7C2B E80A00                          call    ShowS

The question is, how did the contents of the stack get changed to 7C65 when application was tested in DEBUG & SYSDEB it worked just fine.  Also when @ line 37 I directly address Disp mov si, Disp it also works fine.

As usual and has been the case for quite some time, work arounds are in order. My guess would be the BIOS encounters some sort of interrupt and trashes SP. Just thought somebody might have some quick insight. It is working, just not the way I wanted

Thanks for the heads up Frank, as one never knows if the question is stated clearly, if not made aware.
Title: Re: Boot Loader Peculularity
Post by: s_dubrovich on March 01, 2013, 04:29:22 AM
    37 7C2A 5E                                 pop     si

This version works for me, indentical to your first version.  Tested as a real floppy disk boot sector on a pentium mmx laptop.  The output is:

Loading BONES OS

Stack Pointer 0000:0100

(I think you know your code overwrites the segment value with zero.)
(It should be SS:SP 0030:0100h for this machine.)
(I had to try your code, because I couldn't see the problem - there isn't one.  You must've made some other inadvertant error.)

Title: Re: Boot Loader Peculularity
Post by: Keith Kanios on March 01, 2013, 05:05:32 AM
Stack Pointer 0000:03E2

Recommended reading: x86 Memory Map @ (

TL;DR: Stay above 0000:0500 in Real Mode.

Hoping somebody has an idea before I have to code a trace mechanism in to follow what's happening  >:(

Use Bochs (
Title: Re: Boot Loader Peculularity
Post by: TightCoderEx on March 01, 2013, 08:13:12 PM
Thanks for the input, but as it turns out in order to satisfy my curiosity, I did concoct a snippet in the boot loader to give me the information I was looking for. There are two versions I tried this out on, being AMI v02.58 and Phoenix Truecore (Lenovo 3000 J Series)

My trials showed bottom of stack for AMI set to 7C00H & Truecore 400H.  As you pointed out Keith, probably a better idea to stay above BDA, but if Phoenix isn't to worried about it, maybe I'll be ok.  As you can see, I'm only going 24 bytes deeper.  I believe IVT ( only comes as high as 77H ( 1DCH ), so I'm probably not interfering with anything.
Code: [Select]

  00  60                pushaw
  01  0FA8              push gs
  03  0FA0              push fs
  05  16                push ss
  06  06                push es
  07  1E                push ds
  08  0E                push cs
  09  8CD3              mov bx,ss
  0B  89E1              mov cx,sp
  0D  EAAC00C007        jmp word 0x7c0:0xac

Now I set the stack pointer to a place I know it will be safe, but use BIOS data again to determine bottom. On AMI, my pointer winds up @ 9300:C000 = 9FC00 and 91C0:C000 on Truecore. Not sure why real BIOS's need so much room, but emulators like EMU8086 & Bochs don't.
Code: [Select]

  AC  FA                cli
  AD  B84000            mov ax,0x40
  B0  8ED8              mov ds,ax
  B2  A11300            mov ax,[0x13]
  B5  BC3000            mov sp,0x30
  B8  29E0              sub ax,sp
  BA  C1E006            shl ax,0x6
  BD  C1E40A            shl sp,0xa
  C0  8ED0              mov ss,ax
  C2  8CC8              mov ax,cs
  C4  8ED8              mov ds,ax
  C6  8EC0              mov es,ax
  C8  31C0              xor ax,ax
  CA  8EE8              mov gs,ax
  CC  8EE0              mov fs,axAX:  C8D
CX:      4
DX:    80        80             GS:  F000     F000
BX: FFFF                       FS: 2DC2     E3CF
SP:  7E02    3E2             SS:
BP:  7C00                       ES: F000
SI:    80                          DS: 9FC0       40
DI: 6B30                         CS:

  CE  FB                sti

After all is said and done, these are the results I get for Phoenix (left column) & AMI (right column).
Blanks denote zero.

AX:   C8D
CX:     4
DX:    80     80             GS:  F000     F000
BX:  FFFF                    FS:  2DC2     E3CF
SP:  7E02    3E2             SS:
BP:  7C00                    ES:  F000
SI:    80                    DS:  9FC0       40
DI:  6B30                    CS:

Anyone interested in running the code will see these values on the screen, but without labels, as sectors 2 - 66 are video images as to take the overhead away from code.