Лет десять тому назад достались мне предназначенные на помойку ISA-видеокарты от 286...486 машин. Видеокарты были опробованы и с тех пор пылились в ящике. Пару лет назад появилась у меня мысль, а не подключить ли такую видеокарту к микроконтроллеру? Вот об этом я и расскажу в статье.

Чтобы подключить старую ISA-видеокарту достаточно 8-битной шины данных и 20-битной шины адреса. Микроконтроллеры я люблю семейства AVR за их простоту, так что взял я Atmega16. Но вы можете взять любой удобный для вас — в этом случае у того же stm32 ножек точно хватит и без внешней обвязки. А вот у Atmega16 ножек на все эти шины не хватит, так что шина адреса была собрана на трёх ещё советских параллельных регистрах (у меня есть их большой запас) К588ИР1. Микроконтроллер по-очереди задаёт в этих трёх регистрах части адреса. Большего и не требуется.


На ISA-разъёме выходы этой схемы нужно подключить так:

+5,
+12,
GND,
REFRESH (притянут к +5В через резистор),
A0-A19,
D0-D7,
RESET,
MEMW,
MEMR,
IOW,
IOR,
ALE,
RDY,
AEN (подключается к GND).


На картинке красным я отметил требующие подключения контакты разъёма ISA.

Для некоторых видеокарт нужно подключить -5 В и -12 В (их придётся вам откуда-то взять — например, из источников TracoPower) и сигнал OSC (14.318 МГц) — его можно генерировать простейшим генератором на К155ЛН1. Другим видеокартам эти линии не требуются. Тут уж как повезёт. В общем, если на видеокарте соответствующая ножка на ISA висит в воздухе — её точно можно не подключать. Имейте в виду, потребление видеокартой по линии +5В довольно существенное — если вы будете использовать для питания что-то типа LM7805, то обязательно поставьте её на радиатор (лучше с вентилятором).

Лично у меня собранная конструкция выглядит вот так:

image

Осталось дело за малым — как-то инициализировать видеокарту и начать с ней работать. В интернете есть подобные проекты — я нашёл один (ссылка), откуда и взял код инициализации видеокарты Trident 9000i. В той же программе из интернета есть и код инициализации для Trident9000C, но в комментариях указано, что он не работает. Я проверил. Действительно не работает — на экране мусор и видеокарта не реагирует на запись данных в ОЗУ.

Видео работы (картинка передавалась по SPI на Atmega16 (как видите, на схеме эти линии оставлены свободными) через LPT-порт компьютера):


(В видео я оговорился — режим 320x200, а не 320x240)

Объединив данный модуль с оптической мышкой (статья про использование сенсора мышки) я получил вот это:



Если же у вас есть желание запустить любую имеющуюся видеокарту ISA, то для этого вам следует в интернете найти BIOS требуемой видеокарты (скажем, тут) и с помощью IDA дизассемблировать его. Там обычный X86 код. Только начинается он не с 0 адреса — там сигнатура (2 байта) и контрольная сумма (1 байт). Итого, начинать надо с 3-его байта. И последовательно выяснить в какие порты что нужно записать, чтобы карта заработала. Скажу честно, мне терпения не хватило, чтобы понять, что не так с Trident9000C.

Для работы с шиной ISA был написан модуль:

Модуль для работы с шиной ISA
//****************************************************************************************************
//основные системные функции для работы с шиной ISA
//****************************************************************************************************


//****************************************************************************************************
//макроопределения
//****************************************************************************************************

//шина адреса
#define ADDR_BUS_PORT PORTA
#define ADDR_BUS_DDR  DDRA

//переключение адреса A0-A7
#define ADDR_BUS_0_7_SELECT_PORT PORTC
#define ADDR_BUS_0_7_SELECT_DDR  DDRC
#define ADDR_BUS_0_7_SELECT      7

//переключение адреса A8-A15
#define ADDR_BUS_8_15_SELECT_PORT PORTC
#define ADDR_BUS_8_15_SELECT_DDR  DDRC
#define ADDR_BUS_8_15_SELECT      6

//переключение адреса A16-A19
#define ADDR_BUS_16_19_SELECT_PORT PORTC
#define ADDR_BUS_16_19_SELECT_DDR  DDRC
#define ADDR_BUS_16_19_SELECT      5

//шина данных
#define DATA_BUS_PORT PORTB
#define DATA_BUS_DDR  DDRB
#define DATA_BUS_PIN  PINB

//RESET
#define RESET_DDR  DDRD
#define RESET_PORT PORTD
#define RESET      0

//MEMW
#define MEMW_DDR  DDRD
#define MEMW_PORT PORTD
#define MEMW      1

//MEMR
#define MEMR_DDR  DDRD
#define MEMR_PORT PORTD
#define MEMR      2

//IOW
#define IOW_DDR  DDRD
#define IOW_PORT PORTD
#define IOW      3

//IOR
#define IOR_DDR  DDRD
#define IOR_PORT PORTD
#define IOR      4

//ALE
#define ALE_DDR  DDRD
#define ALE_PORT PORTD
#define ALE      5

//RDY
#define RDY_DDR  DDRD
#define RDY_PORT PORTD
#define RDY_PIN  PIND
#define RDY      6

//****************************************************************************************************
//прототипы функций
//****************************************************************************************************

void System_Init(void);//инициализация устройства
void System_SetAddr(unsigned long addr);//установить адрес на шину
void System_RESET_HI(void);//установить RESET в 1
void System_RESET_LO(void);//установить RESET в 0
void System_MEMW_HI(void);//установить MEMW в 1
void System_MEMW_LO(void);//установить MEMW в 0
void System_MEMR_HI(void);//установить MEMR в 1
void System_MEMR_LO(void);//установить MEMR в 0
void System_IOW_HI(void);//установить IOW в 1
void System_IOW_LO(void);//установить IOW в 0
void System_IOR_HI(void);//установить IOR в 1
void System_IOR_LO(void);//установить IOR в 0
void System_ALE_HI(void);//установить ALE в 1
void System_ALE_LO(void);//установить ALE в 0
void System_WaitReady(void);//ждать готовности устройства
void System_Out8(unsigned short port,unsigned char value);//записать в порт значение UInt8
void System_Out16(unsigned short port,unsigned short value);//записать в порт значение UInt16
unsigned char System_In8(unsigned short port);//считать из порта значение UInt8
unsigned short System_In16(unsigned short port);//считать из порта значение UInt16
void System_Poke8(unsigned long addr,unsigned char value);//записать в память значение UInt8
void System_Poke16(unsigned long addr,unsigned short value);//записать в память значение UInt16
unsigned char System_Peek8(unsigned long addr);//считать из памяти значение UInt8
unsigned short System_Peek16(unsigned long addr);//считать из памяти значение UInt16

//****************************************************************************************************
//реализация функций
//****************************************************************************************************


