Showing posts with label software. Show all posts
Showing posts with label software. Show all posts

Thursday, March 21, 2019

Llichen-80 (Retrochallenge 2019-03 Update)

I had originally intended for any time I could devote this month to the 2019-03 Retro Challenge was going to be for a new version of my RC2014 Z80 computer emulator, adding support for the TMS9918A video display chip.  But my plans have changed.  With the recent advancements with The 8-bit Guy's "Commander X16" 6502 computer, I decided to start reviving a project I was considering for a while.  The great thing is that there's so many aspects to it that it's like a grab-bag of things to do.

The original idea was to sit down with a C64, floppy drive, monitor, joystick, mouse and reference books, and start with BASIC, writing a text editor, assembler, IDE, etc and work my way up to having a windowed operating system like GEOS with a few utilities and such.

Except now, the spec of the system is designed by me, and I'm building that too.... Which meshes in perfectly with my original intention to build a RC2014+TMS9918 emulator, which I can use for initial development, and kickstarting the boot rom onto it.

Hardware

I wanted some sort of related name for the project, so I was thinking Llama, TMS, which became Llitmus, like the PH sensitive dye.  I was gonna go with Llitmus-80 with the '80' because it uses a z80, but then once I read that the litmus was made from lichen, I just went with the name Llichen-80.

First, let's get into the Llichen-80 system specification.

  • RC2014 backplane/base system
  • 7.37 MHz clock speed
  • 32k RAM from 8000-FFFF
  • 8k BASIC ROM from 0000-1FFF (*)
  • 32k RAM from 0000-7FFF (*)
  • Digital IO board at port 00
  • TMS 9918A video board at port 10,11
  • ACIA Serial port at 80
  • ACIA Serial port 2 at C0 - connected to CH376S USB drive interface
So (*) Indicates something... there's an overlap of these two items. My existing design piggybacks bit 0 of the output from the digital board to select which of the two of these that memory READs come from.  That is to say that writing 0x00 to the digital out board will select ROM, and 0x01 will select RAM.  Writes to these locations always go to RAM.  So one way I can test this is:
  • Start up the computer
  • select ROM bank
  • Write a basic program to peek from the ROM and write to the RAM
  • select the RAM bank
  • Yank out the ROM board, and everything still works fine
10 OUT 0,0
20 FOR A=0 TO 8192
30 B = PEEK(A)
40 POKE( A, B)
50 NEXT A
60 OUT 0,1
RUN
This all works great!  One addition I made was to intercept the VCC to the RAM chip and have it also connected to some CR2032 batteries for a backup.  It's not perfect, but it works well enough for my system for now.  I also added a switch to force the use of the ROM, since sometimes the bank switching can get into a messed up state and you need to force reboot off of the ROM.

The other thing I created was a second ACIA serial port, essentially duplicating the circuit of the stock ACIA serial port, but changing it to look at ports C0 and C1 instead of 80 and 81.

The TMS could be configured for anywhere, for the most part, but the ACIA chip IO port mapping is messy so they consume essentially from 80 to FF.  I went with the SORD-M5 configuration of putting it at 10 and 11h.  Although there might be issues with this...

Anyway, more about these modifications and such at the end of the month.

Experiments 



I ended up reassembling my RC2014 system, and started working on testing out the configuration.  I Was able to write a few BASIC programs to output to the video chip and to a TV I had hooked up, as you can see in the photo above.  I was noticing some weirdness though. The LEDs on the IO board were flickering when I was doing writes to the TMS, so I may need to move the port around to another location. I think I'm gonna go with the MSX setting of 98 and 99h.

I also became very aware that I do not yet understand how to get the chip to do what I want.

Emulation


I was going to write the emulator using QT Creator for maximum portability, and also using the Z80 emulation core and frameworks I created for my existing RC2014 emulators.  But recently, after thinking about it for a little bit, I realizes that 90% or the emulator I need is already out there and already very well supported.  MSX.

There was really no need to get a whole video display system working with my existing emulator when I could start with an existing, debugged, well supported emulator, openMSX, and just create a new hardware profile for the RC2014 with my system's configuration... and with some changes to support my bank switching hardware and such.

I made a branch of openMSX on github to handle any/all of the changes I've made for it, which at the moment is just getting it to build on OSX/Darwin 10.14 correctly.

I also was messing around with my good old friend, the Texas Instruments TI-99/4A as it uses the TMS9918 chip. I figured that I could experiment around with how color and graphics look.  I'm not a fan of the TI's font, nor the MSX's font for that matter, but I figured it's a good platform to get reacquainted with the weirdness of the TMS and to see how this all might look in the future, perhaps.


This is of course just a screenshot from the impressive web-javascript TI emulator at js99er.net.

More as it develops...

Tuesday, August 14, 2018

Commanding Laserdiscs

