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

Por thegeps

Paladin (1020)

Imagen del 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 sesión o register para postear comentarios

Por thegeps

Paladin (1020)

Imagen del 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)

Por theNestruo

Champion (342)

Imagen del 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)

Por santiontanon

Paragon (1633)

Imagen del 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.

Por thegeps

Paladin (1020)

Imagen del thegeps

26-12-2021, 19:52

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

Por ARTRAG

Enlighted (6828)

Imagen del 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

Por theNestruo

Champion (342)

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

Por ARTRAG

Enlighted (6828)

Imagen del ARTRAG

27-12-2021, 16:30

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

Por Bengalack

Hero (568)

Imagen del 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)

Por ARTRAG

Enlighted (6828)

Imagen del 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

Por thegeps

Paladin (1020)

Imagen del thegeps

27-12-2021, 19:36

Really good site, added to favorites Wink