It is currently Tue Dec 12, 2017 1:43 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 6 posts ] 
Author Message
PostPosted: Mon May 15, 2017 3:47 am 
Offline

Joined: Tue May 17, 2016 10:15 pm
Posts: 66
Hi, I am working on a GameBoy Emulator, it's my first Emulator and i have learned a lot.

So beforehand, i Don't want a copy paste answer, i want a discussion to help me understand as i am a Very Slow learner, and just looking at the answer directly doesn't yield me much,
and i also want to try to get there by myself as much as possible:)


So, now, i am having issues with DAA.
I am trying to follow a table i found here: http://www.chrisantonellis.com/files/ga ... manual.pdf
But even if i seem to do precisely as it says, it doesn't work and i get errors in the Blargg test rom.

I don't think there is a problem with the related instructions as the other roms that test those seem to pass, so it's rather confusing.

Thanks:)!


Top
 Profile  
 
PostPosted: Sun May 21, 2017 12:49 pm 
Offline

Joined: Mon Nov 10, 2008 3:09 pm
Posts: 431
There's a lot of confusion over DAA, due to it behaving subtly differently between the GameBoy and the Z80 with "illegal" input (i.e. when the accumulator and flags contain something other than the results of a valid BCD addition or subtraction). The table in the document you've linked to is incomplete, as it doesn't indicate what the results will be in those cases.

Do you understand what BCD (binary-coded decimal) arithmetic means? Basically, it means interpreting the upper and lower nybbles (a nybble is 4 bits or 1 hex digit) of a byte as two individual decimal digits, rather than the whole byte as one binary number. There are a few different ways to do BCD arithmetic, and every CPU with BCD support (even CPUs in the same family, like the GameBoy CPU and Z80) tends to do them a bit differently. The CPU-specific implementation details matter when you try to do BCD arithmetic on operands that aren't valid BCD numbers (i.e. the upper and/or lower nybble of one or both operands was greater than 0x9)

The DAA instruction adjusts the results of a binary addition or subtraction (as stored in the accumulator and flags) to retroactively turn it into a BCD addition or subtraction. It does so by adding or subtracting 6 from the result's upper nybble, lower nybble, or both. In order to work it has to know whether the last operation was an addition or a subtraction (the n flag), and whether a carry and/or a half-carry occurred (the c and h flags). Incidentally, the DAA instruction is the only thing that the n and h flags are normally ever used by, unless a program pushes the flags onto the stack and pops them into another register to explicitly inspect them.

