Re: Question about yellow brick roads
- From: "Evenbit" <nbaker2328@xxxxxxxxxxx>
- Date: 31 Aug 2006 18:59:18 -0700
Frank Kotler wrote:
Dragontamer wrote:[snip]
Markus Pitha wrote:
It's not my target to see what's happened when I type in a number and see
the result on stdout. That's what computer newbies are probably interested
in but I want to understand what the little gremlins in the background are
doing while this happens.
I just fail to see what HLA does that prevents this.
True. (once you've got the file open :) So why don't the HLA fans
provide an example that *shows* how the little gremlins work, instead of
telling him "just call stdout.put"?
Well, I can do a "walk-through" of a "stdout.putu8" call to see what
Toto is barking at. But, remember: "Pay no attention to the man
behind the curtain."
stdout/putu8.hla
procedure putu8( b:byte );
This executes some CPU gremlins and calls stdout.putu32Size
stdout/putu32size.hla
procedure putu32Size( d:dword; width:int32; fill:char );
This executes some CPU gremlins and calls stdout.puti64Size
stdout/puti64size.hla
procedure puti64Size( q:qword; width:int32; fill:char );
This executes some CPU gremlins and calls fileio.puti64Size
fileio/fputi64size.hla
procedure fputi64Size( Handle:dword; q:qword; width:int32; fill:char );
This executes some more CPU gremlins and makes two calls --
conv.i64ToStr and fputs
conv/i64tostr.hla
procedure conv.i64ToStr( q:qword; width:int32; fill:char; buffer:string
);
This executes some CPU gremlins and calls conv.i64toBuf
conv/i64tobuf.hla
procedure conv.i64ToBuf( q:qword );
This executes some CPU gremlins and calls conv.u64toBuf
conv/u64tobuf.hla
procedure conv.u64ToBuf( q:qword );
This executes some CPU gremlins and sometimes calls the
internally-provided recursive routines recU64ToBuf and DivideBy10
Now, back to fputi64size.hla to the second call...
fileio/fputs.hla
procedure fputs( Handle:dword; s:string );
....where it finally makes a call to linux.write
linux/write.hla
procedure linux.write( fd:dword; var buf:var; count:linux.size_t );
Now, we finally find an "Int $80" instruction.
Phwew! <wipes forehead> Anyone else feeling exhausted yet? Sure
makes 'rocket science' or 'brain surgery' look like child's play! ;-)
If you don't have a 'good novel' to enjoy for the next few days, here
is the source code for the above:
unit StdOutput;
#include( "stdoutunit.hhf" );
/********************************************************/
/* */
/* putu64, */
/* putu32, */
/* putu16, */
/* putu8 */
/* */
/* "Wrapper" versions of the above routines that call */
/* the associate procedure with some default parameters */
/* (field width zero means print the number using the */
/* minimum field width, the fill character will be */
/* ignored). */
/* */
/********************************************************/
procedure putu8( b:byte ); @nodisplay; @noalignstack;
begin putu8;
push( eax );
movzx( b, eax );
push( eax );
pushd( 0 );
pushd( ' ' );
call( putu32Size );
pop( eax );
end putu8;
end StdOutput;
unit StdOutput;
#include( "stdoutunit.hhf" );
/***********************************************/
/* */
/* putu32size- */
/* */
/* Outputs a 32-bit unsigned integer to the */
/* standard output device. Lets the caller */
/* specify a minimum field width and a padding */
/* character (usually a space). */
/* */
/***********************************************/
procedure putu32Size( d:dword; width:int32; fill:char );
@nodisplay;
@noalignstack;
begin putu32Size;
pushd( 0 ); // Zero extend 32-bit value to 64-bits.
pushd( d );
push( width );
push( (type dword fill ));
call( puti64Size );
end putu32Size;
end StdOutput;
unit StdOutput;
#include( "stdoutunit.hhf" );
/*****************************************************/
/* */
/* puti64size- */
/* */
/* Outputs a 64-bit signed integer to the */
/* standard output device. Lets the caller */
/* specify a minimum field width and a padding */
/* character (usually a space). */
/* */
/* The width parameter specifies the minimum field */
/* width for the output. If the number requires */
/* more print positions than the specified value, */
/* puti64size will use however many are necessary. */
/* */
/* If the specified width is larger than the number */
/* of print positions, then puti64size will "pad" */
/* the output with the "fill" character (also passed */
/* as a parameter). If the width value is positive, */
/* then the numeric output will be right justified */
/* in the print field; if the width value is */
/* negative, then the number will be left justified */
/* in the print field. */
/* */
/*****************************************************/
procedure puti64Size( q:qword; width:int32; fill:char ); @nodisplay;
begin puti64Size;
ChkStdOut;
fileio.puti64Size( StdOutHandle, q, width, fill );
end puti64Size;
end StdOutput;
unit FileIOUnit;
#include( "fileiounit.hhf" )
procedure fputi64Size( Handle:dword; q:qword; width:int32; fill:char );
@nodisplay;
@noalignstack;
var
eaxSave: dword;
begin fputi64Size;
mov( eax, eaxSave ); // Save this in a known location!
/*
** Allocate storage for a string large enough
** to hold the output result.
**
** A 64-bit number requires about 20 digits, so
** allocate a minimum of 24 bytes for the string
** (we need to add one byte for the zero terminating
** byte and we need to have a multiple of four bytes).
** Also don't forget that we need to allocate an
** additional eight bytes for string data.
*/
mov( width, eax ); // Get size we need to allocate.
if( (type int32 eax) < 0 ) then // Must take absolute value because
// field widths can be negative!
neg( eax );
endif;
/*
** Just to be on the safe side, let's limit the field
** width to 1024 characters. If the user really needs
** more than this, they can print the extra characters
** themselves.
*/
if( eax > 1024 ) then
raise( ex.WidthTooBig );
endif;
add( 3, eax ); // Round size so it is an even multiple
and( $ffff_fffc, eax ); // of four bytes long.
if( eax < 32 ) then // Set size to a minimum of 32 bytes.
mov( 40, eax );
endif;
sub( eax, esp ); // Make room for the string.
/*
** HLA strings have a couple of values immediately before
** the string data. MaxStrLen is one of these values (which
** is currently in EAX). The following code allocates storage
** for these extra bytes and then stores EAX into the
** MaxStrLen field of this newly created record.
*/
sub( str.BytesBeforeStr+4, esp ); // Allocate extra bytes.
// Store the MaxStrLen field away.
mov( eax, [esp] );
/*
** Compute the base address of the string for later use.
** (The base address points at the character buffer.)
*/
lea( eax, [esp+8] );
pushd( (type dword q[4] ));
pushd( (type dword q ));
pushd( width );
pushd( (type dword fill ));
pushd( eax ); // Address of string to hold result.
call( conv.i64ToStr );
fputs( Handle, eax ); // Print the string.
mov( eaxSave, eax ); // Restore original eax value.
end fputi64Size;
end FileIOUnit;
unit ConvUnit;
#include( "conversions.hhf" );
/***********************************************************/
/* */
/* i64ToStr- */
/* */
/* This procedure converts a signed 64-bit (qword) */
/* value into a string of digits (decimal notation). */
/* */
/* "q" is the value to convert. */
/* */
/* "width" is the minimum number of character positions */
/* to use during the conversion. If the number requires */
/* more than this number of print positions, width's */
/* value is ignored, if the number requires fewer print */
/* positions, then the number is padded with the character */
/* specified by the "fill" parameter. If width is a */
/* positive value, then the number is right justified in */
/* the string. If width is a negative value, then the */
/* number is left justified in the string. */
/* */
/* "fill" contains the padding character to use if the */
/* number requires fewer print positions than specified */
/* by the "width" parameter. */
/* */
/* "buffer" is a pointer to the string that will hold */
/* the converted result. This buffer must be large */
/* enough to hold the converted data including any */
/* padding characters. */
/* */
/***********************************************************/
procedure conv.i64ToStr( q:qword; width:int32; fill:char; buffer:string
);
@nodisplay;
@noalignstack;
var
chars : byte[ 32 ]; // Holds converted result w/o padding.
begin i64ToStr;
pushad();
pushfd();
cld();
lea( edi, chars ); // Store converted digits here.
conv.i64ToBuf( q );
/*
** Compute the length of the string we've just generated.
*/
lea( edx, chars );
neg( edx );
add( edi, edx );
/*
** Determine if the result will fit into the destination buffer.
*/
mov( width, ecx );
if( (type int32 ecx) < 0 ) then // Negative implies left
justification.
neg( ecx );
endif;
mov( edx, eax );
if( edx < ecx ) then
mov( ecx, eax );
endif;
mov( buffer, edi ); // Get ptr to destination string.
/*
** If the destination string is too small, raise an exception.
*/
if( eax >= (type dword [edi+str.MaxStrLenOfs] )) then
raise( ex.StringOverflow );
endif;
mov( eax, [edi+str.lengthOfs] ); // Save new string length.
/*
** If the width value is positive, then the number must be
** right justified in the field width. Output any necessary
** leading padding characters here.
*/
if( width >= 0 ) then
if( edx < ecx ) then
mov( fill, al ); // Get the padding character.
push( ecx );
sub( edx, ecx ); // Computes # of padding chars to store.
rep.stosb();
pop( ecx );
endif;
endif;
/*
** Okay, now output the characters that make up the number.
*/
xchg( edx, ecx );
push( ecx );
lea( esi, chars ); // Pointer to our string.
rep.movsb(); // Copy the chars.
pop( ecx );
/*
** If the width value is negative, then the converted number
** must be left justified in the field width. Output any necessary
** trailing padding characters here.
*/
if( width < 0 ) then
xchg( ecx, edx ); // These were swapped, earlier.
if( edx < ecx ) then
mov( fill, al ); // Get the padding character.
push( ecx );
sub( edx, ecx ); // Computes # of padding chars to store.
rep.stosb();
pop( ecx );
endif;
endif;
/*
** Output the trailing zero terminating byte.
*/
mov( 0, al );
stosb();
popfd();
popad();
end i64ToStr;
end ConvUnit;
unit ConvUnit;
#include( "conversions.hhf" );
/***********************************************************/
/* */
/* i8ToBuf, u8ToBuf, */
/* i16ToBuf, u16ToBuf, */
/* i32ToBuf, u32ToBuf, */
/* i64ToBuf- */
/* */
/* These routines convert integers of their respective */
/* sizes to a sequence of characters. The 8, 16, and */
/* 32-bit integers are passed in al, ax, or eax */
/* (respectively). 64-bit values are passed on the stack. */
/* These routines store the resulting character sequence */
/* into the buffer pointed at by edi. These routines */
/* leave edi pointing at the first byte beyond the */
/* converted character sequence. */
/* */
/* These routines preserve eax. */
/* */
/***********************************************************/
procedure conv.i64ToBuf( q:qword ); @nodisplay; @noalignstack;
begin i64ToBuf;
push( eax );
push( edx );
mov( (type dword q), eax );
mov( (type dword q[4]), edx );
if( (type int32 edx) < 0 ) then
mov( '-', (type char [edi]));
inc( edi );
neg( edx );
neg( eax );
sbb( 0, edx );
mov( eax, (type dword q ));
mov( edx, (type dword q[4]));
endif;
pop( edx );
pop( eax );
conv.u64ToBuf( q );
end i64ToBuf;
end ConvUnit;
unit ConvUnit;
#include( "conversions.hhf" );
/***********************************************************/
/* */
/* i8ToBuf, u8ToBuf, */
/* i16ToBuf, u16ToBuf, */
/* i32ToBuf, u32ToBuf, */
/* i64ToBuf- */
/* */
/* These routines convert integers of their respective */
/* sizes to a sequence of characters. The 8, 16, and */
/* 32-bit integers are passed in al, ax, or eax */
/* (respectively). 64-bit values are passed on the stack. */
/* These routines store the resulting character sequence */
/* into the buffer pointed at by edi. These routines */
/* leave edi pointing at the first byte beyond the */
/* converted character sequence. */
/* */
/* These routines preserve eax. */
/* */
/***********************************************************/
procedure conv.u64ToBuf( q:qword ); @nodisplay; @noalignstack;
// DivideBy10-
//
// Divides edx:eax by 10.
// Returns quotient in edx:eax.
// Returns remainder in ecx.
// Returns 10 in ebx.
procedure DivideBy10; @nodisplay; @noframe;
begin DivideBy10;
push( eax );
mov( edx, eax );
xor( edx, edx );
mov( 10, ebx );
div( ebx, edx:eax );
mov( eax, ecx );
pop( eax );
div( ebx, edx:eax );
xchg( ecx, edx );
ret();
end DivideBy10;
procedure recU64ToBuf; @nodisplay; @noframe;
begin recU64ToBuf;
if
(#{
test( edx, edx );
jnz true;
test( eax, eax );
jz false;
}#) then
DivideBy10();
push( ecx );
recU64ToBuf();
pop( ecx );
or( '0', cl );
mov( cl, [edi] );
inc( edi );
endif;
ret();
end recU64ToBuf;
begin u64ToBuf;
push( eax );
push( ebx );
push( ecx );
push( edx );
mov( (type dword q), eax );
mov( (type dword q[4]), edx );
if
(#{
test( eax, eax );
jnz true;
test( edx, edx );
jz false;
}#) then
recU64ToBuf();
else
mov( '0', (type char [edi]));
inc( edi );
endif;
pop( edx );
pop( ecx );
pop( ebx );
pop( eax );
end u64ToBuf;
end ConvUnit;
unit FileIOUnit;
#include( "fileiounit.hhf" )
/*******************************************************/
/* */
/* fputs- */
/* */
/* This routine writes a string to the file */
/* specified by the file handle passed as a parameter. */
/* */
/*******************************************************/
#if( os.linux )
procedure fputs( Handle:dword; s:string ); @nodisplay;
var
bytesWritten:dword;
begin fputs;
push( eax );
push( esi );
/*
** Write the characters in the string to the specified file.
*/
mov( s, esi );
linux.write( Handle, [esi], (type linux.size_t (type str.strRec
[esi]).length) );
if( (type int32 eax) < 0 ) then // An error has occurred
if( eax = errno.enospc ) then
raise( ex.DiskFullError );
else
raise( ex.FileWriteError );
endif;
elseif( eax <> (type str.strRec [esi]).length ) then
raise( ex.FileWriteError );
endif;
pop( esi );
pop( eax );
end fputs;
#elseif( os.win32 )
procedure fputs( Handle:dword; s:string ); @nodisplay; @noalignstack;
var
bytesWritten:dword;
begin fputs;
pushad();
pushfd();
cld();
/*
** Write the characters in the string to the standard output device.
*/
mov( s, esi ); // Get the address of the string.
WriteFile
(
0,
bytesWritten,
(type str.strRec [esi]).length, // Push the length of the string.
(type byte [esi]), // Push the address of the string.
Handle
);
if( eax = 0 ) then
raise( ex.FileWriteError );
endif;
popfd();
popad();
end fputs;
#endif
end FileIOUnit;
unit LinuxUnit;
#include( "linux.hhf" )
// write - writes data via a file handle.
procedure linux.write( fd:dword; var buf:var; count:linux.size_t );
@nodisplay;
begin write;
linux.pushregs;
mov( linux.sys_write, eax );
mov( fd, ebx );
mov( buf, ecx );
mov( count, edx );
int( $80 );
linux.popregs;
end write;
end LinuxUnit;
Nathan.
.
- Prev by Date: Re: Question About HLA (Was: Question about jumps)
- Next by Date: Re: Question about jumps
- Previous by thread: Re: Question About HLA (Was: Question about jumps)
- Next by thread: Search String
- Index(es):
Relevant Pages
|