MAME – Rants from Vas https://rants.vastheman.com Take a hit with V-Real Fri, 23 Dec 2022 21:58:51 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.1 Kabutogani! (or the XE‑1AP Analog Joypad) https://rants.vastheman.com/2022/12/24/kabutogani/ https://rants.vastheman.com/2022/12/24/kabutogani/#respond Fri, 23 Dec 2022 21:57:16 +0000 https://rants.vastheman.com/?p=361 Way back in the 1980s, the days of the exciting home computer and game console boom, Micom Soft introduced their XE‑1 line of game controller peripherals. From the humble XE‑1B two-button joystick for Japanese home computers, to the XE‑1ST2 switchable 4-way/8-way joystick with support for FM Towns and Sega consoles, to the fully programmable XE‑1SFC Super Famicom stick (with an LCD screen), there was something for everyone. They were known for their excellent build quality, unconventional features, and of course their rotating button clusters.

The XE‑1AJ Intelligent Controller is possible the most impressive, and definitely the most imposing, member of the product line. Also sold as the Sharp CZ-8NJ2 Cyber Stick, it is a large HOTAS flight controller for desktop use. Just the size and weight are impressive: it takes up a lot of space on your desk, the stick and throttle shafts are metal, it’s clearly built to last. It features an analog stick and throttle, a thumb button and trigger on the stick, a thumb button and two-way rocker switch on the throttle, and five panel buttons. The stick button and trigger functions can be exchanged, auto-fire is supported for the stick button and trigger, and a variety of special modes can be enabled by holding panel buttons while pressing the reset button. If that isn’t enough, the stick and throttle positions can be reversed for left-handed use. It can even be switched to digital mode for games that don’t support analog controls.

With the launch of the Sega Mega Drive in 1988, someone at Micom Soft must have started wondering, “How can we bring all the fun of the XE‑1AJ to couch gaming?” The result was the XE‑1AP joypad. Brought to market in 1989, the XE‑1AP was the first control pad to feature analog controls. It was nicknamed the horseshoe crab (kabutogani in Japanese) due to its shape. It actually succeeded in packing all the key functionality of the XE‑1AJ into a hand-held controller, and even added a few features. In many ways, it is the ancestor of modern game controllers, pioneering many features that were only popularised years later. Its features included:

  • Ergonomic shape with grip handles.
  • Analog stick controlled with left thumb.
  • Analog throttle controlled with right thumb; could be rotated through 270° for horizontal or vertical movement in either direction.
  • Two shoulder buttons on each side.
  • Two face buttons on each side, plus Start and Select buttons.
  • Auto-fire support for the right shoulder buttons.
  • Modes to support Japanese home computers and Sega consoles.

The design of the Sega Saturn 3D Controller was directly influenced by the XE‑1AP – the similarity is quite striking. The Saturn 3D Controller was further developed into the Dreamcast controller, which in turn served as the inspiration for the Xbox controller. The Xbox controller was ridiculed for being excessively large, but the XE‑1AP is actually bigger. This is at least partly for practical reasons: it needs space for its 42-pin plastic DIP Fujitsu MB88513 microcontroller and the rest of the components that make it work. Electronics used to be a lot bigger.

It’s interesting how obscure the XE‑1AP has become. Perhaps poor marketing is partly responsible. It was supported by several Mega Drive, Mega‑CD and 32X games, but only mentioned in the Japanese versions of the manuals. The export versions of the manuals never mentioned the XE‑1AP, despite the export versions of the games supporting it in most cases. Micom Soft sold an adapter that allowed the XE‑1AJ or XE‑1AP to be used with the NEC PC Engine. Although five PC Engine games supported analog controls, this was never heavily promoted. Three of them don’t even give any indication when a supported analog controller is detected. The high price probably didn’t help either, but I can’t help wondering what could have happened if the XE‑1AP was promoted more heavily, leading to more adoption, and hence more games developed with support for it.

I recently added support for the XE‑1AP to MAME. In typical MAME fashion, the emulated XE‑1AP can be used with compatible games on several systems, including the Sega Mega Drive, NEC PC Engine, Sharp X68000 and FM Towns families. I’ve also written some basic instructions for using the controller with some of the games I tested.

Sharp provided assembly language source code for an X68000 driver for the XE‑1AJ and XE‑1AP. This may be the reason the X68000 has the best software support, with over a dozen games making use of analog controls. The Mega Drive and PC Engine games with XE‑1AP support use a similar algorithm to read data from the controller, but the games developed by CSK Research Institute for FM Towns use a different approach. Although this meant more effort getting the emulated controller to work across all the platforms with compatible software, it does mean we can infer more details about the controller’s behaviour.

