Tape loading error

Предыстория Будучи любителем ретро железа, приобрёл я как-то у продавца из Великобритании ZX Spectrum+. В комплекте с самим компьютером мне достались несколько а...

Как я восстанавливал данные в неизвестном формате с магнитной ленты

Время прочтения
8 мин

Просмотры 38K

Предыстория

Будучи любителем ретро железа, приобрёл я как-то у продавца из Великобритании ZX Spectrum+. В комплекте с самим компьютером мне достались несколько аудиокассет с играми (в оригинальной упаковке с инструкциями), а также программами, записанными на кассеты без особых обозначений. На удивление данные с кассет 40-летней давности хорошо читались и мне удалось загрузить почти все игры и программы с них.

Однако, на некоторых кассетах я обнаружил записи, сделанные явно не компьютером ZX Spectrum. Звучали они совершенно по-другому и, в отличие от записей с упомянутого компьютера, не начинались с короткого BASIC загрузчика, который обычно присутствует в записях всех программ и игр.

Какое-то время мне не давало это покоя — очень хотелось узнать, что скрыто в них. Если бы получилось прочитать аудио сигнал как последовательность байтов, можно было бы поискать в них символы или что-то, что указывает на происхождение сигнала. Своего рода ретро-археология.

Сейчас, когда я прошёл весь путь и смотрю на этикетки самих кассет, я улыбаюсь, потому что

ответ был прямо перед глазами всё это время

На этикетке левой кассеты — название компьютера TRS-80, и чуть ниже название производителя: «Manufactured by Radio Shack in USA»

(Если хотите сохранить интригу до конца, не заходите под спойлер)

Сравнение аудио сигналов

Первым делом оцифруем аудиозаписи. Можно послушать как это звучит:

И как обычно звучит запись с компьютера ZX Spectrum:

В обоих случаях в начале записи присутствует так называемый пилотный тон — звук одной частоты (на первой записи он очень короткий <1 сек, однако различим). Пилотный тон служит сигналом компьютеру, что необходимо подготовиться к получению данных. Как правило каждый компьютер распознает только «свой» пилотный тон по форме сигнала и его частоте.

Надо сказать о самой форме сигнала. Например, на ZX Spectrum его форма прямоугольная:

При обнаружении пилотного тона ZX Spectrum отображает чередующиеся красно-голубые полоски на бордюрной части экрана, показывая, что сигнал распознан. Пилотный тон заканчивается синхро-импульсом, который сигнализирует компьютеру о том, что следует начинать принимать данные. Он характеризуется меньшей (по сравнению с пилотным тоном и последующими данными) длительностью (см. рисунок)

После того, как синхро-импульс получен, компьютер фиксирует каждый подъём/спуск сигнала, измеряя его длительность. Если длительность меньше опредёленной границы, в память записывается бит 1, иначе 0. Биты собираются в байты и процесс повторяется до тех пор пока не будет получено N байт. Число N, как правило, берётся из заголовка загружаемого файла. Последовательность загрузки следующая:

  1. пилотный тон
  2. заголовок (фиксированной длины), содержит размер загружаемых данных (N), имя и тип файла
  3. пилотный тон
  4. сами данные

Чтобы удостовериться, что данные загружены верно, ZX Spectrum последним байтом читает так называемый байт чётности (parity byte), который вычисляется при сохранении файла операцией XOR над всеми байтами записанных данных. При чтении файла компьютер вычисляет байт чётности из полученных данных и, если результат отличается от сохранённого, выводит сообщение об ошибке «R Tape loading error». Строго говоря, компьютер может выдать это сообщение и раньше, если при чтении не может распознать импульс (пропущен или его длительность не соответствует определённым границам)

Итак, посмотрим теперь, как выглядит неизвестный сигнал:

Это пилотный тон. Форма сигнала значительно отличается, но видно что сигнал состоит из повторяющихся коротких импульсов определённой частоты. При частоте дискретизации 44100 Гц, расстояние между «пиками» примерно равно 48 сэмплов (что соответствует частоте ~918 Гц) Запомним эту цифру.

Посмотрим теперь на фрагмент с данными:

Если измерить расстояние между отдельными импульсами, окажется, что между «длинными» импульсами расстояние по-прежнему в ~48 сэмплов, а между короткими — ~24. Немного забегая вперёд, скажу, что в итоге выяснилось, «опорные» импульсы с частотой 918 Гц следуют непрерывно, от начала и до конца файла. Можно предположить, что при передаче данных, если между опорными импульсами встречается дополнительный импульс, считаем его за бит 1, иначе 0.

Что с синхро-импульсом? Посмотрим на начало данных:

Пилотный тон заканчивается и сразу начинаются данные. Чуть позже, проанализировав несколько разных аудио записей, удалось обнаружить, что первый байт данных всегда один и тот же (10100101b, A5h). Возможно, компьютер начинает считывать данные, после того как получит его.

Можно также обратить внимание на сдвиг первого опорного импульса сразу после последней 1-цы в синхробайте. Его удалось обнаружить значительно позже в процессе разработки программы для распознавания данных, когда данные в начале файла не могли стабильно считаться.

