Jump to content



0

Multiply sign error tested on real hardware?


11 replies to this topic

#1 raisinland OFFLINE  

raisinland

    Combat Commando

  • 5 posts

Posted Tue Jan 17, 2012 7:04 PM

The Lynx documentation states that hardware multiply has a sign error (0x8000 is treated as positive, 0x0 as negative). Has this ever been verified on a real Lynx? I'm working on the Mess driver, and with the sign bug implemented Stun Runner doesn't work. Handy initializes the math registers to 0xffff and updates the sign flag on writes to the low byte of the math registers. This fixes the problem but I'd like to know if this is actually what the lynx does.

I'm considering getting a flash cartridge and learning enough Lynx programming to test this but i was hoping somebody here would have the answer.

#2 LX.NET OFFLINE  

LX.NET

    Star Raider

  • 78 posts

Posted Wed Jan 18, 2012 4:11 PM

View Postraisinland, on Tue Jan 17, 2012 7:04 PM, said:

The Lynx documentation states that hardware multiply has a sign error (0x8000 is treated as positive, 0x0 as negative). Has this ever been verified on a real Lynx? I'm working on the Mess driver, and with the sign bug implemented Stun Runner doesn't work. Handy initializes the math registers to 0xffff and updates the sign flag on writes to the low byte of the math registers. This fixes the problem but I'd like to know if this is actually what the lynx does.

I'm considering getting a flash cartridge and learning enough Lynx programming to test this but i was hoping somebody here would have the answer.

Hi Raisinland,

I ran into this exact same problem. STUN Runner was crashing badly due to memory corruption. When I finally tracked it down, I found in the readme of an old version of Handy (somewhere around 0.70) that Keith Wilkins had the same bug and fixed it like you said. I applied a similar fix.
The Epyx documentation states that:

"Signed Multiplies
When signed multiply is enabled, the hardware will convert the number provided by the CPU into a positive number and save the sign of the original number. The resultant positive number is placed in the same Suzy location as the original number and therefore the original number is lost. At the end of a multiply, the signs of the original numbers are examined and if required, the multiply result is converted to a negative number.
The conversion that is performed on the CPU provided starting numbers is done when the upper byte is sent by the CPU. Therefore, while writing to the lower byte will set the upper byte to zero, it WILL NOT change its sign to positive. Therefore, when using signed multiply, you MUST write both bytes of a number.
"

I originally implemented it like it is described here. That didn't work right.
So, I am interested in the real behavior as well. Maybe we can team up on this.

Alex

#3 raisinland OFFLINE  

raisinland

    Combat Commando

  • 5 posts

Posted Wed Jan 18, 2012 7:27 PM

Yeah, I'll help out as much as I can. I have a lynx II for testing. Do you have a Lynx or flash cart?

#4 sage OFFLINE  

sage

    Moonsweeper

  • 391 posts
  • Location:Germany

Posted Thu Jan 19, 2012 11:53 AM

View Postraisinland, on Wed Jan 18, 2012 7:27 PM, said:

Yeah, I'll help out as much as I can. I have a lynx II for testing. Do you have a Lynx or flash cart?

its know that there are differences in the emulation of the mult/div compared to real hardware.
But if you do mult/div as you should do it, the behaviour is o.k.

PS: for this simple test a serial cable is more useful...

#5 raisinland OFFLINE  

raisinland

    Combat Commando

  • 5 posts

Posted Fri Jan 20, 2012 12:01 PM

I got it working the same way Handy does, but Mess focuses on hardware accuracy so I'd like to have the multiply behavior be as close to the actual hardware as possible. Where is the best place to get a BLL loader cartridge (I'm in the US)?

#6 LX.NET OFFLINE  

LX.NET

    Star Raider

  • 78 posts

Posted Sun Jan 22, 2012 6:30 AM

View Postraisinland, on Wed Jan 18, 2012 7:27 PM, said:

Yeah, I'll help out as much as I can. I have a lynx II for testing. Do you have a Lynx or flash cart?

I've got a Lynx and Lynxman Flashcart.
I'll see if I can whip up a small test. I am thinking about this:

At startup print values:
  • registers AB and CD, plus EFGH and JKLM for multiply (with accumulation)
  • registers EFGH and NP, plus ABCD and JKLM
At the writing of each of the registers to a value the same values need to be examined (upper byte, given the earlier bit of documentation)
We also will need some test case (particularly edge cases) where the behavior (sign conversion, e.g.) is tested.

Any ideas for test cases, anyone?

Some more documentation:
Epyx documentation: "Therefore, if you only have 8 bits in a particular number, there is no need to write the upper byte to '0'. (except for signed multiplies)"
"In divide, the remainder will have 2 possible errors, depending on its actual value. No point in explaining the errors here, just don't use it. Thank You VTI."
Keith Wilkins: "Divide is ALWAYS unsigned arithmetic..."

#7 raisinland OFFLINE  

raisinland

    Combat Commando

  • 5 posts

Posted Mon Jan 23, 2012 3:02 AM

Would these make sense?

Test zero treated as negative:
write zero to D,C registers (should set sign flag)
Write 1 to D register (sign flag should remain set due to setting low byte only)
Write 2 to B, 0 to A
result should be negative according to the documentation.

test 0x8000 treated as positive:
multiply 0x8000 and 0xfffe, result should be negative.

#8 sage OFFLINE  

sage

    Moonsweeper

  • 391 posts
  • Location:Germany

Posted Mon Jan 23, 2012 2:33 PM

View Postraisinland, on Fri Jan 20, 2012 12:01 PM, said:

I got it working the same way Handy does, but Mess focuses on hardware accuracy so I'd like to have the multiply behavior be as close to the actual hardware as possible. Where is the best place to get a BLL loader cartridge (I'm in the US)?

Well, you will never get the timing right. But anyway.

There is actually really a loader cartrigde, which is eh quiet useless, as the loader is embedded in several games. Alpine Games, Champ Rally, , Simis, Lynx Reloaded and maybe some of the newer homebrews.

#9 karri OFFLINE  

karri

    Stargunner

  • 1,049 posts
  • Location:Espoo, Finland

Posted Fri Jan 27, 2012 3:17 AM

MegaPak has an uploader also. If you want to create BLL-compatible binaries you need to specify a config file while compiling. There is a stock config file called lynx-bll.cfg that will produce a *.o target. I believe the syntax is cl65 -C lynx-bll.cfg

--
Karri

#10 BillyHW OFFLINE  

BillyHW

    Dragonstomper

  • 737 posts

Posted Fri Jan 27, 2012 12:03 PM

I'm so glad to hear that MESS is working on a Lynx emulator. I can't wait to try it when it's finished.

#11 LX.NET OFFLINE  

LX.NET

    Star Raider

  • 78 posts

Posted Sun Mar 18, 2012 4:26 PM

View Postraisinland, on Mon Jan 23, 2012 3:02 AM, said:

Would these make sense?

Test zero treated as negative:
write zero to D,C registers (should set sign flag)
Write 1 to D register (sign flag should remain set due to setting low byte only)
Write 2 to B, 0 to A
result should be negative according to the documentation.

test 0x8000 treated as positive:
multiply 0x8000 and 0xfffe, result should be negative.

It certainly took a while, but the new post on safe use of Suzy's math inspired to follow up on this.

I wrote a little Lynx program (my first!) to test the bugs in the Suzy math hardware. Raisinland suggested two tests. Here are the results.

My main code (brute forced, and awaiting some elegant refactoring) for the second test looks like this:

unsigned char a;
unsigned char b;
unsigned char c;
unsigned char d;
unsigned char e;
unsigned char f;
unsigned char g;
unsigned char h;
unsigned char stringTemp[4];
tgi_install(&lynxtgi);
tgi_init();
CLI();
while (tgi_busy())
  ;