//----------------------------------------------------------------------------------------------------
//инициализация устройства
//----------------------------------------------------------------------------------------------------
void System_Init(void)
{
 //настраиваем порты
 DDRA=0;
 DDRB=0;
 DDRD=0;
 DDRC=0;

 ADDR_BUS_DDR=0xff;
 DATA_BUS_DDR=0;
 
 ADDR_BUS_0_7_SELECT_DDR|=(1<<ADDR_BUS_0_7_SELECT);
 ADDR_BUS_8_15_SELECT_DDR|=(1<<ADDR_BUS_8_15_SELECT);
 ADDR_BUS_16_19_SELECT_DDR|=(1<<ADDR_BUS_16_19_SELECT);
 
 RESET_DDR|=(1<<RESET);
 MEMW_DDR|=(1<<MEMW);
 MEMR_DDR|=(1<<MEMR);
 IOW_DDR|=(1<<IOW);
 IOR_DDR|=(1<<IOR);
 ALE_DDR|=(1<<ALE);
 RDY_DDR&=0xff^(1<<RDY);
 RDY_PORT|=1<<RDY;
 //задаём состояние портов  
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ADDR_BUS_0_7_SELECT_PORT)),[bit]"M"(ADDR_BUS_0_7_SELECT));
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ADDR_BUS_8_15_SELECT_PORT)),[bit]"M"(ADDR_BUS_8_15_SELECT));
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ADDR_BUS_16_19_SELECT_PORT)),[bit]"M"(ADDR_BUS_16_19_SELECT));
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(RESET_PORT)),[bit]"M"(RESET));
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(IOW_PORT)),[bit]"M"(IOW));
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(IOR_PORT)),[bit]"M"(IOR));
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(MEMW_PORT)),[bit]"M"(MEMW));
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(MEMR_PORT)),[bit]"M"(MEMR));
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ALE_PORT)),[bit]"M"(ALE));
}
//----------------------------------------------------------------------------------------------------
//установить адрес на шину
//----------------------------------------------------------------------------------------------------
void System_SetAddr(unsigned long addr)
{
 unsigned char al=addr&0xff;
 unsigned char am=(addr>>8)&0xff;
 unsigned char ah=(addr>>16)&0xff;

 System_ALE_LO();//снимаем сигнал ALE
 
 //ставим на шину адреса адрес
 ADDR_BUS_PORT=ah;
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ADDR_BUS_16_19_SELECT_PORT)),[bit]"M"(ADDR_BUS_16_19_SELECT));
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ADDR_BUS_16_19_SELECT_PORT)),[bit]"M"(ADDR_BUS_16_19_SELECT));

 ADDR_BUS_PORT=am;
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ADDR_BUS_8_15_SELECT_PORT)),[bit]"M"(ADDR_BUS_8_15_SELECT));
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ADDR_BUS_8_15_SELECT_PORT)),[bit]"M"(ADDR_BUS_8_15_SELECT));

 ADDR_BUS_PORT=al;
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ADDR_BUS_0_7_SELECT_PORT)),[bit]"M"(ADDR_BUS_0_7_SELECT));
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ADDR_BUS_0_7_SELECT_PORT)),[bit]"M"(ADDR_BUS_0_7_SELECT));
 
 System_ALE_HI();//ставим сигнал ALE
}
//----------------------------------------------------------------------------------------------------
//установить RESET в 1
//----------------------------------------------------------------------------------------------------
inline void System_RESET_HI(void)
{
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(RESET_PORT)),[bit]"M"(RESET));
}
//----------------------------------------------------------------------------------------------------
//установить RESET в 0
//----------------------------------------------------------------------------------------------------
inline void System_RESET_LO(void)
{
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(RESET_PORT)),[bit]"M"(RESET)); 
}
//----------------------------------------------------------------------------------------------------
//установить MEMW в 1
//----------------------------------------------------------------------------------------------------
inline void System_MEMW_HI(void)
{
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(MEMW_PORT)),[bit]"M"(MEMW));
}
//----------------------------------------------------------------------------------------------------
//установить MEMW в 0
//----------------------------------------------------------------------------------------------------
inline void System_MEMW_LO(void)
{
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(MEMW_PORT)),[bit]"M"(MEMW));
}
//----------------------------------------------------------------------------------------------------
//установить MEMR в 1
//----------------------------------------------------------------------------------------------------
inline void System_MEMR_HI(void)
{
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(MEMR_PORT)),[bit]"M"(MEMR));
}
//----------------------------------------------------------------------------------------------------
//установить MEMR в 0
//----------------------------------------------------------------------------------------------------
inline void System_MEMR_LO(void)
{
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(MEMR_PORT)),[bit]"M"(MEMR));
}
//----------------------------------------------------------------------------------------------------
//установить IOW в 1
//----------------------------------------------------------------------------------------------------
inline void System_IOW_HI(void)
{
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(IOW_PORT)),[bit]"M"(IOW));
}
//----------------------------------------------------------------------------------------------------
//установить IOW в 0
//----------------------------------------------------------------------------------------------------
inline void System_IOW_LO(void)
{
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(IOW_PORT)),[bit]"M"(IOW));
}
//----------------------------------------------------------------------------------------------------
//установить IOR в 1
//----------------------------------------------------------------------------------------------------
inline void System_IOR_HI(void)
{
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(IOR_PORT)),[bit]"M"(IOR));
}
//----------------------------------------------------------------------------------------------------
//установить IOR в 0
//----------------------------------------------------------------------------------------------------
inline void System_IOR_LO(void)
{
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(IOR_PORT)),[bit]"M"(IOR));
}
//----------------------------------------------------------------------------------------------------
//установить ALE в 1
//----------------------------------------------------------------------------------------------------
inline void System_ALE_HI(void)
{
 asm volatile ("sbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ALE_PORT)),[bit]"M"(ALE));
}
//----------------------------------------------------------------------------------------------------
//установить ALE в 0
//----------------------------------------------------------------------------------------------------
inline void System_ALE_LO(void)
{
 asm volatile ("cbi %[port],%[bit]"::[port]"I"(_SFR_IO_ADDR(ALE_PORT)),[bit]"M"(ALE));
}
//----------------------------------------------------------------------------------------------------
//ждать готовности устройства
//----------------------------------------------------------------------------------------------------
void System_WaitReady(void)
{ 
 asm volatile ("nop"::);
 asm volatile ("nop"::);
 while(1)
 {
  if (RDY_PIN&(1<<RDY)) break;
 }
}

//----------------------------------------------------------------------------------------------------
//записать в порт значение UInt8
//----------------------------------------------------------------------------------------------------
void System_Out8(unsigned short port,unsigned char value)
{
 //ставим на шину данных данные
 DATA_BUS_DDR=0xff;
 DATA_BUS_PORT=value;
 System_SetAddr(port);
 //даём сигнал записи
 System_IOW_LO();
 System_WaitReady();//ждём готовности
 System_IOW_HI();
 DATA_BUS_DDR=0;
}
//----------------------------------------------------------------------------------------------------
//записать в порт значение UInt16
//----------------------------------------------------------------------------------------------------
void System_Out16(unsigned short port,unsigned short value)
{
 System_Out8(port,value&0xff);
 System_Out8(port+1,(value>>8)&0xff);
}
//----------------------------------------------------------------------------------------------------
//считать из порта значение UInt8
//----------------------------------------------------------------------------------------------------
unsigned char System_In8(unsigned short port)
{
 unsigned char byte;
 System_SetAddr(port);
 DATA_BUS_DDR=0;
 //даём сигнал чтения
 System_IOR_LO();
 System_WaitReady();//ждём готовности
 byte=DATA_BUS_PIN;
 System_IOR_HI();
 return(byte);
}
//----------------------------------------------------------------------------------------------------
//считать из порта значение UInt16
//----------------------------------------------------------------------------------------------------
unsigned short System_In16(unsigned short port)
{
 unsigned short ret=System_In8(port+1);
 ret<<=8;
 ret|=System_In8(port);
 return(ret);
}
//----------------------------------------------------------------------------------------------------
//записать в память значение UInt8
//----------------------------------------------------------------------------------------------------
void System_Poke8(unsigned long addr,unsigned char value)
{
 //ставим на шину данных данные
 DATA_BUS_DDR=0xff;
 DATA_BUS_PORT=value;
 System_SetAddr(addr);
 //даём сигнал записи
 System_MEMW_LO();
 System_WaitReady();//ждём готовности
 System_MEMW_HI();
 DATA_BUS_DDR=0;
}
//----------------------------------------------------------------------------------------------------
//записать в память значение UInt16
//----------------------------------------------------------------------------------------------------
void System_Poke16(unsigned long addr,unsigned short value)
{
 System_Poke8(addr,value&0xff);
 System_Poke8(addr+1,(value>>8)&0xff);
}
//----------------------------------------------------------------------------------------------------
//считать из памяти значение UInt8
//----------------------------------------------------------------------------------------------------
unsigned char System_Peek8(unsigned long addr)
{
 unsigned char byte;
 System_SetAddr(addr);
 DATA_BUS_DDR=0;
 //даём сигнал чтения
 System_MEMR_LO();
 System_WaitReady();//ждём готовности
 byte=DATA_BUS_PIN;
 System_MEMR_HI();
 return(byte);
}
//----------------------------------------------------------------------------------------------------
//считать из памяти значение UInt16
//----------------------------------------------------------------------------------------------------
unsigned short System_Peek16(unsigned long addr)
{
 unsigned short ret=System_Peek8(addr+1);
 ret<<=8;
 ret|=System_Peek8(addr);
 return(ret);
}


Инициализация видеокарты Trident 9000i выполняется вот так:

Инициализация видеокарты Trident 9000i
//****************************************************************************************************
//функции для работы с видеокартой trident
//****************************************************************************************************


//****************************************************************************************************
//макроопределения
//****************************************************************************************************



//****************************************************************************************************
//прототипы функций
//****************************************************************************************************

CHIP_TYPE TRIDENT_Init(void);//тест видеокарты как Trident
unsigned char TRIDENT_TestInx2(unsigned short rg,unsigned char ix,unsigned char msk);//возвращает true, если бит MSK регистра PT по индексу RG равен 1
void TRIDENT_TR9000i_Init(void);//инициализация микросхемы Trident9000i
void TRIDENT_TR8900D_Init(void);//инициализация микросхемы Trident8900D
bool TRIDENT_Unit_594(void);//модуль 594
void TRIDENT_Unit_5A6(void);//модуль 5A6
bool TRIDENT_Unit_4D9(void);//модуль 4D9
void TRIDENT_Unit_51A(void);//модуль 51A
unsigned char TRIDENT_Unit_26A(void);//модуль 26A
void TRIDENT_Unit_179(void);//модуль 179
void TRIDENT_Unit_2EA(void);//модуль 2EA
unsigned char TRIDENT_Unit_292(void);//модуль 292

//****************************************************************************************************
//реализация функций
//****************************************************************************************************

//----------------------------------------------------------------------------------------------------
//тест видеокарты как Trident
//----------------------------------------------------------------------------------------------------
CHIP_TYPE TRIDENT_Init(void)
{ 
 VGA_Reset();//делаем сброс видеокарты
 CHIP_TYPE chiptype=UNKNOWN;//тип микросхемы пока что неизвестен
 
 //Trident VGA (за исключением 8800BR) может работать в 2 режимах:
 //старый режим, с окном 128k, отображаемый в памяти в адресах A000h - BFFFh
 //и новый режим с окном 64k, в памяти в адресах A000h - AFFFh.
 
 System_Out8(VGAENABLE_ADDR,0x00);
 System_Out8(0x46E8,0x16);//регистр управления включением видеосистемы (D3=0)
 System_Out8(0x46E9,0x00);
 System_Out8(0x0102,0x01);
 System_Out8(0x0103,0x00);
 System_Out8(0x46E8,0x0E);//регистр управления включением видеосистемы (D3=1)
 System_Out8(0x46E9,0x00);
 System_Out8(0x4AE8,0x00);
 System_Out8(0x4AE9,0x00);
 
 VGA_OutReg8(SEQ_ADDR,0x0B,0x00);//пробуем прочесть ID микросхемы старым способом
 unsigned char chip=System_In8(0x3C5);//читаем значение новым способом
 unsigned char old=VGA_InReg8(SEQ_ADDR,0x0E);
 System_Out8(0x3C5,0x00);
 unsigned char value=System_In8(0x3C5)&0x0F;
 System_Out8(0x3C5,old);
 //определяем тип микросхемы 
 if (value==2)
 {
  System_Out8(0x3C5,old^2);
  switch(chip)
  {
   case 0x01:
    chiptype=TR8800BR;
    break;
   case 0x02:
    chiptype=TR8800CS;
    break;
   case 0x03:
    chiptype=TR8900;
    break;
   case 0x04:
    chiptype=TR8900C;
    TRIDENT_TR8900D_Init();
    break;
   case 0x13:
    chiptype=TR8900C;
    TRIDENT_TR8900D_Init();
    break;
   case 0x23:
    chiptype=TR9000;
    TRIDENT_TR9000i_Init();
    break;
   case 0x33:
    chiptype=TR8900CLD;
    TRIDENT_TR8900D_Init();
    break;
   case 0x43:
    chiptype=TR9000i;
    TRIDENT_TR9000i_Init();
    break;
   case 0x53:
    chiptype=TR8900CXr;
    break;
   case 0x63:
    chiptype=LCD9100B;
    break;
   case 0x83:
    chiptype=LX8200;
    break;
   case 0x93:
    chiptype=TVGA9400CXi;
    break;
   case 0xA3:
    chiptype=LCD9320;
    break;
   case 0x73:
    chiptype=GUI9420;
	break;
   case 0xF3:
    chiptype=GUI9420;
	break;
  }
 }
 else 
 {
  if ((chip==1)&TRIDENT_TestInx2(SEQ_ADDR,0x0E,6)) chiptype=TVGA8800BR;
 }
 return(chiptype);
}

//----------------------------------------------------------------------------------------------------
//возвращает true, если бит MSK регистра PT по индексу RG равен 1
//----------------------------------------------------------------------------------------------------
unsigned char TRIDENT_TestInx2(unsigned short rg,unsigned char ix,unsigned char msk)
{
 unsigned char old,nw1,nw2;
 old=VGA_InReg8(rg,ix);
 VGA_OutReg8(rg,ix,old&(~msk));
 nw1=VGA_InReg8(rg,ix)&msk;
 VGA_OutReg8(rg,ix,old|msk);
 nw2=VGA_InReg8(rg,ix)&msk;
 VGA_OutReg8(rg,ix,old);
 return((nw1==0)&(nw2==msk));
}

//----------------------------------------------------------------------------------------------------
//инициализация микросхемы Trident9000i
//----------------------------------------------------------------------------------------------------
void TRIDENT_TR9000i_Init(void)
{
 unsigned short i=0;
 System_Out8(VGAENABLE_ADDR,0x00);
 if (TRIDENT_Unit_4D9()==false) return;//ошибка
 do
 {
  System_Out8(0x3C9,0x00);
  i++;
 }
 while (i<768);
 System_Out8(MISC_ADDR,0x23);
 TRIDENT_Unit_51A();
}
//----------------------------------------------------------------------------------------------------
//инициализация микросхемы Trident8900D
//----------------------------------------------------------------------------------------------------
void TRIDENT_TR8900D_Init(void)
{
 if (TRIDENT_Unit_594()==true)
 {
  //задаём палитру 256 цветов 
  System_Out8(0x3C8,0x00);
  for(unsigned short n=0;n<256;n++)
  {
   System_Out8(0x3C9,n);//R
   System_Out8(0x3C9,0);//G
   System_Out8(0x3C9,0);//B
  }
  System_Out8(MISC_ADDR,0x23);
  TRIDENT_Unit_5A6();
 }
 if (TRIDENT_Unit_594()==false)
 {
  System_In8(STATUS_ADDR);
  System_Out8(ATTRCON_ADDR,0x20);
  System_In8(STATUS_ADDR);
  VGA_OutReg8(CRTC_ADDR,0x020,VGA_InReg8(CRTC_ADDR,0x20)&0xDF);
 }
 System_Out8(0x3D8,0x00);
 VGA_OutReg8(CRTC_ADDR,0x23,0x10);
 System_Out8(0x3C6,0xff);
}
//----------------------------------------------------------------------------------------------------
//модуль 594
//----------------------------------------------------------------------------------------------------
bool TRIDENT_Unit_594(void)
{
 VGA_OutReg8(SEQ_ADDR,0x0B,0x00);//установка старого режима
 if ((VGA_InReg8(SEQ_ADDR,0x0D)&0x0E)!=0x0C) return(true);
 if ((System_In8(0x3CC)&0x67)!=0x67) return(true);
 return(false);
}
//----------------------------------------------------------------------------------------------------
//модуль 5A6
//----------------------------------------------------------------------------------------------------
void TRIDENT_Unit_5A6(void)
{
 unsigned char al,bh;

 VGA_InReg8(SEQ_ADDR,0x0B);//установка нового режима
 VGA_OutReg8(SEQ_ADDR,0x0E,(VGA_InReg8(SEQ_ADDR,0x0E)|0x80)^2);
 bh=(VGA_InReg8(SEQ_ADDR,0x0C)&0xFE)|0x80;
 if (VGA_InReg8(SEQ_ADDR,0x0B)==0x53)//установка нового режима
 {
  VGA_InReg8(CRTC_ADDR,0x29);
  VGA_OutReg8(CRTC_ADDR,0x29,0x44);
  VGA_OutReg8(CRTC_ADDR,0x2B,0x03);
  VGA_OutReg8(CRTC_ADDR,0x2C,0x3D);
  VGA_OutReg8(CRTC_ADDR,0x25,0x27);
 }
 if (!(!VGA_InReg8(CRTC_ADDR,0x28)&1))
 {
  bh&=0xCE;
  bh|=0x80;
  VGA_OutReg8(MISC_ADDR,0x01,0x00);//выбираем адрес 0x3D2 (в оригинале 0x01)
  al=(VGA_InReg8(CRTC_ADDR,0x28))&0x0C;
  if (al==0)
  {
   al|=0x04;
   VGA_OutReg8(CRTC_ADDR,0x28,al);
  }
  VGA_OutReg8(SEQ_ADDR,0x0F,VGA_InReg8(SEQ_ADDR,0x0F)&0x7F);
  VGA_InReg8(SEQ_ADDR,0x0C);
  VGA_OutReg8(SEQ_ADDR,0x0C,bh);
  VGA_OutReg8(SEQ_ADDR,0x0E,(VGA_InReg8(SEQ_ADDR,0x0E)&0x7F)^2);
  if (VGA_InReg8(SEQ_ADDR,0x0F)&0x08)
  {
   VGA_InReg8(SEQ_ADDR,0x0B);//установка нового режима
   VGA_OutReg8(SEQ_ADDR,0x0E,(VGA_InReg8(SEQ_ADDR,0x0E)|0x80)^2);
   al=(VGA_InReg8(SEQ_ADDR,0x0C)&0xFE)|0x80;
   al&=0xFE;
   VGA_OutReg8(SEQ_ADDR,0x0C,al);
   VGA_OutReg8(GRACON_ADDR,0x0F,0x00);
   al=VGA_InReg8(SEQ_ADDR,0x0C);//отключаем тестирование
   VGA_OutReg8(SEQ_ADDR,0x0F,VGA_InReg8(SEQ_ADDR,0x0F)|0x80);
   VGA_OutReg8(SEQ_ADDR,0x0E,(VGA_InReg8(SEQ_ADDR,0x0E)&0x7F)^0x02);   
  }
 } 
 VGA_OutReg8(SEQ_ADDR,0x0B,0x00);//старый режим
 VGA_OutReg8(SEQ_ADDR,0x0D,0x20);
 VGA_OutReg8(SEQ_ADDR,0x0E,0xA0);
 VGA_InReg8(SEQ_ADDR,0x0B);//установка нового режима
 VGA_OutReg8(SEQ_ADDR,0x0E,0x02);
 al=VGA_InReg8(GRACON_ADDR,0x06);//atvizsgбlni
 if (al==0)
 {
  al&=0xF3;
  al|=0x04;
  VGA_OutReg8(GRACON_ADDR,0x06,al);
 }
 VGA_OutReg8(SEQ_ADDR,0x0D,0x00);
 VGA_InReg8(CRTC_ADDR,0x1E);
 VGA_OutReg8(CRTC_ADDR,0x1E,0x00);
 if (VGA_InReg8(SEQ_ADDR,0x0B)==0x53) VGA_OutReg8(CRTC_ADDR,0x20,0x1D);//установка нового режима
                                  else VGA_OutReg8(CRTC_ADDR,0x20,0x1C);
 VGA_OutReg8(CRTC_ADDR,0x29,0x44);  
}

//----------------------------------------------------------------------------------------------------
//модуль 4D9
//----------------------------------------------------------------------------------------------------
bool TRIDENT_Unit_4D9(void)
{
 unsigned char al;
 al=TRIDENT_Unit_292()&0x0E;
 if (al!=0x0C) return(true);
 al=System_In8(0x3CC)&0x67;
 if (al!=0x67) return(true);
 return(false);
}
//----------------------------------------------------------------------------------------------------
//модуль 51A
//----------------------------------------------------------------------------------------------------
void TRIDENT_Unit_51A(void)
{
 unsigned char al,bh;
 bh=(TRIDENT_Unit_26A()|0x80)&0xFE;
 VGA_OutReg8(SEQ_ADDR,0x07,0x24);
 System_Out8(MISC_ADDR,0x01);
 if (!((al=VGA_InReg8(CRTC_ADDR,0x28))&0x0C))
 {
  al|=0x04;
  VGA_OutReg8(CRTC_ADDR,0x28,al);
 }
 VGA_OutReg8(SEQ_ADDR,0x0F,VGA_InReg8(SEQ_ADDR,0x0F)&0x7F);
 VGA_OutReg8(SEQ_ADDR,0x0C,bh);
 VGA_OutReg8(SEQ_ADDR,0x0E,(VGA_InReg8(SEQ_ADDR,0x0E)&0x7F)^2);
 if (VGA_InReg8(SEQ_ADDR,0x0F)&0x08) TRIDENT_Unit_179();
 TRIDENT_Unit_2EA();//старый режим
 VGA_OutReg8(SEQ_ADDR,0x0D,0x20);
 VGA_OutReg8(SEQ_ADDR,0x0E,0xA0);
 VGA_InReg8(SEQ_ADDR,0x0B);//новый режим
 VGA_OutReg8(SEQ_ADDR,0x0E,0x02);
 if (!((al=VGA_InReg8(GRACON_ADDR,0x06))&0x0C)) VGA_OutReg8(GRACON_ADDR,0x06,(al&0xF3)|0x04);
 VGA_OutReg8(SEQ_ADDR,0x0D,0x00);
 al=VGA_InReg8(CRTC_ADDR,0x1E);
 VGA_OutReg8(CRTC_ADDR,0x1E,0x00);
}
//----------------------------------------------------------------------------------------------------
//модуль 26A
//----------------------------------------------------------------------------------------------------
unsigned char TRIDENT_Unit_26A(void)
{
 VGA_InReg8(SEQ_ADDR,0x0B);//установка нового режима
 VGA_OutReg8(SEQ_ADDR,0x0E,(VGA_InReg8(SEQ_ADDR,0x0E)|0x80)^2);
 return(VGA_InReg8(SEQ_ADDR,0x0C));
}
//----------------------------------------------------------------------------------------------------
//модуль 179
//----------------------------------------------------------------------------------------------------
void TRIDENT_Unit_179(void)
{
 //настрйоки SP и BP
 VGA_OutReg8(SEQ_ADDR,0x0C,(TRIDENT_Unit_26A()|0x42)&0xFE);
 VGA_OutReg8(SEQ_ADDR,0x0F,VGA_InReg8(SEQ_ADDR,0x0F)|0x80);
 VGA_OutReg8(SEQ_ADDR,0x0E,(VGA_InReg8(SEQ_ADDR,0x0E)&0x7F)^2);
}
//----------------------------------------------------------------------------------------------------
//модуль 2EA
//----------------------------------------------------------------------------------------------------
void TRIDENT_Unit_2EA(void)
{
 VGA_OutReg8(SEQ_ADDR,0x0B,0x00);//установка старого режима
}
//----------------------------------------------------------------------------------------------------
//модуль 292
//----------------------------------------------------------------------------------------------------
unsigned char TRIDENT_Unit_292(void)
{
 TRIDENT_Unit_2EA();//установка старого режима
 return(VGA_InReg8(SEQ_ADDR,0x0D));//старый режим, читаем SEQ_ADDR 0x0D
}


Также я запускал и видеокарту OAK OTI077 (пока я случайно не подал на неё 12 В и она не сгорела):

Инициализация видеокарты OAK OTI077
//****************************************************************************************************
//функции для работы с видеокартой oak
//****************************************************************************************************


//****************************************************************************************************
//макроопределения
//****************************************************************************************************


//****************************************************************************************************
//глобальные переменные
//****************************************************************************************************

unsigned char OAK_InitData[77] PROGMEM=
{
 0x01,0x29,0x04,0x10,0x07,0x00,0x01,0x3E,
 0x00,0x00,0x80,0x70,0xFF,0x01,0xFF,0xFF,
 0x7F,0x00,0xFF,0xC8,0x00,0x00,0xFF,0x00,
 0x01,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
 0x01,0x00,0x08,0x00,0x00,0x00,0xFF,0xFF,
 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
 0x00,0x00,0x00,0x00,0x00,0x00,0xD8,0xFF,
 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,
 0x00,0x00,0x00,0x00,0x10
};


//****************************************************************************************************
//прототипы функций
//****************************************************************************************************

CHIP_TYPE OAK_Init(void);//тест видеокарты как Trident
unsigned char OAK_TestInx2(unsigned short rg,unsigned char ix,unsigned char msk);//возвращает true, если бит MSK регистра PT по индексу RG равен 1
void OAK_OTI077_Init(void);//инициализация OAK077
void OAK_OTI087_Init(void);//инициализация OAK087
void OAK_Unit218C(void);//модуль 218C
void OAK_Unit201B(void);//модуль 201B
void OAK_Unit50F(void);//модуль 50F
void OAK_Unit58F(unsigned char ah,unsigned char al);//модуль 58F
void OAK_Unit552(void);//модуль 552
unsigned char OAK_Unit53D(void);//модуль 53D
void OAK_Unit21A8(void);//модуль 21A8
void OAK_Fill3DE(void);//модуль Fill3DE
bool OAK_Unit21C9(void);//модуль 21C9
void OAK_Unit2167(unsigned short addr,unsigned char v);//модуль 2167
void OAK_Unit2148(unsigned char v);//модуль 2148
void OAK_Unit2A0F(void);//модуль 2A0F
void OAK_Unit28E2(unsigned char al,unsigned char ah);//модуль 28E2
void OAK_Unit28AC(unsigned char al,unsigned char ah);//модуль 28AC
void OAK_Unit29E0(void);//модуль 29E0
unsigned char OAK_Unit1CED(void);//модуль 1CED
void OAK_Unit1E12(void);//модуль 1E12
void OAK_Unit292D(unsigned char al,unsigned char bl,unsigned char bh);//модуль 292D

//****************************************************************************************************
//реализация функций
//****************************************************************************************************

//----------------------------------------------------------------------------------------------------
//тест видеокарты как Trident
//----------------------------------------------------------------------------------------------------
CHIP_TYPE OAK_Init(void)
{ 
 CHIP_TYPE chiptype=UNKNOWN;
 VGA_Reset();
 
 System_Out8(0x46E8,0x17);
 System_Out8(0x0102,System_In8(0x0102)|1);
 System_Out8(0x46E8,0x0F);
 if (OAK_TestInx2(0x3DE,0x0D,0x38))
 {
  if (OAK_TestInx2(0x3DE,0x23,0x1F))
  {
   if((VGA_InReg8(0x3DE,0x00)&0x02)==0)
   {
    chiptype=OAK_087;
    OAK_OTI087_Init();
   }
   else chiptype=OAK_083;
  }
  else
  {
   switch(System_In8(0x3DE)/32)
   {
    case 0:
     chiptype=OAK_037C;
     break;
    case 2:
     chiptype=OAK_067;
     break;
    case 5:
     chiptype=OAK_077;
     OAK_OTI077_Init();
     break;
    case 7:
     chiptype=OAK_057;
	 break;
   }
  }
 }
 return(chiptype);
}


//----------------------------------------------------------------------------------------------------
//возвращает true, если бит MSK регистра PT по индексу RG равен 1
//----------------------------------------------------------------------------------------------------
unsigned char OAK_TestInx2(unsigned short rg,unsigned char ix,unsigned char msk)
{
 unsigned char old,nw1,nw2;
 old=VGA_InReg8(rg,ix);
 VGA_OutReg8(rg,ix,old&(~msk));
 nw1=VGA_InReg8(rg,ix)&msk;
 VGA_OutReg8(rg,ix,old|msk);
 nw2=VGA_InReg8(rg,ix)&msk;
 VGA_OutReg8(rg,ix,old<<8);
 return((nw1==0)&(nw2==msk));
}
//----------------------------------------------------------------------------------------------------
//инициализация OAK087
//----------------------------------------------------------------------------------------------------
void OAK_OTI087_Init(void)
{
 System_Out8(0x320,1);
 System_In8(0x320);
 OAK_Unit218C();
 VGA_OutReg8(0x3DE,0x13,VGA_InReg8(0x3DE,0x13)&0xBF);
 VGA_OutReg8(0x3DE,0x0B,((VGA_InReg8(0x3DE,0x0B)&0x0F)|(VGA_InReg8(0x3DE,0x10)))&0xF0);
 OAK_Unit201B();
 OAK_Unit50F();
 OAK_Unit552();
 System_Out8(0x320,2);
 OAK_Fill3DE(); 
 OAK_Unit21A8();
 OAK_Unit218C();
 System_Out8(0x320,0x0c);
 System_Out8(0x46E8,0x17);
 System_Out8(0x0102,System_In8(0x0102)|1);
 System_Out8(0x46E8,0x0F);
 OAK_Fill3DE();
}
//----------------------------------------------------------------------------------------------------
//инициализация OAK077
//----------------------------------------------------------------------------------------------------
void OAK_OTI077_Init(void)
{
 OAK_Unit2A0F();
 VGA_OutReg8(0x3DE,0x0E,VGA_InReg8(0x3DE,0x0E)&0xDE);
 OAK_Unit1E12();
 System_Out8(0x46E8,0x17);
 System_Out8(0x0102,System_In8(0x0102)|1);
 System_Out8(0x46E8,0x0F);
}
//----------------------------------------------------------------------------------------------------
//модуль 218C
//----------------------------------------------------------------------------------------------------
void OAK_Unit218C(void)
{
 OAK_Unit2148(0x0F);
 if (OAK_Unit21C9()==false) return;
 OAK_Unit2167(0x0094,0x01);
 if (OAK_Unit21C9()==false) return;
 OAK_Unit2167(0x0394,0x01);
 return;
}
//----------------------------------------------------------------------------------------------------
//модуль 201B
//----------------------------------------------------------------------------------------------------
void OAK_Unit201B(void)
{
 unsigned char ah;
 System_In8(0x3C8);
 ah=System_In8(0x3C6);
 System_In8(0x3C8);
 System_In8(0x3C6);
 System_In8(0x3C6);
 System_In8(0x3C6);
 System_In8(0x3C6);
 System_Out8(0x3C6,0);
 System_In8(0x3C8);
 System_Out8(0x3C6,ah);
 System_In8(0x3C8);
}
//----------------------------------------------------------------------------------------------------
//модуль 50F
//----------------------------------------------------------------------------------------------------
void OAK_Unit50F(void)
{
 if ((VGA_InReg8(0x3DE,0x07)&0x04)) return;
 OAK_Unit58F(0xFF,0x05);
}
//----------------------------------------------------------------------------------------------------
//модуль 58F
//----------------------------------------------------------------------------------------------------
void OAK_Unit58F(unsigned char ah,unsigned char al)
{
 System_Out8(0x1E,al);
 System_Out8(0x1F,ah);
}
//----------------------------------------------------------------------------------------------------
//модуль 552
//----------------------------------------------------------------------------------------------------
void OAK_Unit552(void)
{
 if (OAK_Unit53D()) return;
 OAK_Unit58F(0x07,0x04);
 OAK_Unit58F(0xFF,0x05);
 OAK_Unit58F((VGA_InReg8(0x1E,0xFE)|0x40),0xFE);
}
//----------------------------------------------------------------------------------------------------
//модуль 53D
//----------------------------------------------------------------------------------------------------
unsigned char OAK_Unit53D(void)
{
 if (VGA_InReg8(0x3DE,0x07)&0x04) return(1);
 return(VGA_InReg8(0x3DE,0x08)&0x03);
}
//----------------------------------------------------------------------------------------------------
//модуль 21A8
//----------------------------------------------------------------------------------------------------
void OAK_Unit21A8(void)
{
 if (VGA_InReg8(0x3DE,0x07)&0x04) OAK_Unit2148(0x00);
 else
 {
  OAK_Unit2167(0x0094,0x00);
  OAK_Unit2167(0x0394,0x00);
 }
}
//----------------------------------------------------------------------------------------------------
//модуль Fill3DE
//----------------------------------------------------------------------------------------------------
void OAK_Fill3DE(void)
{
 unsigned char i;
 for(i=0;i<77;i++)
 {
  unsigned short byte=pgm_read_byte(&(OAK_InitData[i]));
  VGA_OutReg8(0x3DE,i,byte);
 }
}
//----------------------------------------------------------------------------------------------------
//модуль 21C9
//----------------------------------------------------------------------------------------------------
bool OAK_Unit21C9(void)
{
 unsigned char al=0x55,ah=al;
 while(1)
 {
  System_Out8(0x3DE,al);
  if (al==ah) return(true);
  al^=0xFF;
  ah^=0xFF;
  if (al==0x55 && ah==0x55) return(false);
 }
}
//----------------------------------------------------------------------------------------------------
//модуль 2167
//----------------------------------------------------------------------------------------------------
void OAK_Unit2167(unsigned short addr,unsigned char v)
{
 unsigned char ah;
 ah=System_In8(addr);
 System_Out8(addr,0x80);
 System_Out8(0x102,0x01);
 System_Out8(addr,ah);
 System_Out8(0x3C3,v);
}
//----------------------------------------------------------------------------------------------------
//модуль 2148
//----------------------------------------------------------------------------------------------------
void OAK_Unit2148(unsigned char v)
{
 System_Out8(0x46E8,0x17);
 System_Out8(0x0102,System_In8(0x0102)|1);
 System_Out8(0x46E8,v);
}
//----------------------------------------------------------------------------------------------------
//модуль 2A0F
//----------------------------------------------------------------------------------------------------
void OAK_Unit2A0F(void)
{
 OAK_Unit28E2(0,8);
 OAK_Unit28AC(0,0xBE);
 OAK_Unit29E0();
 OAK_Unit292D(0,0x11,0x06);
 OAK_Unit292D(1,0x11,0x07);
 OAK_Unit292D(2,0x06,0x1F);
 OAK_Unit292D(3,0x06,0x15);
}
//----------------------------------------------------------------------------------------------------
//модуль 28E2
//----------------------------------------------------------------------------------------------------
void OAK_Unit28E2(unsigned char al,unsigned char ah)
{
 unsigned char bl=al,bh=ah;
 VGA_OutReg8(0x3DE,0x0D,VGA_InReg8(0x3DE,0x0D)|0x20);
 System_Out8(0x3C7,0x0E);
 al=System_In8(0x3C9)&bh;
 ah^=0xFF;
 bl&=ah;
 bl|=al;
 System_Out8(0x3C8,0x0E);
 System_Out8(0x3C9,bl);
 VGA_OutReg8(0x3DE,0x0D,VGA_InReg8(0x3DE,0x0D)&0xDF);
}
//----------------------------------------------------------------------------------------------------
//модуль 28AC
//----------------------------------------------------------------------------------------------------
void OAK_Unit28AC(unsigned char al,unsigned char ah)
{
 unsigned char bl=al;
 VGA_OutReg8(0x3DE,0x0D,VGA_InReg8(0x3DE,0x0D)|0x20);
 al=System_In8(0x3C6)&ah;
 ah^=0xFF;
 bl&=ah;
 al|=bl;
 System_Out8(0x3C6,al);
 VGA_OutReg8(0x3DE,0x0D,VGA_InReg8(0x3DE,0x0D)&0xDF);
}
//----------------------------------------------------------------------------------------------------
//модуль 29E0
//----------------------------------------------------------------------------------------------------
void OAK_Unit29E0(void)
{
 if ((System_In8(0x3DE)&0xE0)==0xA0)
 {
  if (!(OAK_Unit1CED()&0x20)) OAK_Unit292D(0x0A,0x02,0x0C);
                          else OAK_Unit292D(0x0A,0x03,0x0D);
 }
 else OAK_Unit292D(0x0A,0x03,0x0D);
}
//----------------------------------------------------------------------------------------------------
//модуль 1CED
//----------------------------------------------------------------------------------------------------
unsigned char OAK_Unit1CED(void)
{
 return((VGA_InReg8(0x3DE,0x10)&0x4F)|(VGA_InReg8(0x3DE,0x0B)&0xB0));
}
//----------------------------------------------------------------------------------------------------
//модуль 1E12
//----------------------------------------------------------------------------------------------------
void OAK_Unit1E12(void)
{
 System_Out8(0x46E8,0x17);
 System_Out8(0x0102,System_In8(0x0102)|1);
 System_Out8(0x46E8,0x00);
}
//----------------------------------------------------------------------------------------------------
//модуль 292D
//----------------------------------------------------------------------------------------------------
void OAK_Unit292D(unsigned char al,unsigned char bl,unsigned char bh)
{
 VGA_OutReg8(0x3DE,0x0D,VGA_InReg8(0x3DE,0x0D)|0x20);
 System_Out8(0x3C8,al);
 System_Out8(0x3C9,bh);
 System_Out8(0x3C9,bl);
 VGA_OutReg8(0x3DE,0x0D,VGA_InReg8(0x3DE,0x0D)&0xDF);
}


Кстати, а нет ли тут специалистов по регистрам адаптера VGA? Я вижу в коде смены видеорежима при инициализации видеокарты странные вещи:

//регистры контроллера атрибутов
#define ATTRCON_ADDR 0x03C0

System_In8(0x03DA);
System_Out8(ATTRCON_ADDR,0x20);

Тут в общем-то, ничего особого нет. Запись в регистры контроллера атрибутов делается за 2 шага: записываем сперва номер регистра, а затем данные. Чтобы всегда начинать с записи номера, читаем ISR1 (из 0x03DA) — так сделано.

Но вот что странно. У контроллера атрибутов нет регистра 0x20! У него регистр последний 0x14. И даже если такой регистр бы был, почему нет записи значения? Должно же быть две записи в порт. А тут она одна. Я поискал в интернете и нашёл, что почему-то (в книгах я этого не нашёл) можно записать, скажем, в регистр 0x10 значение 0x20 за один раз просто объединив биты: System_Out8(ATTRCON_ADDR,0x10|0x20); Тогда указанная запись пишет 0x20 в регистр 0x00? Но почему это работает? И так ли это? Мне это интересно вот почему — всё дело в том, что иногда отваливается цвет после инициализации. Палитру просто не задать. Видно, что она меняется, но цвета вовсе не те, что должны быть. Если инициализацию сделать повторно, то всё восстанавливается. На каком этапе это происходит — не понятно. Экспериментально я выяснил, что с большой вероятностью, это как раз установка видеорежима. Но вот что там именно не то, я не понимаю.

> Ссылка на архив с печатной платой
> Ссылка на архив с прошивкой

Комментарии (45)


  1. hssergey
    24.08.2017 14:28
    +1

    А в текстовом режиме не пробовали ее завести?


    1. dlinyj
      24.08.2017 17:24

      Ниже ответили :)


  1. da-nie Автор
    24.08.2017 14:29
    +1

    Нет, не пробовал. Мне хотелось графики. :) Текст можно и без ISA-видеокарты выводить легко. Но вот по ссылке, откуда я взял код инициализации, там человек заводил в разных режимах.


  1. Vitalley
    24.08.2017 14:51
    +3

    Не плохо было бы вместо регистра адреса счётчик добавить, что бы легко можно было инкрементировать адрес, когда пишешь пакетом, так быстрее будет работа с картой.


    1. da-nie Автор
      24.08.2017 14:55

      Верно. :) Есть счётчики с установкой значения — они бы отлично подошли.


      1. sim2q
        24.08.2017 21:47
        +1

        ИЕ7 :)
        ps вот так и рождается GPU :)


  1. scg
    24.08.2017 14:51

    Неточность в видео: VGA режим 0x13 — это 320x200


    1. da-nie Автор
      24.08.2017 15:03

      Верно. На youtube в описании я указал, что оговорился. :) Надо и тут написать.


      1. scg
        24.08.2017 22:37
        +1

        А в целом, статья интересная, спасибо. Для меня было новостью, что 16-ти битная ISA карта может работать в 8-ми битном режиме.


  1. madf
    24.08.2017 16:58
    +1

    вообще нет смысла так медленно работать с графикой и технически неоправданно (ибо надо ещё найти такую завалявшуюся видиюху)
    смысл видео карты, убыстрять процессы, а не на оборот
    для слабых МК надо специальные решения


    1. rstepanov
      24.08.2017 17:08
      +1

      Слабые МК надо просто выбрасывать. STM32F030 не сказать чтобы дорого стоит, а скорость обеспечивает на порядок быстрее.


      1. golf2109
        24.08.2017 17:33

        как активный пользователь STM32F030 (уважающий старые микроконтроллеры) скажу —
        ну не на порядок, а в разы


        1. rstepanov
          24.08.2017 17:46

          На 320х240@16bit (ILI9341) у меня что-то около 20 fps получалось, сможете на атмеге хотя бы 5 fps? Большие STM32 c FSMC конечно повеселее работают, но и так неплохо…


          1. Gordon01
            25.08.2017 08:43

            Отображаю >60 fps на ssd1306 на xmega128.
            Просто использую DMA


            1. rstepanov
              25.08.2017 09:23
              +2

              ssd1306 — однобитный, с разрешением 128х64. Полный кадр занимает всего 1 кб (против 150 кб на ILI9341) При максимальной скорости(для ssd1306) 10 Мгц на SPI шине на нем легко получить несколько сотен fps. Так что 60 fps — как то жидко, что с DMA, что без DMA.


              1. rstepanov
                25.08.2017 09:37
                +1

                Вдогонку, частота GPIO у STM32F0 — 2 Мгц, чтобы залить один пиксель при параллельном подключении ILI9341 нужно 2 раза переключить WR, получаем где-то 1 Мпикс/с, для ssd1306@10МГц — где-то 12 Мпикс/с.


    1. da-nie Автор
      24.08.2017 17:14
      +1

      В смысле, медленно? Вы не смотрите, что там на видео картинка медленно рисуется — тут не видеокарта и не микроконтроллер виноваты — тут передача данных сделана через LPT-порт компьютера, который эмулирует SPI с использованием библиотеки inpout32.h — она позволяет напрямую дёргать ножками LPT-порта в Windows, но делает это весьма небыстро. Отсюда и медленная скорость передачи данных. Микроконтроллер, конечно, тоже не очень быстр — у него частота всего 16 МГц, но если взять мощный STM32 последних серий…

      А так — смысл видеокарты — отображать картинку. Эти видеокарты не имеют на борту ускорителей, что вполне логично — они конца 80 начало 90 годов. Зато с помощью такой карты вполне себе прилично идёт Doom на старом компьютере типа i486. А это довольно хороший FPS. То есть, активировав такую видеокарту с микроконтроллера можно с неменьшей скоростью рисовать всю нужную графику с множеством цветов (для VGA — максимум 256 цветов), что часто и требуется.


      1. Vitalley
        24.08.2017 19:25

        Вроде бы данные видеокарты и 16 битный цвет держат, возможно для больших разрешений надо ещё памяти добавить.


  1. golf2109
    24.08.2017 17:12
    +1

    не отрицая познавательности вашей информации, всё же спрошу —
    если не секрет, а в чем практический смысл данной работы?


    1. da-nie Автор
      24.08.2017 17:20
      +3

      Для меня он был в применении старой видеокарты. :) Как видите, оказалось удобно подключить к этому блоку сенсор от мышки и смотреть на экране, что же он видит. Также можно подключить всё что угодно — было бы желание. :)
      Да и вообще люблю я старую электронику. :)

      А так — делал я и VGA видеокарту на atmeag48. Просто из интереса. Можно и на FPGA сделать — там же просто нужно времена счётчиками отработать, а ЦАП'ом выдать сигнал.


      1. daggert
        24.08.2017 17:49

        А так — делал я и VGA видеокарту на atmeag48.

        А можете рассказать? Интересная тема.


        1. da-nie Автор
          24.08.2017 17:56
          +3

          Могу. Вот ссылка. :)
          А вот как работает:


          1. daggert
            26.08.2017 23:57
            +1

            Завидую по белому вашему энтуазизму и опыту (:


            Спасибо что воодушевили меня на пару дней!


            1. da-nie Автор
              27.08.2017 15:52
              +1

              Не завидуйте. :) Тут опыта нет. Это жутчайший примитив — это вам любой FPGA-шник скажет.


      1. golf2109
        24.08.2017 17:49
        +1

        вот как это все просто делается на STM32
        www.artekit.eu/vga-output-using-a-36-pin-stm32


        1. nafikovr
          24.08.2017 19:21
          +3

          и рисованием занята сама stm32. ну и для размещения видеопамяти придется взять далеко не самую бюджетную stm.


  1. dlinyj
    24.08.2017 17:26
    +1

    Отличная статья. Много лет думал ISA-видюхи так помучать, да руки так и не дошли. Спасибо!


    1. da-nie Автор
      24.08.2017 17:27

      Пожалуйста. :)


  1. stalinets
    24.08.2017 21:53
    +2

    У меня есть второй пенёк, там есть все слоты — и ISA, и AGP, и PCI.
    На нём сейчас стоит WinXP.
    Нашёл такую ISAшную видеокарту (1990-й год, вроде 512 кб видеопамяти), решил попробовать, будет ли работать. Работает, но вот загрузка компьютера почему-то стала идти втрое дольше, чем с AGP- или PCI-видеокартой.
    Между окончанием теста BIOS и стартом винды проходит секунд 30-40.
    Ну, и ещё очень прикольно выглядит запуск какого-нибудь видеоролика с очень низким FPS и в режиме 16 цветов.
    И вообще интерфейс винды отрисовывается на этой видеокарте неспешно, успеваешь заметить отрисовку всяких элементов окон.

    Как-нибудь руки дойдут — поставлю на этот ПК Win98 и ещё добавлю ISAшную звуковуху и ISAую сетевую карту с BNC-разъёмом, там как раз 3 слота ISA. Будет чудный комп))


    1. nafikovr
      24.08.2017 22:35

      ISA не PnP шина. возможно при наличии устройств на ISA умный BIOS (уже эпохи PnP) что то пытается просканировать.


    1. da-nie Автор
      25.08.2017 07:32
      +1

      Странно. У меня есть видео загрузки AMD 486DX2-66 с 8 МБ ОЗУ, этой видеокартой и Windows 95. :)
      Вот:


      1. Vitalley
        25.08.2017 11:24
        +1

        Так тут VLB видеокарта же!


        1. da-nie Автор
          25.08.2017 19:04
          +1

          Извиняюсь. :) Действительно — забыл за давностью лет. Но с Trident 9000i всё точно с такой же скоростью грузится. :)


  1. sim2q
    24.08.2017 22:38

    А подсмотреть в ядре Линукса драйвера?


    1. da-nie Автор
      25.08.2017 19:36
      +1

      Вряд ли поможет. Дело в том, что карты инициализируются самостоятельно — для этого у них стоит BIOS на плате. Собственно, поэтому раньше, когда Mac был не на Intel, карты с PC на Mac не работали и наоборот — в этой микросхемке должен быть код под нужный процессор и в нужных адресах. При запуске BIOS вызывает этот код, и карта сама себя запускает. А вот дальше уже грузится Linux, Windows и т.п. системы и они дальше уже работают с картой либо через стандартные VGA-SVGA режимы, либо используют данные для конкретной платы.


      1. sim2q
        25.08.2017 22:09

        Когда откомментил — сам засомневался уже, спасибо, что объяснили подробней, я то глубже чем LPT подёргать на linux ничего не пробовал


  1. pwl
    24.08.2017 22:45
    +2

    Но вот что странно. У контроллера атрибутов нет регистра 0x20! У него регистр последний 0x14. И даже если такой регистр бы был, почему нет записи значения? Должно же быть две записи в порт. А тут она одна.

    3C0h:  Attribute Controller: Address register
    bit 0-4  Address of data register to write to port 3C0h or read from port 3C1h
          5  If set screen output is enabled and the palette can not be modified,
    if clear screen output is disabled and the palette can be modified.

    Отсюда: https://github.com/johnsoft/tower-pc/blob/master/docs/video/vgadoc4b/VGAREGS.TXT
    (там кстати есть еще файлы с описанием регистров конкретных видеокарт, и трайдентов в частности)


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


    Я поискал в интернете и нашёл, что почему-то (в книгах я этого не нашёл) можно записать, скажем, в регистр 0x10 значение 0x20 за один раз просто объединив биты: System_Out8(ATTRCON_ADDR,0x10|0x20); Тогда указанная запись пишет 0x20 в регистр 0x00? Но почему это работает? И так ли это?

    Скорее всего вы спутали это с двухбайтовым out-ом. типа такого:


    mov dx, 3d4h
    mov ax, 5501h
    out dx,ax

    это позволяет записать в регистр 1 значение 55.
    01 запишется в 3d4
    55 в 3d5
    С портом 3C0 такое не пройдет, т.к. индекс и значение пишутся в один порт.


    1. da-nie Автор
      25.08.2017 07:34
      +1

      Огромное спасибо! Надо попробовать достать снова эту игрушку и поиграть с инициализацией.


  1. vitalyvitaly
    25.08.2017 09:04
    +1

    Насколько я помню, у Трайдента 8900 по жизни была проблема с «отвалом цветов» на SVGA-мониторах — иногда после загрузки включался черно-белый режим. Исправлялось в те времена какой-то очень простой утилитой с названием вроде vgacolor.com


    1. Bzjick
      26.08.2017 20:21

      или хардварное решение: ногу в VGA разъеме от земли откусывали))


  1. ayden889
    25.08.2017 09:36
    -1

    Для некоторых видеокарт нужно подключить -5 В и -12 В и сигнал OSC его можно генерировать простейшим генератором на К155ЛН1
    javascript obfuscator


  1. igorkozinov
    25.08.2017 11:08

    Мдя… такое ощущение, что микроконтроллер отсылает данные на видюху почтой россии…


    1. Vitalley
      25.08.2017 11:27
      +1

      Он так медленно получает. Добавить бы в регистры инкремент. Да и видюху поменять, насколько знаю, циррус лоджики были тогда самые быстрые, а трайдент такой есть, он даже не можер реализовать всю скорость ISA шины.


      1. da-nie Автор
        25.08.2017 19:15

        Да, цайрус быстрый был. У меня десять лет назад была и WD какая-то. Но в том-то и дело, что я, когда это всё заполучил, часть пустил на детали. Ой, дурак! :( И остались только трайдент и вот эта OKI да ещё несколько EGA с чипом Paradise — эти даже пробовать не хочу — не к чему EGA подключать, да и 16 цветов совсем несерьёзно. О! Вспомнил. Есть ещё одна у меня — там EGA+VGA видеовыход. Чистая 8 бит. Но марку не помню — сейчас все карты и матплаты лежат в коробке, которая заставлена столом и прочим, так что и не достать.

        А вот если stm32 поставить, то что-то мне кажется, нужно контакты с поддержкой 5 В подключать — шина всё-таки 5 В и stm32 там не только писать на неё будет.


  1. shell4692
    25.08.2017 19:05

    А я как-то на досуге придумывал схему моста шины ISA на CPLD для stm32f103. Много разного железа валяется, хочется заюзать его в своих проектах. Да и текстовый режим рулит!