2021-07-16 20:30:48 -04: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-19 22:30:34 -04:00
# include "../../Activity/Source.hpp"
2021-07-16 20:49:12 -04:00
# include "../MachineTypes.hpp"
2023-05-10 17:13:01 -05:00
# include "../../Processors/68000/68000.hpp"
2021-07-16 21:07:12 -04:00
2021-07-16 20:30:48 -04:00
# include "../../Analyser/Static/Amiga/Target.hpp"
2021-07-16 21:07:12 -04:00
# include "../Utility/MemoryPacker.hpp"
# include "../Utility/MemoryFuzzer.hpp"
2021-07-18 20:25:43 -04:00
# include "../../Outputs/Log.hpp"
2021-07-22 21:16:23 -04:00
# include "Chipset.hpp"
2021-11-06 12:03:09 -07:00
# include "Keyboard.hpp"
2021-10-04 06:44:54 -07:00
# include "MemoryMap.hpp"
2021-07-22 18:43:07 -04:00
2021-12-09 19:17:44 -05:00
# include <cassert>
2021-10-04 08:12:13 -07: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-23 20:17:13 -07:00
//constexpr int NTSCClockRate = 7'159'090;
2021-10-04 08:12:13 -07:00
2024-01-19 22:14:24 -05:00
Log : : Logger < Log : : Source : : Amiga > logger ;
2021-10-04 08:12:13 -07:00
}
2021-07-16 20:30:48 -04:00
namespace Amiga {
class ConcreteMachine :
2021-07-19 22:30:34 -04:00
public Activity : : Source ,
2023-05-10 17:13:01 -05:00
public CPU : : MC68000 : : BusHandler ,
2021-12-01 05:37:58 -05:00
public MachineTypes : : AudioProducer ,
2021-11-17 15:33:46 -05:00
public MachineTypes : : JoystickMachine ,
2021-11-06 12:03:09 -07:00
public MachineTypes : : MappedKeyboardMachine ,
2021-10-05 16:12:30 -07:00
public MachineTypes : : MediaTarget ,
2021-10-23 20:17:13 -07:00
public MachineTypes : : MouseMachine ,
2021-07-16 20:49:12 -04:00
public MachineTypes : : ScanProducer ,
public MachineTypes : : TimedMachine ,
2021-07-16 20:30:48 -04:00
public Machine {
public :
2021-07-16 21:07:12 -04:00
ConcreteMachine ( const Analyser : : Static : : Amiga : : Target & target , const ROMMachine : : ROMFetcher & rom_fetcher ) :
2021-07-18 11:49:10 -04:00
mc68000_ ( * this ) ,
2021-12-22 15:30:19 -05:00
memory_ ( target . chip_ram , target . fast_ram ) ,
2021-10-04 08:12:13 -07:00
chipset_ ( memory_ , PALClockRate )
2021-07-16 21:07:12 -04:00
{
// 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-07-22 18:43:07 -04:00
Memory : : PackBigEndian16 ( roms . find ( rom_name ) - > second , memory_ . kickstart . data ( ) ) ;
2021-07-17 21:10:06 -04:00
2021-10-05 16:12:30 -07:00
// For now, also hard-code assumption of PAL.
// (Assumption is both here and in the video timing of the Chipset).
2021-10-04 08:12:13 -07:00
set_clock_rate ( PALClockRate ) ;
2021-10-05 16:12:30 -07:00
// Insert supplied media.
insert_media ( target . media ) ;
}
// MARK: - MediaTarget.
bool insert_media ( const Analyser : : Static : : Media & media ) final {
return chipset_ . insert ( media . disks ) ;
2021-07-17 21:10:06 -04:00
}
// MARK: - MC68000::BusHandler.
2023-12-21 23:08:18 -05:00
template < typename Microcycle > HalfCycles perform_bus_operation ( const Microcycle & cycle , int ) {
2021-08-08 21:52:28 -04:00
// Do a quick advance check for Chip RAM access; add a suitable delay if required.
2021-12-09 19:17:44 -05:00
HalfCycles total_length ;
2023-12-21 23:08:18 -05:00
if ( cycle . operation & CPU : : MC68000 : : Operation : : NewAddress & & * cycle . address < 0x20'0000 ) {
2021-12-09 19:17:44 -05:00
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-08-08 21:52:28 -04:00
}
2021-07-28 19:36:30 -04:00
mc68000_ . set_interrupt_level ( chipset_ . get_interrupt_level ( ) ) ;
2021-07-22 21:45:51 -04:00
2021-07-19 21:50:35 -04:00
// Check for assertion of reset.
2023-12-21 23:08:18 -05:00
if ( cycle . operation & CPU : : MC68000 : : Operation : : Reset ) {
2021-07-19 22:17:40 -04:00
memory_ . reset ( ) ;
2024-01-19 22:14:24 -05:00
logger . info ( ) . append ( " Reset; PC is around %08x " , mc68000_ . get_state ( ) . registers . program_counter ) ;
2021-07-19 21:50:35 -04:00
}
2021-07-26 16:40:42 -04:00
// Autovector interrupts.
2023-12-21 23:08:18 -05:00
if ( cycle . operation & CPU : : MC68000 : : Operation : : InterruptAcknowledge ) {
2021-07-26 16:40:42 -04:00
mc68000_ . set_is_peripheral_address ( true ) ;
2021-12-09 19:17:44 -05:00
return total_length - cycle . length ;
2021-07-26 16:40:42 -04:00
}
2021-07-18 11:49:10 -04:00
// Do nothing if no address is exposed.
2023-12-21 23:08:18 -05:00
if ( ! ( cycle . operation & ( CPU : : MC68000 : : Operation : : NewAddress | CPU : : MC68000 : : Operation : : SameAddress ) ) ) return total_length - cycle . length ;
2021-07-18 11:49:10 -04:00
2021-07-17 21:10:06 -04:00
// Grab the target address to pick a memory source.
const uint32_t address = cycle . host_endian_byte_address ( ) ;
2021-08-05 20:06:48 -04:00
// Set VPA if this is [going to be] a CIA access.
mc68000_ . set_is_peripheral_address ( ( address & 0xe0'0000 ) = = 0xa0'0000 ) ;
2021-07-17 21:36:20 -04:00
2021-07-22 18:43:07 -04:00
if ( ! memory_ . regions [ address > > 18 ] . read_write_mask ) {
2023-12-21 23:08:18 -05:00
if ( ( cycle . operation & ( CPU : : MC68000 : : Operation : : SelectByte | CPU : : MC68000 : : Operation : : SelectWord ) ) ) {
2021-07-17 21:36:20 -04:00
// Check for various potential chip accesses.
2021-07-18 12:13:56 -04:00
2021-07-18 17:17:41 -04:00
// Per the manual:
//
2021-07-18 12:13:56 -04:00
// 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)
2021-07-18 17:17:41 -04:00
//
// 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.
2021-07-18 12:13:56 -04:00
if ( ( address & 0xe0'0000 ) = = 0xa0'0000 ) {
const int reg = address > > 8 ;
2021-10-30 12:05:18 -07:00
const bool select_a = ! ( address & 0x1000 ) ;
const bool select_b = ! ( address & 0x2000 ) ;
2021-07-18 12:13:56 -04:00
2023-12-21 23:08:18 -05:00
if ( cycle . operation & CPU : : MC68000 : : Operation : : Read ) {
2021-07-18 12:13:56 -04:00
uint16_t result = 0xffff ;
2021-10-30 12:05:18 -07:00
if ( select_a ) result & = 0xff00 | ( chipset_ . cia_a . read ( reg ) < < 0 ) ;
if ( select_b ) result & = 0x00ff | ( chipset_ . cia_b . read ( reg ) < < 8 ) ;
2021-07-18 12:13:56 -04:00
cycle . set_value16 ( result ) ;
} else {
2021-10-30 12:05:18 -07:00
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 12:13:56 -04:00
}
2021-07-30 18:22:59 -04:00
2024-01-19 22:14:24 -05:00
// logger.info().append("CIA %d %s %d of %04x", ((address >> 12) & 3)^3, operation & Microcycle::Read ? "read" : "write", reg & 0xf, cycle.value16());
2021-07-17 21:36:20 -04:00
} else if ( address > = 0xdf'f000 & & address < = 0xdf'f1be ) {
2021-07-22 21:16:23 -04:00
chipset_ . perform ( cycle ) ;
2021-12-22 15:17:11 -05:00
} 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.
//
// Relevant quote: "The Zorro II configuration space is the 64K memory block $00E8xxxx"
memory_ . perform ( cycle ) ;
2021-07-17 21:36:20 -04:00
} else {
// This'll do for open bus, for now.
2023-12-21 23:08:18 -05:00
if ( cycle . operation & CPU : : MC68000 : : Operation : : Read ) {
2021-07-21 21:49:20 -04:00
cycle . set_value16 ( 0xffff ) ;
}
2021-07-26 17:02:30 -04:00
2024-01-19 22:14:24 -05:00
// Log only for the region that is definitely not just ROM this machine doesn't have.
2021-07-26 17:02:30 -04:00
if ( address < 0xf0'0000 ) {
2024-01-19 22:14:24 -05:00
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-26 17:02:30 -04:00
}
2021-07-17 21:36:20 -04:00
}
}
} else {
// A regular memory access.
cycle . apply (
2021-07-22 18:43:07 -04:00
& memory_ . regions [ address > > 18 ] . contents [ address ] ,
memory_ . regions [ address > > 18 ] . read_write_mask
2021-07-17 21:36:20 -04:00
) ;
2021-07-17 21:10:06 -04:00
}
2021-07-16 21:07:12 -04:00
2021-12-09 19:17:44 -05:00
return total_length - cycle . length ;
2021-07-16 21:07:12 -04:00
}
2021-07-16 20:49:12 -04:00
private :
2023-05-10 17:13:01 -05:00
CPU : : MC68000 : : Processor < ConcreteMachine , true , true > mc68000_ ;
2021-07-16 21:41:32 -04:00
// MARK: - Memory map.
2021-10-04 06:44:54 -07:00
MemoryMap memory_ ;
2021-07-17 21:10:06 -04:00
2021-07-22 21:16:23 -04:00
// MARK: - Chipset.
2021-07-18 20:25:43 -04:00
2021-07-22 21:16:23 -04:00
Chipset chipset_ ;
2023-05-12 14:16:39 -04:00
2021-07-19 22:30:34 -04:00
// MARK: - Activity Source
2021-12-01 05:37:58 -05:00
2021-07-19 22:30:34 -04:00
void set_activity_observer ( Activity : : Observer * observer ) final {
2021-10-04 06:44:54 -07:00
chipset_ . set_activity_observer ( observer ) ;
2021-07-19 22:30:34 -04:00
}
2021-12-01 05:37:58 -05:00
// MARK: - MachineTypes::AudioProducer.
Outputs : : Speaker : : Speaker * get_speaker ( ) final {
return chipset_ . get_speaker ( ) ;
}
2021-07-16 20:49:12 -04:00
// MARK: - MachineTypes::ScanProducer.
void set_scan_target ( Outputs : : Display : : ScanTarget * scan_target ) final {
2021-07-26 18:44:01 -04:00
chipset_ . set_scan_target ( scan_target ) ;
2021-07-16 20:49:12 -04:00
}
2022-07-07 16:41:49 -04:00
Outputs : : Display : : ScanStatus get_scaled_scan_status ( ) const final {
2021-07-26 18:44:01 -04:00
return chipset_ . get_scaled_scan_status ( ) ;
2021-07-16 20:30:48 -04:00
}
2021-07-16 20:49:12 -04:00
// MARK: - MachineTypes::TimedMachine.
2022-07-07 16:41:49 -04:00
void run_for ( const Cycles cycles ) final {
2021-07-16 21:07:12 -04:00
mc68000_ . run_for ( cycles ) ;
2022-07-07 16:41:49 -04:00
}
2022-07-09 13:33:46 -04:00
void flush_output ( int ) final {
2022-07-08 16:04:32 -04:00
chipset_ . flush ( ) ;
2021-07-16 20:49:12 -04:00
}
2021-10-23 20:17:13 -07:00
// MARK: - MachineTypes::MouseMachine.
Inputs : : Mouse & get_mouse ( ) final {
2024-02-12 14:23:54 -05:00
return chipset_ . get_mouse ( ) ;
2021-10-23 20:17:13 -07:00
}
2021-11-06 12:03:09 -07:00
2021-11-17 15:33:46 -05:00
// MARK: - MachineTypes::JoystickMachine.
2022-07-07 16:41:49 -04:00
const std : : vector < std : : unique_ptr < Inputs : : Joystick > > & get_joysticks ( ) final {
2021-11-17 15:33:46 -05:00
return chipset_ . get_joysticks ( ) ;
}
2021-11-06 12:03:09 -07:00
// MARK: - Keyboard.
Amiga : : KeyboardMapper keyboard_mapper_ ;
KeyboardMapper * get_keyboard_mapper ( ) {
return & keyboard_mapper_ ;
}
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-16 20:30:48 -04:00
}
using namespace Amiga ;
2024-01-12 22:03:19 -05:00
std : : unique_ptr < Machine > Machine : : Amiga ( const Analyser : : Static : : Target * target , const ROMMachine : : ROMFetcher & rom_fetcher ) {
2021-07-16 20:30:48 -04:00
using Target = Analyser : : Static : : Amiga : : Target ;
const Target * const amiga_target = dynamic_cast < const Target * > ( target ) ;
2024-01-12 22:03:19 -05:00
return std : : make_unique < Amiga : : ConcreteMachine > ( * amiga_target , rom_fetcher ) ;
2021-07-16 20:30:48 -04:00
}