SilverSurfer patched for Amiga 500

I just purchased a new HW goodie for my classic Amiga 500. A clockport adapter and a SilverSurfer serial card that allows high baudrates (e.g. 115200 baud) on this machine without generating too much CPU load.

HW installation was fairly easy, but with the software the trouble began… The supplied drivers does not run on an Amiga 500 but only on an A1200 🙁 Being a real retro hacker I had a look at the driver binary, disassembled it and found out that the code could be easily patched to run on an A500, too.

Now it works like a charm even on the 680000 machines. Here try yourself: silversurfer104-a500.zip

If you want to know how this was done… then read on… Be warned: technical details ahead 😉

1. Take a close look at the binary

First of all I did a disassembly of the original driver binary. As the Amiga binaries are in the LoagSeg()able Hunk Format you need a tool that can cope with this type of files.

Fortunately, in my amitools Project I am developing a tool called hunktool that allows to inspect those files. With the help of the dissassemblers vda68k or m68k-elf-objdump from the gnu toolchain (here created like described in the 68k AROS project) it even can disassemble the code segments… Since my tools are written in Python 2.x they run almost everywhere…

On my Mac I did a:

> hunktool info -A silversurfer.device > surfer.txt

Voila! A full disassembly nicely annoted with relocations was generated. You get something like this:

silversurfer.device TYPE_LOADSEG
 header (segments: first=0, last=0, table size=1)
 #000  CODE   size 000037f4  file header @00000018  data @00000020 
 reloc  absreloc32 #1
 To Segment #0:   23 entries

00000000        70ff                          moveq   #-0x1,d0        
00000002        4e75                          rts                     
00000004        7000                          moveq   #0,d0           
00000006        4e75                          rts                     
00000008        4afc                          illegal                 
0000000a        0000 0008                     ori.b   #0x8,d0          ; absreloc32: self: 00000008
0000000e        0000 37f2                     ori.b   #-0xe,d0        
00000012        8002                          or.b    d2,d0           
00000014        0305                          btst    d1,d5           
00000016        0000 0024                     ori.b   #0x24,d0         ; absreloc32: self: 00000024
0000001a        0000 0081                     ori.b   #-0x7f,d0        ; absreloc32: self: 00000081
0000001e        0000 00c8                     ori.b   #-0x38,d0        ; absreloc32: self: 000000c8
00000022        0000 7369                     ori.b   #0x69,d0        
00000026        6c76                          bge.b   0x9e            
00000028        6572                          bcs.b   0x9c            
0000002a        7375                          moveq   #0x75,d1        
0000002c        7266                          moveq   #0x66,d1        
0000002e        6572                          bcs.b   0xa2            
00000030        2e64                          movea.l -(a4),sp        
00000032        6576                          bcs.b   0xaa            
00000034        6963                          bvs.b   0x99            
00000036        6500 5369                     bcs.w   0x53a1          
0000003a        6c76                          bge.b   0xb2            
0000003c        6572                          bcs.b   0xb0            
0000003e        5375 7266                     subq.w  #0x1,(0x66,a5,d7.w*2)

Now its time to analyse the code and to refresh your amiga knowledge. Decode resident structures, library calls and look out for memory accesses in the range of the clockport… Have a look at Inside Silver Surfer and you’ll find out we have to look for addresses like $d80001 in the driver code…

For decoding library calls and data structures I wrote two additional tools in amitools: fdtool and typetool. The first on you run with a library FD file from your Commodore NDK and it gives you details about function calls in a library:

> ./fdtool NDK_3.1/Includes\&Libs/fd/exec_lib.fd NDK_3.1/Includes&Libs/fd/exec_lib.fd
 base: _SysBase
 #0001     30  0x001e                Supervisor [userFunction,a5]
 #0002     72  0x0048                  InitCode [startClass,d0][version,d1]
 #0003     78  0x004e                InitStruct [initTable,a1][memory,a2][size,d0]
 #0004     84  0x0054               MakeLibrary [funcInit,a0][structInit,a1][libInit,a2][dataSize,d0][segList,d1]
 #0005     90  0x005a             MakeFunctions [target,a0][functionArray,a1][funcDispBase,a2]
 #0006     96  0x0060              FindResident [name,a1]
 #0007    102  0x0066              InitResident [resident,a1][segList,d1]
 #0008    108  0x006c                     Alert [alertNum,d7]
 #0009    114  0x0072                     Debug [flags,d0]
