.pos 0x200
.align 4
InputArray:
.long 5
.long 10
Done: .long 0x0
.pos 0x400
.align 4
OutputArray:
.pos 0x0
irmovl InputArray,%eax
irmovl OutputArray, %esi
Loop:
mrmovl (%eax), %ecx # get first element from InputArray
mrmovl (%eax), %edi # a copy of first element used for multiplication
irmovl $4, %ebx # increment the pointer of InputArray
addl %ebx, %eax
mrmovl (%eax), %edx # get second element from InputArray
irmovl $1, %ebx # add first element to its copy for the amount of second element
subl %ebx, %edx
jg mult
rmmovl %ecx,(%esi) # output value to OutputArray
mult:
addl %edi, %ecx
subl %ebx, %edx
jg mult
Exit: halt
This is a really simple program that does unsigned mutiplication of a pair of integers (5 times 10) I wrote for Y86.
When I run the code, the result looks like this:
Stopped in 38 steps at PC = 0x42. Status 'HLT', CC Z=1 S=0 O=0
Changes to registers:
%eax: 0x00000000 0x00000204
%ecx: 0x00000000 0x00000032
%ebx: 0x00000000 0x00000001
%esi: 0x00000000 0x00000400
%edi: 0x00000000 0x00000005
Changes to memory:
The register ecx for the result of calculation is 32 in hex so I definitely know that the mult loop has performed as intended, but nothing is being output into OutputArray which I don't understand why.
I am not sure if I understand what you mean by "No code is supposed to be after a jump statement in a label and the correct alternative was to locate the subsequent code in another label and use another jump statement to invoke that label", but the problem with your code is that you forgot a ret statement in your Mult loop.
Also, your code terminated ungracefully once you were done with the Loop part of the code (you ran through the Mult loop code again before encountering the halt.)
The following code works (but note that it is not ideal -- among other things, it does not account for negative values and overflow):
.pos 0x200
.align 4
InputArray:
.long 5
.long 10
Done: .long 0x0
.pos 0x400
.align 4
OutputArray:
.pos 0x0
irmovl InputArray,%eax
irmovl OutputArray, %esi
Loop:
mrmovl (%eax), %ecx # get first element from InputArray
mrmovl (%eax), %edi # a copy of first element used for multiplication
irmovl $4, %ebx # increment the pointer of InputArray
addl %ebx, %eax
mrmovl (%eax), %edx # get second element from InputArray
irmovl $1, %ebx # add first element to its copy for the amount of second element
subl %ebx, %edx
jg mult
rmmovl %ecx,(%esi) # output value to OutputArray
jmp Exit
mult:
addl %edi, %ecx
subl %ebx, %edx
jg mult
ret
Exit: halt
Related
I'm developing a homebrew os and I can't figure out how to switch from my homebrew bootloader to my homebrew kernel.
I do not know how it works
I know it starts with memory, but I don't know what number it starts with
I've been told that I can just look at the minix source, but when I parse it from the iso, it doesn't tell me anything at all, all it tells me is the configuration of the linux directory.
I've been trying to figure it out for a week, but nothing has come up.
I'm sorry if I'm not trying hard enough.
And since there didn't seem to be any similar questions, I thought I'd ask you a question
I'll show you a well commented minimal example of jumping to a C++ kernel I wrote so maybe you can go from there and write your own. I work on Linux Ubuntu 20.
In your home directory create a folder named OS and create a file named kernel.cpp in that folder. Place the following content in that file:
void map_framebuffer(){
unsigned long* page_directory_table_pointer = (unsigned long*) 0xb018;
*page_directory_table_pointer = 0x1e00f;
unsigned long* page_table_pointer = (unsigned long*) 0x1e000;
/*
This is the actual address of the framebuffer in QEMU
for the video mode I set up in boot.asm.
In this case it is hardcoded. You may want to actually get this from the VBE structure.
The VBE structure is placed at 0x7e00 in memory by the boot.asm routine.
*/
unsigned long framebuffer_address = (unsigned long) 0xfd00000f;
for (unsigned int i = 0; i < 512; i++){
*page_table_pointer = framebuffer_address;
framebuffer_address += 0x1000;
page_table_pointer++;
}
}
void main(){
map_framebuffer();
/*
I made sure that the framebuffer is mapped to adress 0x600000 in the map_framebuffer() function.
You may want to identity map it instead or map it somewhere else. You'll then have to
calculate page table offsets and stuff.
*/
unsigned char* framebuffer_pointer = (unsigned char*)0x600000;
//Make a few pixels become white. Each pixel is 3 bytes RGB.
for (unsigned int i = 0; i < 10000; i++)
*(framebuffer_pointer + i) = 0xff;
asm volatile(
"halt:\n\t"
"hlt\n\t"
"jmp halt\n\t");
}
/*
Memory map of kernel
7e00 - 8000 -> VBE INFO structure
8000 - 9000 -> paging.bin (booting asm code)
9000 - a000 -> pml4
a000 - b000 -> pdpt
b000 - c000 -> pdt
1b000 - 1c000 -> pt1 (kernel identity mapped pages)
1c000 - 1d000 -> pt2 (kernel identity mapped pages)
1d000 - 1e000 -> pt3 (kernel identity mapped pages)
1e000 - 1f000 -> pt4 (mapped to the framebuffer, only 2MB are mapped to the framebuffer which could take more than this)
30000 - 80000 -> kernel.elf (temporary kernel file)
80000 -> kernel stack (growing downward)
*/
Now create a file named boot.asm in the same directory and write the following content into it:
org 0x7c00
bits 16
;Set the video mode to VBE graphics mode for being able to draw onto the screen
jmp get_vbe_info
error:
mov ah, 0x0e
mov al, 'e'
int 0x10
hlt
get_vbe_info:
mov ax, 0x4f01 ;c0 00 00 00
mov cx, 0x118
xor bx, bx
mov es, bx
mov di, 0x7e00
int 0x10
cmp ax, 0x004f
jne error
set_video_mode:
mov ax, 0x4f02
mov bx, 0x4118
int 0x10
cmp ax, 0x004f
jne error
;Set up a real mode stack
mov ax, 0x00
mov ss, ax
mov sp, 0x7c00
;Load the second stage bootloader at 0x8000
mov ah, 0x00 ;reset drive
int 0x13
xor ax, ax
mov es, ax
mov bx, 0x8000 ;es:bx = 0x0000:0x8000 = 0x8000
mov ah, 0x02 ;read sector function
mov al, 0x09 ;read 9 sectors
mov ch, 0x00 ;cylinder 0
mov dh, 0x00 ;head 0
mov cl, 0x02 ;start from sector 2
int 0x13
;Load the cpp kernel at 0x30000
mov ah, 0x00
int 0x13
mov ax, 0x3000
mov es, ax
xor bx, bx ;es:bx = 0x3000:0x0000 = 0x3000 * 0x10 + 0x0 = 0x30000
mov ah, 0x02 ;read sector function
mov al, 0x3c ;read 60 sectors to load the whole file
mov ch, 0x00 ;cylinder 0
mov dh, 0x00 ;head 0
mov cl, 0x0a ;start from sector 10 (because this is where lies the ELF CPP kernel in the disk.img we created with dd)
int 0x13
xor ax, ax
mov ds, ax
;Set up a protected mode stack
mov ax, 0x10 ;10000b = 10 for segment selector 2 (data)
mov ss, ax
mov sp, 0x7c00
cli
lgdt[gdtr]
;Enable protected mode
mov eax, cr0
or al, 1
mov cr0, eax
jmp 0x08:protectedMode ;0x08 = 1000b, 1 for segment selector
;0 for gdt not ldt and 00 for privilege
bits 32
protectedMode:
mov ax, 0x10
mov ds, ax
;Jump to 0x8000 (the second stage bootloader)
jmp 0x08:0x8000
gdt_start:
dq 0x0
gdt_code:
dw 0xFFFF ;limit 0-15
dw 0x0 ;base 0-15
db 0x0 ;base 16-23
db 10011010b ;pr, privi (2), s, ex, dc, rw, ac
db 11001111b ;gr, sz, limit 16-19
db 0x0 ;base 24-31
gdt_data:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdtr:
dw 24
dd gdt_start
times 510 - ($-$$) db 0
dw 0xAA55
Now create a third file named paging.asm and place the following into it:
org 0x8000
bits 32
;Set up paging
mov eax, 0x00009008
mov cr3, eax
pml4t:
mov dword [0x9000], 0x0000a00f
mov dword [0x9004], 0x0
pdpt:
mov dword [0xa000], 0x0000b00f
mov dword [0xa004], 0x0
pdt:
mov dword [0xb000], 0x0001b00f
mov dword [0xb004], 0x0
mov dword [0xb008], 0x0001c00f
mov dword [0xb00c], 0x0
mov dword [0xb010], 0x0001d00f
mov dword [0xb014], 0x0
pt:
mov edx, 0x3
mov eax, 0x200
mov ebx, 0x0000000f
mov ecx, 0x1b000
next_table:
next_entry:
mov dword [ecx], ebx
add ecx, 0x4
mov dword [ecx], 0x0
add ebx, 0x1000 ;add 4096 to the adress pointed to by ebx (the next physical page)
add ecx, 0x4
sub eax, 0x1
cmp eax, 0x0
jne next_entry
mov eax, 0x200
sub edx, 0x1
cmp edx, 0x0
jne next_table
mov eax, cr4 ;enable PAE-paging
or eax, 1 << 5
mov cr4, eax
mov ecx, 0xC0000080 ;set long mode bit in EFER MSR
rdmsr
or eax, 1 << 8
wrmsr
mov eax, cr0 ;enable paging
or eax, 1 << 31
mov cr0, eax
lgdt[gdtr] ;load a 64 bit gdt (will be ignored afterwards)
jmp 0x08:longMode
bits 64
longMode:
mov ax, 0x10 ;10000b = 10 for segment selector 2 (data)
mov ss, ax
mov rsp, 0x80000
mov rdi, 0x30000 ;address where elf reading occurs
mov rsi, 0x30000
add rdi, 24 ;program entry
mov rax, [rdi] ;placed in rax
add rdi, 8 ;program header table position
mov rbx, [rdi] ;put it in rbx
add rdi, 24 ;move to number of entries in program header
mov cx, [rdi] ;put it in cx (2 bytes)
mov rdi, 0x30000 ;beginning of file
add rdi, rbx ;go to program header 0
add rsi, rbx
next_segment:
mov edx, [rdi] ;put the type of segment in edx (4 bytes)
cmp edx, 0x1 ;determine if it is loadable
jne not_loadable ;if it isnt jump to not_loadable
add rdi, 0x8 ;else load it
mov r8, [rdi] ;put the offset in the file where data for this segment resides in r8
add rdi, 0x8 ;go to virtual address of segment
mov r9, [rdi] ;put it in r9
add rdi, 0x10 ;move to size of segment in file
mov r10, [rdi] ;put it in r10
add rdi, 0x8 ;move to size of segment in memory
mov r11, [rdi] ;put it in r11
mov rdi, 0x30000 ;move back to beginning of file
add rdi, r8 ;add segment offset to be at the segment position
next_byte:
mov dl, [rdi] ;put the byte at rdi in dl
mov [r9], dl ;move it to virtual address in r9
add r9, 0x1 ;add 1 byte to virtual address
add rdi, 0x1 ;add 1 byte to rdi
sub r10, 0x1 ;substract 1 byte from size of segment in file
sub r11, 0x1 ;substract 1 byte from size of segment in memory
cmp r10, 0x0 ;is segment finished
jne next_byte ;if not go to next_byte
cmp r11, 0x0 ;if yes tcheck memory size
je no_padding ;if no padding required jmp to no_padding
padding:
xor dl, dl
mov [r9], dl
add r9, 0x01
sub r11, 0x1
cmp r11, 0x0
jne padding
not_loadable:
no_padding:
sub cx, 0x1
cmp cx, 0x0
je finished
add rsi, 56
mov rdi, rsi
jmp next_segment
finished:
jmp rax ;Jump to the entry point of the ELF CPP file
halt:
hlt
jmp halt
;The 64-bits GDT
gdt_start:
dw 0xFFFF
dw 0
db 0
db 0
db 1
db 0
gdt_code:
dw 0x1111 ;limit 0-15
dw 0x0 ;base 0-15
db 0x0 ;base 16-23
db 10011010b ;pr, privi (2), s, ex, dc, rw, ac
db 10101111b ;gr, sz, limit 16-19
db 0x0 ;base 24-31
gdt_data:
dw 0x1111
dw 0x0
db 0x0
db 10010010b
db 00001111b
db 0x0
gdtr:
dw $ - gdt_start - 1
dq gdt_start
Now open a bash terminal and type the following commands in order (making sure to have NASM, g++ and QEMU installed):
nasm -fbin OS/boot.asm -oOS/boot.bin
nasm -fbin OS/paging.asm -oOS/paging.bin
g++ -static -ffreestanding -nostdlib -c -m64 OS/kernel.cpp -oOS/kernel.o
ld --entry main --oformat elf64-x86-64 --no-dynamic-linker -static -nostdlib OS/kernel.o -oOS/kernel.elf
dd if=/dev/zero of=OS/disk.img count=100 & dd if=OS/boot.bin of=OS/disk.img conv=notrunc & dd if=OS/paging.bin of=OS/disk.img seek=1 conv=notrunc & dd if=OS/kernel.elf of=OS/disk.img seek=9 conv=notrunc
qemu-system-x86_64 -hda OS/disk.img -s
You can always put all that in a bash file and launch it automatically. The -s in the QEMU command makes the virtual machine listen on port 1234. You can use gdb to debug the kernel. Type in gdb in a bash shell then target remote localhost:1234. You will then be debugging the machine. You can type dump memory result.bin 0x1000 0x2000 to dump the memory of the machine from 0x1000 to 0x2000 (or any address) in the file result.bin. You can use hexdump -C result.bin to look at the memory dump from terminal. The output is little endian so you need to reverse it for the actual values.
In the code above I'm loading the ELF file compiled with g++ to address 0x30000. Then I'm parsing the file and jumping to its entry point. The cpp code maps the framebuffer in virtual memory and makes a few pixels become white. The code is far from perfect but if you understand it in its entirety then you can go a long way.
I have written a "method" is Nasm 64bit mode. I just want some advise on how I could have done it better as I have just started coding in Nasm. For example is there a way to use less registers, or are my naming conventions horrible(they probably are!), is there a better way to print negative values, etc.
bits 64
section .text
global _start
_start:
;debugging
mov rbp, rsp
push -666 ;number I want to print
call _printVal ;calling the print "method"
;exit
mov rax, 1
mov rbx, 0
int 80h
_printVal:
;prolog
push rbp
mov rbp, rsp
;save registers
push rbx
push rdi
push rsi
;actual code
mov rax, [rbp + 16];get value user wants to print
mov rbx, 10 ;we will be dividing by the to get the last digit
xor rcx, rcx ; clear the rcx register as this will be out counter
cqo; extend the flag
cmp rdx, -1 ;check to see if negative
jne _divisionLoop;if not negative jump
;print negative sign
push 45;negative sign value
mov rax, 1; sys_write
mov rdi, 1; stdout
mov rsi, rsp ; address to what to print
mov rdx, 1 ; how many bytes is it
syscall
pop rax ;pop negative sign value from stack
mov rax, [rbp + 16]; get user value again
converting negative value to positive
dec rax
not rax
xor rcx, rcx; clear rcx because of syscall again
_divisionLoop:
xor rdx, rdx
div rbx ;divides number by 10 to move over last digit into rdx reg
push rdx ;pushes the remainder onto the stack
inc rcx ; count for how many digits added to stack
cmp rax, 0
jnz _divisionLoop ;jump if the division did not result in a zero
mov rax, 1; sys_write
mov rdi, 1; stdout
mov rdx, 1; 1 byte long
_printToScreenVal:
mov rsi, rsp; pop value from stack (most significant digit)
push rcx; (save rcx)
mov rcx, 48
add [rsi], rcx; add 48 to make it into ASCII value
syscall
pop rcx; get rcx register back
pop rbx; pop most siginificant digit
dec rcx; decrement how many more digits i have to print
jnz _printToScreenVal
;restore register
pop rsi
pop rdi
pop rbx
;epilog
pop rbp
ret
Here is the Y86 code for reference
.pos 0
irmovq stack, %rsp # initialize stack pointer
call main
halt
.align 8
input_array:
.quad 6
.quad 4
.quad 5
.quad 2
.quad 3
.quad 1
count:
.quad 6
main:
irmovq input_array, %rdi
irmovq count, %rsi
mrmovq (%rsi), %rsi
call bubble_sort
ret
# bubble_sort(long *data, long count)
bubble_sort:
irmovq $1, %rcx
subq %rcx, %rsi # last = count - 1
irmovq $8, %r14 # %r14 = sizeof(int*)
rrmovq %rdi, %r13 # %r13 = data
outer_loop:
rrmovq %r13, %rdi # data = data (param)
irmovq $0, %rbx # i = 0
inner_loop:
mrmovq (%rdi), %r9 # %r9 = data[i]
addq %r14, %rdi # data += 1
mrmovq (%rdi), %r10 # %r10 = data[i + 1]
if_statement:
rrmovq %r9, %r11
subq %r10, %r11 # %r11 = data[i] - data[i + 1]
jg then # if data[i] > data[i + 1], goto then
jmp end_if
then:
rrmovq %r10, %r11 # temp = data[i + 1]
rmmovq %r9, (%rdi) # data[i + 1] = data[i]
rmmovq %r11, -8(%rdi) # data[i] = temp
end_if:
addq %rcx, %rbx # i++
rrmovq %rbx, %rax # %rax = i
subq %rsi, %rax # %rax = i - last
jl inner_loop # goto inner_loop if i < last
subq %rcx, %rsi # last--
jg outer_loop # goto outer_loop if last > 0
ret
.pos 0x100
stack:
For reference, I'm using this Y86-64 simulator. The problem occurs after the first sequence of inner loops is completed. The register value in %rsi is 4 and I have just substracted 1 from %rsi's previous value just before the jg instruction is processed, but it still doesn't jump to outer_loop.
I wrote a kernel to print hello world. but it's not working,
hello world is not printed.
assembly print is working well.
I using GCC and NASM for cross compile and assemble.
This is the my bootloader, second stage code and C code.
[ORG 0x00]
[BITS 16]
section .text
JMP 0x07C0:START ;
START:
mov ax, cs
mov ds, ax
mov es, ax
mov ax, 0x0000
mov ss, ax
mov ax, 0xffff
mov sp, ax
mov bp, ax
mov si, 0xB800
mov es, si
mov bx, 0x0000
mov cx, 80*25*2
CLEAR: ;Clear screen
mov byte[es:bx], 0x00
inc bx
loop CLEAR
READ: ;Read disk
mov si, 0x1000
mov es, si
mov bx, 0x0000 ; ES:BX, 0x1000:0000
mov ah, 0x02
mov al, 2
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, 0
int 0x13
jnc A20_ENABLE ; No Error -> jump to A20_ENABLE
READERROR: ;If failed to read disk,
jmp $
A20_ENABLE: ;Enable A20 GATE with BIOS
mov ax, 0x2401
int 0x15
jnc ENTRY
A20_FAIL: ;If BIOS Interrupt is not working, use Control Port.
mov al, 2 ;
out 0x92, al
ENTRY: ;Entry Point
cli
lgdt [GDTR]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp $+2 ; for pipeline
nop
nop
jmp dword 0x08:0x10000 ; jump to second stage
GDTR: ; GDT
dw GDTEND - GDT - 1
dd GDT+0x7C00
GDT:
NULL:
dw 0x00 ;프로세서 예약구간
dw 0x00
db 0x00
db 0x00
db 0x00
db 0x00
CODE:
dw 0xFFFF ;LIMIT ADRESS
dw 0x0000 ;BASE ADRESS
db 0x00 ;BASE ADRESS
db 0x9A ;1001 1010 A code segment descriptor (for your kernel, it should have type=0x9A)
db 0xCF ;1100 1111
db 0x00
DATA:
dw 0xFFFF
dw 0x0000
db 0x01
db 0x92 ;1001 0010 A data segment descriptor (you cant write to a code segment, so add this with type=0x92)
db 0xCF ;1100 1111
db 0x00
VIDEO:
dw 0xFFFF
dw 0x8000
db 0x0B
db 0x92
db 0x40
db 0x00
GDTEND:
times 510 - ($-$$) db 0x00
db 0x55
db 0xAA
Second stage.
[ORG 0x10000]
[BITS 32]
PROTECTED:
mov ax, 0x10
mov gs, ax
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
xor esp, esp ; make stack
mov esp, 0xFFFE
mov ebp, 0xFFFE
mov si, 0x18 ; setting for print message
mov es, si
mov bx, 0
lea si, [message]
mov ah, 0x0f
MESSAGE: ; print loop
lodsb
cmp al, 0
je LOADKERNEL
mov [es:bx], ax
add bx, 2
jmp MESSAGE
LOADKERNEL:
jmp dword 0x08:0x10200 ; jump to C kernel
message: db 'Protected Mode Entered.', 0
times 512 - ($ - $$) db 0x00
and C code also.
void write_string();
void main(){
write_string(0x09, "helloworld");
}
void write_string( int colour, const char *string )
{
volatile char *video = (volatile char*)0xB8000;
while( *string != 0 )
{
*video++ = *string++;
*video++ = colour;
}
}
I converted C code to assembly with ld, gcc and objcopy.
gcc -c -m32 -ffreestanding kernel.c -o kernel.o
ld -melf_i386 -Ttext 0x10200 -nostdlib kernel.o -o kernel.tmp
objcopy -O binary kernel.tmp kernel.img
cat bootloader.img secondstage.img kernel.img > Disk
like this.
I really can't understand why it's not working.
If you need more information, plz leave a comment.
I had made a code that display the largest but then my teacher ask us to make another one that input 3 numbers and display the smallest value.
here is the code:
org 100h
jmp start
msg1 db 10,13,"Enter first number: $"
msg2 db 10,13,"Enter second number: $"
msg3 db 10,13,"Enter third Number: $"
num1 db ?
num2 db ?
num3 db ?
start:
lea dx, msg1
mov ah, 9
int 21h
mov ah, 1
int 21h
mov num1, al
lea dx, msg2
mov ah, 9
int 21h
mov ah, 1
int 21h
mov num2, al
lea dx, msg3
mov ah, 9
int 21h
mov ah, 1
int 21h
mov num3, al
mov bl, num1
cmp bl, num2
jng number2
cmp bl, num3
jng number3
mov ah, 2
mov dl, num1
int 21h
jmp escape
number2:
mov bl, num2
cmp bl, num3
jng number3
mov ah, 2
mov dl, num2
jmp escape
number3:
mov ah, 2
mov dl, num3
int 21h
escape:
ret
sample output:
1st no. i enter 3
2nd no, i enter 2
3rd no, i enter 1
and the largest is 3 but the output will be 13 because i don't know how to put space on my code :D...
Pls help!!! XD Also it's my first time posting this... so sorry for my bad grammar ty.
mov ah, 2
mov dl, num2
jmp escape
In this part your program forgot to actually call DOS with int 21h.
i don't know how to put space on my code
Just use the following everywhere you need some space between outputs on the same line:
mov ah, 2
mov dl, " "
int 21h
Or put items on different lines using:
mov ah, 2
mov dl, 10
int 21h
mov dl, 13
int 21h
A nicer solution would be to display a suitable message before outputting the number:
msg4 db 10,13,"Smallest value: $"
...
lea dx, msg4
mov ah, 9
int 21h
my teacher ask us to make another one that input 3 numbers and display the smallest value.
Simply change all of those jng (jump on not greater) instructions by the jnl (jump on not less) instruction.
This is a slightly better version of your code and using jnl:
mov bl, num1
cmp bl, num2
jnl number2
cmp bl, num3
jnl number3
mov dl, num1
jmp Print
number2:
mov bl, num2
cmp bl, num3
jnl number3
mov dl, num2
jmp Print
number3:
mov dl, num3
Print:
mov ah, 2
int 21h
ret
Good luck monday!