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

By thegeps

Paragon (1124)

thegeps's picture

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 or register to post comments

By thegeps

Paragon (1124)

thegeps's picture

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)

By theNestruo

Champion (385)

theNestruo's picture

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)

By santiontanon

Paragon (1695)

santiontanon's picture

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.

By thegeps

Paragon (1124)

thegeps's picture

26-12-2021, 19:52

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

By ARTRAG

Enlighted (6889)

ARTRAG's picture

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

By theNestruo

Champion (385)

theNestruo's picture

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).

By ARTRAG

Enlighted (6889)

ARTRAG's picture

27-12-2021, 16:30

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

By Bengalack

Hero (660)

Bengalack's picture

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)

By ARTRAG

Enlighted (6889)

ARTRAG's picture

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

By thegeps

Paragon (1124)

thegeps's picture

27-12-2021, 19:36

Really good site, added to favorites Wink