How to subtract a negative 8bit number from a 16bit one?

Par thegeps

Paragon (1082)

Portrait de thegeps

26-12-2021, 17:49

hi all, I'm doing it in a not elegant way, for sure, and maybe using too much cpu...

here's the problem:
I have on hl a coordinate in a 16bit range
and in the accumulator I have an offset that could be positive or negative.
I need often to adjust coordinates subtracting the offset value (subtracting a positive offset the coordinate decreases and subtracting a negative value the coordinate increases)

This is to determinate the respawn location in my Turrican code.
Here's how I'm doing it:

	ld	d,a                  ;a is 0 from previous code, so it is like ld d,0
	ld	hl,(map_pos)        ;actual position inside the tilemap
	ld	(restart_point),hl  ;store it
	ld	hl,(x_coord)        ;actual x coordinate
	ld	a,(dx)                ;actual x offset. dx (and dy) are used on scrolling routine to determine if we are moving left or right (or up or down) relatively to the actual tile (tiles are 4x4 chars, so dx and dy values vary from 0 to 3 and are the values you can see in Turrican videos)
	and	a
	jp	m,dx_negative_fix    ;check if dx has negative value
	ld	e,a                           ;copy dx value in e (so we have de= dx
	sbc	hl,de             ;subtract dx from x coordinate (so when respawning to actual tile we can restore also x position to starting char without messing relation between map and coordinates
set_restart_x:
	ld	(restart_x),hl    ;store obtained value to be used in case of respawn
	ld	hl,(y_coord)
	ld	a,(dy)
	and	a
	jp	m,dy_negative_fix
	ld	e,a
	sbc	hl,de
set_restart_y:
	ld	(restart_y),hl
	ret
dx_negative_fix:
	inc	hl                ;subtracting a negative value is like adding its positive value so increase hl
	inc	a                 ;using dx as counter increasing it until 0
	jp	nz,dx_negative_fix
	jp	set_restart_x
dy_negative_fix:
	inc	hl
	inc	a
	jp	nz,dy_negative_fix
	jp	set_restart_y
!login ou Inscrivez-vous pour poster

Par thegeps

Paragon (1082)

Portrait de thegeps

26-12-2021, 17:59

Oh, I forgot: before jp m,dx_negative_fix there is a jp z,set_restart_x (same for dy part)

Par theNestruo

Champion (362)

Portrait de theNestruo

26-12-2021, 18:38

If I have not misunderstood, you are trying to achieve HL (unsigned) -= A (signed), aren't you ?

	ld	hl, (unsigned16bit)
	ld	a, (signed8bit)
; bc = a
	ld	c, a
	add	a	; (sign in carry flag)
	sbc	a	; (high byte 0 or -1)
	ld	b, a
; hl -= bc
	or	a	; (clears carry flag)
	sbc	hl, bc

(note: untested code)

Par santiontanon

Paragon (1665)

Portrait de santiontanon

26-12-2021, 19:23

Exactly as theNestruo mentions. The idea is to "extend the sign" of the 8 bit number into a 16 bit register ( https://plutiedev.com/z80-extend-8bit-to-16bit ), which is exactly what the first 4 ops in the code theNestruo shared above do, and then you can just subtract/add with your other 16 bit number.

Par thegeps

Paragon (1082)

Portrait de thegeps

26-12-2021, 19:52

It is exactly what I need. I didn't know how to extend the sign. Thank you!

Par ARTRAG

Enlighted (6866)

Portrait de ARTRAG

27-12-2021, 10:23

Maybe it could be optimized like this (untested)

ld hl, (unsigned16bit)
ld a, (signed8bit)
neg ; (sign of -a in carry flag)
; bc = -a
ld c, a
sbc a ; (high byte 0 or -1)
ld b, a
; hl += bc
add hl, bc

Par theNestruo

Champion (362)

Portrait de theNestruo

27-12-2021, 13:11

ARTRAG wrote:

Maybe it could be optimized like this (untested)

I'm not sure, but I'd say that NEG subtracts A from 0, so carry flag will be always set regardless the sign of A/-A (unless A=0).

Par ARTRAG

Enlighted (6866)

Portrait de ARTRAG

27-12-2021, 16:30

ah! If this is the case my version will not work

Par Bengalack

Hero (609)

Portrait de Bengalack

27-12-2021, 17:28

I've been using variants of these, but added improvements here and there (when you find something that fits your code better): https://plutiedev.com/z80-add-8bit-to-16bit (same site as santi pointed out, but different link)

Par ARTRAG

Enlighted (6866)

Portrait de ARTRAG

27-12-2021, 18:07

Nice site! There are two options for the requested case
This is 14 bytes and 62 cycles:

    ; We negate A (since HL - A
    ; is the same as HL + -A)
    neg
    ; Now add the "upper byte"
    ; The flags are already set by NEG
    ; The JP PE is because -$80 becomes
    ; +$80 so we need to handle it too
    jp    p, Positive
    jp    pe, Positive
    dec   h
Positive:
    ; Then add the low byte
    add   a, l
    ld    l, a
    adc   a, h
    sub   l
    ld    h, a

This other is 7 bytes and 42 cycles

    ld    e, a    ; DE = A
    add   a, a
    sbc   a
    ld    d, a
    or    a       ; Clear carry
    sbc   hl, de  ; HL = HL-DE

Par thegeps

Paragon (1082)

Portrait de thegeps

27-12-2021, 19:36

Really good site, added to favorites Wink