HexToStr

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.
Post Reply
Crazed123

HexToStr

Post by Crazed123 »

I'm trying to create a HexToStr like function that outputs into a static array of chars in order to aid in debugging the kernel, but can't seem to get the right algorithm. I have one, but it doesn't print any numbers to the screen and is probably slower than it should be. I looked through the board and couldn't find any of the older posts on this subject.

Code: Select all

procedure IntToError(lw: longword; base: byte);
var j,k: longword;
begin
  k:= 0;
  if base = 16 then
  begin
    strError[k]:= '$';
    k:= k + 1;
  end;
  for j:= 7 downto 0 do
    strError[(7 + k) - j]:= HexChars[lw div Power(base,j)];
  strError[(7 + k) + 1]:= #00;
end;
AR

Re:HexToStr

Post by AR »

My Pascal is lacking but I can show you a bit of C code:

Code: Select all

   const char *Hex = "0123456789ABCDEF";
   char i;
   char j = 0;
   
   for(i=28 ; i>-1 ; i-=4)
   {
       result[j++] = Hex[(Num >> i) & 0xF];
   }
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:HexToStr

Post by Brendan »

Hi,

My Pascal is worse than my C (which is worse than my assembly), but how about:

Code: Select all

const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

char *IntToError( char *string, long value, char base )
{
    if( value / base != 0 ) string = IntToError( string, value / base, base );

    *string = digits[ value % base ];
    return string++;
}
This should work for all bases from 1 to 36....


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Kemp

Re:HexToStr

Post by Kemp »

That's quite a nice function Brendan. Beats my function by loads, though mine was deliberately quite crippled and useless outside of where it was designed to be used.

It takes a value in AL and gives you two ascii characters in AX:

Code: Select all

bin2hex:
        MOV AH, AL
        AND AX, 0000111111110000b
        SHR AL, 4
        ADD AX, 3030h
        RETN
Short, but not very useful. Also note that A-F aren't actually done properly, an if or a lookup table could solve that easily enough but I'd just be patching something that won't be used in the end.
Crazed123

Re:HexToStr

Post by Crazed123 »

I've translated Brendan's function into Pascal and adapted it for my global strError variable. Here's how it came out:

Code: Select all

unit SysUtils;

interface

procedure IntToError(lw: longword; base: byte);

const
 HexChars: array [0..15] of Char = '123456789ABCDEF';

var
 strError: array [0..255] of Char;

implementation

var
 btITECounter: Byte;

procedure IntToError(lw: longword; base: byte);
begin
btITECounter:= btITECounter + 1;
if lw div base <> 0 then
 IntToError(lw div base,base);
strError[btITECounter]:= HexChars[lw mod base];
btITECounter:= btITECounter - 1;
end;

end.
The difference between the variable declarations is that other units can't see btITECounter. It's just used to count the recursion level and index into strError accordingly.

Only issue is that it still doesn't print, which is making me doubt my printing code. Oy.
kerim

Re:HexToStr

Post by kerim »

in that case, test out the printing procedure, or post it here so all of us could help you.
Crazed123

Re:HexToStr

Post by Crazed123 »

Funny thing is, the WriteLn() function gets called about 3 times before its use on strError, and it always works up till then.
kerim

Re:HexToStr

Post by kerim »

is it a problem for you to post the WriteLn code and the part of your code when you call the IntToError procedure ?
mystran

Re:HexToStr

Post by mystran »

Are we printing hex here?

