(Reader) Ридер картриджей SEGA Техническая Помощь и Подключение Картриджи и Флеш-картриджи - (Читайте Коды, Секреты, Пароли, Статьи) - OldCityRetroGames

Меню

Категории, жанр

Приставка (Консоль)
Картриджи и Флеш-картриджи
Джойстики и Контроллеры
Телевизор, Приставка и ПК
Дампинг и прочее
Эмуляторы, и другие программы..
Железо, Самоделки и дополнения.
Сайт (Помощь по онлайн играм..)
Прочее
Настройки и другое
Запись игр (образов, ромов)

Главная » Читать » Техническая Помощь и Подключение » Картриджи и Флеш-картриджи


(Reader) Ридер картриджей SEGA (Техническая Помощь и Подключение)

После разбора старых вещей, наткнулся на любопытную самодельную коробочку: читалку сеговских (Sega Genesis, Sega MegaDrive1/2) картриджей. Т.е. с помощью этой коробочки, подключаемой к LPT-порту, можно было считать любой сеговский картридж в файл, а затем играть на PC в сеговские игры с помощью эмулятора. В своё время я содрал немало картриджей и эта коробочка доставила мне немало радостных минут. Хочу заметить, что коробочка придумана в 1996 году,типа, прошлый век, поэтому не стоит судить слишком строго, как схему, так и программу.

Сеговский картридж (в отличие от "Дендевского"), как ни странно, представляет собой обычное ROM с организацией x16, т.е. выборка из ПЗУ возможна только словами. В то время схема сеговского разъёма для подключения картриджей выяснялась путём разбора последнего - в некоторых картриджах попадались корпусированые чипы, а не "капли". Некоторые картриджи имели дополнительную RAM в области старших адресов с питанием от батарейки для сохранения уровней (пример "Beyond Oasis"), но на "играбельность" подобный факт никак не влиял. Это сейчас всё стало известно, появидись схемы и т.п., а в 1996 году не было ни Интернета, ни доков, да и картриджи стоили по 30$. Вобщем, играть на PC в сеговские игры было актуально.

Что было надо для чтения картриджа? Надо было установить 21-разрядный адрес на адресном входе и считать с выходов данных 16-тиразрядное слово. Вроде бы всё просто, но незадача была в том, что в стандартном LPT всего 11 выходных шин и 5 входных. Пришлось соорудить схемку на 5ти счётчиках ИЕ7 и 2х мультиплексорах КП12. Схемка лежит вот здесь.
http://www.anyram.net/forum/download.php?aid=54

Принцип работы схемки - очевиден. Счётчики объединены в пары (кроме последнего), а выходы счётчиков подключены к адресным входам картриджа. Для того чтобы считать содержимое ROM, например, по адресу 0x012036 надо сбросить все счетчики в 0 подачей сигнала AutoFeed(выв.14 LPT), затем дать 0x36 импульсов на "младшую" пару счётчиков (DAT2, выв.4 LPT), 0x20 импульсов на "среднюю" пару счётчиков (DAT3, выв.5 LPT) и один имульс на счётчик старших адресов (DAT4, выв.6 LPT). При этом DAT5 (выв.7 LPT) должен находится в 0, т.к. он управляет состоянием адресной шины A20 (так сложилось, что исторически он появился позже, т.к. долгое время не попадалось картриджей объёмом более 2Мбайт; по-моему, это произошло на картридже "Boogerman").
После того, как адрес на адресной шине установлен, открывается выходная защёлка ~GATE (низкий уровень, выв.1 LPT) для того, чтобы данные появились на шине данных и входе мультиплексоров. По-скольку входов в LPT только четыре, то данные читаем за "четыре приёма" по 4 бита: 1ю часть выставив DAT1=0, DAT0=0; 2ю часть выставив DAT1=0, DAT0=1 и т.д.. Идея проста, как грабли, а теперь реализация на C.
 

Код:

#include <stdio.h>

typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;

ushort BasePort;// здесь будет храниться базовый адрес LPT
uint GL_ADDR;// текущий адрес выборки из картриджа
uchar MB2;// состояние шины A20 на картридже

