Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagohm committed Sep 21, 2024
2 parents 3f37152 + 10e8af9 commit 1a4c949
Show file tree
Hide file tree
Showing 83 changed files with 2,238 additions and 1,488 deletions.
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
package br.tiagohm.nestalgia.core

import br.tiagohm.nestalgia.core.ArkanoidController.Button.*
import br.tiagohm.nestalgia.core.ArkanoidController.Button.FIRE

// https://www.nesdev.org/wiki/Arkanoid_controller

class ArkanoidController(
console: Console, type: ControllerType, port: Int,
private val keyMapping: KeyMapping,
keyMapping: KeyMapping,
) : ControlDevice(console, type, port) {

enum class Button : ControllerButton, HasCustomKey {
FIRE;

enum class Button(override val bit: Int) : ControllerButton, HasCustomKey {
FIRE(0);
override val bit = ordinal

override val keyIndex = 1
}

@Volatile private var currentValue = (0xF4 - 0x54) / 2
@Volatile private var stateBuffer = 0
private val sensibility = SENSIBILITY_PX[console.settings.arkanoidSensibility[port]]
private val key = keyMapping.key(FIRE)

override fun setStateFromInput() {
pressedStateFromKeys()
}

@Suppress("NOTHING_TO_INLINE")
private inline fun pressedStateFromKeys() {
setPressedState(FIRE, keyMapping.key(FIRE))
setPressedState(FIRE, key)
}

override fun refreshStateBuffer() {
Expand Down Expand Up @@ -78,8 +75,12 @@ class ArkanoidController(
strobeOnWrite(value)
}

companion object {
companion object : HasDefaultKeyMapping {

private val SENSIBILITY_PX = intArrayOf(0, 16, 32, 64)

override fun populateWithDefault(keyMapping: KeyMapping) {
keyMapping.customKey(FIRE, MouseButton.LEFT)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package br.tiagohm.nestalgia.core

import br.tiagohm.nestalgia.core.BandaiHyperShot.Button.*
import br.tiagohm.nestalgia.core.ControllerType.*
import br.tiagohm.nestalgia.core.BandaiHyperShot.Button.FIRE
import br.tiagohm.nestalgia.core.ControllerType.BANDAI_HYPER_SHOT
import br.tiagohm.nestalgia.core.Zapper.Companion.isLight

class BandaiHyperShot(console: Console, keyMapping: KeyMapping) : StandardController(console, BANDAI_HYPER_SHOT, EXP_DEVICE_PORT, keyMapping) {
Expand Down Expand Up @@ -65,8 +65,14 @@ class BandaiHyperShot(console: Console, keyMapping: KeyMapping) : StandardContro
hyperShotStateBuffer = s.readInt("hyperShotStateBuffer")
}

companion object {
companion object : HasDefaultKeyMapping {

const val AIM_OFFSCREEN_CUSTOM_KEY = 254

override fun defaultKeyMapping() = StandardController.defaultKeyMapping().also(::populateWithDefault)

override fun populateWithDefault(keyMapping: KeyMapping) {
keyMapping.customKey(FIRE, MouseButton.LEFT)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package br.tiagohm.nestalgia.core

import br.tiagohm.nestalgia.core.BandaiMicrophone.Button.*
import br.tiagohm.nestalgia.core.ControllerType.*
import br.tiagohm.nestalgia.core.ControllerType.BANDAI_MICROPHONE

class BandaiMicrophone(console: Console) : ControlDevice(console, BANDAI_MICROPHONE, MAPPER_INPUT_PORT) {

enum class Button(override val bit: Int) : ControllerButton, HasCustomKey {
A(0),
B(1),
MICROPHONE(2);
enum class Button : ControllerButton, HasCustomKey {
A,
B,
MICROPHONE;

override val bit = ordinal

override val keyIndex = 7 + ordinal
}
Expand Down
58 changes: 58 additions & 0 deletions core/src/main/kotlin/br/tiagohm/nestalgia/core/Bmc830425C4391T.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package br.tiagohm.nestalgia.core

// https://www.nesdev.org/wiki/NES_2.0_Mapper_320

class Bmc830425C4391T(console: Console) : Mapper(console) {

override val prgPageSize = 0x4000

override val chrPageSize = 0x2000

@Volatile private var innerReg = 0
@Volatile private var outerReg = 0
@Volatile private var prgMode = false

override fun initialize() {
selectChrPage(0, 0)
updateState()
}

private fun updateState() {
if (prgMode) {
// UNROM mode
selectPrgPage(0, (innerReg and 0x07) or (outerReg shl 3))
selectPrgPage(1, 0x07 or (outerReg shl 3))
} else {
// UOROM mode
selectPrgPage(0, innerReg or (outerReg shl 3))
selectPrgPage(1, 0x0F or (outerReg shl 3))
}
}

override fun writeRegister(addr: Int, value: Int) {
innerReg = value and 0x0F

if (addr and 0xFFE0 == 0xF0E0) {
outerReg = addr and 0x0F
prgMode = (addr shr 4).bit0
}

updateState()
}

override fun saveState(s: Snapshot) {
super.saveState(s)

s.write("innerReg", innerReg)
s.write("outerReg", outerReg)
s.write("prgMode", prgMode)
}

override fun restoreState(s: Snapshot) {
super.restoreState(s)

innerReg = s.readInt("innerReg")
outerReg = s.readInt("outerReg")
prgMode = s.readBoolean("prgMode")
}
}
53 changes: 53 additions & 0 deletions core/src/main/kotlin/br/tiagohm/nestalgia/core/BmcGn45.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package br.tiagohm.nestalgia.core

// https://wiki.nesdev.com/w/index.php/INES_Mapper_366

class BmcGn45(console: Console) : MMC3(console) {

@Volatile private var selectedBlock = 0
@Volatile private var wramEnabled = false

override val registerStartAddress = 0x6000

override val registerEndAddress = 0xFFFF

override fun reset(softReset: Boolean) {
super.reset(softReset)

if (softReset) {
selectedBlock = 0
wramEnabled = false
resetMMC3()
updateState()
}
}

override fun selectChrPage(slot: Int, page: Int, memoryType: ChrMemoryType) {
super.selectChrPage(slot, (page and 0x7F) or (selectedBlock shl 3), memoryType)
}

override fun selectPrgPage(slot: Int, page: Int, memoryType: PrgMemoryType) {
super.selectPrgPage(slot, (page and 0x0F) or selectedBlock, memoryType)
}

override fun writeRegister(addr: Int, value: Int) {
if (addr < 0x7000) {
if (!wramEnabled) {
selectedBlock = addr and 0x30
wramEnabled = addr.bit7
updateState()
} else {
writePrgRam(addr, value)
}
} else if (addr < 0x8000) {
if (!wramEnabled) {
selectedBlock = value and 0x30
updateState()
} else {
writePrgRam(addr, value)
}
} else {
super.writeRegister(addr, value)
}
}
}
26 changes: 18 additions & 8 deletions core/src/main/kotlin/br/tiagohm/nestalgia/core/Console.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ data class Console(@JvmField val settings: EmulationSettings = EmulationSettings

private val pauseCounter = AtomicInteger(0)

@PublishedApi @JvmField internal var memoryManager = MemoryManager(this)
@PublishedApi internal lateinit var memoryManager: MemoryManager

@JvmField val batteryManager = BatteryManager(this)
@JvmField val notificationManager = NotificationManager()

@Volatile var controlManager = ControlManager(this)
private set

@JvmField internal val debugger = Debugger(this)

@PublishedApi @JvmField internal var controlManager = ControlManager(this)
@PublishedApi @JvmField internal var cpu = Cpu(this)
@PublishedApi @JvmField internal var apu = Apu(this)
@PublishedApi @JvmField internal var ppu = Ppu(this)
@PublishedApi internal lateinit var cpu: Cpu
@PublishedApi internal lateinit var apu: Apu
@PublishedApi internal lateinit var ppu: Ppu
@PublishedApi @JvmField internal var systemActionManager = SystemActionManager(this)

@JvmField internal val videoDecoder = VideoDecoder(this)
Expand Down Expand Up @@ -58,6 +60,8 @@ data class Console(@JvmField val settings: EmulationSettings = EmulationSettings
private set

override fun close() {
running.set(false)

debugger.close()

stop()
Expand Down Expand Up @@ -183,8 +187,12 @@ data class Console(@JvmField val settings: EmulationSettings = EmulationSettings
pollCounter = controlManager.pollCounter
}

controlManager = if (newMapper.info.system == GameSystem.VS_SYSTEM) VsControlManager(this)
else ControlManager(this)
if (newMapper.info.system == GameSystem.VS_SYSTEM && controlManager !is VsControlManager) {
controlManager = VsControlManager(this)
} else if (newMapper.info.system != GameSystem.VS_SYSTEM && controlManager is VsControlManager) {
controlManager = ControlManager(this)
}

controlManager.initialize()

batteryManager.enable()
Expand Down Expand Up @@ -280,6 +288,8 @@ data class Console(@JvmField val settings: EmulationSettings = EmulationSettings
controlManager.reset(softReset)
soundMixer.reset(softReset)

(keyManager as? Resetable)?.reset(softReset)

resetRunTimers = true

notificationManager.sendNotification(if (softReset) GAME_RESET else GAME_LOADED)
Expand Down Expand Up @@ -364,7 +374,7 @@ data class Console(@JvmField val settings: EmulationSettings = EmulationSettings
updateRegion(true)

try {
while (true) {
while (running.get()) {
runFrame()

soundMixer.processEndOfFrame()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ abstract class ControlDevice(

override fun reset(softReset: Boolean) = Unit

protected fun isCurrentPort(addr: Int): Boolean {
@Suppress("NOTHING_TO_INLINE")
protected inline fun isCurrentPort(addr: Int): Boolean {
return port == (addr - 0x4016)
}

Expand Down
53 changes: 33 additions & 20 deletions core/src/main/kotlin/br/tiagohm/nestalgia/core/ControlManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ package br.tiagohm.nestalgia.core
import br.tiagohm.nestalgia.core.ConsoleType.*
import br.tiagohm.nestalgia.core.ControlDevice.Companion.EXP_DEVICE_PORT
import br.tiagohm.nestalgia.core.ControllerType.*
import br.tiagohm.nestalgia.core.MemoryAccessType.*
import br.tiagohm.nestalgia.core.MemoryAccessType.READ
import br.tiagohm.nestalgia.core.MemoryAccessType.WRITE
import org.slf4j.LoggerFactory

open class ControlManager(protected val console: Console) : MemoryHandler, Resetable, Initializable, Snapshotable, AutoCloseable {

private val inputProviders = HashSet<InputProvider>()
private val inputRecorders = HashSet<InputRecorder>()
private val systemDevices = HashSet<ControlDevice>()
private val controlDevices = HashSet<ControlDevice>()
private val inputProviders = HashSet<InputProvider>(1)
private val inputRecorders = HashSet<InputRecorder>(1)
private val systemDevices = HashSet<ControlDevice>(1)
private val controlDevices = HashSet<ControlDevice>(2)
private val controlManagerListeners = HashSet<ControlManagerListener>(1)

@JvmField internal var pollCounter = 0
@JvmField internal var lagCounter = 0
Expand All @@ -37,6 +39,14 @@ open class ControlManager(protected val console: Console) : MemoryHandler, Reset
inputRecorders.remove(inputRecorder)
}

fun registerControlManagerListener(listener: ControlManagerListener) {
controlManagerListeners.add(listener)
}

fun unregisterControlManagerListener(listener: ControlManagerListener) {
controlManagerListeners.remove(listener)
}

fun addSystemControlDevice(device: ControlDevice) {
controlDevices.clear()
systemDevices.add(device)
Expand Down Expand Up @@ -80,10 +90,10 @@ open class ControlManager(protected val console: Console) : MemoryHandler, Reset

clearDevices()

createControllerDevice(settings.port1.type, 0)?.also(::registerControlDevice)
createControllerDevice(settings.port2.type, 1)?.also(::registerControlDevice)
createControllerDevice(settings.port1, 0)?.also(::registerControlDevice)
createControllerDevice(settings.port2, 1)?.also(::registerControlDevice)

val expansionDevice = createControllerDevice(settings.expansionPort.type, EXP_DEVICE_PORT)
val expansionDevice = createControllerDevice(settings.expansionPort, EXP_DEVICE_PORT)

if (expansionDevice != null) {
registerControlDevice(expansionDevice)
Expand All @@ -103,27 +113,23 @@ open class ControlManager(protected val console: Console) : MemoryHandler, Reset
}
}

private fun createControllerDevice(type: ControllerType, port: Int): ControlDevice? {
val settings = console.settings
private fun createControllerDevice(settings: ControllerSettings, port: Int): ControlDevice? {
val type = settings.type
val keyMapping = settings.keyMapping

val keyMapping = when (port) {
0 -> settings.port1.keyMapping
1 -> settings.port2.keyMapping
EXP_DEVICE_PORT -> settings.expansionPort.keyMapping
else -> KeyMapping()
}
settings.populateKeyMappingWithDefault()

val device = when (type) {
NES_CONTROLLER,
FAMICOM_CONTROLLER,
FAMICOM_CONTROLLER_P2 -> StandardController(console, type, port, keyMapping)
NES_ZAPPER -> Zapper(console, type, port, keyMapping)
FAMICOM_ZAPPER -> Zapper(console, type, port, keyMapping)
FAMICOM_ZAPPER -> Zapper(console, type, EXP_DEVICE_PORT, keyMapping)
ASCII_TURBO_FILE -> AsciiTurboFile(console)
BATTLE_BOX -> BattleBox(console)
FOUR_SCORE -> FourScore(console, type, 0, *settings.subPort1)
TWO_PLAYER_ADAPTER -> TwoPlayerAdapter(console, type, *settings.expansionSubPort)
FOUR_PLAYER_ADAPTER -> FourScore(console, type, EXP_DEVICE_PORT, *settings.expansionSubPort)
FOUR_SCORE -> FourScore(console, type, 0, *console.settings.subPort1)
TWO_PLAYER_ADAPTER -> TwoPlayerAdapter(console, type, *console.settings.expansionSubPort)
FOUR_PLAYER_ADAPTER -> FourScore(console, type, EXP_DEVICE_PORT, *console.settings.expansionSubPort)
NES_ARKANOID_CONTROLLER -> ArkanoidController(console, type, port, keyMapping)
FAMICOM_ARKANOID_CONTROLLER -> ArkanoidController(console, type, EXP_DEVICE_PORT, keyMapping)
POWER_PAD_SIDE_A,
Expand All @@ -134,9 +140,16 @@ open class ControlManager(protected val console: Console) : MemoryHandler, Reset
FAMILY_TRAINER_MAT_SIDE_B -> FamilyTrainerMat(console, type, keyMapping)
KONAMI_HYPER_SHOT -> KonamiHyperShot(console, keyMapping)
HORI_TRACK -> HoriTrack(console, keyMapping)
PACHINKO -> Pachinko(console, keyMapping)
PARTY_TAP -> PartyTap(console, keyMapping)
JISSEN_MAHJONG -> JissenMahjong(console, keyMapping)
SUBOR_MOUSE -> SuborMouse(console, port, keyMapping)
SUBOR_KEYBOARD -> SuborKeyboard(console, keyMapping)
else -> return null
}

controlManagerListeners.forEach { it.onControlDeviceChange(console, device, port) }

LOG.info("{} connected. type={}, port={}", device::class.simpleName, device.type, device.port)

return device
Expand Down
Loading

0 comments on commit 1a4c949

Please sign in to comment.