Coming soon...
For the past couple evenings, I've been poking at controlling video playback programmatically... I was inspired by Kevin Savetz' recent project to resurrect the Rollercoaster Apple ][ Laserdisc game.  He got it working, fixed it, enhanced it to work for the DVD version of the game, and was showing it off at KansasFest.

My first quick hack, as seen before on this blog, was to screen-scrape an Infocom game (Hitchhiker's Guide to the Galaxy), and based on text from that, force VLC to play back certain frames or tracks of video from the Hitchhiker's Guide TV show.

I've been poking at actually making Rollercoster be playable inside the browser for my next trick. I'm basing it all on the source code, which Kevin has made available to us, and using a HD transfer of the movie, which hopefully is the same edit of the film as the Discovision Laserdisc.

There will be more on this later, but the short version is that I'm using jsbasic to run Applesoft Basic right in the browser (no emulation of an Apple ][ ), simulating the Super Serial port (6551 ACIA chip), capturing the BASIC game's laserdisc commands being sent to said serial port, and then using that information to control a fakeo-laserdisc player, which is just an HTML 5 video player.  I'm still working through all of this.  The cool thing is that when I'm done with this, it could be a great test system for people wanting to make more LD-AppleII games.  As long as you use video files that can be properly frame-associated with real discs, it should be a drop-in kind of thing.

NOTE: if I learn more information, I'll note it in this post.  I'm really green when it comes to Apple II hardware, and really Apple II programming in general. This is really my first deep-dive into Apple II serial hardware and specifics of Applesoft BASIC!

Super Serial card

The Super Serial for this game is in slot 2, and is fitted with a 6551 ACIA serial interface chip. Writes to the ACIA get sent out immediately, they are not buffered.  The ACIA has one bit on its status port 0x08 or b00001000 that goes high when there's a byte to read.  The LDP will send a 'R' when its current task is complete.

The Super Serial card, being in slot 2 means that its four registers are at decimal locations:

  • 49320 - Data 
    • Read from here to read characters sent to the Apple
    • Write to this to send out characters to the LDP
  • 49321 - Status
    • Read from here to get error codes, 
    • Read and mask off 0x08 to get the "is a character ready to be read from the LDP?
  • 49322 - Command
  • 49323 - Control
    • These two are used to configure the port, for baud rate, parity, etc.  For this project, I'm simulating the behavior of the device at a very high level, so i basically ignore all of this stuff.
    • The real hardware runs at 4800 baud, 8-N-1
This means that to write a character (newline) out to the LDP:
  • POKE 49320,13

LDP Simulation

Anyway. there's been some interest in others wanting to simulate the Laserdisc player (LDP) using other methods (using VLC for example), so I thought I'd just do a quick post to explain my current understanding of the specific commands used by the game. You can look up specifics of the protocol in the LD-V4400 User's Manual, or in the DVD-V8000 User's Manual  All commands are two-letter sequences, uppercase (although the manual says that it accepts lowercase as well.) and can have a number BEFORE the opcode.

There are a lot of commands that these can handle, but these are the commands that we care about for this game:

FR
Go into "frame mode".  This means that any addresses will be in number of frames from the start of the disc.  Frame 0 is the first frame on the disc, the final frame is dependent on the duration of the disc.  CAV discs are about 30 minutes of content. at 29.97FPS (NTSC), that is roughly 54,000 frames.

(address) SE
Seek to the address (frame number) and go into still-frame mode.  Reading from the ACIA, 

(address) PL
Play, and go to still-frame mode when the address (frame number) is reached.

Additionally, the game's command strings use '/' to indicate a carriage return (CR) character, which is sent as decimal value 13.  The player will then execute the command, and will send back an "R" character (decimal 82) when it completes.

Let's go through a couple real examples...


"FR2818SE/"
  • Switch the LDP into frame mode
  • Seek to frame 2818, and still-frame there
  • LDP will send "R" when it has completed.
AKA: Still frame on frame 2818, will send "R" when done.


"FR2134PL/"
  • Switch the LDP into frame mode
  • From the current position, play until the frame number reaches 2134, and still-frame there
  • LDP will send an "R" when it has completed
AKA: Play until frame 2134, will send "R" when it reaches there.

"FR6726SE/FR6959PL/"
  • Switch the LDP into frame mode
  • Seek to frame 6726, and stillframe
  • LDP will send "R" when ready
  • (switch to frame mode)
  • Play until frame 6959, and then stillframe
  • LDP will send "R" when ready
AKA: Play from frame 6726 through 6959, send "R" when done.

Game implementations

The serial port configuration and setup is at line 31000.  The sequence it does is that it POKEs 11 to 49322, and 28 to 49323, which sets up the serial port configuration (baud, parity, etc).  It also does something with CHR$(4); "PR#2" or "PR#0".  I'm honestly not entirely sure what those are for, something with sending data to the serial card.

The BASIC code to send the command strings is at line 40000, and the command string is stored in VC$.  So in the program you'll see stuff like:
34000 VC$ = "FR2818SE/": GOSUB 40000 
34011 VC$ = "FR6726SE/FR6959PL/": GOSUB 40000
You already know what these do from the above examples!


The LDP communication code at 40000 is:
40000  REM  PLAY VIDEO CLIP
40010  IF  NOT DISC THEN  RETURN
40020  FOR I = 1 TO  LEN (VC$)
40030  IF  MID$ (VC$,I,1) = "/" THEN  POKE 49320,13: WAIT 49321,8:J =  PEEK (49320): GOTO 40060
40040  POKE 49320, ASC ( MID$ (VC$,I,1))
40060  NEXT I
40070  RETURN
Or, in human readable terms:
  • if "DISC" is set 0, then there's no VDP, so just return
  • For each character of the command string:
    • if the character is a '/', then send the VDP a carriage return, and wait for "R" response
    • otherwise just send the current command string character to the VDP

The code also uses the "WAIT" command, which was not implemented in the javascript library I was using.  This command essentially blocks, and waits until the conditions are met.  In the above code it essentially is PEEKing at 49321 (ACIA status) for bit 8 to be set; for a character to be ready to read in.. It then reads the character to variable J, which is then ignored. ;)  But it knows the LDP is done with the task.

Saturday, July 28, 2018

Digital Logic in Javascript...


I needed some time off from my main project right now, and I had this bug in my brain to try out some of this, so I did a thing.  It uses javascript, jquery, bootstrap, and css stuff to do the whole thing. I use some of this at work, so this was a great chance to learn a bit more about that tech.

So here's a link to the quickly and horribly named "Logicr", pronounced "lä-jik-ər".

It's essentially a tiny javascript library/set of classes that simulate digital logic.  I'm not going to start drawing out diagrams of class hierarchies because that would take too long to do.  Instead, i'll quickly explain a bit of it, and then explain what the interface is showing.

At the bottom level, there are objects that are "pins".  This is a thing that can be set with a low, high, or floating data level.  I made the level actually a value from 1-100, below 25 is "low", above 75 is "high" and in the middle is "floating".  When you read them, you get back a true/false value, and you can write the 0..100 level, or true/false.  I made it this way so that i can eventually simulate pullups/pulldowns in the circuits.

Those pins are included in "nodes", which have a collection of inputs and outputs.  One example of a node is a logic gate.  A typical gate has two inputs, A, B, and one output, Y.  When the nodes get their update() call, they do whatever math is necessary to read in from their inputs and set their outputs.   Currently the system supports the basic 1-2 input logic gates: Not, And, Nand, Or, Nor, Xor, as well as one-pin output "sources" that generate High, Low, and Float output.

Another node is a "clock".  This one toggles its output once a second (1000ms) between high and low levels.

Another type of thing in the system is a "wire".  Wires are just lists of connections "from" and "to" pins on nodes in the system.

In the above example, there's a clock node (XTAL1), whose output Y goes to the first LED, named "clk".  There's a second wire from XTAL1's output that goes to the input of a Not gate (Inverter), in-turn, whose output goes to the second LED, "!clk".

You may notice that they don't change instantaneously.  That's because the system is set up to update all of the nodes first, then update all of the wires.  This prevented having to actually generate a real directed graph, and deal with loops and recursion and loops and recursion and stuff.  It probably simulates propagation delay on real parts, but i'm sure it has its own unique quirks.

So every 50ms, it goes through and tells all of the nodes to do their math, then immediately goes through the list of wires, reading from their FROM and writing to their TO.

So for the clock circuit:

  • Nodes:
    • Clock XTAL1 running at 1000ms
    • LED D1, blue, labelled "clk"
    • LED D2, blue, labelled "!clk"
    • Not gate IC1
  • Wires
    • XTAL1.Y to D1.A -- connects the clock to the first LED
    • XTAL1.Y to IC1.A -- connects the clock to the not gate
    • IC1.Y to D2.A -- connects the not gate to the second LED

That's the basics of it.

But there's more.

I added a few input devices and output devices.  First is the switch.  The switch is essentially a html checkbox at its core.  When the switch node gets called to update, it reads the value directly out of the checkbox widget in the browser, and sets its output pin to the appropriate value.  Similarly, the LEDs are just a "div" that's made round and the right color (dim/bright red, etc) via CSS.  When the LED nodes are updated, they read the value from their input pin, then apply the appropriate "on" or "off" class to their html div.  The browser and CSS take care of the rest.

There's a couple other things happening in the circuit there too...

The amber switches are the A and B inputs to an XOR gate.  The A and B switches also go to the amber LEDs with the same labels, so that you can see what's going on there.  The output from the XOR gate goes to the green LED labelled Y.

The four red switches labelled 0x01, 0x02, 0x04, and 0x08 are run into a sort-of ROM, implemented as just a simple 16 value lookup table.  The ROM has 4 inputs and 8 outputs.  The 8 outputs are wired to the red seven-segment display node's "a"-"g" inputs, which light up the segments of the display on the web interface.  As you toggle the switches, the appropriate number is displayed.  The ROM data is basically a lookup table of which segments to drive for each value 0x0..0x9,0xA-0xF.  The equivalent of the 74LS47 ic chip.  I was originally going to build this circuit using logic gates, but it got really complex...  I may still do it eventually, once i have a better editor.

Mess with it...

In the interface, if you scroll down, you'll see an editor section at the bottom. Press the "Load Default" button to fill the text box with the circuit description that's running, and you can see all of the logic nodes and wire connections. Press the "Run Circuit" button to read in the JSON content from that window and rebuild the internal logic to use it.  The content there has to be valid JSON, otherwise it won't work.  The "Run" button will have a green border when it's valid, red when it's not.

Future

So yeah. This was meant to be a quick test to see how well it could quickly be done, and expanded upon for a possible future project that I've been thinking about for a while. There are still more component classes I want to add, such as "packages" which can group together circuits... for example, creating flip-flops using gate logic, or building that 7-segment decoder, or more complex things...

I also would like to make an in-browser editor of the circuits.  I've been looking at jsPlumb as well as a few other solutions for doing "create movable boxes with connection magnets that are connected with wires" type of things.  I may just spin up something of my own, as having draggable boxes is fairly easy to do using CSS/JS and then connections are just SVG lines between them, etc...  Although i'm sure the nuances of the interface make it more complex than it seems...

Oh. and this is running in my tiny page thingy, llmin... It's basically a wrapper for a page that sets up jQuery, bootstrap, and some other stuff, so that I don't have to create that boilerplate every time I want to make a page to test stuff. ;)