// объявление тактирующей задержки за счёт чтения из порта;
// на любом компьютере от 486 до P4 выполняется около 500nS
void near takt(ushort);
#pragma aux takt = "in al,dx "\
 parm [dx]\
 modify [al]
// объявление функции вывода байта в порт; в принципе есть
// стандартная outp, но компилятор под такую простую ф-цию
// тратил несоразмерно много байтов
void out_byte(ushort,uchar);
#pragma aux out_byte = "out dx,al "\
 parm [dx][al]
// объявление функции ввода байта из порта
uchar in_byte(ushort);
#pragma aux in_byte = "in al,dx "\
 parm [dx]\
 modify [al]\
 value [al]
// опрос клавиатуры "на лету"
uchar keyz(void);
#pragma aux keyz = "mov ah,0x11"\
 "int 0x16"\
 "setnz al "\
 modify [ax]\
 value [al]
// разве тут нужны комментарии?
#pragma aux OpenFile = "mov ah,0x3C"\
 "xor ecx,ecx"\
 "int 0x21"\
 modify [eax ecx]\
 parm [edx]
uint OpenFile(uchar *);

#pragma aux SaveFile = "mov ah,0x40"\
 "int 0x21"\
 modify [eax]\
 parm [edx][ecx][ebx]
uchar SaveFile(uchar *,uint,uint);

#pragma aux CloseFile= "mov ah,0x3E"\
 "int 0x21"\
 modify [eax]\
 parm [ebx]
uchar CloseFile(uint);

// функция чтения байта из картриджа
// входным параметром этой ф-ции является глобальная переменная
// GL_ADDR - адрес выборки из картриджа, на выходе - данные из картриджа
uint read_word(void)
{
// массив состояний выводов DAT1, DAT0
// данные должны были читаться по
// последовательности DAT1/DAT0: 3, 2, 1, 0, но вышло так, что
// провода на схеме попутал местами и чтобы их не перепаивать,
// просто ввёл перекодировку через массив CRUNCH
 const uchar CRUNCH[]={ 2,3,0,1 };
 uchar t;
 uint r;
 r=0;// приёмник данных
// если адрес перевалил за 1Mb, то выставляем A20 (DAT5)
 if (GL_ADDR>=0x100000) MB2=0x20;
 else MB2=0x00;
// цикл чтения слова данных
 for(t=3;t!=0xFF;t--)
 {
 // высставляем в порт DAT5(A20), DAT1,DAT0 (номер тетрады)
 out_byte(BasePort,CRUNCH[t]|MB2);
 // высставляем сигнал ~GATE==0
 out_byte(BasePort+2,3);// GATE=0
 takt(BasePort+1);// маленькая задержка
 r<<=4;// сдвиг данных на тетраду
 // чтение данных из порта и исправление бита ~BUSY, т.к.
 // этот вход в LPT аппаратно инвертируется
 r|=(in_byte(BasePort+1)>>4)^0x08;
 // снятие строба вывода данных ~GATE==1
 out_byte(BasePort+2,2);// GATE=1
 takt(BasePort+1);// маленькая задержка
 };
 return (r);
}

// функция инкремента счётчика адресов в картридже на заданное число,
// "младший", "средний" или старший счётчик выбирается по маске
void inc_counter(uchar cnt,uchar mask)
{
// пока не исчерпается счётчик инкрементов
 while (cnt)
 {
 // тактируем счётчик с маской mask
 out_byte(BasePort,mask);
 takt(BasePort+1);
 // снимаем тактовый сигнал
 out_byte(BasePort,MB2);
 takt(BasePort+1);
 // декремент счётчика
 cnt--;
 }
}

// функция инкремента адреса выборки из картриджа
void inc_addr(void)
{
// инкремент глобальной переменной адреса текущей выборки
 GL_ADDR++;
// проверка перехода адресов за 1Мб
 if (GL_ADDR>=0x100000) MB2=0x20;
 else MB2=0x00;
// инкремент пары счётчиков младших адресов: DAT2, mask=4
 inc_counter(1,0x04);
// если не было перехода младших адресов через 0 - выход
 if (GL_ADDR&0xFF) goto OUT_FROM_INC;
// инкремент пары счётчиков "средних" адресов: DAT3, mask=8
 inc_counter(1,0x08);
// если не было перехода средних адресов через 0 - выход
 if (GL_ADDR&0xFF00) goto OUT_FROM_INC;
// инкремент счётчика "старшего" адреса: DAT4, mask=0x10
 inc_counter(1,0x10);
OUT_FROM_INC:
// вывод "апендикса" для больших картриджей - состояния шины A20
 out_byte(BasePort,MB2);
}

