How to use a Sega Master System Light Phaser as the pink Konami Justifier

August 15, 2021

This is a simple hack that I put together from several places on the internet to get right. I don’t know that anyone’s done exactly this before, so I figured I’d share it here. The Pink player 2 Konami Justifier is a rare peripheral that has a 6p6c (6 pin phone cord) cable coming out of it, and it connects to either the Sega Genesis OR the Super Nintendo blue player 1 Justifier. This pink gun was only available directly from Konami by mail (yes this is me parroting back the same line in every article about this gun, I have no first hand knowledge of it AT ALL) and as such very few people went through the effort.

why is mine tipped orange? I dunno, ESRB panic of the early ’90s maybe. Orange tip or not, cops would still use it an an excuse…

Other people have gone through different levels of effort to get around the expense of the pink gun including modding a blue player 1 gun to work as the player 2 gun and replacing BOTH guns with a small circuit and two Sega Master System Phasers. I want to split the difference. Say you have a blue gun already but want player two. You can get another player 1 gun, but then the guns would look identical and it would be hard to tell player 1 from player 2. In addition to that, the blue Justifiers are not trivially inexpensive either. The resources linked above were very helpful for making sure all this worked the first time, go check them out.

The pinout for the 6p6c connector is fairly simple and I verified it with the pink gun I got as a reference model. If you hold the end of the cable looking at the gold contacts (and the cable pointing down) the pinout from left to right is:

  1. Trigger
  2. Start
  3. N/C
  4. Gnd
  5. Light Sensor
  6. Power

You can verify that your pinout is not rolled over by plugging in your 6p6c cable to the blue gun and measuring for power between 4 and 6. If you do not see power try 1 and 3, if there’s power there then you have the pinout backwards.

From here it’s a matter of opening up the Sega Light Phaser. The screws are all on the same side of the gun and there’s one hiding under the label (I destroyed mine trying to gently peel it off). Once inside you can remove the PCB and gently remove the microswitch from the pegs it is mounted to. Desolder the cable and save it for future projects. The board (for me) was helpfully labeled with Vcc (Power), Ground, TL, and TH. I looked at the traces on the board and saw the trigger switch was hooked to ground and TH, so that mystery is solved. The light sensor wire goes to TL then.

For the start button I carefully melted/carved away the bump in the back of the Light Phaser so I could install a large tact switch (I think 12mmx12mmx7mm) from a variety pack I had on hand. I epoxied the tact switch in place because I was concerned hot glue would eventually fall off over time, but hot glue cures faster so I hot glued over the drying epoxy so I could use it faster.

I was hoping this would work, and it did! What I didn’t expect was to find that the Phaser performed much better than the Justifier. There’s plenty of times I would shoot at the screen and the Justifier wouldn’t register at all, but the Phaser seems rock solid. I am tempted to replace the light sensor board in the Justifiers I have with ones from more Phasers… but not right now. I did find out that the light sensor in the Justifiers is the IS485E so I ordered some of those from aliexpress but they won’t get installed for a while because of how shipping works out.

As a final note: if you want to mod the player 1 gun into a player 2 gun, just but a double ended 6p6c cable and a board mount 6p6c connector, the PCB is the same and that’s what Konami did!

Neo Geo compatibility for my portable arcade test rig

April 16, 2021

I figured I’d go over some mods done to make my arcade test rig Neo Geo compatible. The first thing to do was make a JAMMA fingerboard adapter. I have a 2-slot motherboard so it’s not the vanilla JAMMA pinout, but it’s pretty close. Take a look:

The main changes I made are these:

moved test button to the right location

moved service button to the right location

tied right and left speaker together through 1uF caps and connected them to speaker positive

tied case ground to speaker negative

wired select up and down to player 1 buttons E and F

wired coin 3 and 4 to player 2 buttons E and F

One quirk I found is that I couldn’t make the mono audio work well, perhaps it’s my amplifier behind a matching transformer, perhaps my board is messed up in that respect, that’s why I had to mix the stereo audio to get it to work. The last time I talked about this project I had also not used the buttons D, E, and F for either player. I have installed those wires now and built an adapter to make those work. I decided to keep the theme running with the genesis controllers, but now I’m using unmodified ones and an arduino to make them work.

I have an arduino pro mini (running off the same 5v power supply as the rest of the system) connected to two MCP23018 I/O expanders. Plenty of people use the MCP23017 for things, the 23018 just has open-drain outputs. That means that when the outputs are live they ground the pins instead of pulling them high, and when deactivated they float rather than pulling them low. This simulates button presses for a system that all the buttons have a common ground pin like the Neo Geo. The code just glues two libraries together, one for reading genesis controllers found here and one for talking to I/O expanders found here. I may re-shuffle the button positions to more closely simulate the button placement on a Neo Geo, but this is more meant as a general purpose expansion to allow for 6-button JAMMA games to be used in this tester, not just the Neo Geo.

Another new feature (along with my 3d-printed fan grills) is video out. Having the ability to throw this up on a bigger monitor is nice, so I added a tap for video out. I don’t know how having both monitors connected will affect the impedance of the video line, but it probably won’t be too bad.

Synthesizing a neo-geo game from scratch (ish)

April 13, 2021

I recently decided to take on a project building a neo-geo MVS cart of a rather rare game, Twinkle Star Sprites. It is possible to buy this game for cheaper on the PS2 or Dreamcast, but why hack one of those into an arcade cabinet when you already have a Neo-Geo? When I started this project the prices on MVS carts on ebay started at around $1200 and went up from there, so it seems like a worthy effort. I’m gonna buzz through the easy stuff and focus on the things I had to do that you cannot easily figure out by googling or reading the silkscreen.

First step is acquiring parts. I needed a cartrige shell, they are reproduced in whatever color you want at RetroModding. I did find that the PCBs fit a bit tight so I shaved some of the plastic tabs with a hobby knife so it was snug but not bowing out. The PCBs I determined by looking at MVS Scans and then getting the bare circuit boards from ebay. You can also compare what games used each PCB and buy a game with compatible boards and pull the ROMs yourself. I did not have enough confidence in this project to try buying cheaper games with boards that MIGHT be compatible and start hacking on them, so to give myself the best chance at this working I got the PROGBK1 and the CHA256B boards. You also need to be sure to get the right chips on your PROGBK1 board, I bought the fully populated one and pulled the bottom chip off just to be sure I would have it if I needed it as I didn’t have the complete plan for how this would work at the time I bought these parts.

The ROMs are a whole other thing, looking at the PCB silkscreen I got this:

P1, V1, V2, TC5316200

C1-C4, KM23C32000

S1, TC531000

M1, TC531001

Looking these chips up and googling for equivalents or people replacing ROMs in neo geo carts I came up with this table:

big roms
small roms

So, it looks like I just need to get some of these UV erasable EPROMs to replace the mask ROMs in this game, that won’t be that hard. Programming them, however, is a bit of a challenge. I can program the 27C101 with my TL866A, but none of the others. The 27C301 is no big deal, I just made a passive adapter swapping /OE and A16 and pretend it’s a ‘101 (this difference is between standard JEDEC pinouts and non-JEDEC and I don’t know why they laid this board out with both, it seems really dumb). The bigger 16-bit ROMs will take some more doing, and I ended up buying one of these: E2R16v2.1 – 27C400/800/160/322 programming adapter for Minipro TL866. There are a bunch out there, but GGlabs does lots of other cool stuff and I like them.

Now that I have established that I have all the parts, how do I set up the boards I just bought? The easy answer is to copy the jumper positions on MVS Scans, but that flies in the face (a little) of the documentation on the Neo Geo Development wiki. I read up on these boards and added some stuff, cleaned up some red links and made some page redirects so the information I needed was all nice and organized on the wiki. There’s still plenty to be done, but largely what I needed that wasn’t already up there has been put up there. There is still an outstanding issue for me about ROM size nomenclature, however. Some of these roms are 16 bits wide, so do you talk in terms of 16bit words? or perhaps if the rom is being used in byte mode and accessed in 8bit bytes you use that? With all this confusion do you just refer to them in bits? Sometimes I find it unclear since there’s no units and that can be frustrating.

