all repos — mgba @ cab3a2272d6819387c096aa3591eeb230e114d9c

mGBA Game Boy Advance Emulator

src/gba/extra/battlechip.c (view raw)

  1/* Copyright (c) 2013-2018 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/gba/interface.h>
  7
  8#include <mgba/internal/gba/gba.h>
  9#include <mgba/internal/gba/io.h>
 10#include <mgba/internal/gba/sio.h>
 11
 12mLOG_DECLARE_CATEGORY(GBA_BATTLECHIP);
 13mLOG_DEFINE_CATEGORY(GBA_BATTLECHIP, "GBA BattleChip Gate", "gba.battlechip");
 14
 15enum {
 16	BATTLECHIP_INDEX_HANDSHAKE_0 = 0,
 17	BATTLECHIP_INDEX_HANDSHAKE_1 = 1,
 18	BATTLECHIP_INDEX_ID = 2,
 19	BATTLECHIP_INDEX_END = 6
 20};
 21
 22enum {
 23	BATTLECHIP_OK = 0xFFC6,
 24	BATTLECHIP_CONTINUE = 0xFFFF,
 25};
 26
 27static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
 28static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
 29static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
 30
 31static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
 32static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
 33
 34void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
 35	gate->d.init = GBASIOBattlechipGateInit;
 36	gate->d.deinit = NULL;
 37	gate->d.load = GBASIOBattlechipGateLoad;
 38	gate->d.unload = NULL;
 39	gate->d.writeRegister = GBASIOBattlechipGateWriteRegister;
 40
 41	gate->event.context = gate;
 42	gate->event.callback = _battlechipTransferEvent;
 43	gate->event.priority = 0x80;
 44}
 45
 46bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
 47	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 48	gate->chipId = 0;
 49	return true;
 50}
 51
 52bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
 53	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 54	gate->index = BATTLECHIP_INDEX_END;
 55	return true;
 56}
 57
 58uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
 59	struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
 60	switch (address) {
 61	case REG_SIOCNT:
 62		value &= ~0xC;
 63		value |= 0x8;
 64		if (value & 0x80) {
 65			_battlechipTransfer(gate);
 66		}
 67		break;
 68	case REG_SIOMLT_SEND:
 69		break;
 70	case REG_RCNT:
 71		break;
 72	default:
 73		break;
 74	}
 75	return value;
 76}
 77
 78void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
 79	int32_t cycles = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1];
 80	mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
 81}
 82
 83void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
 84	struct GBASIOBattlechipGate* gate = user;
 85
 86	uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
 87	uint16_t reply = 0xFFFF;
 88	gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
 89	gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
 90	gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
 91	gate->d.p->multiplayerControl.busy = 0;
 92	gate->d.p->multiplayerControl.id = 0;
 93
 94	mLOG(GBA_BATTLECHIP, DEBUG, "> %04x", cmd);
 95
 96	switch (cmd) {
 97	case 0x4000:
 98		gate->index = 0;
 99	// Fall through
100	case 0:
101		switch (gate->index) {
102		case BATTLECHIP_INDEX_HANDSHAKE_0:
103			reply = 0x00FE;
104			break;
105		case BATTLECHIP_INDEX_HANDSHAKE_1:
106			reply = 0xFFFE;
107			break;
108		case BATTLECHIP_INDEX_ID:
109			reply = gate->chipId;
110			break;
111		default:
112			if (gate->index >= BATTLECHIP_INDEX_END) {
113				reply = BATTLECHIP_OK;
114			} else if (gate->index < 0) {
115				reply = BATTLECHIP_CONTINUE;
116			} else {
117				reply = 0;
118			}
119			break;
120		}
121		++gate->index;
122		break;
123	case 0x8FFF:
124		gate->index = -2;
125	// Fall through
126	default:
127	case 0xA3D0:
128		reply = BATTLECHIP_OK;
129		break;
130	case 0x4234:
131	case 0x574A:
132		reply = BATTLECHIP_CONTINUE;
133		break;
134	}
135
136	mLOG(GBA_BATTLECHIP, DEBUG, "< %04x", reply);
137
138	gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
139
140	if (gate->d.p->multiplayerControl.irq) {
141		GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
142	}
143}