Problem with string output

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
gaf
Member
Member
Posts: 349
Joined: Thu Oct 21, 2004 11:00 pm
Location: Munich, Germany

Post by gaf »

Hello,
I just ran through the disassembled source, picked out the interesting parts and commented them. Unfortunatelly I'm still quite clueless what actually causes the bug, especially as the assembler code looks fine to me:

Code: Select all

this[0] = col
this[4] = pos
this[8] = off
this[C] = vidmem

Video::Video()
24:   55                      push   %ebp
125:   89 e5                   mov    %esp,%ebp
127:   8b 45 08                mov    0x8(%ebp),%eax     ; eax = this
12a:   c7 40 04 00 00 00 00    movl   $0x0,0x4(%eax)     ; pos = 0
131:   8b 45 08                mov    0x8(%ebp),%eax
134:   c7 40 08 00 00 00 00    movl   $0x0,0x8(%eax)     ; off = 0
13b:   8b 45 08                mov    0x8(%ebp),%eax
13e:   c7 00 00 07 00 00       movl   $0x700,(%eax)      ; col = 0x700
144:   8b 45 08                mov    0x8(%ebp),%eax
147:   c7 40 0c 00 80 0b 00    movl   $0xb8000,0xc(%eax) ; vidmem = 0xB8000
14e:   5d                      pop    %ebp
14f:   c3                      ret


Video::accapo()
150:   55                      push   %ebp
151:   89 e5                   mov    %esp,%ebp
153:   8b 45 08                mov    0x8(%ebp),%eax     ; eax = this
156:   8b 40 08                mov    0x8(%eax),%eax     ; eax = off
159:   8d 50 50                lea    0x50(%eax),%edx    ; edx = off[0x50]

15c:   8b 45 08                mov    0x8(%ebp),%eax     ; eax  = this
15f:   89 50 08                mov    %edx,0x8(%eax)     ; off += 80

162:   8b 45 08                mov    0x8(%ebp),%eax     ; eax = this
165:   c7 40 04 00 00 00 00    movl   $0x0,0x4(%eax)     ; pos = 0
16c:   5d                      pop    %ebp
16d:   c3                      ret



void Video::put(const char c)
16e:   55                      push   %ebp
16f:   89 e5                   mov    %esp,%ebp
171:   83 ec 1c                sub    $0x1c,%esp            ; reserve 7 locals
174:   8b 45 0c                mov    0xc(%ebp),%eax        ; eax = param0
177:   88 45 ec                mov    %al,0xffffffec(%ebp)  ; local[ec] = eax
17a:   8b 45 08                mov    0x8(%ebp),%eax        ; eax = this
17d:   c7 40 0c 00 80 0b 00    movl   $0xb8000,0xc(%eax)    ; vidmem = 0xB8000

184:   80 7d ec 0a             cmpb   $0xa,0xffffffec(%ebp) ; if(c == '\n')
188:   75 10                   jne    0x19a                 ; {
18a:   8b 45 08                mov    0x8(%ebp),%eax
18d:   89 04 24                mov    %eax,(%esp)
190:   e8 bb ff ff ff          call   0x150                 ;    accapo()
195:   e9 80 00 00 00          jmp    0x21a                 ;    return
     	       	                                          ; }

19a:   80 7d ec 09             cmpb   $0x9,0xffffffec(%ebp) ; if(c == '\t')
19e:   75 2a                   jne    0x1ca                 ; {
1a0:   c7 45 fc 00 00 00 00    movl   $0x0,0xfffffffc(%ebp) ;    local[fc] = 0
1a7:   eb 19                   jmp    0x1c2                 ;    while(local[fc] < 8)
1a9:   c7 44 24 04 20 00 00    movl   $0x20,0x4(%esp)       ;    {
1b0:   00                                                   ;         push char(' ')
1b1:   8b 45 08                mov    0x8(%ebp),%eax        ;         push this
1b4:   89 04 24                mov    %eax,(%esp)
1b7:   e8 b2 ff ff ff          call   0x16e                 ;         call put
1bc:   8d 45 fc                lea    0xfffffffc(%ebp),%eax
1bf:   83 00 01                addl   $0x1,(%eax)           ;         local[fc] ++
1c2:   83 7d fc 07             cmpl   $0x7,0xfffffffc(%ebp) ;    }
1c6:   7e e1                   jle    0x1a9
1c8:   eb 50                   jmp    0x21a                 ;    return
                                                            ; }