tgi_clear();
a = (unsigned char)PEEK(0xFC55);
b = (unsigned char)PEEK(0xFC54);
c = (unsigned char)PEEK(0xFC53);
d = (unsigned char)PEEK(0xFC52);
e = (unsigned char)PEEK(0xFC63);
f = (unsigned char)PEEK(0xFC62);
g = (unsigned char)PEEK(0xFC61);
h = (unsigned char)PEEK(0xFC60);
tgi_setcolor(COLOR_GREEN);
tgi_outtextxy(0, 0, "Peek A,B,C,D,E,F,G,H");
itoa(a, stringTemp, 16);
tgi_outtextxy(0, 10, stringTemp);
itoa(b, stringTemp, 16);
tgi_outtextxy(0, 20, stringTemp);
itoa(c, stringTemp, 16);
tgi_outtextxy(0, 30, stringTemp);
itoa(d, stringTemp, 16);
tgi_outtextxy(0, 40, stringTemp);
itoa(e, stringTemp, 16);
tgi_outtextxy(0, 50, stringTemp);
itoa(f, stringTemp, 16);
tgi_outtextxy(0, 60, stringTemp);
itoa(g, stringTemp, 16);
tgi_outtextxy(0, 70, stringTemp);
itoa(h, stringTemp, 16);
tgi_outtextxy(0, 80, stringTemp);
tgi_updatedisplay();
__asm__("wait: lda $fcb0");
__asm__(" beq wait");
tgi_clear();
POKE(0xFC92, 0x80);
POKE(0xFC52, 0xfe); // D
POKE(0xFC53, 0xff); // C
POKE(0xFC54, 0x00); // B
POKE(0xFC55, 0x80); // A
__asm__("notready: bit $fc92");
__asm__("  bmi notready");
a = (unsigned char)PEEK(0xFC55);
b = (unsigned char)PEEK(0xFC54);
c = (unsigned char)PEEK(0xFC53);
d = (unsigned char)PEEK(0xFC52);
e = (unsigned char)PEEK(0xFC63);
f = (unsigned char)PEEK(0xFC62);
g = (unsigned char)PEEK(0xFC61);
h = (unsigned char)PEEK(0xFC60);
tgi_setcolor(COLOR_GREEN);
tgi_outtextxy(0, 0, "Peek A,B,C,D,E,F,G,H");
itoa(a, stringTemp, 16);
tgi_outtextxy(0, 10, stringTemp);
itoa(b, stringTemp, 16);
tgi_outtextxy(0, 20, stringTemp);
itoa(c, stringTemp, 16);
tgi_outtextxy(0, 30, stringTemp);
itoa(d, stringTemp, 16);
tgi_outtextxy(0, 40, stringTemp);
itoa(e, stringTemp, 16);
tgi_outtextxy(0, 50, stringTemp);
itoa(f, stringTemp, 16);
tgi_outtextxy(0, 60, stringTemp);
itoa(g, stringTemp, 16);
tgi_outtextxy(0, 70, stringTemp);
itoa(h, stringTemp, 16);
tgi_outtextxy(0, 80, stringTemp);
tgi_updatedisplay();
while (1)
  ;

When I run this code the output is:

Peek at a,b,c,d,e,f,g,h
80
0
0
2
FF
FF
0
0

That means that the result is -0x10000 (two's complement of 0xFFFF0000, which is exactly 0x8000 times -2 (0xFFFE), but only if 0x8000 is considered positive.
You can also see that the value for CD (0XFFFE) has been converted to 2. AB at 0x8000 has not changed, which would be true for positive values, a second proof of the bug.

Conclusion: the bug where 0x8000 is considered positive seems to be validated on actual hardware.

For the first test (0x0000 is considered negative) was implemented almost the same.
POKE(0xFC92, 0x80);
POKE(0xFC52, 0x00); // D
POKE(0xFC53, 0x00); // C
POKE(0xFC52, 0x01); // D
POKE(0xFC54, 0x02); // B
POKE(0xFC55, 0x00); // A

The results there are
0
2
0
1
FF
FF
FF
FE

So, again there is a bug confirmed.
This also proves another important point: the sign is saved when the high (most significant) byte of AB and CD has been written (this means A and C).

I also ran a quick test to see if writing to either the low or high byte of the AB and CD pairs would trigger the changing of the values to be positive (for signed math)
POKE(0xFC52, 0x00); // D
POKE(0xFC53, 0xFF); // C -> set to negative value before signed math
POKE(0xFC54, 0x00); // B

POKE(0xFC92, 0x80); // Turn on signed math

POKE(0xFC52, 0xFF); // D Non-zero value

The result where that CD when read is it has the value of 0x00FF.
Adding an additional POKE there
POKE(0xFC53, 0xFF); // C
the result changed to CD being 0x0001, as described by the documentation.

Strangely enough, the same behavior that we see in CD does not happen in AB. I've changed the values there as well, but when I read back the value for AB after writing to A (which also triggers the actual calculation) it has weird values. At least, I haven't been able to figure out what the logic is.
This table shows some of the values I tried for AB. The results in EFGH where always correct, BTW.


FFFE -> FF00

FF30 -> FF10

FF12 -> FF12

FF24 -> FF11

FE24 -> FE25

FAFA -> FA64

Can anyone make sense of this?

To summarize the results for now:
  • 0x0000 is treated as negative in signed math
  • 0x8000 is treated as positive in signed math
  • when changing AB and CD writing to LSB (low byte, B and D) of will set high byte (A and C) to zero
  • for signed math the value of CD is immediately changed to a positive value when writing to C
  • undefined behavior (for now) when writing to A for AB value during signed math.


#12 raisinland OFFLINE  

raisinland

    Combat Commando

  • 5 posts

Posted Sat Mar 24, 2012 7:16 PM

Cool! Thanks for checking this out. It would seem this means just doing the sign update on low-byte writes isn't the proper way to fix Stun Runners.
Interesting about the AB values, I don't see an obvious pattern either.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users