You will need to manually assign controls to the XE‑1AP inputs in MAME to get a good experience, due to the number of controls and how different the layout is to common controllers. Here are some tips:

  • The XE‑1AP plugs straight into controller ports on the Mega Drive, X68000, and FM Towns.
  • For the PC Engine, you need to plug it in via the XHE‑3 PC joystick adapter. It’s the default peripheral for the XHE‑3, as it’s the most useful thing you can connect to it.

  • X68000 games expect the XE‑1AP to be connected to the first joystick port. Keep in mind that in MAME, the X68000 mouse counts as a “player”, so the XE‑1AP on the first joystick port will use player 2 controls by default.
  • Most Mega Drive games expect the XE‑1AP to be connected to the first controller port. Night Striker for Mega‑CD is an exception – you need to connect the XE‑1AP to the second controller port, and use a regular control pad connected to the first controller port to select analog mode.
  • FM Towns games require you to connect the XE‑1AP to the second controller port, and use a Towns Pad connected to the first controller port to navigate the menus and select analog controls. CSK Research Institute games require you to change the XE‑1AP’s Interface setting to MD in MAME’s Machine Configuration menu.
  • A lot of games only detect controllers on start, so if you switch the Mode from Analog to Digital or vice versa and controls stop working, you might need to restart the emulated software.

Assuming you have fairly standard settings, here are some example command lines for starting different games with the XE‑1AP:

% mame x68000 -joy1 xe1ap shangon
% mame megadriv -ctrl1 xe1ap smgp2
% mame fmtowns -ramsize 2M -pad1 townspad -pad2 xe1ap aburner3
% mame pce -ctrl pcjoy -cartridge scdsys -cdrom forgottnj

For what it’s worth, here’s my take on some of the games I tested:

After Burner II for X68000, Mega Drive and PC Engine
It’s definitely a lot easier to hit oncoming planes with your Vulcan guns with precise control of your flight attitude. It feels like a lot of the controller’s features go unused. Note that for the X68000 version, you need to press OPT.1 on the keyboard (in MAME, this is assigned to PrtScr by default) at the title screen to enable joystick controls. There’s nothing on the screen to tell you to do this, and no visual indication when joystick controls have been enabled.
Ayrton Senna’s Super Monaco GP 2 for Mega Drive
This one feels like it’s been thought out very well: the shoulder buttons are your paddle shifters (left to shift down, right to shift up), you control the accelerator and brakes with the stick using your left thumb, you rotate the throttle to be horizontal and use it to steer with your right thumb, and you press E1 or E2 with your left thumb to make a pit stop. You’ll need to change your input assignments in MAME to make this game playable. With an Xbox-style controller, you can assign the X axis of the right stick to the throttle to get close to the original setup. It feels much better with analog steering – staying on the track is a lot easier.
Thunder Blade for X68000
With analog controls, you get the best possible experience in this game. You can hover and land, just like you could in the arcade version. Assigning the throttle to the left trigger on an Xbox controller lets you fly with your left thumb and index finger, leaving your right thumb and index finger free for controlling weapons. If you assign the throttle to “Joy 1 LT -” (squeeze and release the trigger three times when assigning the input), releasing the trigger hovers and squeezing it flies forward. Assign the right trigger to B or B’ to fire the cannon, and and assign a face button to A or A’ to fire missiles.
Super Hang-On for X68000
Nothing surprising, just responsive analog controls. This game lends itself well to assigning the left and right triggers on an Xbox controller to the brakes and throttle – they even correspond to the positions of the brake lever and throttle twist grip on a motorbike. The correct assignment is “Joy 1 LT Joy 1 RT Reverse” (squeeze and release the left trigger once, then squeeze and release the right trigger four times when assigning the input). Remember not to assign the triggers to other buttons at the same time, or you won’t be able to select stages and music with the brake.
Taito Chase H.Q. for FM Towns
For me, analog steering took this game from being almost unplayable to great fun. If you prefer to use analog triggers or pedals for the accelerator and brakes, see the notes about Super Hang-On for how to assign the throttle. The turbo boost is button D, which makes perfect sense as the throttle thumb button on an XE‑1AJ, but doesn’t make quite as much sense as the lower left shoulder button on an XE‑1AP. If you’re using a thumb stick for the accelerator and brakes, it’s probably most natural to assign it to clicking the stick down, or maybe the (upper) shoulder button on the same hand as the stick you’re using. If you’re using triggers for the accelerator and brakes, using a face button for turbo boost might be a good choice. Remember to change the Interface back to Personal Computer in MAME’s Machine Configuration menu if you previously changed it to MD to play a CRI game.
After Burner III for FM Towns
This is a CRI game, so you need to change the Interface to MD in MAME’s Machine Configuration menu, but it’s worth it, because this is a highlight! It doesn’t even pretend to be accurate flight simulation, but you wanted accurate simulation, you wouldn’t be playing an After Burner game. You can roll all over the place, and it’s an awesome feeling to hold C or D, slam the throttle, and watch the After Burner Level gauge fill while you shake off a bogie on your six. It’s also got more variety than After Burner II, with stuff like ground attack levels where you plink tanks.
Operation Wolf for PC Engine
This game actually does tell you on the title screen when it’s recognised an analog controller. It gives you absolute position aiming using the stick. Definitely a lot faster and more accurate than moving the reticle around with a D-pad.
Forgotten Worlds for PC Engine Super CD-ROM²
Remember to insert the Super CD-ROM² System HuCard as well as the Forgotten Worlds CD. You’ll need to assign a key to the Run button on the joystick adapter, because the Super CD-ROM² System software won’t recognise the Start button on the XE‑1AP, and you’ll need to press it to start the software on the CD. This is the other PC Engine game that lets you know it’s detected an analog controller on the title screen. This one was a really pleasant surprise for me. The analog movement and aiming controls felt beautiful. It probably helps that it has pretty graphics and a lovely CD soundtrack, too. Load times are noticeable, but not long enough to be irritating.
Out Run for PC Engine
As with other racing games, it’s easier to stay on the track with analog steering. Using the analog throttle for the accelerator while using buttons for the brakes was an interesting design choice – you can press any of the A/B/C/D/E buttons to brake. If you decide to reassign the throttle to an analog trigger or pedal on an Xinput controller, you’ll need to assign it to the negative half of the axis. For the right trigger on an Xbox controller, squeeze and release the trigger until it shows “Joy 1 RT -” (three times) when assigning the input, and make sure you don’t have the same trigger assigned to any of the buttons.
Thunder Blade for PC Engine
This one’s a disappointment – it’s no different to using a digital game pad. Move the stick a little and you don’t move; move it a bit further, hit the threshold, and you’re moving at full speed. Also, the weapon controls are reversed compared to the X68000 version, with A firing the cannon and B firing missiles. I guess it’s nice of them to make it recognise the controller in analog mode, but it would have been even nicer if they’d made better use of it.
Musha Aleste for Mega Drive
This game is infamous for being unplayable with the XE‑1AP in analog mode. The stick controls your absolute position on the screen, which really doesn’t work for a shooter like this – it’s far too twitchy. Surprisingly, the game actually becomes playable if you assign mouse axes to control the stick, feeling similar to PC shooters like Raptor: Call of the Shadows.

XE‑1AP support isn’t the only controller-related improvement coming in MAME 0.251 – other things that made it in this month include:

  • Pluggable controllers for the Sega Mega Drive and SG‑1000 families.
  • Pluggable controllers for the NEC PC‑6001, PC‑8801 and PC‑88VA families.
  • Pluggable controllers for the Sharp MZ‑800, MZ-2500 and X68000 families.
  • Sega Mouse (2-button) and Mega Mouse (4-button) support for the Mega Drive family.
  • Sega Tap/4-Player Adaptor/Team Player support for the Mega Drive family.
  • Support for an ATmega-based paddle controller that works with export versions of the Sega Master System.
  • Mouse support for the PC Engine family.
  • Support for the Konami Hyper Shot controller (although it’s somewhat pointless in emulation).
]]>
https://rants.vastheman.com/2022/12/24/kabutogani/feed/ 0
Going Old-School https://rants.vastheman.com/2017/07/08/oldschool/ https://rants.vastheman.com/2017/07/08/oldschool/#comments Fri, 07 Jul 2017 18:27:31 +0000 https://rants.vastheman.com/?p=290 For lulz, I decided to rewrite MAME’s Intel 4004 CPU core and add support for most 4040 features. The new CPU core operates at the bus cycle level, and exposes most useful signals. It also uses lots of address spaces for all the different kinds of memory and I/O it supports (thanks OG). Some CPU core bugs were fixed along the way – notably intra-page jumps on page boundaries were broken.

