1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-10 12:29:01 +00:00

Merge pull request #63 from TomHarte/OricVideoOptimisation

Reduces 6522 emulation costs
This commit is contained in:
Thomas Harte 2016-10-27 21:16:21 -04:00 committed by GitHub
commit b64ae904a5
5 changed files with 83 additions and 46 deletions

View File

@ -230,61 +230,97 @@ template <class T> class MOS6522 {
}
}
#define phase2() \
_registers.last_timer[0] = _registers.timer[0];\
_registers.last_timer[1] = _registers.timer[1];\
\
if(_registers.timer_needs_reload)\
{\
_registers.timer_needs_reload = false;\
_registers.timer[0] = _registers.timer_latch[0];\
}\
else\
_registers.timer[0] --;\
\
_registers.timer[1] --;
// IRQ is raised on the half cycle after overflow
#define phase1() \
if((_registers.timer[1] == 0xffff) && !_registers.last_timer[1] && _timer_is_running[1])\
{\
_timer_is_running[1] = false;\
_registers.interrupt_flags |= InterruptFlag::Timer2;\
reevaluate_interrupts();\
}\
\
if((_registers.timer[0] == 0xffff) && !_registers.last_timer[0] && _timer_is_running[0])\
{\
_registers.interrupt_flags |= InterruptFlag::Timer1;\
reevaluate_interrupts();\
\
if(_registers.auxiliary_control&0x40)\
_registers.timer_needs_reload = true;\
else\
_timer_is_running[0] = false;\
}
/*!
Runs for a specified number of half cycles.
Although the original chip accepts only a phase-2 input, timer reloads are specified as occuring
1.5 cycles after the timer hits zero. It is therefore necessary to emulate at half-cycle precision.
1.5 cycles after the timer hits zero. It therefore may be necessary to emulate at half-cycle precision.
The first emulated half-cycle will be the period between the trailing edge of a phase-2 input and the
next rising edge. So it should align with a full system's phase-1. The next emulated half-cycle will be
that which occurs during phase-2.
Callers should decide whether they are going to use @c run_for_half_cycles or @c run_for_cycles, and not
intermingle usage.
*/
inline void run_for_half_cycles(unsigned int number_of_cycles)
{
while(number_of_cycles--)
if(_is_phase2)
{
if(_is_phase2)
{
_registers.last_timer[0] = _registers.timer[0];
_registers.last_timer[1] = _registers.timer[1];
phase2();
number_of_cycles--;
}
if(_registers.timer_needs_reload)
{
_registers.timer_needs_reload = false;
_registers.timer[0] = _registers.timer_latch[0];
}
else
_registers.timer[0] --;
while(number_of_cycles > 2)
{
phase1();
phase2();
number_of_cycles -= 2;
}
_registers.timer[1] --;
}
else
{
// IRQ is raised on the half cycle after overflow
if((_registers.timer[1] == 0xffff) && !_registers.last_timer[1] && _timer_is_running[1])
{
_timer_is_running[1] = false;
_registers.interrupt_flags |= InterruptFlag::Timer2;
reevaluate_interrupts();
}
if((_registers.timer[0] == 0xffff) && !_registers.last_timer[0] && _timer_is_running[0])
{
_registers.interrupt_flags |= InterruptFlag::Timer1;
reevaluate_interrupts();
if(_registers.auxiliary_control&0x40)
_registers.timer_needs_reload = true;
else
_timer_is_running[0] = false;
}
}
_is_phase2 ^= true;
if(number_of_cycles)
{
phase1();
_is_phase2 = true;
}
else
{
_is_phase2 = false;
}
}
/*!
Runs for a specified number of cycles.
Callers should decide whether they are going to use @c run_for_half_cycles or @c run_for_cycles, and not
intermingle usage.
*/
inline void run_for_cycles(unsigned int number_of_cycles)
{
while(number_of_cycles--)
{
phase1();
phase2();
}
}
#undef phase1
#undef phase2
/*! @returns @c true if the IRQ line is currently active; @c false otherwise. */
inline bool get_interrupt_line()
{

View File

@ -91,8 +91,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
_driveVIA.set_register(address, *value);
}
_serialPortVIA->run_for_half_cycles(2);
_driveVIA.run_for_half_cycles(2);
_serialPortVIA->run_for_cycles(1);
_driveVIA.run_for_cycles(1);
return 1;
}

View File

@ -139,8 +139,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
{
while(!_userPortVIA->get_interrupt_line() && !_keyboardVIA->get_interrupt_line() && !_tape.get_tape()->is_at_end())
{
_userPortVIA->run_for_half_cycles(2);
_keyboardVIA->run_for_half_cycles(2);
_userPortVIA->run_for_cycles(1);
_keyboardVIA->run_for_cycles(1);
_tape.run_for_cycles(1);
}
}
@ -157,8 +157,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
}
}
_userPortVIA->run_for_half_cycles(2);
_keyboardVIA->run_for_half_cycles(2);
_userPortVIA->run_for_cycles(1);
_keyboardVIA->run_for_cycles(1);
if(_typer && operation == CPU6502::BusOperation::ReadOpcode && address == 0xEB1E)
{
if(!_typer->type_next_character())

View File

@ -61,7 +61,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
}
}
_via.run_for_half_cycles(2);
_via.run_for_cycles(1);
_via.tape->run_for_cycles(1);
_cycles_since_video_update++;
return 1;

View File

@ -51,7 +51,7 @@ std::shared_ptr<Outputs::CRT::CRT> VideoOutput::get_crt()
void VideoOutput::run_for_cycles(int number_of_cycles)
{
// Vertical: 039: pixels; otherwise blank; 4853 sync
// Vertical: 039: pixels; otherwise blank; 4853 sync, 5456 colour burst
// Horizontal: 0223: pixels; otherwise blank; 256259 sync
while(number_of_cycles--)
@ -78,6 +78,7 @@ void VideoOutput::run_for_cycles(int number_of_cycles)
}
}
State new_state = Blank;
if(
(h_counter >= 48 && h_counter <= 53) ||