1ca:   8b 45 08                mov    0x8(%ebp),%eax
1cd:   8b 40 04                mov    0x4(%eax),%eax
1d0:   83 f8 50                cmp    $0x50,%eax            ; if(pos == 80)
1d3:   75 0b                   jne    0x1e0                 ; {
1d5:   8b 45 08                mov    0x8(%ebp),%eax
1d8:   89 04 24                mov    %eax,(%esp)
1db:   e8 70 ff ff ff          call   0x150                 ;    accapo()
                                                            ; }

1e0:   8b 45 08                mov    0x8(%ebp),%eax
1e3:   8b 48 0c                mov    0xc(%eax),%ecx        ; ecx =  vidmem
1e6:   8b 45 08                mov    0x8(%ebp),%eax
1e9:   8b 50 04                mov    0x4(%eax),%edx        ; edx = pos
1ec:   8b 45 08                mov    0x8(%ebp),%eax
1ef:   8b 40 08                mov    0x8(%eax),%eax        ; eax = off
1f2:   8d 04 02                lea    (%edx,%eax,1),%eax    ; eax = pos + off
1f5:   01 c0                   add    %eax,%eax             ; multiply with 2 (short)
1f7:   01 c1                   add    %eax,%ecx             ; ecx (vidmem) += (pos + off)*2
1f9:   0f b6 45 ec             movzbl 0xffffffec(%ebp),%eax
1fd:   66 0f b6 d0             movzbw %al,%dx               ; dx = param0
201:   8b 45 08                mov    0x8(%ebp),%eax
204:   8b 00                   mov    (%eax),%eax           ; eax  = col
206:   09 d0                   or     %edx,%eax             ; eax |= param0
208:   66 89 01                mov    %ax,(%ecx)            ; *vidmem = col | param0
20b:   8b 45 08                mov    0x8(%ebp),%eax
20e:   8b 40 04                mov    0x4(%eax),%eax
211:   8d 50 01                lea    0x1(%eax),%edx
214:   8b 45 08                mov    0x8(%ebp),%eax
217:   89 50 04                mov    %edx,0x4(%eax)        ; pos ++
21a:   c9                      leave                        ; leave stackframe
21b:   c3                      ret                          ; return



Video::Clear()
21c:   55                      push   %ebp
21d:   89 e5                   mov    %esp,%ebp
21f:   83 ec 18                sub    $0x18,%esp              ; reserve 6 locals
222:   8b 45 08                mov    0x8(%ebp),%eax
225:   c7 40 04 00 00 00 00    movl   $0x0,0x4(%eax)          ; pos = 0
22c:   8b 45 08                mov    0x8(%ebp),%eax
22f:   c7 40 08 00 00 00 00    movl   $0x0,0x8(%eax)          ; off = 0
236:   c7 45 fc 00 00 00 00    movl   $0x0,0xfffffffc(%ebp)   ; local[fc] = 0
23d:   eb 19                   jmp    0x258                   ; while(local[fc] < 0x7cf)
				   ; {
23f:   c7 44 24 04 20 00 00    movl   $0x20,0x4(%esp)         ;    push char ' '
246:   00
247:   8b 45 08                mov    0x8(%ebp),%eax
24a:   89 04 24                mov    %eax,(%esp)             ;    push this
24d:   e8 1c ff ff ff          call   0x16e                   ;    call put
252:   8d 45 fc                lea    0xfffffffc(%ebp),%eax
255:   83 00 01                addl   $0x1,(%eax)             ;    local[fc]++
258:   81 7d fc cf 07 00 00    cmpl   $0x7cf,0xfffffffc(%ebp)
25f:   7e de                   jle    0x23f                   ; }

