Page 2 of 3
Posted: Wed Jan 26, 2011 11:24 am
by paul_nicholls
@BigEd and dclxvi: thanks guys, I will have a look at the links 
@everyone else: thanks all, I will see if I can come up with something that works for a number in string format to the c64 5 byte fp format...
cheers,
Paul
Posted: Wed Jan 26, 2011 6:53 pm
by BigEd
I had a thought about this. You're writing in a high level language, so you have floats and doubles already available, and these will already be in a binary form. So no accuracy is lost if we operate on numbers by doubling and halving, and so, without making any other assumptions about your programming environment, we can pick out the information we need.
Here's some awk, which you can read as pseudocode - it gives the right answer for BBC Basic and can surely be adjusted for C64. It doesn't attempt zero or negative numbers but that should be a simple adjustment.
Code: Select all
awk 'BEGIN
{
x=4*atan2(1,1); # pi
m=0; # we'll shift the mantissa in, eventually
e=129; # the exponent offset
while(x>1){ # repeatedly halve if x is large
e++; # account for the exponent
x/=2
}
while(x<1){ # repeatedly double if x is small
e--; # account for the exponent
x*=2
}
x-=1; # remove the leading 1 bit
for(i=0;i<31;i++){ #extract 31 bits of mantissa by repeated doubling
if(x>=1){
m++; # adjust the mantissa
x-=1
};
x*=2;
m*=2 # shift the mantissa to the left
}
# we're finished - x might have a small residue - we didn't try to round
printf "%s,%02x,%04x\n",x,e,m
}'
Cheers, Ed
edit: initialise e to 129. BBC Basic's representation of pi is 82490fdaa2 and this awk prints out 0.130505,82,490fdaa2
edit: hmm, will always finish by doubling, so LSB will always be zero. oops.
Posted: Wed Jan 26, 2011 7:42 pm
by paul_nicholls
I had a thought about this. You're writing in a high level language, so you have floats and doubles already available, and these will already be in a binary form. So no accuracy is lost if we operate on numbers by doubling and halving, and so, without making any other assumptions about your programming environment, we can pick out the information we need.
Here's some awk, which you can read as pseudocode - it gives the right answer for BBC Basic and can surely be adjusted for C64. It doesn't attempt zero or negative numbers but that should be a simple adjustment.
Code: Select all
awk 'BEGIN
{
x=4*atan2(1,1); # pi
m=0; # we'll shift the mantissa in, eventually
e=129; # the exponent offset
while(x>1){ # repeatedly halve if x is large
e++; # account for the exponent
x/=2
}
while(x<1){ # repeatedly double if x is small
e--; # account for the exponent
x*=2
}
x-=1; # remove the leading 1 bit
for(i=0;i<31;i++){ #extract 31 bits of mantissa by repeated doubling
if(x>=1){
m++; # adjust the mantissa
x-=1
};
x*=2;
m*=2 # shift the mantissa to the left
}
# we're finished - x might have a small residue - we didn't try to round
printf "%s,%02x,%04x\n",x,e,m
}'
Cheers, Ed
edit: initialise e to 129. BBC Basic's representation of pi is 82490fdaa2 and this awk prints out 0.130505,82,490fdaa2
edit: hmm, will always finish by doubling, so LSB will always be zero. oops.
Wow! Thanks so much Ed
This looks easy for me to convert to Pascal to use in my compiler
cheers,
Paul
Posted: Wed Jan 26, 2011 7:47 pm
by BigEd
I think the final missing LSB (and the rounding) can be fixed up after the for loop with something like
for truncation or
for rounding.
I don't think my method would work well if you had less precision in your HLL than you want in your output format: using doubles should be OK though.
Finally, I note that the comparisons against '1' might need to be strict, or loose: a detail which I didn't think hard about!
Cheers
Ed
Posted: Wed Jan 26, 2011 8:05 pm
by BigEd
I'm going to indulge myself a bit, because the same points often come up when floating point is discussed:
here's a straightforward guide,
here's Goldberg's in-depth classic article and
here's something straightforward from the python docs
(I see Oracle have rearranged all of Sun's content, so that classic article has become difficult to find. Boo.)
Posted: Wed Jan 26, 2011 9:53 pm
by paul_nicholls
I think the final missing LSB (and the rounding) can be fixed up after the for loop with something like
for truncation or
for rounding.
I don't think my method would work well if you had less precision in your HLL than you want in your output format: using doubles should be OK though.
Finally, I note that the comparisons against '1' might need to be strict, or loose: a detail which I didn't think hard about!
Cheers
Ed
Well, in my HLL I have access to single/double/extended so I should be ok when doing the conversions
Thanks again mate, much appreciated!
cheers,
Paul
Posted: Wed Jan 26, 2011 11:17 pm
by paul_nicholls
@BigEd:
Well, I have converted your conversion code over to Pascal
Code: Select all
program C64_Math_Test;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TFloatFunc = (
ffNone,
ffRound,
ffTrunc
);
function atan2(y : extended ; x : extended) : extended ; assembler;
asm
fld [y];
fld [x];
fpatan
end;
procedure FloatToC64Float(var x: Extended; var e,m: Integer; FloatFunc: TFloatFunc);
var
i: Integer;
begin
m := 0; // we'll shift the mantissa in, eventually
e := 129; // the exponent offset
while (x > 1) do
begin // repeatedly halve if x is large
e := e + 1; // account for the exponent
x := x / 2;
end;
while (x < 1) do
begin // repeatedly double if x is small
e := e - 1; // account for the exponent
x := x * 2;
end;
x := x - 1; // remove the leading 1 bit
for i := 0 to 30 do
begin //extract 31 bits of mantissa by repeated doubling
if (x >= 1) then
begin
m := m + 1; // adjust the mantissa
x := x - 1;
end;
x := x * 2;
m := m * 2; // shift the mantissa to the left
end;
// we're finished - x might have a small residue - we didn't try to round
case FloatFunc of
ffRound : if (x > 1) then m := m + 1;
ffTrunc : if (2*x > 1) then m := m + 1;
else
end;
end;
var
x: Extended;
e,m: Integer;
begin
x := 4*atan2(1,1); // pi
FloatToC64Float(x,e,m,ffNone);
WriteLn(LowerCase(Format('%f,%02x,%04x',[x,e,m])));
ReadLn;
end.
And I get almost the same answer as you, which is very good I guess
The only difference is in how much is left in x...
I should now try outputting some number converted using this routine as hex bytes to insert into some 6510 assembly code to see if it works using the c64 floating point routines
Wish me luck!
PS. if/when I get something going I might post my little compiler so you all can have a play
cheers,
Paul
Re: Convert numbers to C64 floating point compact form?
Posted: Wed Jan 26, 2011 11:57 pm
by BigDumbDinosaur
9 significant digits, not 10 or 7, BDD might be thinking of single precision floating point (32bit fp) which has 7 (decimal) digit precision.
Yeah, I was dozing off at the wheel when I posted that. Damned chemo...
Re: Convert numbers to C64 floating point compact form?
Posted: Thu Jan 27, 2011 12:00 am
by paul_nicholls
9 significant digits, not 10 or 7, BDD might be thinking of single precision floating point (32bit fp) which has 7 (decimal) digit precision.
Yeah, I was dozing off at the wheel when I posted that. Damned chemo...
No need to apologise
Chemo? bummer...I hope you get well soon
cheers,
Paul
Posted: Wed Jun 08, 2011 2:09 am
by paul_nicholls
Hi all,
after a large hiatus, I am going to work on my C64 compiler again, and I think I have now come up with an answer to my problem...
I was googling again, and found this page:
http://www.softwolves.com/arkiv/cbm-hackers/7/7617.html
Somehow I had missed it previously (D'OH!)
I have translated the C code to Pascal, and I am going to try it out in my compiler:
Code: Select all
procedure FloatToC64Float(num: Double);
// converted from original code found here:
// http://www.softwolves.com/arkiv/cbm-hackers/7/7617.html
//
var
i,sign,bit,byte_val,flag,count,ei: Integer;
digit: array[0..4] of Byte;
a: Double;
begin
if Abs(num) < 0.000001 then WriteLn('Oh come on. Zero! 00 00 00 00 00');
WriteLn(Format('Original number = %.10f',[num]));
if (num < 0) then
begin
sign := 128;
num := -num;
end
else
sign := 0;
a := 1;
for i := 0 to 126 - 1 do // a = 2^126
begin
a := a * 2;
end;
byte_val := 0;
flag := 0;
count := 0;
ei := 0;
i := 126;
while (i >= -128) and (byte_val < 4) do
begin
if num >= a then
begin
bit := 1;
if flag = 0 then
ei := i;
flag := 1;
end
else
bit := 0;
if flag = 1 then
begin
digit[byte_val] := digit[byte_val] * 2 + bit;
Inc(count);
if count > 7 then
begin
count := 0;
Inc(byte_val);
end;
num := num - a * bit;
end;
a := a / 2;
Dec(i);
end;
digit[0] := (digit[0] - 128) or sign;
Inc(ei,129);
WriteLn(Format('C64 float = $%.2x $%.2x $%.2x $%.2x $%.2x',
[ei,digit[0],digit[1],digit[2],digit[3]]));
end;
When I passed in the value for PI calculated into the routine like I did in the other code I tried back in January:
I get the same output, so I have a good feeling about it
cheers,
Paul
Posted: Thu Jun 09, 2011 11:05 pm
by paul_nicholls
Just so you know, I have now created two routines to translate floating point numbers to C64 5-byte format (memory), and 6-byte format (FP accumulator registers) after reading this page:
ftp://n2dvm.com/Commodore/Commie-CDs/Ka ... se/197.htm
If anyone is interested, here is my code - feel free to use it if you wish
Code: Select all
type
PC64MemFloat = ^TC64MemFloat;
TC64MemFloat = packed record
Exponent: Byte;
Mantissa: array[0..3] of Byte;
end;
PC64RegFloat = ^TC64RegFloat;
TC64RegFloat = packed record
Exponent: Byte;
Mantissa: array[0..3] of Byte;
Sign: Byte;
end;
Code: Select all
procedure FloatToC64Float(num: Double; out aC64Float: TC64MemFloat);
// converts a floating point number to 5-byte memory FP representation: exponent (1), mantissa (4)
//ftp://n2dvm.com/Commodore/Commie-CDs/Kazez%20FREE-CD/c64-knowledge-base/197.htm
var
ExpCount: Integer;
SignBit: Integer;
Index: Integer;
begin
Write(Format('%.10f = ',[num]));
// save sign bit
SignBit := 0;
if num < 0 then
begin
SignBit := 128;
num := -num;
end;
if Abs(num) < 0.000001 then
begin
aC64Float.Exponent := 0;
aC64Float.Mantissa[0] := 0;
aC64Float.Mantissa[1] := 0;
aC64Float.Mantissa[2] := 0;
aC64Float.Mantissa[3] := 0;
C64FloatToStr(aC64Float);
Exit;
end;
// calculate exponent byte
ExpCount := 0;
if num < 1 then
while num < 1 do
begin
Dec(ExpCount);
num := num * 2;
end
else
if num >= 2 then
while num >= 2 do
begin
Inc(ExpCount);
num := num / 2;
end;
aC64Float.Exponent := 129 + ExpCount;
num := num / 2; // 'un-normalize' it for forther processing (immediate mantissa)
// calculate mantissa digits
for Index := 0 to 3 do
begin
num := num * 256;
aC64Float.Mantissa[Index] := Trunc(num);
num := Frac(num);
end;
// round last mantissa digit when required
if num > 0.5 then Inc(aC64Float.Mantissa[3]);
// include sign bit in first mantissa digit
aC64Float.Mantissa[0] := (aC64Float.Mantissa[0] and $7F) or SignBit;
C64FloatToStr(aC64Float);
end;
Code: Select all
procedure FloatToC64Float(num: Double; out aC64Float: TC64RegFloat);
// converts a floating point number to 6-byte register FP representation: exponent (1), mantissa (4), separate sign (1)
//ftp://n2dvm.com/Commodore/Commie-CDs/Kazez%20FREE-CD/c64-knowledge-base/197.htm
var
ExpCount: Integer;
SignBit: Integer;
Index: Integer;
begin
Write(Format('%.10f = ',[num]));
// save sign bit
SignBit := 0;
if num < 0 then
begin
SignBit := 128;
num := -num;
end;
if Abs(num) < 0.000001 then
begin
aC64Float.Exponent := 0;
aC64Float.Mantissa[0] := 0;
aC64Float.Mantissa[1] := 0;
aC64Float.Mantissa[2] := 0;
aC64Float.Mantissa[3] := 0;
aC64Float.Sign := 0;
C64FloatToStr(aC64Float);
Exit;
end;
// calculate exponent byte
ExpCount := 0;
if num < 1 then
while num < 1 do
begin
Dec(ExpCount);
num := num * 2;
end
else
if num >= 2 then
while num >= 2 do
begin
Inc(ExpCount);
num := num / 2;
end;
aC64Float.Exponent := 129 + ExpCount;
num := num / 2; // 'un-normalize' it for forther processing (immediate mantissa)
// calculate mantissa digits
for Index := 0 to 3 do
begin
num := num * 256;
aC64Float.Mantissa[Index] := Trunc(num);
num := Frac(num);
end;
// round last mantissa digit when required
if num > 0.5 then Inc(aC64Float.Mantissa[3]);
// include sign bit in sign part
aC64Float.Mantissa[4] := SignBit;
C64FloatToStr(aC64Float);
end;
Code: Select all
function C64FloatToStr(var aC64Float: TC64MemFloat): String;
begin
//output C64 mem floating point as hex (Exponent, Mantissa)
Result := Format('$%.2x $%.2x $%.2x $%.2x $%.2x (mem FP)',
[aC64Float.Exponent,
aC64Float.Mantissa[0],
aC64Float.Mantissa[1],
aC64Float.Mantissa[2],
aC64Float.Mantissa[3]]);
end;
Code: Select all
function C64FloatToStr(var aC64Float: TC64RegFloat): String;
begin
//output C64 reg floating point as hex (Exponent, Mantissa, Sign)
Result := Format('$%.2x $%.2x $%.2x $%.2x $%.2x $%.2x (reg FP)',
[aC64Float.Exponent,
aC64Float.Mantissa[0],
aC64Float.Mantissa[1],
aC64Float.Mantissa[2],
aC64Float.Mantissa[3],
aC64Float.Sign]);
end;
cheers,
Paul
Posted: Sun Nov 27, 2011 10:21 am
by jgharston
Posted: Sun Nov 27, 2011 7:39 pm
by paul_nicholls
Thanks for the link jgharston
It might come in handy...not sure if I will use it yet though
cheers,
Paul
Re: Convert numbers to C64 floating point compact form?
Posted: Fri Jun 12, 2015 3:48 pm
by Hobbit1972
Sorry for necro-bumping, but there is a different approach using the standard IEEE floating point numbers available on most systems/high language libraries:
Code: Select all
Single: 4 byte 32seee eeee 24emmm mmmm 16mmmm mmmm 08mmmm mmmm
Double: 8 byte 64seee eeee 56eeee mmmm 48mmmm mmmm 40mmmm mmmm 32mmmm mmmm 24mmmm mmmm 16mmmm mmmm 08mmmm mmmm
C64Flp: 5 byte 16mmmm mmmm 24mmmm mmmm 32mmmm mmmm 48smmm mmmm 08 eeee eeee
s: sign bit
m: mantissa
e: exponent
Caveat: byte order C64 is reversed compared to IEEE!
If you have a string containing the floating-point number, you can convert it to CBM-floats using standard library functions, here in freepascal:
Code: Select all
function Str2C64Flp(floatstr : string) : string;ยด{result will contain 5-byte C64-float}
var sr : record {those records for juggling the bytes}
case integer of
1 : (s : single);
2 : (b : array[0..3] of byte);
3 : (l : longint);
4 : (w1: word; w2: word);
end;
dr : record
case integer of
1 : (d : double);
2 : (b : array[0..7] of byte);
3 : (c : comp);
4 : (l1,l2 : longint);
end;
w : word;
vorzeichen : byte; {sign}
exponent : byte;
mantisse : longint;
begin
dr.d := fval(floatstr,w); {one might want to catch some conversion errors here}
sr.s := dr.d;
vorzeichen := sr.b[3] shr 7;
sr.l := sr.l shl 1;
exponent := sr.b[3]+2;
mantisse := (((dr.l2 shl 12) or (dr.l1 shr 20)) shr 1) + (vorzeichen *$80000000);
sr.l := mantisse;
Str2C64Flp := chr(exponent)+chr(sr.b[3])+chr(sr.b[2])+chr(sr.b[1])+chr(sr.b[0]);
end;
(Stumbled over this old thread to see if someone already has done a 65xx-Pascal-compiler, mine is a stub for years now

)
Re: Convert numbers to C64 floating point compact form?
Posted: Mon Jun 15, 2015 10:38 am
by paul_nicholls
@Hobbit1972 thanks for the info

I haven't worked on my compiler for ages now, but if I do this could come in handy...not that it is the memory compact form (4 bytes) I was really after though.
cheers,
Paul