Skip to content
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

add tk10 keyboard device for apple ii clones #13004

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

Conversation

goldnchild
Copy link
Contributor

I found a schematic of the TK10 keyboard and made a device for it.

The rom can be found as part of the am64 apple II clone in apple2.cpp.

@rb6502
Copy link
Contributor

rb6502 commented Nov 21, 2024

If it's part of the AM64 machine, then you should just hook it up to the AM64. Also, unless the keyboard was also used on non-Apple machines you can just put it in src/mame/apple.

Copy link
Member

@cuavas cuavas left a comment

Choose a reason for hiding this comment

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

This isn’t much use if it isn’t actually attached to anything. The device won’t be linked since there’s nothing referenced in the translation unit, so it won’t appear in -listxml or detect things with -romident, etc.

Also it should be in src/mame/apple.

Comment on lines 92 to 101
m_tk10cpu->p1_in_cb().set([this] () -> u8
{
for (int i = 0; i < 8; i++)
{
if (!BIT(m_tk10cpu->p2_r(), i)) return m_keyboard[i]->read();
// read key matrix:
// a low value in p2 bit will drive bit of p1 low when switch contact made
}
return 0xff;
});
Copy link
Member

Choose a reason for hiding this comment

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

You should lift the p2_r out of the loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, done

Comment on lines 145 to 156
PORT_START("P21")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) // _ SHIFT=^
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) // ]
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) // [
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) // @ SHIFT=^
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) // /
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD)
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD)
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK)

PORT_START("P22")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) // BACKSLASH
Copy link
Member

Choose a reason for hiding this comment

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

Do these keys really have blank key caps? It’s bad to have inputs that lack either PORT_NAME or PORT_CHAR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, tried to label them all

Comment on lines 4 to 5
#ifndef MAME_TK10_KEYBOARD_H
#define MAME_TK10_KEYBOARD_H
Copy link
Member

Choose a reason for hiding this comment

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

This doesn’t match the path.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, moved to src/mame/apple

Comment on lines 24 to 28
INPUT_CHANGED_MEMBER( tk10_func );
INPUT_CHANGED_MEMBER( tk10_reset );

auto reset_write_cb() { return m_reset_write.bind(); }
auto data_write_cb() { return m_data_write.bind(); }
Copy link
Member

Choose a reason for hiding this comment

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

Conventionally, configuration members come before other stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, fixed

Comment on lines 31 to 35
// device-level overrides
virtual void device_start() override;
virtual void device_add_mconfig(machine_config &config) override;
virtual ioport_constructor device_input_ports() const override;
virtual const tiny_rom_entry *device_rom_region() const override;
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 copy/paste this meaningless comment. Also, please add ATTR_COLD for typical cold lifecycle member functions as @holub has done for most existing devices.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, done

@goldnchild
Copy link
Contributor Author

If it's part of the AM64 machine, then you should just hook it up to the AM64. Also, unless the keyboard was also used on non-Apple machines you can just put it in src/mame/apple.

I wanted to check with you on changes to apple2.cpp.

adding a member variable to hold the value of tk10 reset line.

void apple2_state::tk10_data_write(u8 data)
{
if (m_maincpu->total_cycles() < 25000)
{
return;
}

	if (data & 0x80)
	{
		m_transchar = data & 0x7f;
		m_strobe = 0x80;
	}

}

void apple2_state::tk10_reset_write(u8 data)
{
m_tk10_reset_data = data;
}

TIMER_DEVICE_CALLBACK_MEMBER(apple2_state::apple2_interrupt_tk10)
{
basically the same as the regular function, only this time using the tk10 reset line
}

void apple2_state::am64(machine_config &config)
{
apple2p(config);
m_scantimer->configure_scanline(FUNC(apple2_state::apple2_interrupt_tk10), "screen", 0, 1);

tk10_keyboard_device &tk10(TK10_KEYBOARD(config, "tk10", 0));
tk10.data_write_cb().set(FUNC(apple2_state::tk10_data_write));
tk10.reset_write_cb().set(FUNC(apple2_state::tk10_reset_write));

}

I didn't want to touch any of the existing code, like removing the normal keyboard.
// config.device_remove(A2_KBDC_TAG);

When you switch the keyboard selection under the Input Menu, the normal keyboard can be deactivated and the tk10 can be activated.

Copy link
Member

@cuavas cuavas left a comment

Choose a reason for hiding this comment

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

You really should move the systems with the different keyboard to a derived class so any additional members don’t pollute the machines that don’t have it. You can also conveniently override machine_start to save additional members.

If these machines don’t have the “standard” keyboard in addition to the TK-10 keyboard, they shouldn’t be emulated as having both (if they could be used with one or the other, it needs to be slotted, but that’s another story).

You could make the driver state class get the keyboard reset input via a virtual member function to avoid copy/pasting the the scan line callback.