Теперь попробуем описать алгоритм, который обработает аудио файл и загрузит данные.

Загрузка данных

Сперва рассмотрим несколько допущений, чтобы не усложнять алгоритм:

  1. Будем рассматривать файлы только в формате WAV;
  2. Аудиофайл должен начинаться с пилотного тона и не должен содержать тишину в начале
  3. Исходный файл должен иметь частоту дискретизации 44100 Гц. В таком случае расстояние между опорными импульсами в 48 сэмплов уже определено и нам не нужно программно его рассчитывать;
  4. Формат сэмплов может быть любой (8/16 бит/с плавающей точкой) — так как при чтении мы можем сконвертировать его в нужный;
  5. Предполагаем, что исходный файл нормализован по амплитуде, что должно стабилизировать результат;

Алгоритм чтения будет следующий:

  1. Читаем файл в память, одновременно конвертируем формат сэмплов в 8 бит;
  2. Определяем позицию первого импульса в аудиоданных. Для этого нужно вычислить номер сэмпла с максимальной амплитудой. Для простоты посчитаем его один раз вручную. Сохраним в переменную prev_pos;
  3. Прибавляем к позиции последнего импульса 48 (pos := prev_pos + 48)
  4. Так как увеличение позиции на 48 не гарантирует, что мы попадём в позицию следующего опорного импульса (дефекты ленты, нестабильная работа лентопротяжного механизма и прочее), нужно откорректировать позицию импульса pos. Для этого возьмем небольшой отрезок данных (pos-8;pos+8) и найдем на нём максимум значения амплитуды. Позицию, соответствующую максимуму, сохраним в pos. Здесь 8 = 48/6 — экспериментально полученная константа, которая гарантирует что мы определим верный максимум и не затронем другие импульсы, которые могут идти рядом. В очень плохих случаях, когда расстояние между импульсами сильно меньше или больше 48, можно реализовать принудительный поиск импульса, но в рамках статьи я не буду описывать это в алгоритме;
  5. На предыдущем шаге также необходимо бы проверить, что опорный импульс вообще найден. То есть, если просто искать максимум, это не гарантирует что импульс в данном отрезке присутствует. В своей последней реализации программы чтения я проверяю разницу между максимальным и минимальным значением амплитуды на отрезке, и если она превышает некоторую границу, засчитываю наличие импульса. Вопрос также, что делать если опорный импульс не найден. Тут 2 варианта: либо данные закончились и далее следует тишина, либо это следует рассматривать как ошибку чтения. Однако опустим это для упрощения алгоритма;
  6. На следующем шаге нужно определить наличие импульса данных (бит 0 или 1), для этого возьмем середину отрезка (prev_pos;pos) middle_pos равную middle_pos := (prev_pos+pos)/2 и в некоторой окрестности middle_pos на отрезке (middle_pos-8;middle_pos+8) посчитаем максимум и минимум амплитуды. Если разница между ними больше 10, записываем в результат бит 1 иначе 0. 10 — константа полученная опытным путём;
  7. Сохраняем текущую позицию в prev_pos (prev_pos := pos)
  8. Повторяем начиная с шага 3, пока не прочитаем весь файл;
  9. Полученный битовый массив необходимо сохранить как набор байт. Поскольку мы не учли синхро-байт при чтении, количество битов может оказаться не кратно 8, а также неизвестно необходимое смещение в битах. В первой реализации алгоритма я не знал о существовании синхро-байта и потому просто сохранял 8 файлов с разным количеством бит смещения. Один из них содержал корректные данные. В финальном алгоритме я просто удаляю все биты до A5h, что позволяет сразу получать корректный файл на выходе

Алгоритм на Ruby, кому интересно

В качестве языка для написания программы я выбрал Ruby, т.к. бОльшую часть времени программирую на нём. Вариант не высокопроизводительный, однако задача сделать скорость чтения максимально быстрой не стоит.

# Используем gem 'wavefile'
require 'wavefile'

reader = WaveFile::Reader.new('input.wav')
samples = []
format = WaveFile::Format.new(:mono, :pcm_8, 44100)

# Читаем WAV файл, конвертируем в формат Mono, 8 bit 
# Массив samples будет состоять из байт со значениями 0-255
reader.each_buffer(10000) do |buffer|
  samples += buffer.convert(format).samples
end

# Позиция первого импульса (вместо 0)
prev_pos = 0
# Расстояние между импульсами
distance = 48
# Значение расстояния для окрестности поиска локального максимума
delta = (distance / 6).floor
# Биты будем сохранять в виде строки из "0" и "1"
bits = ""

loop do
  # Рассчитываем позицию следующего импульса
  pos = prev_pos + distance
  
  # Выходим из цикла если данные закончились 
  break if pos + delta >= samples.size

  # Корректируем позицию pos обнаружением максимума на отрезке [pos - delta;pos + delta]
  (pos - delta..pos + delta).each { |p| pos = p if samples[p] > samples[pos] }

  # Находим середину отрезка [prev_pos;pos]
  middle_pos = ((prev_pos + pos) / 2).floor

  # Берем окрестность в середине 
  sample = samples[middle_pos - delta..middle_pos + delta]

  # Определяем бит как "1" если разница между максимальным и минимальным значением на отрезке превышает 10
  bit = sample.max - sample.min > 10
  bits += bit ? "1" : "0"
