2021-07-17 00:30:48 +00:00
//
// Amiga.cpp
// Clock Signal
//
// Created by Thomas Harte on 16/07/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
# include "Amiga.hpp"
2021-07-20 02:30:34 +00:00
# include "../../Activity/Source.hpp"
2021-07-17 00:49:12 +00:00
# include "../MachineTypes.hpp"
2023-05-10 22:13:01 +00:00
# include "../../Processors/68000/68000.hpp"
2021-07-17 01:07:12 +00:00
2021-07-17 00:30:48 +00:00
# include "../../Analyser/Static/Amiga/Target.hpp"
2021-07-17 01:07:12 +00:00
# include "../Utility/MemoryPacker.hpp"
# include "../Utility/MemoryFuzzer.hpp"
2021-07-19 00:25:43 +00:00
# include "../../Outputs/Log.hpp"
2021-07-23 01:16:23 +00:00
# include "Chipset.hpp"
2021-11-06 19:03:09 +00:00
# include "Keyboard.hpp"
2021-10-04 13:44:54 +00:00
# include "MemoryMap.hpp"
2021-07-22 22:43:07 +00:00
2021-12-10 00:17:44 +00:00
# include <cassert>
2021-10-04 15:12:13 +00:00
namespace {
// NTSC clock rate: 2*3.579545 = 7.15909Mhz.
// PAL clock rate: 7.09379Mhz; 227 cycles/line.
constexpr int PALClockRate = 7'093'790 ;
2021-10-24 03:17:13 +00:00
//constexpr int NTSCClockRate = 7'159'090;
2021-10-04 15:12:13 +00:00
2024-01-20 03:14:24 +00:00
Log : : Logger < Log : : Source : : Amiga > logger ;
2021-10-04 15:12:13 +00:00
}
2021-07-17 00:30:48 +00:00
namespace Amiga {
class ConcreteMachine :
2021-07-20 02:30:34 +00:00
public Activity : : Source ,
2023-05-10 22:13:01 +00:00
public CPU : : MC68000 : : BusHandler ,
2021-12-01 10:37:58 +00:00
public MachineTypes : : AudioProducer ,
2021-11-17 20:33:46 +00:00
public MachineTypes : : JoystickMachine ,
2021-11-06 19:03:09 +00:00
public MachineTypes : : MappedKeyboardMachine ,
2021-10-05 23:12:30 +00:00
public MachineTypes : : MediaTarget ,
2021-10-24 03:17:13 +00:00
public MachineTypes : : MouseMachine ,
2021-07-17 00:49:12 +00:00
public MachineTypes : : ScanProducer ,
public MachineTypes : : TimedMachine ,
2021-07-17 00:30:48 +00:00
public Machine {
2024-12-02 02:44:14 +00:00
public :
ConcreteMachine ( const Analyser : : Static : : Amiga : : Target & target , const ROMMachine : : ROMFetcher & rom_fetcher ) :
mc68000_ ( * this ) ,
memory_ ( target . chip_ram , target . fast_ram ) ,
chipset_ ( memory_ , PALClockRate )
{
// Temporary: use a hard-coded Kickstart selection.
constexpr ROM : : Name rom_name = ROM : : Name : : AmigaA500Kickstart13 ;
ROM : : Request request ( rom_name ) ;
auto roms = rom_fetcher ( request ) ;
if ( ! request . validate ( roms ) ) {
throw ROMMachine : : Error : : MissingROMs ;
2021-10-05 23:12:30 +00:00
}
2024-12-02 02:44:14 +00:00
Memory : : PackBigEndian16 ( roms . find ( rom_name ) - > second , memory_ . kickstart . data ( ) ) ;
// For now, also hard-code assumption of PAL.
// (Assumption is both here and in the video timing of the Chipset).
set_clock_rate ( PALClockRate ) ;
// Insert supplied media.
insert_media ( target . media ) ;
}
// MARK: - MediaTarget.
bool insert_media ( const Analyser : : Static : : Media & media ) final {
return chipset_ . insert ( media . disks ) ;
}
// MARK: - MC68000::BusHandler.
template < typename Microcycle > HalfCycles perform_bus_operation ( const Microcycle & cycle , int ) {
// Do a quick advance check for Chip RAM access; add a suitable delay if required.
HalfCycles total_length ;
if ( cycle . operation & CPU : : MC68000 : : Operation : : NewAddress & & * cycle . address < 0x20'0000 ) {
total_length = chipset_ . run_until_after_cpu_slot ( ) . duration ;
assert ( total_length > = cycle . length ) ;
} else {
total_length = cycle . length ;
chipset_ . run_for ( total_length ) ;
2021-07-18 01:10:06 +00:00
}
2024-12-02 02:44:14 +00:00
mc68000_ . set_interrupt_level ( chipset_ . get_interrupt_level ( ) ) ;
2021-07-18 01:10:06 +00:00
2024-12-02 02:44:14 +00:00
// Check for assertion of reset.
if ( cycle . operation & CPU : : MC68000 : : Operation : : Reset ) {
memory_ . reset ( ) ;
logger . info ( ) . append ( " Reset; PC is around %08x " , mc68000_ . get_state ( ) . registers . program_counter ) ;
}
2021-08-06 00:06:48 +00:00
2024-12-02 02:44:14 +00:00
// Autovector interrupts.
if ( cycle . operation & CPU : : MC68000 : : Operation : : InterruptAcknowledge ) {
mc68000_ . set_is_peripheral_address ( true ) ;
return total_length - cycle . length ;
}
2021-07-18 01:36:20 +00:00
2024-12-02 02:44:14 +00:00
// Do nothing if no address is exposed.
if ( ! ( cycle . operation & ( CPU : : MC68000 : : Operation : : NewAddress | CPU : : MC68000 : : Operation : : SameAddress ) ) ) return total_length - cycle . length ;
// Grab the target address to pick a memory source.
const uint32_t address = cycle . host_endian_byte_address ( ) ;
// Set VPA if this is [going to be] a CIA access.
mc68000_ . set_is_peripheral_address ( ( address & 0xe0'0000 ) = = 0xa0'0000 ) ;
if ( ! memory_ . regions [ address > > 18 ] . read_write_mask ) {
if ( ( cycle . operation & ( CPU : : MC68000 : : Operation : : SelectByte | CPU : : MC68000 : : Operation : : SelectWord ) ) ) {
// Check for various potential chip accesses.
// Per the manual:
//
// CIA A is: 101x xxxx xx01 rrrr xxxx xxx0 (i.e. loaded into high byte)
// CIA B is: 101x xxxx xx10 rrrr xxxx xxx1 (i.e. loaded into low byte)
//
// but in order to map 0xbfexxx to CIA A and 0xbfdxxx to CIA B, I think
// these might be listed the wrong way around.
//
// Additional assumption: the relevant CIA select lines are connected
// directly to the chip enables.
if ( ( address & 0xe0'0000 ) = = 0xa0'0000 ) {
const int reg = address > > 8 ;
const bool select_a = ! ( address & 0x1000 ) ;
const bool select_b = ! ( address & 0x2000 ) ;
if ( cycle . operation & CPU : : MC68000 : : Operation : : Read ) {
uint16_t result = 0xffff ;
if ( select_a ) result & = 0xff00 | ( chipset_ . cia_a . read ( reg ) < < 0 ) ;
if ( select_b ) result & = 0x00ff | ( chipset_ . cia_b . read ( reg ) < < 8 ) ;
cycle . set_value16 ( result ) ;
} else {
if ( select_a ) chipset_ . cia_a . write ( reg , cycle . value8_low ( ) ) ;
if ( select_b ) chipset_ . cia_b . write ( reg , cycle . value8_high ( ) ) ;
}
2021-07-18 16:13:56 +00:00
2024-12-02 02:44:14 +00:00
// logger.info().append("CIA %d %s %d of %04x", ((address >> 12) & 3)^3, operation & Microcycle::Read ? "read" : "write", reg & 0xf, cycle.value16());
} else if ( address > = 0xdf'f000 & & address < = 0xdf'f1be ) {
chipset_ . perform ( cycle ) ;
} else if ( address > = 0xe8'0000 & & address < 0xe9'0000 ) {
// This is the Autoconf space; right now the only
// Autoconf device this emulator implements is fast RAM,
// which if present is provided as part of the memory map.
2021-07-18 21:17:41 +00:00
//
2024-12-02 02:44:14 +00:00
// Relevant quote: "The Zorro II configuration space is the 64K memory block $00E8xxxx"
memory_ . perform ( cycle ) ;
} else {
// This'll do for open bus, for now.
if ( cycle . operation & CPU : : MC68000 : : Operation : : Read ) {
cycle . set_value16 ( 0xffff ) ;
}
2021-07-30 22:22:59 +00:00
2024-12-02 02:44:14 +00:00
// Log only for the region that is definitely not just ROM this machine doesn't have.
if ( address < 0xf0'0000 ) {
logger . error ( ) . append ( " Unmapped %s %06x of %04x " , cycle . operation & CPU : : MC68000 : : Operation : : Read ? " read from " : " write to " , ( * cycle . address ) & 0xffffff , cycle . value16 ( ) ) ;
2021-07-18 01:36:20 +00:00
}
}
2021-07-18 01:10:06 +00:00
}
2024-12-02 02:44:14 +00:00
} else {
// A regular memory access.
cycle . apply (
& memory_ . regions [ address > > 18 ] . contents [ address ] ,
memory_ . regions [ address > > 18 ] . read_write_mask
) ;
2021-07-17 01:07:12 +00:00
}
2021-07-17 00:49:12 +00:00
2024-12-02 02:44:14 +00:00
return total_length - cycle . length ;
}
2021-07-17 01:41:32 +00:00
2024-12-02 02:44:14 +00:00
private :
CPU : : MC68000 : : Processor < ConcreteMachine , true , true > mc68000_ ;
2021-07-17 01:41:32 +00:00
2024-12-02 02:44:14 +00:00
// MARK: - Memory map.
2021-07-18 01:10:06 +00:00
2024-12-02 02:44:14 +00:00
MemoryMap memory_ ;
2021-07-19 00:25:43 +00:00
2024-12-02 02:44:14 +00:00
// MARK: - Chipset.
2023-05-12 18:16:39 +00:00
2024-12-02 02:44:14 +00:00
Chipset chipset_ ;
2021-12-01 10:37:58 +00:00
2024-12-02 02:44:14 +00:00
// MARK: - Activity Source
2021-07-20 02:30:34 +00:00
2024-12-02 02:44:14 +00:00
void set_activity_observer ( Activity : : Observer * observer ) final {
chipset_ . set_activity_observer ( observer ) ;
}
2021-12-01 10:37:58 +00:00
2024-12-02 02:44:14 +00:00
// MARK: - MachineTypes::AudioProducer.
2021-12-01 10:37:58 +00:00
2024-12-02 02:44:14 +00:00
Outputs : : Speaker : : Speaker * get_speaker ( ) final {
return chipset_ . get_speaker ( ) ;
}
2021-07-17 00:49:12 +00:00
2024-12-02 02:44:14 +00:00
// MARK: - MachineTypes::ScanProducer.
2021-07-17 00:49:12 +00:00
2024-12-02 02:44:14 +00:00
void set_scan_target ( Outputs : : Display : : ScanTarget * scan_target ) final {
chipset_ . set_scan_target ( scan_target ) ;
}
2021-07-17 00:30:48 +00:00
2024-12-02 02:44:14 +00:00
Outputs : : Display : : ScanStatus get_scaled_scan_status ( ) const final {
return chipset_ . get_scaled_scan_status ( ) ;
}
2021-07-17 00:49:12 +00:00
2024-12-02 02:44:14 +00:00
// MARK: - MachineTypes::TimedMachine.
2022-07-07 20:41:49 +00:00
2024-12-02 02:44:14 +00:00
void run_for ( const Cycles cycles ) final {
mc68000_ . run_for ( cycles ) ;
}
2021-10-24 03:17:13 +00:00
2024-12-02 02:44:14 +00:00
void flush_output ( int ) final {
chipset_ . flush ( ) ;
}
2021-10-24 03:17:13 +00:00
2024-12-02 02:44:14 +00:00
// MARK: - MachineTypes::MouseMachine.
2021-11-06 19:03:09 +00:00
2024-12-02 02:44:14 +00:00
Inputs : : Mouse & get_mouse ( ) final {
return chipset_ . get_mouse ( ) ;
}
2021-11-17 20:33:46 +00:00
2024-12-02 02:44:14 +00:00
// MARK: - MachineTypes::JoystickMachine.
2021-11-17 20:33:46 +00:00
2024-12-02 02:44:14 +00:00
const std : : vector < std : : unique_ptr < Inputs : : Joystick > > & get_joysticks ( ) final {
return chipset_ . get_joysticks ( ) ;
}
2021-11-06 19:03:09 +00:00
2024-12-02 02:44:14 +00:00
// MARK: - Keyboard.
2021-11-06 19:03:09 +00:00
2024-12-02 02:44:14 +00:00
Amiga : : KeyboardMapper keyboard_mapper_ ;
KeyboardMapper * get_keyboard_mapper ( ) {
return & keyboard_mapper_ ;
}
2021-11-06 19:03:09 +00:00
2024-12-02 02:44:14 +00:00
void set_key_state ( uint16_t key , bool is_pressed ) {
chipset_ . get_keyboard ( ) . set_key_state ( key , is_pressed ) ;
}
void clear_all_keys ( ) {
chipset_ . get_keyboard ( ) . clear_all_keys ( ) ;
}
} ;
2021-07-17 00:30:48 +00:00
}
using namespace Amiga ;
2024-01-13 03:03:19 +00:00
std : : unique_ptr < Machine > Machine : : Amiga ( const Analyser : : Static : : Target * target , const ROMMachine : : ROMFetcher & rom_fetcher ) {
2021-07-17 00:30:48 +00:00
using Target = Analyser : : Static : : Amiga : : Target ;
const Target * const amiga_target = dynamic_cast < const Target * > ( target ) ;
2024-01-13 03:03:19 +00:00
return std : : make_unique < Amiga : : ConcreteMachine > ( * amiga_target , rom_fetcher ) ;
2021-07-17 00:30:48 +00:00
}