// установка заданного адреса выборки из картриджа (GL_ADDR)
void set_addr(void)
{
 uchar t;
 uint addr;
// проверка перехода через 1Мб адресного пространства
 if (GL_ADDR>=0x100000) MB2=0x20;
 else MB2=0x00;
 addr=GL_ADDR;
 out_byte(BasePort ,0);// выключение ~GATE
// сброс всех счетчиков адреса в 0
 out_byte(BasePort+2,0);// ~AutoFeed==1
 takt(BasePort+1);// маленькая задержка
 takt(BasePort+1);
//снятие сигнала сброса счётчиков ~Auto
 out_byte(BasePort+2,2);// ~AutoFeed=0
// установка "младшей" пары счётчиков
 inc_counter(addr&0xFF,0x04);
// установка "средней" пары счётчиков
 addr>>=8;
 inc_counter(addr&0xFF,0x08);
// установка "старшего" счётчика
 addr>>=8;
 inc_counter(addr&0xFF,0x10);
// вывод "апендикса" для больших картриджей - состояния шины A20
 out_byte(BasePort,MB2);
}

// вход в основную программу чтения картриджей
void main(void)
{
 uint sc;
 uint tmp;
 ushort CHECKSUM;
 uchar REPEAT;

 ushort *BUFFER;

 uint M_SIZE;
 uint f_h;

 printf("\nSEGA's ROM reader , Ver. 2.04, 1996\n\n");
 M_SIZE=0x400000;// здесь указан максимальный размер картриджа в Мб
 REPEAT=2;// кол-во попыток чтения
// следует отметить, что программа проверяет подключенность читалки
// только к порту LPT1
 BasePort=(*((ushort *)0x408L));// выяснение адреса порта LPT1
// если значение порта превышает пределы "разумного" - выход
 if ((BasePort==0)||(BasePort>0x400))
 {
 printf("LPT1 not found , program terminated ...\n");
 return;
 }
 printf("BasePort = 0x%.3X\n",BasePort); // LPT1
 printf("BaseSize = %4d kB\n",M_SIZE>>10);// размер картриджа
// установка начального состояния порта A LPT для вывода тактовых импульсов
 out_byte(BasePort ,0x00);
// установка начального состояния порта C LPT для вывода сигнала сброса
 out_byte(BasePort+2,0x02);
// выделение памяти под закачиваемый картридж, следует отметить, что
// программа работает в простой FLAT-модели памяти
 if ((BUFFER=(ushort *)malloc(M_SIZE))==0L)
 {
 printf("Not enough memory (%4d kB) ...\n",M_SIZE>>10);
 return;
 };
 GL_ADDR=0;// начальный адрес для выборки данных
 set_addr();// установка адреса на картридже
// сейчас будет проверятся наличие картриджа в читалке
// идея проверки состоит в поиске вначале адресного пространства хоть одного
// несовпадающего байта; проверяются первые 0x400 байт (счётчик ==0x200, т.к.
// длина данных - слово)
// здесь и далее до начала чтения картриджа CHECKSUM используется как временная
// переменная для хранения данных
 CHECKSUM=read_word();// читаем значение по адресу 0x00000
// переходим к следующему адресу
 inc_addr();
 for(sc=1;sc<0x200;sc++)
 if (CHECKSUM!=read_word()) goto BEGIN_READ;// переход, если найдено отличие
 printf("Reader isn't active ...\n");
 goto EXIT;
// начинаем читать картридж
BEGIN_READ:
// для начала выведем идентификатор картриджа (обычно торчит по адресу 0x80)
 printf("\nCartridge ID : ");
 GL_ADDR=0x80;// адрес для выборки ID
 set_addr();// установка адреса
// цикл чтения ID
 for(sc=8;sc>0;sc--)
 {
 CHECKSUM=read_word();
 inc_addr();// переход к след.адресу в картридже
 // вывод мл.символа ID
 if ( ((CHECKSUM&0xFF)>=0x20)&&((CHECKSUM&0xFF)<0x80) )
 printf("%c",CHECKSUM&0xFF);
 // вывод ст.символа ID
 CHECKSUM>>=8;
 if ( ((CHECKSUM&0xFF)>=0x20)&&((CHECKSUM&0xFF)<0x80) )
 printf("%c",CHECKSUM&0xFF);
 }
// вывод версии игры/картриджа
 printf("\nVersion ID : ");
 GL_ADDR=0x88;// адрес для выборки версии
 set_addr();// установка адреса
// цикл чтения версии картриджа
 for(sc=8;sc>0;sc--)
 {
 CHECKSUM=read_word();
 inc_addr();
 // вывод мл.символа имени
 if ( ((CHECKSUM&0xFF)>=0x20)&&((CHECKSUM&0xFF)<0x80) )
 printf("%c",CHECKSUM&0xFF);
 // вывод ст.символа имени
 CHECKSUM>>=8;
 if ( ((CHECKSUM&0xFF)>=0x20)&&((CHECKSUM&0xFF)<0x80) )
 printf("%c",CHECKSUM&0xFF);
 }
// вывод имени картриджа (обычно тут торчит имя, написанное на самом картридже)
 printf("\nCartridge name : ");
 GL_ADDR=0x90;// адрес для выборки
 set_addr();// установка адреса
// цикл чтения имени картриджа
 for(sc=32;sc>0;sc--)
 {
 CHECKSUM=read_word();
 inc_addr();
 // вывод мл.символа имени
 if ( ((CHECKSUM&0xFF)>=0x20)&&((CHECKSUM&0xFF)<0x80) )
 printf("%c",CHECKSUM&0xFF);
 // вывод ст.символа имени
 CHECKSUM>>=8;
 if ( ((CHECKSUM&0xFF)>=0x20)&&((CHECKSUM&0xFF)<0x80) )
 printf("%c",CHECKSUM&0xFF);
 }
 printf("\n");
// всё, созрели для того, чтобы считать весь картридж!
 GL_ADDR=0;// начальный адрес для чтения ==0
 set_addr();// установка адреса в читалке
// выводим сообщение о начале чтения/перечитывания
 if (REPEAT==2) printf("Read ...\n");
 else printf("Re-read ...\n");
// контрольная сумма ==0
 CHECKSUM=0;
// старт цикла чтения картриджа, кол-во адресов в картридже равно размеру картриджа/2,
// т.к. выборка из картриджа не байт, а слово
 for(sc=0;sc<(M_SIZE>>1);sc++)
 {
 BUFFER[sc]=read_word();// чтение слова
 CHECKSUM+=BUFFER[sc];// установка контрольной суммы
 if (keyz())// проверка нажатия кнопки на клавиатуре "на лету", аналог kbhit()
 {
 if (getch()==0x1B)// проверка нажатия 
 {
 printf("\n pressed ...\n");
 goto EXIT;
 }
 }
 inc_addr();// переход к след.адресу в картридже
 if ((GL_ADDR&0x3FF)==0)// вывод на экран сообщения о считанном объёме
 {
 printf("%.4ld kB\r",GL_ADDR>>9);
 }
 }
// после чтения выводим контрольную сумму
 printf("O.b. , CHECKSUM=%.4X\n",CHECKSUM);
// записываем считанный буфер файл
 printf("Saving in file out.bin ...\n");
 f_h=OpenFile("out.bin");
 SaveFile((uchar *)BUFFER,M_SIZE,f_h);
 CloseFile(f_h);

// начало верефикации данных
 GL_ADDR=0;// начальный адрес для чтения ==0
 set_addr();// установка адреса в читалке
 printf("\nVerify ...\n");
 CHECKSUM=0;// контрольная сумма ==0
// старт цикла верификации данных из картриджа, кол-во адресов в картридже
// равно размеру картриджа/2, т.к. выборка из картриджа не байт, а слово
 for(sc=0;sc<(M_SIZE>>1);sc++)
 {
 tmp=read_word();
 if (BUFFER[sc]!=tmp)
 {
 printf("\nVerify error at 0x%.5X ...\n",sc<<1);
 printf("Wait %.4X , found %.4X\n",BUFFER[sc],tmp);
 if (--REPEAT) goto BEGIN_READ;
 goto EXIT;
 };
 CHECKSUM+=tmp;
 if (keyz())
 {
 if (getch()==0x1B)
 {
 printf("\n pressed ...\n");
 goto EXIT;
 }
 }
 inc_addr();
 if ((GL_ADDR&0x3FF)==0) printf("%ld kB\r",GL_ADDR>>9);
 }
 printf("O.b. , CHECKSUM=%.4X\n",CHECKSUM);
// вобщем, если доползли сюда, то верификация прошла успешно
// сейчас начинается проверка объёма картриджа - ведь "засасывали" сразу 4Мб,
// а картридж мог оказаться размером 1Мб
 if (memcmp(BUFFER,&BUFFER[0x80000],0x100000)==0)
 {
 printf("Detect 1MB cartridge , out.bin truncated now ...\n");
 f_h=OpenFile("out.bin");
 SaveFile((uchar *)BUFFER,0x100000,f_h);
 CloseFile(f_h);
 goto EXIT;
 }
 if (memcmp(BUFFER,&BUFFER[0x100000],0x200000)==0)
 {
 printf("Detect 2MB cartridge , out.bin truncated now ...\n");
 f_h=OpenFile("out.bin");
 SaveFile((uchar *)BUFFER,0x200000,f_h);
 CloseFile(f_h);
 goto EXIT;
 }
EXIT:
// здесь гасится светодиод, которого нет на схеме, но который я прицепил к
// одному из выводов счётчиков: при чтении картриджа он красиво мигает
 GL_ADDR=0x2000;
 set_addr();// ура, светодиод погасили
 free(BUFFER);// освободили занятую под буфер память
 printf("Bye ...\n");// попрощались 
}

