"""test_pop_idx — формат дисковых индексов аудио (tools/pop_idx.py). Индекс читают ДВЕ стороны: упаковщик на хосте и загрузчик на Z80. Тест закрепляет ровно то, на что рассчитывает вторая сторона: точные смещения полей, little-endian, размер сектора и НАБОР ОТКАЗОВ — потому что битый индекс обязан выключить звук, а не увести насос в чужую память. Запуск: make test-tools (либо python3 -m unittest discover tools/tests) """ import os import struct import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import pop_idx as X def empty(): return [(0, 0, 0)] * X.SND_COUNT class SndFormat(unittest.TestCase): """PSI1: раскладка байтов, на которую смотрит Z80.""" def test_size_is_one_sector(self): self.assertEqual(len(X.snd_pack(empty(), 1)), X.SECTOR) def test_header_fields(self): d = X.snd_pack(empty(), 9) self.assertEqual(d[0:4], b"PSI1") self.assertEqual(d[4], 57) # число записей self.assertEqual(d[5], 5) # sizeof(pop_snd_ent_t) в SDCC z80 self.assertEqual(d[6], 7) # log2(128) self.assertEqual(d[7], 1) # u8 mono 10937.5 self.assertEqual(d[8], 9) # фактических страниц self.assertEqual(d[9:16], b"\0" * 7) def test_entry_is_little_endian_at_fixed_offset(self): """Смещение записи и порядок байтов — контракт с загрузчиком.""" e = empty() e[3] = (2, 0x1500, 0x0480) d = X.snd_pack(e, 9) at = X.SND_HDR + 3 * X.SND_REC self.assertEqual(d[at], 2) self.assertEqual(d[at + 1:at + 3], b"\x00\x15") # off LE self.assertEqual(d[at + 3:at + 5], b"\x80\x04") # len LE self.assertEqual(struct.unpack_from("