end

# Определяем синхро-байт и заменяем все предшествующие биты на 256 бит нулей (согласно спецификации формата) 
bits.gsub! /^[01]*?10100101/, ("0" * 256) + "10100101"

# Сохраняем выходной файл, упаковывая биты в байты
File.write "output.cas", [bits].pack("B*")

Результат

Перепробовав несколько вариантов алгоритма и констант, мне повезло получить что-то в крайней степени интересное:

Итак, судя по символьным строкам, мы имеем программу для построения графиков. Однако в тексте программы отсутствуют ключевые слова. Все ключевые слова закодированы в виде байтов (значение каждого > 80h). Теперь нужно выяснить, какой компьютер из 80-х мог сохранять программы в таком формате.

На самом деле это очень похоже на программу на языке BASIC. Примерно в таком же формате компьютер ZX Spectrum хранит в памяти и сохраняет программы на ленту. На всякий случай я проверил ключевые слова на соответствие с таблицей. Однако результат, очевидно, оказался отрицательным.

Также я проверил ключевые слова BASIC популярных в то время компьютеров Atari, Commodore 64 и нескольких других, на которые удалось найти документацию, однако безуспешно — мои познания в разновидностях ретро-компьютеров оказались не столь широки.

Тогда я решил пойти по списку, и тут мой взгляд упал на название производителя Radio Shack и компьютера TRS-80. Именно эти названия были написаны на этикетках кассет, которые лежали у меня на столе! Я ведь не знал ранее эти названия и не был знаком с компьютером TRS-80, поэтому мне казалось, что Radio Shack это производитель аудиокассет, такой кaк BASF, Sony или TDK, a TRS-80 — длительность воспроизведения. Почему нет?

Компьютер Tandy/Radio Shack TRS-80

Очень вероятно, что рассматриваемая аудиозапись, которую я привёл в качестве примера в начале статьи, сделана на таком компьютере:

Оказалось, что данный компьютер и его разновидности (Model I/Model III/Model IV и т.д.) были очень популярны в свое время (конечно, не в России). Примечательно, что процессор, которых в них использовался — тоже Z80. По данному компьютеру в Интернете можно найти много информации. В 80-х годах информация о компьютере распространялась в журналах. На данный момент существует несколько эмуляторов компьютера под разные платформы.

Я загрузил эмулятор trs80gp и мне впервые удалось посмотреть как работал этот компьютер. Конечно, компьютер не поддерживал вывод цвета, разрешение экрана всего 128х48 точек, но существовало множество расширений и модификаций которые могли увеличивать разрешение экрана. Также существовало множество вариантов операционных систем для данного компьютера и вариантов реализации языка BASIC (который, в отличие от ZX Spectrum, в некоторых моделях даже не был «прошит» в ПЗУ и любой вариант мог загружаться с дискеты, также как и сама ОС)

Также я нашёл утилиту для конвертирования аудиозаписей в формат CAS, которых поддерживается эмуляторами, однако прочитать с их помощью записи с моих кассет по каким-то причинам не удалось.

Разобравшись с форматом файла CAS (который оказался просто побитовой копией данных с ленты, которая у меня уже имелась на руках, за исключением заголовка с наличием синхро-байта), я внес несколько изменений в свою программу и смог получить на выходе рабочий CAS файл, который заработал в эмуляторе (TRS-80 Model III):

Последний вариант утилиты для конвертации с автоматическим определением первого импульса и расстоянием между опорными импульсами я оформил в виде GEM пакета, исходный код доступен на Github.

Заключение

Пройденный путь оказался увлекательным путешествием в прошлое, и я рад что в итоге нашёл разгадку. Помимо прочего я:

  • Разобрался с форматом сохранения данных в ZX Spectrum и изучил встроенные в ПЗУ подпрограммы сохранения/чтения данных с аудиокассет
  • Познакомился с компьютером TRS-80 и его разновидностями, изучил операционную систему, посмотрел примеры программ и даже имел возможность заняться отладкой в машинных кодах (всё-таки все мнемоники Z80 мне хорошо знакомы)
  • Написал полноценную утилиту для конвертирования аудио записей в CAS формат, которая может считывать данные, нераспознаваемые «официальной» утилитой

In the first part I showed how the Sinclair ZX Spectrum stored data on tape. This second part explains what is stored, and what causes a tape loading error.

The ZX Spectrum BASIC offers a SAVE command for saving all kind of data. It can be used to save a BASIC program, variable arrays, but also arbitrary parts of memory. These files are always saved in two separate blocks. The first block is called header. It contains the file name, data type, and other meta information. The second block follows about a second later and contains the data itself.

The internal structure of each block is identical. The first byte distinguishes between header ($00) and data blocks ($FF). The final byte is a parity checksum. Everything between these two bytes is the payload.

