10
20
30
40
50
60
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%