2024-03-04 17:06:43 +00:00
//
// Archimedes.cpp
// Clock Signal
//
// Created by Thomas Harte on 04/03/2024.
// Copyright © 2024 Thomas Harte. All rights reserved.
//
# include "Archimedes.hpp"
2024-03-20 18:25:20 +00:00
# include "HalfDuplexSerial.hpp"
# include "InputOutputController.hpp"
# include "Keyboard.hpp"
# include "MemoryController.hpp"
# include "Sound.hpp"
2024-03-04 17:06:43 +00:00
# include "../../AudioProducer.hpp"
# include "../../KeyboardMachine.hpp"
# include "../../MediaTarget.hpp"
# include "../../ScanProducer.hpp"
# include "../../TimedMachine.hpp"
2024-03-19 15:34:10 +00:00
# include "../../../InstructionSets/ARM/Disassembler.hpp"
2024-03-04 17:08:46 +00:00
# include "../../../InstructionSets/ARM/Executor.hpp"
2024-03-06 14:54:39 +00:00
# include "../../../Outputs/Log.hpp"
2024-03-16 19:00:23 +00:00
# include "../../../Components/I2C/I2C.hpp"
2024-03-04 17:08:46 +00:00
2024-03-05 02:09:24 +00:00
# include <algorithm>
# include <array>
2024-03-09 03:54:42 +00:00
# include <set>
2024-03-05 02:09:24 +00:00
# include <vector>
2024-03-05 02:43:06 +00:00
namespace {
2024-03-06 14:54:39 +00:00
Log : : Logger < Log : : Source : : Archimedes > logger ;
2024-03-05 02:43:06 +00:00
}
2024-03-04 17:06:43 +00:00
namespace Archimedes {
class ConcreteMachine :
public Machine ,
2024-03-05 02:09:24 +00:00
public MachineTypes : : MediaTarget ,
2024-03-04 17:06:43 +00:00
public MachineTypes : : TimedMachine ,
public MachineTypes : : ScanProducer
{
2024-03-20 15:42:37 +00:00
private :
// TODO: pick a sensible clock rate; this is just code for '24 MIPS, please'.
static constexpr int ClockRate = 24'000'000 ;
2024-03-07 16:12:40 +00:00
2024-03-20 15:42:37 +00:00
// Runs for 24 cycles, distributing calls to the various ticking subsystems
// 'correctly' (i.e. correctly for the approximation in use).
//
// The implementation of this is coupled to the ClockRate above, hence its
// appearance here.
2024-03-22 14:01:34 +00:00
template < int video_divider >
2024-03-20 15:42:37 +00:00
void macro_tick ( ) {
macro_counter_ - = 24 ;
// This is a 24-cycle window, so at 24Mhz macro_tick() is called at 1Mhz.
// Hence, required ticks are:
//
// * CPU: 24;
2024-03-22 14:01:34 +00:00
// * video: 24 / video_divider;
2024-03-20 15:42:37 +00:00
// * timers: 2;
2024-03-21 15:24:47 +00:00
// * sound: 1.
2024-03-20 15:42:37 +00:00
2024-03-22 14:01:34 +00:00
tick_cpu_video < 0 , video_divider > ( ) ; tick_cpu_video < 1 , video_divider > ( ) ;
tick_cpu_video < 2 , video_divider > ( ) ; tick_cpu_video < 3 , video_divider > ( ) ;
tick_cpu_video < 4 , video_divider > ( ) ; tick_cpu_video < 5 , video_divider > ( ) ;
tick_cpu_video < 6 , video_divider > ( ) ; tick_cpu_video < 7 , video_divider > ( ) ;
tick_cpu_video < 8 , video_divider > ( ) ; tick_cpu_video < 9 , video_divider > ( ) ;
tick_cpu_video < 10 , video_divider > ( ) ; tick_cpu_video < 11 , video_divider > ( ) ;
2024-03-20 15:42:37 +00:00
tick_timers ( ) ;
2024-03-22 14:01:34 +00:00
tick_cpu_video < 12 , video_divider > ( ) ; tick_cpu_video < 13 , video_divider > ( ) ;
tick_cpu_video < 14 , video_divider > ( ) ; tick_cpu_video < 15 , video_divider > ( ) ;
tick_cpu_video < 16 , video_divider > ( ) ; tick_cpu_video < 17 , video_divider > ( ) ;
tick_cpu_video < 18 , video_divider > ( ) ; tick_cpu_video < 19 , video_divider > ( ) ;
tick_cpu_video < 20 , video_divider > ( ) ; tick_cpu_video < 21 , video_divider > ( ) ;
2024-03-20 15:42:37 +00:00
tick_timers ( ) ;
2024-03-21 15:24:47 +00:00
tick_sound ( ) ;
2024-03-20 15:42:37 +00:00
}
int macro_counter_ = 0 ;
2024-03-07 16:12:40 +00:00
2024-03-22 14:01:34 +00:00
template < int offset , int video_divider >
void tick_cpu_video ( ) {
tick_cpu ( ) ;
if constexpr ( ! ( offset % video_divider ) ) {
tick_video ( ) ;
}
}
2024-03-04 17:06:43 +00:00
public :
ConcreteMachine (
const Analyser : : Static : : Target & target ,
const ROMMachine : : ROMFetcher & rom_fetcher
2024-03-22 14:01:34 +00:00
) : executor_ ( * this , * this ) {
2024-03-07 16:12:40 +00:00
set_clock_rate ( ClockRate ) ;
2024-03-05 02:09:24 +00:00
constexpr ROM : : Name risc_os = ROM : : Name : : AcornRISCOS319 ;
ROM : : Request request ( risc_os ) ;
auto roms = rom_fetcher ( request ) ;
if ( ! request . validate ( roms ) ) {
throw ROMMachine : : Error : : MissingROMs ;
}
executor_ . bus . set_rom ( roms . find ( risc_os ) - > second ) ;
insert_media ( target . media ) ;
2024-03-04 17:06:43 +00:00
}
2024-03-20 18:25:20 +00:00
void update_interrupts ( ) {
using Exception = InstructionSet : : ARM : : Registers : : Exception ;
const int requests = executor_ . bus . interrupt_mask ( ) ;
if ( ( requests & InterruptRequests : : FIQ ) & & executor_ . registers ( ) . interrupt < Exception : : FIQ > ( ) ) {
return ;
}
if ( requests & InterruptRequests : : IRQ ) {
executor_ . registers ( ) . interrupt < Exception : : IRQ > ( ) ;
}
2024-03-12 15:34:31 +00:00
}
2024-03-04 17:06:43 +00:00
2024-03-22 14:01:34 +00:00
void update_clock_rates ( ) {
2024-03-22 14:24:24 +00:00
video_divider_ = executor_ . bus . video ( ) . clock_divider ( ) ;
2024-03-22 14:01:34 +00:00
}
2024-03-12 15:34:31 +00:00
private :
2024-03-04 17:06:43 +00:00
// MARK: - ScanProducer.
void set_scan_target ( Outputs : : Display : : ScanTarget * scan_target ) override {
2024-03-22 00:41:24 +00:00
executor_ . bus . video ( ) . crt ( ) . set_scan_target ( scan_target ) ;
2024-03-04 17:06:43 +00:00
}
Outputs : : Display : : ScanStatus get_scaled_scan_status ( ) const override {
2024-03-22 00:41:24 +00:00
return executor_ . bus . video ( ) . crt ( ) . get_scaled_scan_status ( ) ;
2024-03-04 17:06:43 +00:00
}
2024-03-19 15:34:10 +00:00
std : : array < uint32_t , 10 > pc_history ;
std : : size_t pc_history_ptr = 0 ;
uint32_t instr_count = 0 ;
2024-03-04 17:06:43 +00:00
// MARK: - TimedMachine.
void run_for ( Cycles cycles ) override {
2024-03-20 15:42:37 +00:00
macro_counter_ + = cycles . as < int > ( ) ;
2024-03-07 15:23:46 +00:00
2024-03-20 15:42:37 +00:00
while ( macro_counter_ > 0 ) {
2024-03-22 14:01:34 +00:00
switch ( video_divider_ ) {
default : macro_tick < 2 > ( ) ; break ;
case 3 : macro_tick < 3 > ( ) ; break ;
case 4 : macro_tick < 4 > ( ) ; break ;
case 6 : macro_tick < 6 > ( ) ; break ;
}
2024-03-20 15:42:37 +00:00
}
}
2024-03-22 14:01:34 +00:00
int video_divider_ = 1 ;
2024-03-07 16:12:40 +00:00
2024-03-20 15:42:37 +00:00
void tick_cpu ( ) {
static uint32_t last_pc = 0 ;
static bool log = false ;
2024-03-20 00:26:17 +00:00
2024-03-20 15:42:37 +00:00
uint32_t instruction ;
pc_history [ pc_history_ptr ] = executor_ . pc ( ) ;
pc_history_ptr = ( pc_history_ptr + 1 ) % pc_history . size ( ) ;
if ( ! executor_ . bus . read ( executor_ . pc ( ) , instruction , executor_ . registers ( ) . mode ( ) , false ) ) {
logger . info ( ) . append ( " Prefetch abort at %08x; last good was at %08x " , executor_ . pc ( ) , last_pc ) ;
executor_ . prefetch_abort ( ) ;
2024-03-07 19:28:39 +00:00
2024-03-20 15:42:37 +00:00
// TODO: does a double abort cause a reset?
executor_ . bus . read ( executor_ . pc ( ) , instruction , executor_ . registers ( ) . mode ( ) , false ) ;
} else {
last_pc = executor_ . pc ( ) ;
}
// TODO: pipeline prefetch?
2024-03-19 15:34:10 +00:00
2024-03-20 15:42:37 +00:00
if ( executor_ . pc ( ) = = 0x03810bd8 ) {
printf ( " At %08x; after last PC %08x and %zu ago was %08x \n " , executor_ . pc ( ) , pc_history [ ( pc_history_ptr - 2 + pc_history . size ( ) ) % pc_history . size ( ) ] , pc_history . size ( ) , pc_history [ pc_history_ptr ] ) ;
}
2024-03-05 02:09:24 +00:00
2024-03-22 14:24:24 +00:00
log = executor_ . pc ( ) = = 0x03808de0 ;
2024-03-19 15:34:10 +00:00
2024-03-20 15:42:37 +00:00
if ( log ) {
InstructionSet : : ARM : : Disassembler < arm_model > disassembler ;
InstructionSet : : ARM : : dispatch < arm_model > ( instruction , disassembler ) ;
2024-03-11 16:14:00 +00:00
2024-03-20 15:42:37 +00:00
auto info = logger . info ( ) ;
info . append ( " [%d] %08x: %08x \t \t %s \t prior:[ " ,
instr_count ,
executor_ . pc ( ) ,
instruction ,
disassembler . last ( ) . to_string ( executor_ . pc ( ) ) . c_str ( ) ) ;
for ( uint32_t c = 0 ; c < 15 ; c + + ) {
info . append ( " r%d:%08x " , c , executor_ . registers ( ) [ c ] ) ;
2024-03-07 16:12:40 +00:00
}
2024-03-20 15:42:37 +00:00
info . append ( " ] " ) ;
2024-03-05 02:09:24 +00:00
}
2024-03-20 15:42:37 +00:00
// logger.info().append("%08x: %08x", executor_.pc(), instruction);
InstructionSet : : ARM : : execute ( instruction , executor_ ) ;
+ + instr_count ;
// if(
// executor_.pc() > 0x038021d0 &&
// last_r1 != executor_.registers()[1]
// ||
// (
// last_link != executor_.registers()[14] ||
// last_r0 != executor_.registers()[0] ||
// last_r10 != executor_.registers()[10] ||
// last_r1 != executor_.registers()[1]
// )
// ) {
// logger.info().append("%08x modified R14 to %08x; R0 to %08x; R10 to %08x; R1 to %08x",
// last_pc,
// executor_.registers()[14],
// executor_.registers()[0],
// executor_.registers()[10],
// executor_.registers()[1]
// );
// logger.info().append("%08x modified R1 to %08x",
// last_pc,
// executor_.registers()[1]
// );
// last_link = executor_.registers()[14];
// last_r0 = executor_.registers()[0];
// last_r10 = executor_.registers()[10];
// last_r1 = executor_.registers()[1];
// }
2024-03-05 02:09:24 +00:00
}
2024-03-20 18:25:20 +00:00
void tick_timers ( ) { executor_ . bus . tick_timers ( ) ; }
2024-03-22 00:41:24 +00:00
void tick_sound ( ) { executor_ . bus . sound ( ) . tick ( ) ; }
void tick_video ( ) { executor_ . bus . video ( ) . tick ( ) ; }
2024-03-20 15:42:37 +00:00
2024-03-05 02:09:24 +00:00
// MARK: - MediaTarget
bool insert_media ( const Analyser : : Static : : Media & ) override {
// int c = 0;
// for(auto &disk : media.disks) {
// fdc_.set_disk(disk, c);
// c++;
// if(c == 4) break;
// }
// return true;
return false ;
2024-03-04 17:06:43 +00:00
}
2024-03-04 17:08:46 +00:00
// MARK: - ARM execution
2024-03-05 02:09:24 +00:00
static constexpr auto arm_model = InstructionSet : : ARM : : Model : : ARMv2 ;
2024-03-22 14:01:34 +00:00
InstructionSet : : ARM : : Executor < arm_model , MemoryController < ConcreteMachine , ConcreteMachine > > executor_ ;
2024-03-04 17:06:43 +00:00
} ;
}
using namespace Archimedes ;
std : : unique_ptr < Machine > Machine : : Archimedes ( const Analyser : : Static : : Target * target , const ROMMachine : : ROMFetcher & rom_fetcher ) {
return std : : make_unique < ConcreteMachine > ( * target , rom_fetcher ) ;
}