PDA

Просмотр полной версии : Формат TZX



ZXMAK
10.12.2024, 05:06
Делая утилиту для дампа TZX в компилируемый asm файл в формате zasm (https://k1.spdns.de/cgi-bin/zasm.cgi) (чтобы можно было легко руками подпраивить содержимое и скомпилить обратно в TZX), я заметил одну странность в TZX блоке #19 (generalized data).

Вот пример с дампом hollywood-poker.tzx из набора тестов для TZX v1.20:


#TZX GENERALIZED, BLOCK10_DATA, 0, *, flag=$21, pause=0
.tzx-pilot-sym 0, 2168,0 ; symbol#0 for pilot
.tzx-pilot-sym 0, 667,735 ; symbol#1 for pilot
.tzx-pilot 0,256, 1,1, 0,256, 1,1, 0,256, 1,1, 0,256, 1,1, 0,256, 1,1, 0,256, 1,1, 0,256, 1,1, 0,256, 1,1, 0,256, 1,1, 0,256, 1,1, 0,256, 1,1, 0,258, 1,1 ; pilot 256 x #0, 1 x #1, 256 x #0, 1 x #1, 256 x #0, 1 x #1, 256 x #0, 1 x #1, 256 x #0, 1 x #1, 256 x #0, 1 x #1, 256 x #0, 1 x #1, 256 x #0, 1 x #1, 256 x #0, 1 x #1, 256 x #0, 1 x #1, 256 x #0, 1 x #1, 258 x #0, 1 x #1
.tzx-data-sym 0, 855,855 ; symbol#0 for data
.tzx-data-sym 0, 1710,1710 ; symbol#1 for data


А именно - в словаре импульсов для пилот-тона, последний импульс 0, что технически некорректно, т.к. такой импульс приведет к смене полярности. В эмуляторах нулевые импульсы в словаре пилот-тона просто игнорируются (стоит проверка на ноль и если она срабатывает, импульс игнорируется), поэтому все работает.

Кстати, zasm не принимает этот ноль в словаре пилот-тона при компиляции, его нужно удалить, чтобы блок скомпилировался. И, что интересно, он сам добавляет 0 в конец словаря пилот-тона.

В описании формата TZX v1.20 присутствует этот ноль в примере словаря пилот-тона (судя по данным, пример в документации TZX приведен именно из вышеуказанного блока hollywood-poker.tzx)


0x12 SYMDEF[0]: ( 0, 2168, 0 ) Pilot/sync symbol definitions
SYMDEF[1]: ( 0, 667, 735)


Однако непонятно - в чем смысл был включать нулевой импульс в словарь пилот-тона? Это ошибка, которую все эмуляторы игнорируют, т.к. она описана в примере блока #19 в самом описании формата? Или я чтото не понимаю?

В аттачменте сам tzx и два его дампа в архиве - компилируемый asm дамп с проигнорированным импульсом 0 в словаре и асм дамп с префиксом -raw, т.е. в том виде как он записан в tzx.

Chwe
10.12.2024, 11:29
"shorter waves are terminated by a zero-length pulse in the sequence." Если количество импульсов в SYMDEF меньше NPP/NPD, оно «добивается» нулями. Другими словами, все записи SYMDEF должны быть одинаковой длины, читать до нуля но не более NPP/NPD.

Lethargeek
10.12.2024, 13:20
(судя по данным, пример в документации TZX приведен именно из вышеуказанного блока hollywood-poker.tzx)
для обычной пзушной процедуры этот пример

- - - Добавлено - - -

жаль, конечно, что блок #19 появился поздно, а не с самого начала вместо других
он же все остальные блоки данных (кроме csw) покрывает, и насколько упростился бы код

ZXMAK
10.12.2024, 14:40
"shorter waves are terminated by a zero-length pulse in the sequence." Если количество импульсов в SYMDEF меньше NPP/NPD, оно «добивается» нулями. Другими словами, все записи SYMDEF должны быть одинаковой длины, читать до нуля но не более NPP/NPD.

хм, точно! Сразу не заметил, что число импульсов задается заранее для всех символов, поэтому т.к. в первом символе используется только одна длительность, а во втором две, то NPP=2 и соответственно для обоих символов число записей будет 2, но по факту для 0 символа используется только одна запись, поэтому не используемая прописывается нулем. :)


для обычной пзушной процедуры этот пример

вообщето этот пример с прерывистым пилот-тоном, прерываемым специальным импульсом... :)

Lethargeek
10.12.2024, 19:42
вообщето этот пример с прерывистым пилот-тоном, прерываемым специальным импульсом...
еще раз:

(судя по данным, пример в документации TZX

ZXMAK
13.12.2024, 17:06
а какие есть подходы к распознаванию TZX/TAP из WAV?

Нужно было перевести wav/flac в tap, утилит по linux не нашел. Пришлось набросать свой простенький вариант, получилось вот такая процедурка на C#. На входе длительности импульсов распознанные в аудиофайле:



static byte[][] DecodeZXTape(List<int> pulses)
{
const int pilot_t = 2168; // Pilot-tone pulse length
const int sync1_t = 667; // Sync 1 pulse length
const int sync2_t = 735; // Sync 2 pulse length
const int bit0_t = 855; // Bit 0 pulse length
const int bit1_t = 1710; // Bit 1 pulse length
const int pilotThreshold = 256; // Minimum pilot-tone edges
const int tolerancePilot = 3170-2168;
const int toleranceSync = 400;
const int toleranceBit = 450;

var outputBlocks = new List<byte[]>();

int index = 0;
while (index < pulses.Count)
{
// Search pilot-tone
int pilotCount = 0;
while (index < pulses.Count && Math.Abs(pulses[index] - pilot_t) <= tolerancePilot)
{
pilotCount++;
index++;
}
if (pilotCount < pilotThreshold)
{
// if pilot-tone is too short skip current pulse and continue search
index++;
continue;
}
if (index + 1 >= pulses.Count ||
Math.Abs(pulses[index] - sync1_t) > toleranceSync ||
Math.Abs(pulses[index + 1] - sync2_t) > toleranceSync)
{
// if sync pulse is incorrect, skip current pulse and start to search new pilot-tone
index++;
continue;
}
index += 2; // skip sync pulse

// Read byte stream
var byteStream = new List<byte>();
byte currentByte = 0;
byte currentMask = 0x80;
while (index + 1 < pulses.Count)
{
if (pulses[index] > (bit1_t + toleranceBit) ||
pulses[index+1] > (bit1_t + toleranceBit)) break;

var period = pulses[index] + pulses[index+1];
var isZero = Math.Abs(period - bit0_t*2) <= toleranceBit*2;
var isOne = Math.Abs(period - bit1_t*2) <= toleranceBit*2;

index += 2;

// Check for end of bit stream
if (!isOne && !isZero) break;

if (isOne) currentByte |= currentMask;
currentMask >>= 1;
if (currentMask == 0)
{
byteStream.Add(currentByte);
currentByte = 0;
currentMask = 0x80;
}
}
if (byteStream.Count > 0)
{
outputBlocks.Add(byteStream.ToArray());
byteStream.Clear();
}
}
return outputBlocks.ToArray();
}
}


Для простых tap вроде неплохо работает. Но стало интересно, какие есть алгоритмы для распознавания более сложных записей с турбоблоками и т.п. для TZX?

Chwe
13.12.2024, 20:02
Нужно было перевести wav/flac в tap, утилит по linux не нашел.

Да ну, их минимум больше двух и это не считая ультрамодных «пописушек» на всяких питонах и рубях (которые, впрочем, практически бесполезны).

Почтенный audio2tape (https://sourceforge.net/p/fuse-emulator/fuse-utils/ci/master/tree/audio2tape.cc) и не менее уважаемый maketzx (1.x source (https://github.com/mincebert/maketzx), 2.x только скомпилированное, ЕМНИП, вряд ли уже запустится). Это то, что я навскидку помню, собственно оцифровкой никогда пока не занимался, но, опять же, ЕМНИП, было и ещё что-то.

ZXMAK
13.12.2024, 20:40
maketzx под aarch64 (ARM Cortex A72) собрался, правда он старый. А для второй версии нет исходника?

morozov
15.12.2024, 00:15
Для второй версии исходников нет (как минимум официально). В мануале написано:

9. Is the source code of MakeTZX available?
We are not yet going to release the sources of version 2.xx, but the old v1.02.1 code is freely available for download at our site. Please read carefully the enclosed documentation.

Lethargeek
15.12.2024, 18:55
мутные моменты в рамсофтовском описании формата:

1) сказано, что при неулевых паузах нужно делать как минимум 1мс противоположного уровня, только потом нулевой
непонятно, эта 1мс входит в длительность паузы или нет? а если пауза всего 1мс, противоположный делать на всю?