One nice benefit we get from this is being able to hook up the I/O for the Bally/Nutting solid-state Flicker pinball prototype (supposedly the first microprocessor-controlled pinball machine) how the hardware actually worked. I also hooked up the playfield lamp matrix as outputs and the operator adjustments as machine configuration while I was at it. We need a proper thermal model of a lamp with PWM dimming support before that part can be declared perfect. (It previously used a hack, pulling the low bits of RC out of the CPU using the state interface. This worked due to a quirk of the game program, and there was no way to implement it properly without the 4004 CM-RAM signals being exposed.)

Possibly more interestingly, we can now emulate the Intel INTELLEC® 4/MOD 40 development system. There seem to be very few surviving examples of this system, but fortunately we’ve got monitor PROM dumps, and there’s some information floating around the web. It has interesting debugging features on the front panel. There’s a scanned manual, but the schematics are very poor quality. However, with some idea of how it works, it’s possible to work out what all the chips are supposed to be. That’s the fun part. Turning it into MAME code isn’t as much fun, but it’s doable.

The front panel looks like this:

That requires clickable artwork for the switches and outputs for the LEDs to get a usable experience (writing the MAME XML layout really isn’t fun). There’s a simple monitor in PROM, designed to be used with an ASCII teleprinter with paper tape reader and punch (e.g. a Teletype Model 33 ASR). MAME’s RS-232 video terminal will have to do as a substitute for that.

If you get bleeding edge MAME (i.e. built from source no more than a day or so old), you can try it out in emulation. Did you ever wonder how developers may have debugged 4004 code in the mid to late ’70s? Well even if you didn’t, now you can find out.

The front panel looks like this in emulation (without the explanatory labels), and by default MAME shows the video terminal below this:

All those LEDs are functional, and all those switches are clickable and visually reflect their current state.

So how would you actually use it in practice? That’s where this brief instruction manual on the MAMEdev wiki comes in. It’s complete with examples of how some of the monitor commands and front panel debugging features can be used. It’s marked NOT_WORKING in MAME for now because you need to manually set up the terminal the first time you use it, and I haven’t finished implementing the universal slots and storage cards. But you can do all the things described on that page.

Does anyone care? Probably not. Will anyone actually even try this system out in MAME? Probably not (apart from Robbbbbbbert). But this is another example of something that you can only do in MAME, and how completely unrelated systems can both benefit from emulating the same chip properly. It also gets rid of one previously unavoidable hack, and gets us one step closer to feature parity with EmuAllSystems.

]]>
https://rants.vastheman.com/2017/07/08/oldschool/feed/ 1
Attacking the Weak https://rants.vastheman.com/2017/02/13/attacking/ https://rants.vastheman.com/2017/02/13/attacking/#comments Sun, 12 Feb 2017 23:37:19 +0000 https://rants.vastheman.com/?p=285 ShouTime dumped the incredibly rare game Omega (Nihon System). It’s a ball-and-paddle game running on similar hardware to Sega’s Gigas. These games use an NEC MC-8123 CPU module containing a Z80 core, decryption circuitry, and an 8 KiB encryption key in battery-backed RAM. When fetching a byte from ROM or RAM, the CPU chooses a byte from the encryption key based on twelve of the address bits and whether it’s an M1 (opcode fetch) cycle or not. This byte from the encryption key controls what permutation (if any) is applied to the byte the CPU fetches. This encryption scheme could have been brutal, requiring extensive analysis of a working CPU module to crack, if it weren’t for a fatal flaw: Sega used a simple linear congruential generator algorithm to create 8 KiB keys from 24-bit seeds. That means there are less than seventeen million encryption keys to test. Seventeen million might sound like a lot, but it’s far less than the total possible number of keys, and definitely small enough to apply a known plaintext attack in a reasonable amount of time.

So how do we go about attacking it? First we have to make an assumption about what the game program is going to be doing. Given that the hardware looks pretty similar to Gigas and Free Kick, I guessed that one of the first things the program would do is write a zero somewhere to disable the non-maskable interrupt generator disable maskable interrupts. So I wrote a program to find candidate seeds (no, I won’t show you the source code for this program – it’s embarrassingly ugly and hacky, not something I could ever be proud of):

  • Start with first possible 24-bit seed value
  • Generate 8 KiB key using algorithm known to be used by Sega
  • Decrypt first few bytes of program ROM using this key
  • If it looks like Z80 code to store zero somewhere and disable interrupts, log the seed
  • Repeat for next possible seed value until we run out of values to try