The first questionable thing is on the CHA256B board (which is supposedly just a slightly different layout of the CHA256 board). The scan of the OEM board has jumpers J2, J3, J9, J10, J11, and J16 set. According to the wiki the combination of J9/J10 means the board is set for 4MiB (the largest) C-ROMs, J2/J3 means it uses a 128KiB M-ROM (also, the bigger of the two available configurations), but J11/J16 seems to be an invalid configuration. There isn’t a fully reverse engineered schematic of this section of the board so I can’t check it but here’s my thoughts. If the ROMs in C1/C2 are less than 4MiB then you set J15, if they are 4MiB you set J11. If the ROMs in C3/C4 are less than 4MiB then you set J16, if they are 4MiB you set J12, and so on… After checking the MAME source I can see that C3/C4 are indeed smaller than C1/C2 which makes sense why the jumper would be set differently. This game does not use C5-C8 so that also makes sense why those jumpers are just not present.

Programming these chips was as easy as finding the ROMs on the internet (this was even easier than it usually is, they were hosted somewhere halfway-legitimate) and burning them. S1 and M1 burned ok, after using my adapter to program S1. The S1 spot on the board helpfully has power and ground run so that when you put in the 32pin EPROM the /OE line is grounded and power goes to the Vcc pin, even though with the mask ROM those pins on the board aren’t connected to anything. I programmed the C-ROMs using 27C322s even for C3/C4 and just put more copies of the code in the empty space so it would simulate the mirroring of code into upper memory that would happen if smaller ROMs were installed there. To use the adapter I bought you need to split the ROMs into chunks, a friend wrote some scripts to make that easy.

The next board was easier in some ways and harder in others. The PROGBK1 has the chip enable schematics traced out, but I had some shenanigans to perform to make this one work right. Looking at the PCB on MVS Scans I moved all the jumpers and removed the un-needed bank switching chip. Now I want to understand what is actually going on so I can know which chips to install here.

stock Twinkle Star Sprites configuration

Here is the stock configuration, it uses half of the 74ls139 to decode the A21 and A22 lines to chip select lines for each of the 4 chip positions. What is of particular interest to me is the stuff going on with JV11 and JV8. JV11 sends A20 to a pin on V1, JV8 grounds that pin on V2. I know V2 is half the size of V2 so this makes some sense, but look at what the pin is labeled: A20 or /BYTE. Now when I was looking for equivalent chips I found that the 27C160 has what is known as byte mode where the contents of the chip can be read out in 8bit bytes instead of 16bit words. One of the unused data lines is used as another address line and that’s how the chip can be 1Mx16 OR 2Mx8. Looking closer at the traces on the board I can see that NO V-ROM uses all 16 data lines, so they are all operating in BYTE mode all the time. This is making me sweat a little because the 27C322 that I THOUGHT could be an equivalent for the larger rom here does not have that mode. Looking at the V-ROM page on the wiki reveals that little oversight of mine:

The TC5332204 mask ROMs have a similar pinout to the 27C322 EPROMs. The only difference is that they’re set to byte mode only: half of the data output pins are unused. Pin 30 (Q15) is used as the address lowest bit.

some of those traces don’t go anywhere…

Oops. It looks like there are no easy solutions for how to get a TC5332204 equivalent EPROM. But where did that come from, I thought these were TC5316200 ROMs? Turns out the silkscreen only covers one possible ROM that can fill that spot, MAME has a listing of the actual ROM types in the comments (differences from above in bold):

P1, TC5316200

V1, TC5332204

V2, TC5316200

C1, C2, TC5332205

C3, C4, TC538200

S1, TC531000

M1, TC531001

So how do I make this work with parts I can get? One solution I saw involved a couple 74LS245 gates to ‘make’ a byte mode for the 27C322. That seemed like a lot of work and not much fun. My next thought was to do some of my own address decoding to split the memory space of V1 into two chips. Turns out that is exactly what this board is FOR!

my new configuration

I decided that because A22 is unused (the top half of the available memory that is being decoded) then I would just remap the available 4 chips into the range of memory I care about. JV7, JV8, and JV9 now set the V1, V2, and V3 chip locations to 27C160s in BYTE mode. JV3 and JV4 now decode the memory range into chunks half the size they were before, which lets me use chips half the size (but twice as many) and all is well.

There are some differences between this memory map and the original. In the original case since A20 was not wired to v2 at all that means if you access the memory region from 0x300000 through 0x3FFFFF you get the same data that was from 0x200000 to 0x2FFFFF, what MAME calls a mirror. I checked and MAME doesn’t implement the mirror even though the original game has it, that means to me that the game will work without it. Also when accessing memory above 0x400000 you get noise (probably all 0 actually) because there are physically no chips at those locations, so when the 74ls139 enables that socket, nothing happens. The difference now is that the way I rewired it reading from 0x300000 through 0x3FFFFF gives you nothing instead of that mirror and reading from 0x400000 to 0x7FFFFF gives you a mirror of what’s on 0x000000 to 0x3FFFFF. This is because now A22 is hooked to nothing so the decoder just happily routes those requests to V1 and 2 again since it doesn’t know the requests are actually for higher addresses because the only difference is A22 going high.

In the case of this game it doesn’t matter, but for some games they use tricks where they access the same information from different locations because it’s faster in code to do that, they use the mirroring intentionally to save instructions. Also, some security checks in games check for mirroring because inexact implementations like mine can be detected by looking at higher memory that’s supposed to be empty and finding data. It would get a bit more complicated if I wanted to get around that, basically I’d add a 74ls138 that is like the 139 but uses 3 bits to decode to 4 chip locations instead of 2 bits to 4 chip locations, then I’d use A22 on that and have it route to 4 unconnected chips (I may actually do this to make a metal slug 3 cart and stack chips to give me 8 V-ROMs decoded using a 74ls138 instead of the ‘139 that’s in there).

To finish off the cart there are replacement labels available for download in various places. I use Irfanview for my images because it was nice like a decade ago and this is an old windows installation. The print dialog in that program lets you pick the image size in X and Y (or just one and preserve the aspect ratio). for my printer and the specific label it took some tweaking as I printed test pieces on regular paper but once it was just right I ran a sheet of inkjet printable photo sticker paper and cut it out. Now I only realized that the original labels are not nearly that shiny after printing it, but it’ll probably be fine.

Lavender Hazelnut cake, attempt one

April 8, 2021
the serving tray is plastic, but focus on the fact I even have one, this chunk of kitchen counter is plywood

I decided to create a cake based on the following directions: “hazelnut and lavender spongecake with italian buttercream and frosted with a little chocolate ganache” and this is what my first try looks like. For all the ingredients I tried to get the best stuff I could: good quality chocolate, milk, cream, and butter from a local dairy, and homemade roasted sugar. If I was more patient I would have gotten a few dozen eggs from a friend’s farm an hour or so away. My girlfriend was a big help when making this cake, even though it took until 2am to complete.

Starting with the easiest thing first, the ganache. 8oz dark chocolate melting wafers (I used dark because it felt right with the buttercream being so sweet) and a half a cup of heavy cream. Stir together in a double boiler and once smooth you’re done. I used the entire amount on my cake and it ended up being a large clumsy puck topping the cake instead of a thin layer frosting the entire outside. If I can get the cakes to come out smoothly next time I won’t have to hide them in so much frosting.

Next is the italian buttercream. I made way more than I really needed, but it wasn’t all that difficult. Considering I had two 9″ round cakes this website gave me the recipe and I scaled it up to 10 cups (except the vanilla). My advice for this is to use a thermometer with an alarm in the sugar so you can be informed when it hits the right temperatures instead of staring at it the whole time. In addition to that I substituted the vanilla extract for Nielsen-Massey Vanilla Paste on the recommendation of the podcast A Hotdog is a Sandwich.

I don’t know how much frosting people normally like on cakes, but 10 cups seems like WAY too much for two 9″ cakes and I have quite the sweet tooth. That is neglecting the fact I never intended to frost anything but the center layer so maybe 3 cups max for that? maybe 2? When making this it is important to have faith. Your wonderful italian meringue will separate after being refrigerated, just whip it back up. It will also look like thin soupy clumps of butter in a broken hollandaise but have faith and keep going and it will come together into a very very stable frosting. Also, fix your stand mixer beforehand so you don’t have to take the back off and hold down the governor like I did. Repairs during cooking are exciting but counterproductive.

