Author Topic: How to reset the carry flag - SOLVED  (Read 6805 times)

0 Members and 1 Guest are viewing this topic.

Offline endangermice

  • Supporter
  • CPC6128
  • *
  • Posts: 235
  • Country: gb
    • endangermice
  • Liked: 56
  • Likes Given: 14
How to reset the carry flag - SOLVED
« on: 15:30, 10 July 12 »
Hi Guys,

In my new and much improved sprite routine, I'm having a problem with Subtracting &4000 from an overflown HL. The code gets to a state where HL ends up being &10000 so actually overflows to &0000 with the carry bit set. I then call a routine with CALL C, routine which executes the following code:

routine
LD BC, &4000
SBC HL, BC
RET

The problem is that the result in HL is &BFFF not &C000 so it's one out. Can anyone explain why this is? I tested exactly the same routine at the beginning of my code and it returns &C000 as I would expect so something must be affecting the result.  Is the carry bit doing something that I am not aware of and if so is there a way to set it to zero so the calculation works correctly?


Cheers,

Damien.
« Last Edit: 15:50, 10 July 12 by endangermice »
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

Offline arnoldemu

  • Supporter
  • 6128 Plus
  • *
  • Posts: 5.336
  • Country: gb
    • Unofficial Amstrad WWW Resource
  • Liked: 2278
  • Likes Given: 3478
Re: Subtracted (with SBC) result not correct
« Reply #1 on: 15:40, 10 July 12 »
Hi Guys,

In my new and much improved sprite routine, I'm having a problem with Subtracting &4000 from an overflown HL. The code gets to a state where HL ends up being &10000 so actually overflows to &0000 with the carry bit set. I then call a routine with CALL C, routine which executes the following code:

routine
LD BC, &4000
SBC HL, BC
RET

The problem is that the result in HL is &BFFF not &C000 so it's one out. Can anyone explain why this is? I tested exactly the same routine at the beginning of my code and it returns &C000 as I would expect so something must be affecting the result.  Is the carry bit doing something that I am not aware of and if so is there a way to set it to zero so the calculation works correctly?


Cheers,

Damien.


do you want:

ld bc,&4000
or a
sbc hl,bc
??

or a  resets carry btw.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Offline endangermice

  • Supporter
  • CPC6128
  • *
  • Posts: 235
  • Country: gb
    • endangermice
  • Liked: 56
  • Likes Given: 14
Re: How to reset the carry flag
« Reply #2 on: 15:45, 10 July 12 »
Kev


You're a star that's exactly what I was after. After searching the web i found that in SBC the carry bit is treated as one so it will give me the wrong result since it is taken into account so my next question was going to be how can i reset the carry bit which you have answered perfectly!


Thanks again, it's a godsend having guys like you on this forum makes the learning process so much easier. I'll post my new routine once I have it properly working! I think you'll agree it's somewhat better than my original one!
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

Offline MaV

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.103
  • Country: at
  • Ius summum saepe summa est malitia.
  • Liked: 400
  • Likes Given: 828
Re: How to reset the carry flag
« Reply #3 on: 17:01, 10 July 12 »
After searching the web i found that in SBC the carry bit is treated as one so it will give me the wrong result since it is taken into account so my next question was going to be how can i reset the carry bit which you have answered perfectly!
OR A
AND A

both clear the carry flag. In example source code you can find either of them for that purpose. Don't let that irritate you.

XOR A  clears the carry flag and sets register A to zero. It's unlikely that you'll need both at the same time in your code, but good to know.


SBC means subtract with carry. Unfortunately, for 16 bit operations only SBC is available, 8 bit operations have both SUB and SBC. That is strange because the add instruction is complete with ADC and ADD for both 8 bit and 16 bit.


BTW, to clear the carry flag I've also seen this combination:
SCF ;; set carry flag
CCF ;; complement carry flag

That works, but is not necessary. ;)
Black Mesa Transit Announcement System:
"Work safe, work smart. Your future depends on it."

Offline Axelay

  • 6128 Plus
  • ******
  • Posts: 587
  • Country: au
  • Liked: 385
  • Likes Given: 88
Re: How to reset the carry flag - SOLVED
« Reply #4 on: 17:09, 10 July 12 »
If you are only calling 'routine' on carry, an option might be to count on it in the equation and just use:


LD BC,&3fff
SBC HL,BC


with no need to clear carry.


But if you are using the routine for instances where carry is not known, maybe an option would be:


LD BC,&c000
ADD HL,BC