Monday, May 1, 2017

RC2017/4 Final! (Replica Results and Post-Mortem)

"The Treachery Of Pop Pixel Art" - Scott Lawrence, 4/30/17
Inspired by Andy Warhol and René Magritte's works
As you might know, I spent time this past month working on what I nicknamed "The Andy Project". The idea was to re-create some of Andy Warhol's art pieces on an Amiga, well, an emulated Amiga, using appropriate tools, as pixel-accurately as possible.

This blog post will be a summary of results, mentions of pitfalls, and that kind of thing.  The previous three blog posts cover most of the other information for the project.  Links for these are on the bottom of this post.

Philosophical musings...

Andy's work is interesting because you don't see any other of the "classic" artists doing their work in such a media, where you can get pixel-for-pixel copies of the originals.  Nowadays, sure, you get photographs of the original, but with these digital art works, you can get essentially the EXACT same content that was originally produced. This is likely why the "originals" have never been made public directly... Understandably so.... That floppy gets out, and we see posters of pop-pixel-art available in every mall. That said, I want to make poster blow-ups of some of these. ;)

This actually got me thinking a lot about the resulting media files that I will be produced.  These will be virtually indistinguishable from the originals.  Are they the same as the originals?  In some of these, I am the person who drew all of the individual pixels in Deluxe Paint.  It is distinctly not the original files, which are still under lock-and-key.  But those may surface, and you may find that mine are nearly identical with respect to which color pixels are where on the digital canvas.

In the music industry, if you were to make a copy of a waveform, by looking at numerical representations of the wave, and typing them in to a computer, making a replica of the original waveform.  When played back, it's still Debbie Harry's "Rush Rush"... but you actually created it from scratch by copying the numbers from the original.  It's a replica... that is functionally and actually identical to the original.  In fact, if you were to write a computer program that did this for you; go through the audio file, and look at the numbers, then write those numbers into another file... then the computer has generated a replica of the original... That's exactly the same as I'm doing here, right?

That software already exists.. it's "cp", "copy" or "drag-and-drop the icon to another folder."

Does that mean that I cannot take credit for my reproductions?  This is kind of why I had wanted to add specific mistakes/changes to mine, so even if you were to compare them at the pixel level, mine would be at the least, a derivative work. ... but is that enough?  Couldn't Andy's estate come after me and say "that imagery is exactly the same as the original, and you're putting it up online for free! Stop that now!"

I know that mine are replicas...  perhaps that's enough.

But then, if you think about the pop-art movement of the '50s and onward, it delves DIRECTLY into this, head on.  Andy, for example, took iconic things (soup cans, Marilyn Monroe) and used them as the basis for his iconic works.  If the Campbell's Soup can looked different, so would have his artwork.

So in that vein... My reproductions are essentially my brand new art pieces, just using his pieces as the inspiration...

Wait.. are mine replicas of his works?  Are mine new works, like the originals under the pop-art umbrella?

I don't know how to resolve this.

The Andy Project

I started out with a list of 11 images to pick from.  I thought I might get a few done, but I never set specific goals for myself... so I only got three of them done.  I still feel like this is a major success. Mainly because I can now do many of the other ones at my leisure using all of the tools and frameworks I've already set in place, with full knowledge of the pitfalls for the various steps.

I also created two pieces that I was inspired to make from the project, so I included those in the ZIP and slideshow ADF as well.  Links to the slideshow ADF downloads are at the bottom of this blog post, along with an in-browser emulator that can play it in-browser.

Everything provided here is to be considered to be Creative-Commons BY-NC-SA. (Attribution, Non-Commercial, Share-Alike).

What went wrong

I think the big thing that went wrong was wrangling with tools. There are two instances that come to mind.
Nothing really went wrong here, I just thought it would almost
be appropriate for this to be my end-state of this image. :)
I feel like Andy might have accepted this.

ObeseBits

First of all, my tool "ObeseBits" was giving me trouble.  It's the first Javascript-Processing app I've ever really made.  Most of the time for Processing, I just use Java mode.  That seemed a bit excessive, and I wanted to make something I could easily share, so I went Javascript instead. 

If you watch the time lapse, you may see bits like this roll through.
The frustration was real.
This is a fine choice, but I had a problem where... I'm not exactly sure what was going wrong, but I was re-drawing the entire screen every frame.  It seemed to be chewing on more RAM that it should, especially after it sits for 5 minutes... especially running in Chrome on a 8 year old mac with 2 gig of RAM.  Yes, I see the irony here that I'm creating these images on a machine that could do this with 1/2 megabyte of memory... hehe  Anyway, it would get so unresponsive that it would essentially lock up the machine, but it always seemed like it was JUST ABOUT TO wake up.  But no... spinny rainbow pizza instead...  Force reboots were my friends.

FS-UAE emulator

The other main issue was that I couldn't get the ADF file exported from the emulator I was working in!  Seems like a pretty straightforward thing to do:

  1. Mount an ADF disk image
  2. Diskcopy Amiga Workbench 1.3 to it
  3. Make space by removing printer drivers, fonts, utilities, etc that aren't needed
  4. Copy my "Files" folder from the virtual hard disk to the virtual floppy
  5. Set the startup-sequence to just add disk buffers and then run the slideshow (DPShow)
  6. Quit out of the emulator
  7. The ADF disk image file you have there has all of the above done to it, right?
            RIGHT?

WRONG
.  I was using FS-UAE that, because of reasons that kinda make sense, treats all mounted ADF disk images as read-only, and creates an "overlay" in a separate directory with all of your changes.  So... here's the list of things I tried to get the modified ADF out of the thing:

  1. "apply" the changes to the ADF.
    • Can't do this.  The change overlay is in a custom format.
    • No tools exist to do this. (!!s)
  2. Turn on the UAE emulator flag "writable_floppy_iamges=1", which tells the emulator that it's okay to write directly to disk images for new disks.
    • Then you must mount a new disk, once this is set, diskcopy the content over, and you'll end up with an ADF with all of these changes. Right?
    • WRONG. FS-UAE ignores this flag.  There is literally NO WAY to write to an ADF that I have found directly from FS-UAE.  (let it be noted: otherwise, FS-UAE is freaking awesome.  this just really frustrated me.)
  3. At some point in here, I needed to edit the startup-sequence on the ADF, and I couldn't do it via the emulator's mounted-folder-as-disk-image, so i grabbed "Vim" from Fred Fish disk 591.
    • This required ARP for some reason, so I grabbed ARP and ran the installer.  
    • It replaced a bunch of AmigaDOS commands, added a library, etc.
    • Vi now worked.  Yay!
  4. Make a disk image to a file within the emulated Amiga.
    • Sounds ridiculous, but in fact, yes... it is ridiculous.
    • The only one I found that "should" work in AmigaDOS 1.3 was "TransADF"
      • Grabbed TransADF off Aminet, put it on my "Work13" disk (which is a mounted folder from my Mac). 
      • Ran TransADF, and... GURU MEDITATION
    • Fumbled for a while and realized I could boot an emulated AmigaDOS 3.1 system.
    • Booted up AmigaDOS 3.1, ran TransDisk, created the ADF, looked good.
    • Booted back into 1.3, and suddenly, on "loadwb", the system just locked up with a blank blue screen.
    • Eventually traced it back to ARP being an issue.
    • Luckily, my Work13: disk was being maintained by Git, so i just reverted the changes that the ARP installer did... and now it all worked again.  blerg.
So yeah.  Cumbersome as all get out, but I managed to get the ADF made, and I now know I can just boot into 3.1 and run TransDisk to do this again.

What went right...

I got three image replicas done! YAY!

I feel super proud of the fact that I was able to do it to the best of my abilities, to the best effort I could provide and do.  I tried my best to be pixel-perfect, to be color-accurate, and I think the results paid off really well!

You can see me work on these in a time-lapse on YouTube too!

What you see here are EXACTLY what I was doing this project to avoid.  Incorrect aspect ratio, wrong file format, etc. ;)  But they show the effort. I  left the Deluxe Paint panels up just for fun, and to show that these are in fact the ones that I made, and not the source Warhol material.  If you want the proper files, they're downloadable at the bottom of this file.

"campbells.pic" (As seen in Deluxe Paint III)
Original by Andy Warhol, Replica by Scott Lawrence

"Campbells" took full advantage of the foray into GraphiCraft, as I used the palette generated from the saved out Graphicraft 1.2 image, but then tweaked by hand to accurately match the pre-release palette that Andy would have had available to him.  I feel like this is a great representative pice of his work on the Amiga, as well as for this project itself.
"venus" (As seen in Deluxe Paint III)
Original by Andy Warhol, Replica by Scott Lawrence

