Skip to content

src/devices/bus/a2bus/a2frob.cpp: Add Apple II frob device #13596

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

goldnchild
Copy link
Contributor

Adding a driver for the FrobCo Frob Atari 2600 ROM emulator for the Apple II.

https://forums.atariage.com/topic/380267-the-frob-livesagain/

Comment on lines +22 to +41
#include "emu.h"

#include "a2bus.h"
#include "bus/vcs/rom.h"
#include "bus/vcs/vcs_slot.h"
#include "bus/vcs_ctrl/ctrl.h"
#include "cpu/m6502/m6507.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "sound/tiaintf.h"
#include "speaker.h"
#include "../../mame/atari/tia.h"

#include "machine/mos6530.h"

//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"

#include "a2frob.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The module’s own header should be the first thing after the prefix header.
  • Group included headers by module, order from most dependent to least dependent, and sort within groups.
  • Things that are inline code (e.g. logmacro.h) come after declaration headers.

This is all documented: https://docs.mamedev.org/contributing/cxx.html#structural-organization

Also, you can’t have things in src/devices depending on things in src/mame.

Comment on lines +88 to +95

virtual bool take_c800() override { return false; }

u8 m_frob_mem[0x1000] = { 0 }; // 4k frob memory

u8 read_frob_mem(offs_t offset) { return !m_frob_apple_control ? 0 : m_frob_mem[offset & 0xfff]; }

void write_frob_mem(offs_t offset, u8 data) { m_frob_mem[offset & 0xfff] = data; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don’t intermix data members and member functions. Keep each grouped separately.

Comment on lines +97 to +113
public:
void a2600_write_fff0(offs_t offset, u8 data)
{
m_byte_for_apple = data;
m_byte_waiting_flag_apple = 1;
}

u8 a2600_read_fff2(offs_t offset)
{
if (!machine().side_effects_disabled()) m_byte_waiting_flag_vcs = 0;
return m_byte_for_vcs;
}

u8 a2600_read_fff1(offs_t offset)
{
return (m_byte_waiting_flag_apple ? 0 : 1) << 7 | (m_byte_waiting_flag_vcs << 6);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have you made this stuff public?

Also, you need to use synchronize barriers to get data from one CPU to the other to avoid things being seen in the wrong order. The card CPU should see the flag indicating that the mailbox for the host CPU is full as soon as it writes it, but the host CPU should not see the flag or the updated value until your on the other side of a sync barrier. The same applies for communication in the opposite direction.

Copy link
Member

@happppp happppp Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

synchronize() doesn't work the opposite direction*, some drivers do trickery with machine().scheduler().perfect_quantum(x) to make sure the 'opposite' side comms answer timing is correct.

*:
scheduler runs CPUs in serial, CPU A, then CPU B.
let's say CPU A: 1ms.
scheduler runs CPU B.
At 0.5ms point, CPU B wants to write to a latch that shared with CPU A. CPU A however, is already in the future (from CPU B perspective). And if synchronize() is used here, AFAIK it will be delayed until another 0.5ms has passed. If CPU B were to verify its own written latch value during this time, things will go wrong. -- actually, not sure what would happen here. I think CPU B -> CPU C comms here would go fine with a synchronize()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And to solve that properly (since you don’t know in advance whether a CPU is going to attempt to communicate across scheduling domains), you need an efficient way to roll back and retry a smaller timeslice until you get an “acceptable” delay. But since the CPUs themselves don’t know who’s ahead at any given time, putting both directions through synchronize at least means that whichever CPU is ahead will try to end its timeslice early when possible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for CPU B to be ahead of CPU A? And I don't mean slight deviations with icount < 0.

eg. you have 3 CPUs in a driver. Then the scheduler runs the CPUs in this order A,B.C.A,B,C,A,B,C, etc. (or A,B,C,B,C,B,C if CPU B does a synchronize()). It doesn't eenie meenie miney mo them. CPU B is never ahead of CPU A. CPU C is never ahead of CPU A or B.
The order is the same as the machine config order. It's why for arcade drivers with a 1-way soundlatch, it's important to list the audiocpu after maincpu.

Comment on lines +154 to +172
u8 a2600_frob_base_device::read_c0nx(u8 offset)
{
// offset 0 is read from frob status (bits 7 = write ok, 6 = read ok) (other bits will float)
switch (offset & 0x1)
{
case 0:
return ((m_byte_waiting_flag_vcs ? 0 : 1) << 7) |
(m_byte_waiting_flag_apple << 6);
break;
case 1:
if (!machine().side_effects_disabled())
{
m_byte_waiting_flag_apple = 0; // clear byte waiting for apple
}
return m_byte_for_apple;
default:
return 0;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment about needing sync barriers applies. Host CPU should see the flag indicating the mailbox for the card CPU is full as soon as it writes it, but should not see the mailbox full from the card CPU until it’s on the other side of a sync barrier.

Comment on lines +183 to +187
m_bidirectional_active = BIT(data, 5);
m_frob_page = BIT(data, 0, 4);
// printf("m_frob_apple_control = %x m_frob_page = %x\n",m_frob_apple_control, m_frob_page);
break;
case 1:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use logmacro.h helpers for configurable logging, not commented prints. Commented code always rots.

Comment on lines +207 to +214
void a2600_frob_base_device::switch_A_w(uint8_t data)
{
/* Left controller port */
m_joy1->joy_w( data >> 4 );

/* Right controller port */
m_joy2->joy_w( data & 0x0f );
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use consistent formatting for function calls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants