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…
Will it run with HyperPort 1200 ?
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!!!