/*
 * lseek — 32-bit file position via ESTEX MOVE_FP ($15).
 *
 *   ESTEX MOVE_FP: A=handle, B=whence (0=SET, 1=CUR, 2=END),
 *                  HL=offset high16, IX=offset low16
 *                  → HL:IX = new absolute position, CF=err
 *
 * SDCC __sdcccall(1) on z80 for `long lseek(int fd, long offset, int whence)`:
 *   - fd     → HL (1st 16-bit arg in register)
 *   - offset → stack as two 16-bit words (low first, then high)
 *   - whence → stack (top after offset)
 *   - 32-bit return: DE = low16, HL = high16
 *   - caller-pops (caller-side `pop af; pop af; pop af` after the call)
 *
 * IX is saved (caller frame pointer).
 */

#include <unistd.h>

long lseek(int fd, long offset, int whence) __naked
{
    (void)fd; (void)offset; (void)whence;
    __asm
        push  ix                    ; save caller-side IX
        ;; Layout after push:
        ;;   SP+0..1 = saved IX
        ;;   SP+2..3 = ret addr
        ;;   SP+4..5 = offset low16
        ;;   SP+6..7 = offset high16
        ;;   SP+8..9 = whence

        ld    a, l                  ; A = fd low byte (was in HL)

        ;; Walk through 5 consecutive stack bytes via HL — cheaper than
        ;; IY-indexed because `ld r,(hl); inc hl` (2B/13T per byte) beats
        ;; `ld r, n(iy)` (3B/19T) for sequential reads.
        ld    hl, #4
        add   hl, sp                ; HL → offset_low

        ld    e, (hl)
        inc   hl
        ld    d, (hl)               ; DE = offset_low
        push  de
        pop   ix                    ; IX = offset_low
        inc   hl
        ld    e, (hl)
        inc   hl
        ld    d, (hl)               ; DE = offset_high
        inc   hl
        ld    b, (hl)               ; B  = whence (low byte)
        ex    de, hl                ; HL = offset_high (ESTEX wants it here)

        ld    c, #0x15              ; ESTEX MOVE_FP
        rst   #0x10
        jr    c, _lseek_err

        ;; Returns HL:IX = new position.  Convert to SDCC long return (DE:HL).
        push  ix
        pop   de                    ; DE = low16 (was IX)
        ;; HL already has high16
        pop   ix                    ; restore caller-side IX
        ret

    _lseek_err:
        call  __errno_set
        ld    hl, #0xFFFF
        ld    de, #0xFFFF           ; long -1
        pop   ix
        ret
    __endasm;
}
