/* * pop_pace.c — фиксированный логический кадр по лучу. Зачем именно так, * а не по счётчику кадровых прерываний — в шапке pop_pace.h (там же * условие точности и замеры). * * Модуль НЕ банковый (EXTRA_SRCS): pop_beam_sample зовётся из банков * рисования, а базовый код в W1 доступен им прямым call — трамплин на * каждую выборку съел бы весь смысл. */ #include #include #include #include "pop_pace.h" uint8_t pop_speed_mode; /* crt0 зануляет _DATA → NORMAL */ volatile uint8_t pop_frame_tick; /* Бит 5 порта 0xFE на прошлой выборке: 0 или 0x20. На виду у asm-тела. */ uint8_t pop_beam_prev; static uint8_t pace_anchor; /* тик на начало текущего логического кадра */ static uint8_t pace_ok; /* 0 — луч не работает, откат на gfx_wait_vsync */ /* ---- выборка луча --------------------------------------------------- * * in a,(0xFE) кладёт A на старший байт адреса (выбор ряда клавиатуры) — * биты 5/7 от него не зависят, так же читает и gfx_wait_vsync. * Быстрый путь (состояние не изменилось) — 40 T + вызов. */ void pop_beam_sample(void) __preserves_regs(b, c, d, e) __naked { __asm in a, (#0xFE) and a, #0x20 ld hl, #_pop_beam_prev cp a, (hl) ret Z ; состояние то же — выходим сразу ld (hl), a or a, a ret NZ ; 0→1: вошли в бланк, это не граница ld hl, #_pop_frame_tick inc (hl) ; 1→0: vpos = 0, начало кадра ret __endasm; } uint8_t pop_pace_arm(void) { uint16_t tries; uint8_t t0; /* Взводит cbl_mode (_cbl_port_ref) и заодно ставит нас на фронт. */ gfx_wait_vsync(); /* Затравка beam_prev без ложного фронта: что бы луч ни показал сейчас, * первая выборка либо совпадёт (ранний выход), либо запишет 0x20 и * уйдёт по ветке «вошли в бланк». */ pop_beam_prev = 0; pop_beam_sample(); t0 = pop_frame_tick; for (tries = 0; tries < 20000; tries++) { pop_beam_sample(); if ((uint8_t)(pop_frame_tick - t0) >= 2) { pace_anchor = pop_frame_tick; pace_ok = 1; return 1; } } pace_ok = 0; /* бит не шевелится — прежнее поведение */ return 0; } void pop_wait_edge(void) { uint8_t t; if (!pace_ok) { gfx_wait_vsync(); return; } t = pop_frame_tick; do { pop_beam_sample(); kbd_raw_poll(); /* KBD-1: опрос обязан быть плотным */ } while (pop_frame_tick == t); } void pop_pace_end(uint8_t n) { if (!pace_ok) { /* откат: как было — добрать ожиданиями */ while (--n) gfx_wait_vsync(); return; } while ((uint8_t)(pop_frame_tick - pace_anchor) < n) pop_wait_edge(); pace_anchor = pop_frame_tick; /* якорь ПО ФАКТУ — фаза не копится */ } uint8_t pop_pace_n(uint8_t fight) { if (pop_speed_mode == POP_SPEED_NORMAL) return fight ? 5 : 4; if (pop_speed_mode == POP_SPEED_FAST) return fight ? 4 : 3; return 3; /* FASTEST: и вне боя, и в бою */ }