I had originally thought that "venus" was going to be pretty simple. It was mostly the "venus.iff" file that is included with every copy of Deluxe Paint.  (Here's a link to the original as seen in Deluxe Paint IV.) Well, there's those two orange dots on the left, the scribble on one eye, and the third eye... so it should be easy.  Right?  Nope.  

  1. The source material that I had was a JPEG, with its entire color palette completely wonky.  Browns looked violet... whites looked yellow... that kind of thing
  2. This means that I had to spend time not just picking the right color, but also cross-referencing the pixel in the jpeg with another of the same wrong color, but unchanged by andy... then find THAT color in my DPaint version, and use that color.  
  3. The forehead.  I don't know how... I don't know why, but Andy replaced MOST of the forehead with the version that's there... He probably just grabbed a brush and pasted it down a lot. There's some evidence of this being the case along the left side. Perhaps I'll analyze it more for another blog post in the future.  I ended up having to wipe out the entire forehead and re-do it entirely.
I do feel it necessary to note that many of the pixels in this are "close".. not specifically the exact correct shade of the colors.  There are a lot of orange/browns that are VERY similar and with the process of cross-referencing mentioned above, I know that I made a few mistakes here and there on this one.

I had felt a bit uneasy looking at this one for a while, but couldn't place exactly why... but it turned out that the forehead's replacement was a huge subconscious "something's not right here" flag in my head that I couldn't quite place. 
"flower" (As seen in Deluxe Paint III)
Original by Andy Warhol, Replica by Scott Lawrence

This one looked like it was going to be fairly straightforward to make... and it in fact was!  There were a lot of stepped lines, and the source material was scaled up and fuzzy, but I was able to work around this.  I remembered that DPaint has a tool "Coords" (press | to enable) which shows where your cursor is in X and Y, and also length of lines you're drawing.  This took was invaluable for doing this replica.

Notes...

Yes, I did notice that "Flower" and "Campbells" are missing the red rectangles in the upper right of the imagery on the provided ZIP and ADF files below..  Those are the screen raise/lower tools from AmigaDOS shining through.  Much like the "GraphiCraft" drag bar being visible, those should also have been visible. That's my mistake. The next version I cut of the slideshow will have that fixed.

Oops!

Links!

Here's a list of links of products and stuff that may be interesting:
Everything provided here is to be considered to be Creative-Commons BY-NC-SA. (Attribution, Non-Commercial, Share-Alike). 

And previous blog posts about the topic:

Friday, June 24, 2016

The RC2014 Computer: 1. Emulation


As you may know, I'm bigtime into Z80 computerey stuff.  For the past 20 years or so, I've been hacking Pac-Man ROMs, been maintaining the Ms Pac Disassembly, and have made my own Z80-Pac based programes over the years.

Fairly recently, I got a Kaypro II from a friend at interlock, and it worked perfectly, and looked brand new.  I felt like I couldn't hold on to it... "it belongs in a museum!" ...so I donated it to ICHEG/Strong Museum of Play.  But it helped whet my appetite for a CP/M computer.

Another project I've been wanting to do was to start with a Commodore 64 (I know it's not Z80, it's 6502ish), a floppy drive, some blank disks and a hardware manual and code up, from scratch, an OS.  Start out by making a text editor, assembler, GEOS-like GUI, etc.

These projects recently had an opportunity to overlap, and they all seem to converge on the RC2014 modular Z80 computer.

The RC2014 computer is a backplane-based modular computer created by Spencer Owen, based on the Z80SBC by Grant Searle.  There are modules for the CPU, RAM, ROM, Serial Terminal interface, and so on.  It is available as a kit from tindie.com.  Once assembled, you hook up a serial terminal to it, power it on, and you get a 1980s-esque BASIC prompt onto which you can write your 32kbytes of program.  This is based on Grant's simplified Z80 computer, so there is no off-line storage.

My general plan for the RC2014 is:
  1. Emulation:
    1. Create an emulator for the system to aid with rapid development
      1. also bring my "bleu-romtools" from Google Code to github
    2. Add a serial-based storage solution to the RC2014 emulation to confirm proof-of-concept
    3. Add ROM swap out to the emulation
    4. Add 32k of ram to give a full flat 64k of ram to the emulation
  2. Hardware:
    1. Get a RC2014 kit
    2. Build the RC2014 kit
    3. Make a test ROM to run on real hardware to verify my toolchain is working
    4. Create a new serial card that sits at port 0xC0 (second serial)
    5. Create the SD Drive firmware for the serial arduino
    6. Hack the ROM and Digital IO boards to allow for disabling the ROM
    7. Add 32k hacked RAM to the system
  3. Name: RC2014/LL
    1. At this point, the architecture is different enough and well defined enough that I think a new name for this configuration is in order. I call this configuration "RC2014/LL".
  4. Port CP/M
    1. Create the BIOS
    2. Create the sector-based emulation layer in the SD drive
    3. Boot CP/M
    4. Play Zork


I started out by making an emulator using the Z80 Pack emulation system.  Once I got this running, I realized the limitations of this emulator and looked around and found another emulator that suited my needs better. (I wanted a way to "swap" memory around, which Z80 Pack would not do in a way that wasn't a major hack.)

I created a layer that adds disableable memory regions, and added emulation of the 6850 ACIA serial chip, and threw the 32k RAM BASIC ROM at it, and it started right up, running BASIC!

I added a second 32k of RAM (easy to do when you're emulating it!), and started creating the SD interface, also using the 6850 ACIA for communications.  I then added a port, emulating the IO card, on which bit 0 (0x01), when set, will disable the ROM... So any reads to the low area of memory will read from the ROM.  Whether this is set or not, all writes to that area of memory will actually happen to the RAM... they will just be hidden from reads until that bit is set.

I now have the basics of the RC2014/LL system emulated in software!  I created a boot/diagnostic ROM which can be used for all RC2014 systems which can probe memory to determine type (ROM, RAM, unpopulated), peek and poke memory, In and out IO, and other utility functions for the SD card interface.

Currently, I'm writing the SD card API which I will port to the Arduino Leonardo and SD breakout board which I have ordered, once those come this weekend, I'll shove them into my own serial board and burn a test ROM and see how it goes....

Monday, February 1, 2016

A (mostly) Finished 6502 LlamaCalc(ulator) (RC2016/1 Post-Mortem)


February 1st sees the end of RetroChallenge RC2016/1.  My entry for this month was to create a calculator for the Commodore/MOS KIM-1, by way of 6502 and the KIM-Uno emulation project.  I wanted to have a working somewhat-calculator running on the system, but more importantly, I wanted to learn 6502 assembler.

So let's see what my goals were for this RetroChallenge, as I set them out at the beginning of the project:
Starting today, I'm going to attempt to better learn 6502 asm in my copious amounts of free time for the  RC2016/01 Retrocmputing Competition.  To prepare for this, over the past year I've gotten into working with Oscar Vermeulen's awesome KIM Uno kit, as well as pushing out my own updated firmware for it in the form of my Kim Uno Remix project on github. 
...
For the challenge, I want to use this system to make a simple integer programmer's calculator which I can run on the KIM Uno itself.  Press keys to shift in the nibbles, then switch it into a mode where i can affect the data.  Convert hex to decimal, do bitshifts, add, multiply, etc.
In short, even though I didn't accomplish everything I outlined here, I feel like I was completely successful in the project.  The calculator application is incomplete according to the above feature set, but that wasn't really the goal of this whole thing. I wanted to learn 6502 Asm, which I did. (I didn't finish the BCD to HEX conversions, nor did I implement multiply/divide math functions.)

What were my problems?

I think that one thing that held me back was getting my head around doing multibyte math with only a carry bit. For some reason I got it into my head that this wasn't enough, which of course it is.

Another thing that kept me from getting everything done was that I spent a lot of time to understand the BCD/Hex algorithms.  The code that I used was ultimately very similar to sample code online, but I decided that I really wanted to understand how it worked, so I didn't put it in until that was true.

And of course, just the general lack of time because of various other things including: my daytime job, playing with my kid, two contracts to work on at home, being sick, etc.

What did I achieve?

Over the course of the month, I learned a lot about how to work with such a limited set of registers.  I came from Z80 world where you have a bunch of 16 bit registers.  6502 has one 8 bit accumulator (A), two 8 bit indexing registers (X,Y) which each can only be used for certain operations.

Most everything, seemingly, is done by interacting with memory locations, specifically those in the "zero page". The 6502 has this idea where the 16 byte address's top byte is the "page" of memory.  the memory in the zero page would be bytes from $0000 through $00FF.  This is generally used for OS and general use variables, etc since there are small opcodes specifically for working with them.

I'm getting into too much detail. I'll instead outline all of my accomplishments for the month...

  • Learned 6502 ASM
  • Improved the "KIM Uno Remix" Desktop application (QT for portability)
    • Added a memory snooper
    • Better graphics palette
    • More speed support
  • Learned indexing (using X and Y registers)
  • Wrote the LlamaCalc input routine 3 times, learning 6502 opcodes better each time
  • Came up with a decent user interface for LlamaCalc that's somewhat learned-intuitive
  • LlamaCalc features implemented:
    • Display/UI states for LlamaCalc (Splash, Result, Menu, Error)
    • centralized interface for doing math functions, error handling, etc
    • 8 level stack of numbers to be used (changeable at build time)
    • Push/Pop stack functions
    • Hexadecimal to BCD conversion
    • bit shift left by one bit
    • bit shift right by one bit
    • 24 bit addition
    • 24 bit subtraction
  • Designed an RLE compression scheme for graphics
  • Added RLE decompressor to the source code projects
  • Played with optimizing screen display
  • Oh yeah, created a full repository for 6502 code, with libraries etc. on github
  • Every time I learned something new, I created another project in the Projects6502 repo

So yeah. I feel like i was successful...


I will soon have a walkthrough of using LlamaCalc using a KIM-Uno device.

Here's the source code for everything:

Monday, January 25, 2016

6502 - 24 Bit Math and a little BCD (RC2016/1)

I decided that another experiment/lesson to do on my way to making my calculator app was to learn how to do multibyte math and possibly experiment with BCD/Decimal vs Hexadecimal. (Or as Mark Watney calls them "Hexidecimals".

I kinda like breaking down this project into multiple "lessons" as it were.  It makes me feel like I'm following along lesson plans in a book.  Perhaps I should go the other way around and actually make the book I would be following if I were following a book to make this thing.

The code for this can be found in my github repository.

I broke down the application into a few main steps:

  1. display the last result
  2. add together the two previous results
  3. store that sum into a result variable
  4. repeat
When broken down further, we see that we also have to have some method for "kickstarting" it, as it were, since the first two numbers in the sequence do not follow the standard fibonacci sequence. (quick reminder: each value in the fibonacci sequence is the previous two values added together. very simple.  So, for the first two values, there is no "previous two" so they are just hardcoded as "0, 1"

Computation of the sequence can be described as :
  1. hardcoded "0"
  2. hardcoded "1"
  3. use algorithm to sum previous two values
  4. same as 3
  5. etc
For doing the math, I wanted to have variables that mimiced the 3 bytes we are able to display on the KIM, so I use 24 bits (3 bytes) to store them.  I broke down the math functions to be generic in that they can perform using two variables ("i" and "j") and store their result in a third variable "RESULT".  From there, additional functions were created to move the values around between them.  For example, we need to "roll" the values through if we want to make this repeatable. So the computation sequence can be described as:
  1. RESULT, I and J all set to '0'
  2. refresh display RESULT "0"
  3. RESULT gets "1"
  4. refresh display RESULT "1"
  5. shift the values through:
    1. J gets I's value
    2. I gets RESULT's value
  6. add:  RESULT gets the value from adding I and J
  7. repeat at step "4"
And this is basically the procedure as seen in the source.  

The multibyte addition was actually a lot simpler than I thought it was going to be. My first thought was "how could this possibly work if i were to add like 100 to 100... you end up with "2" for the carry instead of "1"."  Obviously, you can see the error here, but for some reason this got stuck in my head and suddenly, all of the multibyte (16+ bit) math seemed near impossible to deal with.  I think it was the multiplication that seemed hard, but when you break it down as multistep additions instead of multiplications, it all makes sense.  I blame this on the cold and fuzzy head I have right now.  I'm just not thinking right... also extra time at work... sure... and um... an ARP storm.  all contributing factors to not thinking clearly. ;)

The basic procedure for doing multibyte math is to observe the carry bit.  The carry bit is set when math on two 8 bit values exceeds the 8 bit container.  If you think of it in decimal, when you add 1 to 9, you get "0" with a carry of "1" which ends up in the next digit space, resulting in a "10".  So if you were to add 99 and 04, you end up with 03 with a carry of 1, resulting in "103".  Math on the 6502 is no different, other than we're (probably) using hex where the value can go from 0-9,a-f rather than just 0-9 for each digit.  The math for addition is basically:
  1. for each byte (starting from the least significant on the right)
    1. add one byte to the other, with the carry bit from the previous byte
    2. store that result in the RESULT
Or, more precisely
  1. clear "Carry" (Carry = 0)
  2. register A gets I0 (A = J0)
  3. add j0 to A.  (A = A + J0 + Carry)
  4. store the result in RESULT0
  5. A = I1
  6. A = A + J1 + Carry
  7. RESULT1 = A
  8. A = I2
  9. A = A + J2 + Carry
  10. RESULT2 = A 
I think you can see that this can be carried out indefinitely for multiple bytes.

The "display the result" was pretty straightforward as well.  The "RESULT" bytes were stored into INH, POINTL and POINTH, and then the SCANDS function is called. This refreshes those three values out to the KIM's LED display.  Then a call to GETKEY stores the current key press value into the accumulator register.  If nothing is pressed, this fills A with $15, or KEY_NONE as I have it defined.  Then it just sits in a tight loop refreshing the display and waiting for any key to be pressed.
  1. refresh display
  2. check for key press
  3. no key press? repeat at 1
  4. return
So the end result is a program that advances to the next sequence number each time you press a key.

When it fills all the digits, when we get a "carry" on the third digit while doing the math, i display "EEEEEE" as a cheesy error display and wait for a press.  When something is pressed then, it resets and starts all over agian.


As for BCD, I basically have run the code both in BCD (decimal) mode and hex mode, just to see how it works out.  Turns out i was worried for nothing,  It all 'just worked' fine in both modes.

So yeah.  My throat is sore, and I'd love to just go to sleep right now.

Thursday, January 21, 2016

6502 - RLE Image Renderer (RC2016/1)


I finished up my RLE (Run-Length Encoded) image renderer last night.  It would have been much simpler but there were a few things that I wanted to deal with to have proper full support for sprite placement and large image rendering.

The basic concept of RLE is that instead of storing just a series of pixel colors, we also store the number of times each pixel is repeated.  As described in the previous post, we know that this hardware uses the lower nibble of each byte to store the color number.  We will use the upper nibble to indicate repetitions as well as other commands, which we'll get to later...

Using '0' for the number of repetitions makes no sense, so it will never be used when the image is encoded. (repeat "red" pixels 0 times? nope.) So we'll use '0' in the top nibble to indicate commands.  A few commands that we will need are:

$00 - End of image (stop rendering, return)
$0F - End of line (no more pixels on this line, start over vertically down one pixel from the start of this line)

Which leaves $01 through $0E, which we will use as a "skip".  Advance the screen position, but do not draw any pixels to the screen. We can use this to allow images to have "transparency".

One thing to deal with was that after 255 bytes (at most), the referencing will go into another bank.  If everything fits in one bank, that's fine, but the screen itself is 4 banks, so this was something that needed to be addressed.  (HA! Addressed! I'm hilarious!)  If this isn't dealt with, and we only are incrementing the lower byte of a two byte address, we'll just keep reading (or writing) forever inside of one bank. $41FE, $41FF, $4100, etc  rather than $41FE, $41FF, $4200, $4201 ...

So basically instead of just incrementing the screen pointer by one, indirectly using
    inc IMGPTR    ; will wrap around inside a bank. bad.
I instead had to add a '1' to it, then add the carry bit onto the high byte of the value.  I need to take a step back here.  The 6502 only really has grasp of 8 bit (one byte) values.  It can use 16 bit values for addresses, stored as two bytes, but all math functions happen on the one-byte scale.
    clc          ; clear the carry bit  (Carry = 0)
    lda IMGPTR   ; A = *IMGPTR
    adc #$01     ; A = A + 1 + C
    sta IMGPTR   ; *IMGPTR = A
      ; at this point, the carry bit is either set or not,
      ; so we will add 0 into the next byte with carry
    lda IMGPTR+1 ; A = *IMGPTR+1
    adc $#00     ; A = A + 0 + C
    sta IMGPTR+1 ; *IMGPTR+1 = A

Why use RLE? A couple reasons.  First of all, it will save ROM space.  The RLE encoded (compressed) images should take a bit less space inside the rom.  An alternate we could do is to store color data in both nibbles of the byte, then just shift them out to the screen.  We would lose the ability for transparency, but you're guaranteed 50% space savings with the system we have here.

The full source code for this project is over at github.

The image shown at the top of this post shows three sprites stored in the ROM.  They were hand-encoded from graph paper sketches of various sources.  The rainbow was just coded by scratch to test out everything.

The red ghost is obviously borrowed from Namco's "Pac-Man" arcade game.  The mouse is borrowed from Nintendo's "Goonies" arcade game. Both are used for educational/demonstrative purposes here.

Wednesday, January 20, 2016

6502 Learning (RC2016/1)... Video buffer sidetrack...

I got a little sidetracked while working on the KIM-Uno calculator, playing with video buffers. I had added a video buffer to the desktop Kim Uno Remix project. Ultimately, I want to make a compressed image decoder and viewer.. to draw sprites to the screen or full-screen images.

I've written stuff like this before back on the Z80 for Pac-Man hardware, so I thought it would be a fun exercise to see how something like this would be implemented on 6502. It gives me a good chance to learn addressing modes and methods for this architecture... which is very different than Z80's.

You can attempt to play along by using this web-based assembler system. I based the video buffer in KIM Uno Remix on this system.  There are a few differences though...

6502asm.com:

  • 32x32 pixels
  • 16 colors
  • Commodore 64 palette
  • starts at $0200, continues horizontally then down, starting top left
  • one byte per pixel
  • bottom nibble indicates the color ($00..$0F)
  • top nibble is ignored
  • code starts at $0600
KIM Uno Remix:

  • is 32x32 pixels
  • 16 colors
  • Modified Deluxe Paint palette
  • starts at $4000, continues horizontally then down, starting top left
  • one byte per pixel
  • bottom nibble indicates the color ($00..$0F)
  • top nibble is ignored
  • code starts at $0200
Here's the output from a small program (shown below) that shows off the palettes of the systems. The KIM Uno is on the left, and shows the very reasonable "rainbow" palette.  The one on the right shows the more convoluted "Commodore 64 palette" of the web tool.


The colored sections are 8 rows of 32 pixels across. Since there's only 16 colors, the color stripes get repeated twice along the horizontal of the screen.

The code to run the above was essentially identical on both systems but there are some tweaks to accommodate the addresses and some minor differences between the CC65 tools that I use and the web-based tool.


Here's the source code listing used for CC65, which generated the image on the left above.  You can see that it writes to two of the four banks of memory space, at $4000 and $4200, while not doing anything with the $4100 and $4300 banks, which is why we see two segments of stripes, and two segments of black in the above image. It is a very simple program that simply increments "X" and writes it to videobuffer[x].


And here's the source for the web-based tool.  I colorized it to match the above CC65/KIM Uno Remix listing.  Notice that the program is the same, although it uses $0200 and $0400 for the screen memory, skipping the $0300 and $0500 sections.  I also switched the "unnamed label" from the above code to be a label named "loop" for this one.  It apparently doesn't support that.

Monday, October 5, 2015

Arduino Ethernet Library Modification

I was recently working on a piece of test/demo code running on an Arduino that talked through a Seeed Studio Ethernet shield.  The standard Ethernet library will not acknowledge a connection back to the host code until it receives something from the client.  For most things, this works out just fine (HTTP, chat servers, etc) but in some cases, like for RFB/VNC servers, it must first send out something itself to the client.  The as-shipped library does not allow for this, so it required a minor hack to the library code to make this option available.

in (Libraries)/Ethernet/src/EthernetServer.cpp, we can see the following code:
  01 EthernetClient EthernetServer::available()
  02 {
  03   accept();
  04   for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
  05     EthernetClient client(sock);
  06     if (EthernetClass::_server_port[sock] == _port) {
  07       uint8_t s = client.status();
  08       if (s == SnSR::ESTABLISHED || s == SnSR::CLOSE_WAIT) {
  09         if (client.available()) {
  10         return client;
  11         }
  12       }
  13     }
  14   }
  15   return EthernetClient(MAX_SOCK_NUM);
  16 }

As you can see, it checks to see if there is a connection established (line 08), then it checks to see if there's any content available from the client (bolded line 09), and only then will it return the handle to the newly connected client. This will be called in your Arduino code like this (from the WebServer example, packed with the Ethernet library)
void loop() {
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // do stuff here...
    client.stop();
    Serial.println("client disconnected");
  }
}

This loop will only make and break connections if the client is actually connected AND it has data available. For most servers, this is sufficient.  However, if you want to allow for clients that connect and then just listen, you need to do the following...

In EthernetServer.cpp, create a copy of the ::available() method, eliminating lines 9 and 11 above. I call this function ::connectionEstablished(), plopped right into the EthernetServer.cpp file, just after the ::available() method, and it is implemented as follows:

EthernetClient EthernetServer::connectionEstablished()
{
  accept();
  for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
    EthernetClient client(sock);
    if (EthernetClass::_server_port[sock] == _port) {
      uint8_t s = client.status();
      if (s == SnSR::ESTABLISHED || s == SnSR::CLOSE_WAIT) {
        // "if" statement removed
        return client;
      }
    }
  }
  return EthernetClient(MAX_SOCK_NUM);
}

Next, you need to add the method to the header file, EthernetServer.h, as seen in this snippet
class EthernetServer :
  public Server {
    private: 
    uint16_t _port;
    void accept();
  public: 
    EthernetServer(uint16_t); 
    EthernetClient connectionEstablished();
    EthernetClient available();
    virtual void begin();
    virtual size_t write(uint8_t);
    virtual size_t write(const uint8_t *buf, size_t size);
    using Print::write;
};


Now, you can call this new method like this:
void loop() {
  // listen for incoming clients
  EthernetClient client = server.connectionEstablished();
  if (client) {
    Serial.println("new client has connected");
    // do stuff here...
    client.println( "Hello, goodbye!" ); 
    client.stop();
    Serial.println("client disconnected");
  }
}

Tuesday, August 18, 2015

Retro Challenge 2015/7: Post-Mortem and project notes...

Current Project State

For this Summer's Retro Challenge (2015/7), I worked on a small widget that lets you use an Amiga mouse, Atari ST mouse or Atari-style Joystick.  I was successful in building it and having it be manually configurable.  There's a shell based interface that you can use to select which input device and output style is used.

All of the code for this project as well as further and future documentation is available at the project's github repository.

There are a bunch of things that I did complete during the "challenge"...

  • Arduino Leonardo based firmware with simple direct connection to a single D9 male connector
  • Two versions of the hardware, one quick and dirty, the other optimized for the parts I had.
  • Designed to use minimal external parts - just the Arduino board and a D9.
  • Serial interface shell to configure which device it is
    • Amiga Mouse (fully tested)
    • Atari ST Mouse (fully tested)
    • Atari Digital Joystick (fully tested)
  • Serial interface to select which output the joystick provides
    • Mouse movements
    • WASD keypress input
    • Arrow Key keypress input
    • HJKL keypress input
    • etc.  (support for MAME, Stella, Vice, etc)
  • "Explorer" to experiment with input device auto detection algorithm (which does work)
  • Amiga 500 keyboard input header (incomplete)
  • Amiga 500 to Amiga 1000 keyboard adapter (to RJ-22 4P4C telephone handset jack)
  • Documented Amiga keyboard adaption 
  • Documented Amiga - Atari ST mouse differences
  • Documented other D9 joystick pinouts (and how they relate, etc)

There are two features that I did not complete before the end of the month...

First of all, I was unable to get the keyboard support working before the end of the month. I was having a hard time with it only to realize it was a power issue.  The keyboard was consuming more power than my widget could provide.  It will require a secondary power supply to give enough oomph to get it working.  I also was unable to read from it once I hacked in some power.

Secondly, I had designed an algorithm to autodetect between the three different supported input devices, but I was not able to integrate the code in before the end of the month.  At some point I'll mix it in, and it will switch the kind of device it will be based on analysis of the signals (and signal pairs) of data coming in when it detects "motion".

Next Steps

At this point, I'd like to take the codebase as it exists now, and change the program flow to do the auto-detection of the device type, and then do the "appropriate" thing with it.  "out of the box" it will just "do the right thing" when devices are plugged in. Namely:

  • Amiga mouse becomes USB HID Mouse
  • Atari ST mouse becomes USB HID Mouse
  • Atari Digital Joystick gets configured to send STELLA keypresses (arrow keys, etc)
I will implement this at some point, perhaps during the next Retro Challenge.

Sunday, January 18, 2015

KIM-UNO (Software)

As mentioned in the previous post about the hardware for my implementation of the KIM-UNO, some changes needed to be made to accommodate the updated hardware.  First, let's look at the new schematic layout for the updated circuitry:

The reference version had a 3x8 matrix, and this one uses 4x6.  I tried doing this a few different ways. I knew (or at least I thought I knew) that I needed to come up with something different for scanning the keypad since this has a common-cathode LED display. (It's what I had in my toolbox.)  I tried one version that added about a dozen resistors... It didn't work.  I tried one with a half dozen resistors, and a dozen 1N1914 diodes... that didn't work either.

On a whim, I decided to just hook it up directly and see how it goes... I tried scanning the keypad myself but it wasn't working.  I tried scanning the keypad using the Arduino Keypad library, with nothing else, and that worked fine... okay.  got something.

I then added the Keypad library to my test program that displayed stuff to the LED display, and it was broken again. Okay. so it's something between the two of them.  What I came up with is far from optimal, but it works. (The screen flickers visibly a tiny bit... which might also be due to the added 220 ohm resistors, making the display dimmer than before.)


First, I replaced the code block that handles the LED rendering in the KIM UNO program with my display routine which works well.  I've included the arduino/c code here in case someone else is crazy enough to do this.
void driveLEDs_CommonCathode( void )
{
  char digit = 0;

  for( byte i = 0 ; i<3 ; i++ )
  {
    for( byte j = 0 ; j<2 ; j++ )
    {
      // get the current byte to display
      byte b = threeHex[i][j];
   
      // get the segment mask
      byte s = dig[b];
   
      // select just this digit
      for( int d=0 ; d<8 ; d++ ) {
        pinMode( ledSelect[d], OUTPUT ); // make sure we're output
        digitalWrite( ledSelect[d], (d==digit)?LOW:HIGH );
      }
   
      // now go through and turn on/off the right segments
      for( byte col =0 ; col <8 ; col++ )
      {
        pinMode( aCols[col], OUTPUT );
        digitalWrite( aCols[col], (s & (0x80>>col))? HIGH : LOW );
      }
   
      // wait a moment...
      delay( 2 ); // necessary?
   
      // go to the next display digit
      digit++;
    }
  }
}


Next, I replaced the code block that handles the 3x8 keypad scanning with mine; It initializes the keypad lines every time through the loop.  The LED scanning gets things into an unexpected/unwanted state for the Keypad scanning.  It's kind of a kludge, but it works well enough. Once it scans the keypad, I munge the output data slightly to match up with the original code.  I filled in the keypad library to return the proper key codes.  The only one i had to monkey with was the SST toggling. Not a big deal though.

#include <Keyboard.h>
void scanKeysFourBySix()
{
  static int keyCode = -1, prevKey = 0;
  static unsigned long timeFirstPressed = 0;
  
  // scan the keys and then remap them
   int i;
  
  // first, wipe everything
  for( i = 0 ; i<8 ; i++ ) {
    pinMode( aCols[i], INPUT );
    digitalWrite( aCols[i], LOW );
    pinMode( ledSelect[i], INPUT );
    digitalWrite( ledSelect[i], LOW );
  }
  
  for( i=0 ; i<ROWS ; i++ ) {
    pinMode( rowPins[i], INPUT );
    digitalWrite( rowPins[i], HIGH );
  }
  
  for( i=0 ; i<COLS ; i++ ) {
    pinMode( colPins[i], OUTPUT );
    digitalWrite( colPins[i], HIGH );
  }
  
  char key = keypad.getKey();
  
  if( key == NO_KEY && ((keypad.getState() == IDLE) || ( keypad.getState() == RELEASED ))) {
    prevKey = 0;
    return;
  }

  // convert key to keycode (addressing in the other matrix )
  if( key == 't' ) {
    // sst toggle
    key = SSTmode==0?']':'[';
  }
  
  if( key != NO_KEY )
    keyCode = key; // fill this.

  if (keyCode!=prevKey)
  {
    prevKey = keyCode;
    curkey = key; // we're already decoded. parseChar(keyCode);
    timeFirstPressed=millis();  // 
  }
  else // if pressed for >1sec, it's a ModeShift key
  {
    if ((millis()-timeFirstPressed)>1000) // more than 1000 ms
    {
        if (key == '[' || key == ']' ) //keyCode==17) //it was the SST button
        {
          keyboardMode=(keyboardMode==0?1:0); // toggle
//                Serial.print(F("                                      Eeprom R/O     ")); 
          Serial.print(F("                                keyboardMode: "));  Serial.print(keyboardMode, DEC); 
          SSTmode=0;
          curkey=0;  // don't do anything else with this keypress
        }
        if (keyCode==18) // it was RS button // fix
          curkey = '>';  // toggle eeprom write protect
        timeFirstPressed=millis(); // because otherwise you toggle right back!
    }          
  }

}

The second half is pretty much lifted directly from the core scanKeys().

Thursday, April 24, 2014

Fixing Pac-Man - Attract mode with less burn-in


One of the problems with Pac-Man, especially for home use, is that once a coin is dropped in, or you have it in Free Play mode, it will sit on the "Press Start" button screen forever, until all of the credits are depleated... which can take a long time, especially in Free Play mode. ;)

After being inspired by the ET bugfix project (a great read, btw), I decided to attempt to fix this myself. I should note that there are other versions of this fix on the net, and I have no idea if any of those are better or worse than mine.  Mine is a total of 30 bytes changed in the ROM.


My first thought is that it switches into the "Press Start" screen after it has some credits in it.  So the first thing to look for in the Ms. Pac-Man documented disassembly is the RAM location for number of credits in the machine.


At the top of the disassembly, are a bunch of RAM locations that have been decoded by contributors. As you can see, 0x4e6e contains the number of credits in the system.  Next thing to do is to search for "4e6e" in the file, and sooner or later, I figured I'd find the right location.  There's got to be a section of code that reads a value in from this, and compares it with 0 to determine if it should stay in attract mode or show the "Press Start" screen.  Sure enough...


Here we see a snippet of code.  This is in the main attract sequence.  This block of code above draws the number of credits on the screen (at 03fe), then checks the number of credits against zero (0401-0405).  If it's still 0, it goes to 0x0413, where it continues through the attract sequences.  Otherwise, it falls through, to some code (0407-0412) that advances the game mode to the dreaded "Press Start" screen that we want to eliminate.  Jackpot!

All that I need to do now, is to patch off of this code to my own routine.  The basic thought (which did work out) is that the only way that it will switch to the "Press Start" screen is if the user is pressing start.  Once it changes to that screen, the start button will still be pressed, and the game will just start.

This means that the only way that the code from 0407-0412 will get run here is if there is not zero credits AND a start button is being pressed.  On top of this, we don't want to run that code when the user is pressing 2P start unless there's at least 2 credits.



And that is the entire patch.  30 bytes of code.

The original code will check if there's credits in the machine.  If there are, it falls through to 0407, which is where my code takes over.

It simply jumps to some unused space in the ROM, starting at 0f3c.  "attStTest" checks to see if the user is pressing P1 or P2 start.  If they're not, we jump on over to 0413, as before, to continue with the attract sequence.  If they are pressing P2 start, it does the extra check in "credChk2" to make sure that there's at least 2 credits in the machine.  If there's not, it again will jump over to 0413.  Finally, we know that we can switch the game mode to the "Press Start" screen, where our button press will promptly get gobbled up, and the game will start.

The code in "startIt" should look a little familiar.  The first two opcodes are the opcodes we overwrote in 0407 where we jumped over here to this mess.  Then we jump back to "startItR" return location, and the original code resumes at 040b, performing the game mode switch.  For clarity, I could have dropped the remaining 4 opcodes which start at 040b in here, but I decided to save space and do it this way.

30 bytes different, and the game is the way it should be... especially if your friends come over to your basement arcade, and press the credit button 12 times, or you have it set on Free Play. ;)