This ran in just a few minutes on an i7 notebook, and narrowed down the millions of possible seed values to just five candidates: 36DF3D, 6F45E0, 7909D0, 861226, and BE78C9 (in hexadecimal notation). Now I could have tried these in order, but it looked like Sega had made another misstep: besides using a predictable algorithm to generate the key, they also used a predictable seed value to feed this algorithm. The candidate seeds value 861226 looks like a date in year-month-day format. It turns out this seed generates the correct key to decrypt the game program, so I guess we know what someone at Sega was doing the day after Christmas in 1986.

Brian Troha hooked up the peripheral emulation, and the game will be playable in MAME 0.183 (due for release on 22 February). Colours aren’t quite right as we don’t have dumps of the palette PROMs yet, but we expect to resolve this in a future release. Thanks to ShouTime and everyone else involved in preserving this very rare piece of arcade history.

]]>
https://rants.vastheman.com/2017/02/13/attacking/feed/ 3
My PAL with the LASERs https://rants.vastheman.com/2015/12/15/lasers/ https://rants.vastheman.com/2015/12/15/lasers/#respond Tue, 15 Dec 2015 12:54:05 +0000 https://rants.vastheman.com/?p=250 Back in the distant past, MAME started cataloguing programmable logic devices (PLDs) in addition to ROMs. This was met with considerable hostility from certain segments of the community, as it was seen as forcing them to obtain files they saw as unnecessary for emulation in order to run their precious games. However PLDs are programmable devices, and it’s important to preserve them. So far though, the PLD dumps have mainly been used by PCB owners looking to repair their games. The haven’t been used by MAME for emulation. However, PLDs are key to the operation of many arcade games, performing functions like address decoding and video mixing.

One such arcade board is Zaccaria’s Laser Battle, also released under license by Midway as Lazarian. This board uses complex video circuitry that was poorly understood. It includes:

  • TTL logic for generating two symmetrical area effects, or one area effect and one point effect
  • TTL logic for for generating an 8-colour background tilemap
  • Three Signetics S2636 Programmable Video Interfaces (PVIs), drawing four 4×4 monochrome sprites each
  • TTL logic for generating a single 32×32 4-colour sprite
  • A Signetics 82S101 16×48×8 Programmable Logic Array (PLA) for mixing the video layers and mapping colours

On top of this, they decided it was a good idea to use some clever logic to divide the master clock by four when feeding the Signetics S2621 Universal Sync Generator (USG) that generates video timings, but to divide it by three to generate the pixel clock feeding the rest of the video hardware. This lets them get one third more horizontal resolution than the Signetics chips are designed to work with. They need additional logic to line up the pixel clock with the end of horizontal blanking, because the number of pixels in a line isn’t divisible by three, and some more logic for delaying the start of the visible portion of each frame and cutting it off early because they wanted less vertical resolution than the Signetics chips are designed for. It uses an GRBGRBGR colour scheme where the most significant bits are are in the middle of the byte and the missing least significant blue bit effectively always, so it can’t produce black, only a dark blue Was this design effort worth it? I guess they must’ve made some money off the Midway license at least.

Anyway, this game has never worked properly in MAME. It’s always been missing the area and point effects, the colours have always been completely wrong, and the mixing between layers hasn’t properly either. And that’s done inside the PLA. The PLA has 48 internal logic variables, each of which can be programmed to recognise an arbitrary combination of levels on the 16 input line. Each of the internal variables can drive any combination of the eight output lines. The outputs can be configured to be inverting or non-inverting.

In theory this sounds like a job for a ROM, so why use a PLA instead? Well a ROM with 16 input bits and eight output bits would need 64kB of space. Such a ROM would likely have been prohibitively expensive when this game was produced. I mean, its program ROMs are only 2kB each, so there’s no way they’d be sourcing a ROM 32 times that size just for video mixing. The PLA maps the same number of inputs to the same number of outputs with just 1,928 bit of storage, or a little less than one of the program ROMs. It can’t produce absolutely any arbitrary input to output mapping, but it’s more than enough for this application. In fact, it turns out they didn’t even need to use all the space in the PLA.

Read the rest of the post if you want to know more about the process of decoding the PLA bitstream and examining its contents.

The hookup

By examining the schematics, we can see that the PLA’s inputs are hooked up like this:

Bit Name Description
0 NAV0 Sprite bit 0
1 NAV1 Sprite bit 1
2 CLR0 Sprite colour bit 0
3 CLR1 Sprite colour bit 0
4 LUM Sprite luminance (brightness)
5 C1* Combined PVI colour bit 1 (red) gated with blanking (active low)
6 C2* Combined PVI colour bit 2 (green) gated with blanking (active low)
7 C3* Combined PVI colour bit 3 (blue) gated with blanking (active low)
8 BKR Background tilemap red
9 BKG Background tilemap green
10 BKB Background tilemap blue
11 SHELL Shell point
12 EFF1 Effect 1 area
13 EFF2 Effect 2 area
14 COLEFF0 Area effect colour bit 0
15 COLEFF1 Area effect colour bit 1

CLR0, CLR1, LUM, COLEFF1 and COLEFF2 are relatively static bits that the game program sets by writing to I/O ports. The rest of the bits are generated dynamically based on video register and RAM contents.

Streams of bits

The PLA bitstream consists of 48 sets of 40 bits for each of the internal logic variables, followed by a final eight bits specifying which of the outputs should be active low. Each internal variable has two bits controlling how it’s affected by each of the 16 inputs (32 bits total), followed by eight bits specifying which outputs it shouldn’t activate (32+8 makes for a total of 40). If a pair of input bits are both zero (00), it’s impossible for that logic variable to be activated. This is an easy way to spot unused variables, and it’s the default state in an unprogrammed chip. If only the first bit in a pair is set (10), the corresponding input line must be high in order for the variable to be activated. If only the second of the bits is set (01), the corresponding input line must be low in order for the variable to be activated. If both bits in a pair are set (11), the input may be activated irrespective of the state of the corresponding input line (often called a “don’t care” condition).

Finally, MAME expects the bitstream in a file containing a 32-bit integer specifying the total number bits, followed by the bits themselves, packed into bytes least-significant bit first. Armed with this knowledge, we can write a some code that transforms converts the bitstream to an intermediate representation and displays it as C code:

UINT8 const *bitstream = memregion("plds")->base() + 4;
UINT32 products[48];
UINT8 sums[48];
for (unsigned term = 0; 48 > term; term++)
{
    products[term] = 0;
    for (unsigned byte = 0; 4 > byte; byte++)
    {
        UINT8 bits = *bitstream++;
        for (unsigned bit = 0; 4 > bit; bit++, bits >>= 2)
        {
            products[term] >>= 1;
            if (bits & 0x01) products[term] |= 0x80000000;
            if (bits & 0x02) products[term] |= 0x00008000;
        }
    }
    sums[term] = ~*bitstream++;
    if (PLA_DEBUG)
    {
        UINT32 const sensitive = ((products[term] >> 16) ^ products[term]) & 0x0000ffff;
        UINT32 const required = ~products[term] & sensitive & 0x0000ffff;
        UINT32 const inactive = ~((products[term] >> 16) | products[term]) & 0x0000ffff;
        printf("if (!0x%04x && ((x & 0x%04x) == 0x%04x)) y |= %02x; /* %u */\n", inactive, sensitive, required, sums[term]);
    }
}
UINT8 const mask = *bitstream;
if (PLA_DEBUG) printf("y ^= %02x;\n", mask);

Equations