Well.. the function I use (which isn't high-tech or anything) looks like this:

Code: Select all

#ifdef __DEBUG__
/*
 * Returns hex string of any number type with "0x" prefix.
 * Should be called with hex_num(x) macro.
 * 
 * Pointer returned is valid until next call.
 *
 * Only available when debugging is enabled. Should not be
 * used when it's not, as this is quite kludgy and slow.
 * 
 * Anyone is free to rewrite this to be more efficient.
 *
 * THIS IS NOT REENTRANT!
 *
 */
static const char __hex_numbers[] = "0123456789ABCDEF";

/* 16 characters == 8 bytes == sizeof(unsigned long long) */
static char __hex_num_buffer[] = "0123456789ABCDEF_";

char * __hex_num(const char * num, int bytes) {
    char * p;

    int i;

    p = (char *) __hex_num_buffer;
    memset(__hex_num_buffer, 0, sizeof(__hex_num_buffer));

    p[0] = '0';
    p[1] = 'x';

    for(i = 0; i < bytes; i++) {
        p[2 + (i*2)] = __hex_numbers[(num[bytes - i - 1] & 0xF0) >> 4];
        p[3 + (i*2)] = __hex_numbers[(num[bytes - i - 1] & 0x0F)];
    }

    return p;
}
#endif

For production code, you might want something a bit less kludgy.
Crazed123

Re:HexToStr

Post by Crazed123 »

[edit]Bingo! I still don't know why, but the exception happens because I enable interrupts.[/edit]

Alright, this post has the whole textconsole.pas file (renamed textconsole.c w/o actual translation, can somebody please fix that!) and equus.pas. All the variables and functions related to working the GDT and IDT are in my RTL and debugged as damn near bug-free as I've been able to make them.

Code: Select all

program Equus;

uses Multiboot,Textconsole,Basic_Exceptions;

var
 lwMagic: longword; cvar;
 pMBInfo: PMultibootInformation; cvar;

procedure RemapPICs(PIC1,PIC2: byte);
var
data1,data2: Byte;
begin
{PIC1_COMMAND_PORT = $20
 PIC1_DATA_PORT = $21}
InFromPort($21,data1);
{PIC2_COMMAND_PORT = $A0
 PIC2_DATA_PORT = $A1}
InFromPort($A1,data2);
{ICW1_INIT = $10
 ICW1_ICW4 = $01}
OutToPort($20,Byte($10 + $01));
IOWait();
OutToPort($A0,Byte($10 + $01));
IOWait();
OutToPort($21,PIC1);
IOWait();
OutToPort($A1,PIC2);
IOWait();
OutToPort($21,Byte(4));
IOWait();
OutToPort($A1,Byte(2));
IOWait();
{ICW4_8086 = $01 -- 8086 mode}
OutToPort($21,Byte($01));
IOWait();
OutToPort($A1,Byte($01));
IOWait();
OutToPort($21,data1);
OutToPort($A1,data2);
end;

begin
{Exit if the Multiboot information isn't correct.}
if (lwMagic <> MULTIBOOT_MAGIC) or (CheckMultibootFlags(pMBINfo) = false) then
 Exit;
//Clear the screen and notify the user of the correct Multiboot information.
SetTextColor(COLOR_WHITE,COLOR_BLACK);
ClearScreen();
WriteLn(PChar('Multiboot magic number and flags correct.'));

{Set up the GDT.}
SetGDTEntry(0,0,0,0,0);
//Ring 0 segments.  First Data, then Code.
SetGDTEntry(1,0,$FFFFFFFF,$92,$CF);
SetGDTEntry(2,0,$FFFFFFFF,$9A,$CF);
//Ring 1 segments.  First Data, then Code.
SetGDTEntry(3,0,$FFFFFFFF,$B2,$CF);
SetGDTEntry(4,0,$FFFFFFFF,$BA,$CF);
//Ring 2 segments.  First Data, then Code.
SetGDTEntry(5,0,$FFFFFFFF,$D2,$CF);
SetGDTEntry(6,0,$FFFFFFFF,$DA,$CF);
//Ring 3 segments.  First Data, then Code.
SetGDTEntry(7,0,$FFFFFFFF,$F2,$CF);
SetGDTEntry(8,0,$FFFFFFFF,$FA,$CF);
//Set the GDT pointer.
pGDT.wLimit:= sizeof(TGDTEntry) * 9 - 1;
pGDT.pBase:= @geGDT[0];
//Set the GDT and segment registers.
SetGDT();
SetSegmentRegisters($08,$10);
//Notify the user the GDT is set up.
WriteLn(PChar('The Global Descriptor Table is set up with code and data segments for rings 1, 2 and 3.'));

{Remap the Programmable Interrupt Controllers.  This is done for two reasons:
 1.  So normal interrupts firing from IRQ 0-7 don't trigger interrupts.
 2.  So all the IRQs from 0-F have consecutive entries in the IDT.}
RemapPICs($20,$28);
WriteLn(PChar('The Programmable Interrupt Controllers have been remapped to put IRQs $00-$0F at IDT entries $20-$28.'));

{Set up the Interrupt Descriptor Table with our basic exception handlers.}
//Zero out the IDT itself.
FillChar(ieIDT[0],0,256 * sizeof(TIDTEntry));
//Set our first and basic exception handlers.
SetExceptionHandler(0,Pointer(@DivisionByZero),$10,$8E);
SetExceptionHandler(1,Pointer(@Debug),$10,$8E);
SetExceptionHandler(2,Pointer(@NonMaskableInterrupt),$10,$8E);
SetExceptionHandler(3,Pointer(@Breakpoint),$10,$8E);
SetExceptionHandler(4,Pointer(@IntoDetectedOverflow),$10,$8E);
SetExceptionHandler(5,Pointer(@OutOfBounds),$10,$8E);
SetExceptionHandler(6,Pointer(@InvalidOpcode),$10,$8E);
SetExceptionHandler(7,Pointer(@NoCoprocessor),$10,$8E);
SetExceptionHandler(8,Pointer(@DoubleFault),$10,$8E);
SetExceptionHandler(9,Pointer(@CoprocessorSegmentOverrun),$10,$8E);
SetExceptionHandler(10,Pointer(@BadTSS),$10,$8E);
SetExceptionHandler(11,Pointer(@SegmentNotPresent),$10,$8E);
SetExceptionHandler(12,Pointer(@StackFault),$10,$8E);
SetExceptionHandler(13,Pointer(@GeneralProtectionFault),$10,$8E);
SetExceptionHandler(14,Pointer(@PageFault),$10,$8E);
SetExceptionHandler(15,Pointer(@UnknownInterrupt),$10,$8E);
SetExceptionHandler(16,Pointer(@CoprocessorFault),$10,$8E);
SetExceptionHandler(17,Pointer(@AlignmentCheck),$10,$8E);
SetExceptionHandler(18,Pointer(@MachineCheck),$10,$8E);
SetExceptionHandler(19,Pointer(@Reserved),$10,$8E);
//Set the IDT pointer.
pIDT.wLimit:= 256 * sizeof(TIDTEntry) - 1;
pIDT.pBase:= @ieIDT[0];
SetIDT();
WriteLn(PChar('The basic exceptions are installed and so is the Interrupt Descriptor Table.'));

{We have the PICs and IDT set, so we should be able to enable interrupts.}
//Warning: enabling interrupts is DANGEROUS.
EnableInterrupts();
WriteLn(PChar('Interrupts are enabled.'));

//While loop forever.
while true do
 begin
 end;
end.
kerim

Re:HexToStr

Post by kerim »

I don't see the code of textconsole.pas ???
Crazed123

Re:HexToStr

Post by Crazed123 »

OK, now it's attached as textconsole.c, but not actually in C.
Crazed123

Re:HexToStr

Post by Crazed123 »

The bug was in the iteration counter, the first iteration would have a 1 counter, therefore a 1 index into the strError array and therefore a NULL in the 0 index and the end of the string on the first character. Here's the proper code for any Pascal users that want it, gratis and libre:

Code: Select all

var
btITECounter: Byte;

procedure IntToError(lw: longword; base: byte);
begin
btITECounter:= btITECounter + 1;
if lw div base <> 0 then
IntToError(lw div base,base);
strError[btITECounter - 1]:= HexChars[lw mod base];
btITECounter:= btITECounter - 1;
end;
Major kudos to Brendan for the C original! 8)
Post Reply