A header block always contains a payload of 17 bytes. The first byte identifies the file type, followed by the file name (10 characters), followed by the length of the data block, and closed by two optional parameters that have different meanings depending on the file type. The length and the two parameters consume two bytes each, with the lower byte coming first because the Z80 CPU is little endian.

This is an example header block of a screenshot:

00 $00 = Header
00 03 $03 = Binary file (Code or SCREEN$)
01 53 S
02 68 h
03 72 r
04 65 e
05 64 d
06 2E .
07 7A z
08 6F o
09 6E n
10 65 e
11 001B Length: 6912 bytes ($1B00)
13 0040 Parameter 1, here: starting address ($4000)
15 0000 Parameter 2, here: unused
20 Parity

A screenshot is actually just a memory dump that starts at address $4000 (which is the starting address of the screen buffer) and is exactly 6912 bytes long (the ZX Spectrum has a resolution of 256×192 monochrome pixels plus 32×24 bytes color attributes, giving a screen buffer size of 6912 bytes).

For other file types, the two optional parameters have different meanings. For example, a BASIC program file stores the line number to start at after loading.

The final byte is the parity. It is used for error detection, and computed just by XOR-ing all the bytes that have been read. The result must be $00, otherwise a «R Tape loading error» is reported.

This kind of error detection is rather weak. Due to the nature of the XOR operation, two wrongs give a right. This means that when the block contains an even number of bad bits at the same position, they will be undetected. It is also not possible to correct reading errors, as the XOR operation only allows to identify the position of the bad bit, but not the actual byte that contained the error. More sophisticated error correction algorithms would have slowed down the loading process, though.

The parity is computed as a final step, after all the bytes have been read from the block on tape. For that reason, the loader can only decide at the end of the recording whether the loading was successful or not.

But then, why does the tape loading error sometimes appear while the block is still loading? Well, in the first part I have explained that the loading routine just reads an unknown number of bytes. It ends when waiting for a pulse change took to long. Now, if there is an audio gap on tape, the signal seems to end just in the middle of the block. It is then very likely that the parity checksum is wrong because there are still bytes missing.

Some simple copy protections made use of the way the Spectrum loads data from tape. A very common way were “headerless” files, where the header block was left out and only the data block was recorded on tape. The BASIC LOAD command was unable to read those files because of the missing header.

tym

Enthusiast
Posts: 83
Liked: 9 times
Joined: Mar 26, 2015 9:30 pm
Full Name: Tim Diekhans
Contact:

Tape Loading Error (but library says everything ok)

Hi

I try to make a backup copy to tape but failed until now.
Errors that I got the last time I tried:

Code: Select all