2)в блоке #19 при ASD>2 номер символа из битового потока little или big endian? проще-то, конечно, big делать
да и вообще, существуют ли хотя бы искусственные примеры образов с кол-вом символов данных больше двух?
вот с тремя пилотными символами попадались несколько раз, но пилотные кодируются иначе
также непонятно, как обрабатывать номера символов данных больше ёмкости алфавита
(может быть. если размер алфавита не стпень двойки)

3) в том же #19 для полей ASP и ASD примечание, что нулевое значение кол-ва считается как 256
а вот для NPP и NPD такого не сказано (хотя, конечно, нулевое кол-во импульсов - бессмысленно же)

ZXMAK
15.12.2024, 22:04
да и вообще, существуют ли хотя бы искусственные примеры образов с кол-вом символов данных больше двух?

asd=2, asd=4 и asd=256 довольно часто в играх используется. Например в официальном тестовом наборе для TZX есть asd=256, см.Book-of-the-dead.tzx.

Вот тестовый файл с блоками asd=2, asd=4, asd=16 и asd=256, который делал для тестов.

С этими значениями вопросов нет. Вопрос есть - как быть когда asd=8, 32, 64 или 128? т.е. когда число бит на символ 3,5, 6 или 7. Будут использованы только задействованные биты внутри байта или нужно битовый поток обрабатывать, разделяя на N-битные слова?

Lethargeek
16.12.2024, 06:18
asd=2, asd=4 и asd=256 довольно часто в играх используется
где "довольно часто", когда блока #19 (да и самой версии 1.20) вообще почти не встречается?
https://spectrumcomputing.co.uk/forums/viewtopic.php?t=11934
https://skoolkit.ca/tapes/tzxblocks.html


Например в официальном тестовом наборе для TZX есть asd=256, см.Book-of-the-dead.tzx.
про какой "набор" ты всё время говоришь? ссылку дай
BotD находимая на сайтах - простая турба


Вот тестовый файл с блоками asd=2, asd=4, asd=16 и asd=256, который делал для тестов.
тесты ладно, но в таком виде для реальных случаев было бы невыгодно делать тызыксы
алфавит получается большой, только место зря занимает, а данных столько же
еще и потому и сомневаюсь в существовании


С этими значениями вопросов нет. Вопрос есть - как быть когда asd=8, 32, 64 или 128? т.е. когда число бит на символ 3,5, 6 или 7. Будут использованы только задействованные биты внутри байта или нужно битовый поток обрабатывать, разделяя на N-битные слова?
да уж наверно паковать вплотную, иначе смысл какой?

непонятно только, как реагировать, если определено, допустим, пять символов (0-4), а в потоке встретились трёхбитные номера 5/6/7
можно было бы просто засчитать нулевую длительность, но тогда теоретически могут быть и блоки нулевой (по времени) длительности
а это лишняя трудность при анализе на зацикленность без движения (сканить весь поток придётся до первого нормального символа)

ZXMAK
16.12.2024, 10:17
про какой "набор" ты всё время говоришь? ссылку дай

про набор от автора TZX, вот про этот:

Lethargeek
16.12.2024, 11:15
ZXMAK, а где брал? есть еще какая-то страничка где-то живая?

ну и видим, что даже автор сам осилил переделать только один пример для ASD>2
оно, конечно, запилить поддержку несложно, но смущает недосказанность в описании

ZXMAK
16.12.2024, 12:01
а где брал? есть еще какая-то страничка где-то живая?

https://www.alessandrogrussu.it/tapir/

Lethargeek
16.12.2024, 12:32
https://www.alessandrogrussu.it/tapir/
а, как-то пропустил эту ссылку; но владелец сайта не автор (никакой из версий) формата и не член рамсофта, просто итальянец тоже

Shiny
01.07.2025, 10:44
82425
Непонятно, зачем в TZX к заголовкам и файлам добавлен 1 байт со значением 128?
BlockEditor и taper ругаются.

creator
02.07.2025, 07:16
Shiny, ленточные копировщики на такое ругаются tape loading error. Защита. :) А basic грузит ровно столько, сколько надо.

Shiny
02.07.2025, 12:43
Shiny, ленточные копировщики на такое ругаются tape loading error. Защита. :) А basic грузит ровно столько, сколько надо.

Да этих файлов масса:
https://planetasinclair.blogspot.com/2025/06/general-purpose-graph-plotter-mia.html
https://planetasinclair.blogspot.com/2025/06/loader-mc-mia.html

ZXMAK
06.07.2025, 21:58
82425
Непонятно, зачем в TZX к заголовкам и файлам добавлен 1 байт со значением 128?
BlockEditor и taper ругаются.

tzx2tap нормально дизассемблит



