/*
 * lseek — 32-битная позиция файла через 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 = новая абсолютная позиция, CF=err
 *
 * SDCC __sdcccall(1) на z80 для `long lseek(int fd, long offset, int whence)`:
 *   - fd     — HL (первый 16-битный аргумент в регистре)
 *   - offset — на стеке двумя 16-битными словами (сначала low, потом high)
 *   - whence — на стеке (выше offset)
 *   - 32-битный возврат: DE = low16, HL = high16
 *   - стек чистит вызывающий (`pop af; pop af; pop af` после call)
 *
 * IX сохраняется (frame pointer вызывающего).
 */

#include <unistd.h>

long lseek(int fd, long offset, int whence) __naked
{
    (void)fd; (void)offset; (void)whence;
    __asm
        push  ix                    ; сохранить IX вызывающего
        ;; Раскладка после push:
        ;;   SP+0..1 = сохранённый IX
        ;;   SP+2..3 = адрес возврата
        ;;   SP+4..5 = offset low16
        ;;   SP+6..7 = offset high16
        ;;   SP+8..9 = whence

        ld    a, l                  ; A = младший байт fd (был в HL)

        ;; 5 последовательных байтов стека читаем через HL — дешевле,
        ;; чем IY-индексация: `ld r,(hl); inc hl` (2Б/13Т на байт)
        ;; против `ld r, n(iy)` (3Б/19Т) при последовательном чтении.
        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 (младший байт)
        ex    de, hl                ; HL = offset_high (ESTEX ждёт его тут)

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

        ;; Вернулось HL:IX = новая позиция.  Приводим к long-возврату
        ;; SDCC (DE:HL).
        push  ix
        pop   de                    ; DE = low16 (был IX)
        ;; HL уже содержит high16
        pop   ix                    ; восстановить IX вызывающего
        ret

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