261:   8b 45 08                mov    0x8(%ebp),%eax
264:   c7 40 04 00 00 00 00    movl   $0x0,0x4(%eax)          ; pos = 0
26b:   8b 45 08                mov    0x8(%ebp),%eax
26e:   c7 40 08 00 00 00 00    movl   $0x0,0x8(%eax)          ; off = 0
275:   c9                      leave
276:   c3                      ret                            ; return
277:   90                      nop



void Video::put(const char* c)
278:   55                      push   %ebp
279:   89 e5                   mov    %esp,%ebp
27b:   83 ec 18                sub    $0x18,%esp            ; reserve 6 locals
27e:   8b 45 0c                mov    0xc(%ebp),%eax        ; eax = param0
281:   89 45 f8                mov    %eax,0xfffffff8(%ebp) ; local[f8] = param0
284:   8b 45 f8                mov    0xfffffff8(%ebp),%eax
287:   89 45 fc                mov    %eax,0xfffffffc(%ebp) ; local[fc] = param0

28a:   eb 1e                   jmp    0x2aa                 ; if(*param0 != 0)
28c:   8b 45 fc                mov    0xfffffffc(%ebp),%eax ; {
28f:   0f b6 00                movzbl (%eax),%eax
292:   0f be d0                movsbl %al,%edx              ;     edx = *param0
295:   8d 45 fc                lea    0xfffffffc(%ebp),%eax
298:   83 00 01                addl   $0x1,(%eax)           ;     param0 ++
29b:   89 54 24 04             mov    %edx,0x4(%esp)        ;     push edx
29f:   8b 45 08                mov    0x8(%ebp),%eax
2a2:   89 04 24                mov    %eax,(%esp)           ;     push this
2a5:   e8 c4 fe ff ff          call   0x16e                 ;     call put

2aa:   8b 45 fc                mov    0xfffffffc(%ebp),%eax
2ad:   0f b6 00                movzbl (%eax),%eax
2b0:   84 c0                   test   %al,%al
2b2:   75 d8                   jne    0x28c                 ; }
2b4:   c9                      leave
2b5:   c3                      ret                          ; return



void Video::set(unsigned int kol)
2b6:   55                      push   %ebp            ; setup stack-frame
2b7:   89 e5                   mov    %esp,%ebp
2b9:   8b 55 08                mov    0x8(%ebp),%edx  ; edx = this = &this[0]
2bc:   8b 45 0c                mov    0xc(%ebp),%eax  ; eax = param0
2bf:   89 02                   mov    %eax,(%edx)     ; col = param0
2c1:   5d                      pop    %ebp
2c2:   c3                      ret                    ; return
Your video writing code is not making past 78 characters, do some internal tests on the value of your pointers (i.e. hard write "pos + off" on screen somewhere for each character written).
That's only true for the first string. The second line actually doesn't even make it past the 76th charater, and I'm quite sure that this trend would continue for a 3rd or 4th string. Apart from that the newline character at the end of the string should get "printed" regardless of the screen position.
I would also suggest changing "if(pos == 80)" to "if(pos > 79)"... never assume things will work perfectly... that is when really nasty and annoying bugs start to pop up.
That actually means hidding a bug..

regards,
gaf
SpooK
Member
Member
Posts: 260
Joined: Sun Jun 18, 2006 7:21 pm

Post by SpooK »

gaf wrote: That's only true for the first string. The second line actually doesn't even make it past the 76th charater
Both strings are indentical, and behave indentically in his code. The starting position of the string should be the factor in determining length, not the beginning of the next column (i.e. "second line"). This comment could possibly move him further away from the real problem, not closer to it.
gaf wrote: That actually means hidding a bug...
Hard to track down a bug when memory gets garbled-up and you go triple-fault for no reason ;)
User avatar
Daedalus
Member
Member
Posts: 74
Joined: Sun Oct 16, 2005 11:00 pm
Location: Australia
Contact:

