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.