13.06.2015 10:32:43 :: Loading tape 000622L5 from Slot 4131 to Drive 257 (Drive ID: Tape1) Error: The operation completed successfully.
DeviceIoControl(ChangerGetElementStatus) with sourceAddress = '35', destAddress = '1', symLink = '\?scsi#changer&ven_ibm&prod_3573-tl#5&fff8b2a&0&000301#{53f56310-b6bf-11d0-94f2-00a0c91efb8b}'
--tr:Timed out waiting for device ready status on MoveMedium operation, source element ChangerSlot#35, destination element ChangerDrive#1
The operation completed successfully.
DeviceIoControl(ChangerGet 


13.06.2015 10:34:38 :: Failed to create backup metadata info. Error: Matching tape backup was not found (M09:b4f39a06-e1c8-47db-84eb-1898b952987f) 

13.06.2015 10:35:48 :: [This server] [TapeService] Failed to Get media info
A cleaner cartridge is present in the tape library.
Can not get tape media information with status '4340' in changer '\.Tape1'
--tr:Timed out waiting for device ready status on FindMediaInfo operation, symLink = \.Tape1'
A cleaner cartridge is present in the tape library.
Can not get tape media information with status '4340' in changer '\.Tape1'

A cleaner cartridge is present in the tape library.
Can not get tape media information with status

Any ideas?

Thanks in advance.

Best,
Tim







tym

Enthusiast
Posts: 83
Liked: 9 times
Joined: Mar 26, 2015 9:30 pm
Full Name: Tim Diekhans
Contact:

Re: Tape Loading Error (but library says everything ok)

Post

by tym » Jul 15, 2015 9:54 am

i also suspect that we ran into a job conflict last time: we do a synth full backup each weekend, and I think the synth job conficted with the tape job (which purpose was to duplicate to last full backup to tape). But thanks for the link — will try that.


veremin

Product Manager
Posts: 19898
Liked: 2154 times
Joined: Oct 26, 2012 3:28 pm
Full Name: Vladimir Eremin
Contact:

Re: Tape Loading Error (but library says everything ok)

Post

by veremin » Jul 15, 2015 10:10 am

Based on the provided error message, I’d say the cleaner cartridge being present inside the library has been the major cause of the failure. Job overlap, if it had happened, would have resulted in some different message.

Anyway, glad to hear that now everything is up and running.

Thanks.


Connectsys

Novice
Posts: 4
Liked: never
Joined: Nov 15, 2009 4:20 pm
Full Name: Ollie Tansley
Contact:

Re: Tape Loading Error (but library says everything ok)

Post

by Connectsys » Aug 03, 2015 9:05 am

Hi there

We are having this exact problem, probably 1 tape backup job out of three fails. Our autoloader (Quantum SuperLoader3) always has a cleaning tape in slot 16 because the library is set to autoclean itself if it needs cleaning. In competitor products there is a way to mark that slot as containing a cleaning tape but I can’t see how to do this with Veeam 8.0.0.2030 — are we missing something?

Thanks in advance

Steve


veremin

Product Manager
Posts: 19898
Liked: 2154 times
Joined: Oct 26, 2012 3:28 pm
Full Name: Vladimir Eremin
Contact:

Re: Tape Loading Error (but library says everything ok)

Post

by veremin » Aug 03, 2015 11:05 am

Nope, you’re right currently there is no such functionality.

We’ve heard that request several times before, but also have more demanded tape features waiting to be implemented.

Thanks.


Adonis

Novice
Posts: 5
Liked: never
Joined: Aug 07, 2015 8:30 am
Full Name: Adonis
Contact:

Re: Tape Loading Error (but library says everything ok)

Post

by Adonis » Aug 07, 2015 8:52 am

You get your error from Veeam, but does the library load the tape??

I’m having troubles with a MSL4048, Veeam tries to load the tape, but nothing happens but an error. MSL does not perform anything, the tape is not loaded and no error shown.


Dima P.

Product Manager
Posts: 13751
Liked: 1395 times
Joined: Feb 04, 2013 2:07 pm
Full Name: Dmitry Popov
Location: Prague
Contact:

Re: Tape Loading Error (but library says everything ok)

Post

by Dima P. » Aug 07, 2015 3:59 pm

Adonis,
Can you please elaborate is that happening right after you set your tape infrastructure? Have you checked with the support team? Thanks


Adonis

Novice
Posts: 5
Liked: never
Joined: Aug 07, 2015 8:30 am
Full Name: Adonis
Contact:

Re: Tape Loading Error (but library says everything ok)

Post

by Adonis » Aug 10, 2015 6:27 am

It’s happening just after a new Veeam Installation, setting up a new tape infraestructure.

I opened a support case ID 00986942

Support says it maybe an issue with Backup exec services. It’s a partitioned Library used by Backup Exec and Veeam.



Adonis

Novice
Posts: 5
Liked: never
Joined: Aug 07, 2015 8:30 am
Full Name: Adonis
Contact:

Re: Tape Loading Error (but library says everything ok)

Post

by Adonis » Aug 18, 2015 7:21 am

v.Eremin wrote:Are those solutions installed on the very same machine?

No, they are both installed on distinct physical servers

The last test I did was to connect the library only to Veeam, and it worked fine. Then I partitioned the library and let to Veeam the first partition and the Second to Symantec, then Symantec couldn’t operate the library


veremin

Product Manager
Posts: 19898
Liked: 2154 times
Joined: Oct 26, 2012 3:28 pm
Full Name: Vladimir Eremin
Contact:

Re: Tape Loading Error (but library says everything ok)

Post

by veremin » Aug 18, 2015 7:48 am

Based on the experience of our users, with tape partitioning it should be possible to use the very same library by two different tape solutions. However, it’s hard to comment on a specific model provided by certain vendor, as implementation might vary among those. Thanks.


Who is online

Users browsing this forum: No registered users and 6 guests

 


Глава 20,21 — внешняя память на магнитной ленте. Устройство печати.

Глава 20
                Внешняя память на магнитной ленте
Краткое содержание: LOAD, SAVE, VERIFY, MERGE
    Основные  команды работы с магнитофоном SAVE,  LOAD и  VERIFY
уже  рассматривались во вводном описании.  Вы могли  видеть,  что
LOAD  затирает старую программу в памяти компьютера при  загрузке
новой программы с ленты,  есть другая команда MERGE,  не делающая
этого.  Эта команда стирает лишь те строки программы или перемен-
ные,  которые  совпадают  с номерами строк  новой  программы  или
именами  новых  переменных.   Программу  "DICE"  ("игральная
кость")  из  главы 11 запишем на ленту под  именем  "DICE",  а
теперь введем и выполним следующую программу:
              1 PRINT 1
              2 PRINT 2
             10 PRINT 10
             20 LET X
    Осуществим ее проверку,  заменив команду VERIFY "DICE"
на команду MERGE "DICE",  вы увидите,  что строки 1 и 2 сохра-
няются, а строки 10 и 20 заменяются на строки с этими номерами их
программы "DICE", переменная X тоже сохраняется (проверте PRINT X).
    Теперь  вы  знаете  четыре оператора для работы  с  кассетным
магнитофоном:
     SAVE     - записывает программу и переменные на магнитофон;
     VERIFY   - проверяет программу и переменные в памяти
                компьютера по их копии на ленте;
     LOAD     - очищает  память  компьютера  от  всех  программ  и
                загружает в нее новые, считанные с магнитофона;
     MERGE    - подобна LOAD, только не очищает всю память, а лишь
                заменяет те строки программы или переменные, у
                которых совпадают номера или имена с такими же на
                магнитной ленте.
    За каждой из этих команд следует ключевое слово - имя програм-
мы, определенное первоначально в команде SAVE,  пока  компьютер
ищет  указанную программу,  он выводит имена всех  программ,  уже
прочитанные с ленты, имеется две возможности для снятия справки с
ленты.
    Вариант 1,  в операторах VERIFY,  LOAD и MERGE вместо  имени
можно указать пустую строку, тогда будет взят первый встретивший-
ся файл.
    Вариант 2, с использованием оператора SAVE:
             SAVE STRING LINE NUMBER
программа  запишется  на  ленту так,  что когда она  будет  вновь
считана по команде LOAD (но не MERGE),  она автоматически устано-
вится на строку с указанным номером и сама инициирует свое выпол-
нение.
    Кроме текстов программ на ленту можно записывать также масси-
вы или данные.
    Записать на ленту массив вы можете,  используя команду SAVE с
DATA таким образом:
            SAVE STRING DATA ARRAU NAME()
здесь  "STRING" - имя,  присваиваемое  файлу  данных,  которое
может  состоять  из букв или букв и символа "$"  (перечеркнутая
буква  "S").  Для строковых данных это  требование  здесь  не
важно. Загружаются такие данные по команде:
             LOAD STRING DATA ARRAY NAME()
Нельзя использовать оператор MERGE.
    Если  загружается строковый массив,  то после обнаружения его
на  ленте,  компьютер выдает:  "CHARAUTER ARRAY:" и  далее  имя
второго массива.
Существует  возможность  записи  на магнитную ленту  и  отдельных
байтов  информации,  так например,  это может быть  телевизионная
картинка или определенные ползователем графические символы и т.д.
    Для этого используется ключевое слово CODE, например:
        SAVE "PICTURE" CODE 16384,6912
здесь первое  число - адрес первого байта в  области  памяти  где
расположены данные,  а второе число - количество байтов,  которые
нужно записать на ленту (6912 -обьем в байтах одного экрана,
16384 - адрес экрана в памяти). Загружаются эти данные по команде:
        LOAD "PICTURE" CODE
после CODE можно указать числа:
            LOAD "PICTURE" CODE START, LENGTH
    LENGTH  (длина) - определяет сколько данных (байтах)  надо
загрузить с ленты,  если длина больше,  чем записано на ленту, то
выдается  сообщение "R TAPE LOADING ERROR" (ошибка загрузки  с
ленты).  Этот  параметр можно опустить и тогда компьютер  считает
все данные, которые записаны на ленте.
    START (начало) - указывает адрес,с которого должны загружаться
                     данные и может быть отличным от адреса,
                     указанного  в SAVE, вы можете опустить этот па-
                     раметр в команде LOAD.
выражение CODE 16364,6912 можно заменить на SKREEN$:
     SAVE "PICTURE" SCREEN$ и затем
     LOAD "PICTURE" SCREEN$
    Это тот случай когда VERIFY не работает,  в остальных случаях
VERIFY$ можно использовать везде где используется SAVE.
    В  заключение,  везде,  где указывается имя файла  на  ленте,
используются  только первые 10 символов,  существует четыре  типа
информации, которые могут быть записаны на ленту:
    - программы и переменные ( совместно);
    - числовые массивы;
    - строковые массивы;
    - непосредственно байты
    Когда команды VERIFY, LOAD, и MERGE осуществляют поиск данных
на ленте,  они выводят на экран все считанные ими с ленты имена с
укажанием типа в виде:
    " PROGRAM:",  " NAMBER ARRAY:",  " CHARACTER ARRAY:",
    " BYTES:"
    Если  имя - пустая строка,  эти команды берут первый встретив-
шийся файл с указанным типом.
    Команда SAVE служит для записи информации на ленту под задан-
ным  именем,  сообщение  об ошибке F выдается если  вместо  имени
указана пустая строка или число символов в имени 11 и более, SAVE
всегда выдает сообщение.
    " START  TAPE,  THEN PRESS ANY KEY"
  ( "запусти  магнитофон  инажми любую клавишу"),
  и ждет нажатия,  после всего  записывает данные на ленту.
    1. Программа и переменные,
               SAVE NAME LINE LINE NUMBER
Записывает  программу  на ленту таким  образом,  что  последующая
команда LOAD автоматически вставляет в программу
                GOTO LINE NUMBER выполнять .
     2. Байты.
                SAVE NAME CODE START, LENGHT
Записывает на ленту "LENGHT" байт, начиная с адреса START
                SAVE NAME SCREEN$
эквивалентно    SAVE NAME CODE 16384,6912
и записывает один телевизионный экран.
      3. Массивы
                SAVE NAME DATA LETTER () или
                SAVE NAME DATA LETTER()
записывают  числовой  или  строковый массив  (требование  $  не
относится к "NAME").
    Команда  VERIFY проверяет (сравнивает) информацию в памяти  и
на ленте, может выдавать сообщение
        "R TAPE LOADING ERROR",
    1. Программа и переменные,
          VERIFY NAME
    2. Байты,
          VERIFY NAME CODE START LENGTH
    Если  данных  в  файле  "NAME" более  чем  указано  в
"LENGHT" то выдается сообщение об ошибке "R"
           VERIFY NAME CODE START
Здесь  осуществляется сравнение данных на ленте с данными в памя-
ти,  начиная  с адреса,  с которого записывается на ленту  первым
байт данных.
                    VERIFY NAME SCREEN$
 эквивалентно       VERIFY NAME CODE 16384,6912
Однако это будет проверка уже проверенного файла.
     3. Массивы.
           VERIFY NAME DATA LETTER()
           VERIFY NAME DATA LETTER$()
    Команда  LOAD загружает новые данные с ленты,  стирая  старые
данные в памяти.
    1. Программа и переменные.
Команда -   LOAD NAME
может выдавать сообщение "4 OUT OF MEMORY", если нет места для
новой программы. В этом случае старая программа не уничтожается
     2. Байты.
Команда -     LOAD NAME CODE START, LENGTH
Если данных в файле "NAME" больше, чем указано в "LENGTH", то
выдается сообщение R.
Команда -     LOAD NAME CODE START
производит загрузку данных из "NAME" в память, начиная с адреса
"START"
Команда -     LOAD NAME CODE
загружает прграмму по адресу,  с которого записывались данные  на
ленту в файл "NAME".
     3. Массивы
Команда -     LOAD NAME DATE LETTER() или
              LOAD NAME DATE LETTER$()
уничтожает  в  памяти массив с именем "LETTER"  или "LETTER$",
формирует  новый  массив  и переписывает  туда  данные  из  файла
"NAME", может выдать сообщение "4 OUT OF MEMORY" при нехват-
ке памяти под массив, в этом случае старый массив не уничтожается.
Команда MERGE загружает новые данные с ленты, не уничтожая старые.
    1. Программа и переменные.
Команда -    MERGE NAME
дописывает программу "NAME" к некоторой программе, находящейся
в памяти, может выдать сообщение
             "4 OUT OF MEMORY"
     2. Байты.
не поддерживается.
     3. Массивы.
не поддерживается.
    Пример:  записать  на  ленту информайию о  21-м  определенном
пользователем символе.
       SAVE "CHESS" CODE USR "A", 21*8
    Обратная загрузка
       LOAD "CHESS" CODE
       LOAD "CHESS" CODE USR "A"
Глава 21
                У С Т Р О Й С Т В О   П Е Ч А Т И
Краткое содержание:  LPRINT, LLIST, COPY.
    Эта глава описывает операторы бейсика, необходимые для работы
с принтером ZX SPECTRUM.
    Два оператора LPRINT и LLIST подобны операторам PRINT и LIST
но с той разницей, что они работают не с телевизором, а с
принтером, попробуйте для примера выполнить следующую программу:
                 10 LPRINT ' " THIS PROGRAM." '
                 20 LLIST
                 30 LPRINT ' " PRINTS OUT THE CHARACTER SET." '
                 40 FOR N=32 TO 255
                 50 LPRINT CHR$ N
                 60 NEXT N
    Оператор COPY позволяет распечатать экран телевизора,
например по LIST текст программы будет выведен на экран, а затем
по COPY его можно распечатать на принтере.
    Вы всегда можете прекратить вывод на печать выдав BREAK (CAPS
SHIFT и SPASE).
    Если вы задали операторы управления принтером без подключения
реального устройства, то вывода просто не будет и выполнение
программы продолжится до следующего оператора.
    Теперь ппробуйте изменятить следующую программу:
                    10 FOR N=31 TO 0 STEP -1
                    20 PRINT AT 31-N,N; CHR$(CODE"0"+N);
                    30 NEXT N
Вы получите последовательность символов расположенных по
диагонали экрана, начиная с правого верхнего угла, теперь заменим
в строке 20 PRINT AT 31-N,N' на 'TAB N' программа будет работать
также как и прежде, теперь заменим в строке 20 PRINT на LPRINT.
заметим, что развертки по диагонали не получится, а заменив
теперь 'TAB N' на 'AT 31-N,N' и сохранив LPRINT получим по одному
символу на строку, что и требовалось получить.
    Вообще при печати перевод строки осцществляется в следующих
случаях:
    а) при заполнении буфера строки;
    б) после LPRINT, если это не конец оператора и в нем
       встретилась запятая или точка с запятой;
    в) если запятая, апостроф или TAB требуют новой строки;
    г) при окончании программы, если остались невыведенные данные.