Post by Daedalus »

One thing I noticed was that you simply do a 'pos += 80' when encountering a \n.

But if you print out a string that is not 80 chars, then this wont move you down to the next line.

Maybe it would be easier to just keep track of line and column (y and x) and when it comes time to print them, work out your pos as:

Code: Select all

(y * 80) + x
You could take it a step further by making an array of each y line location as such:

Code: Select all

unsigned int y_adr[25];
void init_y (void) {
  int i;
  for(i = 0; i < 25; i++)
    y_adr[i] = i * 80;
}
// Then just use it as such:
pos = y_adr[y] + x;
User avatar
gaf
Member
Member
Posts: 349
Joined: Thu Oct 21, 2004 11:00 pm
Location: Munich, Germany

Post by gaf »

SpooK wrote:Both strings are indentical, and behave indentically in his code. The starting position of the string should be the factor in determining length, not the beginning of the next column (i.e. "second line"). This comment could possibly move him further away from the real problem, not closer to it.
In my opinion the length of the string shouldn't play any role as the actual printing is done on a per character level. The string printing procedure put(char* string) is merely a wrapper that runs through a c-string until the zero-termination is reached. Once it calls put(char c) for a character, the position in the string no longer matters, so that the output shoul be independant from the length of the string.

The only possible reason why put(char* string) might stop printing is that a zero character is reached. I thus also checked the string data itself to make sure that it's not currupted. While the string starting at 0x7dc in the disassembly seems to be intact, it is possible that it gets overwritten at run-time..
Daedalus wrote:One thing I noticed was that you simply do a 'pos += 80' when encountering a \n.

But if you print out a string that is not 80 chars, then this wont move you down to the next line.
Actually he already uses two variables to keept track of the screen position. The first variable ("off") always points to the beginning of the current culumn (0, 80, 160, ... y*80), while the second ("pos") defines the position in the current line ([0; 80[). Basically that's nothing else, but an "optimized" version of the more common x|y approach: When the end of a line is reached (or if a '\n' character is printed), the code increases y by one (off += 80) and resets x to zero (pos = 0).

SpooK and Deadalus already mentioned the two possible reasons:
- The string is somehow corrupted or gets owverwritten. One way to make sure that the string is correct is to calcluate its checksum. Just write some code that adds together the ASCII values of all the characters and then compares the result with a precalculated value (2625).
- It's also possible that put(char c) doesn't print a character under certain conditions. The actual print command of the procedure (vidmem[off + pos] = c | col) fails if off + pos exceeds the size of the screen. You can check if this is happening by changing the line, so that all characters get printed at 0|0 (vidmem[0] = c | col). What can you see in the upper left corner of the screen ?

Also try if the code works if you (temporarily) disabled '\t' and '\n' handling in put(char c)..

regards,
gaf
origin of
Member
Member
Posts: 27
Joined: Sun Jul 09, 2006 2:35 am

Post by origin of »

ok, well...without "/t" and "/n", using this kernel.cpp

Code: Select all

write << "Open Source Operative System";
write << "Inizializzazione PIC [ KO ]";
asm("sti");
Idt idt;
idt.init_8259();
asm("cli");
write.set(0x0F00);
write << "Inizializzazione PIC [ KO ]";
write << "abcdefghilmopqrstuvz;
this is the output :cry:

Image

==============================================

Doing this
- It's also possible that put(char c) doesn't print a character under certain conditions. The actual print command of the procedure (vidmem[off + pos] = c | col) fails if off + pos exceeds the size of the screen. You can check if this is happening by changing the line, so that all characters get printed at 0|0 (vidmem[0] = c | col). What can you see in the upper left corner of the screen ?
and with this kernel.cpp

Code: Select all

write << "Open Source Operative System";
write << "Inizializzazione PIC [ KO ]";
asm("sti");
Idt idt;
idt.init_8259();
asm("cli");
write.set(0x0F00);
write << "Inizializzazione PIC [ KO ]";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
write << "abcdefghilmnopqrstuvz";
i see M letter.....

============================================

I can't do this
- The string is somehow corrupted or gets owverwritten. One way to make sure that the string is correct is to calcluate its checksum. Just write some code that adds together the ASCII values of all the characters and then compares the result with a precalculated value (2625).
for the same reaseon of the topic....string of ascii wasn't visualized....

bad english :oops:
Last edited by origin of on Mon Aug 14, 2006 5:48 am, edited 1 time in total.
SpooK
Member
Member
Posts: 260
Joined: Sun Jun 18, 2006 7:21 pm

Post by SpooK »

Look for something more simple, like pointer/counter logic errors.
User avatar
Daedalus
Member
Member
Posts: 74
Joined: Sun Oct 16, 2005 11:00 pm
Location: Australia
Contact:

Post by Daedalus »

I'm having a hard time following the thread now, because on of the posts has an unbroken MASSIVE line (the abcdefghijkl.... line.)

Please break this up into several lines so we can read the thread properly.
origin of
Member
Member
Posts: 27
Joined: Sun Jul 09, 2006 2:35 am

Post by origin of »

i can't :roll:
User avatar
Daedalus
Member
Member
Posts: 74
Joined: Sun Oct 16, 2005 11:00 pm
Location: Australia
Contact:

Post by Daedalus »

Scroll all the way over to the right side of the screen, and click 'Edit' on the post in question.
Then just break the line up by putting spaces in, or new lines.
User avatar
gaf
Member
Member
Posts: 349
Joined: Thu Oct 21, 2004 11:00 pm
Location: Munich, Germany

Post by gaf »

About the screenshot: Are you sure that "write << abc.." was included in main() when you took the screenshot ? It's a bit strange that it's not printed at all. It's also interesting that the first string still only gets printed up to the K although it's now much shorter. Maybe you could replace the last few letters with something different, like "... PIC [ In ]". Is there any improvement ? It's possible the problem is specific to some characters..

About the M: I guess you mean "m" as "M" isn't included in any of the strings. The idea of the test was to exclude the possibility of a bug in the printing code itself. Maybe you could go even a little further and (temporarily) replace you put function with this code:

Code: Select all

Video:put(const char* c, char line=0)
{
    char* vidmem_char = (char*) 0xB8000;
    char  position = 0;

    while(*c != 0)
    {
        vidmem_char[(position+line*80)*2]  = *c;

        position++;
        c++;
    }
}
The code is independant of all class member varibles and should always work. If you're still having problems, it must be some issue with the string itself.

About the checksum: There's no need to print the actual number. Just compare the calculated checksum with the expected value and then print a single character to signal if they agree or not:

Code: Select all

Video:put(const char* c)
{
    int chechsum = 0;

    while(*c != 0)
    {
        checksum += *c;
        c++;
    }

    char result = (checksum == 2625) ? 'Y' : 'N';
    vidmem[0] = result | col; 
}
If you now print your origninal "Inizializzazione PIC \t\t\t\t\t\t[ KO ]\n" (and don't print any other strings after it), you should either see a Y if the string is intact, or a N if it's broken.

Could you please also post the linker-script of your kernel ?

regards,
gaf
Last edited by gaf on Wed Aug 16, 2006 9:43 am, edited 1 time in total.
User avatar
matthias
Member
Member
Posts: 158
Joined: Fri Oct 22, 2004 11:00 pm
Location: Vlaardingen, Holland
Contact:

Post by matthias »

just a small guess, make your write object static.
The source of my problems is in the source.
origin of
Member
Member
Posts: 27
Joined: Sun Jul 09, 2006 2:35 am

Post by origin of »

well...with this...

Code: Select all

write << "InizializzazionePIC[KO]";
and this

Code: Select all

int checksum = 0; 

    while(*c != 0) 
    { 
        checksum += *c; 
        c++; 
    } 

    char result = (checksum == 2053) ? 'Y' : 'N'; 
    videomem[0] = result | 0x0700 ;
 
i take an N


this is my link.ld

Code: Select all

OUTPUT_FORMAT("binary")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS
{
  . = 0x10000;
  .text           :  { *(.text .stub .text.* .gnu.linkonce.t.*)  }
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  . = DATA_SEGMENT_ALIGN(0x1000, 0x1000);
  . = ALIGN(32 / 8);
  .data           :
  {


  /*costruttori del c++*/
           __CTOR_LIST__ = .; LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) *(.ctors)   LONG(0) __CTOR_END__ = .;

        __DTOR_LIST__ = .; LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) *(.dtors) LONG(0) __DTOR_END__ = .;

    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .ctors          :
  {
    KEEP (*crtbegin.o(.ctors))
    KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .bss            :  {
    *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   . = ALIGN(32 / 8);
  }
  . = ALIGN(32 / 8);
  _end = .;
  PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
}
User avatar
gaf
Member
Member
Posts: 349
Joined: Thu Oct 21, 2004 11:00 pm
Location: Munich, Germany

Post by gaf »

It doesn't really surprise me that the checksum test fails as you're using a wrong value for the comparison ;). Just retry the test with 2273 and you should see if the strings are really broken..

a) If the code prints a Y the strings are still intact, and the problem must be somewhere in your code. I've written a very simple output-routine that does work on my kernel - if the strings are alright it will also work on yours. To locate the bug you might then extend this procedure until it satifies your requirements. It might be tedious, but I really don't see any other way as code and disassembly both look fine to me.

Code: Select all

void simple_write(char* string, char line, char offset)
{
    short* video = (short*) 0xB8000;

    while(*string != 0)
    {
        video[line*80 + offset] = *string | 0x700;

        offset++;
        string++;
    }
}
b) In case that the procedure still prints a N there must be something in your code that overwrites your strings. Just run through all of your source searching for anything that may overwrite the data (eg copying routines).

