I'm trying to print something on screen using my print function.
I've stumbled on a small problem - when I pass the character array like this:
char s[] = "abc";
print(s);
It works fine, but when i call it like this there is no effect.
print("abc");
Here is my function declaration
//print function
void print(char* message);
Am I missing something? printf works the same way, and you can pass the string by the second way.
EDIT:
definitions
void print_at(char* message, int col, int row){
if(col >= 0 && row >= 0){
set_cursor(get_screen_offset(col,row));
}
int i = 0;
while(message[i] != 0){
print_char(message[i++],-1,-1,WHITE_ON_BLACK);
}
}
void print(char* message){
print_at(message, -1,-1);
}
EDIT2: objdump of kernel.o
void start(){
clear_screen();
char s[] = "abc";
print("abc");
print(s);
while(1);
}
Disassembly of section .text:
00000000 <_start>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 ec 28 sub esp,0x28
6: e8 00 00 00 00 call b <_start+0xb> //clear_screen()
b: c7 45 f4 61 62 63 00 mov DWORD PTR [ebp-0xc],0x636261 //"bca"
12: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0
19: e8 00 00 00 00 call 1e <_start+0x1e> //print()
1e: 8d 45 f4 lea eax,[ebp-0xc]
21: 89 04 24 mov DWORD PTR [esp],eax
24: e8 00 00 00 00 call 29 <_start+0x29> //print()
29: eb fe jmp 29 <_start+0x29>
2b: 90 nop
EDIT3:
Since this might be something with the way I am initilising the enviroment, here are the 2 files responsible:
pmode.asm -initializes segments, and jumps to start of kernel
[bits 16]
switch_to_pm:
cli ; switch interuppts off
lgdt [gdt_descriptor] ; load global descriptor table
mov eax, cr0 ; set control registers first bit to protected mode
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:init_pm ;flush cache by far jump
[bits 32]
init_pm:
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
call BEGIN_PM
here is how I build the gdt:
; GDT
gdt_start:
gdt_null: ; the mandatory null descriptor
dd 0x0 ; ' dd ' means define double word ( i.e. 4 bytes )
dd 0x0
gdt_code: ; the code segment descriptor
; base =0 x0 , limit =0 xfffff ,
; 1 st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b
; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b
; 2 nd flags : ( granularity )1 (32- bit default )1 (64- bit seg )0 ( AVL )0 -> 1100 b
dw 0xffff ; Limit ( bits 0-15)
dw 0x0 ; Base ( bits 0-15)
db 0x0 ; Base ( bits 16-23)
db 10011010b ; 1 st flags , type flags
db 11001111b ; 2 nd flags , Limit ( bits 16-19)
db 0x0 ; Base ( bits 24-31)
gdt_data: ; the data segment descriptor
; Same as code segment except for the type flags :
; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b
dw 0xffff ; Limit ( bits 0-15)
dw 0x0 ; Base ( bits 0-15)
db 0x0 ; Base ( bits 16-23)
db 10010010b ; 1 st flags , type flags
db 11001111b ; 2 nd flags , Limit ( bits 16-19)
db 0x0 ; Base ( bits 24-31)
gdt_end: ; The reason for putting a label at the end of the
; GDT is so we can have the assembler calculate
; the size of the GDT for the GDT decriptor ( below )
; GDT descriptior
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one
; of the true size
dd gdt_start ; Start address of our GDT
; Define some handy constants for the GDT segment descriptor offsets , which
; are what segment registers must contain when in protected mode. For example ,
; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our
; case is the DATA segment (0 x0 -> NULL ; 0 x08 -> CODE ; 0 x10 -> DATA )
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
A whole array cannot be passed as an argument to a function in C++. You can, however, pass a pointer to an array without an index by specifying the array's name. In C, when we pass an array to a function say fun(), it is always treated as a pointer by fun().
In C programming, the collection of characters is stored in the form of arrays. This is also supported in C++ programming. Hence it's called C-strings. C-strings are arrays of type char terminated with null character, that is, \0 (ASCII value of null character is 0).
In C, char* means a pointer to a character. Strings are an array of characters eliminated by the null character in C.
In C, if you need to amend a string in a called function, pass a pointer to the first char in the string as an argument to the function. If you have allocated storage for the string outside the function, which you cannot exceed within the function, it's probably a good idea to pass in the size.
I have found the answer, after looking at a disassembly with a much bigger string.
The reason was the way I linked the kernel. These were the commands I was advised to use:
ld -o kernel.bin -Ttext 0x1000 $^ --oformat binary
but since I had a windows gcc, and my asm and C files were in elf format, I had to use this trick:
ld -o kernel.out -Ttext 0x1000 $^
objcopy -O binary -j .text kernel.out $@
This copied only the text part of the object, so I was left with the binary version. Since I only copied the .text part of the object, my strings that were kept in the .rdata sections were lost. So it was simply a matter of adding this to objcopy:
objcopy -O binary -j .text -j .rdata kernel.out $@
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With