...

Now its very easy to decode a jsr -off(a6) library call (see second column for offsets!). typetool gives you the offset of data structures (Unfortunately, not all NDK datatypes are available in this tool right now):

> ./typetool ExecLibrary
 @0000        ExecLibrary {
 @0000          Library {
 @0000            Node {
0000 @0000/0000 +0004        Node*      ln_Succ               (ptr=True, sub=False)
0001 @0004/0004 +0004        Node*      ln_Pred               (ptr=True, sub=False)
0002 @0008/0008 +0001        UBYTE      ln_Type               (ptr=False, sub=False)
0003 @0009/0009 +0001        BYTE       ln_Pri                (ptr=False, sub=False)
0004 @0010/000a +0004        char*      ln_Name               (ptr=True, sub=False)
 @0014 =0014      } lib_Node
0005 @0014/000e +0001      UBYTE      lib_Flags             (ptr=False, sub=False)
0006 @0015/000f +0001      UBYTE      lib_pad               (ptr=False, sub=False)
0007 @0016/0010 +0002      UWORD      lib_NegSize           (ptr=False, sub=False)
0008 @0018/0012 +0002      UWORD      lib_PosSize           (ptr=False, sub=False)
0009 @0020/0014 +0002      UWORD      lib_Version           (ptr=False, sub=False)
0010 @0022/0016 +0002      UWORD      lib_Revision          (ptr=False, sub=False)
0011 @0024/0018 +0004      APTR       lib_ldString          (ptr=False, sub=False)
0012 @0028/001c +0004      ULONG      lib_Sum               (ptr=False, sub=False)
0013 @0032/0020 +0002      UWORD      lib_OpenCnt           (ptr=False, sub=False)
 @0034 =0034    } LibNode
...

Furthermore, you can use hunktool to have a look at a hexdump of the segments, too.

> ./hunktool info -x silversurfer.device
silversurfer-hack.device TYPE_LOADSEG
 header (segments: first=0, last=0, table size=1)
 #000  CODE   size 000037f4  file header @00000018  data @00000020  
 00000000: 70 ff 4e 75 70 00 4e 75 4a fc 00 00 00 08 00 00  p<FF>Nup.NuJ<FC>......
 00000010: 37 f2 80 02 03 05 00 00 00 24 00 00 00 81 00 00  7<F2><80>......$...<81>..
 00000020: 00 c8 00 00 73 69 6c 76 65 72 73 75 72 66 65 72  .<C8>..silversurfer
 00000030: 2e 64 65 76 69 63 65 00 53 69 6c 76 65 72 53 75  .device.SilverSu
 00000040: 72 66 65 72 20 42 6f 61 72 64 2d 00 20 55 6e 69  rfer Board-. Uni
 00000050: 74 00 00 00 20 52 00 00 20 57 00 00 53 69 6c 76  t... R.. W..Silv
 00000060: 65 72 53 75 72 66 65 72 20 6c 69 67 68 74 73 70  erSurfer lightsp
 00000070: 65 65 64 20 53 65 72 76 65 72 00 00 24 56 45 52  eed Server..$VER
 00000080: 3a 53 69 6c 76 65 72 53 75 72 66 65 72 20 32 2e  :SilverSurfer 2.
 00000090: 31 30 34 20 28 31 33 2e 31 31 2e 30 30 29 0d 0a  104 (13.11.00)..
 000000a0: 28 63 29 20 31 39 39 31 2d 45 56 45 52 20 62 79  (c) 1991-EVER by
 000000b0: 20 56 4d 43 20 48 61 72 61 6c 64 20 46 72 61 6e   VMC Harald Fran
 000000c0: 6b 0d 0a 0d 0a 00 00 00 00 00 00 f4 00 00 00 d8  k..........<F4>...<D8>
...

With the help of these tools and my trusty old Developer manuals I soon found out everything I need to know about the driver. See my partially annotated disassembly here: silversurfer104-disasm.txt

Code Analysis

The interesting part looking for a silver surfer HW is here:

; ----- detecte silver surfer -----
00002b9c        48e7 7ffe                     movem.l d1-d7/a0-a6,-(sp) 
00002ba0        2f0e                          move.l  a6,-(sp)         
00002ba2        2c6e 003c                     movea.l 0x3c(a6),a6      ; a6 = ExecBase
00002ba6        43fa 0be8                     lea     0x3790(pc),a1    ; a1 = "card.resource"
00002baa        4eae fe0e                     jsr     -0x1f2(a6)       ; OpenResource [resName,a1]
00002bae        2c5f                          movea.l (sp)+,a6         ; libaddr
00002bb0        4a80                          tst.l   d0               
00002bb2        6648                          bne.b   0x2bfc           ; ok! -> look for surfer

Aha! It looks for card.resource and if this is found then try to detect one. This one works like a charm on an A1200 with a card slot but not on A500 where this resource is missing. So on my machine this driver will never try to even look for the HW…

Ok, we need to patch this in order to have the driver look for my surfer… One way of doing this might be to replace the bne.s branch with a bra.s. I chose another option: just replace the resource name and use ciaa.resource instead of card.resource 😉 This resource is available on the A500, too 😉 A simple string replace in the driver binary does the trick!

Now the driver allows to open unit 0 on my machine! Yehaw!!

Unfortunately, it still crashes when trying to actually transfer data with it… 🙁

While scanning the disassembly I soon found some long branches (bra.l, bne.l, …) that are only available on cpus starting with 68020. So my A500 with a 68000 only sees a bogus (odd destination address) byte branch and crashes…

Damn! Its 68020 code… But as the init already works I need to find out how many 68020 instructions are actually used.. Again I use hunktool to find this out: It allows to use m68k-elf-objdump to do the disassemlby and this allows me to select the cpu type. So I did a dissassembly for the 68000 and the 68020 and simply diff’ed the result to see the 68020 portions:

> ./hunktool info -A -o -c 68000 silversurfer.device > ss000.txt
> ./hunktool info -A -o -c 68020 silversurfer.device > ss020.txt
> diff ss000.txt ss020.txt
307,308c307
< 0000040a    61ff                    bsrs 0x40b                     
< 0000040c    0000 02a6               orib #-90,%d0                  
---
> 0000040a    61ff 0000 02a6          bsrl 0x6b2                     
310,311c309
< 00000414    61ff                    bsrs 0x415                     
< 00000416    0000 029c               orib #-100,%d0                 
..

Aha! Its only the long branches…  And we are lucky ones: the binary is only 16k and so each long branch could be converted into a 68000 compatible word branch… The spare word in each instruction will then be filled with a NOP opcode… So I could patch the binary in place and do not need to relocate anything… Phew!

The branch conversion actually needs to clear the branch offset in the first word (0xff -> 00), remove the next word and keep the lower branch offset. Then add a NOP opcode word. This could be done by hand, but we are hackers so I wrote a little Python script to perform the patch: fix_bsrl.py

Just call it with the offsets to patch and it will do the job… (If you wonder where the global offset 32 in the -a switch comes from: Its the begin of the code segment in the hunk file. You can find the value in the output of hunktool: look for CODE data @20)

> grep bsrl ss020.txt > offsets
> hacks/fix_bsrl.py -a 32 silversurfer.device offsets
OK: 0000042a  distance: 678
OK: 00000434  distance: 668
OK: 00000e80  distance: 6862
OK: 0000106a  distance: 236
OK: 000010a0  distance: 62
OK: 0000110a  distance: 26
OK: 0000111a  distance: 10
OK: 00002288  distance: 1930
OK: 000026e8  distance: 614
OK: 000028a2  distance: 172
writing 'silversurfer.device.patched'... done

Ok! Patch complete…

The driver now works on my A500 without any problems and I can do SLIP via AmiTCP with 115200 Baud resulting in approx. 8 KiB/s FTP transfer rate…

That’s it… I hope you enjoyed my little archeological excursion in hacking classic Amiga driver code…

 

 

 

2 thoughts on “SilverSurfer patched for Amiga 500

  1. You are my HERO!!!
    I have an A500, and messed around for a month trying to get the PortJNR serial device working (never did – I could never get it to boot with the device attached). I then bought the Silver Surfer, and although I did not have any problems booting, I couldn’t get AExplorer to work with the silversurfer.device.
    THEN I saw your post. I downloaded the device, installed it on my A500, and PRESTO – I can not transfer files back and forth at 115200 (compared to the previous max of 38400).
    THANK YOU!!!

Leave a Reply