2020-10-21 02:18:11 +00:00
//
// AppleIIgs.cpp
// Clock Signal
//
// Created by Thomas Harte on 20/10/2020.
// Copyright 2020 Thomas Harte. All rights reserved.
//
# include "AppleIIgs.hpp"
2020-11-14 23:00:06 +00:00
# include "../../../Activity/Source.hpp"
2020-10-22 01:19:18 +00:00
# include "../../MachineTypes.hpp"
# include "../../../Processors/65816/65816.hpp"
2020-10-21 02:18:11 +00:00
# include "../../../Analyser/Static/AppleIIgs/Target.hpp"
2020-11-01 01:00:15 +00:00
# include "ADB.hpp"
2020-10-26 01:10:04 +00:00
# include "MemoryMap.hpp"
2020-11-01 00:39:32 +00:00
# include "Video.hpp"
2020-11-05 02:29:44 +00:00
# include "Sound.hpp"
2020-10-23 23:41:10 +00:00
2020-10-29 02:07:34 +00:00
# include "../../../Components/8530/z8530.hpp"
2020-10-30 01:38:36 +00:00
# include "../../../Components/AppleClock/AppleClock.hpp"
2020-11-12 02:04:38 +00:00
# include "../../../Components/AudioToggle/AudioToggle.hpp"
2020-10-29 02:07:34 +00:00
# include "../../../Components/DiskII/IWM.hpp"
2020-11-10 23:38:23 +00:00
# include "../../../Components/DiskII/MacintoshDoubleDensityDrive.hpp"
2020-11-21 02:37:17 +00:00
# include "../../../Components/DiskII/DiskIIDrive.hpp"
2020-10-29 02:07:34 +00:00
2020-11-18 23:39:11 +00:00
# include "../../../Outputs/Speaker/Implementation/CompoundSource.hpp"
2020-11-12 02:04:38 +00:00
# include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
2020-11-01 00:03:23 +00:00
# include "../../Utility/MemoryFuzzer.hpp"
2020-11-08 00:40:26 +00:00
# include "../../../ClockReceiver/JustInTime.hpp"
2020-10-25 22:28:32 +00:00
# include <cassert>
# include <array>
2020-11-05 22:56:20 +00:00
namespace {
constexpr int CLOCK_RATE = 14318180 ;
}
2020-10-21 02:18:11 +00:00
namespace Apple {
namespace IIgs {
2020-10-22 01:19:18 +00:00
class ConcreteMachine :
2020-11-14 23:00:06 +00:00
public Activity : : Source ,
2020-10-22 01:19:18 +00:00
public Apple : : IIgs : : Machine ,
2020-11-12 02:04:38 +00:00
public MachineTypes : : AudioProducer ,
2020-11-12 23:01:26 +00:00
public MachineTypes : : MediaTarget ,
public MachineTypes : : ScanProducer ,
public MachineTypes : : TimedMachine ,
2020-10-22 01:19:18 +00:00
public CPU : : MOS6502Esque : : BusHandler < uint32_t > {
public :
ConcreteMachine ( const Analyser : : Static : : AppleIIgs : : Target & target , const ROMMachine : : ROMFetcher & rom_fetcher ) :
2020-11-10 23:38:23 +00:00
m65816_ ( * this ) ,
2020-11-13 02:44:51 +00:00
iwm_ ( CLOCK_RATE / 2 ) ,
2020-11-21 02:37:17 +00:00
drives35_ {
2020-11-12 23:09:31 +00:00
{ CLOCK_RATE / 2 , true } ,
{ CLOCK_RATE / 2 , true }
2020-11-12 02:04:38 +00:00
} ,
2020-11-21 02:37:17 +00:00
drives525_ {
{ CLOCK_RATE / 2 } ,
{ CLOCK_RATE / 2 }
} ,
2020-11-18 23:39:11 +00:00
sound_glu_ ( audio_queue_ ) ,
2020-11-12 02:04:38 +00:00
audio_toggle_ ( audio_queue_ ) ,
2020-11-18 23:39:11 +00:00
mixer_ ( sound_glu_ , audio_toggle_ ) ,
speaker_ ( mixer_ ) {
2020-10-22 01:19:18 +00:00
2020-11-05 22:56:20 +00:00
set_clock_rate ( double ( CLOCK_RATE ) ) ;
2020-11-12 02:04:38 +00:00
speaker_ . set_input_rate ( float ( CLOCK_RATE ) / float ( audio_divider ) ) ;
2020-10-22 01:19:18 +00:00
using Target = Analyser : : Static : : AppleIIgs : : Target ;
std : : vector < ROMMachine : : ROM > rom_descriptions ;
const std : : string machine_name = " AppleIIgs " ;
switch ( target . model ) {
case Target : : Model : : ROM00 :
/* TODO */
case Target : : Model : : ROM01 :
rom_descriptions . emplace_back ( machine_name , " the Apple IIgs ROM01 " , " apple2gs.rom " , 128 * 1024 , 0x42f124b0 ) ;
break ;
case Target : : Model : : ROM03 :
rom_descriptions . emplace_back ( machine_name , " the Apple IIgs ROM03 " , " apple2gs.rom2 " , 256 * 1024 , 0xde7ddf29 ) ;
break ;
}
2020-11-26 17:54:20 +00:00
rom_descriptions . push_back ( video_ - > rom_description ( Video : : Video : : CharacterROM : : EnhancedIIe ) ) ;
2020-11-07 22:45:03 +00:00
2020-10-22 01:19:18 +00:00
const auto roms = rom_fetcher ( rom_descriptions ) ;
2020-11-07 22:45:03 +00:00
if ( ! roms [ 0 ] | | ! roms [ 1 ] ) {
2020-10-22 01:19:18 +00:00
throw ROMMachine : : Error : : MissingROMs ;
}
2020-10-22 23:25:04 +00:00
rom_ = * roms [ 0 ] ;
2020-11-08 00:40:26 +00:00
video_ - > set_character_rom ( * roms [ 1 ] ) ;
2020-10-22 23:25:04 +00:00
2020-12-03 01:48:19 +00:00
// Run only the currently-interesting self test.
rom_ [ 0x36402 ] = 2 ;
// rom_[0x36403] = 0x7c; // ROM_CHECKSUM [working, when hacks like this are removed]
// rom_[0x36404] = 0x6c;
// rom_[0x36403] = 0x82; // MOVIRAM [working]
// rom_[0x36404] = 0x67;
// rom_[0x36403] = 0x2c; // SOFT_SW [working]
// rom_[0x36404] = 0x6a;
// rom_[0x36403] = 0xe8; // RAM_ADDR [working]
// rom_[0x36404] = 0x6f;
// rom_[0x36403] = 0xc7; // FPI_SPEED [working]
// rom_[0x36404] = 0x6a;
// rom_[0x36403] = 0xd7; // SER_TST [broken]
// rom_[0x36404] = 0x68;
// rom_[0x36403] = 0xdc; // CLOCK [broken]
// rom_[0x36404] = 0x6c;
rom_ [ 0x36403 ] = 0x1b ; // BAT_RAM [broken]
rom_ [ 0x36404 ] = 0x6e ;
// rom_[0x36403] = 0x11; // FDB (/ADB?) [broken]
// rom_[0x36404] = 0x6f;
// rom_[0x36403] = 0x41; // SHADOW_TST [working]
// rom_[0x36404] = 0x6d;
// rom_[0x36403] = 0x09; // CUSTOM_IRQ [broken?]
// rom_[0x36404] = 0x6b;
// rom_[0x36403] = 0xf4; // DOC_EXEC
// rom_[0x36404] = 0x70;
// rom_[0x36403] = 0xab; // ECT_SEQ
// rom_[0x36404] = 0x64;
2020-10-22 23:25:04 +00:00
size_t ram_size = 0 ;
switch ( target . memory_model ) {
case Target : : MemoryModel : : TwoHundredAndFiftySixKB :
ram_size = 256 ;
break ;
case Target : : MemoryModel : : OneMB :
2020-10-23 23:41:10 +00:00
ram_size = 128 + 1024 ;
2020-10-22 23:25:04 +00:00
break ;
case Target : : MemoryModel : : EightMB :
2020-10-23 23:41:10 +00:00
ram_size = 128 + 8 * 1024 ;
2020-10-22 23:25:04 +00:00
break ;
}
ram_ . resize ( ram_size * 1024 ) ;
2020-10-26 01:31:21 +00:00
memory_ . set_storage ( ram_ , rom_ ) ;
2020-11-08 00:40:26 +00:00
video_ - > set_internal_ram ( & ram_ [ ram_ . size ( ) - 128 * 1024 ] ) ;
2020-10-29 01:23:45 +00:00
2020-11-10 00:05:48 +00:00
// Select appropriate ADB behaviour.
adb_glu_ . set_is_rom03 ( target . model = = Target : : Model : : ROM03 ) ;
2020-11-10 23:38:23 +00:00
// Attach drives to the IWM.
2020-11-21 02:37:17 +00:00
iwm_ - > set_drive ( 0 , & drives35_ [ 0 ] ) ;
iwm_ - > set_drive ( 1 , & drives35_ [ 1 ] ) ;
2020-11-10 23:38:23 +00:00
2020-11-26 04:33:55 +00:00
// Randomise RAM contents.
Memory : : Fuzz ( ram_ ) ;
2020-11-01 00:03:23 +00:00
2020-10-29 01:23:45 +00:00
// Sync up initial values.
2020-12-01 22:46:30 +00:00
memory_ . set_speed_register ( speed_register_ ^ 0x80 ) ;
2020-11-12 23:01:26 +00:00
insert_media ( target . media ) ;
2020-10-22 01:19:18 +00:00
}
2020-11-13 02:44:51 +00:00
~ ConcreteMachine ( ) {
audio_queue_ . flush ( ) ;
}
2020-10-22 01:19:18 +00:00
void run_for ( const Cycles cycles ) override {
m65816_ . run_for ( cycles ) ;
}
2020-11-08 01:42:34 +00:00
void flush ( ) {
video_ . flush ( ) ;
2020-11-10 23:38:23 +00:00
iwm_ . flush ( ) ;
2020-11-26 22:14:46 +00:00
AudioUpdater updater ( this ) ;
2020-11-12 02:04:38 +00:00
audio_queue_ . perform ( ) ;
2020-11-08 01:42:34 +00:00
}
void set_scan_target ( Outputs : : Display : : ScanTarget * target ) override {
video_ - > set_scan_target ( target ) ;
2020-10-22 01:19:18 +00:00
}
Outputs : : Display : : ScanStatus get_scaled_scan_status ( ) const override {
2020-11-08 01:42:34 +00:00
return video_ - > get_scaled_scan_status ( ) * 2.0f ; // TODO: expose multiplier and divider via the JustInTime template?
}
void set_display_type ( Outputs : : Display : : DisplayType display_type ) final {
video_ - > set_display_type ( display_type ) ;
}
Outputs : : Display : : DisplayType get_display_type ( ) const final {
return video_ - > get_display_type ( ) ;
2020-10-22 01:19:18 +00:00
}
2020-11-12 02:04:38 +00:00
Outputs : : Speaker : : Speaker * get_speaker ( ) final {
return & speaker_ ;
}
2020-11-12 23:01:26 +00:00
// MARK: MediaTarget.
bool insert_media ( const Analyser : : Static : : Media & media ) final {
if ( ! media . disks . empty ( ) ) {
2020-11-21 02:37:17 +00:00
const auto disk = media . disks [ 0 ] ;
if ( disk - > get_maximum_head_position ( ) . as_int ( ) > 35 ) {
drives35_ [ 0 ] . set_disk ( media . disks [ 0 ] ) ;
} else {
drives525_ [ 0 ] . set_disk ( media . disks [ 0 ] ) ;
}
2020-11-12 23:01:26 +00:00
}
return true ;
}
2020-11-14 23:00:06 +00:00
// MARK: Activity::Source
void set_activity_observer ( Activity : : Observer * observer ) final {
2020-11-21 02:37:17 +00:00
drives35_ [ 0 ] . set_activity_observer ( observer , " First 3.5 \" Drive " , true ) ;
drives35_ [ 1 ] . set_activity_observer ( observer , " Second 3.5 \" Drive " , true ) ;
drives525_ [ 0 ] . set_activity_observer ( observer , " First 5.25 \" Drive " , true ) ;
drives525_ [ 1 ] . set_activity_observer ( observer , " Second 5.25 \" Drive " , true ) ;
2020-11-14 23:00:06 +00:00
}
2020-11-12 23:01:26 +00:00
// MARK: BusHandler.
2020-10-22 01:19:18 +00:00
forceinline Cycles perform_bus_operation ( const CPU : : WDC65816 : : BusOperation operation , const uint32_t address , uint8_t * const value ) {
2020-10-26 01:10:04 +00:00
const auto & region = MemoryMapRegion ( memory_ , address ) ;
2020-11-02 23:55:28 +00:00
static bool log = false ;
2020-11-15 21:08:29 +00:00
static uint64_t total = 0 ;
2020-12-01 22:46:30 +00:00
bool is_1Mhz = false ;
2020-10-22 23:25:04 +00:00
2020-11-30 02:29:15 +00:00
if ( operation = = CPU : : WDC65816 : : BusOperation : : ReadVector ) {
// I think vector pulls always go to ROM?
// That's slightly implied in the documentation, and doing so makes GS/OS boot, so...
* value = rom_ [ rom_ . size ( ) - 65536 + address ] ;
} else if ( region . flags & MemoryMap : : Region : : IsIO ) {
2020-10-29 01:58:20 +00:00
// Ensure classic auxiliary and language card accesses have effect.
2020-10-30 01:38:36 +00:00
const bool is_read = isReadOperation ( operation ) ;
memory_ . access ( uint16_t ( address ) , is_read ) ;
2020-10-29 01:58:20 +00:00
2020-11-14 23:39:16 +00:00
// TODO: which of these are actually 2.8Mhz?
2020-11-02 23:55:28 +00:00
const auto address_suffix = address & 0xffff ;
2020-11-14 23:23:31 +00:00
# define ReadWrite(x) (x) | (is_read * 0x10000)
# define Read(x) (x) | 0x10000
# define Write(x) (x)
switch ( ReadWrite ( address_suffix ) ) {
2020-10-28 02:31:58 +00:00
// New video register.
2020-11-14 23:23:31 +00:00
case Read ( 0xc029 ) :
2020-12-07 01:26:24 +00:00
* value = video_ - > get_new_video ( ) ;
2020-11-14 23:23:31 +00:00
break ;
case Write ( 0xc029 ) :
video_ - > set_new_video ( * value ) ;
2020-12-07 01:26:24 +00:00
assert ( * value & 1 ) ;
2020-11-05 22:56:20 +00:00
2020-11-14 23:23:31 +00:00
// TODO: I think bits 7 and 0 might also affect the memory map.
// The descripton isn't especially clear — P.90 of the Hardware Reference.
// Revisit if necessary.
2020-11-05 22:56:20 +00:00
break ;
// Video [and clock] interrupt register.
2020-11-14 23:23:31 +00:00
case Read ( 0xc023 ) :
* value = video_ - > get_interrupt_register ( ) ;
break ;
case Write ( 0xc023 ) :
video_ - > set_interrupt_register ( * value ) ;
2020-11-05 22:56:20 +00:00
break ;
2020-11-14 23:23:31 +00:00
// Video interrupt-clear register.
case Write ( 0xc032 ) :
video_ - > clear_interrupts ( * value ) ;
2020-10-28 02:31:58 +00:00
break ;
2020-11-30 01:08:59 +00:00
case Read ( 0xc032 ) :
// TODO: this seems to be undocumented, but used. What value is likely?
* value = 0xff ;
break ;
2020-10-28 02:31:58 +00:00
2020-10-29 01:58:20 +00:00
// Shadow register.
2020-11-14 23:23:31 +00:00
case Read ( 0xc035 ) :
* value = memory_ . get_shadow_register ( ) ;
break ;
case Write ( 0xc035 ) :
memory_ . set_shadow_register ( * value ) ;
2020-10-29 01:58:20 +00:00
break ;
2020-10-30 01:38:36 +00:00
// Clock data.
2020-11-14 23:23:31 +00:00
case Read ( 0xc033 ) :
* value = clock_ . get_data ( ) ;
break ;
case Write ( 0xc033 ) :
clock_ . set_data ( * value ) ;
2020-10-30 01:38:36 +00:00
break ;
// Clock and border control.
2020-11-14 23:23:31 +00:00
case Read ( 0xc034 ) :
2020-11-26 18:13:48 +00:00
* value = ( clock_ . get_control ( ) & 0xf0 ) | ( video_ . last_valid ( ) - > get_border_colour ( ) & 0x0f ) ;
2020-11-14 23:23:31 +00:00
break ;
case Write ( 0xc034 ) :
clock_ . set_control ( * value ) ;
video_ - > set_border_colour ( * value ) ;
2020-11-08 04:14:50 +00:00
break ;
// Colour text control.
2020-11-14 23:23:31 +00:00
case Write ( 0xc022 ) :
video_ - > set_text_colour ( * value ) ;
2020-10-30 01:38:36 +00:00
break ;
2020-10-29 01:23:45 +00:00
// Speed register.
2020-11-14 23:23:31 +00:00
case Read ( 0xc036 ) :
2020-12-01 22:46:30 +00:00
* value = speed_register_ ^ 0x80 ;
2020-11-14 23:23:31 +00:00
break ;
case Write ( 0xc036 ) :
2020-11-14 23:39:16 +00:00
// b7: 1 => operate at 2.8Mhz; 0 => 1Mhz.
// b6: power-on status; 1 => system has been turned on by the power switch (TODO: what clears this?)
// b5: reserved
// b4: [bank shadowing bit; cf. the memory map]
// b0– 3: motor on-off speed detectors;
// 1 => switch to 1Mhz if motor is on; 0 => don't;
// b0 = slot 4 (i.e. watches addresses c0c9, c0c8)
// b1 = slot 5 (i.e. c0d9, c0d8)
// b2 = slot 6 (i.e. c0e9, c0e8)
// b3 = slot 7 (i.e. c0f9, c0f8)
2020-11-14 23:23:31 +00:00
memory_ . set_speed_register ( * value ) ;
2020-12-01 22:46:30 +00:00
speed_register_ = * value ^ 0x80 ;
2020-10-29 01:23:45 +00:00
break ;
2020-10-29 01:58:20 +00:00
// [Memory] State register.
2020-11-14 23:23:31 +00:00
case Read ( 0xc068 ) :
* value = memory_ . get_state_register ( ) ;
break ;
case Write ( 0xc068 ) :
memory_ . set_state_register ( * value ) ;
2020-12-01 03:56:19 +00:00
video_ - > set_page2 ( * value & 0x40 ) ;
2020-10-29 01:58:20 +00:00
break ;
2020-11-05 02:15:10 +00:00
// Various independent memory switch reads [TODO: does the IIe-style keyboard provide the low seven?].
2020-12-01 22:46:30 +00:00
# define SwitchRead(s) *value = memory_.s ? 0x80 : 0x00; is_1Mhz = true;
2020-10-29 01:58:20 +00:00
# define LanguageRead(s) SwitchRead(language_card_switches().state().s)
# define AuxiliaryRead(s) SwitchRead(auxiliary_switches().switches().s)
2020-12-01 22:46:30 +00:00
# define VideoRead(s) *value = video_.last_valid()->s ? 0x80 : 0x00; is_1Mhz = true;
2020-12-01 03:35:51 +00:00
case Read ( 0xc011 ) : LanguageRead ( bank2 ) ; break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc012 ) : LanguageRead ( read ) ; break ;
case Read ( 0xc013 ) : AuxiliaryRead ( read_auxiliary_memory ) ; break ;
case Read ( 0xc014 ) : AuxiliaryRead ( write_auxiliary_memory ) ; break ;
case Read ( 0xc015 ) : AuxiliaryRead ( internal_CX_rom ) ; break ;
case Read ( 0xc016 ) : AuxiliaryRead ( alternative_zero_page ) ; break ;
case Read ( 0xc017 ) : AuxiliaryRead ( slot_C3_rom ) ; break ;
case Read ( 0xc018 ) : VideoRead ( get_80_store ( ) ) ; break ;
2020-11-17 02:55:41 +00:00
case Read ( 0xc019 ) :
VideoRead ( get_is_vertical_blank ( video_ . time_since_flush ( ) ) ) ;
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc01a ) : VideoRead ( get_text ( ) ) ; break ;
case Read ( 0xc01b ) : VideoRead ( get_mixed ( ) ) ; break ;
case Read ( 0xc01c ) : VideoRead ( get_page2 ( ) ) ; break ;
case Read ( 0xc01d ) : VideoRead ( get_high_resolution ( ) ) ; break ;
case Read ( 0xc01e ) : VideoRead ( get_alternative_character_set ( ) ) ; break ;
case Read ( 0xc01f ) : VideoRead ( get_80_columns ( ) ) ; break ;
2020-11-01 00:39:32 +00:00
# undef VideoRead
2020-10-29 01:58:20 +00:00
# undef AuxiliaryRead
# undef LanguageRead
# undef SwitchRead
2020-11-02 23:55:28 +00:00
// Video switches (and annunciators).
2020-11-14 23:23:31 +00:00
case Read ( 0xc050 ) : case Read ( 0xc051 ) :
case Write ( 0xc050 ) : case Write ( 0xc051 ) :
2020-11-08 00:40:26 +00:00
video_ - > set_text ( address & 1 ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-01 00:39:32 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc052 ) : case Read ( 0xc053 ) :
case Write ( 0xc052 ) : case Write ( 0xc053 ) :
2020-11-08 00:40:26 +00:00
video_ - > set_mixed ( address & 1 ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-01 00:39:32 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc054 ) : case Read ( 0xc055 ) :
case Write ( 0xc054 ) : case Write ( 0xc055 ) :
2020-12-01 03:56:19 +00:00
video_ - > set_page2 ( address & 1 ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-01 00:39:32 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc056 ) : case Read ( 0xc057 ) :
case Write ( 0xc056 ) : case Write ( 0xc057 ) :
2020-11-08 00:40:26 +00:00
video_ - > set_high_resolution ( address & 1 ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-01 00:39:32 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc058 ) : case Read ( 0xc059 ) :
case Write ( 0xc058 ) : case Write ( 0xc059 ) :
case Read ( 0xc05a ) : case Read ( 0xc05b ) :
case Write ( 0xc05a ) : case Write ( 0xc05b ) :
case Read ( 0xc05c ) : case Read ( 0xc05d ) :
case Write ( 0xc05c ) : case Write ( 0xc05d ) :
2020-11-02 23:55:28 +00:00
// Annunciators 0, 1 and 2.
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-02 23:55:28 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc05e ) : case Read ( 0xc05f ) :
case Write ( 0xc05e ) : case Write ( 0xc05f ) :
2020-11-08 00:40:26 +00:00
video_ - > set_annunciator_3 ( ! ( address & 1 ) ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-01 00:39:32 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Write ( 0xc000 ) : case Write ( 0xc001 ) :
video_ - > set_80_store ( address & 1 ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-01 01:00:15 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Write ( 0xc00c ) : case Write ( 0xc00d ) :
video_ - > set_80_columns ( address & 1 ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-01 01:00:15 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Write ( 0xc00e ) : case Write ( 0xc00f ) :
video_ - > set_alternative_character_set ( address & 1 ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-01 01:00:15 +00:00
break ;
2020-11-05 02:35:11 +00:00
// ADB and keyboard.
2020-11-14 23:23:31 +00:00
case Read ( 0xc000 ) :
* value = adb_glu_ . get_keyboard_data ( ) ;
2020-11-01 01:00:15 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc010 ) :
* value = adb_glu_ . get_any_key_down ( ) ? 0x80 : 0x00 ;
[[fallthrough]] ;
case Write ( 0xc010 ) :
2020-11-05 02:35:11 +00:00
adb_glu_ . clear_key_strobe ( ) ;
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc024 ) :
* value = adb_glu_ . get_mouse_data ( ) ;
2020-11-01 01:00:15 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc025 ) :
* value = adb_glu_ . get_modifier_status ( ) ;
2020-11-01 01:00:15 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc026 ) :
* value = adb_glu_ . get_data ( ) ;
2020-11-01 01:00:15 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Write ( 0xc026 ) :
adb_glu_ . set_command ( * value ) ;
break ;
case Read ( 0xc027 ) :
* value = adb_glu_ . get_status ( ) ;
break ;
case Write ( 0xc027 ) :
adb_glu_ . set_status ( * value ) ;
2020-11-01 01:00:15 +00:00
break ;
2020-11-01 00:39:32 +00:00
2020-10-29 02:07:34 +00:00
// The SCC.
2020-11-14 23:23:31 +00:00
case Read ( 0xc038 ) : case Read ( 0xc039 ) : case Read ( 0xc03a ) : case Read ( 0xc03b ) :
* value = scc_ . read ( int ( address ) ) ;
break ;
case Write ( 0xc038 ) : case Write ( 0xc039 ) : case Write ( 0xc03a ) : case Write ( 0xc03b ) :
scc_ . write ( int ( address ) , * value ) ;
2020-10-29 02:07:34 +00:00
break ;
2020-11-05 02:29:44 +00:00
// The audio GLU.
2020-11-26 22:14:46 +00:00
case Read ( 0xc03c ) : {
AudioUpdater updater ( this ) ;
2020-11-14 23:23:31 +00:00
* value = sound_glu_ . get_control ( ) ;
2020-11-26 22:14:46 +00:00
} break ;
case Write ( 0xc03c ) : {
AudioUpdater updater ( this ) ;
2020-11-14 23:23:31 +00:00
sound_glu_ . set_control ( * value ) ;
2020-11-26 22:14:46 +00:00
} break ;
case Read ( 0xc03d ) : {
AudioUpdater updater ( this ) ;
2020-11-14 23:23:31 +00:00
* value = sound_glu_ . get_data ( ) ;
2020-11-26 22:14:46 +00:00
} break ;
case Write ( 0xc03d ) : {
AudioUpdater updater ( this ) ;
2020-11-14 23:23:31 +00:00
sound_glu_ . set_data ( * value ) ;
2020-11-26 22:14:46 +00:00
} break ;
case Read ( 0xc03e ) : {
AudioUpdater updater ( this ) ;
2020-11-14 23:23:31 +00:00
* value = sound_glu_ . get_address_low ( ) ;
2020-11-26 22:14:46 +00:00
} break ;
case Write ( 0xc03e ) : {
AudioUpdater updater ( this ) ;
2020-11-14 23:23:31 +00:00
sound_glu_ . set_address_low ( * value ) ;
2020-11-26 22:14:46 +00:00
} break ;
case Read ( 0xc03f ) : {
AudioUpdater updater ( this ) ;
2020-11-14 23:23:31 +00:00
* value = sound_glu_ . get_address_high ( ) ;
2020-11-26 22:14:46 +00:00
} break ;
case Write ( 0xc03f ) : {
AudioUpdater updater ( this ) ;
2020-11-14 23:23:31 +00:00
sound_glu_ . set_address_high ( * value ) ;
2020-11-26 22:14:46 +00:00
} break ;
2020-11-05 02:29:44 +00:00
2020-10-29 01:58:20 +00:00
// These were all dealt with by the call to memory_.access.
// TODO: subject to read data? Does vapour lock apply?
2020-11-30 02:32:24 +00:00
case Read ( 0xc002 ) : case Read ( 0xc003 ) : case Read ( 0xc004 ) : case Read ( 0xc005 ) :
case Read ( 0xc006 ) : case Read ( 0xc007 ) : case Read ( 0xc008 ) : case Read ( 0xc009 ) : case Read ( 0xc00a ) : case Read ( 0xc00b ) :
* value = 0xff ;
break ;
case Write ( 0xc002 ) : case Write ( 0xc003 ) : case Write ( 0xc004 ) : case Write ( 0xc005 ) :
case Write ( 0xc006 ) : case Write ( 0xc007 ) : case Write ( 0xc008 ) : case Write ( 0xc009 ) : case Write ( 0xc00a ) : case Write ( 0xc00b ) :
2020-10-29 01:58:20 +00:00
break ;
2020-10-31 01:42:43 +00:00
// Interrupt ROM addresses; Cf. P25 of the Hardware Reference.
2020-11-14 23:23:31 +00:00
case Read ( 0xc071 ) : case Read ( 0xc072 ) : case Read ( 0xc073 ) :
case Read ( 0xc074 ) : case Read ( 0xc075 ) : case Read ( 0xc076 ) : case Read ( 0xc077 ) :
case Read ( 0xc078 ) : case Read ( 0xc079 ) : case Read ( 0xc07a ) : case Read ( 0xc07b ) :
case Read ( 0xc07c ) : case Read ( 0xc07d ) : case Read ( 0xc07e ) : case Read ( 0xc07f ) :
* value = rom_ [ rom_ . size ( ) - 65536 + address_suffix ] ;
2020-11-02 23:55:28 +00:00
break ;
// Analogue inputs. All TODO.
2020-11-14 23:23:31 +00:00
case Read ( 0xc060 ) : case Read ( 0xc061 ) : case Read ( 0xc062 ) : case Read ( 0xc063 ) :
2020-11-02 23:55:28 +00:00
// Joystick buttons (and keyboard modifiers).
2020-12-07 01:26:24 +00:00
* value = 0x00 ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-02 23:55:28 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc064 ) : case Read ( 0xc065 ) : case Read ( 0xc066 ) : case Read ( 0xc067 ) :
2020-11-02 23:55:28 +00:00
// Analogue inputs.
2020-11-14 23:23:31 +00:00
* value = 0x00 ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-02 23:55:28 +00:00
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc070 ) : case Write ( 0xc070 ) :
2020-11-02 23:55:28 +00:00
// TODO: begin analogue channel charge.
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-02 23:55:28 +00:00
break ;
2020-11-10 02:54:25 +00:00
// Monochome/colour register.
2020-11-14 23:23:31 +00:00
case Read ( 0xc021 ) :
2020-11-10 02:54:25 +00:00
// "Uses bit 7 to determine whether composite output is colour 9) or gray scale (1)."
2020-11-30 00:57:35 +00:00
* value = video_ . last_valid ( ) - > get_composite_is_colour ( ) ? 0x00 : 0x80 ;
2020-11-14 23:23:31 +00:00
break ;
case Write ( 0xc021 ) :
video_ - > set_composite_is_colour ( ! ( * value & 0x80 ) ) ;
2020-11-10 02:54:25 +00:00
break ;
2020-11-30 00:57:35 +00:00
case Read ( 0xc02e ) :
* value = video_ . last_valid ( ) - > get_vertical_counter ( video_ . time_since_flush ( ) ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-30 00:57:35 +00:00
break ;
case Read ( 0xc02f ) :
* value = video_ . last_valid ( ) - > get_horizontal_counter ( video_ . time_since_flush ( ) ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-30 00:57:35 +00:00
break ;
2020-11-27 03:36:32 +00:00
// case Read(0xc037): case Write(0xc037):
// // TODO: "Used during DMA as bank address"?
// break;
2020-11-30 02:21:46 +00:00
case Read ( 0xc041 ) :
* value = megaii_interrupt_mask_ ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-30 02:21:46 +00:00
break ;
case Write ( 0xc041 ) :
megaii_interrupt_mask_ = * value ;
video_ - > set_megaii_interrupts_enabled ( * value ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-30 02:21:46 +00:00
break ;
case Read ( 0xc044 ) :
// MMDELTAX byte.
* value = 0 ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-30 02:21:46 +00:00
break ;
case Read ( 0xc045 ) :
// MMDELTAX byte.
* value = 0 ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-30 02:21:46 +00:00
break ;
case Read ( 0xc046 ) :
* value = video_ - > get_megaii_interrupt_status ( ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-30 02:21:46 +00:00
break ;
case Read ( 0xc047 ) : case Write ( 0xc047 ) :
video_ - > clear_megaii_interrupts ( ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-30 02:21:46 +00:00
break ;
case Read ( 0xc048 ) : case Write ( 0xc048 ) :
// No-op: Clear Mega II mouse interrupt flags
2020-12-01 22:46:30 +00:00
is_1Mhz = true ;
2020-11-30 02:21:46 +00:00
break ;
2020-11-27 03:36:32 +00:00
// Language select.
// b7, b6, b5: character generator language select;
// b4: NTSC/PAL (0 = NTC);
// b3: language select — primary or secondary.
2020-11-14 23:23:31 +00:00
case Read ( 0xc02b ) :
* value = language_ ;
break ;
case Write ( 0xc02b ) :
language_ = * value ;
2020-11-10 02:54:25 +00:00
break ;
2020-11-27 03:36:32 +00:00
// TODO: 0xc02c is "Addr for tst mode read of character ROM". So it reads... what?
2020-11-10 02:54:25 +00:00
// Slot select.
2020-11-14 23:23:31 +00:00
case Read ( 0xc02d ) :
2020-11-02 23:55:28 +00:00
// b7: 0 = internal ROM code for slot 7;
// b6: 0 = internal ROM code for slot 6;
// b5: 0 = internal ROM code for slot 5;
// b4: 0 = internal ROM code for slot 4;
// b3: reserved;
// b2: internal ROM code for slot 2;
// b1: internal ROM code for slot 1;
// b0: reserved.
2020-11-14 23:23:31 +00:00
* value = card_mask_ ;
break ;
case Write ( 0xc02d ) :
card_mask_ = * value ;
2020-11-02 23:55:28 +00:00
break ;
2020-11-26 22:14:46 +00:00
case Read ( 0xc030 ) : case Write ( 0xc030 ) : {
AudioUpdater updater ( this ) ;
2020-11-12 02:04:38 +00:00
audio_toggle_ . set_output ( ! audio_toggle_ . get_output ( ) ) ;
2020-11-26 22:14:46 +00:00
} break ;
2020-11-10 03:21:52 +00:00
2020-11-02 23:55:28 +00:00
// 'Test Mode', whatever that is (?)
2020-11-14 23:23:31 +00:00
case Read ( 0xc06e ) : case Read ( 0xc06f ) :
case Write ( 0xc06e ) : case Write ( 0xc06f ) :
2020-11-02 23:55:28 +00:00
test_mode_ = address & 1 ;
break ;
2020-11-14 23:23:31 +00:00
case Read ( 0xc06d ) :
* value = test_mode_ * 0x80 ;
2020-10-31 01:42:43 +00:00
break ;
2020-11-10 03:21:52 +00:00
// Disk drive controls additional to the IWM.
2020-11-14 23:23:31 +00:00
case Read ( 0xc031 ) :
* value = disk_select_ ;
break ;
case Write ( 0xc031 ) :
2020-11-10 03:21:52 +00:00
// b7: 0 = use head 0; 1 = use head 1.
// b6: 0 = use 5.25" disks; 1 = use 3.5".
2020-11-14 23:23:31 +00:00
disk_select_ = * value ;
iwm_ - > set_select ( * value & 0x80 ) ;
// Presumably bit 6 selects between two 5.25" drives rather than the two 3.5"?
if ( * value & 0x40 ) {
2020-11-21 02:37:17 +00:00
iwm_ - > set_drive ( 0 , & drives35_ [ 0 ] ) ;
iwm_ - > set_drive ( 1 , & drives35_ [ 1 ] ) ;
2020-11-10 23:59:23 +00:00
} else {
2020-11-21 02:37:17 +00:00
iwm_ - > set_drive ( 0 , & drives525_ [ 0 ] ) ;
iwm_ - > set_drive ( 1 , & drives525_ [ 1 ] ) ;
2020-11-10 23:59:23 +00:00
}
2020-11-10 03:21:52 +00:00
break ;
2020-11-26 04:33:55 +00:00
// Addresses on other Apple II devices which do nothing on the GS.
2020-11-27 03:36:32 +00:00
case Read ( 0xc020 ) : case Write ( 0xc020 ) : // Reserved for future system expansion.
case Read ( 0xc028 ) : case Write ( 0xc028 ) : / / ROMBANK ; " not used in Apple IIGS " .
case Read ( 0xc02a ) : case Write ( 0xc02a ) : // Reserved for future system expansion.
case Read ( 0xc040 ) : case Write ( 0xc040 ) : // Reserved for future system expansion.
case Read ( 0xc042 ) : case Write ( 0xc042 ) : // Reserved for future system expansion.
case Read ( 0xc043 ) : case Write ( 0xc043 ) : // Reserved for future system expansion.
case Read ( 0xc049 ) : case Write ( 0xc049 ) : // Reserved for future system expansion.
case Read ( 0xc04a ) : case Write ( 0xc04a ) : // Reserved for future system expansion.
case Read ( 0xc04b ) : case Write ( 0xc04b ) : // Reserved for future system expansion.
case Read ( 0xc04c ) : case Write ( 0xc04c ) : // Reserved for future system expansion.
case Read ( 0xc04d ) : case Write ( 0xc04d ) : // Reserved for future system expansion.
case Read ( 0xc04e ) : case Write ( 0xc04e ) : // Reserved for future system expansion.
case Read ( 0xc04f ) : case Write ( 0xc04f ) : // Reserved for future system expansion.
case Read ( 0xc06b ) : case Write ( 0xc06b ) : // Reserved for future system expansion.
case Read ( 0xc06c ) : case Write ( 0xc06c ) : // Reserved for future system expansion.
2020-11-26 04:33:55 +00:00
case Write ( 0xc07e ) :
break ;
2020-10-28 02:31:58 +00:00
default :
2020-11-14 23:39:16 +00:00
// Update motor mask bits.
switch ( address_suffix ) {
case 0xc0c8 : motor_flags_ & = ~ 0x01 ; break ;
case 0xc0c9 : motor_flags_ | = 0x01 ; break ;
case 0xc0d8 : motor_flags_ & = ~ 0x02 ; break ;
case 0xc0d9 : motor_flags_ | = 0x02 ; break ;
case 0xc0e8 : motor_flags_ & = ~ 0x04 ; break ;
case 0xc0e9 : motor_flags_ | = 0x04 ; break ;
case 0xc0f8 : motor_flags_ & = ~ 0x08 ; break ;
case 0xc0f9 : motor_flags_ | = 0x08 ; break ;
}
2020-11-02 23:55:28 +00:00
// Check for a card access.
if ( address_suffix > = 0xc080 & & address_suffix < 0xc800 ) {
// This is an abridged version of the similar code in AppleII.cpp from
// line 653; it would be good to factor that out and support cards here.
// For now just either supply the internal ROM or nothing as per the
// current card mask.
size_t card_number = 0 ;
2020-11-22 03:22:27 +00:00
if ( address_suffix > = 0xc100 ) {
2020-11-02 23:55:28 +00:00
/*
Decode the area conventionally used by cards for ROMs :
0xC n00 to 0xC nff : card n .
*/
2020-11-22 03:22:27 +00:00
card_number = ( address_suffix - 0xc000 ) > > 8 ;
2020-11-02 23:55:28 +00:00
} else {
/*
Decode the area conventionally used by cards for registers :
C0n0 to C0nF : card n - 8.
*/
2020-11-22 03:22:27 +00:00
card_number = ( address_suffix - 0xc080 ) > > 4 ;
2020-11-02 23:55:28 +00:00
}
const uint8_t permitted_card_mask_ = card_mask_ & 0xf6 ;
if ( permitted_card_mask_ & ( 1 < < card_number ) ) {
// TODO: Access an actual card.
2020-11-30 23:15:02 +00:00
assert ( operation ! = CPU : : WDC65816 : : BusOperation : : ReadOpcode ) ;
2020-11-02 23:55:28 +00:00
if ( is_read ) {
* value = 0xff ;
}
} else {
2020-11-10 23:38:23 +00:00
switch ( address_suffix ) {
default :
2020-11-15 21:08:29 +00:00
// Temporary: log _potential_ mistakes.
if ( address_suffix < 0xc100 ) {
printf ( " Internal card-area access: %04x \n " , address_suffix ) ;
log | = operation = = CPU : : WDC65816 : : BusOperation : : ReadOpcode ;
}
2020-11-10 23:38:23 +00:00
if ( is_read ) {
* value = rom_ [ rom_ . size ( ) - 65536 + address_suffix ] ;
}
break ;
// IWM.
case 0xc0e0 : case 0xc0e1 : case 0xc0e2 : case 0xc0e3 :
case 0xc0e4 : case 0xc0e5 : case 0xc0e6 : case 0xc0e7 :
case 0xc0e8 : case 0xc0e9 : case 0xc0ea : case 0xc0eb :
case 0xc0ec : case 0xc0ed : case 0xc0ee : case 0xc0ef :
if ( is_read ) {
* value = iwm_ - > read ( int ( address_suffix ) ) ;
} else {
iwm_ - > write ( int ( address_suffix ) , * value ) ;
}
break ;
}
2020-11-05 02:15:10 +00:00
// log = true;
2020-11-02 23:55:28 +00:00
}
2020-11-14 23:23:31 +00:00
# undef ReadWrite
# undef Read
# undef Write
2020-10-29 01:58:20 +00:00
} else {
2020-11-19 00:49:45 +00:00
// Access the internal ROM.
//
// TODO: should probably occur only if there was a preceding access to a built-in
// card ROM?
if ( is_read ) {
* value = rom_ [ rom_ . size ( ) - 65536 + address_suffix ] ;
}
2020-11-02 23:55:28 +00:00
if ( address_suffix < 0xc080 ) {
// TODO: all other IO accesses.
2020-11-14 23:23:31 +00:00
printf ( " Unhandled IO %s: %04x \n " , is_read ? " read " : " write " , address_suffix ) ;
2020-12-01 03:35:51 +00:00
// assert(false);
2020-10-29 01:58:20 +00:00
}
}
2020-10-28 02:31:58 +00:00
}
2020-10-22 23:25:04 +00:00
} else {
2020-10-31 00:11:55 +00:00
// For debugging purposes; if execution heads off into an unmapped page then
// it's pretty certain that my 65816 still has issues.
assert ( operation ! = CPU : : WDC65816 : : BusOperation : : ReadOpcode | | region . read ) ;
2020-12-01 22:46:30 +00:00
is_1Mhz = region . flags & MemoryMap : : Region : : Is1Mhz ;
2020-10-31 00:11:55 +00:00
2020-10-22 23:25:04 +00:00
if ( isReadOperation ( operation ) ) {
2020-10-26 01:10:04 +00:00
MemoryMapRead ( region , address , value ) ;
2020-10-22 23:25:04 +00:00
} else {
2020-11-14 23:39:16 +00:00
// Shadowed writes also occur "at 1Mhz".
// TODO: this is probably an approximation. I'm assuming that there's the ability asynchronously to post
// both a 1Mhz cycle and a 2.8Mhz cycle and since the latter always fits into the former, this is sufficiently
// descriptive. I suspect this isn't true as it wouldn't explain the speed boost that Wolfenstein and others
// get by adding periodic NOPs within their copy-to-shadow step.
//
// Maybe the interaction with 2.8Mhz refresh isn't as straightforward as I think?
is_1Mhz | = region . flags & MemoryMap : : Region : : IsShadowed ;
2020-11-08 00:40:26 +00:00
// Use a very broad test for flushing video: any write to $e0 or $e1, or any write that is shadowed.
// TODO: at least restrict the e0/e1 test to possible video buffers!
if ( ( address > = 0xe00000 & & address < 0xe1000000 ) | | region . flags & MemoryMap : : Region : : IsShadowed ) {
video_ . flush ( ) ;
}
2020-10-26 01:10:04 +00:00
MemoryMapWrite ( memory_ , region , address , value ) ;
2020-10-22 23:25:04 +00:00
}
}
2020-11-05 02:15:10 +00:00
if ( operation = = CPU : : WDC65816 : : BusOperation : : ReadOpcode ) {
assert ( address ) ;
2020-11-05 01:35:41 +00:00
}
2020-12-01 03:35:51 +00:00
// if(address >= 0xE11700 && address < 0xe11b00) {
2020-11-15 00:10:41 +00:00
// printf("%06x %s %02x%s\n", address, isReadOperation(operation) ? "->" : "<-", *value,
// operation == CPU::WDC65816::BusOperation::ReadOpcode ? " [*]" : "");
// }
2020-12-03 01:48:19 +00:00
if ( operation = = CPU : : WDC65816 : : BusOperation : : ReadOpcode ) {
log = ( address > = 0xff6cdc ) & & ( address < 0xff6d43 ) ;
}
2020-12-01 03:56:19 +00:00
// log &= !((operation == CPU::WDC65816::BusOperation::ReadOpcode) && ((address < 0xff6a2c) || (address >= 0xff6a9c)));
2020-11-17 02:46:19 +00:00
if ( log ) {
2020-12-01 22:46:30 +00:00
printf ( " %06x %s %02x [%s] " , address , isReadOperation ( operation ) ? " -> " : " <- " , * value , ( is_1Mhz | | ( speed_register_ & motor_flags_ ) ) ? " 1.0 " : " 2.8 " ) ;
2020-11-02 23:55:28 +00:00
if ( operation = = CPU : : WDC65816 : : BusOperation : : ReadOpcode ) {
2020-11-15 21:08:29 +00:00
printf ( " a:%04x x:%04x y:%04x s:%04x e:%d p:%02x db:%02x pb:%02x d:%04x [tot:%llu] \n " ,
2020-11-02 23:55:28 +00:00
m65816_ . get_value_of_register ( CPU : : WDC65816 : : Register : : A ) ,
m65816_ . get_value_of_register ( CPU : : WDC65816 : : Register : : X ) ,
m65816_ . get_value_of_register ( CPU : : WDC65816 : : Register : : Y ) ,
m65816_ . get_value_of_register ( CPU : : WDC65816 : : Register : : StackPointer ) ,
m65816_ . get_value_of_register ( CPU : : WDC65816 : : Register : : EmulationFlag ) ,
m65816_ . get_value_of_register ( CPU : : WDC65816 : : Register : : Flags ) ,
m65816_ . get_value_of_register ( CPU : : WDC65816 : : Register : : DataBank ) ,
m65816_ . get_value_of_register ( CPU : : WDC65816 : : Register : : ProgramBank ) ,
2020-11-15 21:08:29 +00:00
m65816_ . get_value_of_register ( CPU : : WDC65816 : : Register : : Direct ) ,
2020-11-30 23:15:02 +00:00
static_cast < unsigned long long > ( total )
2020-11-02 23:55:28 +00:00
) ;
} else printf ( " \n " ) ;
}
2020-10-31 00:11:55 +00:00
2020-12-03 01:48:19 +00:00
// Automatic test overrides.
// if(operation == CPU::WDC65816::BusOperation::ReadOpcode) {
// // SCC.
// if(address == 0xff68d7) *value = 0x18; // CLC
// if(address == 0xff68d8) *value = 0x6b; // RTL
//
// // Clock.
// if(address == 0xff68d7) *value = 0x18; // CLC
// if(address == 0xff68d8) *value = 0x6b; // RTL
// }
2020-11-14 23:39:16 +00:00
Cycles duration ;
2020-12-01 22:46:30 +00:00
// In preparation for this test: the top bit of speed_register_ has been inverted,
// so 1 => 1Mhz, 0 => 2.8Mhz, and motor_flags_ always has that bit set.
if ( is_1Mhz | | ( speed_register_ & motor_flags_ ) ) {
2020-11-15 00:23:01 +00:00
// TODO: this is very implicitly linked to the video timing; make that overt somehow. Even if it's just with a redundant video setter at construction.
const int current_length = 14 + 2 * ( slow_access_phase_ / 896 ) ; // Length of cycle currently ongoing.
const int phase_adjust = ( current_length - slow_access_phase_ % 14 ) % current_length ; // Amount of time to expand waiting until end of cycle, if not actually at start.
const int access_phase = ( slow_access_phase_ + phase_adjust ) % 912 ; // Phase at which access will begin.
const int next_length = 14 + 2 * ( access_phase / 896 ) ; // Length of cycle that this access will occur within.
duration = Cycles ( next_length + phase_adjust ) ;
2020-11-14 23:39:16 +00:00
} else {
2020-11-15 00:10:41 +00:00
// Clues as to 'fast' refresh timing:
//
2020-11-15 21:08:29 +00:00
// (i) "The time required for the refresh cycles reduces the effective
// processor speed for programs in RAM by about 8 percent.";
2020-11-15 00:10:41 +00:00
// (ii) "These cycles occur approximately every 3.5 microseconds"
//
// 3.5µs @ 14,318,180Hz => one every 50.11 cycles. Safe to assume every 10th fast cycle
// is refresh? That feels like a lot.
//
// (and the IIgs is smart enough that refresh is applicable only to RAM accesses).
const int phase_adjust = ( 5 - fast_access_phase_ % 5 ) % 5 ;
const int refresh = ( fast_access_phase_ / 45 ) * bool ( region . write ) * 5 ;
duration = Cycles ( 5 + phase_adjust + refresh ) ;
2020-11-14 23:39:16 +00:00
}
2020-11-15 21:08:29 +00:00
// TODO: lookup tables to avoid the above? LCM of the two phases is 22,800 so probably 912+50 bytes plus two counters.
2020-11-15 00:10:41 +00:00
fast_access_phase_ = ( fast_access_phase_ + duration . as < int > ( ) ) % 50 ;
2020-11-15 00:23:01 +00:00
slow_access_phase_ = ( slow_access_phase_ + duration . as < int > ( ) ) % 912 ;
2020-11-05 22:56:20 +00:00
// Propagate time far and wide.
cycles_since_clock_tick_ + = duration ;
auto ticks = cycles_since_clock_tick_ . divide ( Cycles ( CLOCK_RATE ) ) . as_integral ( ) ;
while ( ticks - - ) {
clock_ . update ( ) ;
2020-11-08 00:40:26 +00:00
video_ . last_valid ( ) - > notify_clock_tick ( ) ; // The video controller marshalls the one-second interrupt.
2020-11-16 19:42:50 +00:00
// TODO: I think I may have made a false assumption here; does
// the VGC have an independent 1-second interrupt?
2020-11-08 00:40:26 +00:00
update_interrupts ( ) ;
2020-11-05 22:56:20 +00:00
}
2020-11-08 00:40:26 +00:00
video_ + = duration ;
2020-11-10 23:38:23 +00:00
iwm_ + = duration ;
2020-11-12 02:04:38 +00:00
cycles_since_audio_update_ + = duration ;
2020-11-15 21:08:29 +00:00
total + = decltype ( total ) ( duration . as_integral ( ) ) ;
2020-11-05 22:56:20 +00:00
2020-11-26 22:14:46 +00:00
if ( cycles_since_audio_update_ > = cycles_until_audio_event_ ) {
AudioUpdater updater ( this ) ;
update_interrupts ( ) ;
}
2020-11-26 21:11:03 +00:00
if ( video_ . did_flush ( ) ) {
2020-11-08 00:40:26 +00:00
update_interrupts ( ) ;
}
2020-11-05 22:56:20 +00:00
2020-10-22 23:25:04 +00:00
return duration ;
2020-10-22 01:19:18 +00:00
}
2020-11-08 00:40:26 +00:00
void update_interrupts ( ) {
2020-11-26 22:14:46 +00:00
// Update the interrupt line.
// TODO: are there other interrupt sources?
2020-11-30 02:21:46 +00:00
m65816_ . set_irq_line ( video_ . last_valid ( ) - > get_interrupt_line ( ) | | sound_glu_ . get_interrupt_line ( ) ) ;
2020-11-08 00:40:26 +00:00
}
2020-10-22 01:19:18 +00:00
private :
CPU : : WDC65816 : : Processor < ConcreteMachine , false > m65816_ ;
2020-10-26 01:10:04 +00:00
MemoryMap memory_ ;
2020-11-01 00:39:32 +00:00
2020-11-02 23:55:28 +00:00
// MARK: - Timing.
2020-10-22 23:25:04 +00:00
int fast_access_phase_ = 0 ;
int slow_access_phase_ = 0 ;
2020-10-31 01:50:39 +00:00
uint8_t speed_register_ = 0x40 ; // i.e. Power-on status. (TODO: only if ROM03?)
2020-12-01 22:46:30 +00:00
uint8_t motor_flags_ = 0x80 ;
2020-10-29 01:23:45 +00:00
2020-10-25 22:28:32 +00:00
// MARK: - Memory storage.
2020-10-22 23:25:04 +00:00
2020-11-01 00:03:23 +00:00
std : : vector < uint8_t > ram_ { } ;
2020-10-22 23:25:04 +00:00
std : : vector < uint8_t > rom_ ;
2020-10-29 02:07:34 +00:00
// MARK: - Other components.
2020-11-02 23:55:28 +00:00
Apple : : Clock : : ParallelClock clock_ ;
2020-11-26 17:54:20 +00:00
JustInTimeActor < Apple : : IIgs : : Video : : Video , 1 , 2 , Cycles > video_ ; // i.e. run video at 7Mhz.
2020-11-02 23:55:28 +00:00
Apple : : IIgs : : ADB : : GLU adb_glu_ ;
2020-10-29 02:07:34 +00:00
Zilog : : SCC : : z8530 scc_ ;
2020-11-12 23:09:31 +00:00
JustInTimeActor < Apple : : IWM , 1 , 2 , Cycles > iwm_ ;
2020-11-05 22:56:20 +00:00
Cycles cycles_since_clock_tick_ ;
2020-11-21 02:37:17 +00:00
Apple : : Macintosh : : DoubleDensityDrive drives35_ [ 2 ] ;
Apple : : Disk : : DiskIIDrive drives525_ [ 2 ] ;
2020-11-10 23:38:23 +00:00
2020-11-12 02:04:38 +00:00
// The audio parts.
Concurrency : : DeferringAsyncTaskQueue audio_queue_ ;
2020-11-18 23:39:11 +00:00
Apple : : IIgs : : Sound : : GLU sound_glu_ ;
2020-11-12 02:04:38 +00:00
Audio : : Toggle audio_toggle_ ;
2020-11-18 23:39:11 +00:00
using AudioSource = Outputs : : Speaker : : CompoundSource < Apple : : IIgs : : Sound : : GLU , Audio : : Toggle > ;
AudioSource mixer_ ;
Outputs : : Speaker : : LowpassSpeaker < AudioSource > speaker_ ;
2020-11-12 02:04:38 +00:00
Cycles cycles_since_audio_update_ ;
2020-11-26 22:14:46 +00:00
Cycles cycles_until_audio_event_ ;
2020-11-20 02:19:27 +00:00
static constexpr int audio_divider = 16 ;
2020-11-12 02:04:38 +00:00
void update_audio ( ) {
2020-11-18 23:39:11 +00:00
const auto divided_cycles = cycles_since_audio_update_ . divide ( Cycles ( audio_divider ) ) ;
sound_glu_ . run_for ( divided_cycles ) ;
speaker_ . run_for ( audio_queue_ , divided_cycles ) ;
2020-11-12 02:04:38 +00:00
}
2020-11-26 22:14:46 +00:00
class AudioUpdater {
public :
AudioUpdater ( ConcreteMachine * machine ) : machine_ ( machine ) {
machine_ - > update_audio ( ) ;
}
~ AudioUpdater ( ) {
machine_ - > cycles_until_audio_event_ = machine_ - > sound_glu_ . get_next_sequence_point ( ) ;
}
private :
ConcreteMachine * machine_ ;
} ;
friend AudioUpdater ;
2020-11-12 02:04:38 +00:00
2020-11-02 23:55:28 +00:00
// MARK: - Cards.
// TODO: most of cards.
uint8_t card_mask_ = 0x00 ;
bool test_mode_ = false ;
2020-11-10 02:54:25 +00:00
uint8_t language_ = 0 ;
2020-11-10 23:59:23 +00:00
uint8_t disk_select_ = 0 ;
2020-11-30 02:21:46 +00:00
uint8_t megaii_interrupt_mask_ = 0 ;
2020-10-22 01:19:18 +00:00
} ;
2020-10-21 02:18:11 +00:00
}
}
using namespace Apple : : IIgs ;
Machine * Machine : : AppleIIgs ( const Analyser : : Static : : Target * target , const ROMMachine : : ROMFetcher & rom_fetcher ) {
2020-10-22 01:19:18 +00:00
return new ConcreteMachine ( * dynamic_cast < const Analyser : : Static : : AppleIIgs : : Target * > ( target ) , rom_fetcher ) ;
2020-10-21 02:18:11 +00:00
}
Machine : : ~ Machine ( ) { }