2017-12-28 16:40:15 +00:00
/ *
* Copyright ( C ) 2012 Brendan Robert ( BLuRry ) brendan . robert @gmail.com.
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 . 1 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 USA
* /
package jace.core ;
2023-07-03 20:44:23 +00:00
import java.io.IOException ;
import java.util.Optional ;
import java.util.concurrent.CompletableFuture ;
import java.util.concurrent.ExecutionException ;
import java.util.logging.Level ;
import java.util.logging.Logger ;
import jace.LawlessLegends ;
import jace.apple2e.SoftSwitches ;
2017-12-28 16:40:15 +00:00
import jace.config.ConfigurableField ;
import jace.config.InvokableAction ;
import jace.config.Reconfigurable ;
import jace.state.StateManager ;
import javafx.beans.property.BooleanProperty ;
import javafx.beans.property.SimpleBooleanProperty ;
import javafx.beans.value.ChangeListener ;
/ * *
* This is a very generic stub of a Computer and provides a generic set of
* overall functionality , namely boot , pause and resume features . What sort of
* memory , video and cpu get used are totally determined by fully - baked
* subclasses .
*
* @author Brendan Robert ( BLuRry ) brendan . robert @gmail.com
* /
public abstract class Computer implements Reconfigurable {
2023-07-03 20:44:23 +00:00
private RAM memory ;
2017-12-28 16:40:15 +00:00
public CPU cpu ;
2023-07-03 20:44:23 +00:00
private Video video ;
2017-12-28 16:40:15 +00:00
public Keyboard keyboard ;
public StateManager stateManager ;
public Motherboard motherboard ;
2021-11-09 17:20:19 +00:00
public final CompletableFuture < Boolean > romLoaded ;
2017-12-28 16:40:15 +00:00
@ConfigurableField ( category = " advanced " , name = " State management " , shortName = " rewind " , description = " This enables rewind support, but consumes a lot of memory when active. " )
public boolean enableStateManager ;
public final SoundMixer mixer ;
final private BooleanProperty runningProperty = new SimpleBooleanProperty ( false ) ;
/ * *
* Creates a new instance of Computer
* /
public Computer ( ) {
keyboard = new Keyboard ( this ) ;
mixer = new SoundMixer ( this ) ;
2021-11-09 17:20:19 +00:00
romLoaded = new CompletableFuture < > ( ) ;
2017-12-28 16:40:15 +00:00
}
public RAM getMemory ( ) {
return memory ;
}
public Motherboard getMotherboard ( ) {
return motherboard ;
}
ChangeListener < Boolean > runningPropertyListener = ( prop , oldVal , newVal ) - > runningProperty . set ( newVal ) ;
public void setMotherboard ( Motherboard m ) {
if ( motherboard ! = null & & motherboard . isRunning ( ) ) {
motherboard . suspend ( ) ;
}
motherboard = m ;
}
public BooleanProperty getRunningProperty ( ) {
return runningProperty ;
}
2020-06-06 07:05:08 +00:00
2017-12-28 16:40:15 +00:00
public boolean isRunning ( ) {
return getRunningProperty ( ) . get ( ) ;
}
2020-06-06 07:05:08 +00:00
2017-12-28 16:40:15 +00:00
public void notifyVBLStateChanged ( boolean state ) {
for ( Optional < Card > c : getMemory ( ) . cards ) {
c . ifPresent ( card - > card . notifyVBLStateChanged ( state ) ) ;
}
if ( state & & stateManager ! = null ) {
stateManager . notifyVBLActive ( ) ;
}
}
public void setMemory ( RAM memory ) {
if ( this . memory ! = memory ) {
2023-07-03 20:44:23 +00:00
// for (SoftSwitches s : SoftSwitches.values()) {
// s.getSwitch().unregister();
// }
2017-12-28 16:40:15 +00:00
if ( this . memory ! = null ) {
this . memory . detach ( ) ;
}
2023-07-03 20:44:23 +00:00
this . memory = memory ;
if ( memory ! = null ) {
memory . attach ( ) ;
for ( SoftSwitches s : SoftSwitches . values ( ) ) {
s . getSwitch ( ) . register ( ) ;
}
}
2017-12-28 16:40:15 +00:00
}
}
public void waitForNextCycle ( ) {
//@TODO IMPLEMENT TIMER SLEEP CODE!
}
public Video getVideo ( ) {
return video ;
}
public void setVideo ( Video video ) {
this . video = video ;
2023-07-03 20:44:23 +00:00
if ( LawlessLegends . getApplication ( ) ! = null ) {
LawlessLegends . getApplication ( ) . controller . connectVideo ( video ) ;
}
2017-12-28 16:40:15 +00:00
}
public CPU getCpu ( ) {
return cpu ;
}
public void setCpu ( CPU cpu ) {
this . cpu = cpu ;
}
public void loadRom ( String path ) throws IOException {
memory . loadRom ( path ) ;
2021-11-09 17:20:19 +00:00
romLoaded . complete ( true ) ;
2017-12-28 16:40:15 +00:00
}
public void deactivate ( ) {
2017-12-29 07:50:53 +00:00
if ( cpu ! = null ) {
cpu . suspend ( ) ;
}
if ( motherboard ! = null ) {
motherboard . suspend ( ) ;
}
if ( video ! = null ) {
2020-06-06 07:05:08 +00:00
video . suspend ( ) ;
2017-12-29 07:50:53 +00:00
}
if ( mixer ! = null ) {
mixer . detach ( ) ;
}
2017-12-28 16:40:15 +00:00
}
@InvokableAction (
name = " Cold boot " ,
description = " Process startup sequence from power-up " ,
category = " general " ,
alternatives = " Full reset;reset emulator " ,
defaultKeyMapping = { " Ctrl+Shift+Backspace " , " Ctrl+Shift+Delete " } )
public void invokeColdStart ( ) {
2021-11-09 17:20:19 +00:00
if ( ! romLoaded . isDone ( ) ) {
2017-12-28 16:40:15 +00:00
Thread delayedStart = new Thread ( ( ) - > {
2021-11-09 17:20:19 +00:00
try {
romLoaded . get ( ) ;
2023-07-03 20:44:23 +00:00
memory . resetState ( ) ;
2021-11-09 17:20:19 +00:00
coldStart ( ) ;
} catch ( InterruptedException | ExecutionException ex ) {
Logger . getLogger ( Computer . class . getName ( ) ) . log ( Level . SEVERE , null , ex ) ;
2017-12-28 16:40:15 +00:00
}
} ) ;
delayedStart . start ( ) ;
} else {
2023-07-03 20:44:23 +00:00
memory . resetState ( ) ;
2017-12-28 16:40:15 +00:00
coldStart ( ) ;
}
}
public abstract void coldStart ( ) ;
@InvokableAction (
name = " Warm boot " ,
description = " Process user-initatiated reboot (ctrl+apple+reset) " ,
category = " general " ,
2018-01-11 08:21:20 +00:00
alternatives = " reboot;reset;three-finger-salute;restart " ,
2017-12-28 16:40:15 +00:00
defaultKeyMapping = { " Ctrl+Ignore Alt+Ignore Meta+Backspace " , " Ctrl+Ignore Alt+Ignore Meta+Delete " } )
public void invokeWarmStart ( ) {
warmStart ( ) ;
}
public abstract void warmStart ( ) ;
public Keyboard getKeyboard ( ) {
return this . keyboard ;
}
protected abstract void doPause ( ) ;
protected abstract void doResume ( ) ;
@InvokableAction ( name = " Pause " , description = " Stops the computer, allowing reconfiguration of core elements " , alternatives = " freeze;halt " , defaultKeyMapping = { " meta+pause " , " alt+pause " } )
public boolean pause ( ) {
boolean result = getRunningProperty ( ) . get ( ) ;
doPause ( ) ;
getRunningProperty ( ) . set ( false ) ;
return result ;
}
2018-01-11 08:21:20 +00:00
@InvokableAction ( name = " Resume " , description = " Resumes the computer if it was previously paused " , alternatives = " unpause;unfreeze;resume;play " , defaultKeyMapping = { " meta+shift+pause " , " alt+shift+pause " } )
2017-12-28 16:40:15 +00:00
public void resume ( ) {
doResume ( ) ;
getRunningProperty ( ) . set ( true ) ;
}
@Override
public void reconfigure ( ) {
mixer . reconfigure ( ) ;
if ( enableStateManager ) {
stateManager = StateManager . getInstance ( this ) ;
} else {
stateManager = null ;
StateManager . getInstance ( this ) . invalidate ( ) ;
}
}
}