#tzx STANDARD, BLOCK1_DATA, 0, *, flag=none, checksum=none, pause=956
defm $00004c4f414445525f4d43204c110000e310ce80 ; #0000



если удалить байт, убрать флаг checksum=none и скомпилить заново, то получаются нормальные блоки, которые читаются.

https://transfiles.ru/srz4g

Видимо какой-то артефакт кассетной распознавалки.

goodboy
06.07.2025, 23:24
Видимо какой-то артефакт кассетной распознавалки.
не артефакт, а копировщик (на реале) который сохранял в таком виде (для защиты)

- - - Добавлено - - -


ленточные копировщики на такое ругаются tape loading error. Защита.
наверно самый популярный у нас TF-COPY действительно несправлялся,
вспомнил чем мы такое копировали.

https://pic.maxiol.com/thumbs2/1751833391.3645248024.omnicopy.png (https://pic.maxiol.com/?v=1751833391.3645248024.omnicopy.png&dp=2)

Shiny
07.07.2025, 16:00
Автор блога ответил, что возможен глюк со старой кассетой. Что касается TF-COPY, то он и давился на проблемных блоках. Так я сделал защиту - поменял код копировщика Н.Родионова, который дописывал мусорный байт к записи, и прокатило - копировщик не справлялся.

cafedead
24.07.2025, 15:07
Что касается TF-COPY, то он и давился на проблемных блоках. Так я сделал защиту - поменял код копировщика Н.Родионова, который дописывал мусорный байт к записи, и прокатило - копировщик не справлялся.
Так, ну нет, давайте не будем крошить батон на TF-COPY, что он там где-то на чем-то давился. Я не понял, с чем, по-вашему, должен "справляться" или "не справляться" TF-COPY. С первоначальным чтением блока со сломанным контрольным байтом четности? Вы дописали к блоку мусорный байт. Копировщик, в отличие от программы-загрузчика, НЕ ЗНАЕТ и НЕ МОЖЕТ ЗНАТЬ размера блока, который вы в него загружаете, чтение заканчивается только по таймауту, что как раз делает копировщик всеядным. После загрузки блока копировщик считает последний прочитанный байт - байтом четности, но при этом для кастомного загрузчика копировщика он является неотъемлемой частью блока, загруженного в память, рядовым байтом блока. Если байт четности не соответствует вычисленной четности (в итоге тупо ноль должен получиться в регистре, в котором накапливается четность в процедуре загрузки, т.е., другими словами, если этот регистр != ноль), а у вас будет именно такой случай, вы же сами своими руками сломали последний байт, TF-COPY просигнализирует вам ошибкой "PARITY", причем Load будет со знаком вопроса, обратите на это внимание. Можете нажать клавишу "Y", и копировщик оставит вам файл в таком виде, дописав к нему новый байт четности. Можете его потом сохранить, а потом даже проверить, VERIFY будет сравнивать прочитанные данные с тем, что у него в памяти, и выдаст ошибку только при несоответствии. Любые мусорные данные, будь то ваш мусорный байт, или байты системы защиты, будут дотошно сохранены, и, если это часть защиты, подготовленная к этому программа-загрузчик не заметит разницы. И потом сохраненные таким образом данные будут даже скорее всего всегда КОРРЕКТНО грузиться, несмотря на ваш мусорный байт, потому что копировщик допишет за вашим мусорным байтом новый байт четности. Точно не уверен, но процентов на 99 уверен. В любом случае, программой-загрузчиком будет загружено старое количество байтов из этого блока, потому что она знает, сколько ей нужно загрузить байтов, и для контроля ошибок ею будет использован старый байт четности, остальная же часть блока будет проигнорирована, но при последующей загрузке в копировщик этого нового блока, старый байт четности будет лишь очередным байтом блока, после которого будет идти ваш мусорный байт, после которого будет идти новый байт четности, в накопительном регистре получится ноль, и опять же ошибки уже не будет. Максимально корректная реализация, к которой не может быть предъявлено никаких претензий, с моей точки зрения.

Исходя из разъяснений, не вытекает, что TF-COPY с чем-то "не справляется". Со всем справился, ну разве что вы своими же действиями потеряли возможность установить, корректно ли считался блок в копировщик, собственноручно сломав средство контроля ошибок. Ну это ваш косяк, ваша зона ответственности, а не копировщика. Сорян, но копировщик не занимается магией, телепатией и угадываниями, где в блоке находилась контрольная сумма до вашего в этот порядок вмешательства. Это вы 1) не поняли сути происходящих процессов, 2) сами сломали байт четности, ожидаемо получили ошибку, но это, по-вашему, почему-то плохо и неправильно, виноват почему-то TF-COPY, и 3) не знали, что конкретно это сообщение об ошибке носит информационный характер, что можно принять блок, нажав "Y". :v2_dizzy_facepalm: Ну спустя 30 лет узнали. -) Ой, какие 30, 38 лет. Забавно.