When we feed this the PLA bitstream dumped from a Lazarian board, we get the following output (blank lines added for readability:

if (!0x0000 && ((x & 0x001f) == 0x0001)) y |= 01; /* 0 */
if (!0x0000 && ((x & 0x001f) == 0x0002)) y |= 03; /* 1 */
if (!0x0000 && ((x & 0x001f) == 0x0003)) y |= 04; /* 2 */
if (!0x0000 && ((x & 0x001f) == 0x0011)) y |= 08; /* 3 */
if (!0x0000 && ((x & 0x001f) == 0x0012)) y |= 18; /* 4 */
if (!0x0000 && ((x & 0x001f) == 0x0013)) y |= 20; /* 5 */
if (!0x0000 && ((x & 0x001f) == 0x0005)) y |= 07; /* 6 */
if (!0x0000 && ((x & 0x001f) == 0x0006)) y |= 03; /* 7 */
if (!0x0000 && ((x & 0x001f) == 0x0007)) y |= 04; /* 8 */
if (!0x0000 && ((x & 0x001f) == 0x0015)) y |= 38; /* 9 */
if (!0x0000 && ((x & 0x001f) == 0x0016)) y |= 18; /* 10 */
if (!0x0000 && ((x & 0x001f) == 0x0017)) y |= 20; /* 11 */
if (!0x0000 && ((x & 0x001f) == 0x0009)) y |= 05; /* 12 */
if (!0x0000 && ((x & 0x001f) == 0x000a)) y |= 04; /* 13 */
if (!0x0000 && ((x & 0x001f) == 0x000b)) y |= 06; /* 14 */
if (!0x0000 && ((x & 0x001f) == 0x0019)) y |= 28; /* 15 */
if (!0x0000 && ((x & 0x001f) == 0x001a)) y |= 20; /* 16 */
if (!0x0000 && ((x & 0x001f) == 0x001b)) y |= 30; /* 17 */
if (!0x0000 && ((x & 0x001f) == 0x000d)) y |= 07; /* 18 */
if (!0x0000 && ((x & 0x001f) == 0x000e)) y |= 04; /* 19 */
if (!0x0000 && ((x & 0x001f) == 0x000f)) y |= 04; /* 20 */
if (!0x0000 && ((x & 0x001f) == 0x001d)) y |= 38; /* 21 */
if (!0x0000 && ((x & 0x001f) == 0x001e)) y |= 20; /* 22 */
if (!0x0000 && ((x & 0x001f) == 0x001f)) y |= 20; /* 23 */

if (!0x0000 && ((x & 0x0023) == 0x0000)) y |= 01; /* 24 */
if (!0x0000 && ((x & 0x0043) == 0x0000)) y |= 02; /* 25 */
if (!0x0000 && ((x & 0x0083) == 0x0000)) y |= 04; /* 26 */
if (!0x0000 && ((x & 0x29e3) == 0x01e0)) y |= 01; /* 27 */
if (!0x0000 && ((x & 0x2ae3) == 0x02e0)) y |= 02; /* 28 */
if (!0x0000 && ((x & 0x2ce3) == 0x04e0)) y |= 04; /* 29 */

if (!0x0000 && ((x & 0x08e3) == 0x08e0)) y |= 38; /* 30 */
if (!0x0000 && ((x & 0xffe3) == 0x10e0)) y |= 84; /* 31 */
if (!0x0000 && ((x & 0xffe3) == 0x50e0)) y |= 84; /* 32 */

if (!0x0000 && ((x & 0x0000) == 0x0000)) y |= 00; /* 33 */

if (!0x0000 && ((x & 0xffe3) == 0xd0e0)) y |= 04; /* 34 */
if (!0x0000 && ((x & 0xe0e3) == 0x20e0)) y |= 80; /* 35 */
if (!0x0000 && ((x & 0xe0e3) == 0x60e0)) y |= 40; /* 36 */
if (!0x0000 && ((x & 0xe0e3) == 0xa0e0)) y |= c0; /* 37 */
if (!0x0000 && ((x & 0xe0e3) == 0xe0e0)) y |= c0; /* 38 */
if (!0x0000 && ((x & 0xffe3) == 0x90e0)) y |= 40; /* 39 */

if (!0xffff && ((x & 0x0000) == 0x0000)) y |= ff; /* 40 */
if (!0xffff && ((x & 0x0000) == 0x0000)) y |= ff; /* 41 */
if (!0xffff && ((x & 0x0000) == 0x0000)) y |= ff; /* 42 */
if (!0xffff && ((x & 0x0000) == 0x0000)) y |= ff; /* 43 */
if (!0xffff && ((x & 0x0000) == 0x0000)) y |= ff; /* 44 */
if (!0xffff && ((x & 0x0000) == 0x0000)) y |= ff; /* 45 */
if (!0xffff && ((x & 0x0000) == 0x0000)) y |= ff; /* 46 */
if (!0xffff && ((x & 0x0000) == 0x0000)) y |= ff; /* 47 */

y ^= 00;

Fortunately this PLA seems to have been programmed manually and not using an automatic logic minimiser. Can you spot the patterns yet? The last eight terms (40–47) have been left in their virgin, unprogrammed states and hence can’t affect the output. Term 33 has been programmed to have no effect on the output, so they’ve used 81.25% of the chip. They could’ve gotten clever and added more logic if they really wanted to, but I guess with everything else they did on the board they were out of tricks by this point. But anyway, on to the equations.

Sprite mapping

The first 24 terms (0–23) only depend on the sprite-related bits, so it’s a good bet they control sprite colours. Let’s make a table of the mappings these terms produce. For convenience we’ll treat NAV0/NAV1 and CLR0/CLR1 as two-bit numbers:

NAV 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
CLR 0 0 0 0 0 0 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3
LUM 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1
01 03 04 08 18 20 07 03 04 38 18 20 05 04 06 28 20 30 07 04 04 38 20 20

Now the pattern should be really clear. Sprite pixel value 0 produce no output (making it dark or transparent), while the other three values are each mapped to colour depending on the value of CLR. Setting LUM shifts the colour left by 3 bits into the most significant bits of the colour output. This can be summarised in a table with NAV in rows and CLR in columns:

0 1 2 3
0 00/00 00/00 00/00 00/00
1 01/08 07/38 05/28 07/38
2 03/18 03/18 04/20 04/20
3 04/20 04/20 06/30 04/20

Or we could even use human-readable colour names since we know the output format is GRBGRBGR (there’s a distinct lack of green in this palette):

0 1 2 3
0 dark dark dark dark
1 red white magenta white
2 yellow yellow blue blue
3 blue blue cyan blue

So they used half the PLA to handle sprite colour mapping.

Backgrounds and PVIs

The next set of six terms look straightforward enough, let’s make a table and see what they produce (once again we’re treating NAV0/NAV1 as a two-bit number):

NAV 0 0 0 0 0 0
C1* 0 1 1 1
C2* 0 1 1 1
C3* 0 1 1 1
BKR 1
BKG 1
BKB 1
SHELL 0 0 0
EFF2 0 0 0
01 02 04 01 02 04

This shows that the PVIs’ outputs are mapped directly onto the low red, green and blue bits of the output (not actually the LSBs, they’re at the top of the output byte). The TTL-generated sprite has priority over both the PVI outputs and the background tilemap; additionally, the PVIs, the shot point, and area effect 2 also have priority over the background tilemap (since the OBJ/SCR lines from the PVIs are not considered, the PVIs don’t take priority with black object pixels, only with coloured pixels).

Effects

The rest of the terms relate to effects. We can look at them all at once (NAV and COLEFF are two-bit numbers):

NAV 0 0 0 0 0 0 0 0 0
C1* 1 1 1 1 1 1 1 1 1
C2* 1 1 1 1 1 1 1 1 1
C3* 1 1 1 1 1 1 1 1 1
BKR 0 0 0 0
BKG 0 0 0 0
BKB 0 0 0 0
SHELL 1 0 0 0 0
EFF1 1 1 1 1
EFF2 0 0 0 1 1 1 1 0
COLEFF 0 1 3 0 1 2 3 2
38 84 84 04 80 40 c0 c0 40

So what does this tell us? Lots of things! You might notice that the second and third columns can easily be reduced to a single term, and so could the seventh and eighth columns, clearly an oversight but not important since there’s space to spare in the PLA. More seriously, we can work out priorities from the rows:

  • Sprite and PVI output always have priority over these effects
  • Background, shell and effect 2 have priority over effect 1
  • Remember that shell and effect 2 are mutually exclusive, so there’s no priority between them

Now we can look at the output each effect actually produces:

  • The first column says the shell sets the MSBs of all three colours, making it light grey or “dark white”.
  • Effect 1 sets the background colour (depending on COLEFF) to medium blue (3), dark magenta (2), or just on the cyan side of medium blue (1 or 0).
  • Effect 2 sets the background colour (depending on COLEFF) to dark cyan (0), dark magenta (1), or dark grey (2 or 3).

Emulation

So how do we get this information into MAME? Well we could take what we’ve learned and write C++ code to draw the graphics in the appropriate sequence, but that would have several disadvantages. Firstly it’s not how the real machine works — the real machine works by composoing a 16-bit value and feeding it through the PLA to get the output colour. Secondly, implementing it in C++ wouldn’t allow someone to try a different PLA dump to see what effects is has on gameplay. Thirdly, someone couldn’t develop their own PLA program to drop into MAME to play with for homebrew purposes. Instead, we use some more code to turn our intermediate representation of the PLA terms into a 64kB mapping table:

for (UINT32 inp = 0x0000; 0xffff >= inp; inp++)
{
    m_mixing_table[inp] = 0;
    for (unsigned term = 0; 48 > term; term++)
    {
        if (!~(inp | (~inp << 16) | products[term]))
            m_mixing_table[inp] |= sums[term];
    }
    m_mixing_table[inp] ^= mask;
}

Then we render scanlines into a 16-bit bitmap and pass it through this table to convert it to colours for MAME’s video output. (Yes, it’s possible to run the PLA equations on the bitmap data directly from the compact intermediate representation. However it’s slower as it involves several logical operations, and 64kB is a small amount of memory these days for a cached lookup table. However, CPUs are getting pretty fast, and cache miss latency keeps getting worse, so perhaps sticking with the intermediate form wouldn’t be such a bad idea.)

]]>
https://rants.vastheman.com/2015/12/15/lasers/feed/ 0