Программа была написана на WATCOM C 10.x под DOS4G/GW, но она отлично работает под Extender'ами PMODE/W V1.33, Dos32, CauseWay. Более того, эта программа по прошествии стольких лет отлично работает под WinXP. В те времена со считанными картриджами играли на эмуляторе KGen, а сейчас выбор значительно богаче - многое изменилось, а KGen дорос до WinGen-а

 

Материал форума сайта - http://www.anyram.net/forum/viewtopic.php?id=16


У вас есть интересный материал - (Коды, секреты, статьи, и даже стихи и др.) и его нет на сайте, и вы желаете его добавить. Мы будем вам признательны за наполнение сайта. Найдите кнопку Добавить. и добавьте то, что вам и гостям будет интересно и полезно. Желаем вам хорошего настроения и ностальгии - OldCityRetroGames.ru

 

Категория: Картриджи и Флеш-картриджи | Добавил: Emerald (07-Май-2014)
Просмотров: 1643 | Теги: Reader, картридж, Sega, Ридер | Рейтинг: 5.0/1
Похожие материалы:
Всего комментариев: 0
avatar

Музыка из игр:

Macross / Choujikuu Yousai Macross / Макросс (Воздушная Крепость) - Избранное Ecco the Dolphin / Дельфин Экко TaleSpin / Чудеса на Виражах 


Онлайн игры:

Поддержать.

 

Мой профиль


[ Управление профилем ]
Чат
Поделиться
Что смотрят
Статистика
Яндекс.Метрика

Геймеров на сайте: 15
Гостей: 15
Участников: 0

Сейчас на сайте:

Сегодня сайт посетили
Друзья сайта.
tmnttf best-football radiogameplay Вся правда о ретроиграх и не только! Скачать игры и программы - Торрентыч KinoLegenda

Все права на материал с сайта сохранены и принадлежат их авторам. Любое копирование текста с сайта должно сопровождаться прямой активной ссылкой на наш сайт. Все игры (в том числе и онлайн), эмуляторы, и другие программы и материалы, предоставляются на некоммерческой основе (бесплатно) игры скачанные с этого сайта, носят ознакомительный характер и после ознакомления должны быть удалены. Некоторые материалы доступны на картриджах и других легальных носителях, поэтому могут быть приобретены для частного использования.

OldCityRetroGames.ru © 2011 - 2016 Мобильная версия

%

Используются технологии uCoz