Как средство защиты это тоже не сработало, как видите, если это можно скопировать с помощью TF-COPY, и это будет потом корректно грузиться. Ну или я просто не понял, от чего вы защищались. От дурака?;)

Shiny
29.07.2025, 19:54
Как средство защиты это тоже не сработало, как видите, если это можно скопировать с помощью TF-COPY, и это будет потом корректно грузиться. Ну или я просто не понял, от чего вы защищались. От дурака?;)

этот способ защиты работал, безо всяких заумных рассуждений.

cafedead
31.07.2025, 01:55
этот способ защиты работал, безо всяких заумных рассуждений.
Ахахах.... Ну конечно. А потом я вернулся в прошлое, в 1987-й год, и доработал TF-COPY так, чтобы он начал принимать блоки с ошибками четности. Причем все версии. Так все и было.:v2_lol:

ZXMAK
31.07.2025, 16:57
эх... не уверен тот ли это TF-COPY который я последний раз загружал где-то 33 года назад...
Подумалось - как время летит 33 года... 100 лет назад только радио начало появляться, 33 года это треть от 100. А кажется так много...
у меня помоемому немного другой был, без опций при запуске

https://i.imgur.com/vxqWLhp.png

насчет блоков с битым crc - понимает, просто спрашивает принять блок или нет:
https://i.imgur.com/enNcx9M.png

Xela
31.07.2025, 17:43
у меня помоемому немного другой был
"тысячи их (https://zxart.ee/rus/soft/system-software/copybackup/tape-file-copy/)" (С)

cafedead
01.08.2025, 18:53
у меня помоемому немного другой был, без опций при запуске
У меня был с опциями на розовом фоне, который 06/87, там "Y" надо было нажимать. А в этой версии, что вы показываете, "A" надо.

В 90-е хотел еще свой копировщик сделать, с таким же интерфейсом, но со своим алгоритмом сжатия. Но потом забил. Время летит, да. Жесть.

Shiny
03.08.2025, 17:47
Ахахах.... Ну конечно. А потом я вернулся в прошлое, в 1987-й год, и доработал TF-COPY так, чтобы он начал принимать блоки с ошибками четности. Причем все версии. Так все и было.:v2_lol:

сколько желчи в тебе. Покопайся в TF-COPY и изучи, как он работает. Но мне что-то подсказывает, что не сможешь, мамкин аналитик.

cafedead
03.08.2025, 20:40
сколько желчи в тебе. Покопайся в TF-COPY и изучи, как он работает. Но мне что-то подсказывает, что не сможешь, мамкин аналитик.
Ну... До тебя мне далеко в этом смысле. Займись лучше своими проблемами, потому что я лишь указал на твои ошибки, а у тебя почему-то сгорела задница, хотя нет никаких причин для этого. Ну не знал, и не знал, бывает. Ни к чему так остро реагировать.

Чел, у меня опыт программирования 30+ лет, куда уж мне. Копание в чужих продуктах с целью изучения их работы - это этап развития специалистов с меньшим стажем, все этим когда-то занимались. Но если интересно, как специалист я оцениваю этот продукт как качественный и сложный, особенно учитывая время, когда он создавался, когда я был малышком, и про Спектрум ничего даже не знал. Это сейчас, с доступной каждому документацией, в эмуляторе легко все отладить и такты посчитать, но тогда это было не так просто, на конкретной платформе в доступных на тот момент топорных средах разработки это был ад. Я так попытался в GENS3 что-то делать спустя 20 лет, чуть с ума не сошел. Ну и сжатие в процессе загрузки, разжатие в процессе сохранения, все чувствительное ко времени исполнения - сам понимаешь, это сложная и комплексная работа, требующая от автора компетенций, которых на момент создания в Интернете не найдешь, от чатаГПТ не узнаешь. Это не простой человек, котел у него варил что надо. Я сам писал и алгоритмы сжатия, и кастомные лоадеры, поэтому мне не очень нравится, когда в сторону такого продукта, который я вот таким образом оцениваю, кто-то начинает что-то моросить не по делу, причем кто-то, не имеющий для этого достаточно компетенций. Это не говоря даже о том, что в итоге этот копировщик предоставил всем его пользователям. Благодарю за понимание.