3

My understanding is that in order to evaluate X - Y - borrow, we can either perfrom X - (Y + borrow) or (X - Y) - borrow. Examples in the textbooks depicts the former approach. In that case, what will happen is the subtrahend (Y) is FFH and borrow is set (to 1)?

I tried the following code in GNUSim8085 (http://gnusim8085.srid.ca/) which shows a wrong answer (CY flag is not set). Is it an implementation issue of GNUSim8085 or this actually happens in physical Intel 8085 chip due to the overflow?

STC
MVI A,0FFH
SBI 0FFH
HLT

Whereas, the next code gives the correct answer (A = FFH, CY=1) as expected.

STC
MVI A,00H
SBI 00H
hlt
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
codeR
  • 165
  • 1
  • 1
  • 13

2 Answers2

3

My understanding is that in order to evaluate X - Y - borrow, we can either perfrom X - (Y + borrow) or (X - Y) - borrow.

Typically, real CPUs do not perform two operations, but they use an ALU that uses "full adder-subtractors". These circuits perform a subtraction with borrow in one operation.

Typically, there is one circuit for each bit (e.g. 8 circuits for an 8-bit subtraction).

The circuit calculating the carry flag also calculates the upper bit (bit 7) of the result; it takes the upper bits (bit 7) of both numbers and an internal flag (which can be 0 or 1) as input. It has no other information!

We calculate 90h-80h: The result is 10h or 0Fh (depending on the former state of the carry flag) and carry is clear. This means that the circuit will calculate 0 as upper bit of the result and clear carry if the upper bits of both input numbers are 1 and the "internal flag" has a certain state (0 or 1).

We calculate 80h-90h: The result is 0F0h or 0EFh and carry is set. This means that the circuit will calculate 1 as upper bit of the result and set carry if the upper bits of both input numbers are 1 and the "internal flag" has the other state.

Calculating 0FFh-0FFh, the upper bits of both input values also set to 1. This means: Depending on the "internal flag", the circuit will either set the upper bit of the output to 0 and clear carry, or set the upper bit of the output to 1 and set carry.

So a result of A=0xFFh and CY=0 is not possible.

Is it an implementation issue of GNUSim8085 or this actually happens in physical Intel 8085 chip due to the overflow?

There are CPUs that do not use a "full-adder" in the lowest bit to calculate a subtraction with borrow.

However, I doubt that the 8085 is one of those CPUs.

Having a look at the source code of GNUSim8085, you'll see comments like this (this one is taken from the function that performs SBI):

  /* I'm not sure abt the new code
   * Old code:

Reading such comments, I think that there are still some bugs in that emulator.

EDIT

If someone having an actual 8085 kit ...

A result of CY=0 would definitely be a bug!

Why? Because a subtraction of a 32-bit constant from a 32-bit number would lead to the wrong result:

1000FF00h - 0FF01h = 0FFFFFFFh  (if CY=1 in your example)
1000FF00h - 0FF01h = 1000FFFFh  (if CY=0 in your example)

If Intel really produced some (buggy) 8085 ICs resulting in CY=0, Intel would have corrected this bug in ICs produced later!

So you will definitely find real 8085 ICs resulting in CY=1, while it is pure speculation that there might also be (buggy) ICs resulting in CY=0.

By the way: Using the 8085 simulator "sim8085", your example results in CY=1.

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
  • Thanks for the explanation. But I'm still curious about the actual implementation of `SBB' or `SBI' in 8085. If someone having an actual 8085 kit would be kind enough to try it out, let me know your findings :) – codeR Jun 17 '21 at 12:38
2

x86 was designed for 8080 asm source to be mostly 1:1 translatable to 8086), so it's likely that 8080 / 8085 work the same way as 8086.

In x86 sbb propagates a borrow the way you'd expect mathematically, in a way that's useful for extended precision. e.g. FF - FF - 1 does have a borrow output (CF=1), just like working by hand in decimal 9 - 9 with an incoming borrow of 1.

Really X - (Y + borrow) and (X - Y) - borrow are equivalent, you just need to avoid truncating Y+borrow to 8-bit (or save the carry-out from it) if you do it that way. In both cases, you need to set the borrow output if either operation wraps.

(Also, if 8080 / 8085 didn't work that way, it sounds like a bug that would make it unusable for subtracting uint32_t for example without branching to special-case that.)


So (unless 8085 had a hardware bug or insane design), that's a simulator bug: I'd guess it needs to use a wider intermediate type, or check for wrapping on both the A -= src and A -= carry steps. (Or check for wrapping in the src + carry step if it does that first: that means the correct sum is 0x100, larger than any possible 8-bit value.)

C doesn't expose add-with-carry or sub-with-borrow operations, and it's very easy to get it wrong for the general case of carry in and out. (And hard to write efficiently compared to asm.)


Fun fact: a recent 6502 question about a possibly-buggy emulator was I think running into a similar emulator bug: 6502 assembly: carry result in 16bit subtraction. One answer even had the branchy work-around which seemed to be necessary on their broken emulator. (6502 doesn't have sub-without-borrow, and it treats its carry flag as a not-borrow. So to do a normal subtract, you have to SEC (set carry) then SBC. Unlike 8080 / 8085 and x86 where the carry flag is set where there is a borrow output.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • (Martin's explanation in terms of a full-adder ([wikipedia](https://en.wikipedia.org/wiki/Adder_(electronics)#Full_adder)) makes more sense for CPU internals than my attempt to describe it in terms of two separate operations; that's just how you might emulate it in a language like C without ADC / SBB.) – Peter Cordes Jun 16 '21 at 13:35