Sunday 26 November 2017

Snowball Sunday

As it's almost December, let's dig out the awesome Snowball Sunday for the Commodore 64 by Ash & Dave, released 1988.

I first played this on a Commodore Disk User cover disk, Volume 2 Number 2 Jan/Feb 1989.  It's on Side B of the physical disk or cdu202b.d64 if you've pulled the image from the CDU archive on the web.

Also check out the game Microdot from side A if you get chance, especially the level editor.

Snowball Sunday (Commodore 64) 1988
Commodore Disk User Vol 2 Num 2 1989

I was specifically looking for the Lou Gray SID file used on the title screen (space.sid).

From HVSC the two SID tracks are:
/MUSICIANS/G/Gray_Lou/Space.sid
/MUSICIANS/B/Beben_Wally/Winter_Olympiad_88_preview.sid

Sunday 20 August 2017

Commodore 64 High Resolution Audio Capture

In my last post I captured the audio output from an Amiga A1200 using a M-Audio ADC.  For fun, here's a few lossless recordings I made of my favourite Commodore 64 tunes.

These SID files were played on a real Commodore 64c fitted with a MOS 8580 SID chip, played from Sidplay64.  Audio  is captured from the AV DIN jack with a M-Audio M-Track MkII and recorded using Audacity on Linux at 48kHz 24bit PCM.

Google Drive Link for C64 FLAC files

Files:
C64_Cybernoid_II.maudio.flac               C64_Ocean_Loader_2.maudio.flac
C64_Deflektor.maudio.flac                    C64_Ocean_Loader_3.maudio.flac
C64_Equinox.maudio.flac                      C64_Out_Run_tune1.maudio.flac
C64_Finders_Keepers.maudio.flac         C64_Out_Run_tune2.maudio.flac
C64_Flight_Path_747.maudio.flac         C64_Park_Patrol.maudio.flac
C64_Mask_II.maudio.flac                      C64_Rebounder.maudio.flac
C64_Monty_on_the_Run_High_Score.maudio.flac  C64_Thing_on_a_Spring.maudio.flac
C64_Monty_on_the_Run_Main.maudio.flac   C64_Wizardry.maudio.flac

Sidplay2 on C=64
If you enjoyed these files, there's a project to record the whole of the HVSID collection from a real C64 called the Authentic SID Collection: http://www.6581-8580.com/
 
M-Audio M-Track MkII
 
Enjoy!

Saturday 19 August 2017

Amiga A1200 High Resolution Audio Capture

I'm working on a customer's M-Audio M-Track MkII and couldn't resist the opportunity to test it by using it to capture the audio output from the Amiga A1200.  This is the same Amiga 1200 with the Rev 1A board which I've blogged about previously.