How else could I fancy this up a bit? make it a little more special? I used roasted sugar for the cake so here’s how I did that.

  • Start with 30oz. of white sugar, empty it into an even layer in a 9”x13” glass baking dish.
  • Place the baking dish into an oven and set the oven for 300F (I did not preheat mine, it came up to temperature with the sugar in the oven).
  • Bake for one hour, then open the oven and stir the sugar. It should already have started to take on a slight color and is probably clumped together. Break it up as best as possible to avoid issues later.
  • Bake for half an hour and stir again.
  • Bake for half an hour and stir again.  At this point I decided my sugar was ready, you might want a darker color and flavor, in which case repeat the above process as needed. 
  • Remove the sugar and let cool completely (you may want to stir while cooling, I did not). 
  • Once the sugar is cooled, sift into your seal-able retention vessel (I used a hand crank sifter to break it up into a light fluffy powder and stored it in a wire-top glass container). 

Notes on the sugar: I made more roasted sugar than was needed for this cake, by a lot. To convert it into caster sugar put a bit more than you think you will need in a food processor with the metal blade and let go for… a while. Weigh it AFTER that process to make sure you didn’t lose any that stuck to things or floated away. Do not try to process all 30oz. of sugar in the food processor, this will take forever. Did this add much flavor? I dunno, but it seemed like a nice touch and it was flavored enough that my girlfriend did not like the clumps which did not sift well in her tea, so it did something.

unmodified sponge (slightly overcooked and dry) to check for what the crumb is supposed to look like

For the cake I tweaked the recipe for this Classic Victoria sandwich cake. I am not happy with how it turned out but I have thoughts on how to help it. First I did a 1.5x batch because it calls for 20cm cake tins and I had 9″ ones. I buttered the tins and coated with the roasted sugar, but I would use cut rounds of parchment paper on the bottom of the pans if I did it again. You could use cake tins where the bottom comes out, but I couldn’t find these and I don’t like having tons of parts rattling around that can get lost.

I used 1/3 hazelnut flour and used a bit more baking powder to compensate for the heaviness (perhaps more was needed?). The rest of the flour I used cake flour and just added baking powder and salt to compensate for not using the self rising flour. The extra fine texture I was hoping would offset the hazelnut flour’s coarse grind. I might try a double-whammy of whipping the egg whites separately, creaming the egg yolks, butter, and sugar, then adding the dry ingredients, then folding in the whites to give extra mechanical lift in addition to chemical leavening. If I do that I will be sure to put them in a low rack in the oven, take out all the other racks, and then never move them (so as not to deflate them) when I open the oven to check them for done-ness with a toothpick.

seems too dense and not tall enough despite the extra baking powder

The lavender ratio was from someone else’s recipe and I used the ratio of lavender to other dry ingredients to figure out how much to use. I think it was too much, but not by a lot. Redoing this I might try 3tsp. The reasoning for powdered lavender was I envisioned this being done with picked fresh lavender buds from the garden of a nice victorian or second empire house, then ground by hand. Lacking that I ordered some from Bulk Apothecary and ground them in a spice grinder. I needed 4 grams and it was too much. DO NOT buy a pound because it’s not that expensive and you’d rather not run out. In fact, e-mail me if you want any. I have a lifetime supply.

  • 300g caster sugar (from roasted sugar)
  • 300g softened unsalted butter
  • 6 eggs beaten
  • 300g self-rising flour (made from the following ingredients)
    • 200g cake flour
    • 100g hazelnut flour
    • 5tsp baking powder
    • 1.25tsp salt
  • 1.5tsp baking powder (in addition to above)
  • 3tbsp milk
  • 4tsp lavender, finely ground

I creamed together the sugar and butter until smooth, then mixed in the eggs and milk, then slowly incorporated the sifted together dry ingredients. I weighed the two baking pans, going back and forth one dollop at a time trying to get them even before spreading them evenly. Cooked until the toothpick comes out clean (I think mine were a bit too long).

cleaning the frosting off those little bumps around this cake stand is not fun, don’t be messy like me

For frosting the cake I like to run my spatula under hot water so it doesn’t stick to the frosting while I’m spreading it. Also, it’s probably important to have a real offset spatula and not just a big solid stainless steel one used mostly for burgers and grilling. If I had thought about it at the time I would have put down a cake round, carefully sliced it with my knife to make it a clean cylinder trimming the crumbly and messy edges. Then frosted the top and added my second layer, also making it a perfect cylinder of the same diameter. Then after cleanup I could have lightly frosted the whole thing in ganache (I don’t exactly know how, I think I would have had to wait for it to cool a lot more that I did this time just pouring it into the hole in the top of my frosting mountain).

This has been attempt one of this cake, there may be another but not for a while (I’m out of eggs, butter, and some other stuff…). Now I’m going to get back to house remodeling (I kid you not, the paint in the bedroom is slightly off and it’s bugging the crap out of me).

Croque Monsieur En Croûte

April 8, 2021

Small deviation from our regularly scheduled programming. After bodging a bunch of recipes together I think this deserves to be preserved as a flexible recipe that turns out pretty good.

The inspiration for this was watching a bunch of youtube cooking shows like Binging With Babbish, and what was in the fridge at the time. The idea here is to make something like a croque monsieur but to use up the crescent roll dough that was the only bread like stuff in the house. My girlfriend brought the dough and has a deep love of ham-based foods, so even though we had ham steaks in the fridge I went out to get deli ham because it fits this recipe much better.

This is the form factor that came to mind. I will say now that puff pastry would have suited this better, but working with what we had in the house the crescent roll dough worked. In addition to that I wanted to jazz up the cheese a bit (and we didn’t have the gruyere that everyone seems to want in a croque monsieur anyway) so I stole the spice blend from this Alton Brown recipe for grilled cheese.

I don’t have exact measurements for anything but this is what ended up happening:

  1. 50/50 mix of finely grated swiss and un-smoked gouda tossed generously with that spice mixture
  2. thinly sliced deli black forest ham sliced even more into strips
  3. a single container of crescent roll dough (8 rolls)
  4. the bechamel as discribed in the Babbish recipe up top

Lay out the crescent roll triangles, spread with bechamel. Layer with sliced ham. Absolutely douse them in cheese, you should not be able to see them because your surface is so uniformly coated in cheese (this is fine). Spray down some muffin tins. Roll the triangles up and place them in the tins. Bake at about 375F until the tops are getting brown and you are worried they are getting done too fast on top. Take them out of the oven, free them up with a knife so they aren’t sticking at all. Remove the two stragglers in the otherwise empty muffin tin to a cutting board temporarily. Invert that muffin tin on top of the other six. Flip the whole thing. Place two stragglers back in the now available tin, but upside down. Return rolls to oven until toasty on bottoms (new tops). Dollop with remaining bechamel (it should be kept warm during this process, it will not have time to reheat in the next step). Coat liberally with remaining cheese mixture. Return to oven and broil the shit out of them. Remove when you are slightly worried about getting the cheese off the muffin tins. Quickly use a spatula to remove them from the tins immediately because that cheese will solidify there and you will never get them out at that point. Eat.

Fixes for next time:

  1. Keep the sauce warm while the rolls are in the oven
  2. Use less sweet dough (like the puff pastry)
  3. Maybe reduce cooking temperature or cover with foil to stop browning
  4. Cook a number of rolls equal to exactly half of your available muffin tin space, inverting halfway through may have been a good thing for browning and even cooking
  5. Do whatever you want with the spices in the cheese and sauce
  6. Honestly do whatever you want with the cheese mix and meat/filling entirely

Demon Debugger – alternative to a conventional ICE

December 1, 2020

This system is more of a concept than a single device, but that just means it’s very extensible. The concept is this: in order to explore, reverse engineer, or repair a system you can run a very small bit of software on the target system’s CPU and interface it to a computer to command it to read and write various IO lines or chunks of memory. How you go about doing this almost doesn’t matter, and we have put together a variety of different ways that may work for various platforms.

