Compare commits
2 commits
0dfb431aa3
...
2076cfd1c9
Author | SHA1 | Date | |
---|---|---|---|
|
2076cfd1c9 | ||
|
90b23a8613 |
5 changed files with 183 additions and 187 deletions
|
@ -2057,7 +2057,7 @@ static void ExecGroup_80( void )
|
|||
case 0xB: /* BUSCC */
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TRACE, CPU_I_CALLED, "ExecBUSCC" );
|
||||
// FIXME: 49g bugs here on display change
|
||||
DEBUG_print_cpu_instruction();
|
||||
// DEBUG_print_cpu_instruction();
|
||||
|
||||
ChfGenerate( CPU_CHF_MODULE_ID, __FILE__, __LINE__, CPU_F_INTERR, CHF_WARNING, "BUSCC" );
|
||||
ChfSignal( CPU_CHF_MODULE_ID );
|
||||
|
@ -2818,7 +2818,7 @@ void DEBUG_print_cpu_instruction( void )
|
|||
|
||||
/* Dump PC and current instruction */
|
||||
( void )Disassemble( cpu_status.PC, dob );
|
||||
fprintf( stderr, "\n%s\n\n", dob );
|
||||
fprintf( stderr, "%s\n", dob );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2853,6 +2853,7 @@ void OneStep( void )
|
|||
Address offset;
|
||||
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TRACE, CPU_I_EXECUTING, cpu_status.PC );
|
||||
DEBUG_print_cpu_instruction();
|
||||
|
||||
/* Get first instruction nibble */
|
||||
n = GetNibble( cpu_status.PC++ );
|
||||
|
|
352
src/emulator.c
352
src/emulator.c
|
@ -258,6 +258,180 @@ static void EmulatorLoop( void )
|
|||
}
|
||||
}
|
||||
|
||||
static ChfAction do_SHUTDN( void )
|
||||
{
|
||||
/* 3.1: CPU_SPIN_SHUTDN is not defined, and the cpu emulator
|
||||
has just executed a shutdown instruction.
|
||||
Let's do something a little tricky here:
|
||||
|
||||
1- redraw the LCD
|
||||
|
||||
2- handle serial port activities
|
||||
|
||||
3- determine which timer will expire first, and
|
||||
compute an approximate value of the maximum duration
|
||||
of the shutdown --> ms
|
||||
|
||||
4- handle serial port activities
|
||||
|
||||
5- enter the inner idle loop; it breaks when either an
|
||||
X Event occurred (possibly clearing the shutdown) or
|
||||
the shutdown timeout elapses
|
||||
|
||||
6- determine the actual time we spend in the idle loop
|
||||
(X timeouts are not accurate enough for this purpose)
|
||||
|
||||
7- update T1 and T2, check their state and wake/interrupt
|
||||
the CPU if necessary
|
||||
|
||||
Activities 3-7 above are enclosed in an outer loop because we
|
||||
cannot be absolutely sure of the actual time spent
|
||||
in the idle loop; moreover, not all X Events actually
|
||||
spool up the CPU. The outer loop breaks when the CPU is
|
||||
actually brought out of shutdown.
|
||||
|
||||
frac_t1 and frac_t2 contain the number of microseconds
|
||||
not accounted for in the last T1/T2 update, respectively;
|
||||
they help minimize the cumulative timing error induced
|
||||
by executing the outer idle loop more than once.
|
||||
*/
|
||||
struct timeval start_idle, end_idle;
|
||||
int frac_t1 = 0, frac_t2 = 0;
|
||||
|
||||
gettimeofday( &start_idle, NULL );
|
||||
|
||||
/* Redraw the LCD immediately before entering idle loop;
|
||||
this ensures that the latest LCD updated actually
|
||||
get to the screen.
|
||||
*/
|
||||
// ui_update_display();
|
||||
|
||||
/* Handle serial port activity before entering the outer idle
|
||||
loop, because this could possibly bring the cpu out of
|
||||
shutdown right now.
|
||||
*/
|
||||
HandleSerial();
|
||||
|
||||
/* XXX
|
||||
If either timer has a pending service request,
|
||||
process it immediately. It is not clear why it was
|
||||
not processed *before* shutdown, though.
|
||||
*/
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_SREQ ) {
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_WAKE )
|
||||
CpuWake();
|
||||
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_INT )
|
||||
CpuIntRequest( INT_REQUEST_IRQ );
|
||||
}
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_SREQ ) {
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_WAKE )
|
||||
CpuWake();
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_INT )
|
||||
CpuIntRequest( INT_REQUEST_IRQ );
|
||||
}
|
||||
|
||||
while ( cpu_status.shutdn ) {
|
||||
unsigned long ms = MAX_IDLE_X_LOOP_TIMEOUT;
|
||||
unsigned long mst;
|
||||
int ela;
|
||||
int ela_ticks;
|
||||
|
||||
debug3( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T1 (during SHUTDN)", mod_status.hdw.t1_ctrl, mod_status.hdw.t1_val );
|
||||
debug3( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T2 (during SHUTDN)", mod_status.hdw.t2_ctrl, mod_status.hdw.t2_val );
|
||||
|
||||
/* Determine which timer will expire first */
|
||||
if ( mod_status.hdw.t1_ctrl & ( T1_CTRL_INT | T1_CTRL_WAKE ) ) {
|
||||
/* T1 will do something on expiration */
|
||||
mst = ( ( unsigned long )mod_status.hdw.t1_val + 1 ) * T1_MS_MULTIPLIER;
|
||||
|
||||
debug2( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_EXP, "T1", mst );
|
||||
|
||||
if ( mst < ms )
|
||||
ms = mst;
|
||||
}
|
||||
|
||||
if ( ( mod_status.hdw.t2_ctrl & T2_CTRL_TRUN ) && ( mod_status.hdw.t2_ctrl & ( T2_CTRL_INT | T2_CTRL_WAKE ) ) ) {
|
||||
/* T2 is running and will do something on expiration */
|
||||
mst = ( ( unsigned long )mod_status.hdw.t2_val + 1 ) / T2_MS_DIVISOR;
|
||||
|
||||
debug2( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_EXP, "T2", mst );
|
||||
|
||||
if ( mst < ms )
|
||||
ms = mst;
|
||||
}
|
||||
|
||||
/* Handle serial port activities at each iteration of
|
||||
the outer idle loop; this ensures that the serial
|
||||
port emulation will not starve.
|
||||
*/
|
||||
HandleSerial();
|
||||
|
||||
/* Enter idle loop, possibly with timeout;
|
||||
The loop breaks when:
|
||||
- any X Event occurs (possibly clearing the shutdown)
|
||||
- the given timeout expires
|
||||
*/
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_IDLE_X_LOOP, ms );
|
||||
// IdleXLoop( ms );
|
||||
usleep( ms );
|
||||
|
||||
/* End of idle loop; compute actual elapsed time */
|
||||
gettimeofday( &end_idle, NULL );
|
||||
|
||||
ela = ( end_idle.tv_sec - start_idle.tv_sec ) * 1000000 + ( end_idle.tv_usec - start_idle.tv_usec );
|
||||
|
||||
/* Update start_idle here to contain lag */
|
||||
start_idle = end_idle;
|
||||
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_ELAPSED, ela );
|
||||
|
||||
/* Update timers and act accordingly */
|
||||
ela_ticks = ( ( ela + frac_t1 ) + T1_INTERVAL / 2 ) / T1_INTERVAL;
|
||||
frac_t1 = ( ela + frac_t1 ) - ela_ticks * T1_INTERVAL;
|
||||
|
||||
if ( ela_ticks > mod_status.hdw.t1_val ) {
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER1_EX, mod_status.hdw.t1_ctrl );
|
||||
|
||||
mod_status.hdw.t1_ctrl |= T1_CTRL_SREQ;
|
||||
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_WAKE )
|
||||
CpuWake();
|
||||
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_INT )
|
||||
CpuIntRequest( INT_REQUEST_IRQ );
|
||||
}
|
||||
|
||||
mod_status.hdw.t1_val = ( mod_status.hdw.t1_val - ela_ticks ) & T1_OVF_MASK;
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_TRUN ) {
|
||||
ela_ticks = ( ( ela + frac_t2 ) + T2_INTERVAL / 2 ) / T2_INTERVAL;
|
||||
frac_t2 = ( ela + frac_t2 ) - ela_ticks * T2_INTERVAL;
|
||||
|
||||
if ( ela_ticks > mod_status.hdw.t2_val ) {
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER2_EX, mod_status.hdw.t2_ctrl );
|
||||
|
||||
mod_status.hdw.t2_ctrl |= T2_CTRL_SREQ;
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_WAKE )
|
||||
CpuWake();
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_INT )
|
||||
CpuIntRequest( INT_REQUEST_IRQ );
|
||||
}
|
||||
|
||||
mod_status.hdw.t2_val = ( mod_status.hdw.t2_val - ela_ticks ) & T2_OVF_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
debug3( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T1 (after SHUTDN)", mod_status.hdw.t1_ctrl, mod_status.hdw.t1_val );
|
||||
debug3( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T2 (after SHUTDN)", mod_status.hdw.t2_ctrl, mod_status.hdw.t2_val );
|
||||
|
||||
return CHF_CONTINUE;
|
||||
}
|
||||
|
||||
/* Condition handler for the EmulatorLoop */
|
||||
static ChfAction EmulatorLoopHandler( const ChfDescriptor* d, const ChfState s, void* _ctx )
|
||||
{
|
||||
|
@ -272,183 +446,7 @@ static ChfAction EmulatorLoopHandler( const ChfDescriptor* d, const ChfState s,
|
|||
/* Condition from CPU modules; check Condition Code */
|
||||
switch ( d->condition_code ) {
|
||||
case CPU_I_SHUTDN:
|
||||
{
|
||||
/* 3.1: CPU_SPIN_SHUTDN is not defined, and the cpu emulator
|
||||
has just executed a shutdown instruction.
|
||||
Let's do something a little tricky here:
|
||||
|
||||
1- redraw the LCD
|
||||
|
||||
2- handle serial port activities
|
||||
|
||||
3- determine which timer will expire first, and
|
||||
compute an approximate value of the maximum duration
|
||||
of the shutdown --> ms
|
||||
|
||||
4- handle serial port activities
|
||||
|
||||
5- enter the inner idle loop; it breaks when either an
|
||||
X Event occurred (possibly clearing the shutdown) or
|
||||
the shutdown timeout elapses
|
||||
|
||||
6- determine the actual time we spend in the idle loop
|
||||
(X timeouts are not accurate enough for this purpose)
|
||||
|
||||
7- update T1 and T2, check their state and wake/interrupt
|
||||
the CPU if necessary
|
||||
|
||||
Activities 3-7 above are enclosed in an outer loop because we
|
||||
cannot be absolutely sure of the actual time spent
|
||||
in the idle loop; moreover, not all X Events actually
|
||||
spool up the CPU. The outer loop breaks when the CPU is
|
||||
actually brought out of shutdown.
|
||||
|
||||
frac_t1 and frac_t2 contain the number of microseconds
|
||||
not accounted for in the last T1/T2 update, respectively;
|
||||
they help minimize the cumulative timing error induced
|
||||
by executing the outer idle loop more than once.
|
||||
*/
|
||||
struct timeval start_idle, end_idle;
|
||||
int frac_t1 = 0, frac_t2 = 0;
|
||||
|
||||
gettimeofday( &start_idle, NULL );
|
||||
|
||||
/* Redraw the LCD immediately before entering idle loop;
|
||||
this ensures that the latest LCD updated actually
|
||||
get to the screen.
|
||||
*/
|
||||
// ui_update_display();
|
||||
|
||||
/* Handle serial port activity before entering the outer idle
|
||||
loop, because this could possibly bring the cpu out of
|
||||
shutdown right now.
|
||||
*/
|
||||
HandleSerial();
|
||||
|
||||
/* XXX
|
||||
If either timer has a pending service request,
|
||||
process it immediately. It is not clear why it was
|
||||
not processed *before* shutdown, though.
|
||||
*/
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_SREQ ) {
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_WAKE )
|
||||
CpuWake();
|
||||
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_INT )
|
||||
CpuIntRequest( INT_REQUEST_IRQ );
|
||||
}
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_SREQ ) {
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_WAKE )
|
||||
CpuWake();
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_INT )
|
||||
CpuIntRequest( INT_REQUEST_IRQ );
|
||||
}
|
||||
|
||||
while ( cpu_status.shutdn ) {
|
||||
unsigned long ms = MAX_IDLE_X_LOOP_TIMEOUT;
|
||||
unsigned long mst;
|
||||
int ela;
|
||||
int ela_ticks;
|
||||
|
||||
debug3( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T1 (during SHUTDN)", mod_status.hdw.t1_ctrl,
|
||||
mod_status.hdw.t1_val );
|
||||
debug3( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T2 (during SHUTDN)", mod_status.hdw.t2_ctrl,
|
||||
mod_status.hdw.t2_val );
|
||||
|
||||
/* Determine which timer will expire first */
|
||||
if ( mod_status.hdw.t1_ctrl & ( T1_CTRL_INT | T1_CTRL_WAKE ) ) {
|
||||
/* T1 will do something on expiration */
|
||||
mst = ( ( unsigned long )mod_status.hdw.t1_val + 1 ) * T1_MS_MULTIPLIER;
|
||||
|
||||
debug2( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_EXP, "T1", mst );
|
||||
|
||||
if ( mst < ms )
|
||||
ms = mst;
|
||||
}
|
||||
|
||||
if ( ( mod_status.hdw.t2_ctrl & T2_CTRL_TRUN ) &&
|
||||
( mod_status.hdw.t2_ctrl & ( T2_CTRL_INT | T2_CTRL_WAKE ) ) ) {
|
||||
/* T2 is running and will do something on expiration */
|
||||
mst = ( ( unsigned long )mod_status.hdw.t2_val + 1 ) / T2_MS_DIVISOR;
|
||||
|
||||
debug2( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_EXP, "T2", mst );
|
||||
|
||||
if ( mst < ms )
|
||||
ms = mst;
|
||||
}
|
||||
|
||||
/* Handle serial port activities at each iteration of
|
||||
the outer idle loop; this ensures that the serial
|
||||
port emulation will not starve.
|
||||
*/
|
||||
HandleSerial();
|
||||
|
||||
/* Enter idle loop, possibly with timeout;
|
||||
The loop breaks when:
|
||||
- any X Event occurs (possibly clearing the shutdown)
|
||||
- the given timeout expires
|
||||
*/
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_IDLE_X_LOOP, ms );
|
||||
// IdleXLoop( ms );
|
||||
usleep( ms );
|
||||
|
||||
/* End of idle loop; compute actual elapsed time */
|
||||
gettimeofday( &end_idle, NULL );
|
||||
|
||||
ela = ( end_idle.tv_sec - start_idle.tv_sec ) * 1000000 + ( end_idle.tv_usec - start_idle.tv_usec );
|
||||
|
||||
/* Update start_idle here to contain lag */
|
||||
start_idle = end_idle;
|
||||
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_ELAPSED, ela );
|
||||
|
||||
/* Update timers and act accordingly */
|
||||
ela_ticks = ( ( ela + frac_t1 ) + T1_INTERVAL / 2 ) / T1_INTERVAL;
|
||||
frac_t1 = ( ela + frac_t1 ) - ela_ticks * T1_INTERVAL;
|
||||
|
||||
if ( ela_ticks > mod_status.hdw.t1_val ) {
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER1_EX, mod_status.hdw.t1_ctrl );
|
||||
|
||||
mod_status.hdw.t1_ctrl |= T1_CTRL_SREQ;
|
||||
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_WAKE )
|
||||
CpuWake();
|
||||
|
||||
if ( mod_status.hdw.t1_ctrl & T1_CTRL_INT )
|
||||
CpuIntRequest( INT_REQUEST_IRQ );
|
||||
}
|
||||
|
||||
mod_status.hdw.t1_val = ( mod_status.hdw.t1_val - ela_ticks ) & T1_OVF_MASK;
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_TRUN ) {
|
||||
ela_ticks = ( ( ela + frac_t2 ) + T2_INTERVAL / 2 ) / T2_INTERVAL;
|
||||
frac_t2 = ( ela + frac_t2 ) - ela_ticks * T2_INTERVAL;
|
||||
|
||||
if ( ela_ticks > mod_status.hdw.t2_val ) {
|
||||
debug1( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER2_EX, mod_status.hdw.t2_ctrl );
|
||||
|
||||
mod_status.hdw.t2_ctrl |= T2_CTRL_SREQ;
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_WAKE )
|
||||
CpuWake();
|
||||
|
||||
if ( mod_status.hdw.t2_ctrl & T2_CTRL_INT )
|
||||
CpuIntRequest( INT_REQUEST_IRQ );
|
||||
}
|
||||
|
||||
mod_status.hdw.t2_val = ( mod_status.hdw.t2_val - ela_ticks ) & T2_OVF_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
debug3( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T1 (after SHUTDN)", mod_status.hdw.t1_ctrl,
|
||||
mod_status.hdw.t1_val );
|
||||
debug3( CPU_CHF_MODULE_ID, DEBUG_C_TIMERS, CPU_I_TIMER_ST, "T2 (after SHUTDN)", mod_status.hdw.t2_ctrl,
|
||||
mod_status.hdw.t2_val );
|
||||
|
||||
act = CHF_CONTINUE;
|
||||
}
|
||||
act = do_SHUTDN();
|
||||
break;
|
||||
|
||||
case CPU_I_EMULATOR_INT:
|
||||
|
|
|
@ -201,9 +201,8 @@ void ModSelectDescription( int model )
|
|||
ModRegisterDescription( hw49_description );
|
||||
break;
|
||||
default:
|
||||
/* ChfGenerate( MOD_CHF_MODULE_ID, __FILE__, __LINE__, MOD_E_NO_MATCH, CHF_ERROR, config.hw ); */
|
||||
/* ChfSignal( MOD_CHF_MODULE_ID ); */
|
||||
fprintf( stderr, "Error: Unknown model %i\n", model );
|
||||
ChfGenerate( MOD_CHF_MODULE_ID, __FILE__, __LINE__, MOD_E_NO_MATCH, CHF_ERROR, "Unknown model" );
|
||||
ChfSignal( MOD_CHF_MODULE_ID );
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1251,7 +1251,7 @@ void ModConfig( Address config_info )
|
|||
if ( mod == N_MOD ) {
|
||||
/* All modules are configured - Signal a warning */
|
||||
// FIXME: 48gx bugs here when running VERSION
|
||||
DEBUG_print_cpu_instruction();
|
||||
// DEBUG_print_cpu_instruction();
|
||||
|
||||
ChfGenerate( MOD_CHF_MODULE_ID, __FILE__, __LINE__, MOD_W_BAD_CONFIG, CHF_WARNING, config_info );
|
||||
ChfSignal( MOD_CHF_MODULE_ID );
|
||||
|
|
|
@ -144,9 +144,7 @@ void RomInit( void )
|
|||
}
|
||||
|
||||
if ( ReadNibblesFromFile( config.rom_file_name, N_ROM_SIZE, mod_status_rom ) ) {
|
||||
/* ChfGenerate( MOD_CHF_MODULE_ID, __FILE__, __LINE__, MOD_F_ROM_INIT, CHF_FATAL ); */
|
||||
/* ChfSignal( MOD_CHF_MODULE_ID ); */
|
||||
// HACK: To load 48SX ROM, try again with half the size this time.
|
||||
// To load 48SX ROM, try again with half the size this time.
|
||||
if ( ReadNibblesFromFile( config.rom_file_name, N_ROM_SIZE / 2, mod_status_rom ) ) {
|
||||
ChfGenerate( MOD_CHF_MODULE_ID, __FILE__, __LINE__, MOD_F_ROM_INIT, CHF_FATAL );
|
||||
ChfSignal( MOD_CHF_MODULE_ID );
|
||||
|
@ -233,7 +231,7 @@ void RomWrite( Address rel_address, Nibble datum )
|
|||
debug1( MOD_CHF_MODULE_ID, DEBUG_C_TRACE, MOD_I_CALLED, "RomWrite" );
|
||||
|
||||
// FIXME: 48gx: saturn48gx-Mid <12>d (src/romram.c,235)-E-Write into ROM A[1B632] D[9]
|
||||
DEBUG_print_cpu_instruction();
|
||||
// DEBUG_print_cpu_instruction();
|
||||
|
||||
ChfGenerate( MOD_CHF_MODULE_ID, __FILE__, __LINE__, MOD_E_ROM_WRITE, CHF_ERROR, rel_address, datum );
|
||||
ChfSignal( MOD_CHF_MODULE_ID );
|
||||
|
|
Loading…
Reference in a new issue