2019-10-06 23:14:05 -04: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 15:55:19 -04:00
# include <cstring>
2019-10-10 22:46:58 -04:00
# define LOG_PREFIX "[MFP] "
2019-10-21 23:02:30 -04:00
//#define NDEBUG
2019-10-07 22:44:35 -04:00
# include "../../Outputs/Log.hpp"
2019-10-06 23:14:05 -04:00
using namespace Motorola : : MFP68901 ;
uint8_t MFP68901 : : read ( int address ) {
2019-10-07 22:44:35 -04:00
address & = 0x1f ;
switch ( address ) {
2019-10-21 23:02:30 -04:00
case 0x00 :
LOG ( " Read: general purpose IO " < < PADHEX ( 2 ) < < int ( gpip_input_ | gpip_direction_ ) ) ;
return gpip_input_ | gpip_direction_ ;
case 0x01 :
LOG ( " Read: active edge " < < PADHEX ( 2 ) < < int ( gpip_active_edge_ ) ) ;
return gpip_active_edge_ ;
case 0x02 :
LOG ( " Read: data direction " < < PADHEX ( 2 ) < < int ( gpip_direction_ ) ) ;
return gpip_direction_ ;
2019-10-23 23:09:49 -04:00
case 0x03 :
LOG ( " Read: interrupt enable A " ) ;
2019-10-24 22:33:42 -04:00
return uint8_t ( interrupt_enable_ > > 8 ) ;
2019-10-23 23:09:49 -04:00
case 0x04 :
LOG ( " Read: interrupt enable B " ) ;
2019-10-24 22:33:42 -04:00
return uint8_t ( interrupt_enable_ ) ;
2019-10-23 23:09:49 -04:00
case 0x05 :
LOG ( " Read: interrupt pending A " ) ;
2019-10-24 22:33:42 -04:00
return uint8_t ( interrupt_pending_ > > 8 ) ;
2019-10-23 23:09:49 -04:00
case 0x06 :
LOG ( " Read: interrupt pending B " ) ;
2019-10-24 22:33:42 -04:00
return uint8_t ( interrupt_pending_ ) ;
case 0x07 :
LOG ( " Read: interrupt in-service A " ) ;
return uint8_t ( interrupt_in_service_ > > 8 ) ;
case 0x08 :
LOG ( " Read: interrupt in-service B " ) ;
return uint8_t ( interrupt_in_service_ ) ;
case 0x09 :
LOG ( " Read: interrupt mask A " ) ;
return uint8_t ( interrupt_mask_ > > 8 ) ;
case 0x0a :
LOG ( " Read: interrupt mask B " ) ;
return uint8_t ( interrupt_mask_ ) ;
2019-10-25 22:36:01 -04:00
case 0x0b :
LOG ( " Read: vector " ) ;
return interrupt_vector_ ;
2019-10-07 22:44:35 -04:00
case 0x0c : LOG ( " Read: timer A control " ) ; break ;
case 0x0d : LOG ( " Read: timer B control " ) ; break ;
case 0x0e : LOG ( " Read: timers C/D control " ) ; break ;
case 0x0f : case 0x10 : case 0x11 : case 0x12 :
return get_timer_data ( address - 0xf ) ;
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-10 22:46:58 -04:00
return 0x00 ;
2019-10-06 23:14:05 -04:00
}
void MFP68901 : : write ( int address , uint8_t value ) {
2019-10-07 22:44:35 -04:00
address & = 0x1f ;
switch ( address ) {
2019-10-21 23:02:30 -04:00
case 0x00 :
LOG ( " Write: general purpose IO " < < PADHEX ( 2 ) < < int ( value ) ) ;
gpip_output_ = value ;
break ;
case 0x01 :
LOG ( " Write: active edge " < < PADHEX ( 2 ) < < int ( value ) ) ;
gpip_active_edge_ = value ;
reevaluate_gpip_interrupts ( ) ;
break ;
case 0x02 :
LOG ( " Write: data direction " < < PADHEX ( 2 ) < < int ( value ) ) ;
gpip_direction_ = value ;
reevaluate_gpip_interrupts ( ) ;
break ;
2019-10-24 22:33:42 -04:00
case 0x03 :
LOG ( " Write: interrupt enable A " < < PADHEX ( 2 ) < < int ( value ) ) ;
interrupt_enable_ = ( interrupt_enable_ & 0x00ff ) | ( value < < 8 ) ;
break ;
case 0x04 :
LOG ( " Write: interrupt enable B " < < PADHEX ( 2 ) < < int ( value ) ) ;
interrupt_enable_ = ( interrupt_enable_ & 0xff00 ) | value ;
break ;
2019-10-25 22:46:30 -04:00
case 0x05 :
LOG ( " Write: interrupt pending A " < < PADHEX ( 2 ) < < int ( value ) ) ;
2019-10-26 15:55:19 -04:00
interrupt_pending_ & = 0x00ff | ( value < < 8 ) ;
2019-10-25 22:46:30 -04:00
update_interrupts ( ) ;
break ;
case 0x06 :
LOG ( " Write: interrupt pending B " < < PADHEX ( 2 ) < < int ( value ) ) ;
2019-10-26 15:55:19 -04:00
interrupt_pending_ & = 0xff00 | value ;
update_interrupts ( ) ;
break ;
case 0x07 :
LOG ( " Write: interrupt in-service A (no-op?) " < < PADHEX ( 2 ) < < int ( value ) ) ;
interrupt_in_service_ & = 0x00ff | ( value < < 8 ) ;
update_interrupts ( ) ;
break ;
case 0x08 :
LOG ( " Write: interrupt in-service B (no-op?) " < < PADHEX ( 2 ) < < int ( value ) ) ;
2019-10-25 22:46:30 -04:00
interrupt_in_service_ & = 0xff00 | value ;
update_interrupts ( ) ;
break ;
2019-10-24 22:33:42 -04:00
case 0x09 :
LOG ( " Write: interrupt mask A " < < PADHEX ( 2 ) < < int ( value ) ) ;
interrupt_mask_ = ( interrupt_mask_ & 0x00ff ) | ( value < < 8 ) ;
update_interrupts ( ) ;
break ;
case 0x0a :
LOG ( " Write: interrupt mask B " < < PADHEX ( 2 ) < < int ( value ) ) ;
interrupt_mask_ = ( interrupt_mask_ & 0xff00 ) | value ;
update_interrupts ( ) ;
break ;
2019-10-25 22:36:01 -04:00
case 0x0b :
LOG ( " Write: vector " < < PADHEX ( 2 ) < < int ( value ) ) ;
interrupt_vector_ = value ;
2019-10-26 15:55:19 -04:00
// If automatic end-of-interrupt mode has now been enabled, clear
// the in-process mask and re-evaluate.
if ( ! ( interrupt_vector_ & 0x08 ) ) {
interrupt_in_service_ = 0 ;
update_interrupts ( ) ;
}
2019-10-25 22:36:01 -04:00
break ;
2019-10-07 22:44:35 -04:00
case 0x0c :
case 0x0d : {
const auto timer = address - 0xc ;
const bool reset = value & 0x10 ;
switch ( value & 0xf ) {
case 0x0 : set_timer_mode ( timer , TimerMode : : Stopped , 0 , reset ) ; break ;
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 ;
case 0x8 : set_timer_mode ( timer , TimerMode : : EventCount , 0 , reset ) ; break ;
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 :
switch ( value & 7 ) {
case 0 : set_timer_mode ( 3 , TimerMode : : Stopped , 0 , false ) ; break ;
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 ) {
case 0 : set_timer_mode ( 2 , TimerMode : : Stopped , 0 , false ) ; break ;
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 ;
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-06 23:14:05 -04:00
}
2019-10-07 22:44:35 -04:00
void MFP68901 : : run_for ( HalfCycles time ) {
cycles_left_ + = time ;
// TODO: this is the stupidest possible implementation. Improve.
int cycles = cycles_left_ . flush < Cycles > ( ) . as_int ( ) ;
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-09 23:01:11 -04:00
decrement_timer ( c ) ;
2019-10-07 22:44:35 -04:00
}
}
}
}
2019-10-06 23:14:05 -04:00
}
HalfCycles MFP68901 : : get_next_sequence_point ( ) {
return HalfCycles ( - 1 ) ;
}
2019-10-07 22:44:35 -04: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-09 23:01:11 -04: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 ) {
// TODO: interrupt. Reload, possibly.
}
}
2019-10-21 23:02:30 -04: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 ( ) {
const uint8_t gpip_state = gpip_input_ ^ gpip_active_edge_ ;
2019-10-24 22:33:42 -04:00
2019-10-21 23:02:30 -04:00
// An interrupt is detected on any falling edge.
2019-10-24 22:33:42 -04: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-24 22:37:32 -04:00
( ( new_interrupt_mask & 0x30 ) < < 2 ) |
2019-10-24 22:33:42 -04:00
( ( new_interrupt_mask & 0xc0 ) < < 8 )
) ;
2019-10-21 23:02:30 -04:00
}
gpip_interrupt_state_ = gpip_state ;
}
2019-10-23 23:09:49 -04:00
// MARK: - Interrupts
2019-10-24 22:33:42 -04:00
void MFP68901 : : begin_interrupts ( int interrupt ) {
2019-10-26 15:55:19 -04:00
interrupt_pending_ | = interrupt ;
2019-10-24 22:33:42 -04:00
update_interrupts ( ) ;
}
void MFP68901 : : end_interrupts ( int interrupt ) {
2019-10-26 15:55:19 -04:00
interrupt_pending_ & = ~ interrupt ;
2019-10-24 22:33:42 -04:00
update_interrupts ( ) ;
}
void MFP68901 : : update_interrupts ( ) {
2019-10-26 15:55:19 -04:00
const auto old_interrupt_line = interrupt_line_ ;
const auto permitted_interrupts = interrupt_pending_ & interrupt_mask_ ;
if ( ! permitted_interrupts ) {
interrupt_line_ = false ;
} else {
if ( interrupt_vector_ & 0x8 ) {
// Software interrupt mode: permit only if no higher interrupts
// are currently in service.
const int highest_bit = 1 < < ( fls ( permitted_interrupts ) - 1 ) ;
interrupt_line_ = ! ( interrupt_in_service_ & ~ ( highest_bit + highest_bit - 1 ) ) ;
} else {
// Auto-interrupt mode; just signal.
interrupt_line_ = true ;
}
}
2019-10-24 22:33:42 -04:00
2019-10-26 15:55:19 -04:00
// Update the delegate if necessary.
2019-10-25 22:36:01 -04:00
if ( interrupt_delegate_ & & interrupt_line_ ! = old_interrupt_line ) {
interrupt_delegate_ - > mfp68901_did_change_interrupt_status ( this ) ;
2019-10-24 22:33:42 -04:00
}
}
2019-10-23 23:09:49 -04:00
2019-10-24 22:33:42 -04:00
bool MFP68901 : : get_interrupt_line ( ) {
return interrupt_line_ ;
2019-10-23 23:09:49 -04:00
}
2019-10-25 22:36:01 -04:00
uint8_t MFP68901 : : acknowledge_interrupt ( ) {
2019-10-26 15:55:19 -04: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-25 22:36:01 -04:00
}
2019-10-26 15:55:19 -04:00
update_interrupts ( ) ;
return ( interrupt_vector_ & 0xf0 ) | uint8_t ( selected ) ;
2019-10-25 22:36:01 -04:00
}
void MFP68901 : : set_interrupt_delegate ( InterruptDelegate * delegate ) {
interrupt_delegate_ = delegate ;
2019-10-23 23:09:49 -04:00
}