| Tronix |
29th October 2013 15:52 |
На досуге ковырялся с PCem... Хочется все-таки сделать эмулятор с соблюдением тактов 8088, чтоб по скорости было максимально похоже на Поиск. Ну и чтоб работал "честно", то есть рисовал все символы программно, как это делает реал.
Пытался впилить в него обработку NMI. BTW, обработки NMI практически ни у кого нет, ни у fake86, ни у PCE. Вообще, хорошо если TRAP (int 1) обрабатывают, некоторые и на него забивают. Ввел несколько переменных и флагов:
Code:
// POISK variables
uint16_t p28h; // port 28 value
uint8_t nmi_enabled; // NMI enabled ?
uint8_t nmi_int_now=0; // force NMI
uint8_t in_nmi=0; // in NMI?
uint8_t p6Ah,p68h; // port 6Ah and port 68h
Дальше vid_cga.c. Откуда-то скопипастил такую процедуру установки графического режима в CGA через порты. В качестве аргумента (mode) - 0x1E это по идее 640x400, 0x2E - это 320x200.
Code:
static int mode_graph[] = {0x38, 0x28, 0x2D, 0x0A, 0x7F, 0x06,
0x64, 0x70, 2, 1, 6, 7,0, 0, 0, 0};
void set_poisk_mode(uint8_t mode)
#define MODE_REG 0x3d8
#define VID_DISABLE 0
#define ADDRESS_REG 0x3d4
#define REGISTER_PORT 0x3d5
{
int loop_count = 0;
cga_out(MODE_REG,VID_DISABLE); /*disable video signal */
for( ; loop_count < 0x0e; ++loop_count) {
cga_out( ADDRESS_REG, loop_count ); /* set up CRT register */
cga_out( REGISTER_PORT, mode_graph[loop_count]); /* write to reg */
}
cga_out( MODE_REG, mode); /* switch in mode now */;
}
Процедура обработки порта 68. Смотрит, не сменился ли режим, если сменился - устанавливает новый. Также устанавливает флаг разрешения или запрещения NMI.
Code:
void poisk_out(uint16_t addr, uint8_t val)
{
uint8_t b;
uint8_t tmp_b;
switch (addr)
{
case 0x68:
p68h = val;
b = 0x2e; //320x200
if (val & 0x80) b = 0x1e; //hires
if (poisk_mode != b)
{
poisk_mode = b;
set_poisk_mode(b);
pclog("POISK set mode %02X: \n",b);
}
if (!(val & 8))
{
nmi_enabled = 1;
pclog("[poisk] NMI enable\n");
}
else
{
nmi_enabled = 0;
pclog("[poisk] NMI disable\n");
}
pclog("POISK 68h value: %02X cgamode: %02X cgacol: %02X\n",val,tmp_b,b);
return;
case 0x6A:
p6Ah = val;
val = p68h;
// TO DO:
return;
}
}
uint8_t poisk_in(uint16_t addr)
{
switch (addr)
{
case 0x68:
return p68h;
case 0x6A:
return p6Ah;
}
return 0xFF;
}
При возникновении NMI в порт 28 должно находится смещение, куда произошла запись:
Code:
uint16_t p28h_in(uint16_t addr)
{
switch (addr)
{
case 0x28:
{
pclog("[poisk] IN 0x28h = %04X\n",(p28h) & 0xFF);
return ((p28h) & 0xFF);
}
case 0x29:
{
pclog("[poisk] IN 0x29h = %04X\n",(p28h>>8) & 0xFF);
return ((p28h>>8) & 0xFF);
}
}
}
Дальше, установил новые хендлеры для портов:
Code:
io_sethandler(0x0068, 0x0002, poisk_in, NULL, NULL, poisk_out, NULL, NULL);
io_sethandler(0x006A, 0x0002, poisk_in, NULL, NULL, poisk_out, NULL, NULL);
io_sethandler(0x0028, 0x0002, p28h_in, NULL, NULL, NULL, NULL, NULL);
Если что-то пишет в видео-память, проверить разрешены ли NMI, если разрешены - запомнить смещение куда произошла запись (для обработки 28 порта) и установить флаг вызова NMI.
Code:
void cga_write(uint32_t addr, uint8_t val)
{
// pclog("CGA_WRITE %04X %02X\n", addr, val);
vram[addr&0x3FFF]=val;
charbuffer[ ((int)(((dispontime - vidtime) * 2) / CGACONST)) & 0xfc] = val;
charbuffer[(((int)(((dispontime - vidtime) * 2) / CGACONST)) & 0xfc) | 1] = val;
cycles -= 4;
p28h = addr&0x3FFF;
if (nmi_enabled == 1)
{
flags&=~I_FLAG;
nmi_int_now = 1;
}
}
Дальше в x86.c обработку NMI сделал по аналогии обработки IRQ с PIC, вставил такой текст:
Code:
if (nmi_int_now && !in_nmi && !ssegs && !noint) // NMI interrupt
{
pclog("NMI INT NOW\n");
//if (ssegs) ss=oldss;
// if (inhlt) pc++;
writememw(ss,((SP-2)&0xFFFF),flags|0xF000);
writememw(ss,((SP-4)&0xFFFF),CS);
writememw(ss,((SP-6)&0xFFFF),pc);
SP-=6;
addr=2<<2;
flags&=~I_FLAG;
flags&=~T_FLAG;
pc=readmemw(0,addr);
loadcs(readmemw(0,addr+2));
FETCHCLEAR();
inint=1;
nmi_int_now=0;
in_nmi=1;
cycles-=72;
}
Пока результата никакого.
|