2019-10-07 03:14:05 +00:00
//
// MFP68901.cpp
// Clock Signal
//
// Created by Thomas Harte on 06/10/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
# include "MFP68901.hpp"
2019-10-26 19:55:19 +00:00
# include <cstring>
2019-10-11 02:46:58 +00:00
# define LOG_PREFIX "[MFP] "
2019-10-22 03:02:30 +00:00
//#define NDEBUG
2019-10-08 02:44:35 +00:00
# include "../../Outputs/Log.hpp"
2019-10-07 03:14:05 +00:00
using namespace Motorola : : MFP68901 ;
uint8_t MFP68901 : : read ( int address ) {
2019-10-08 02:44:35 +00:00
address & = 0x1f ;
2019-10-30 03:07:03 +00:00
// Interrupt block: various bits of state can be read, all passively.
if ( address > = 0x03 & & address < = 0x0b ) {
const int shift = ( address & 1 ) < < 3 ;
switch ( address ) {
case 0x03 : case 0x04 : return uint8_t ( interrupt_enable_ > > shift ) ;
case 0x05 : case 0x06 : return uint8_t ( interrupt_pending_ > > shift ) ;
case 0x07 : case 0x08 : return uint8_t ( interrupt_in_service_ > > shift ) ;
case 0x09 : case 0x0a : return uint8_t ( interrupt_mask_ > > shift ) ;
case 0x0b : return interrupt_vector_ ;
default : break ;
}
}
2019-10-08 02:44:35 +00:00
switch ( address ) {
2019-10-30 03:07:03 +00:00
// GPIP block: input, and configured active edge and direction values.
case 0x00 : return ( gpip_input_ & ~ gpip_direction_ ) | ( gpip_output_ & gpip_direction_ ) ;
case 0x01 : return gpip_active_edge_ ;
case 0x02 : return gpip_direction_ ;
/* Interrupt block dealt with above. */
default : break ;
// Timer block: read back A, B and C/D control, and read current timer values.
case 0x0c : case 0x0d : return timer_ab_control_ [ address - 0xc ] ;
case 0x0e : return timer_cd_control_ ;
case 0x0f : case 0x10 :
case 0x11 : case 0x12 : return get_timer_data ( address - 0xf ) ;
// USART block: TODO.
2019-10-08 02:44:35 +00:00
case 0x13 : LOG ( " Read: sync character generator " ) ; break ;
case 0x14 : LOG ( " Read: USART control " ) ; break ;
case 0x15 : LOG ( " Read: receiver status " ) ; break ;
case 0x16 : LOG ( " Read: transmitter status " ) ; break ;
case 0x17 : LOG ( " Read: USART data " ) ; break ;
}
2019-10-11 02:46:58 +00:00
return 0x00 ;
2019-10-07 03:14:05 +00:00
}
void MFP68901 : : write ( int address , uint8_t value ) {
2019-10-08 02:44:35 +00:00
address & = 0x1f ;
2019-10-30 03:07:03 +00:00
// Interrupt block: enabled and masked interrupts can be set; pending and in-service interrupts can be masked.
if ( address > = 0x03 & & address < = 0x0b ) {
const int shift = ( address & 1 ) < < 3 ;
const int preserve = 0xff00 > > shift ;
const int word_value = value < < shift ;
switch ( address ) {
default : break ;
case 0x03 : case 0x04 : // Adjust enabled interrupts; disabled ones also cease to be pending.
interrupt_enable_ = ( interrupt_enable_ & preserve ) | word_value ;
interrupt_pending_ & = interrupt_enable_ ;
break ;
case 0x05 : case 0x06 : // Resolve pending interrupts.
interrupt_pending_ & = ( preserve | word_value ) ;
break ;
case 0x07 : case 0x08 : // Resolve in-service interrupts.
interrupt_in_service_ & = ( preserve | word_value ) ;
break ;
case 0x09 : case 0x0a : // Adjust interrupt mask.
interrupt_mask_ = ( interrupt_mask_ & preserve ) | word_value ;
break ;
case 0x0b : // Set the interrupt vector, possibly changing end-of-interrupt mode.
interrupt_vector_ = value ;
// If automatic end-of-interrupt mode has now been enabled, clear
// the in-process mask and re-evaluate.
if ( interrupt_vector_ & 0x08 ) return ;
interrupt_in_service_ = 0 ;
break ;
}
// Whatever just happened may have affected the state of the interrupt line.
update_interrupts ( ) ;
return ;
}
2019-10-08 02:44:35 +00:00
switch ( address ) {
2019-10-30 03:07:03 +00:00
// GPIP block: output and configuration of active edge and direction values.
2019-10-22 03:02:30 +00:00
case 0x00 :
gpip_output_ = value ;
break ;
case 0x01 :
gpip_active_edge_ = value ;
reevaluate_gpip_interrupts ( ) ;
break ;
case 0x02 :
gpip_direction_ = value ;
reevaluate_gpip_interrupts ( ) ;
break ;
2019-10-26 19:55:19 +00:00
2019-10-30 03:07:03 +00:00
/* Interrupt block dealt with above. */
default : break ;
// Timer block.
2019-10-08 02:44:35 +00:00
case 0x0c :
case 0x0d : {
const auto timer = address - 0xc ;
const bool reset = value & 0x10 ;
2019-10-28 02:38:49 +00:00
timer_ab_control_ [ timer ] = value ;
2019-10-08 02:44:35 +00:00
switch ( value & 0xf ) {
2019-10-27 03:20:13 +00:00
case 0x0 : set_timer_mode ( timer , TimerMode : : Stopped , 1 , reset ) ; break ;
2019-10-08 02:44:35 +00:00
case 0x1 : set_timer_mode ( timer , TimerMode : : Delay , 4 , reset ) ; break ;
case 0x2 : set_timer_mode ( timer , TimerMode : : Delay , 10 , reset ) ; break ;
case 0x3 : set_timer_mode ( timer , TimerMode : : Delay , 16 , reset ) ; break ;
case 0x4 : set_timer_mode ( timer , TimerMode : : Delay , 50 , reset ) ; break ;
case 0x5 : set_timer_mode ( timer , TimerMode : : Delay , 64 , reset ) ; break ;
case 0x6 : set_timer_mode ( timer , TimerMode : : Delay , 100 , reset ) ; break ;
case 0x7 : set_timer_mode ( timer , TimerMode : : Delay , 200 , reset ) ; break ;
2019-10-27 03:20:13 +00:00
case 0x8 : set_timer_mode ( timer , TimerMode : : EventCount , 1 , reset ) ; break ;
2019-10-08 02:44:35 +00:00
case 0x9 : set_timer_mode ( timer , TimerMode : : PulseWidth , 4 , reset ) ; break ;
case 0xa : set_timer_mode ( timer , TimerMode : : PulseWidth , 10 , reset ) ; break ;
case 0xb : set_timer_mode ( timer , TimerMode : : PulseWidth , 16 , reset ) ; break ;
case 0xc : set_timer_mode ( timer , TimerMode : : PulseWidth , 50 , reset ) ; break ;
case 0xd : set_timer_mode ( timer , TimerMode : : PulseWidth , 64 , reset ) ; break ;
case 0xe : set_timer_mode ( timer , TimerMode : : PulseWidth , 100 , reset ) ; break ;
case 0xf : set_timer_mode ( timer , TimerMode : : PulseWidth , 200 , reset ) ; break ;
}
} break ;
case 0x0e :
2019-10-28 02:38:49 +00:00
timer_cd_control_ = value ;
2019-10-08 02:44:35 +00:00
switch ( value & 7 ) {
2019-10-27 03:20:13 +00:00
case 0 : set_timer_mode ( 3 , TimerMode : : Stopped , 1 , false ) ; break ;
2019-10-08 02:44:35 +00:00
case 1 : set_timer_mode ( 3 , TimerMode : : Delay , 4 , false ) ; break ;
case 2 : set_timer_mode ( 3 , TimerMode : : Delay , 10 , false ) ; break ;
case 3 : set_timer_mode ( 3 , TimerMode : : Delay , 16 , false ) ; break ;
case 4 : set_timer_mode ( 3 , TimerMode : : Delay , 50 , false ) ; break ;
case 5 : set_timer_mode ( 3 , TimerMode : : Delay , 64 , false ) ; break ;
case 6 : set_timer_mode ( 3 , TimerMode : : Delay , 100 , false ) ; break ;
case 7 : set_timer_mode ( 3 , TimerMode : : Delay , 200 , false ) ; break ;
}
switch ( ( value > > 4 ) & 7 ) {
2019-10-27 03:20:13 +00:00
case 0 : set_timer_mode ( 2 , TimerMode : : Stopped , 1 , false ) ; break ;
2019-10-08 02:44:35 +00:00
case 1 : set_timer_mode ( 2 , TimerMode : : Delay , 4 , false ) ; break ;
case 2 : set_timer_mode ( 2 , TimerMode : : Delay , 10 , false ) ; break ;
case 3 : set_timer_mode ( 2 , TimerMode : : Delay , 16 , false ) ; break ;
case 4 : set_timer_mode ( 2 , TimerMode : : Delay , 50 , false ) ; break ;
case 5 : set_timer_mode ( 2 , TimerMode : : Delay , 64 , false ) ; break ;
case 6 : set_timer_mode ( 2 , TimerMode : : Delay , 100 , false ) ; break ;
case 7 : set_timer_mode ( 2 , TimerMode : : Delay , 200 , false ) ; break ;
}
break ;
case 0x0f : case 0x10 : case 0x11 : case 0x12 :
set_timer_data ( address - 0xf , value ) ;
break ;
2019-10-30 03:07:03 +00:00
// USART block: TODO.
2019-10-08 02:44:35 +00:00
case 0x13 : LOG ( " Write: sync character generator " ) ; break ;
case 0x14 : LOG ( " Write: USART control " ) ; break ;
case 0x15 : LOG ( " Write: receiver status " ) ; break ;
case 0x16 : LOG ( " Write: transmitter status " ) ; break ;
case 0x17 : LOG ( " Write: USART data " ) ; break ;
}
2019-10-07 03:14:05 +00:00
}
2019-10-08 02:44:35 +00:00
void MFP68901 : : run_for ( HalfCycles time ) {
cycles_left_ + = time ;
// TODO: this is the stupidest possible implementation. Improve.
2019-10-30 02:36:29 +00:00
int cycles = int ( cycles_left_ . flush < Cycles > ( ) . as_integral ( ) ) ;
2019-10-08 02:44:35 +00:00
while ( cycles - - ) {
for ( int c = 0 ; c < 4 ; + + c ) {
if ( timers_ [ c ] . mode > = TimerMode : : Delay ) {
- - timers_ [ c ] . divisor ;
if ( ! timers_ [ c ] . divisor ) {
timers_ [ c ] . divisor = timers_ [ c ] . prescale ;
2019-10-10 03:01:11 +00:00
decrement_timer ( c ) ;
2019-10-08 02:44:35 +00:00
}
}
}
}
2019-10-07 03:14:05 +00:00
}
HalfCycles MFP68901 : : get_next_sequence_point ( ) {
return HalfCycles ( - 1 ) ;
}
2019-10-08 02:44:35 +00:00
// MARK: - Timers
void MFP68901 : : set_timer_mode ( int timer , TimerMode mode , int prescale , bool reset_timer ) {
timers_ [ timer ] . mode = mode ;
timers_ [ timer ] . prescale = prescale ;
if ( reset_timer ) {
timers_ [ timer ] . divisor = prescale ;
timers_ [ timer ] . value = timers_ [ timer ] . reload_value ;
}
}
void MFP68901 : : set_timer_data ( int timer , uint8_t value ) {
if ( timers_ [ timer ] . mode = = TimerMode : : Stopped ) {
timers_ [ timer ] . value = value ;
}
timers_ [ timer ] . reload_value = value ;
}
uint8_t MFP68901 : : get_timer_data ( int timer ) {
return timers_ [ timer ] . value ;
}
2019-10-10 03:01:11 +00:00
void MFP68901 : : set_timer_event_input ( int channel , bool value ) {
if ( timers_ [ channel ] . event_input = = value ) return ;
timers_ [ channel ] . event_input = value ;
if ( timers_ [ channel ] . mode = = TimerMode : : EventCount & & ! value ) { /* TODO: which edge is counted? "as defined by the associated Interrupt Channel’ s edge bit"? */
decrement_timer ( channel ) ;
}
}
void MFP68901 : : decrement_timer ( int timer ) {
- - timers_ [ timer ] . value ;
if ( ! timers_ [ timer ] . value ) {
2019-10-27 03:20:13 +00:00
switch ( timer ) {
case 0 : begin_interrupts ( Interrupt : : TimerA ) ; break ;
case 1 : begin_interrupts ( Interrupt : : TimerB ) ; break ;
case 2 : begin_interrupts ( Interrupt : : TimerC ) ; break ;
case 3 : begin_interrupts ( Interrupt : : TimerD ) ; break ;
}
if ( timers_ [ timer ] . mode = = TimerMode : : Delay ) {
timers_ [ timer ] . value = timers_ [ timer ] . reload_value ;
}
2019-10-10 03:01:11 +00:00
}
}
2019-10-22 03:02:30 +00:00
// MARK: - GPIP
void MFP68901 : : set_port_input ( uint8_t input ) {
gpip_input_ = input ;
reevaluate_gpip_interrupts ( ) ;
}
uint8_t MFP68901 : : get_port_output ( ) {
return 0xff ;
}
void MFP68901 : : reevaluate_gpip_interrupts ( ) {
2019-10-30 03:07:03 +00:00
const uint8_t gpip_state = ( gpip_input_ & ~ gpip_direction_ ) ^ gpip_active_edge_ ;
2019-10-25 02:33:42 +00:00
2019-10-22 03:02:30 +00:00
// An interrupt is detected on any falling edge.
2019-10-25 02:33:42 +00:00
const uint8_t new_interrupt_mask = ( gpip_state ^ gpip_interrupt_state_ ) & gpip_interrupt_state_ ;
if ( new_interrupt_mask ) {
begin_interrupts (
( new_interrupt_mask & 0x0f ) |
2019-10-25 02:37:32 +00:00
( ( new_interrupt_mask & 0x30 ) < < 2 ) |
2019-10-25 02:33:42 +00:00
( ( new_interrupt_mask & 0xc0 ) < < 8 )
) ;
2019-10-22 03:02:30 +00:00
}
gpip_interrupt_state_ = gpip_state ;
}
2019-10-24 03:09:49 +00:00
// MARK: - Interrupts
2019-10-25 02:33:42 +00:00
void MFP68901 : : begin_interrupts ( int interrupt ) {
2019-10-30 03:07:03 +00:00
interrupt_pending_ | = interrupt & interrupt_enable_ ;
2019-10-25 02:33:42 +00:00
update_interrupts ( ) ;
}
void MFP68901 : : end_interrupts ( int interrupt ) {
2019-10-26 19:55:19 +00:00
interrupt_pending_ & = ~ interrupt ;
2019-10-25 02:33:42 +00:00
update_interrupts ( ) ;
}
void MFP68901 : : update_interrupts ( ) {
2019-10-26 19:55:19 +00:00
const auto old_interrupt_line = interrupt_line_ ;
2019-10-27 04:02:55 +00:00
const auto firing_interrupts = interrupt_pending_ & interrupt_mask_ ;
2019-10-26 19:55:19 +00:00
2019-10-27 04:02:55 +00:00
if ( ! firing_interrupts ) {
2019-10-26 19:55:19 +00:00
interrupt_line_ = false ;
} else {
if ( interrupt_vector_ & 0x8 ) {
2019-10-29 02:51:00 +00:00
// Software interrupt mode: permit only if neither this interrupt
// nor a higher interrupt is currently in service.
2019-10-27 04:02:55 +00:00
const int highest_bit = 1 < < ( fls ( firing_interrupts ) - 1 ) ;
2019-10-29 02:51:00 +00:00
interrupt_line_ = ! ( interrupt_in_service_ & ~ ( highest_bit - 1 ) ) ;
2019-10-26 19:55:19 +00:00
} else {
// Auto-interrupt mode; just signal.
interrupt_line_ = true ;
}
}
2019-10-25 02:33:42 +00:00
2019-10-26 19:55:19 +00:00
// Update the delegate if necessary.
2019-10-26 02:36:01 +00:00
if ( interrupt_delegate_ & & interrupt_line_ ! = old_interrupt_line ) {
2019-10-28 02:38:49 +00:00
if ( interrupt_line_ )
LOG ( " Generating interrupt: " < < std : : hex < < interrupt_pending_ < < " / " < < std : : hex < < interrupt_mask_ < < " : " < < std : : hex < < interrupt_in_service_ ) ;
2019-10-26 02:36:01 +00:00
interrupt_delegate_ - > mfp68901_did_change_interrupt_status ( this ) ;
2019-10-25 02:33:42 +00:00
}
}
2019-10-24 03:09:49 +00:00
2019-10-25 02:33:42 +00:00
bool MFP68901 : : get_interrupt_line ( ) {
return interrupt_line_ ;
2019-10-24 03:09:49 +00:00
}
2019-10-26 02:36:01 +00:00
uint8_t MFP68901 : : acknowledge_interrupt ( ) {
2019-10-26 19:55:19 +00:00
const int selected = fls ( interrupt_pending_ & interrupt_mask_ ) - 1 ;
const int mask = 1 < < selected ;
// Clear the pending bit regardless.
interrupt_pending_ & = ~ mask ;
// If this is software interrupt mode, set the in-service bit.
if ( interrupt_vector_ & 0x8 ) {
interrupt_in_service_ | = mask ;
2019-10-26 02:36:01 +00:00
}
2019-10-26 19:55:19 +00:00
update_interrupts ( ) ;
2019-10-27 04:02:55 +00:00
LOG ( " Interrupt acknowledged: " < < selected ) ;
2019-10-26 19:55:19 +00:00
return ( interrupt_vector_ & 0xf0 ) | uint8_t ( selected ) ;
2019-10-26 02:36:01 +00:00
}
void MFP68901 : : set_interrupt_delegate ( InterruptDelegate * delegate ) {
interrupt_delegate_ = delegate ;
2019-10-24 03:09:49 +00:00
}