Здесь можно скачать актуальные версии Virtual Vector (VV)
С любовью к вам, Yandex.Direct
Размещение рекламы на форуме способствует его дальнейшему развитию
Это результат работы TURBO-Copy VladTru, таких можно много наделать.
Мне встречалась защита в виде уникального формата (для которого нет копировщиков) на сборнике игрушек из Кишинева. Тогда эти хитрые форматы меня не очень интересовали, зато нужна была возможность копирования и все игрушки оттуда я хакнул в обычный rom, даже не уверен, что осталась оригинальная запись. Как называлась "фирма" - не помню.
Еще у меня был BASIC-M с оригинальной защитой от кишиневского центра "Компьютер". И вроде его я не хакал, но у меня из другого источника была ломаная версия.
Центр "Компьютер", кстати, по крайней мере на коммерческом пике своей деятельности, писал игрушки и программы в каком-то хитром формате с хитрым загрузчиком. Вроде там при загрузке подобно спеку шли цветные полосы (в basic-m был другой загрузчик).
Да, что-то припоминается, что и для Вектора было, на кассете перед программой шел "загрузчик" в 1 блок, он запускался, потом шла программа в "своём" формате. Но тоже, его форматом как-то не интересовался... Только было понятно, что там инфа была без повторов. Малейший дефект ленты и запись в ведро... Кстати, именно этот факт был стимулом "ломануть", поскольку магнитофоны очень любили жувать ленту... а терять купленное было жалко... Правда вроде-бы на кассету записывали такие проги два раза, но не сильно спасало...
- - - Добавлено - - -
Пока изучаю библиотеку, на данный момент в коде "чётр ногу сломит". Немного разберусь, причешу, будет видно...
У меня вот ардуинный дисплей с тачем давно без дела лежит, может интерфейс на него выведу.
Последний раз редактировалось KTSerg; 01.03.2018 в 10:28.
У меня тож, заработал... пока без дисплея и кнопок, управление по СОМ-порту с РС...
Сильно не экскрементировал, получается с моей схемой выходного каскада, на "громкости" чуть меньше максимума уверенно грузит с периодом таймера 210мкс. При таймере 190 гарантированы "пропуски".
Отличие номиналов от схемы (собирал из того, что попалось под руку) резюк не 3К а 4.7К , 10Мкф(плюсом к процессору), кондёр пикушник не 10 а 120.
Скрытый текст
Пару часов промаялся, пока нашел прикол компилятора, раньше с таким не сталкивался...
Все переменные объявлены как BYTE, ожидал, что и результат вычисления тоже будет BYTE.
в строке : while((IndW+1)==IndR);
никогда не останавливался, хотя я ожидал, что будет крутиться пока IndW на 1 меньше чем IndR.
Заработал только в варианте: while((BYTE)(IndW+1)==IndR);[свернуть]
Последний раз редактировалось KTSerg; 01.03.2018 в 06:25.
Это интуитивно очевидно, но я не сразу вспомнил, как это правило называется.
Глава 2.8.1.1. Integral promotions.
http://publications.gbdirect.co.uk/c...rithmetic.html
В общих чертах: все целочисленные выражения независимо от типа аргументов вычисляются в типе int, если таковой их в себя вмещает. То есть unsigned char + unsigned char == int.
Больше игр нет
Эти эксперименты сподвигли меня вчера провести свой -- я всё же попробовал подать цифровой сигнал с ардуины прямо на вход РС4 контроллера Д30 Вектора... То есть мимо всех конденсаторов, делителей, компараторов -- только цифра в чистом виде. И результат, скажу прямо, меня не порадовал: скорость передачи удалось поднять всего лишь на один шаг (8 мкс). А если к этому ещё прибавить опыт KTSerg, который собрал плеер на другой, более быстрой элементной базе, то можно сделать неутешительный вывод, что не так всё хорошо с выводом сигнала в rom-плеере. :-(
Думаю, надо будет снять ещё одну wav-ку сигнала, но с частотой дискредитации 96кГц, плюс такую же с живого Вектора и сравнить циклы "под микроскопом"...
Исправление ошибок, как и обещано :-)
ROM-плеер 2.1
Код:/* SD-картридер подключяется к выводам ардуино: * MOSI - D11 * MISO - D12 * CLK - D13 * CS - D10 Выход - D3 Подключение экрана 1602А (LCD Keypad Shield): * LCD RS pin - D8 * LCD Enable pin - D9 * LCD D4 pin - D4 * LCD D5 pin - D5 * LCD D6 pin - D6 * LCD D7 pin - D7 * LCD R/W pin - GND * LCD 5V pin - 5V Кнопки подключаются на вывод A0 (схема LCD Keypad Shield) */ // include the library code: #include <LiquidCrystal.h> #include <SdFat.h> #include <TimerOne.h> // initialize the library with the numbers of the interface pins LiquidCrystal lcd(8, 9, 4, 5, 6, 7); SdFat sd; SdFile romFile; #define filenameLength 100 // максимальная длина имени файла char fileName[filenameLength + 1]; // имя текущего файла char sfileName[13]; // короткое имя текущего файла int currentFile = 1; // текущая позиция в директории int maxFile = 0; // всего позиций в лиректории (файлов и поддиректорий) byte isDir = 0; // признак того, что текущая позиция -- это директория byte BLs = 0x01; // начальный блок unsigned int Nbt; // размер файла, байт volatile byte BUFF[256]; // буфер данных volatile unsigned int CRB = 0; // индекс чтения из буфера volatile byte bBit = 15; // индекс читаемого полубита unsigned int CWB = 0; // индекс записи в буфер unsigned int CRB_temp = 0; // временная переменная для CRB // Заголовок byte SB[27] = { 0x4E, 0x4F, 0x44, 0x49, 0x53, 0x43, 0x30, 0x30, // NODISK00 0x31, 0x34, 0x30, 0x32, 0x31, 0x38, // дата: 140218 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // имя программы 0x20, 0x20, 0x20, 0x00, 0x00 }; int Tpp = 256; // Начальная длительность задержки сигнала в микросекундах (один полупериод) const int p = 3; // номер пина, на который будет вывод сигнала const int BT_none = 0; // константы -- коды нажатой кнопки const int BT_right = 1; const int BT_up = 2; const int BT_down = 3; const int BT_left = 4; const int BT_select = 5; void setup() { pinMode(p, OUTPUT); // объявляем пин как выход pinMode(10, OUTPUT); // CS для SD-картридера lcd.begin(16, 2); // объявляем размер экрана 16 символов и 2 строки Timer1.initialize(Tpp); // инициализировать timer1, и установить период Tpp мкс. Timer1.attachInterrupt(SendHalfBit); // прикрепить SendHalfBit(), как обработчик прерывания по переполнению таймера Timer1.stop(); while (!sd.begin(10,SPI_FULL_SPEED)){ // SD-карта готова? printtext("SD-card failed!",0); tone(p, 200, 100); // нет -- включаем на 200 Гц на 100 мс delay(3000); // ждем 3 с } printtext("SD-card is OK.",0); sd.chdir(); // устанавливаем корневую директорию SD getMaxFile(); // получаем количество файлов в директории seekFile(); // переходим к первому файлу в директории } void loop() { lcd.setCursor(0, 1); // устанавливаем курсор в позицию 0 в строке 1 lcd.print(millis()/1000); // выводим количество секунд с момента влючения ардуины -- это для теста... int button = getPressedButton(); // какая кнопка нажата? switch (button) { case BT_right: // вход в директорию, или запуск файла на воспроизведение if(isDir==1) { //Если это директория, то переход в неё sd.chdir(fileName, true); getMaxFile(); currentFile=1; seekFile(); } else { // если не директория -- пробуем воспроизвести файл if(romFile.cwd()->exists(sfileName)) { printtext("Not ROM-file",1); if (Nbt != 0xFFFF) { // проверяем размер файла for (int i=0;12;i++){ // цикл по имени файла if (sfileName[i]=='.'){ // ищем точку в имени файла if ((sfileName[i+1]|0x20)=='r') { // проверяем следующий символ (расширения) == 'r'|'R' if (((sfileName[i+2]|0x20)=='o')|((sfileName[i+2]|0x20)=='0')) { // == 'o'|'O'|'0' if ((sfileName[i+2]|0x20)!='0') { // проверка на вывод нулевого блока по расширению файла BLs = 0x01; // с первого блока } else { BLs = 0x00; // с нулевого блока } if ((sfileName[i+3]|0x20)=='m') { // == 'm'|'M' printtext("Playing...",1); int RCrom = PlayROM(sfileName, i);// Передаём короткое имя файла и позицию точки в качестве параметров switch (RCrom) // Проверяем код возврата { case 0: printtext("Done.",1); // Всё закончилось успешно. break; case 1: printtext("Stopped",1); // Сообщение об остановке while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка delay(50); } break; default: printtext("ERROR!",1); // Сообщение об ошибке } seekFile(); } } } break; } } } else { printtext("File is too big",1); } } else { printtext("No File Selected",1); } delay(1000); // ждем 1 с printtext(" ",1); // очищаем строку 1 } break; case BT_left: // возврат в корневую директорию, ремарка ниже: //SDFat has no easy way to move up a directory, so returning to root is the easiest way. //each directory (except the root) must have a file called ROOT (no extension) sd.chdir(true); getMaxFile(); currentFile=1; seekFile(); break; case BT_up: // вверх по файлам в директории currentFile--; if(currentFile<1) { getMaxFile(); currentFile = maxFile; } seekFile(); break; case BT_down: // вниз по файлам в директории currentFile++; if(currentFile>maxFile) { currentFile=1; } seekFile(); break; case BT_select: // выход в настройки... printtext("Period(mks):",0); do { while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка delay(50); } delay(200); button = getPressedButton(); // какая кнопка нажата? switch (button) { case BT_up: if (Tpp<400) { Tpp += 8; } break; case BT_down: if (Tpp>200) { Tpp = Tpp - 8; } break; } lcd.setCursor(12,0); lcd.print(Tpp); } while(button!=BT_select); // Цикл пока не будет снова нажата кнопка select seekFile(); break; case BT_none: // ничего не нажато break; } while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка delay(50); } } //================================================================= int getPressedButton() // функция проверки нажатой кнопки { int buttonValue = analogRead(0); if (buttonValue < 100) return BT_right; else if (buttonValue < 200) return BT_up; else if (buttonValue < 400) return BT_down; else if (buttonValue < 600) return BT_left; else if (buttonValue < 800) return BT_select; return BT_none; } void printtext(char* text, int l) { // Вывод текста на экран в строке l с очисткой строки lcd.setCursor(0,l); lcd.print(" "); lcd.setCursor(0,l); lcd.print(text); } void getMaxFile() { // считаем файлы в текущей директории и сохраняем в maxFile romFile.cwd()->rewind(); maxFile=0; while(romFile.openNext(romFile.cwd(),O_READ)) { romFile.close(); maxFile++; } } void seekFile() { // переход на позицию в директории, сохранение имени файла и показ его на экране romFile.cwd()->rewind(); for(int i=1;i<currentFile;i++) { // читаем первые файлы до currentFile romFile.openNext(romFile.cwd(),O_READ); romFile.close(); } romFile.openNext(romFile.cwd(),O_READ); // читаем данные текущего файла romFile.getName(fileName,filenameLength); romFile.getSFN(sfileName); isDir = romFile.isDir(); if (romFile.fileSize()<=49152) { // проверка размера файла Nbt = romFile.fileSize(); // размер файла ОК } else { Nbt = 0xFFFF; // слишком большой для загрузки } romFile.close(); printtext(sfileName,0); // вывод имени текущего файла if (isDir==1) lcd.print('>'); // если это директория, добавляем в конце символ '>' } int PlayROM(char FName[], int pnt) // функция вывода файла { delay(1000); // ждем 1 с if (romFile.open(FName,O_READ)) { // открываем файл. Открылся? byte BLe = Nbt/256; // всего блоков byte BLt; // осталось блоков byte Nst; // номер строки byte St; // выводимый байт byte CSz = 0x00; // контрольная сумма заголовка byte CSs = 0x00; // контрольная сумма строки byte i; byte j; if (Nbt%256 != 0) BLe++; // корректировка количества блоков, если размер файла не кратен 256 for (i=0; i<=7; i++){ // заносим в SB имя файла if (i < pnt) { // имя ещё не закончилось? if ((FName[i]>=0x61) && (FName[i]<=0x7A)) { SB[i+14] = FName[i] - 0x20; // на заглавные буквы } else if (FName[i]!=0x7E) { // не тильда SB[i+14] = FName[i]; } else { SB[i+14] = '_'; // меняем тильду на подчёркивание, иначе это будет русская "Ч" } } else SB[i+14] = 0x20; // пробелы } for (i=1; i<=3; i++){ // заносим в SB расширение файла if ((FName[pnt+i]>=0x61) && (FName[pnt+i]<=0x7A)) { SB[i+21] = FName[pnt+i] - 0x20; // на заглавные буквы } else { SB[i+21] = FName[pnt+i]; } } dir_t d; romFile.dirEntry(&d); // Считываем дату файла uint16_t AAA = FAT_DAY(d.lastWriteDate); // Сохраняем дату файла в заголовке -- день SB[8] = (AAA%100)/10 + '0'; // перевод из целого в символ SB[9] = AAA%10 + '0'; AAA = FAT_MONTH(d.lastWriteDate); // месяц SB[10] = (AAA%100)/10 + '0'; SB[11] = AAA%10 + '0'; AAA = FAT_YEAR(d.lastWriteDate); // последние две цифры года SB[12] = (AAA%100)/10 + '0'; SB[13] = AAA%10 + '0'; // Начинаем наполнять буфер for (i=0; i<=3; i++){ // преамбула (4*(00H*25+55H*25)) for (j=0; j<=24; j++){ BUFF[lowByte(CWB)] = 0x00; CWB++; } for (j=0; j<=24; j++){ BUFF[lowByte(CWB)] = 0x55; CWB++; } } Timer1.setPeriod(Tpp); // Выставляем период таймера Timer1.start(); // Запускаем таймер............ for (BLt=BLe; BLt>=1; BLt--){ // Вывод блоков данных в цикле CSz = BLs; CSz += BLe; CSz += BLt; for (j=0; j<=15; j++) ToBUFF(0x00);// 00h*16 for (j=0; j<=3; j++) ToBUFF(0x55);// 55h*4 ToBUFF(0xE6); // E6h*1 for (j=0; j<=3; j++) ToBUFF(0x00);// 00h*4 for (j=0; j<=26; j++){ // заголовок блока CSz += SB[j]; ToBUFF(SB[j]); } ToBUFF(BLs); // начальный блок ToBUFF(BLe); // конечный блок ToBUFF(BLt); // осталось блоков lcd.setCursor(12, 1); // выводим на экран кол-во оставшихся блоков lcd.print(BLt); lcd.print(" "); ToBUFF(CSz); // контр.сумма заголовка for (Nst=0x80; Nst<=0x87; Nst++){ // вывод строк (8 шт.) for (j=0; j<=3; j++) ToBUFF(0x00);// 00h*4 ToBUFF(0xE6); // E6h*1 CSs = Nst; ToBUFF(Nst); // номер строки CSs += CSz; ToBUFF(CSz); // контр.сумма заголовка // начинаем вывод строки данных for (j=0; j<=31; j++){ // цикл на 32 байта if (Nbt > 0){ // ещё есть данные? St = romFile.read(); // читаем очередной байт из файла Nbt--; } else { // нет -- дополняем нулями St = 0x00; } ToBUFF(St); // передаём считанный байт CSs += St; if (getPressedButton()!=BT_none) { // кнопка нажата? Timer1.stop(); // Останавливаем таймер CRB = 0; // Сбрасываем индексы. CWB = 0; bBit = 15; romFile.close(); // закрываем файл return 1; // выход из ПП с ошибкой 1. } if (CRB_temp > CWB) { // проверка -- не обогнало ли чтение запись? Timer1.stop(); // Останавливаем таймер CRB = 0; // Сбрасываем индексы. CWB = 0; bBit = 15; romFile.close(); // закрываем файл return 2; // выход из ПП с ошибкой 2. } } ToBUFF(CSs); // контр.сумма строки } } romFile.close(); // закрываем файл for (j=0; j<=31; j++) ToBUFF(0x00);// 00h*32 -- завершение вывода программы (?) } else tone(p, 200, 300); // нет файла -- включаем на 200 Гц на 300 мс do{ // Ждём опустошения буфера delay(Tpp/64); // задержка на вывод 1 байта (~Tpp*16/1000) noInterrupts(); // запрет прерываний CRB_temp = CRB; // сохраняем CRB во временную переменную interrupts(); // разрешение прерываний } while (CRB_temp < CWB); Timer1.stop(); // Останавливаем таймер............ CRB = 0; // Сбрасываем индексы. CWB = 0; bBit = 15; return 0; // выход из ПП с кодом 0. } void ToBUFF(byte SBb){ // Подпрограмма записи байта в буфер noInterrupts(); // запрет прерываний CRB_temp = CRB; // сохраняем CRB во временную переменную interrupts(); // разрешение прерываний if (CWB > (CRB_temp + 255)) { // Если позиция записи больше, чем позиция чтения + размер буфера - 1 delay(Tpp); // Задержка (Tpp*1000 мкс = Tpp мс = 125 байт) } BUFF[lowByte(CWB)] = SBb; CWB++; } void SendHalfBit() { // Подпрограмма вывода полубита по циклу таймера byte Pd=PORTD; if (bBit & 1){ // проверка индекса полубитов на чётность if (( (Pd >> p)^( BUFF[lowByte(CRB)] >> (bBit >> 1) ))&1){ // Если состояние порта и выводимый бит разные Pd ^= (1 << p); // инвертируем бит в позиции p } } else{ // нечётный -- просто инвертируем порт Pd ^= (1 << p); // инвертируем бит в позиции p(=3) } PORTD = Pd; // вывод в порт p if (bBit > 0) { // правим счётчики полубитов и байтов bBit--; } else{ bBit = 15; CRB++; } }[свернуть]
Исправлен глюк, от которого повторное воспроизведение того же файла не работало. Ну и плюс к тому, убрал ненужный параметр у процедуры seekFile().
Архив скетча в полном комплекте: ROM-player_2_1.zip
- - - Добавлено - - -
По выходному каскаду: собрал я опубликованную тут чуть ранее схемку, но с ней всё плохо... Скорости выше, чем 264мкс нет. Стал подбирать выходной конденсатор, и оказалось, что чем он меньше, тем лучше, начиная примерно от 1 мкФ идёт ухудшение скорости. Тот самый мой 0,33 мкФ, пожалуй, самый большой по ёмкости, с которым работает с задержкой от 232мкс. Ставил там даже 0,0068 мкФ -- работает не хуже, чем 0,33 мкФ. В общем, не понятно...
Вскрыл свой Вектор и глянул -- на входе там стоит вообще другая микросхема, не та, что в схеме, а К553УД2. Возможно они и взаимозаменяемы с К553УД1В. И ещё, интересный момент -- в даташитах на эти микросхемы пишут, что им нужно питание +/-15В, у нас же они запитаны от +/-5В, т.е. работают не в штатном режиме... Странно... Хотя в них может быть заложен больший потенциал, чем указано в документации и в военное время вообще весь Вектор можно было запитать от одной пальчиковой батарейки. :-)))
И ещё, по выходу rom-плеера: в ридми к проекту TZXDuino пишут, что на выход надо ставить простейший аналоговый усилитель, типа LM386 или подобного, надо попробовать что-нибудь такое. Но если уж и так ничего не выйдет -- не беда, пусть будет у меня 256 мкс.
Думаю, надо эту схему (вместе с выходной плеера) сделать в эмуляторе и посмотреть, как она работает на разных частотах...
- - - Добавлено - - -
Кстати, встречал в доках, что период таймера 1 желательно выставлять кратным 4, а лучше 8...
Если уменьшать размах сигнала делителем, то получившийся сигнал желательно усилить при подаче на вход с низким импедансом, иначе нагрузка будет перекашивать собой делитель. Для этого должно быть достаточно простого эмиттерного повторителя. Но зачем? КМОП-выход ардуины и так мощен, качает 20мА в любую сторону. Входной импеданс у схемы в Векторе при 5кгц порядка 20К, если я не ошибся. Если сделать выходной делитель из 1К резистора, то соотношение импедансов будет в районе 1:10. То есть нагрузка подкузьмит максимум на одну десятую, это вполне терпимо должно быть.
Это наверное рекомендованный максимальный размах. Чем больше размах питания, тем шире динамический диапазон и тем шире диапазон допустимых входных напряжений. Поэтому в общем случае, если нет других ограничений, желательно иметь возможность сделать размах побольше. У нас же тут всего 1 бит динамики и уровень входного сигнала в пределах 1В, так что все должно быть нормально.
Больше игр нет
Сделал небольшой анализ того, что происходит на входе Вектора... Для начала, несколько скриншотов графиков того, что происходит по схеме, которую тут запостил svofski, т.е. на компараторе К554СА3:
Скрытый текст
1. "Стандартная" ситуация -- входной сигнал прямоугольной формы с периодом 512 мкс и размахом +/-1,2В:
* Зеленый график -- входной сигнал
* Синий график -- на выводе "-" компаратора
* Красный график -- на выводе "+" компаратора
2. Сигнал с rom-плеера с конденсатором 10 мкФ на выходе:
3. И то же самое с конденсатором 0,1 мкФ (для информации):
В этом варианте видно, что читаться информация явно не будет...
[свернуть]
Теперь схема моего Вектора (фоткал из прилагаемой документации):
Схема практически полностью отличается, что, собственно, объясняет различие в ёмкости выходных конденсаторов на rom-плеере. Вот что у меня получается по графикам:
Скрытый текст
1. Входной сигнал прямоугольной формы с периодом 512 мкс и размахом +/-1,2В:
* Синий график -- входной сигнал
* Зеленый график -- на выводе "-" усилителя
2. Сигнал с rom-плеера с конденсатором 10 мкФ на выходе:
Видно достаточно плавное понижение сигнала до симметричного относительно "0" положения. Подозреваю, что из-за этого на высоких скоростях передачи сигнал просто не успевает выйти в нужный режим для корректного определения скорости.
3. И, наконец, то же самое с конденсатором 0,33 мкФ:
[свернуть]
Выходит, что по каждой конкретной модели/версии Вектора на схему выхода rom-плеера надо будет немного менять... И, опять же, надо ещё попробовать поставить усилитель -- может это будет универсальное решение.
Все картинки в архиве (а то что-то форум их зажимает до неприличия): archive.zip
Последний раз редактировалось Improver; 02.03.2018 в 22:27. Причина: Заменил картинки...
Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)