Интересы: 
ACW/CSA,
Субмарины,
Шitdows,
GNU/Linux:
Ubuntu
Manjaro
Kali,
Python,
Настольные игры,
Го,
ZX Spectrum,
Hard&Soft,
Torrent:
Книги
Музыка
Фильмы
Картины,
Дарк-Петербург,
Япония,
Древний Египет,
日本語,
Сёдо,
Манга,
Анимэ,
БЖД,
Брюнетки,
Gothic,
Вампиры,
Врановые,
Лисы 狐,
Карты Таро,
666.

 

Дневник (2018 » Октябрь » 26 в 02:56)

ZX Spectrum Кассеты возрастом в четверть века (upd)

R Tape loading error

Попались мне в цепкие лапы кассеты с играми, спасибо сохранившему их с 90х годов комраду. 26 штук, судя по надписям им самим по столько же лет почти. Из них с 11 кассет МК-60-5(6) 2-4 игры грузятся, остальные с ошибками. Ещё 3 импортные кассеты с каким-то нестандартным турбо загрузчиком, хотя надпись и уверяет, что доработок компьютера не требуется, но у меня ZX Evo на них гарантированно сбрасывается в basic48.  Остальные 13 не читаются вообще, или на соплях с помощью кузькиной матери грузят от силы одну игру. Ещё в коробку затесалось две кассеты с какой-то попсой вместо игр=)