Offline ralferoo

  • Supporter
  • 6128 Plus
  • *
  • Posts: 970
  • Country: gb
  • Liked: 583
  • Likes Given: 222
Re: How to reset the carry flag
« Reply #5 on: 18:24, 10 July 12 »
You're a star that's exactly what I was after. After searching the web i found that in SBC the carry bit is treated as one so it will give me the wrong result since it is taken into account so my next question was going to be how can i reset the carry bit which you have answered perfectly!
I'm not entirely sure what you mean by that, but it's important that you understand exactly what SBC does, as from what you've written, it sounds like you've been reading about the 6502... ;)

On Z80:
Code: [Select]
   SBC HL,DE  ; HL = HL - DE - CF
On 6502:
Code: [Select]
   SBC #10 ; A = A - #10 + CF - 1 (note how carry is inverted)
So, there's a fundamental difference. Z80 is more correct IMHO as it's "subtract with carry", but 6502 is easier to implement in logic.

Also, as MaV said, there are a bunch of options to clear the carry flag. Get to know and love these - AND, OR, XOR all clear the carry flag. Chances are you've probably done one of these just before anyway, and you might be able to make other assumptions about your entry conditions.

As Axelay said, you should also consider ADD instead of SBC if you really just want to subtract without carry. Anywhere you use an immediate subtract (i.e. a literal value), use ADD and the negative value.

Also, there are other tricks you can use the carry flag. My favourite is:
Code: [Select]
   SBC A,A ; A=00 if carry clear, FF if carry set
This can be a nice way of for instance pulling a bitmap out of a sprite (once you have 00/FF in A, you can AND with the current pixel colour mask and OR for remaining pixels).

I frequently also use it for conditional ring buffers where I want constant execution time, e.g. this code is part of my tape decode logic:
Code: [Select]
   INC C         ; increase counter
   LD (HL),C    ; store counter
    RL B           ; get next bit out of shift register
   SBC A,A
   ADD A,L
   LD L,A         ; decrement L if bit was set

Offline endangermice

  • Supporter
  • CPC6128
  • *
  • Posts: 235
  • Country: gb
    • endangermice
  • Liked: 56
  • Likes Given: 14
Re: How to reset the carry flag - SOLVED
« Reply #6 on: 19:21, 10 July 12 »
Sorry I wasn't very clear. My understanding of the effect of carry was from one of the many Z80 instruction sites (unfortunately I can't remember which one off hand). It basically said that if the carry flag is set will also be subtracted from register in question, so exactly as you have written:


SBC HL,DE  ; HL = HL - DE - CF


Which is why the instruction is called Subtract with Carry. I'm using it because it operates nicely on 16bit numbers which solves my addressing issues.


Really interesting that there are multiple ways to clear the flag and I'm not at all surprised this is the case. I'm increasingly appreciating that the Z80 is a sophisticated beast with many instructions that can do all sorts of useful things. I feel that I'm still scratching the surface and I hope no one minds that I'm asking a lot of questions at the moment - I'm just trying to figure things out!


Thanks again for all your answers, as always they have helped immensely!




For all the latest Starquake remake news check out my website - www.endangermice.co.uk

Offline endangermice

  • Supporter
  • CPC6128
  • *
  • Posts: 235
  • Country: gb
    • endangermice
  • Liked: 56
  • Likes Given: 14
Re: How to reset the carry flag - SOLVED
« Reply #7 on: 19:26, 10 July 12 »
And here is my leaner meaner Sprite machine


Code: [Select]

; Faster Sprite routine, assumes sprites are fixed width and height
; In this case 8 x 8 (mode 0) but can be easily modified
; Makes no use of Push in the main loop


ORG &4000


; set screen mode and clear the screen
LD A, 0
CALL &BC0E
CALL &BC14


LD BC, 499
LD DE, 0
LD HL, 0


CALL DrawMaze
RET


DrawMaze
   PUSH BC

   PUSH HL
   PUSH DE


   ; Call sprite routine
   LD BC, Sprite1
   CALL DrawSprite


   POP DE
   LD BC, &8
   EX HL, DE
   ADD HL, BC
   EX HL, DE


   POP HL


   LD A, E ; Load the Y-Coord into accumilator
   LD B, A
   LD A, 160
   CP B ; Have we reached 160?
   CALL Z, MazeNextRow


   POP BC
   LD A, B
   OR C
   DEC BC


   JR NZ, DrawMaze
   
   RET


MazeNextRow
   LD DE, 0 ; Reset XCoord
   LD BC, &8
   ADD HL, BC   
   RET

