I'm writing a Forth inner interpreter and getting stuck at what should be the simplest bit. Using NASM on Mac (macho)
msg db "k thx bye",0xA ; string with carriage return
len equ $ - msg ; string length in bytes
xt_test:
dw xt_bye ; <- SI Starts Here
dw 0
db 3,'bye'
xt_bye dw $+2 ; <- Should point to...
push dword len ; <-- code here
push dword msg ; <--- but it never gets here
push dword 1
mov eax, 0x4 ; print the msg
int 80h
add esp, 12
push dword 0
mov eax, 0x1 ; exit(0)
int 80h
_main:
mov si,xt_test ; si points to the first xt
lodsw ; ax now points to the CFA of the first word, si to the next word
mov di,ax
jmp [di] ; jmp to address in CFA (Here's the segfault)
I get Segmentation Fault: 11 when it runs. As a test, I can change _main to
_main:
mov di,xt_bye+2
jmp di
and it works
EDIT - Here's the simplest possible form of what I'm trying to do, since I think there are a few red herrings up there :)
a dw b
b dw c
c jmp _my_actual_code
_main:
mov si,a
lodsw
mov di,ax
jmp [di]
EDIT - After hexdumping the binary, I can see that the value in b above is actually 0x1000 higher than the address where label c is compiled. c is at 0x00000f43, but b contains 0x1f40
First, it looks extremely dangerous to use the 'si' and 'di' 16-bit registers on a modern x86 machine which is at least 32-bit.
Try using 'esi' and 'edi'. You might be lucky to avoid some of the crashes when 'xt_bye' is not larger than 2^16.
The other thing: there is no 'RET' at the end of xt_bye.
One more: see this linked question Help with Assembly. Segmentation fault when compiling samples on Mac OS X
Looks like you're changing the ESP register to much and it becomes unaligned by 16 bytes. Thus the crash.
One more: the
jmp [di]
may not load the correct address because the DS/ES regs are not used thus the 0x1000 offset.
Related
I'm trying to create a line-drawing algorithm in assembly (more specifically Bresenham's line algorithm). After trying an implementation of this algorithm, the algorithm fails to work properly even though I almost exactly replicated the plotLineLow() function from this Wikipedia page.
It should be drawing a line between 2 points, but when I test it, it draws points in random places in the window. I really don't know what could be going wrong because debugging in assembly is difficult.
I'm using NASM to convert the program to binary, and I run the binary in QEMU.
[bits 16] ; 16-bit mode
[org 0x7c00] ; memory origin
section .text ; code segmant
global _start ; tells the kernal where to begin the program
_start: ; where to start the program
; main
call cls ; clears the screen
update: ; main loop
mov cx, 0x0101 ; line pos 1
mov bx, 0x0115 ; line pos 2
call line ; draws the line
jmp update ; jumps to the start of the loop
; functions
cls: ; function to clear the screen
mov ah, 0x00 ; set video mode
mov al, 0x03 ; text mode (80x25 16 colours)
int 0x10 ; BIOS interrupt
ret ; returns to where it was called
point: ; function to draw a dot at a certain point (dx)
mov bx, 0x00ff ; clears the bx register and sets color
mov cx, 0x0001 ; clears the cx register and sets print times
mov ah, 0x02 ; set cursor position
int 0x10 ; BIOS interrupt
mov ah, 0x09 ; write character
mov al, ' ' ; character to write
int 0x10 ; BIOS interrupt
ret ; returns to where it was called
line: ; function to draw a line at two points (bx, cx)
push cx ; saves cx for later
push bx ; saves bx for later
sub bh, ch ; gets the value of dx
mov [dx_L], bh ; puts it into dx
sub bl, cl ; gets the value of dy
mov [dy_L], bl ; puts it into dy
mov byte [yi_L], 1 ; puts 1 into yi (positive slope)
cmp byte [dy_L], 0 ; checks if the slope is negative
jl .negative_y ; jumps to the corresponding sub-label
jmp .after_negative_y ; if not, jump to after the if
.negative_y: ; if statement destination
mov byte [yi_L], -1 ; sets yi to -1 (negative slope)
neg byte [dy_L] ; makes dy negative as well
.after_negative_y: ; else statement destination
mov ah, [dy_L] ; moves dy_L into a temporary register
add ah, ah ; multiplies it by 2
sub ah, [dx_L] ; subtracts dx from that
mov [D_L], ah ; moves the value into D
pop bx ; pops bx to take a value off
mov [y_L], bh ; moves the variable into the output
pop cx ; pops the stack back into cx
mov ah, bh ; moves x0 into ah
mov al, ch ; moves x1 into al
.loop_x: ; loop to go through every x iteration
mov dh, ah ; moves the iteration count into dh
mov dl, [y_L] ; moves the y value into dl to be plotted
call point ; calls the point function
cmp byte [D_L], 0 ; compares d to 0
jg .greater_y ; if greater, jumps to the if statement
jmp .else_greater_y ; if less, jumps to the else statement
mov bh, [dy_L] ; moves dy into a temporary register
.greater_y: ; if label
mov bl, [yi_L] ; moves yi into a temporary register
add [y_L], bl ; increments y by the slope
sub bh, [dx_L] ; dy and dx
add bh, bh ; multiplies bh by 2
add [D_L], bh ; adds bh to D
jmp .after_greater_y ; jumps to after the if statement
.else_greater_y: ; else label
add bh, bh ; multiplies bh by 2
add [D_L], bh ; adds bh to D
.after_greater_y: ; after teh if statement
inc ah ; increments the loop variable
cmp ah, al ; checks to see if the loop should end
je .end_loop_x ; if it ended jump to the end of teh loop
jmp .loop_x ; if not, jump back to the start of the loop
.end_loop_x: ; place to send the program when the loop ends
ret ; returns to where it was called
section .data ; data segmant
dx_L: db 0 ; used for drawing lines
dy_L: db 0 ; ^
yi_L: db 0 ; ^
xi_L: db 0 ; ^
D_L: db 0 ; ^
y_L: db 0 ; ^
x_L: db 0 ; ^
section .text ; code segmant
; boot the OS
times 510-($-$$) db 0 ; fills up bootloader space with empty bytess
db 0x55, 0xaa ; defines the bootloader bytes
I see no video mode
just 80x25 text VGA mode (mode = 3) you set at start with cls so how can you render points? You should set the video mode you want assuming VGA or VESA/VBE see the link above.
why to heck use VGA BIOS for point rendering?
that will be slooooooooow and I have no idea what it does when no gfx mode is present. You can render points by direct access to VRAM (at segment A000h) Ideal use 8/16/24/32bit video modes as they have pixels aligned to BYTEs ... my favorite is 320x200x256c (mode = 19) as it fits into 64K segment so no paging is needed and pixels are Bytes.
In case you are using characters instead of pixels then you still can use access to VRAM in the same way just use segment B800h and chars are 16 bit (color and ASCII).
integer DDA is faster then Bresenham on x86 CPUs since 80386
I do not code in NASM for around 2 decades and closest thing to line I found in my archive is this:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
line: pusha ;ax=x0,bx=x1,dl=y0,dh=y1,cl=col
push ax ;expecting ES = A000h
mov si,bx
sub si,ax
sub ah,ah
mov al,dl
mov bx,ax
mov al,dh
sub ax,bx
mov di,ax
mov ax,320
sub dh,dh
mul dx
pop bx
add ax,bx
mov bp,ax
mov ax,1
mov bx,320
cmp si,32768
jb .r0
neg si
neg ax
.r0: cmp di,32768
jb .r1
neg di
neg bx
.r1: cmp si,di
ja .r2
xchg ax,bx
xchg si,di
.r2: mov [.ct],si
.l0: mov [es:bp],cl
add bp,ax
sub dx,di
jnc .r3
add dx,si
add bp,bx
.r3: dec word [.ct]
jnz .l0
popa
ret
.ct: dw 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
So you have something to cross check (took me a while to find it in my archives as I coded whole 3D polygonal engines with textures at that time so I do not have much 2D code in NASM...)
The example expects 320x200x256c VGA video mode
If you were writing a DOS .com file, things would be a bit simpler: segment registers would all be set the same, with your code/data at offset 100 from them. And you could end with ret.
[BITS 16]
[ORG 100h]
[SEGMENT .text]
ret
As #bitRAKE and #PeterCoders pointed out in case You run this in BOOT SECTOR the org is ok. However in such case there is no OS present so if you were going to do more with the stack or any other block of memory outside your 512 bytes, you'd want to point the stack to somewhere known. (It does start out valid, though, because interrupts are enabled.)
More importantly, you need to initialize DS to match your ORG setting, so ds:org reaches a linear address of 7C00. With org 0x7c00, that means you want DS=0. Otherwise instructions like mov [dx_L], bh would be using memory at some unknown location.
[BITS 16]
[ORG 7C00h]
[SEGMENT .text]
mov ax,0000h
mov ds,ax ; DS=0 to match ORG
mov ss,ax ; if you set SS:SP at all, do it back-to-back
mov sp,7C00h ; so an interrupt can't fire half way through.
; here do your stuff
l0:
hlt ; save power
jmp l0
Hope you are using VC or NC configured as IDE for NASM and not compiling/linking manually
This one is usable in MS-DOS so if you are running BOT SECTOR you out of luck. Still You can create a *.com executable debug and once its working in dos change to BOOT SECTOR...
see Is there a way to link object files for DOS from Linux? on how to setup MS-DOS Volkov commander to automatically compile and link your asm source code just by hitting enter on it ... You can also run it just by adding line to the vc.ext line ... but I prefer not to so you can inspect error log first
Convenient debugging
You can try to use MS-DOS (DOSBox) with ancient Borland Turbo C/C++ or Pascal and use their inline asm { .... } code which can be traced and stepped directly in the IDE. However it uses TASM (different syntax to NASM) and have some restrictions ...
Sadly I never saw any decent IDE for asm on x86 platform. The best IDE for asm I worked with was Herkules on ZX Spectrum ... was possible to done things even modern C++ IDEs doesnt have.
I am trying to build an x86 program that reads a file into memory. It uses a few different syscalls, and messes with memory and such. There's a lot in there to figure out.
To simplify debugging and figuring this out, I wanted to add assert statements which, if there's a mismatch, it prints out a nice error message. This is the first step in learning assembly so I can print the numbers and strings that get placed on different registers and such after operations. Then I can print them out and debug them without any fancy tools.
Wondering if one could help me write an ASSERT AND PRINT in NASM for Mac x86-64. I have this so far:
%define a rdi
%define b rsi
%define c rdx
%define d r10
%define e r8
%define f r9
%define i rax
%define EXIT 0x2000001
%define EXIT_STATUS 0
%define READ 0x2000003 ; read
%define WRITE 0x2000004 ; write
%define OPEN 0x2000005 ; open(path, oflag)
%define CLOSE 0x2000006 ; CLOSE
%define MMAP 0x2000197 ; mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset)
%define PROT_NONE 0x00 ; no permissions
%define PROT_READ 0x01 ; pages can be read
%define PROT_WRITE 0x02 ; pages can be written
%define PROT_EXEC 0x04 ; pages can be executed
%define MAP_SHARED 0x0001 ; share changes
%define MAP_PRIVATE 0x0002 ; changes are private
%define MAP_FIXED 0x0010 ; map addr must be exactly as requested
%define MAP_RENAME 0x0020 ; Sun: rename private pages to file
%define MAP_NORESERVE 0x0040 ; Sun: don't reserve needed swap area
%define MAP_INHERIT 0x0080 ; region is retained after exec
%define MAP_NOEXTEND 0x0100 ; for MAP_FILE, don't change file size
%define MAP_HASSEMAPHORE 0x0200 ; region may contain semaphores
;
; Assert equals.
;
%macro ASSERT 3
cmp %1, %2
jne prepare_error
prepare_error:
push %3
jmp throw_error
%endmacro
;
; Print to stdout.
;
%macro PRINT 1
mov c, getLengthOf(%1) ; "rdx" stores the string length
mov b, %1 ; "rsi" stores the byte string to be used
mov a, 1 ; "rdi" tells where to write (stdout file descriptor: 1)
mov i, WRITE ; syscall: write
syscall
%endmacro
;
; Read file into memory.
;
start:
ASSERT PROT_READ, 0x01, "Something wrong with PROT_READ"
mov b, PROT_READ
mov a, PROT_WRITE
xor a, b
mov f, 0
mov e, -1
mov d, MAP_PRIVATE
mov c, a
mov b, 500000
mov a, 0
mov i, MMAP
syscall
PRINT "mmap output "
PRINT i ; check what's returned
PRINT "\n"
mov e, i
mov b, O_RDONLY
mov a, "Makefile"
mov i, OPEN
syscall
mov a, i
mov b, e
mov i, READ
syscall
;
; Exit status
;
exit:
mov a, EXIT_STATUS ; exit status
mov i, EXIT ; syscall: exit
syscall
throw_error:
PRINT pop() ; print error or something
jmp exit
mov rsi, "abcdefgh" is a mov-immediate of the string contents, not a pointer to it. It only exists as an immediate if you do that.
Your macro will need to switch to .rodata and back to put the string in memory; possibly you could turn it into a sequence of push-immediate onto the stack with NASM macros, but that sounds hard.
So you can use the usual msglen equ $ - msg to get the length. (Actually using NASM local labels so the macro doesn't create conflicts).
See NASM - Macro local label as parameter to another macro where I wrote basically this answer a couple weeks ago. But not exactly a duplicate because it didn't have the bug of using the string as an immediate.
NASM's mechanism for letting macros switch sections and then return to whatever section they expanded in is to have section foo define a macro __?SECT?__ as [SECTION foo]. See the manual and the above linked Q&A.
; write(1, string, sizeof(stringarray))
; clobbers: RDI, RSI, RDX, RCX,R11 (by syscall itself)
: output: RAX = bytes written, or -errno
%macro PRINT 1
[section .rodata] ; change section without updating __?SECT?__ macro
;; NASM macro-local labels
%%str db %1 ; put the string in read-only memory
%%strln equ $ - %%str ; current position - string start
__?SECT?__ ; change back to original sectoin
mov edx, %%strlen ; len
lea rsi, [rel %%str] ; buf = the string. (RIP-relative for position-independent)
mov edi, 1 ; fd = stdout
mov eax, WRITE
syscall
%endmacro
This doesn't attempt to combine duplicates of the same string. Using it many times with the same message will be inefficient. This doesn't matter for debugging.
I could have left your %defines for RDI, and let NASM optimize mov rdi, 1 (7 bytes) into mov edi, 1 (5 bytes). But YASM won't do that so it's better to make it explicit if you care about anyone building your code with YASM.
I used a RIP-relative LEA because that's the most efficient way to put a static address into a register in position-independent code. In Linux non-PIE executables, use mov esi, %%str (5 bytes and can run on any port, more than LEA). But on OS X, the base virtual address where an executable is mapped/loaded is always above 2^32, and you never want mov r64, imm64 with a 64-bit absolute address.
See How to load address of function or label into register
On Linux, where system-call numbers are small integers, you could use lea eax, [rdi-1 + WRITE] to do eax = SYS_write with a 3 byte instruction vs. 5 for mov.
The standard names for call-number constants are POSIX SYS_foo from sys/syscall.h or Linux __NR_foo from asm/unistd.h. But NASM can't #include C preprocessor #define macros, so you'd need to mechanically convert one of those headers to NASM syntax, e.g. with some script.
Or if manually defining names, just choose %define SYS_write 1
I found an implementation of unsigned integer conversion in x86 assembly, and I tried plugging it in but being new to assembly and not having a debugging env there yet, it's difficult to understand why it's not working. I would also like it to work with signed integers so it can capture error messages from syscalls.
Wondering if one could show how to fix this code to get the signed integer to print, without using printf but using strprn provided by this answer.
%define a rdi
%define b rsi
%define c rdx
%define d r10
%define e r8
%define f r9
%define i rax
%define EXIT 0x2000001
%define EXIT_STATUS 0
%define READ 0x2000003 ; read
%define WRITE 0x2000004 ; write
%define OPEN 0x2000005 ; open(path, oflag)
%define CLOSE 0x2000006 ; CLOSE
%define MMAP 0x2000197 ; mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset)
; szstr computes the lenght of a string.
; rdi - string address
; rdx - contains string length (returned)
strsz:
xor rcx, rcx ; zero rcx
not rcx ; set rcx = -1 (uses bitwise id: ~x = -x-1)
xor al,al ; zero the al register (initialize to NUL)
cld ; clear the direction flag
repnz scasb ; get the string length (dec rcx through NUL)
not rcx ; rev all bits of negative -> absolute value
dec rcx ; -1 to skip the null-term, rcx contains length
mov rdx, rcx ; size returned in rdx, ready to call write
ret
; strprn writes a string to the file descriptor.
; rdi - string address
; rdx - contains string length
strprn:
push rdi ; push string address onto stack
call strsz ; call strsz to get length
pop rsi ; pop string to rsi (source index)
mov rax, WRITE ; put write/stdout number in rax (both 1)
mov rdi, 1 ; set destination index to rax (stdout)
syscall ; call kernel
ret
; mov ebx, 0xCCCCCCCD
itoa:
xor rdi, rdi
call itoal
ret
; itoa loop
itoal:
mov ecx, eax ; save original number
mul ebx ; divide by 10 using agner fog's 'magic number'
shr edx, 3 ;
mov eax, edx ; store quotient for next loop
lea edx, [edx*4 + edx] ; multiply by 10
shl rdi, 8 ; make room for byte
lea edx, [edx*2 - '0'] ; finish *10 and convert to ascii
sub ecx, edx ; subtract from original number to get remainder
lea rdi, [rdi + rcx] ; store next byte
test eax, eax
jnz itoal
exit:
mov a, EXIT_STATUS ; exit status
mov i, EXIT ; exit
syscall
_main:
mov rdi, msg
call strprn
mov ebx, -0xCCCCCCCD
call itoa
call strprn
jmp exit
section .text
msg: db 0xa, " Hello StackOverflow!!!", 0xa, 0xa, 0
With this working it will be possible to properly print signed integers to STDOUT, so you can log the registers values.
https://codereview.stackexchange.com/questions/142842/integer-to-ascii-algorithm-x86-assembly
How to print a string to the terminal in x86-64 assembly (NASM) without syscall?
How do I print an integer in Assembly Level Programming without printf from the c library?
https://baptiste-wicht.com/posts/2011/11/print-strings-integers-intel-assembly.html
How to get length of long strings in x86 assembly to print on assertion
My answer on How do I print an integer in Assembly Level Programming without printf from the c library? which you already linked shows that serializing an integer into memory as ASCII decimal gives you a length, so you have no use for (a custom version of) strlen here.
(Your msg has an assemble-time constant length, so it's silly not to use that.)
To print a signed integer, implement this logic:
if (x < 0) {
print('-'); // or just was_negative = 1
x = -x;
}
unsigned_intprint(x);
Unsigned covers the abs(most_negative_integer) case, e.g. in 8-bit - (-128) overflows to -128 signed. But if you treat the result of that conditional neg as unsigned, it's correct with no overflow for all inputs.
Instead of actually printing a - by itself, just save the fact that the starting number was negative and stick the - in front of the other digits after generating the last one. For bases that aren't powers of 2, the normal algorithm can only generate digits in reverse order of printing,
My x86-64 print integer with syscall answer treats the input as unsigned, so you should simply use that with some sign-handling code around it. It was written for Linux, but replacing the write system call number will make it work on Mac. They have the same calling convention and ABI.
And BTW, xor al,al is strictly worse than xor eax,eax unless you specifically want to preserve the upper 7 bytes of RAX. Only xor-zeroing of full registers is handled efficiently as a zeroing idiom.
Also, repnz scasb is not fast; about 1 compare per clock for large strings.
For strings up to 16 bytes, you can use a single XMM vector with pcmpeqb / pmovmskb / bsf to find the first zero byte, with no loop. (SSE2 is baseline for x86-64).
I have been trying to interface with the standard C library in Windows in assembler and I'm having trouble. For some reason, I can't make printf accept floating point variables, so something is wrong here.
This is the shortest program I can create that demonstrates the problem. I've included comments that explain my understanding of what is supposed to be happening.
Thanks
;
; Hello64.asm
; A simple program to print a floating point number in windows
;
; assemble: nasm float64.asm -f win64
; link: golink /console /entry main float64.obj MSVCRT.dll
;
; tell assembler to generate 64-bit code
;
bits 64
; data segment
section .data use64
pi dq 3.14159
textformat: db "hello, %lf!",0x0a, 0x00 ; friendly greeting
; set up the .text segment for the code
section .text use64
; global main is the entry point
global main
; note that there is no _ before printf here, unlike in OS X
extern printf
main:
mov rcx, textformat
movq xmm0, qword [pi]
mov rax, 1 ; need to tell printf how many floats
call printf
; note next step - this puts a zero in rax
xor rax,rax
ret ; this returns to the OS based on how Windows calls programs.
; this return causes a delay then the program exits.
You managed to mix the microsoft and the sysv convention. The correct way is:
mov rcx, textformat
movq xmm1, qword [pi]
movq rdx, xmm1 ; duplicate into the integer register
sub rsp, 40 ; allocate shadow space and alignment (32+8)
call printf
add rsp, 40 ; restore stack
xor eax, eax
ret
According to MSDN, when using varargs:
For floating-point values only, both the integer and the floating-point register will contain the float value in case the callee expects the value in the integer registers.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
I am writing a toy kernel for learning purposes, and I am having a bit of trouble with it. I have made a simple bootloader that loads a segment from a floppy disk (which is written in 32 bit code), then the bootloader enables the A20 gate and turns on protected mode. I can jump to the 32 bit code fine if I write it in assembler, but if I write it in C, I get a triple fault. When I disassemble the C code I can see that the first two instructions involve setting up a new stack frame. This is the key difference between the working ASM code and the failing C code.
I am using NASM v2.10.05 for the ASM code, and GCC from the DJGPP 4.72 collection for the C code.
This is the bootloader code:
org 7c00h
BITS 16
entry:
mov [drive], dl ;Save the current drive
cli
mov ax,cs ; Setup segment registers
mov ds,ax ; Make DS correct
mov ss,ax ; Make SS correct
mov bp,0fffeh
mov sp,0fffeh ;Setup a temporary stack
sti
;Set video mode to text
;===================
mov ah, 0
mov al, 3
int 10h
;===================
;Set current page to 0
;==================
mov ah, 5
mov al, 0
int 10h
;==================
;Load the sector
;=============
call load_image
;=============
;Clear interrupts
;=============
cli
;=============
;Disable NMIs
;============
in ax, 70h
and ax, 80h ;Set the high bit to 1
out 70h, ax
;============
;Enable A20:
;===========
mov ax, 02401h
int 15h
;===========
;Load the GDT
;===========
lgdt [gdt_pointer]
;===========
;Clear interrupts
;=============
cli
;=============
;Enter protected mode
;==================
mov eax, cr0
or eax, 1 ;Set the low bit to 1
mov cr0, eax
;==================
jmp 08h:clear_pipe ;Far jump to clear the instruction queue
;======================================================
load_image:
reset_drive:
mov ah, 00h
; DL contains *this* drive, given to us by the BIOS
int 13h
jc reset_drive
read_sectors:
mov ah, 02h
mov al, 01h
mov ch, 00h
mov cl, 02h
mov dh, 00h
; DL contains *this* drive, given to us by the BIOS
mov bx, 7E0h
mov es, bx
mov bx, 0
int 13h
jc read_sectors
ret
;======================================================
BITS 32 ;Protected mode now!
clear_pipe:
mov ax, 10h ; Save data segment identifier
mov ds, ax ; Move a valid data segment into the data segment register
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax ; Move a valid data segment into the stack segment register
mov esp, 90000h ; Move the stack pointer to 90000h
mov ebp, esp
jmp 08h:7E00h ;Jump to the kernel proper
;===============================================
;========== GLOBAL DESCRIPTOR TABLE ==========
;===============================================
gdt: ; Address for the GDT
gdt_null: ; Null Segment
dd 0
dd 0
gdt_code: ; Code segment, read/execute, nonconforming
dw 0FFFFh ; LIMIT, low 16 bits
dw 0 ; BASE, low 16 bits
db 0 ; BASE, middle 8 bits
db 10011010b ; ACCESS byte
db 11001111b ; GRANULARITY byte
db 0 ; BASE, low 8 bits
gdt_data: ; Data segment, read/write, expand down
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
gdt_end: ; Used to calculate the size of the GDT
gdt_pointer: ; The GDT descriptor
dw gdt_end - gdt - 1 ; Limit (size)
dd gdt ; Address of the GDT
;===============================================
;===============================================
drive: db 00 ;A byte to store the current drive in
times 510-($-$$) db 00
db 055h
db 0AAh
And this is the kernel code:
void main()
{
asm("mov byte ptr [0x8000], 'T'");
asm("mov byte ptr [0x8001], 'e'");
asm("mov byte ptr [0x8002], 's'");
asm("mov byte ptr [0x8003], 't'");
}
The kernel simply inserts those four bytes into memory, which I can check as I am running the code in a VMPlayer virtual machine. If the bytes appear, then I know the code is working. If I write code in ASM that looks like this, then the program works:
org 7E00h
BITS 32
main:
mov byte [8000h], 'T'
mov byte [8001h], 'e'
mov byte [8002h], 's'
mov byte [8003h], 't'
hang:
jmp hang
The only differences are therefore the two stack operations I found in the disassembled C code, which are these:
push ebp
mov ebp, esp
Any help on this matter would be greatly appreciated. I figure I am missing something relatively minor, but crucial, here, as I know this sort of thing is possible to do.
Try using the technique here:
Is there a way to get gcc to output raw binary?
to produce a flat binary of the .text section from your object file.
I'd try moving the address of your protected mode stack to something else - say 0x80000 - which (according to this: http://wiki.osdev.org/Memory_Map_(x86)) is RAM which is guaranteed free for use (as opposed to the address you currently use - 0x90000 - which apparently may not be present - depending a bit on what you're using as a testing environment).