Производитель отечественных кассет МК-60-5(6) явно не рассчитывал, что ими будут пользоваться спустя четверть века в какой-то возникшей на обломках СССР капстране третьего мира. Поэтому я сбился со счета, сколько раз при перемотке обрывалась пленка в местах её склейки с прозрачной лентой. Приходилось разбирать кассету и чинить. Сначала я заново клеил суперклеем, затем забил и просто подцеплял к катушке саму магнитную пленку.

Местами устав пытаться подстроить звук, я осматривал пленку и замечал, что она зажевана вхлам буквально до дыр, одну такую сфотографировал, перед тем как отложить в кучу отбраковки. Да, бывало и такое — в 90е даже магнитофоны голодали и любили пожевать плёнку).

Я занимался ими непрерывно с вечера до утра, и то не успел проверить все. Пробовал и прямо грузить игры, и читать их копировщиком TF-COPY. На каждой кассете я пытался грузить 2-3 игры подряд на каждой стороне. Если не читалось, пробовал грузить дальше, и так пока не находил или не находил повод оставить такую кассету на полке. Требование — сносное чтение хотя бы трех игр на всей кассете. Меня буквально убило то, что первые 11 кассет подряд оказались полностью не читаемыми.

Я сорвал шлиц на подстроечном винте головки магнитофона, пытаясь поймать волну несущей, я вспомнил как надо прижимать правый верхний угол кассеты, чтобы она лучше читалась) У меня такой боли от R Tape loading error в конце загрузки не было лет с 16-ти)…  Мне даже стало казаться, что это не я купил кассеты, а меня наняли реанимировать записи за большие деньги ^_^/ таким образом к 11 утра я чувствовал себя довольно обескуражено, а звук несущей звучал даже, когда магнитофон был давно выключен, а кассеты убраны на полку..