; Entry
; DE - XCoordinate
; HL - YCoordinate
; BC - Pointer to SpriteData
DrawSprite
   PUSH BC
   CALL GetScrAddr
   POP BC
   EX DE, HL
   LD H, B
   LD L, C
   LD A, 8 ; Sprite height - 8 lines in this case


DrawLoop
   LD BC, 4 ; Sprite width - 8 pixels (mode 0), 16 bit numbers represent 2 pixels each
   LDIR ; So we only copy 4 16 bit memory addresses
   EX DE, HL
   
   ; Move to next line
   LD BC, &7FC ; Takes into account sprite width
   ADD HL, BC ; So it will be &800 (row width) - pixelwidth / 2 in this case &800 - &4 = &7FC


   ; If we have overflown carry will be set indicating it's time to move to a new row
   JR C, NewRow ; JR should be faster than a call since no pushing of PC


AfterNewRow
   EX DE, HL


   DEC A
   AND A


   JR NZ, DrawLoop
RET


NewRow
   ; Do the below when we move onto a new column
   LD BC, &4000;
   OR A ; Zero the carry flag   
   SBC HL, BC


   LD BC, &50
   ADD HL, BC


   JR AfterNewRow


GetScrAddr                 
   LD A, L         ;A = Lowbyte Y
   AND %00000111      ;isolate Bit 0..2
   LD H, A         ;= y MOD 8 to H
   XOR L         ;A = Bit 3..7 of Y
   LD L, A         ;= (Y\*8 to L
   LD C, A         ;store in C
   LD B, &60      ;B = &C0\2 = Highbyte Screenstart\2
     
   ADD HL, HL      ;HL * 2
   ADD HL, HL      ;HL * 4
   ADD HL, BC      ;+ BC = Startaddress
   ADD HL, HL      ;of the raster line
     
   SRL E         ;calculate X\2, because 2 pixel per byte, Carry is X MOD 2
     
   LD C, %10101010           ;Bitmask for MODE 0
   JR NC, NSHIFT      ;-> = 0, no shift
SHIFT    LD C, %01010101           ;other bitmask for right pixel
     
NSHIFT   ADD HL, DE      ;+ HL = Screenaddress
   RET         ;done




Sprite1
DEFB &FF, &00, &FF, &00
DEFB &00, &FF, &00, &FF
DEFB &FF, &00, &FF, &00
DEFB &00, &FF, &00, &FF
DEFB &FF, &00, &FF, &00
DEFB &00, &FF, &00, &FF
DEFB &FF, &00, &FF, &00
DEFB &00, &FF, &00, &FF

I think it's a big improvement on the original though it doesn't do any masking or background restoration at all yet and cannot deal with shifted sprites - I'm hoping that someone can show me how I can calculate the shift using a lookup table without having to have two (or more depending on mode) different versions for the same frame - but hopefully it shows I'm learning something at least ;) .

This example uses an 8x8 sprite to draw the shape over the entire (non-overscan) screen. I think it's pretty fast!
« Last Edit: 19:28, 10 July 12 by endangermice »
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

Offline Executioner

  • Supporter
  • 6128 Plus
  • *
  • Posts: 783
  • Country: au
  • WinAPE Developer
    • WinAPE
  • Liked: 392
  • Likes Given: 60
Re: How to reset the carry flag - SOLVED
« Reply #8 on: 07:14, 23 July 12 »
I'll perform a couple of small optimisations on your draw loop:

1) You can convert the LDIR into 4 LDI instructions. This removes the requirement for setting up BC, and it's faster.
2) Tou don't need the AND A after the DEC A since DEC A will set the zero flag when A reaches zero.
3) When the carry flag is set the NewRow routine does HL = HL - #4000 + #50, that's the same as HL = HL + (-#4000) + #50 or HL = HL + #C050

Code: [Select]
DrawLoop
   LDI ; Sprite width - 8 pixels (mode 0), 16 bit numbers represent 2 pixels each
   LDI
    LDI
   LDI ; So we only copy 4 16 bit memory addresses
   EX DE, HL
   
   ; Move to next line
   LD BC, &7FC ; Takes into account sprite width
   ADD HL, BC ; So it will be &800 (row width) - pixelwidth / 2 in this case &800 - &4 = &7FC


   ; If we have overflown carry will be set indicating it's time to move to a new row
   JR C, NewRow ; JR should be faster than a call since no pushing of PC


AfterNewRow
   EX DE, HL

   DEC A

   JR NZ, DrawLoop
   RET


NewRow
   ; Do the below when we move onto a new character row
   LD BC, #C050
   ADD HL, BC

   JR AfterNewRow