10 REM > SMLoad/src v0.13
   20 REM Load Sideways Relocatable Module
   30 REM 14-Jan-2001 v0.10 JGH: Initial version
   40 REM 15-Jan-2001 v0.11 JGH: Add LDX &F4 before JSR OldServ
   50 REM 16-Jan-2001 v0.12 JGH: Optimised and squeezed down to fit in &880-&AFF
   60 REM 22-Jan-2001 v0.13 JGH: Doesn't call OSGBPB past end of file
   70 :
   80 OSFIND=&FFCE:OSARGS=&FFDA:OSGBPB=&FFD1
   90 DIM mcode% &400:load%=&FFFF0880
  100 lptr=&A8:size=&A8:byte=&A8
  110 addr=&AA:dest=&AC:base=&AE
  120 load=load% AND &FFFF
  130 :
  140 FOR P=0 TO 1
  150   P%=load%:O%=mcode%
  160   [OPT P*2+4
  170   .end
  180   .errSyntax
  190   BRK:EQUB 220:EQUS "Syntax: SMLoad <afsp>"
  200   .errNotFound
  210   \BRK:\EQUB 214:\EQUS "File not found":\BRK
  220   BRK:EQUB 214:EQUS "Not found":BRK
  230   :
  240   .exec%
  250   LDA #1:LDX #lptr:LDY #0
  260   JSR OSARGS:LDA (lptr),Y       :\ Get parameters
  270   CMP #13:BEQ errSyntax         :\ No filename, error
  280   :
  290   LDX lptr+0:LDY lptr+1
  300   LDA #&40:JSR OSFIND           :\ Open file
  310   TAY:BEQ errNotFound
  320   LDX #size:LDA #2:JSR OSARGS   :\ Read file size
  330   LDA size+3:ORA size+2:BNE errTooBig
  340   LDA size+1:CMP #&40:BCC SizeOk
  350   BNE errTooBig
  360   LDA size+0:BNE errTooBig
  370   .SizeOk
  380   :
  390   \ Now look for a bank of RAM with enough space
  400   LDA &F4:PHA                   :\ Save current ROM
  410   TYA:PHA                       :\ Save channel
  420   LDX #15                       :\ Start at bank 15
  430   .FindLoop
  440   JSR SelectROM                 :\ Select ROM
  450   LDA &8008:EOR #&AA:STA &8008  :\ Test for RAM
  460   CMP &8008:BNE NotRAM          :\ Not RAM, look at next
  470   EOR #&AA:STA &8008            :\ Restore tested byte
  480   LDY #&7F:STY addr+1           :\ Set to &7FFF in case empty
  490   LDY #&FF:STY addr+0
  500   LDA &2A1,X:AND #&C0:BEQ AllEmpty :\ Whole bank is empty, Y=&FF
  510   INY:LDA #&BF:STA addr+1       :\ Start at &BFFF, Y=&00
  520   .ScanLoop
  530   LDA (addr),Y
  540   CMP &BFFF:BNE StartOfFree     :\ Look for start of free space
  550   LDA addr+0:BNE P%+4           :\ Dec. ROM address
  560   DEC addr+1:DEC addr+0
  570   LDA addr+1:BMI ScanLoop       :\ Loop until <&8000
  580   BPL AllEmpty                  :\ Whole bank is empty
  590   :
  600   \ Found start of free space, check it for size
  610   .StartOfFree
  620   SEC:LDA addr+0:ADC size+0
  630   PHA:LDA addr+1:ADC size+1
  640   CMP #&BF:BCC FoundSpace         :\ Free+Size<&BF00 - use it
  650   BNE NotEnoughSpace              :\ Size>=&C000 - not enough
  660   PLA:PHA:CMP #&FE:BCC FoundSpace :\ Free+Size<&BFFE - use it
  670   .NotEnoughSpace
  680   PLA
  690   .NotRAM
  700   DEX:BPL FindLoop              :\ Step to next bank
  710   PLA:TAY                       :\ Get channel back
  720   .errTooBig
  730   JSR Close
  740   BRK:EQUB 133:EQUS "Not enough SRAM":BRK
  750   \BRK:\EQUB 133:\EQUS "SRAM full":\BRK
  760   :
  770   .FoundSpace
  780   PLA                           :\ Balance stack
  790   .AllEmpty                     :\ addr=&7FFF
  800   JSR UpdateAddr
  810   \
  820   \ addr=>start of empty space
  830   \ Y=&FF - empty ROM, Y=&00 - non-empty ROM
  840   \
  850   \ Follow links to insertion point
  860   INY:STY base+0:STY base+1:BEQ linkhere :\ ROM is empty
  870   DEC base+0:LDA #&80:STA base+1         :\ Point to start of ROM
  880   \
  890   \ addr=>start of loaded code
  900   \ base=>current module
  910   \ dest=>next module
  920   \
  930   \ -----------------------
  940   \      zzz zzz zzz
  950   \      JMP ccc
  960   \ bbb
  970   \ -----------------------
  980   \ ccc  JSR bbb
  990   \     (LDX &F4)
 1000   \      JMP eee
 1010   \ ddd
 1020   \ -----------------------
 1030   \ base JSR ddd
 1040   \     (LDX &F4)
 1050   \      JMP fff         --> JMP mmm ------+
 1060   \                                        |
 1070   \ dest JSR ggg   xxx xxx       <-----+   |
 1080   \      xxx                           |   |
 1090   \ ggg                                |   |
 1100   \ -----------------------            |   |
 1110   \ mmm  BRK xxx xxx     --> JSR dest -+ <-+
 1120   \ nnn  JMP ooo
 1130   \
 1140   \ ooo  ....
 1150   \
 1160   \ -----------------------
 1170   \
 1180   .followlinks
 1190   LDY #3
 1200   .followlinks1
 1210   LDA (base),Y:INY:CMP #&4C     :\ Look for 'JMP', skipping any LDX &F4
 1220   BNE followlinks1
 1230   TYA:CLC:ADC base+0:STA base+0 :\ base=>dest field of 'JMP'
 1240   LDA base+1:ADC #0:STA base+1
 1250   LDY #2
 1260   .followlinks3
 1270   DEY:LDA (base),Y:STA dest,Y   :\ Point to next module
 1280   TYA:BNE followlinks3          :\  or into service code
 1290   LDA (dest),Y:CMP #&20:BNE linkhere :\ Not 'JSR', not another module
 1300   LDY #3
 1310   LDA (dest),Y:CMP #&4C:BEQ nextlink :\ JSR,JMP - short module header
 1320   CMP #&A6:BNE linkhere              :\ JSR,LDX,JMP - long module header
 1330   .nextlink
 1340   LDA dest+0:STA base+0
 1350   LDA dest+1:STA base+1
 1360   BNE followlinks
 1370   .linkhere
 1380   \
 1390   \ addr=>start of empty space
 1400   \ base=>current final module JMP dest address or 0000
 1410   \ dest=>service code of current final module or XXXX
 1420   \
 1430   PLA:TAY                        :\ Get channel
 1440   LDA base+1:PHA:LDA base+0:PHA  :\ Save insertion address
 1450   LDA base+1:BEQ emptyrom        :\ Empty ROM
 1460   JSR UpdateAddr:JSR UpdateAddr  :\ If not at start of ROM, add two bytes
 1470   .emptyrom
 1480   \
 1490   LDA addr+0:STA dest+0:STA base+0
 1500   LDA addr+1:STA dest+1:AND #&3F:STA base+1
 1510   \
 1520   \ stacked=>start of previous module or &0000
 1530   \ addr=>start of loaded code
 1540   \ dest=>start of loaded code
 1550   \ base=>offset of loaded code from start of ROM
 1560   \
 1570   \ Everything before here can now be overwritten
 1580   :
 1590   TYA:PHA                       :\ Channel
 1600   .loadloop
 1610   LDA #0:LDX #12
 1620   .loadclear
 1630   STA gbpb,X:DEX:BPL loadclear  :\ Clear OSGBPB block
 1640   PLA:PHA:STA gbpb+0            :\ Channel
 1650   LDA #load AND 255:STA gbpb+1  :\ Addr=&FFFFload
 1660   LDA #load DIV 256:STA gbpb+2
 1670   STX gbpb+3:STX gbpb+4
 1680   LDA #&01:STA gbpb+6           :\ Num=256
 1690   LDX #gbpb AND 255
 1700   LDY #gbpb DIV 256
 1710   LDA #4:JSR OSGBPB
 1720   LDY gbpb+6:BNE loaded
 1730   LDX gbpb+5
 1740   .byteloop
 1750   LDA load%,Y:STA (addr),Y
 1760   INY:INX:BNE byteloop
 1770   INC addr+1:TYA:BEQ loadloop
 1780   .loaded
 1790   PLA:TAY:JSR Close             :\ Close
 1800   \
 1810   \ addr=corrupted
 1820   \ dest=>start of loaded code
 1830   \ base=>offset of loaded code from start of ROM
 1840   \
 1850   \ Check if header exists
 1860   LDY #6:LDA (dest),Y:AND #&8F
 1870   CMP #&82:BNE jmpNoHeader      :\ Not Serv+6502
 1880   INY:LDA (dest),Y:TAY:LDX #3+1
 1890   .CheckHdr
 1900   LDA (dest),Y:CMP CopyStr-1,X:BNE jmpNoHeader
 1910   INY:DEX:BNE CheckHdr                    :\ Ends with X=0, CS
 1920   LDY #2:LDA (dest),Y:BPL jmpNoRelocate   :\ Jump with CS
 1930   LDY #0:LDA (dest),Y:BNE jmpNoRelocate   :\ Jump with CS
 1940   \
 1950   \ addr=corrupted
 1960   \ dest=>start of loaded code
 1970   \ base=>offset of loaded code from start of ROM
 1980   \
 1990   CLC
 2000   INY:LDA (dest),Y:ADC dest+0:STA addr+0:STA end+0
 2010   INY:LDA (dest),Y:AND #&3F:ADC dest+1:STA addr+1:STA end+1
 2020   \
 2030   \ addr=>end of loaded code/start of relocation table
 2040   \ dest=>start of loaded code
 2050   \ base=>offset of loaded code from start of ROM
 2060   \ end =>end of loaded code/start of relocation table
 2070   \
 2080   LDY #0:BEQ relocloop
 2090   .CopyStr
 2100   EQUS ")C("+CHR$0
 2110   \
 2120   .jmpNoHeader
 2130   CLC
 2140   .jmpNoRelocate
 2150   PLA:PLA                        :\ Drop link address
 2160   JMP NoRelocation               :\ Skip relocation, CC=no header CS=header
 2170   \
 2180   .relocloop
 2190   LDA (dest),Y:BPL nextbyte      :\ <&80 - no change
 2200   CMP #&C0:BCS nextbyte          :\ >&BF - no change
 2210   DEX:BPL testbit                :\ Relocation bits still available
 2220   LDA (addr),Y:STA byte          :\ Get relocation byte
 2230   JSR UpdateAddr                 :\ Update relocation address
 2240   LDX #7                         :\ 7+1 bits available
 2250   .testbit
 2260   ROR byte:BCC nextbyte                 :\ Bit clear, no relocation
 2270   DEY:DEC dest+1:CLC                    :\ Point to low byte of address
 2280   LDA (dest),Y:ADC base+0:STA (dest),Y  :\ Relocate low byte of address
 2290   INY:INC dest+1                        :\ Point back to high byte of address
 2300   LDA (dest),Y:ADC base+1:STA (dest),Y  :\ Relocate high byte of address
 2310   .nextbyte
 2320   JSR UpdateDest                        :\ Update code address
 2330   CMP end+1:BNE relocloop               :\ Loop until end of code reached
 2340   LDA dest+0:CMP end+0:BNE relocloop
 2350   \
 2360   \ addr=>end of relocation table
 2370   \ dest=>end of loaded code
 2380   \ base=>offset of loaded code from start of ROM
 2390   \ end =>end of loaded code/start of relocation table
 2400   \
 2410   .clearloop
 2420   TYA:STA (dest),Y                      :\ Clear relocation table
 2430   JSR UpdateDest                        :\ Update code address
 2440   CMP #&C0:BCC clearloop                :\ Loop until end of ROM
 2450   LDA base+1:ORA #&80:STA base+1        :\ Point base back into ROM
 2460   \
 2470   \ addr=>end of relocation table
 2480   \ dest=>&C000
 2490   \ base=>newly loaded code
 2500   \ end =>end of loaded code/start of relocation table
 2510   \
 2520   PLA:STA addr+0:PLA:STA addr+1         :\ Get insertion addresses
 2530   BPL NoRelocation                      :\ No previous module
 2540   \
 2550   \ Cy  = CS
 2560   \ addr=>start of penultimate module+4 or +6 - service entry point
 2570   \ dest=>&C000
 2580   \ base=>newly loaded code
 2590   \ end =>end of loaded code/start of relocation table
 2600   \
 2610   LDA base+0:SBC #2:STA base+0   :\ Space for 'LDX &F4'
 2620   LDA base+1:SBC #0:STA base+1
 2630   LDY #0:LDA #&20:STA (base),Y   :\ New module does JSR OldService
 2640   .addrlp1
 2650   LDA (addr),Y:INY:STA (base),Y
 2660   CPY #2:BNE addrlp1             :\ Ends with CS
 2670   LDA #&A6:INY:STA (base),Y      :\ Insert 'LDX &F4'
 2680   LDA #&F4:INY:STA (base),Y
 2690   LDY #1
 2700   .addrlp2
 2710   LDA base,Y:STA (addr),Y        :\ OldServEntry does JMP NewModule
 2720   DEY:BPL addrlp2
 2730   :
 2740   .NoRelocation
 2750   BCC NoHeader                   :\ No header found earlier
 2760   LDA &8006:LDX &F4:STA &2A1,X   :\ Set ROM type
 2770   :
 2780   .NoHeader
 2790   PLA:TAX
 2800   .SelectROM
 2810   STA &FF30,X:STX &F4:JMP &FFB9  :\ Enable WERAM writes, select ROM
 2820   :
 2830   .Close
 2840   LDA #0:JMP OSFIND
 2850   :
 2860   .UpdateAddr
 2870   INC addr+0:BNE P%+4:INC addr+1:RTS
 2880   .UpdateDest
 2890   INC dest+0:BNE P%+4:INC dest+1:LDA dest+1:RTS
 2900   :
 2910   EQUS "0.13"
 2920   :
 2930   ]
 2940   gbpb=load+256
 2950 NEXT
 2960 PRINT" *Save SMLoad "+STR$~mcode%+" "+STR$~O%+" "+STR$~(exec%OR&FFFF0000)+" "+STR$~load%