the most basic architecture

The project consists of three parts, the host PC software (interfacing to an arduino’s usb serial port for communication), the arduino as an i2c-to-serial converter, and the target system running a tiny kernel and wired to the arduino. Each of these is optional and can be replaced by other stuff. Let’s examine the simplest implementation: Sega’s Star Trek arcade machine, a Z80. For this implementation we use the demon.py software (either via demoncmd.py or demonapi.py, or your own script) to talk to the arduino, we use the arduino interface software to talk to the arcade board, and we use some already existing input and output pins determined by checking out the MAME driver and schematic for star trek.

; Moniker - Z80 Version
; by Frank Palazzolo
; For Sega Star Trek
;
; SCL  - OUT F9, bit7, (0x80) coin counter 1, pin 5, U11 - R1
; DOUT - OUT F9, bit6, (0x40) coin counter 2, pin 9, U11 - R3
; DIN  - IN  F8, bit3, (0x08) DIP, SW1, pin9, U2-pin 6
;
; Note: We cannot use opcode 0x32 on this platform, or it will trigger
;       the security chip

At the beginning of the z80 assembly code we can see the choices that were made. In the Z80 processor the code starts at 0x0000 so this program will be burned to a rom chip that is placed in the socket of the memory chip already mapped there. The Z80 also has a seperate IO space from memory space, it is accessed with different opcodes but generally behaves like memory reads and writes. We have decided to hook up to two pins of the coin counter for outputs and one pin of a dip switch as an input. It is important that we find 5v logic lines that are (for the outputs) latched to allow for this rather slow implementation of I2C. Speaking of I2C, why are we using three wires? well, getting bidirectional communication on arcade PCBs is a bit difficult, especially since the wires we’re hooking up to were never meant for communication. The 2n7000 FET basically multiplexes the signals together sort of how these level shifter boards do. One thing that’s also noted about this platform is a ‘gotcha’ put in by the manufacturer that means the security chip is triggered by a 0x32.

START:  DI                  ; Disable interrupts - we don't handle them
        LD      A,81h
        LD      HL,0xE000
        LD      (HL),A      ; blank the screen
        JP      INIT        ; go to initialization code

This is also a small amount of application specific code. For this platform we can want to disable the screen before moving on.

; Set the SCL pin high
; D is the global coin counter buffer
; Destroys A
SETSCL:
        LD      A,D
        OR      0x80
        LD      D,A
        OUT     (CCPORT),A
        CALL    I2CDELAY
        RET
    
; Set the SCL pin low
; D is the global coin counter buffer
; Destroys A
CLRSCL:
        LD      A,D
        AND     0x7F
        LD      D,A
        OUT     (CCPORT),A
        RET
; Set the DOUT pin low
; D is the global coin counter buffer
; Destroys A 
SETSDA:
        LD      A,D
        AND     0xBF
        LD      D,A
        OUT     (CCPORT),A
        CALL    I2CDELAY
        RET
; Set the DOUT pin high
; D is the global coin counter buffer
; Destroys A  
CLRSDA:
        LD      A,D
        OR      0x40
        LD      D,A
        OUT     (CCPORT),A
        CALL    I2CDELAY
        RET
; Read the DIN pin 
; returns bit in carry flag    
READSDA:
        IN      A,(DSPORT)  ;0x08
        SRL     A           ;0x04
        SRL     A           ;0x02
        SRL     A           ;0x01
        SRL     A           ;carry flag
        RET

This is the remainder of the application specific code. Because the target system is the I2C master and I2C is a synchronous bus it doesn’t matter how slow or variable the speed commands are sent out, everything just works. That means if it takes another 10 cycles to read a bit than it does to write one then everything is still ok. It doesn’t even matter what the speed of the cpu is on the target system because the arduino is so much faster in this case. The functions above will execute reads and writes to the bits we defined at the top for our hookups. All the electrical necessities for shifting bits, decoding addresses, latching outputs, that’s all already done by the designers of this board, we’re just trusting that they work and using them. But what happens if they don’t work, or if there aren’t easily accessible pins?

I give you, the Bit90! This is a rare colecovision clone that has built in basic roms and a keyboard. In fixing and reverse engineering it Frank and I developed the next generation of demon debugger: the entirely memory mapped interface. For this we gutted a colecovision cartridge and installed a memory chip containing our code, and two digital logic chips sitting where there used to be more memory.

        .org    0x8000	    ; cartridge start
    		
    	.db	0xaa	    ; cartridge signature
    	.db	0x55
    	
    	.dw     0x0000
    	.dw     0x0000
    	.dw     0x0000
    	.dw     0x0000
    	.dw     START
    	JP      0x0008
    	JP      0x0010
    	JP      0x0018
    	JP      0x0020
    	JP      0x0028
    	JP      0x0030
    	JP      0x0038
    	JP      0x0066
    	
    	.ascii  "BY: EVAN&FRANK/DEMON DEBUGGER/2019"
    	
START:  DI                  ; Disable interrupts - we don't handle them
        JP      INIT        ; go to initialization code

In this code we had some new challenges. The colecovision uses a BIOS, meaning that unlike other cartridge based consoles it runs code off the main board even if there is no cart inserted. This also means we cannot place the code at 0x0000 if we just use the cart slot. The cart is mapped to 0x8000 as you can see above and requires a header to be recognized as valid.

; Set the SCL pin high
; D is the global buffer
; Destroys A
SETSCL:
        LD      A,D
        OR      0x01
        LD      D,A
        PUSH    HL
        LD      H,0xc0
        LD      L,A
        LD      A,(HL)
        POP     HL
        CALL    I2CDELAY
        RET
    
; Set the SCL pin low
; D is the global buffer
; Destroys A
CLRSCL:
        LD      A,D
        AND     0xfe
        LD      D,A
        PUSH    HL
        LD      H,0xc0
        LD      L,A
        LD      A,(HL)
        POP     HL
        CALL    I2CDELAY
        RET
; Set the DOUT pin low
; D is the global buffer
; Destroys A 
SETSDA:
        LD      A,D
        AND     0xfd
        LD      D,A
        PUSH    HL
        LD      H,0xc0
        LD      L,A
        LD      A,(HL)
        POP     HL
        CALL    I2CDELAY
        RET
; Set the DOUT pin high
; D is the global buffer
; Destroys A  
CLRSDA:
        LD      A,D
        OR      0x02
        LD      D,A
        PUSH    HL
        LD      H,0xc0
        LD      L,A
        LD      A,(HL)
        POP     HL
        CALL    I2CDELAY
        RET

The act of writing data out is interesting because we are using a rom socket so we can only trigger the chip select line in this socket by reading from the memory region at 0xc000. The output lines we have are the address bus so by reading from various sections of the 0xc000 range we can trigger those address lines to go high or low and latch out a one or a zero to the 74HC74 chip. This act of writing by performing reads is incredibly inefficient without a lot more decoding, but it you have unused rom sockets it is an easy way to get output pins from memory mapped rom sockets.

DATAIN	.equ	0xa000		; where to read data in from
; Read the DIN pin 
; returns bit in carry flag    
READSDA:
        LD      HL,DATAIN
        LD      A,(HL)      ;perform a read into bit0
        SRL     A           ;carry flag
        RET

Reading is a lot more straightforward. I used a 74ls244 to put 8 TTL input pins on the bus to be read any time you read from the entire 0xa000 range (that’s where this rom chip used to be mapped). For this application I think we use D0 but this method can just five you 8 pins of input if you don’t want to bother with any more address decoding. Some 74xx138 chips could be hooked to the address lines to give you the entire memory space as discrete I/O but that is way overkill for this project, all we need is one bit.

Another success! after learning about how the bank switching inside the Bit90 worked we were able to dump the BIOS and BASIC roms by triggering bank switches and reading from the same section of the Z80 memory map.

The last iteration I’m going to talk about in detail is a setup that fits entirely in a single 2716 socket. The decoding logic is a bit more complex than it needs to be because it’s set up for either a Z80 or a 6502 which need the code to start at different locations, but the setup didn’t end up that complex.