Amiga A1200 (ignore the MIDI cable, that's for a later post)
The M-Track MkII is a 2 channel analogue to digital converter and I have it connected to a Linux PC running Fedora 26 which immediately recognised the device and it showed up as as both a stereo input and output device in Pulseaudio.  I used Audacity to record and edit the audio at 48kHz stereo, exporting as 24 bit FLAC.

M-Audio M-Track MkII
The M-Audio M-Track is a pretty awesome bit of kit and worked well under Linux.  I was even able to play audio to the device and mix between Direct (analogue) Input and USB using the front panel mixer control.  Very nice indeed.

The Amiga audio captured directly from the rear panel L+R phono sockets also sounds amazing.  Unfortunately I don't have another A1200 to compare with so I don't know how this Rev 1A computer compares with later hardware revisions.

You can download a selection of audio files in FLAC format here which are obviously quite big being lossless:

Google Drive - Amiga A1200 Audio Files

The files available are:
  • A1200_mod.chippermodphosis_maudio.flac
  • A1200_mod.fresh_house_maudio.flac
  • A1200_mod.human_invasion_maudio.flac
  • A1200_mod.milk_punch_maudio.flac
These are just a few of my current favourites which I've been using for testing and repair of this particular A1200.  Only Amiga makes it possible!

Saturday 3 June 2017

Amiga A1200 Real-time Clock Module (RTC)

The Amiga 1200 doesn't have a battery backed real-time clock fitted by default.  It's shown in the schematics as an optional item but was never installed in any of the board revisions to my knowledge.

Some trapdoor expansions have a battery backed RTC fitted but I don't have one as yet.  I opted for a small battery backed RTC that's installed on the clock header next to the Kickstart ROMs.  I went for the Individual Computers RTC as I particularly like the products Jens produces.

Here is the board...

A1200 battery backed real-time clock module

And here it is installed in an Amiga 1200 with a Rev 1A board.  This 1A board has the full width header pins installed so the board is installed using the right most pins.


A1200 with battery backed real-time clock fitted

Firing up the Amiga I was able to set the time & date and save it to the RTC with "setclock save".  AmigaOS was able to see the fitted RTC and read back the correct time/date with "setclock load".

However, the following day when I started up the Amiga, the clock had jumped back to an earlier date as shown below.  The 3.1 ROM behaviour appears to be loading the clock from the battery backed RTC module, but if it returns a default value because the hardware has reset, the OS instead uses the DATESTAMP of the boot disk instead, which in my case was the installation time of the hard disk drive device DH0.


Amiga setclock with failed real-time clock battery

As the 3V battery was brand new, I initially suspected the RTC had a failed oscillator and wasn't incrementing when the unit was powered off.  However, this is easy to test by writing the correct date/time to the RTC (setclock save) and then periodically reading it back (setclock load) and checking the OS clock with "date".  In this case the RTC was working fine but was losing it's settings when the Amiga was powered down which indicates a problem with the battery backup.

Removing the RTC board I measured good battery voltage (3V) but wasn't reading any backup voltage on the clock IC itself.  On closer inspection it appears the battery holder possibly wasn't soldered to the board properly as shown...

Re-solder the tabs on the battery holder
After the repair I was able to measure 2.4V on the clock IC (there's 0.6V drop across D1).  The Amiga now seems to remember its clock properly on a cold boot as shown.

Amiga setclock with correctly working RTC

Amiga 1200 Rev 1A Distorted Audio Output - Bonus Material

Here is some additional information related to the Amiga 1200 audio fix described in my previous posts

This is the PCB audio section with the completed modifications.  Note that R325 and R335 are on the bottom side of the board, the other modified components are on the top.

A1200 audio opamp and filter section (top)
A1200 audio opamp and filter section (bottom)

The following oscilloscope images show the audio output at the phono jacks on the rear of the unit after the final Rev 2 board modifications.  The Amiga is playing a 1 kHz test tone at maximum volume.

The first image is with the low pass filter disabled (power LED dim).  The second image has the low pass filter enabled (power LED bright).

Low pass filter disabled

Low pass filter enabled

The Amiga schematics for each board revision are available online.  For reference here are the Rev 2 schematics for the audio section (Paula) with the changes from the Rev 1A board highlighted.

A1200 Rev 2 Schematics for audio section


Saturday 27 May 2017

Amiga 1200 Rev 1A Distorted Audio Output - Part 4 (Final)

This is the final instalment of my quest to get nice sounding audio out of an Amiga A1200 Rev 1A.

Here is the same mod file captured at 0dB with the following modifications:

R331 changed to 680R
R321 changed to 680R
C331 changed to 6800pF
C321 changed to 6800pF
R335 changed to 360R
R325 changed to 360R
R345 changed to 680R

(R335 and R325 are on the bottom side of the board)

These modifications are the same as Commodore implemented between the Rev 1 and Rev 2 boards according to the schematics I have.


For comparison with previous posts, here's the 1kHz sound test with the LED filter disabled (jaggies) and enabled (smooth)...


The audio output sounds about perfect to me and I'm more than satisfied with the result.  It does appear the changes Commodore specified for the Rev 2 board onwards were well engineered.

The output level is still a little higher than the A600 (Rev 1.5 board) and could perhaps be lowered just slightly (say changing R331 and R321 to ~600R).  Also, with the optional (LED) filter switched off there's noticeably more aliasing present than on the A600.  However, I think these slight differences give the A1200 its own characteristic sound verses the other models and I rather like it.

In theory this machine should sound the same as a stock Rev 2 board but I don't have one to compare with.

That's the end of this mod.  Thanks for reading!

Saturday 13 May 2017

Amiga 1200 Rev 1A Distorted Audio Output - Part 3


Here's the same mod file being captured at 0dB after the following motherboard modifications:

R331 changed to 340R (2 x 680R in parallel)
R321 changed to 340R (2 x 680R in parallel)
C331 changed to 6800pF
C321 changed to 6800pF






Gain is significantly reduced although still quite a bit higher than the A600 (Rev 1.5 board). I'd say the playback is 99% correct and I could easily use it like this but the gain is still just a tiny bit too high for my taste.

Another notable difference between the A600 and A1200 is the aliasing present when playing the 1kHz test tone with the low pass filter disabled (dim power LED).

Here's the full volume 1kHz test tone captured at 0dB.  Note the level is now much reduced and there's visible aliasing on the waveform.


Here is the same test with the low pass filter switched on (bright power LED).  Note the nice clean waveform.


I might have another go at reducing the gain a little more and trying to clean up the default filtering to remove the aliasing.

Thursday 11 May 2017

Amiga 1200 Rev 1A Distorted Audio Output - Part 2

Here's the same mod file being captured at 0dB after the following motherboard modifications:

R331 changed to 680R
R321 changed to 680R
C331 changed to 6800pF
C321 changed to 6800pF

Gain is slightly reduced but is still very high compared to the A600 and it sounds distorted. It's significantly better than the unmodified board but still not quite right.


I think the gain is definitely reduced as I was able to record at 0dB level without having to heavily attenuate the line input to stop it clipping so badly.

AudioTest

Using the Amiga 1kHz Audio Test program (copy available here AudioTest.lha), here's the audio capture at 0dB from both the A600 and A1200 for comparison.  This is the 1kHz tone at maximum volume output with the filter turned off.  The difference in output level is clear to see.



I'll follow up with Part 3 after I've done some more troubleshooting.  I'm not done yet!

Wednesday 19 April 2017

Amiga 1200 Rev 1A Distorted Audio Output - Part 1

I've been playing the Chiperia #7 demo from Revision 2017 on the Amiga 600, which sounds fantastic.  The audio from the Left & Right phono jacks on the rear of the Amiga connects to the RGB SCART input connector on the rear of my good old Sony CRT television. 

There's likely very few people playing this demo on real hardware, especially from an actual floppy disk.  It's a similar feel to playing records ("Vinyl" in modern speak), where taking the time to write the disk and watch it load adds to the experience.

When I tried playing the same demo on the A1200 (Rev 1A motherboard) the audio level was significantly higher and the audio quality seemed distorted and over-driven.

There's a nice Youtube video discussing a known issue with Rev 1A motherboards where the audio opamp feedback resistors are too large and should be replaced with a lower value, thus decreasing the gain.  I'll try this out and post the outcome.  This post captures the current A1200 and A600 audio as a benchmark for before & after comparison.

Before, A1200 (sounds terrible!)



For reference, A600 (sounds great!)

Saturday 25 March 2017

Amiga Explorer to Linux

I use Amiga Explorer from CloanTo on the Amiga for transferring files to/from my Linux PC.

Although it's fairly easy in Linux to transfer serial data to the Amiga using transwarp and similar, using Amiga Explorer makes things easier.

Amiga Explorer has a PC client for Windows but nothing for Linux.  The Explorer program partially works under Wine but seems flaky and crashes doing directory listings on my standard Fedora 25 machine.

I've created a script that can be used as a Linux client for communicating with the Explorer program running on the Amiga.  The script supports both RS232 serial and LAN connections.  This is a command line tool, there's no graphical user interface.

I've uploaded the code to GitHub here:
https://github.com/marksmanuk/lxamiga

I have uploaded a bootable Amiga Explorer ADF disk image Amiga Explorer 6.0.  Transfer and write it to disk from Linux using transwarp as mentioned in my earlier post.  (edit: new version that boots on A500 onwards)

Example Usage (Serial):

     lxamiga -l
     lxamiga -d df0:
     lxamiga -s myfile.txt df0:Empty/myfile.txt
     lxamiga -r df0:Empty/remote.txt -w local.txt
     lxamiga -r :df0:disk.adf -w image.adf
     lxamiga -s image.adf :df0:empty.adf/image.adf
     lxamiga -u df0:Empty/deleteme.txt
     lxamiga -f df1: Empty


It's the same commands to use the LAN connection, just use the -t switch first:

     lxamiga -t -l

1. List available devices/volumes:

$ lxamiga.pl -l
Connected to host successfully at 19200
Read 996/996 Bytes 100.0%
14 entries:
 DH0:WORKBENCH           10234 kB    10710 kB 18/01/1996 20:18 RWED                  
 DH1:SIMULATOR           25628 kB    51510 kB 18/01/1996 20:35 RWED                  
 DH2:CREATE              14187 kB    95795 kB 18/01/1996 20:49 RWED                  
 DH3:DOCUMENTS           25220 kB    94095 kB 18/01/1996 21:05 RWED                  
 RAM:Ram Disk              605 kB      611 kB 25/03/2017 15:47 RWED                  
 DF0:Explorer              174 kB      880 kB 16/02/1993 15:40 RWED                  
 DF1:Blank                   2 kB      880 kB 25/03/2017 09:26 RWED                  
 :R:Kick.rom                 0 kB      512 kB 15/07/1993 00:00 R       AMIGA ROM Operating
 :DF0:Explorer.adf           0 kB      880 kB 16/02/1993 15:40 R                     
 :DF1:Blank.adf              0 kB      880 kB 25/03/2017 09:26 RW                    
 :DH0:WORKBENCH.hdf          0 kB    10710 kB 18/01/1996 20:18 RW      SEC:34 SUR:5 RES:2
 :DH1:SIMULATOR.hdf          0 kB    51510 kB 18/01/1996 20:35 RW      SEC:34 SUR:5 RES:2
 :DH2:CREATE.hdf             0 kB    95795 kB 18/01/1996 20:49 RW      SEC:34 SUR:5 RES:2
 :DH3:DOCUMENTS.hdf          0 kB    94095 kB 18/01/1996 21:05 RW      SEC:34 SUR:5 RES:2



2. Format disk:

$ lxamiga.pl -f df1: Blank
Connected to host successfully at 19200
Formatting df1: Blank
Formatting 100.0%
Finished.



3. Send ADF image to disk:

$ lxamiga.pl -s DragonsMegaDemoI.adf :DF1:Blank.adf
Connected to host successfully at 19200
Uploading DragonsMegaDemoI.adf to :DF1:Blank.adf
Sent 901120/901120 Bytes 100.0%



4. Read disk to ADF image:

$ lxamiga.pl -r :DF0:Explorer.adf -w AExplorer.adf
Connected to host successfully at 19200
Read 901120/901120 Bytes 100.0%
901120 bytes saved to AExplorer.adf



5. Read a file from Amiga to local filesystem:

$ lxamiga.pl -r :R:Kick.rom -w kick.rom
Connected to host successfully at 19200
Read 524288/524288 Bytes 100.0%
524288 bytes saved to kick.rom


$ md5sum kick.rom
e40a5dfb3d017ba8779faba30cbd1c8e  kick.rom (Kickstart 3.1)


6. Upload a file to the Amiga (over LAN):

$ lxamiga.pl -t -s ProTracker-3.15.lha RAM:
Connected to 192.168.1.200:356 successfully.
Uploading ProTracker-3.15.lha to RAM:/ProTracker-3.15.lha
Sent 97654/97654 Bytes 100.0%
 

$ lxamiga.pl -t -d RAM:
Connected to 192.168.1.200:356 successfully.
Read 168/168 Bytes 100.0%
4 entries:
 Clipboards              (dir) 28/10/2017 21:27 RWED                  
 ENV                     (dir) 28/10/2017 21:28 RWED                  
 T                       (dir) 28/10/2017 21:28 RWED                  
 ProTracker-3.15.lha     97654 07/04/2017 18:45 RWED  
            
    

I've only implemented the features useful to me. If there's interest I'm happy to add the remaining functionality from Explorer.

I originally wrote lxamiga for Linux about 10 years ago but lost the source.  The earlier version supported both serial and network connections and had a separate graphical interface front end written in Perl/GTK.

EDIT: A kind reader had the source and sent me a copy, which to my surprise was written in C++, but it's horribly broken on 64 bit machines so I won't post it here.

Thursday 9 March 2017

MIDI Commodore 64 tunes

Awesome MIDI files of a couple of my favourite Rob Hubbard tunes from the Commodore 64.  Enjoy!



Sunday 5 March 2017

Amiga disk transfer bootstrap

I was given an old Amiga A600HD in a pretty poor state with a collection of various disks.  To be usable I need to transfer Amiga disk images in ADF format between my PC and the Amiga.

I'm on a Linux PC so I followed the guide here:
https://www.area536.com/projects/amiga/transfer-disk-images-from-linux-pc-to-amiga/

First we need to bootstrap the machine by getting an ADF program (transwarp) onto an Amiga format disk which the machine can read.  We can then use transwarp to transfer other ADF disk images and build from there.

The only Workbench disk I could find was a bad copy of Workbench 2.05 (37.71) which I couldn't even copy due to bad sectors.  Fortunately it would boot into Workbench and open a CLI.

I found a 25way to 9 way serial cable and plugged it into my Linux PC via a USB to serial adapter as /dev/ttyUSB0 


1. Transfer ARexx receive script to Amiga

The version of "type" on the A600 with Workbench 2.05 (the version I have) has problems with binary files.  Use the following ARexx receive script as described here:

http://adfsender.stoeggl.com/adfsenderterminal/methods.html

On Amiga:
Go into Prefs and set Serial to 9600 baud 8N1, RTS/CTS hardware flow control enabled.  I left everything else at defaults.

In CLI:
> type ser: to ram:receive

In Linux:
You can use minicom by adding a new binary File Transfer Protocol (/usr/bin/cat) but it's fairly easy from the command line.

$ stty -F /dev/ttyUSB0 cs8 -parenb -cstopb -crtscts raw speed 9600
$ cat receive.rexxlong > /dev/ttyUSB0

You may need to send additional characters to fill Amiga serial buffer if the command prompt doesn't appear immediately.  I sent a break character from Minicom (Ctrl-A, Z,  F).

Check the file is received on the Amiga side with:
> type ram:receive


2. Transfer Transwarp to Amiga

We'll do this in the RAM disk.

Run the ARexx script with:
> RAM:
> rx receive

Filename? transwarp.run
Bytes? 21363

$ stty -F /dev/ttyUSB0 cs8 -parenb -cstopb -crtscts raw speed 9600
$ cat transwarp.run > /dev/ttyUSB0

The ARexx script knows exactly how many bytes to receive and terminates nicely.


3. Unpack Transwarp.run self-extracting archive.
Make sure file is named "transwarp.run" on the ram disk.

> transwarp.run

It should unpack the relevant files (transwarp, baudbandit.device) to the ram disk. If there's an error (file is not executable, bad loadfile hunk, etc.) the file was corrupted during serial transfer.  Try again and maybe at a lower baud rate.

To avoid having to do this initial serial transfer ever again, copy the Transwarp files from RAM: to a blank disk DF0:



3. Transfer Hombre utilities disk image to blank disk

http://wiki.abime.net/file_transfer/hombre

This step isn't really necessary as we already have Transwarp available on an Amiga format disk and can therefore transfer disk images over serial.  However, this is a useful set of utilities and boots stand-alone without Workbench.
 
Transfer the Hombre v1.01 (single disk version) ADF to a new bank disk.

On Amiga:

> RAM:
> transwarp -w ser: -b 9600 -1

On Linux:

Send the binary file as we did before:

$ stty -F /dev/ttyUSB0 cs8 -parenb -cstopb -crtscts raw speed 9600
$ cat Hombre*.adf > /dev/ttyUSB0

    0860e27d5ae3cd519a0f7740d774f3ff  Hombre_v1.01_1d_[pal].adf

Now reboot into Hombre!



4. Reading ADF images from the Amiga

As I only had a broken copy of Workbench 2.05, I then wrote a copy of Workbench 2.1 using the same transfer method above using transwarp.

As a sanity check I read back the newly written Workbench disk image from the Amiga and compared the md5sum with the original ADF file.

On Linux:
For reading Amiga disks we can run at full 115,200 baud rate.
$ stty -F /dev/ttyUSB0 cs8 -parenb -cstopb -crtscts raw speed 115200
$ cat < dev/ttyUSB0 > verify.adf

On Amiga:

> transwarp -b 115200 -s 0 -e 79

Do a Control-C to terminate cat on Linux when all bytes have been received.

7310f152b05f66bcfbffe3529a41f3fb  Workbench v2.1 rev 38.35 (1992)(Commodore)(M10)(Disk 2 of 5)(Workbench).adf
7310f152b05f66bcfbffe3529a41f3fb verify.adf

Perfect match!
I'm happy to use Transwarp for now until I can get an Ethernet connection set up and maybe use the Amiga Forever from CloneTo.

Saturday 4 March 2017

Impossible Mission, Epyx 1984

I talked about the synthesised speech in Impossible Mission provided by ESS in a previous post.  Now let's have a look at this great game itself.

Published in 1984 by Epyx and written by Dennis Caswell.  There are various write-ups in the retro gaming media so I doubt I can add anything new.  In short, it's a great game and a must-play if you've never played it before.

Let's hack it... (assume using .d64 image from well-known C64 site)

Summary of POKEs:

POKE 33697,96    - Stop the clock (1s tick)

POKE 33731,165  - Stop the clock on falling, touching a robot, using the modem, etc.

POKE 13369,137   - Add maximum (9) Snooze passwords
POKE 13382,137   - Add maximum (9) Lift passwords

POKE 41422,234 - Prevent snooze/lift passwords from decrementing on use
POKE 41423,234
POKE 41424,234 (must enter all 3 pokes)

POKE 36809,234 - Disable robot laser beams
POKE 36810,234
POKE 36811,234 (must enter all 3 pokes)

POKE 39075,234 - Remove object "Searching" delay
POKE 39076,234
POKE 39077,234 (must enter all 3 pokes)


The Long Version:

CIA2 Port A
$DD00 = $C6    1100 0110

VIC-II Bank (10) = 1  $4000-$7fff

VIC-II Mode
$d011 = $1b    0001 1011    Text Mode, Extended background off
$d016 = $d8    1101 1000    Multicolour mode on   

Lift Shaft Screen:
$d018 = $03    0000 0011    Character memory    001 = $0800-$0fff
                                              Screen memory        0000 = $0000-$03ff

 VIC-II is running in text mode.
    Screen Memory        = $4000-$43ff
    Colour Memory         = $d800-$dbe7
    Character Memory    = $4800-$4fff (Lift)
    Character Memory    = $5000-$57ff (Room)


1. Hack the clock.  Clock starts at 12:00:00 and stops as 06:00:00 when game ends.  It's displayed on the "Computer" screen when in the lift shaft screen.

Let's find the screen memory where clock is displayed.  Without doing maths, let's freeze and fill the lower half of the screen with a known value, unfreeze and let the game update the clock, then re-freeze and see what changed.

f 4300 43ff ff
m 4300 43ff

>C:4390  ff ff ff ff  ff ff ff ff  ff ff ff ff   ............
>C:439c  ff ff ff ff  ff ff ff ff  ff ff ff ff   ............
>C:43a8  ff ff ff ff  81 82 ff 80  80 ff 81 85   ............
>C:43b4  ff ff ff ff  ff ff ff ff  ff ff ff ff   ............
>C:43c0  ff ff ff ff  ff ff ff ff  ff ff ff ff   ............

Clock characters are drawn at...

(C:$dd03) m 43ac 43b3
>C:43ac  81 82 f6 80  80 f6 82 83                ........

Trace code where these characters are updated:
 watch store 43b3
#1 (Stop on store 43b3)  259 029
.C:851d  8D B3 43    STA $43B3      - A:83 X:88 Y:D1 SP:fc N.-.....  137569034
.C:8520  A5 D2       LDA $D2        - A:83 X:88 Y:D1 SP:fc N.-.....  137569034

Looks like it's the routine at $8516


Time is in zero page $d2(ss) $d3(mm) $d4(hh)

m d2 d4
>C:00d2  25 00 12                                %..

Time reads = 12:00:25

Lets test it:
> d2 30 15 05

Yes, time now displayed as 5:15:30 on computer.

We can slow down the time by altering the time delay at $83a8, e.g. with:
 a 83a8
.83a8  cmp #$ff
.83a8

Or we can stop the clock completely with:

(C:$83cb) a 83a1
.83a1  nop
.83a2  nop
.83a3 

(POKE 33697,96)


2. Stop the clock decrementing on falling, touching a robot, using the modem, etc.

watch store d3
WATCH: 1  C:$00d3  (Stop on store)
(C:$dd03) x
#1 (Stop on store 00d3)  266 036
.C:83c5  85 D3       STA $D3        - A:10 X:FF Y:5A SP:f9 ..-.DI..  872526978
.C:83c7  C9 60       CMP #$60       - A:10 X:FF Y:5A SP:f9 ..-.DI..  872526978
(C:$83c7) bt
(0) 96f2
(2) 860d
(4) 7288

Looks like routine at 83c2 adds minutes to clock from A register.
Fix for all cases, stop $83c2 routine from adding to minutes counter $d3.

a 83c3
.83c3  lda $d3
.83c5 

(POKE 33731,165)

2. Change the number of Snooze and Lift passwords.

We already know from earlier that the ':' character is $f6 and zero is $80, so we're looking for f6 80 in the previous line of screen memory.

(C:$4425) m 437d
>C:437d  f6 80 53 71  6e 6b 79 53  6e 73 6e 79   ..SqnkySnsny
>C:4389  78 f6 80 2b  2c 62 63 2d  62 63 2d 62   x..+,bc-bc-b

Snoozes = $437e, Lifts = $438b

Trace where this gets written when computer display is opened...

(C:$4425) watch store 437e

.C:801d  99 76 43    STA $4376,Y    - A:80 X:FF Y:08 SP:fd N.-..... 1283098714
.C:8020  B9 C1 35    LDA $35C1,Y    - A:80 X:FF Y:08 SP:fd N.-..... 1283098714
(must be this one... it's writing char $80 ('0') to screen mem.

(C:$3b12) d 8018
.C:8018  A0 3D       LDY #$3D
.C:801a  B9 31 34    LDA $3431,Y
.C:801d  99 76 43    STA $4376,Y    ; When Y=$08 (A=$80)
.C:8020  B9 C1 35    LDA $35C1,Y
.C:8023  99 76 DB    STA $DB76,Y
.C:8026  88          DEY
.C:8027  10 F1       BPL $801A
.C:8029  60          RTS

Above routine copies bytes to screen mem from offset $3d downwards, including snoozes and lifts at offset $08 and snoozes at offset $15.  Looks like simple copy from $3431 + offset.

(C:$8043) m $3431
>C:3431  78 73 74 74  7f 6a 78 f6  80 53 71 6e   xstt.jx..Sqn
>C:343d  6b 79 53 6e  73 6e 79 78  f6 80 2b 2c   kySnsnyx..+,

Snoozes = 3439, Lifts = 3446

Let's watch 3439 and try and find the routine that updates the Snooze password count.

(C:$8606) watch store 3439
WATCH: 13  C:$3439  (Stop on store)
(C:$8606) x
#13 (Stop on store 3439)  014 039
.C:9953  FE 39 34    INC $3439,X    - A:80 X:00 Y:FF SP:fb N.-..... 1337277225
.C:9956  20 83 9B    JSR $9B83      - A:80 X:00 Y:FF SP:fb N.-..... 1337277225


.C:9940  50 88       BVC $98CA
.C:9942  C0 FF       CPY #$FF
.C:9944  D0 F6       BNE $993C
.C:9946  E6 D7       INC $D7        ; High Score passwords found total
.C:9948  BD 1A A2    LDA $A21A,X
.C:994b  AA          TAX
.C:994c  BD 39 34    LDA $3439,X
.C:994f  C9 89       CMP #$89        ; Maximum of 9 passwords
.C:9951  B0 03       BCS $9956
.C:9953  FE 39 34    INC $3439,X    ; Increment char (80-89)
.C:9956  20 83 9B    JSR $9B83
.C:9959  4C B4 99    JMP $99B4

Game appears to keep track of passwords by storing character in screen mem backing store only.  Lets test it:
> 3439 89
> 3446 89

(POKE 13369,137)
(POKE 13382,137)

Yes, we now have 9 of each password.

Decrement occurs at:
#13 (Stop on store 3439)  178 000
.C:a1ce  DE 39 34    DEC $3439,X    - A:89 X:00 Y:25 SP:cf N.-..I.C 1820510622
.C:a1d1  08          PHP            - A:89 X:00 Y:25 SP:cf N.-..I.C 1820510622

Prevent passwords from decrementing with:
(C:$a1e2) a a1ce
.a1ce  nop
.a1cf  nop
.a1d0  nop
.a1d1
(C:$a1d1) x

(POKE 41422,234)
(POKE 41423,234)
(POKE 41424,234)

3. Disable robot laser beam sprites:

Sprites:
1 = Robot laser
2 = Character
3 = Character
4 = Character
5 = Robot 1        (top down)
6 = Robot 2
7 = Robot 3
8 = Robot 4

Sprite 5 (robot 1) X, Y updates:

(C:$8f82) watch store d00a
WATCH: 2  C:$d00a  (Stop on store)
(C:$8f82) x
#2 (Stop on store d00a)  268 041
.C:91b1  99 08 D0    STA $D008,Y    - A:7D X:06 Y:02 SP:ee ..-..I.. 1541342165
.C:91b4  90 0C       BCC $91C2      - A:7D X:06 Y:02 SP:ee ..-..I.. 1541342165
(C:$91b4) bt
(0) 8340
(13) 871e
(15) 7288

JSR $8ECA Robot movement and lasers
JSR $92C1 "Rover" ball movement


Disable laser beam sprite mux:
Trace sprite control register $d015

#3 (Stop on store d015)  269 022
.C:8fc9  8D 15 D0    STA $D015      - A:0F X:06 Y:01 SP:ee ..-B.I.. 1723415737
.C:8fcc  AD 1C D0    LDA $D01C      - A:0F X:06 Y:01 SP:ee ..-B.I.. 1723415737
(C:$8fcc) bt
(0) 8340
(13) 7f70
(15) 7249

(C:$8fcc) a 8fc9
.8fc9  nop
.8fca  nop
.8fcb  nop
.8fcc 
(C:$8fcc) x

(POKE 36809,234)
(POKE 36810,234)
(POKE 36811,234)


4. Remove variable delay when "Searching" an object.

Find where "Searching" box is drawn from....
Put a trace on screen memory where Searching box is going to appear next.

#7 (Stop on store 4180)  286 022
.C:9ba6  91 10       STA ($10),Y    - A:52 X:1B Y:04 SP:f9 ..-..... 1819790176
.C:9ba8  A9 01       LDA #$01       - A:52 X:1B Y:04 SP:f9 ..-..... 1819790176
(C:$9ba8) bt
(0) 98a0
(2) 92bd
(4) 726f

.C:98a0  20 96 9B    JSR $9B96        ; Overlay Searching box

.C:98a3  20 6B 7F    JSR $7F6B        ; Searching delay

 Remove delay entirely:
(Note that delay routine at $7f6b) is used elsewhere, e.g. when riding elevator platforms, so can't be sped up/removed globally without side effects.)

(C:$8606) a 98a3
.98a3  nop
.98a4  nop
.98a5  nop
.98a6 

(POKE 39075,234)
(POKE 39076,234)
(POKE 39077,234)

All puzzles solved, password to control room found.
Elvin Atombender (No, No, Nooooo!)
I went ahead and completed the game with a couple of minor pokes enabled.  I used the 'fast searching' hack and switched off robot lasers.  Even so, I was still down to less than 1 hour to go at game completion.  Room 00 used up a lot of time by falling or touching robots whilst attempting to get the jumps timed just right.

A quick cheat is to disable sprite collisions with a freeze cartridge but that takes all the fun out of the game.

Sunday 26 February 2017

Everyone's a Wally (Mikro-Gen) Part 2

Following on from the last post where we hacked Everyone's a Wally to get infinite lives, let's have a look at how the game items are stored.

Remember we found the lives for each of the 5 characters are stored at $5c18 onwards:

>C:5c18  03 03 03 03  03 00 08 10  18 20 00 00   ......... ..

    5c18    Lives Wally
    5c19    Lives Wilma
    5c1a    Lives Tom
    5c1b    Lives Dick
    5c1c    Lives Harry

It's a safe assumption that this area of memory is also used for other state information for each character, for example which items they're currently carrying.  Each character holds exactly 2 items in their inventory at all times.

(C:$582e) m 5c18
>C:5c18  03 03 03 03  03 04 08 10  1c 20 00 00   ......... ..
>C:5c24  00 00 00 ff  ff 00 01 ff  01 01 ff ff   ............
>C:5c30  01 00 00 00  00 00 0c 0c  0c 0c 0c 23   ...........#
>C:5c3c  01 08 1f 26  18 19 20 27  06 04 1f 05   ...&.. '....


By picking up/dropping different items it's easy to determine that the following memory locations are used by each character for holding their current inventory:

    5c3b & 5c40    - Wally
    5c3c & 5c41    - Wilma
    5c3d & 5c42    - Tom
    5c3e & 5c43    - Dick
    5c3f & 5c44    - Harry

The item IDs are as follows:

    00    The Plunger
    01    Letter A
    02    Book Two
    03    The Sand
    04    The Battery
    05    Letter K
    06    The Cement
    07    Book Three
    08    The Trowel
    09    The Matches
    0A    The Cracked Insulator
    0B    Book One
    0C     The Monkey Nuts
    0D    The Money
    0E    Superglue
    0F    The Chewing Gum
    10    The Bucket
    11    The Jump Leads
    12    The Bunsen Burner
    13    The Letter B
    14    The Gas Mask
    15    The Parcel
    16    The Fuse
    17    The Meat
    18    The Monkey Wrench
    19    The Screwdriver
    1A    Letter E
    1B    The Hook
    1C    The Patch
    1D    The Pipe
    1E    Letter R
    1F    The Oil Can
    20    The Fuse Wire
    21    The Good Insulator
    22    The Bottle
    23    The Red Herring
    24    The Test Tube
    25    The Can of Beans
    26    The Rubber Stamp
    27    The Pliers
    28    The Whistle

This is quite useful for completing the game because we can work our whether another character has picked up the item we're looking for by checking their inventory, e.g. for Wally, Wilma, Tom, Dick, Harry


m 5c3b,5c40 
m 5c3c,5c41
m 5c3d,5c42
m 5c3e,5c43
m 5c3f,5c44


The first & last bytes printed are inventory position 1 & 2, ignore the middle bytes. The game already displays the map location of each character by pressing 1-5 on the keyboard so we don't need to work that out ourselves.

OK, that's useful for knowing which character is holding an item, but it would be great if we could determine where all the other game items are without having to search the entire map.

Let's figure this out the hard way rather than just looking for the known item IDs in memory.  By using watch points & break points on the character memory map where the items are drawn on screen, we can eventually locate the subroutine that draws the items at $3db to -$3e68.

Interestingly this routine is called several times per second and constantly re-draws any items present in the room.  Note that the currently held 2 inventory items at the top of the screen are only redrawn when they change or when switching characters.   This constant redraw effect is used for animating items, e.g. the flickering flame outside of the Bee-Pee garage from the cave gas leak.

It's also interesting to note that the items drawn are all 2 or 3 bytes in width and 2 characters high.

The routine at $3db0 draws the item ID specified in the Accumulator with the x, y position taken from addresses in zero page ($29, $2a).  The item IDs are the same as listed above.

The item draw routine is called from the periodic update polling routine in $a1ca to $a1ed.  It appears to read the variable length item for each room from memory as follows:

.C:a1d6  B1 04       LDA ($04),Y    $04 = 6030
.C:a1d8  C9 FF       CMP #$FF
.C:a1da  F0 14       BEQ $A1F0
.C:a1dc  48          PHA

>C:6030  0b 38 90 9f  12 78 90 9f  13 c0 90 9f   .8...x......


So the routine reads the Item data structure (ID, x pos, y pos, etc.) for each room from memory somewhere around $c030 until it reaches $FF.  In this example $c030 is the room location for the Library which draws 3 items, e.g. in this example

0b 38 90 9f = Book One at position 38,90
12 78 90 9f = Bunsen Burner at position 78, 90
13 c0 90 9f = Letter B at position c0, 90

So for example, we can manipulate the Library (room offset $6030) as follows and move items around the screen.

 > 6030 0b 38 a8 9f 4e 78 90 9f 07 df 68 9f

The Library with modified item placement

By examining memory from $5fd8 to $6070 the room offsets can be determined as follows:

    5fd8    Town Square
    5fda    Post Office
    5fdf    Market Street  
    5fe0    Supermarket
    5fe9    Park
    5fee    Wobbly Walk
    5fef    Rubble Road
    5ff0    Wall Street
    5ff9    Pete Street
    5ffe    Workshop
    600b    School Lane
    6010    School
    6015    Baker Street
    6016    Bakery
    601b    Red Lion
    6020    Motor Way
    6021    Kemco
    602a    Bee-Pee
    602f    Reference Road
    6030    Library
    603d    Penny Lane
    603e    Bank
    6043    Wally's House
    6048    Meat Street
    6049    Butchers
    6052    Trunk Road
    6053    Zoo
    6058    Rail Road
    6059    Station
    605e    Dock
    6067    Sewer
    606c    Cave


This is quite useful because we can now use the following command to locate a required item and determine which rooms it's in:

m 5fd8 6070

If the required item doesn't appear in any location it's being carried by another character which we can check with the following (see first & last byte) as described above.

m 5c3b,5c40
m 5c3c,5c41
m 5c3d,5c42
m 5c3e,5c43
m 5c3f,5c44


So armed with this inside information I went ahead and played the game to completion.  I did have infinite lives enabled but I reckon it's possible to complete the game without it if you know exactly where all the items are to be found to avoid endlessly walking the map and switching characters.  It still takes quite some time to complete and several times another character had picked up an item before I had time to walk to the the room it was held in.  No, I didn't complete every task so the final monetary haul is reduced from the maximum £3,000.

Bank with all letters collected and safe opened

End game screen

End game score table

What an excellent game!  I've got as much enjoyment out of it from reverse engineering the code as I did from playing it properly back in the 80s.

Everyone's a Wally (Mikro-Gen) Part 1

I bought Everyone's a Wally as a budget title back in the 80s on cassette for the Commodore 64.  I owned and played several of the Wally games but kept going back to this one, although I never got close to finishing it.

Everyone's a Wally (C64) - Town square

Looking at the game now, it was certainly very well-featured for the budget price.  There are 5 playable characters you can switch between and they continue to roam the map and pick up/drop objects as they go around.  There's several interesting puzzles to complete and the aim of the game is to collect several letters (B-R-E-A-K) and take them to the Bank to finish.  If all this wasn't enough, the graphics are bright and colourful, there's a back story for all the characters and there's a music single on the B side of the tape.

The first puzzle game I really got into in a big way was Adventure for the Atari 2600 and this game is similar in some respects, e.g. objects being moved around randomly by other characters (The Bat), nasties roaming the map (Dragons), puzzles (Keys/Castles/Bridge/Magnet), collecting items and taking them to a special location to finish the game (Chalice and Yellow Castle).

This YouTube video has the Mike Berry song from the tape B side and shows the C64 gameplay.  Note the character inventory in the top left and the energy/lives at top right.

 

Back in the day I found the game difficult to complete because I ran out of lives quickly and it's often time consuming to track down specific items as they could either be carried by another character or in one of the many locations.  The longer you roam the map looking for items, the greater the likelihood of running into a nasty.  I didn't have any pokes for the game and my early Datel Action Replay Mk IV couldn't locate the lives counter.

Looking at the game today, the newer Action Replay Mk VI correctly finds the code for decrementing the lives counter (for all characters) as follows:

Infinite lives poke:
POKE 36317,189        (Switches STA to LDA instruction)

However, that's way too easy.  Let's hack this the hard way for fun and get a better understanding of the Commodore 64 memory map and play with some 6502.

I used the following cracked .d64 for this (I do own the physical tape so don't judge me!).

006980209167de90736f18fd032e059b  Everyone's a wally.DC.d64

Infinite Lives

The game sets up the 64's VIC-II graphics as follows:

CIA2 Port A
$DD00 = $C4 (1100 0100)

VIC-II Bank = 3 (00), memory mapped $c000-$ffff

VIC-II Mode
$d011 = $bb (1011 1011) Bitmap Mode, Extended bg mode off
$d016 = $c8 (1100 1000) Multicolour mode off
$d018 = $39 (0011 1001) Char memory 1xx = $2000-$3fff
                        Screen memory 0011 = $0c00-$0fff

So we're running in VIC-II bitmap mode at 320x200.

    Screen memory (colour)  = $cc00-cfff
    Bitmap/Character memory = $e000-$efff

Let's find the character memory location where the lives (heart symbols) are drawn on screen.  We can do this with maths or by trial and error writing to $e000 onwards.

$e3a0-$e3a7

We can overwrite the character with FF to prove this is the correct location:
> f e3a0 e3a7 ff

Let's watch this memory location for updates.

watch e3a0

Now touch a nasty and/or lose a life.  Note how both the energy bar and the lives remaining are always redrawn every time energy is decremented even if a life isn't actually lost.

#4 (Stop on  load e3a0)  098 037
.C:3c2d  91 0A       STA ($0A),Y    - A:6C X:24 Y:00 SP:78 ..-..I..  171937243
.C:3c2f  C8          INY            - A:6C X:24 Y:00 SP:78 ..-..I..  171937243


Add breakpoint on 3c2d where bitmap memory update occurs.

break $3c2d

#6 (Stop on  exec 3c2d)  106 049
.C:3c2d  91 0A       STA ($0A),Y    - A:6C X:24 Y:00 SP:78 ..-..I..  125117167
(C:$3c2d) bt
(0) 8e0f
(2) 9acc
(4) 4f07
(6) 4edb
(118) 0b77
(131) a677


$3bd5 subroutine updates the lives bitmap (decrement happens before this)

.C:8de2  4C 51 43    JMP $4351
.C:8de5  84 42       STY $42
.C:8de7  A9 70       LDA #$70
.C:8de9  85 0C       STA $0C
.C:8deb  A9 92       LDA #$92
.C:8ded  8D EE 3B    STA $3BEE
.C:8df0  A9 83       LDA #$83
.C:8df2  8D F5 3B    STA $3BF5
.C:8df5  A6 0F       LDX $0F
.C:8df7  BD 18 5C    LDA $5C18,X    ; Probably lives?
.C:8dfa  85 23       STA $23

.C:8dfc  C6 23       DEC $23
.C:8dfe  A2 24       LDX #$24
.C:8e00  A0 02       LDY #$02
.C:8e02  84 24       STY $24
.C:8e04  A5 23       LDA $23
.C:8e06  D0 05       BNE $8E0D
.C:8e08  A9 20       LDA #$20
.C:8e0a  4C 0F 8E    JMP $8E0F
.C:8e0d  A9 1F       LDA #$1F
.C:8e0f  20 D5 3B    JSR $3BD5


Looks like lives are probably stored in memory at $5c18 onwards:
The offset is for the character being played (5 different characters).

>C:5c18  03 03 03 03  03 00 08 10  18 20 00 00   ......... ..

Lose a life (Wally)...
>C:5c18  02 03 03 03  03 04 0c 14  1c 20 00 00   ......... ..

Yep, this is it.  Let's prove it by restoring the starting number of lives (Wally):
> 5c18 3

Watch this memory location for writes...

watch store 5c18
WATCH: 10  C:$5c18  (Stop on store)

#10 (Stop on store 5c18)  033 050
.C:8ddd  DE 18 5C    DEC $5C18,X    - A:80 X:00 Y:01 SP:7a ..-..I.C  215883977
.C:8de0  D0 03       BNE $8DE5      - A:80 X:00 Y:01 SP:7a ..-..I.C  215883977



Found it! Remaining lives counters are decremented by the DEC instruction at $8ddd


Remove the 3 byte DEC instruction and test it:
a 8ddd
nop
nop
nop
x

Yes, we now have unlimited lives.  Let's make this an Action Replay style poke by switching the STA to an LDA instruction with the same addressing mode which takes the same number of bytes in memory.

Infinite lives poke:
POKE 36317,189        (Switch STA to LDA instruction)


Note that this poke matches the poke determined by Datel's Action Replay Mk VI cartridge (see above), which demonstrates just how clever that cartridge was.

Remaining lives memory locations:

    5c18    Lives Wally
    5c19    Lives Wilma
    5c1a    Lives Tom
    5c1b    Lives Dick
    5c1c    Lives Harry

More tracing and disassembly to come in the next post...