The following is the simplest correct implementation of DAA for the GameBoy CPU. Note that it does the upper nybble adjust first to ensure that the two adjusts are independent without anything needing to be copied into a temporary variable (adding/subtracting 0x6 can carry into the upper nybble, but adding/subtracting 0x60 can't change the lower nybble)

Code:
// note: assumes a is a uint8_t and wraps from 0xff to 0
if (!n_flag) {  // after an addition, adjust if (half-)carry occurred or if result is out of bounds
  if (c_flag || a > 0x99) { a += 0x60; c_flag = 1; }
  if (h_flag || (a & 0x0f) > 0x09) { a += 0x6; }
} else {  // after a subtraction, only adjust if (half-)carry occurred
  if (c_flag) { a -= 0x60; }
  if (h_flag) { a -= 0x6; }
}
// these flags are always updated
z_flag = (a == 0); // the usual z flag
h_flag = 0; // h flag is always cleared


If you're still having problems, make sure you're setting the h and n flags correctly in your other instructions--remember that many arithmetic instructions set them, but DAA is the only instruction that uses them!


Top
 Profile  
 
PostPosted: Thu May 25, 2017 9:21 pm 
Offline

Joined: Tue May 17, 2016 10:15 pm
Posts: 66
Hmm, well my method was to just do precisely as the Table said, with a ton of If/Else, but there are some cases which just doesn't exist, which you might be talking about.

I tried the code you linked and it works.

Though i am a bit confused how.

I have read up on BCD, and i get the principle of it, but not much else.
I do understand that it uses the flags to determine what has happened and how to adjust it into a decimal equivalent if you will.

Looking at your code, the first check is if it's above 0x99 or the carry flag is set.
The table usually says both must be set in some cases.

And also, isn't it "high nibble is above 0x09" that's what i did as the table said so, which i think is equal to A > 0x90.


Top
 Profile  
 
PostPosted: Fri May 26, 2017 5:49 am 
Offline

Joined: Thu Jul 29, 2010 4:39 am
Posts: 24
zerowalker wrote:
And also, isn't it "high nibble is above 0x09" that's what i did as the table said so, which i think is equal to A > 0x90.


That would adjust the accumulator for values 91..99 as well, you would need to mask A (A & 0xF0) to use that as a comparison.


Top
 Profile  
 
PostPosted: Fri May 26, 2017 2:08 pm 
Offline

Joined: Mon Nov 10, 2008 3:09 pm
Posts: 431
zerowalker wrote:
Hmm, well my method was to just do precisely as the Table said, with a ton of If/Else, but there are some cases which just doesn't exist, which you might be talking about.

I tried the code you linked and it works.

Though i am a bit confused how.

I have read up on BCD, and i get the principle of it, but not much else.
I do understand that it uses the flags to determine what has happened and how to adjust it into a decimal equivalent if you will.

Looking at your code, the first check is if it's above 0x99 or the carry flag is set.
The table usually says both must be set in some cases.


Let's look at a couple of examples, with only one significant digit to keep things simple.

0x60 + 0x60 = 0xc0

Adding 0x60 + 0x60 doesn't produce a carry, but the result is not a valid BCD number; it needs to be adjusted to 0x120, and the carry flag needs to be newly set because 0x120 doesn't fit in a byte.

0x90 + 0x90 = 0x120 (0x20 + carry)

This time the result is a valid BCD number, but it isn't the right BCD number; it should be 0x180. The carry flag indicates that a result does need to be adjusted even if it looks like a valid BCD number.

The same logic applies to the ones digit and the half-carry flag, except that the CPU doesn't bother setting the half-carry flag after a DAA, because only DAA uses the half-carry flag and doing two DAAs in a row makes no sense.

When it comes to subtraction, as long as the operands were two valid BCD numbers, the only time a hex digit > 9 can appear in the result is if a borrow occurred. Think about it: if you subtract 0-9 from 0-9, the result can't be more than 9. So for subtraction the value in A doesn't matter, only the flags (note that this only applies to the GameBoy CPU. The Z80 does DAA after subtraction a bit differently)

Quote:
And also, isn't it "high nibble is above 0x09" that's what i did as the table said so, which i think is equal to A > 0x90.


A > 0x90 includes 0x91 to 0x99, which are perfectly good BCD numbers. On the other hand, if A is between 0x9a and 0x9f, then the high nybble will be greater than 9 after the lower nybble has been adjusted. Comparing A to 0x99 is equivalent to adjusting the lower nybble first and then comparing A to 0x9f. However, if you adjust the lower nybble first then you have to worry about 0xfa-0xff wrapping around to 0x00-0x05. You want the non-wrapped result when deciding whether to adjust the upper nybble (0xfa-0xff should get both nybbles adjusted) but you want the wrapped 8 bit result for the Z flag (Z should be set if the result of adjustment is 0x100) Doing the upper nybble first spares you the trouble of juggling different integer sizes or explicitly masking.

Also, it's not relevant to the GameBoy, but on the Z80, comparing with 0x99 before adjusting either nybble is the only way to nail all the edge cases of subtraction of non-valid BCD operands (some online documents and Z80 emulators describe or implement excessively complicated tests which work out to be arithmetically equivalent to "A > 0x99")


Top
 Profile  
 
PostPosted: Sun May 28, 2017 12:10 am 
Offline

Joined: Tue May 17, 2016 10:15 pm
Posts: 66
Great explanation.

Confusing, but i think i get a better grasp at it.
I seem to at least be able to remember what to do so that's something;P

Much appreciated:)

Now i need to figure out what's wrong with me timer as some timing tests fails which i find inconsistent.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group