First, this is my somewhat complicated additional demon debugger hardware that is jumper selectable. I had some extra PCB space so I added the 2n7000 that muxes the single direction lines into actual I2c, but in order to keep compatible with existing demon debugger hardware I made it selectable. To the left of the dotted line is the suggested demon debugger hardware on the arduino side. To the right is my new hardware that is incorporated into the target board. The first thing I added was a pull up on the Din (or SDA) line. we had an issue once where the internal pullup failed and we were chasing a bug for quite a while before we found that out. External pull ups are cheap and easy so that could help a setup that uses a vanilla arduino or one with the FET on it. I also added a 2n7000 of my own. In the configuration I show it with two jumpers connecting the outer most pins on that header the rightmost FET is disabled, its gate is tied to ground so it is not having an effect on the Din line and the Dout line is wired to the gate of the FET on the arduino side. If you pull those two jumpers and instead fit a single jumper between the middle two pins you allow the device on the left side of the dotted line to just be a vanilla arduino with no additional hardware. You may also be able to use a buspirate or FTDI chip with no software on it as an I2C slave at that point, but the python code would have to be tweaked to accommodate that. If you were using a demon debugger bit of hardware on the left side you could also connect the top and bottom pins to ground the gate of the FET on the left in case it’s acting up for some reason.

Using OE instead of CE or both is because we are actually interfacing to a socket that used to have a 2364 installed, this should work for most setups though

This is the output enable logic for the devil board as we called it. Basically there’s a 2716 or equivalent socket on the target system and we have a 2732 that we use to maps into that space. I used a 2732 so we can have a jumper on the A11 line to get us two programs on one chip. The devil board sniffs four address lines, can inject data on D0, and man-in-the-middles the Output Enable line to redirect some of the memory calls away from the ROM and to our latch circuits. The 74LS74 and 74LS244 are the same as we had in the Coleco cart, but they are now mapped as follows:

; "2716" Memory Map
; X000-X3FF - ROM
; X400-X5FF - I/O (Special region)
; X600-X7FF - ROM
; SCL connected to A0
; DOUT connected to A1
; DIN connected to D0
; "Special Region"
; Read from X4X0 - clear SCL, clear DOUT
; Read from X4X1 -   set SCL, clear DOUT
; Read from X4X2 - clear SCL,   set DOUT
; Read from X4X3 -   set SCL,   set DOUT
; All reads return DIN as bit 0

The input and output all occur in the 0x400 to 0x5FF section of the 2716 as selected by the 74LS139. The ‘139 basically breaks the 0x800 space into chunks of 0x000-0x1FF, 0x200-0x3FF, 0x400-0x5FF, and 0x600-0x7FF. Three of those are routed back to the ROM, but one is stolen and enables our registers when the system thinks it’s accessing ROM. Since we’re setting SCL and Dout at once we have four different sections of memory we can read from, each sets the outputs in a different combination. Every output set also triggers an input on Din because unlike last time the input register is triggered at the same time as the output.

        .org    0x2000	    ; cartridge start
        
        .byte   0x55	    ; cartridge header
        .word   0x0218	    ; next menu item (first one)
        .word   TITLE	    ; title pointer
        .word   START	    ; start pointer
        
        ret		    ; rst8
        nop
        nop
        ret		    ; rst16
        nop
        nop
        
	ret		    ; rst24
	nop
        nop
        
	ret		    ; rst32
	nop
        nop
        
	ret		    ; rst40
	nop
        nop
        
	ret		    ; rst48
	nop
        nop
        
TITLE:	.ascii	"DEMON DEBUGGER"
	.byte	0x00