In my opinion your linker script is also quite complex for what you're actually trying to achieve. I could imagine that there's some problem with the script, that then causes the strings to get corrupted at loading time. It's for example possible that the .rodata section you declared doesn't work in combination with the binary file-format (just speculating - I always used elf). It might then get handled in some unexpected way that causes it to get corrupted.

You migth try a less complex linker-script to avoid such problmes. Here's a very basic script taken from a C++ kernel tutorial for beginners (link):

Code: Select all

OUTPUT_FORMAT("binary")
ENTRY(_start)

SECTIONS
{
    .text 0x10000 :
    {
        *(.text)
        *(.rodata*)
    }

    .data :
    {
        __CTOR_LIST__ = .;
        LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
        *(.ctors)   LONG(0)
        __CTOR_END__ = .; 

        __DTOR_LIST__ = .;
        LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
        *(.dtors) LONG(0)
        __DTOR_END__ = .; 

        *(.data)
    }

    .bss :
    {
        *(.bss*)
        *(.comment*)
    }
}
Just relace your script with that one and see what happends..

Hope that helps,
gaf
origin of
Member
Member
Posts: 27
Joined: Sun Jul 09, 2006 2:35 am

Post by origin of »

i get an N :cry:


Edit : i modify my link.ld with yours....and now write an Y

wait...revert to the old put function....if it write correctly the string....i pray for all you to lourdes :wink:
origin of
Member
Member
Posts: 27
Joined: Sun Jul 09, 2006 2:35 am

Post by origin of »

thanks
thanks thanks to all....i love you... :lol:

but now.... 8) why with the old link.ld i had the mistake ??
Post Reply