PDP-11 32-bit Addition and Subtraction ====================================== The normal way on any CPU to do arithmetic on numbers larger than the registers is to use Add-And-Carry-type instructions to propagate overflows into higher value registers, for example: ; (r2:r3) = (r0:r1) + (r2:r3) ; eg r0=0002 r1=8000 ; r2=FFFE r3=8000 ; sum = Cy 0001 0000 add r1,r3 ; r3=8000+8000=0000 + Cy adc r2 ; r2=FFFE+Cy =FFFF add r0,r2 ; r2=0002+FFFF=0001 + Cy ; sum=1:0001:0000 However, if the high word is FFFF, and there is an overflow from the bottom words, the final overflow is lost: ; (r2:r3) = (r0:r1) + (r2:r3) ; eg r0=0001 r1=8000 ; r2=FFFF r3=8000 ; sum = Cy 0001 0000 add r1,r3 ; r3=8000+8000=0000 + Cy adc r2 ; r2=FFFF+Cy =0000 + Cy add r0,r2 ; r2=0001+0000=0001 + NoCy ; sum=0:0001:0000 So, doing FFFE8000+00028000 correctly gives the 33-bit result of 1:00010000, but the "same" sum of doing FFFF8000+00018000 incorrectly gives the 33-bit result of 0:00010000. This is fine if you are not interested in the overflow from the 32-bit addition, but if you do need it you are left with a range of numbers that you cannot add and know the correct result. Other CPUs have ADC instruction that perform the addition as well as adding the carry from the previous addition, for example: ADD low1,(low2) ; low1=low1+low2 ADC high1,(high2) ; high1=high1+high2+carry Sketching out the low level operations, I've come to this. My application that uses this code adds a 32-bit number on the stack to r3:r4, so I've written it that way. ; r3:r4 = r3:r4 + 2(sp):0(sp) ; r4=low word, 0(sp)=low word ; r3=high word, 2(sp)=high word add (sp)+,r4 bcc -----+ bcs------+ no carry | carry | from low | from low | | v | adc r3 v bcc ----+ bcs----+ | | | | hi was 0000-FFFE hi was FFFF | hi now 0001-FFFF hi now 0000 | | final answer needs CS | | | v v v add (sp)+,r3 add (sp)+,r3 add (sp)+,r3 CS=Carry out CS=Carry out hi was FFFF, is now 0000-FFFF and CC from hi1+hi2 from hi1+hi2+Cy sec ensure CS for 32-bit overflow If Carry is clear, then ADC is a null operation, not changing the dest and not changing the Carry. But, if Carry is set, then ADC changes the Carry, so the paths cannot be merged. So, I think the best I can do: .Add32 ; r3:r4 = r3:r4 + 2(sp):0(sp) ; r4=low word, 0(sp)=low word ; r3=high word, 2(sp)=high word add (sp)+,r4 ; low1=low1+low2 bcc add32f3 ; no carry --------------+ adc r3 ; hi1=hi1+carry | bcc add32f3 ; no wrap FFFF->0000 ----------+ add (sp)+,r3 ; hi1=hi1+hi2 | sec ; correct answer always carries | br add32f4 ; ----------------------------- | --+ add32f3: ; <-----------------------------+ | add (sp)+,r3 ; hi1=hi1+hi2 | ; CS=Carry out | add32f4: ; <---------------------------------+ ; r3:r4=32-bit result ; CS=Carry out (bit 33 of result) 32-bit subtraction can be done with similar code. .Sub32 ; r3:r4 = r3:r4 + 2(sp):0(sp) ; r4=low word, 0(sp)=low word ; r3=high word, 2(sp)=high word sub (sp)+,r4 ; low1=low1-low2 bcc sub32f3 ; no carry --------------+ sbc r3 ; hi1=hi1-carry | bcc sub32f3 ; no wrap 0000->FFFF ----------+ sub (sp)+,r3 ; hi1=hi1-hi2 | sec ; correct answer always carries | br sub32f4 ; ----------------------------- | --+ sub32f3: ; <-----------------------------+ | sub (sp)+,r3 ; hi1=hi1-hi2 | ; CS=Carry out | sub32f4: ; <---------------------------------+ ; r3:r4=32-bit result ; CS=Carry out (bit 33 of result)