In this implementation we installed it in the cartridge of a Bally Astrocade console so we needed a cart header here as well, the astrocade not only has a BIOS like the Colecovision, but built in games (and our demon debugger shows up as a selectable ‘game’. Like the Coleco/Bit90 implementation we set both output bits at once so the functions have to grab what the other bit is supposed to be from RAM and make sure both bits are asserted correctly when the function to assert just one is called.

That’s all the hardware I helped develop, and honestly it’s at a pretty good stage. Some implementations like Gorf or the Astrocade require you to hit a hardware watchdog or the machine will keep resetting, and any new applications will have their own quirks. You can always use something like this PET RAM/ROM board to get some code right into the memory map of any 6502 based machine (still have to locate some I/O or add a version of our devil board to map some in). There’s also a special implementation for the Intellivision based on the LTO Flash cart and its built in USB serial port (so just need the PC side software there). All of the above are various options for the target system interface hardware and some replacements for the intermediate arduino I2C to serial converter. Now it’s time to talk a bit about the PC-side software.

So far I’ve been talking about the platform dependent changes that need to be made, but this is all the common stuff. In addition to those functions to set and read the I/O lines each kernel includes functions that can read and write memory, read and write ports, and start executing code form a given location. That all remains the same for a given architecture (so far the Z80 and 6502). The PC side software is written in python for windows, but should be able to be ported just about anywhere. There is a python API exposed for demon debugger so you can write python code to interface to the target board as well as do some other things. This has so far been used for automating data collection from a rigol scope on the network to improve the Gorf mame emulation colors. This stuff is located in the DEV branch on the github repo so be sure to go there to check out the latest on how to automate the reverse engineering of vintage hardware.

The last thing to mention is that there is an arduino emulator to check out your hardware. using a second arduino uno you can emulate a Z80 target for demon debugger and practice reading and writing memory and I/O before you hook up to a real system and attempt to debug that. If you’ve got a tiny bit of RAM, and a place to install your own code then nearly any system can be hooked up to a PC and the hardware on it can be investigated to see what’s hooked where, or to determine experimentally what is broken on the board.

Ascii Keyboard project

November 30, 2020

When we put together the ZRT-80 it was complete luck that there was an unassembled kit for an ascii keyboard to just use for that project. Plenty of people aren’t so lucky and have to resort to extremely expensive vintage offerings or converters that use pretty outdated keyboards as the base anyhow. I can’t exactly throw stones because I’m still using an older keyboard every day and the matrix I chose for this project is not really great quality, but at least it was free. Well, at least it was free to me. The archer 227-1020 keyboard is not exactly cheap these days if you want to do an exact clone of this project. EDIT: this is a Coleco Adam keyboard. In fact, to interface with my matrix you need a nearly unobtainable 2.5mm spacing flat flex socket that’s exactly 21 positions wide. Almost no one makes those connectors in that spacing any more and only one that I could find that wide.

Even if you don’t want to spend an unreasonable amount of money on a hard to use and poor feeling keyboard, this project can help with other options. We live in the golden age of custom keyboards! standardized switch shapes and footprints, custom printed and molded keycaps, diy frames and 3d printed cases, you could do everything that people do to make custom USB keyboards and replace the controller with mine here and you have an extremely high quality parallel keyboard for any old ’70s or ’80s system that needs it. If you’re doing your own matrix and can lay it out however you want then you might look at the 2376 encoder chip which (with minimal extra parts) can serve as a one-chip solution parallel keyboard.

Once I had obtained the connector for the key matrix I had to decide on a strategy for talking to the keyboard and the computer/terminal. I needed 21 pins for the matrix, 8 data bits, and a strobe line. I read somewhere that slower computers have a busy line so they can keep the keyboard from sending more data until the computer is ready so we can also add that. I’m up to 31 pins and I don’t even have any LED indicators or aux switch inputs/outputs. At that big of a pin count my microcontroller options are annoyingly expensive so we’re back to the old atmega328 and associated chips to reduce the pin count. Above you can see 2x 74hc138 chips wired together as a 4-to-16 matrix scanner, a 74hc595 as a SPI to parallel output chip for driving the bus to the computer, a 74hc597 as a SPI to parallel input for reading the matrix, and a 74hc86 xor gate for selectable inversions of the strobe and busy lines. I’m pretty proud of this setup but it needed a little rework after first contact with the matrix.

Here is my brilliant attempt to scan the key matrix. These ‘138 chips output a high logic level on all the outputs except the one that’s selected. These days chips tend to go into high-z mode on the lines that aren’t being used but in some applications driving outputs like this would reduce the need for pull up resistors and cut down on the part count of the design. I used one of the positive enable lines and one of the negative enable lines to add a fourth address line to these two chips, making it act like one big one. This meant that I just needed one extra pin on the arduino and I could double my scanning output. This worked, but if you look up at the matrix layout above we have a problem. When you press and hold one key while pressing another it is possible to interfere with the scanning and give the voltage somewhere else to escape. On this matrix the modifier keys, control and shift, are in the matrix, but not on any shared rows. This means they can be scanned by the matrix and if you do it right they will not interfere with any other keys. I did it wrong. Holding shift and A for example connects two of the outputs of the ‘138s together, inevitably connecting a high output to a low output whenever one of those rows is scanned. I then had to reverse course.

Here is my revised (cut down) strobe circuit, now only scanning the 8 columns. This also shows my use of XOR gates for selectable inversion of the strobe and busy lines. With this configuration you can run the keyboard with the busy line unconnected and the pull up will make it think that the connected computer is always ready for more data. If the computer needs inverse polarity of either or both of those lines just short those jumpers and you can invert the signals in hardware. If you want a really general purpose keyboard I guess you can break those out to toggle switches, but usually something like this would be paired with a specific computer or terminal and they can be set once (also possible to do in software, but I find it annoying to have to recompile some software every time I want to change a little setting like that).

Here is the new 597 chained input section. This can read up to 16 lines of the matrix all latched in at once and using hardware SPI too! The 595 adds 8 output pins and only costs one more pin on the arduino as they are the same bus. The chainable output on the 595 is left floating because we don’t need to drive another chip (although we could) and the 597s are tied together with theirs. This would have been a perfect time to use SIP resistor packages but I didn’t have any of the right size convenient. I can use the unused input lines to detect if there’s a problem with the hardware or the reading routine for the chips. The top 3 lines are always wired to 5v through their pull up resistors, the unused chainable input is wired to ground. That means if I read anything other than high logic signals on the lines that are supposedly unconnected I have an issue, and if I read three or more bytes and the additional bytes are anything other than low logic signals I also have an issue.

The hardware was the easy part, the software is where things are more interesting. Driving the ‘138 scanner is trivial, just put the address on the bus. Converting from a byte to individual lines though is nice and compact: We iterate over the entire array of output pins, writing the result of bit shifting and checking if the resulting number is even or odd, basically if the least significant bit is 0 or 1. The bit shifting just puts that bit in the position to drive the output of that modulus check.

  //set address line
  address += 1;
  if (address > 7)
    address = 0;
  for (int i = 0; i < sizeof(addr_pin) / sizeof(addr_pin[0]); ++i)
    digitalWrite(addr_pin[i], (address >> i) % 2);
  delay(1);

Then we have to read from the ‘597s. We take that data into a 2-byte wide variable and similarly check if the bit at a given position is a one or a zero. In this case a zero means the key is being detected as down. we then update a current working copy of all the key statuses (if they are pressed or released)

  SPI.beginTransaction(set597);
  digitalWrite (ss, LOW);
  digitalWrite (ss, HIGH); //Latch inputs
  //read entire row of keys
  temp = SPI.transfer(0);
  temp += (SPI.transfer(0) << 8);
  SPI.endTransaction();
  for (int i = 0; i < 16; i++) //iterate through each bit of the 16 lines we just read
  {
    if (!((temp >> i) % 2))
    {
      key_status[i][address] = 1;
    }
    else
    {
      key_status[i][address] = 0;
    }
  }

The next part is a bit hard to follow. We iterate through the whole matrix (8×16) checking the key statuses versus what they were last time we scanned them. If they move from a 0 to a 1 the key must have been just pressed, if it went from a 1 to a 0 it must have been released. On key down, if the key is shift or control and nothing else is complicating things we move into either control or shift mode. In these modes the keyboard keys are fully remapped to send different codes, all definable in tables. On key release if you are releasing the shift or control keys we cancel that mode unless some lock has been set. This keyboard has a lock key so I decided to make shift lock and control lock each work as if you are holding down those keys all the time. The lock status is indicated by the state of two LEDs that can be wired to the top of a future enclosure. If a key goes down and you are in either shift or control mode then we check to see if it’s the lock key and either set or un-set the lock mode. The last thing we do after checking each key is to update the other table of key statuses so we can detect the next time it goes up or down properly.

  for (int i = 0; i < 8; i++)
  {
    for (int j = 0; j < 16; j++)
    {
      if (prev_key_status[j][i] && !key_status[j][i]) //key went up
      {
        if (!controled && !shifted)
        {
          //do nothing
        }
        if (i == 0 && j == 13 && !shift_locked)//hardcoded shift
          shifted = 0; //only stop shifting if the lock isn't set
        if (i == 0 && j == 12 && !control_locked)//hardcoded control
          controled = 0; //only stop controling if the lock isn't set
      }
      if (!prev_key_status[j][i] && key_status[j][i]) //key went down
      {
        if (controled)
        {
          if (i == 0 && j == 14 && !control_locked)//hardcoded lock
          {
            control_locked = 1; //set control lock
            digitalWrite (ctrl_lock, control_locked);
            Serial.println("set control lock");
          }
          else if (i == 0 && j == 14 && control_locked)//hardcoded lock
          {
            control_locked = 0; //un-set control lock
            digitalWrite (ctrl_lock, control_locked);
            Serial.println("un-set control lock");
          }
          else
          {
            HandleKey(j, i, 0, 1);//controlled key
          }
        }
        else if (shifted)
        {
          if (i == 0 && j == 14 && !shift_locked)//hardcoded lock
          {
            shift_locked = 1; //set shift lock
            digitalWrite (shift_lock, shift_locked);
            Serial.println("set shift lock");
          }
          else if (i == 0 && j == 14 && shift_locked)//hardcoded lock
          {
            shift_locked = 0; //un-set shift lock
            digitalWrite (shift_lock, shift_locked);
            Serial.println("un-set shift lock");
          }
          else
          {
            HandleKey(j, i, 1, 0);//shifted key
          }
        }
        else if (!controled && !shifted)
        {
          // modifier keys handled state triggered now, not edge triggered
          HandleKey(j, i, 0, 0);//normal key
        }
      }
      prev_key_status[j][i] = key_status[j][i];
    }
  }

I still had some edge cases based on how the matrix was scanned so I had to add some checks outside of the key edge detection logic, I added some state dependent logic. I may be able to go entirely to state based logic but for now this works. Depending on what order I scan that far left column it is possible to detect two keys going down in the same scan window and act on them in whichever order I want, but that’s probably too far in the weeds for a matrix that scans so fast.

  //this handles edge cases where you unlock while not holding down the modifier key
  if (!key_status[13][0] && !shift_locked)
    shifted = 0;
  if (!key_status[12][0] && !control_locked)
    controled = 0;
  //this handles edge cases where you unlock while holding down the other modifier key
  if (key_status[12][0] && !key_status[13][0] && !shift_locked) //holding control and not holding shift and not shift locked
    controled = 1;
  if (key_status[13][0] && !key_status[12][0] && !control_locked) //holding shift and not holding control and not control locked
    shifted = 1;

The HandleKey function basically replaced my debug statements, taking all the data I used to print out and handling it sanely.

void HandleKey(int j, int i, boolean shift, boolean control)
{
  if (!shift && !control)
  {
    if (key_scancode[j][i] > 0x7f)
      HandleMacro(key_scancode[j][i]);
    else
      SendKey(key_scancode[j][i]);
  }
  if (shift)
  {
    if (shifted_scancode[j][i] > 0x7f)
      HandleMacro(shifted_scancode[j][i]);
    else
      SendKey(shifted_scancode[j][i]);
  }
  if (control)
  {
    if (controled_scancode[j][i] > 0x7f)
      HandleMacro(controled_scancode[j][i]);
    else
      SendKey(controled_scancode[j][i]);
  }
}

The next function is something I’m pretty happy with. Macros can be any character from 0x80 to 0xFF, I started counting backward from 0xFF because it’s easy to see all the undefined 0xFF key behavior in the scancode tables. This means that in my current implementation you cannot define extended ascii characters except through single character macro sequences which is a bit cumbersome to read but I don’t expect to come up often, if at all. I don’t actually know how various terminals and computers will respond to getting 8-bit extended ascii sequences over a parallel keyboard port, the 2376 controller chip won’t generate them so maybe they’re just passed on to the operating system or program? maybe they’re filtered out? maybe they just ignore the high bit entirely.

These macros can be useful for talking to a specific terminal and automating escape or control sequences. They can also be useful for talking through the terminal transparently and talking to the computer operating system or program to execute a specific function that could take multiple keys.

void HandleMacro(int macro)
{
  switch (macro) {
    case 0xff:
      Serial.println("do nothing");
      break;
    case 0xfe:
      Serial.println("up"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      //SendKey(0x31); //1 might be unneeded, defaults to 1
      SendKey(0x41); //A
      break;
    case 0xfd:
      Serial.println("down"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      //SendKey(0x31); //1 might be unneeded, defaults to 1
      SendKey(0x42); //B
      break;
    case 0xfc:
      Serial.println("left"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      //SendKey(0x31); //1 might be unneeded, defaults to 1
      SendKey(0x44); //D
      break;
    case 0xfb:
      Serial.println("right"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      //SendKey(0x31); //1 might be unneeded, defaults to 1
      SendKey(0x43); //C
      break;
    case 0xfa:
      Serial.println("wtf? no. stop."); //you are mucking with control and shift at the same time, stop it!
      break;
    case 0xf9:
      Serial.println("Erase in Display"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x32); //2
      SendKey(0x4a); //J
      break;
    case 0xf8:
      Serial.println("home"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x48); //H
      break;
    case 0xf7:
      Serial.println("F1"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x31); //1
      SendKey(0x50); //P
      break;
    case 0xf6:
      Serial.println("F2"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x31); //1
      SendKey(0x51); //Q
      break;
    case 0xf5:
      Serial.println("F3"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x31); //1
      SendKey(0x52); //R
      break;
    case 0xf4:
      Serial.println("F4"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x31); //1
      SendKey(0x53); //S
      break;
    case 0xf3:
      Serial.println("normal charset"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x67); //g
      break;
    case 0xf2:
      Serial.println("alt charset"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x66); //f
      break;
    case 0xf1:
      Serial.println("clear display"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x45); //E
      break;
    case 0xf0:
      Serial.println("clear"); //spare
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x32); //2
      SendKey(0x4a); //J
      break;
    default:
      Serial.println("undefined macro");
      break;
  }
}

The last part is how I deviate a little from the 2376 I’m modeling my timing on. That controller holds a state for as long as you are holding down that key. With my multi-key sequence macros I can’t do that. I took the information on the world’s fastest typist (216WPM), converted that to characters per minute (1080) and then to the maximum amount of time the whole transaction needs to take place (55.5ms). That helped me set the timing of the keyboard pulses so as to mimic someone typing faster than the world’s fastest typist hopefully to allow for the whole macro to be sent out before someone tries to hit the next key (although for very long macros that time would have to be further reduced to get that effect).

void SendKey(byte code)
{
  //set bits
  SPI.beginTransaction(set595);
  digitalWrite (ss, LOW);
  //set output bits
  SPI.transfer(code);
  digitalWrite (ss, HIGH);
  SPI.endTransaction();
  Serial.println(code, HEX);
  //settle 4.4ms + 88cycles @ 50khz = 6.16ms
  delayMicroseconds(6160);
  //set strobe
  digitalWrite (strobe, HIGH);
  //settle 0.5us + 30ms (way exceed human typing speed)
  delay(30);
  //look for busy signal
  while (!digitalRead(busy)) { //blocks on busy signal being low
    Serial.println("blocking for busy line");
  }
  //un-set strobe
  digitalWrite (strobe, LOW);
  //settle for more than 0.5us
  delayMicroseconds(1);
}

The keys I chose for the macro sequences came from the manual for the ZRT-80 (which this will probably hook up to), the manual for the ADM-3a (a pretty standard terminal), and the wikipedia entry on escape sequences. I will probably want to tune the macros to the application, but for now these will do. To edit the codes that each key sends just edit the three tables near the top of the code, 0x00 indicates no key is present in the matrix (or to send the null byte), 0xff indicates that a function has not been assigned to that key in that mode, anything 0x7f or less just gets sent right out, anything above that gets handled by the macro function to send one or more keys in sequence. I wired the output exactly like a parallel port that printers hook up to so if I were to get a dot matrix printer I may be able to make this act like a teletype in loopback mode. Now I just have to model up and print an enclosure. Either that or I can just buy a metal keyboard case and cut a big hole for the keys to stick out.

c64DTV power on reset circuit

November 27, 2020

I wanted to get this project all documented at once, but that’s a pretty tall order with how many moving parts it has. I also have issues with being able to track down all the information I want to include in a blog entry months after I did the project. I’m going to start with the latest aspect of this project I’ve finished and work backward through parts that I can document.

This is the issue I have. With my ps/2 keyboard when I turn on the c64DTV I get a “V” on the screen. As I type I get some characters, but not all, and a bunch of “V”s included until some point where it snaps out of it and is ok. The easiest option for this is to press the reset button after the device is powered on and then the keyboard operates properly. I recessed my reset button a little too far to be comfortable so now I want to have an automatic option. The standard option to get around this slight flaw in the c64DTV implementation is a keyboard twister. The problem I have is that even though it fixed the initialization function the keypresses were much slower, there was lag, it got confused if you type too fast. I don’t need the german translation functions or macros offered by that device so I designed my own solution.

I started by looking up “power on reset circuit” and came across the one for the apple 2. The problem is in order to simulate pushing the reset button I wanted to wait some amount of time, then press the button for a different amount of time, then release it for the duration of the power being applied. This circuit simply generates a high pulse for a given amount of time. In order to generate a pulse after some time I need something more complicated.

This circuit has two of those pulse generators that have slightly different periods. I combined them with a 2n7000 to get an open drain output and only have the reset pulse enabled when the output of side A is low and B is high. I did that by grounding the 2n7000 through the A half of the 556 so the pulse on the B side is not causing a reset until the pulse on the A side is done.

prototype circuit installed in the c64dtv
compact module, removable if needed
Fixed!

Upgrading a cloned logic analyzer

November 26, 2020

In my last post the logic analyzer I built so many years ago was actually useful. I did have some problems though. Due to it not being in a case or protected at all it wound up not having all 8 functioning channels. For this project it wasn’t a big deal, but for the future I want to fix that. While I’m fixing that I might as well make an interesting upgrade: double the channels! The first hack was centered around cloning an existing device to use its software, that is no longer desirable. Pulseview is now plenty good for using on a regular basis and I have no need of any proprietary software. That being said, I still (had to?) write a cloned eeprom to this board to make it identify as what I wanted.

The one I chose is the Braintechnology USB-LPS, this device is auto-detected in Pulseview with 16 channels and therefore can do what I want. I tossed out the shield that I had made so many years ago and decided to use the onboard eeprom spot since I no longer want to be able to switch between USB identifiers.

The chip I used happens to be a 24LC16B, since I’m not trying to get around the Saleae copy protection I don’t have to worry about 16bit addressing. The formatting I determined from this site ended up being C0 (tells the board to use the eeprom’s following VID/PID/DID) D0 16 98 04 84 23 00. I don’t know what DID I should use but it doesn’t seem to matter. Note that the addressing is backward-endian so every other byte is swapped. These bytes need to be placed at address 0x00 and it will detect in windows properly.

I had assumed that the software would just sort itself out because of the USB Vid/Pid setup. That was not the case for me. I got the device show up as unidentified until I installed the Braintechnology USB-LPS driver. That went well but still resulted in it not showing up in Pulseview. I got the error “fx2lafw: Failed to open potential device with VID:PID 16d0:0498: LIBUSB_ERROR_NOT_SUPPORTED”. To fix it I had to run Zadig and force the device to use WinUSB driver. I find myself having to use that program a lot lately, still not really sure what the usb drivers it switches out are for.

The next step is input protection. For the last one I cloned the input stage of the Saleae directly, but I don’t think that is as important this time. Based on the above table I decided to go with 100R inline resistors and 74HC245 buffer chips (I don’t need the bidirectionality, but the layout of the 244 is just annoying). I’m socketing them so I can try out HC, HCT, and other families (or none). This time around I’m not limiting myself to the same footprint as the breakout since I need 16 channels plus a couple grounds. I think I’m going to omit the use of a power header like the USBee, I never used them before and usb ports can’t source that much power anyway.

Since my original design I replaced the cable that had cheap chinese micro grabbers hardwired to bits of ethernet cable with a higher quality one. The new one has 9 pins, 8 for data and one for ground, it is color coded in the resistor color order and terminates in female 0.1″ crimps so I can use brand name Tektronix micro grabbers or plug into board pins directly. The newest replacement uses the color scheme that Pulseview assigns to each of the 16 channels. 0-7 are black through purple and 8-15 are grey through green. This means that each of the standard rainbow colors are used and even reused, but I think it can be obvious the order it goes in. That is except for needing to have ground on different cables; the colors get even more confusing because black is used as a data color twice by Pulseview.

still need to crimp the second 8 conductor cable

VCP200 reverse engineering

November 26, 2020

Some years ago I got a few VCP200 Speech recognition chips. Even though they are very limited in what they can do I was motivated by the simple interface to try to find a use for them. I eventually built the circuit available in the April 1991 edition of Radio Electronics to make use of the chip (with minor modifications). After finding some soldering faults and understanding more of what the amplification and clipping sections were doing I actually got it to work! My next plan for the three chips I have now is to draw up a design for the circuit to drive them using either through hole or surface mount components and have some slightly more robust modules than the one I hand soldered together.

But this post is not about that. When I watched the EEVblog episode on this chip it interested me greatly that it was not an ASIC, but probably a microcontroller with mask ROM inside. Having done a lot of work with MAME and dumping whatever I can get my hands on this seemed like an interesting challenge. After asking around it seemed that it may be possible to electrically dump the contents of the chip using a diagnostic mode Motorola included. Now comes the challenge of building the circuit to get those modes working and eventually figure out if the data I want comes out somewhere undocumented.

That’s the problem, the documentation says that this chip, the MC6804J2 has four modes of operation, and none of the documentation for any of them indicates that it can be used to read out the internal program or data memory. Based on some previous reverse engineering efforts on similar Motorola parts we started by looking at Non-User Mode, but that ended up being a dead-end (for now). For starters the documentation says that ports A, B, and C are used to interface to external instructions, but on the J2 chip we use there’s only port B and half of port A available at all! In addition the non-user mode interface is described in no documents I can find for this chip, but we may be able to eventually infer what the interface is based on other similar Motorola chips with more surviving documentation.

Based on the schematics for these test circuits I built something similar. What I made used the reset circuit and PA6/PA7 drive circuit from the functional test (which is self-check from above), but changed the crystal to an oscillator and made a whole bunch of pin configuration jumper settable. Before we move on to my design, take a look at the circuit for the ROM verify mode. This chip has a hardware CRC circuit inside and the ROMs, when properly authored, will be able to pass a CRC check using this circuit. That means that at very least the ROM data is coming out on one or more of those pins, XORed or otherwise garbled, and that at least one pin is clocking all those bits into this circuit which can verify that the CRC passes or fails.

As you can see I have adopted my usual use of massive piles of 0.1″ header and jumpers to configure things. Here I have pin headers that can be connected to a logic analyzer (port B only) as well as jumpers to enable pull ups on any available port pin. For the non-port pins: /IRQ is floating, as is EXTAL, XTAL goes to the output of my 1.000MHZ oscillator can, MDS and TIMER are both pulled up with 1K resistors. The reset circuit pins are pulled up with 4.7K resistors as are all of port B. Port A pins 6 and 7 are pulled up with 10K resistors, and pins 4 and 5 are pulled up with 1K resistors. Now for the taking of the data.

For this we used sigrok (the pulseview GUI). I had previously built a clone logic analyzer, but really the only important part about it is that it’s compatible with this open source software stack that’s pretty easy to use. For this dump we connected to pins PB0 (D7), PB2 (D6), PB3 (D4), PB5 (D2), PB6 (D1), and PB7 (D0). The other pins didn’t seem to do anything interesting and I was having an issue with some channels of the logic analyzer (soon to be overhauled). You can see that PB7 is the master bit clock, and that makes sense because in the rom verify crc circuit it is clocking the latches. PB6 is not used in that circuit but seems to be a pulse happening once every 12 clock cycles, our frame clock. PB5 seems to be the address of memory being represented in those 12 bits of data (each address is sent twice before moving on). PB3 and PB2 look random, probably the CRC values. PB0 looks like our serial program data! Here is the explanation for the above captured data:

one frame clock every 12 bits
one cycle of data every 24 bits
we get the address twice
we get 0xff for the first data byte (not real)
we get the real data for the second data byte
also, the data is delayed by 24 bits (1 address cycle)
Example as seen in the capture pic:
1 00000000000 D1, PB7 frame clock
1 00000111010 D2, PB5 reverse is (010 1110 0000) (0x2e0 address)
1 10000000011 D7, PB0 reverse is (11 00000000 1) (0x00 data from 0x2df)
1 00000000000 (frame clock)
1 10000111010 reverse is (010 1110 0001) (0x2e1 address)
1 11111111111 reverse is (11 11111111 1) (0xff data not real)
1 00000000000 (frame clock)
1 10000111010 reverse is (010 1110 0001) (0x2e1 address)
1 11010011011 reverse is (11 01100101 1) (0x65 data from 0x2e0)
1 00000000000 (frame clock)
1 01000111010 reverse is (010 1110 0010) (0x2e2 address)
1 11111111111 reverse is (11 11111111 1) (0xff data not real)

The python code that does all this parsing is based around the raw binary logic data that Pulseview can export. This keeps the filesizes nice and small while still being pretty easy to parse. That python code was able to generate a binary file representing the entirety of the program memory on the chip.

Now that we have a big block of everything in program memory it needs to be disassembled. You can do this by hand, but that’s infinitely tedious, and since there was no disassembler for the 6804 readily available we had to write one. The first pass was my super easy method of decoding every byte. I just checked one table to see what instruction decoded to and printed the contents of that table entry. Then I checked another table to see if it was at more than one byte, if so I grabbed another byte and printed it verbatim. Then I checked that table again to see if it was three bytes long, if so I grabbed the last byte and printed it, otherwise I moved on. There’s no re-checking or anything so if you get off by one byte somewhere you may never get lined up again and you’re reading data as opcodes and everything is all screwy. Frank had an improvement to my disassembler that made it a little smarter in handling each op code and formatting them in a way that matches standard disassembly syntax better.

Inside the ROM we see key areas described in the datasheet memory map. The User Restart Vector and the User /IRQ Vector are the same because in the VCP200 datasheet the /IRQ line of the microcontroller is tied to Vcc. The self-test ROM Reset Vector goes to AF6 which is interesting since the region starts at AE0. It turns out that AE0 is used for a subroutine that gets called later. The self-test /IRQ line goes to some sort of subroutine in the middle of the self test region so that makes sense, whenever that line is triggered then the program can verify it happened based on where it is in the code. The bottom of the self-test ROM actually contains some ascii data saying “MC6804J2 – ATX” which must mean that this is the self-test code specific to that processor variant, confirming what we suspected (except now we know it’s probably the HMOS variant). In the preliminary reverse engineering of the program memory it looks like the VCP200 code looks at three bytes of the data ROM that we don’t have. It also doesn’t look like the self-test code look at it at all. I am unclear what part of this processor checks the data ROM for integrity, if any.

It’s possible the data ROM is somewhere in the data we’ve already captured, it’s also possible it’s accessible in non-user mode, or through some part of the functional test program that we’ve started reverse engineering. If we emulate the processor, then send it known streams of pulses representing speech data and see what the real chip does versus the emulated chip we may even be able to reverse engineer those three bytes it reads out of the data ROM. It should be a tedious, but simple task of designing test sequences that cause the program to read those data bytes and analyzing what it does after to determine what those bytes were. That is beyond my current level of ambition though.