Помимо не читаемой ленты меня поджидали и другие неприятности вроде кривых рук составителей этих сборников.Например в ламерской студии Bird`s &K Games studio записывали на ленту с дискет, их выдала обрезанная длина имён до 8 символов, в то время, как на кассетах она 10 символов. вдобавок их дисковые файлы были с ошибками или записывались на ленту без проверки.  То есть кассета грузится, но на ней заведомо испорченные файлы. Или картинка загрузки, или спрайты, или код.

Вот читаемые кассеты, какие-то игры грузятся, какие-то нет:

А это с турбозагрузкой, которые не грузятся. Несущая видна, затем считывает не
прекращая несущую заголовок и начинает грузить. Появляется черный бордюр
и цветные полосы в пиксель высотой. И через пару секунд сброс в 48
basic. Пробовал много раз. ZX EVO rev.3 + Легенда М404.

UPDATE.28.10.18

Примеры вложенных в кассеты бумажек:

Типы использованных для записи игр кассет:

Всего комментариев: 2.

Порядок вывода комментариев:

0  

У этих турбо кассет большая чувствительность к настройке головки мафона. Без отвертки у меня они не грузились, более того они продавались 3 разных типов для разной настройки мафонов — что бы юзерам было проще
(й) NewArt про три кассеты с компрессионными записями.


0  

Konstantin Kalantaj в чате заметил:

просто надо чтобы запись чёткая была. я ж тебе выше дал файл, который читается вот он нормально считан был но!
взял только левый канал и на 15% уменьшил громкость этого канала. Только тогда он смог прочитаться в спектакуляторе. вангую что для нормального считывания в образ надо сделать такое: на еве запустить копировщик, который может сжатые данные копировать и в него загрузить с ленты игру, а потом выгружать с копировщика данные а с выхода магнитофонного читать в PC вот тогда уровень будет тот что надо вся запись сделана типа одним блоком.


В целях защиты от спама, добавлять комментарии могут только зарегистрированные пользователи.
Если Вы не хотите регистрироваться под своим именем, войдите под логином: anonymous, паролем: anonymous.  
( Регистрация,   Вход).

Авторизация

Анонимный вход:
anonymous / anonymous

Поиск
Статистика

Яндекс.Метрика

Онлайн всего: 2

Гостей: 2

Пользователей: 0

Понравилась статья? Поделить с друзьями:
  • Tap0901 error failed to uninstall driver
  • Tap устройство домена vpn выключено hamachi как исправить
  • Tamper ошибка на терминале
  • Tales of wind ошибка обновления
  • Tales of wind код ошибки 2