Comment on lines 949 to 952
if (m_maincpu->total_cycles() < 25000)
{
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

This is definitely the wrong way to do this. What’s actually going on here? Is there a monostable that holds the microcontroller reset for a while after power-on? If there is, is it also affected by reset? Or is there some weird glue logic that just ignores data from the keyboard after power-on/reset? Either way you should probably be using a timer.

Copy link
Contributor

Choose a reason for hiding this comment

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

This has nothing to do with hardware and everything to do with MAME. The keypress to start the emulation past the info screen leaks into the emulated system, and that was causing issues with disks where you boot them and then press a key to skip a title screen or select a menu item or whatever.

Copy link
Member

Choose a reason for hiding this comment

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

That “leak” is very important for systems that require you to hold a button very early in the boot process to get into the desired mode :/

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't disagree that it can be a handy shortcut, but I also spent over a week trying to reconcile reports that MAME skipped the initial screens of a bunch of random software for no apparent reason.

Copy link
Member

Choose a reason for hiding this comment

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

I got fooled by it as well – I was hitting an arrow key to dismiss the message, which toggles a switch by default on an Intel dev system in MAME. I was wondering why the switch wasn’t in its default state when I started with no .cfg file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My strategy was to match the routines as closely as possible to the existing code for processing the keyboard.

Comment on lines 4 to 5
#ifndef MAME_TK10_KEYBOARD_H
#define MAME_TK10_KEYBOARD_H
Copy link
Member

Choose a reason for hiding this comment

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

This will fail CI now that #include guards are checked.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, so I just have to rename it so it matches the filename

MAME_TK10_KEYBOARD_H
to
MAME_APPLE_TK10_KEYBOARD_H

so that the include guard has the proper name like:

MAME_APPLE_APPLE2COMMON_H

Comment on lines +69 to +74
for (int i = 0; i < 8; i++)
{
if (!BIT(p2_r, i)) return m_keyboard[i]->read();
// read key matrix:
// a low value in p2 bit will drive bit of p1 low when switch contact made
}
Copy link
Member

Choose a reason for hiding this comment

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

You need to AND together the active rows in a switch matrix. It isn’t a multiplexer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not quite sure that AND would work here in this situation.

It treats P2 as a selector to select which column of the matrix is being scanned.

I got the schematic from:

http://john.ccac.rwth-aachen.de:8000/patrick/schematics.htm
http://john.ccac.rwth-aachen.de:8000/patrick/data/AppleClone_Keyboard.pdf

I did it that way because the code scans the p2 bits one by one, and then reads all of the p1 bits as a byte for the selected column.

I setup the PORT_BITS to match which bits of P1 will get driven low when P2 gets scanned.

0:05A mov r6,#$3F BE 3
0:05C mov r2,#$7F BA 7
0:05E mov r4,#$08 BC 0
0:060 mov a,r2 FA
0:061 rl a E7
0:062 mov r2,a AA
0:063 outl p2,a 3A
0:064 in a,p1 09
... loops back to 060

It writes a single bit low to p2, then reads p1. The low bit in p2 rotates through (the RL A), doing the scanning .

Comment on lines 954 to 959
if (data & 0x80)
{
m_transchar = data & 0x7f;
m_strobe = 0x80;
// printf("new char = %04x (%02x)\n", m_lastchar&0x3f, m_transchar);
}
Copy link
Member

Choose a reason for hiding this comment

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

If the top bit is the strobe, you need to actually look for edges.

Copy link
Contributor Author

@goldnchild goldnchild Dec 2, 2024

Choose a reason for hiding this comment

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

It seems to work fine without edge detection, I don't think the 8039 will write the OUTL BUS unless it actually changes, so its won't write the OUTL BUS twice with the strobe high.

0:147 mov r6,a AE
0:148 jmp $109 24 0
0:14A mov a,r5 FD
0:14B orl a,#$80 43 8
0:14D outl bus,a 02 write it with strobe high
0:14E mov r5,#$10 BD 1
0:150 djnz r5,$150 ED 5 delay a bit
0:152 anl a,#$7F 53 7 write it with strobe low
0:154 outl bus,a 02

tk10: fix the include guard to have proper filename
@goldnchild
Copy link
Contributor Author

You really should move the systems with the different keyboard to a derived class so any additional members don’t pollute the machines that don’t have it. You can also conveniently override machine_start to save additional members.

ok, created a new class and moved all the members to it, however there were a couple of member variables that were private and couldn't be accessed until I marked them as protected.

I also moved all of the routines for the tk10 and the class definition to be together so they aren't scattered around the file.

If these machines don’t have the “standard” keyboard in addition to the TK-10 keyboard, they shouldn’t be emulated as having both (if they could be used with one or the other, it needs to be slotted, but that’s another story).

I didn't want to disturb the existing code so I removed the ay3600 with config.device_remove()

The IO ports still show up in the configuration menus but the ay3600 is effectively disabled.

You could make the driver state class get the keyboard reset input via a virtual member function to avoid copy/pasting the the scan line callback.

I wanted to match the existing code as much as possible so is it really bad to reassign the scan line callback to use the tk10?

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