#include "std.h"
#include "emul.h"
#include "vars.h"
#include "memory.h"
#include "draw.h"
#include "atm.h"
static void atm_memswap()
{
if (!conf.atm.mem_swap) return;
// swap memory address bits A5-A7 and A8-A10
for (unsigned start_page = 0; start_page < conf.ramsize*1024; start_page += 2048) {
unsigned char buffer[2048], *bank = memory + start_page;
for (unsigned addr = 0; addr < 2048; addr++)
buffer[addr] = bank[(addr & 0x1F) + ((addr >> 3) & 0xE0) + ((addr << 3) & 0x700)];
memcpy(bank, buffer, 2048);
}
}
static void AtmApplySideEffectsWhenChangeVideomode(unsigned char val)
{
int NewVideoMode = (val & 7);
int OldVideoMode = (comp.pFF77 & 7);
// ╩юэёЄрэЄ√ ьюцэю чрфрЄ№ ц╕ёЄъю, яюЄюьє ўЄю ьхцфє ьюфхы ьш └╥╠2 юэш эх ьхэ ■Єё .
const int tScanlineWidth = 224;
const int tScreenWidth = 320/2;
const int tEachBorderWidth = (tScanlineWidth - tScreenWidth)/2;
const int iLinesAboveBorder = 56;
int iRayLine = cpu.t / tScanlineWidth;
int iRayOffset = cpu.t % tScanlineWidth;
/*
static int iLastLine = 0;
if ( iLastLine > iRayLine )
{
printf("\nNew Frame Begin\n");
__debugbreak();
}
iLastLine = iRayLine;
printf("%d->%d %d %d\n", OldVideoMode, NewVideoMode, iRayLine, iRayOffset);
*/
if (OldVideoMode == 3 || NewVideoMode == 3)
{
// ╧хЁхъы■ўхэшх шч/т sinclair Ёхцшь эх шьххЄ яюыхчэ√ї яюсюўэ√ї ¤ЇЇхъЄют
// (яю ёыютрь AlCo фрцх ёшэїЁр ёсштрхЄё )
for(unsigned y = 0; y < 200; y++)
{
AtmVideoCtrl.Scanlines[y+56].VideoMode = NewVideoMode;
AtmVideoCtrl.Scanlines[y+56].Offset = int(((y & ~7U) << 3) + 0x01C0);
}
return;
}
if (OldVideoMode != 6 && NewVideoMode != 6)
{
// ═рё шэЄхЁхёє■Є Єюы№ъю яхЁхъы■ўхэш ьхцфє ЄхъёЄют√ь Ёхцшьюь ш Ёрё°шЁхээ√ьш уЁрЇшўхёъшьш.
// ╥хъёЄютюую Ёхцшьр эхЄ эш фю, эш яюёых яхЁхъы■ўхэш .
// ╤ыхфютрЄхы№эю, эхЄ ш яюсюўэ√ї ¤ЇЇхъЄют.
// ╨рёяЁюёЄЁрэ хь эют√щ тшфхюЁхцшь эр эхюЄЁшёютрээ√х ёърэышэшш
if (iRayOffset >= tEachBorderWidth)
++iRayLine;
while (iRayLine < 256)
{
AtmVideoCtrl.Scanlines[iRayLine++].VideoMode = NewVideoMode;
}
// printf("%d->%d SKIPPED!\n", OldVideoMode, NewVideoMode);
return;
}
//
// ╥хЁьшэюыюуш ш ъюэёЄрэЄ√:
// ▌ъЁрэ ёюфхЁцшЄ 312 ёърэышэшщ, яю 224ЄръЄр (noturbo) т ърцфющ.
//
// "ёЄЁюър" - ьырф°шх 3 сшЄр эюьхЁр юЄЁшёют√трхьющ тшфхюъюэЄЁюыыхЁюь (ыєўюь) ёърэышэшш
// "рфЁхё" - Єхъє∙шщ рфЁхё т тшфхюярь Єш, ё ъюЄюЁюую т√сшЁрхЄ срщЄ√ тшфхюъюэЄЁюыыхЁ
// рфЁхё ьхэ хЄё ё уЁрэєы ЁэюёЄ№■ +8 шыш +64
// "¤ъЁрэ" - ЁрёЄЁютр ърЁЄшэър. ╤ю тёхї ёЄюЁюэ юъЁєцхэр "сюЁф■Ёюь".
// ╨рчьхЁ√ сюЁф■Ёр:
// ётхЁїє ш ёэшчє юЄ ЁрёЄЁр: 56 ёърэышэшщ
// ёыхтр ш ёяЁртр юЄ ЁрёЄЁр: 32 ЄръЄр (64яшъёхы ).
//
// +64 яЁюшёїюфшЄ, ъюуфр CROW 1->0, Єю хёЄ№:
// ышсю т ЄхъёЄьюфх яЁш яхЁхїюфх ёю ёЄЁюъш 7 эр ёЄЁюъє 0,
// ышсю яЁш яхЁхъы■ўхэшш ЄхъёЄьюф->уЁрЇшър яЁш рфЁхёх A5=0,
// ышсю яЁш яхЁхъы■ўхэшш уЁрЇшър->ЄхъёЄьюф яЁш рфЁхёх A5=1 ш ёЄЁюъх 0..3
//
// +8 яЁюшёїюфшЄ эр ЁрёЄЁх т ъюэЎх 64-сыюър яшъёхыхщ (ърцф√х 32ЄръЄр) эхчртшёшью юЄ Ёхцшьр
// (┬└╞═╬: +8 эх чртЁряыхэ√ тэєЄЁш A3..A5 р яЁюшчтюф Є ўхёЄэюх ёыюцхэшх ё рфЁхёюь.)
//
// ёсЁюё A3..A5 (эръюяыхээ√ї +8) яЁюшёїюфшЄ, ъюуфр RCOL 1->0, Єю хёЄ№:
// ышсю т ЄхъёЄьюфх яЁш яхЁхїюфх ё ЁрёЄЁр эр сюЁф■Ё,
// ышсю эр сюЁф■Ёх яЁш яхЁхъы■ўхэшш уЁрЇшър->ЄхъёЄьюф
//
if (iRayLine >= 256)
{
return;
}
// ╧юыєўшь юЇЇёхЄ Єхъє∙хщ ёърэышэшш (ЇръЄшўхёъш тшфхюрфЁхё х╕ эрўрыр)
int Offset = AtmVideoCtrl.Scanlines[iRayLine].Offset;
// ┬√ўшёышь Ёхры№э√щ тшфхюрфЁхё, ё єў╕Єюь шэъЁхьхэЄют яЁш юЄЁшёютъх ЁрёЄЁр.
// ╥ръцх юяЁхфхышь, ъєфр яЁшьхэ хь +64 шэъЁхьхэЄ√ яЁш яхЁхъы■ўхэшш тшфхюЁхцшьр:
// - ┼ёыш ыєў эр сюЁф■Ёх ётхЁїє юЄ ЁрёЄЁр шыш эр сюЁф■Ёх ёыхтр юЄ ЁрёЄЁр - шчьхэ хь юЇЇёхЄ фы Єхъє∙хщ ёърэышэшш
// - ┼ёыш ыєў эр ЁрёЄЁх шыш эр сюЁф■Ёх ёяЁртр юЄ ЁрёЄЁр - шчьхэ хь юЇЇёхЄ фы ёыхфє■∙хщ ёърэышэшш
if ( iRayLine < iLinesAboveBorder || iRayOffset < tEachBorderWidth )
{
// ╦єў эр сюЁф■Ёх. ╦шсю ётхЁїє юЄ ЁрёЄЁр, ышсю ёыхтр юЄ ЁрёЄЁр.
// ┬ёх шчьхэхэш яЁшьхэ хь ъ Єхъє∙хщ ёърэышэшш.
// ╬сЁрсюЄрхь яхЁхъы■ўхэшх тшфхюЁхцшьр.
if ( NewVideoMode == 6 )
{
// ╧хЁхъы■ўхэшх ┬ ЄхъёЄют√щ Ёхцшь.
if ( (Offset & 32) // яЁютхЁър єёыютш "яЁш рфЁхёх A5=1"
&& (iRayLine & 7) < 4 // яЁютхЁър єёыютш "т ёЄЁюъх 0..3"
)
{
// printf("CASE-1: 0x%.4x Incremented (+64) on line %d\n", Offset, iRayLine);
Offset += 64;
AtmVideoCtrl.Scanlines[iRayLine].Offset = Offset;
}
AtmVideoCtrl.Scanlines[iRayLine].VideoMode = NewVideoMode;
// ╧юёых яЁюїюфр тёхї Єюўхъ ЁрёЄЁр Єхъє∙хщ ёърэышэшш т ЄхъёЄютюь Ёхцшьх сєфєЄ ёсЁю°хэ√ A3..A5
Offset &= (~0x38); // ╤сЁюё A3..A5
// printf("CASE-1a, reset A3..A5: 0x%.4x\n", Offset);
// ╨рёёў╕Є тшфхюрфЁхёют фы эшцыхцр∙шї ёърэышэшщ
while (++iRayLine < 256)
{
if ( 0 == (iRayLine & 7))
{
Offset += 64;
}
AtmVideoCtrl.Scanlines[iRayLine].Offset = Offset;
AtmVideoCtrl.Scanlines[iRayLine].VideoMode = NewVideoMode;
}
} else {
// ╧хЁхъы■ўхэшх ╚╟ ЄхъёЄютюую Ёхцшьр.
if ( 0 == (Offset & 32) ) // яЁютхЁър єёыютш "яЁш рфЁхёх A5=0"
{
// printf("CASE-2: 0x%.4x Incremented (+64) on line %d\n", Offset, iRayLine);
Offset += 64;
AtmVideoCtrl.Scanlines[iRayLine].Offset = Offset;
}
AtmVideoCtrl.Scanlines[iRayLine].VideoMode = NewVideoMode;
// ╨рёёў╕Є тшфхюрфЁхёют фы эшцыхцр∙шї ёърэышэшщ
while (++iRayLine < 256)
{
AtmVideoCtrl.Scanlines[iRayLine].Offset = Offset;
AtmVideoCtrl.Scanlines[iRayLine].VideoMode = NewVideoMode;
}
}
} else {
// ╦єў ЁшёєхЄ ЁрёЄЁ, ышсю сюЁф■Ё ёяЁртр юЄ ЁрёЄЁр.
// ┬√ўшёы хь Єхъє∙хх чэрўхэшх тшфхюрфЁхёр
// ╧Ёшсрты хь ъ тшфхюрфЁхёє тёх +64 шэъЁхьхэЄ√,
// ёфхырээ√х т їюфх юЄЁшёютъш ЁрёЄЁр фрээющ ёърэышэшш
if (iRayLine == AtmVideoCtrl.CurrentRayLine)
{
Offset += AtmVideoCtrl.IncCounter_InRaster;
} else {
// ╤ў╕Єўшъ шэъЁхьхэЄют єёЄрЁхы (Є.ъ. эръЁєўхэ эр ёърэышэш■ юЄышўэє■ юЄ Єхъє∙хщ)
// ╚эшЎшрышчшЁєхь хую фы Єхъє∙хщ ёърэышэшш.
AtmVideoCtrl.CurrentRayLine = iRayLine;
AtmVideoCtrl.IncCounter_InRaster = 0;
AtmVideoCtrl.IncCounter_InBorder = 0;
}
// ╧Ёшсрты хь ъ тшфхюрфЁхёє тёх +8 шэъЁхьхэЄ√, яЁюшчю°хф°шх яЁш юЄЁшёютъх ЁрёЄЁр
bool bRayInRaster = iRayOffset < (tScreenWidth + tEachBorderWidth);
int iScanlineRemainder = 0; // ╤ъюы№ъю +8 шэъЁхьхэЄют х∙╕ сєфхЄ ёфхырэю фю ъюэЎр ёърэышэшш
// (Є.х. єцх яюёых яхЁхъы■ўхэш тшфхюЁхцшьр)
if ( bRayInRaster )
{
// ╦єў ЁшёєхЄ ЁрёЄЁ.
// ╧Ёшсрты хь ъ Єхъє∙хьє тшфхюрфЁхёє ёЄюы№ъю +8,
// ёъюы№ъю с√ыю яюыэюёЄ№■ юЄЁшёютрээ√ї 64яшъёхы№э√ї сыюър.
int iIncValue = 8 * ((iRayOffset-tEachBorderWidth)/32);
iScanlineRemainder = 40 - iIncValue;
// printf("CASE-4: 0x%.4x Incremented (+%d) on line %d\n", Offset, iIncValue, iRayLine);
Offset += iIncValue;
} else {
// ╬ЄЁшёютър ЁрёЄЁр ыєўюь чртхЁ°хэр.
// ╥.х. тёх 5- Є№ 64-яшъёхы№э√ї сыюър с√ыш яЁющфхэ√. ╧Ёшсрты хь ъ рфЁхёє +40.
// printf("CASE-5: 0x%.4x Incremented (+40) on line %d\n", Offset, iRayLine);
Offset += 40;
// ┼ёыш яЁхф√фє∙шь Ёхцшьюь с√ы ЄхъёЄют√щ Ёхцшь,
// ╥ю яЁш яхЁхїюфх ё ЁрёЄЁр эр сюЁф■Ё фюыцэ√ с√Є№ ёсЁю°хэ√ A3..A5
if (OldVideoMode == 6)
{
Offset &= (~0x38); // ╤сЁюё A3..A5
// printf("CASE-5a, reset A3..A5: 0x%.4x\n", Offset);
}
}
// ╧Ёшсрты хь ъ тшфхюрфЁхёє тёх +64 шэъЁхьхэЄ√,
// ёфхырээ√х т їюфх юЄЁшёютъш сюЁф■Ёр чр ЁрёЄЁюь фрээющ ёърэышэшш
Offset += AtmVideoCtrl.IncCounter_InBorder;
// ╥хъє∙хх чэрўхэшх тшфхюрфЁхёр т√ўшёыхэю.
// ╬сЁрсрЄ√трхь яхЁхъы■ўхэшх тшфхюЁхцшьр.
int OffsetInc = 0;
if ( NewVideoMode == 6 )
{
// ╧хЁхъы■ўхэшх ┬ ЄхъёЄют√щ Ёхцшь.
if ( (Offset & 32) // яЁютхЁър єёыютш "яЁш рфЁхёх A5=1"
&& (iRayLine & 7) < 4 // яЁютхЁър єёыютш "т ёЄЁюъх 0..3"
)
{
OffsetInc = 64;
// printf("CASE-6: 0x%.4x Incremented (+64) on line %d\n", Offset, iRayLine);
Offset += OffsetInc;
}
// ╨рёёў╕Є тшфхюрфЁхёют фы эшцыхцр∙шї ёърэышэшщ
Offset += iScanlineRemainder;
while (++iRayLine < 256)
{
if ( 0 == (iRayLine & 7))
Offset += 64;
AtmVideoCtrl.Scanlines[iRayLine].Offset = Offset;
AtmVideoCtrl.Scanlines[iRayLine].VideoMode = NewVideoMode;
}
} else {
// ╧хЁхъы■ўхэшх ╚╟ ЄхъёЄютюую Ёхцшьр.
if ( 0 == (Offset & 32) ) // яЁютхЁър єёыютш "яЁш рфЁхёх A5=0"
{
OffsetInc = 64;
// printf("CASE-7: 0x%.4x Incremented (+64) on line %d\n", Offset, iRayLine);
Offset += OffsetInc;
}
// ╨рёёў╕Є тшфхюрфЁхёют фы эшцыхцр∙шї ёърэышэшщ
Offset += iScanlineRemainder;
while (++iRayLine < 256)
{
AtmVideoCtrl.Scanlines[iRayLine].Offset = Offset;
AtmVideoCtrl.Scanlines[iRayLine].VideoMode = NewVideoMode;
Offset += 40;
}
}
// чряюьшэрхь ёфхырээ√щ шэъЁхьхэЄ эр ёыєўрщ,
// хёыш шї сєфхЄ эхёъюы№ъю т їюфх юЄЁшёютъш Єхъє∙хщ ёърэышэшш.
if ( bRayInRaster )
{
AtmVideoCtrl.IncCounter_InRaster += OffsetInc;
} else {
AtmVideoCtrl.IncCounter_InBorder += OffsetInc;
}
}
}
void set_atm_FF77(unsigned port, unsigned char val)
{
if ((comp.pFF77 ^ val) & 1)
atm_memswap();
if ((comp.pFF77 & 7) ^ (val & 7))
{
// ╧ЁюшёїюфшЄ яхЁхъы■ўхэшх тшфхюЁхцшьр
AtmApplySideEffectsWhenChangeVideomode(val);
}
comp.pFF77 = val;
comp.aFF77 = port;
cpu.int_gate = (comp.pFF77 & 0x20) != false;
set_banks();
}
void set_atm_aFE(unsigned char addr)
{
unsigned char old_aFE = comp.aFE;
comp.aFE = addr;
if ((addr ^ old_aFE) & 0x40) atm_memswap();
if ((addr ^ old_aFE) & 0x80) set_banks();
}
static u8 atm_pal[0x10] = { 0 };
void atm_writepal(unsigned char val)
{
assert(comp.border_attr < 0x10);
atm_pal[comp.border_attr] = val;
// ╧ЁхюсЁрчютрэшх ярышЄЁ√ т ЇюЁьрЄ ULA+
u8 PalIdx = ((comp.border_attr & 8) << 1) | (comp.border_attr & 7);
comp.comp_pal[PalIdx + 0*8] =
comp.comp_pal[PalIdx + 1*8] =
comp.comp_pal[PalIdx + 3*8] =
comp.comp_pal[PalIdx + 5*8] = u8(t.atm_pal_map[val]);
temp.comp_pal_changed = 1;
}
u8 atm_readpal()
{
return atm_pal[comp.border_attr];
}
unsigned char atm450_z(unsigned t)
{
// PAL hardware gives 3 zeros in secret short time intervals
if (conf.frame < 80000) { // NORMAL SPEED mode
if ((unsigned)(t-7200) < 40 || (unsigned)(t-7284) < 40 || (unsigned)(t-7326) < 40) return 0;
} else { // TURBO mode
if ((unsigned)(t-21514) < 40 || (unsigned)(t-21703) < 80 || (unsigned)(t-21808) < 40) return 0;
}
return 0x80;
}