10
20
30 :
40 OSARGS=&FFDA:OSFILE=&FFDD:OSGBPB=&FFD1:OSFIND=&FFCE
50 GSINIT=&FFC2:GSREAD=&FFC5
60 :
70 DIM mcode% &300:load%=&FFFF08C0:ver$="010"
80 :
90 gbpb=&2EE
100 addr=&A8:num=&AC:handle=&AF:lptr=&F2:latch=&FCFF:jim=&FD00
110 FOR P=0 TO 1
120 P%=load%:O%=mcode%
130 [OPT P*3+4
140 .CopyData
150 LDA num+2:ORA num+1:BNE Copy256:\ 256 or more bytes to go
160 LDA num+0:BNE P%+5:JMP Close :\ Nothing left
170 TAX:CLC:ADC addr+0:BCC CopyBank:\ Doesn't overlap page boundary
180 LDA num+0:BNE CopySub256 :\ Copy up to page boundary
190 .Copy256
200 LDA addr+0:TAX:BEQ CopyBank :\ addr is paged aligned
210 .CopySub256
220 EOR #&FF:TAX:INX :\ number to align transfer
230 .CopyBank
240 LDA num:PHA :\ Save current count
250 STX num:TXA:PHA :\ num=count to do in this pass
260 LDA addr+1:STA latch-0
270 LDA addr+2:STA latch-1
280 LDA addr+3:STA latch-2
290 LDX #0:LDY addr+0
300 .CopyBytes
310 LDA jim,Y:STA buffer,X
320 INX:INY:DEC num:BNE CopyBytes
330 :
340 PLA:PHA:STA gbpb+5 :\ count b0-b7
350 LDX #0:TAY:BNE P%+3:INX :\ &0xx or &100
360 STX gbpb+6:LDX #0
370 STX gbpb+7:STX gbpb+8 :\ count=&000000xx or &00000100
380 DEX
390 STX gbpb+4:STX gbpb+3 :\ addr=&FFFFxxxx
400 LDA #buffer DIV 256:STA gbpb+2
410 LDA #buffer AND 255:STA gbpb+1 :\ addr=&FFFFbuffer
420 LDA handle:STA gbpb+0 :\ handle
430 LDX #gbpb AND 255
440 LDY #gbpb DIV 256
450 LDA #2:JSR OSGBPB
460 PLA:TAX :\ X=number written
470 :
480 CLC:ADC addr+0:STA addr+0:PHP :\ Update addr
490 LDA #0:CPX #1:BCS P%+4:LDA #1 :\ &100 or &0xx
500 PLP:ADC addr+1:STA addr+1
510 LDA #0:ADC addr+2:STA addr+2
520 LDA #0:ADC addr+3:STA addr+3
530 :
540 PLA:STA num:TXA:PHA:TSX:SEC :\ Restore num, SP=>number written
550 LDA num+0:SBC &101,X:STA num+0 :\ Update count
560 PLA:PHP:TAX :\ Save Carry, X=number written
570 LDA #0:CPX #1:BCS P%+4:LDA #1 :\ &100 or &0xx
580 PLP:PHA:TSX
590 LDA num+1:SBC &101,X:STA num+1
600 PLA:LDA num+2:SBC #0:STA num+2
610 JMP CopyData
620 :
630 .Close
640 LDY handle:LDA #0:JSR OSFIND :\ Close file
650 TAX:\LDX #0
660 .GetAddrs1
670 PLA:STA ctrl+0,X:INX:CPX #6:BNE GetAddrs1
680 LDA #2:JSR CallOsfile:LDX #0 :\ Write load address
690 .GetAddrs2
700 PLA:STA ctrl+6,X:INX:CPX #4:BNE GetAddrs2
710 LDA #3 :\ Write exec address
720 .CallOsfile
730 LDX #ctrl AND 255
740 LDY #ctrl DIV 256
750 JMP OSFILE
760 :
770 \ Code used once, use as buffer area
780 .ctrl
790 .buffer
800 :
810 .start%
820 LDA #1:LDX #addr:LDY #0:JSR OSARGS
830 \ Can't read directly to &F2/3 'cos 4-byte address, would overwrite &F4 ROMNUM
840 LDA addr+0:STA lptr+0:STA ctrl+0 :\ =>filename
850 LDA addr+1:STA lptr+1:STA ctrl+1
860 CLC:JSR GSINIT:BEQ jmpSyntax
870 .SkipName
880 JSR GSREAD:BCC SkipName :\ Step past name
890 JSR SkipSpace:BEQ jmpSyntax :\ No start address
900 :
910 LDX #10:JSR GetHexCheck:BEQ jmpSyntax :\ No <end>/<length>
920 PHA:LDX #3 :\ Save next character
930 .CopyAddr
940 LDA ctrl+10,X:STA addr+0,X :\ addr=<start>
950 STA ctrl+6,X:STA ctrl+2,X :\ <load>=<start>, <exec>=<start>
960 DEX:BPL CopyAddr
970 :
980 PLA:CMP #ASC"+":PHP:BNE ScanSize
990 JSR SkipSpace1 :\ Step past + and spaces
1000 .ScanSize
1010 LDX #14:JSR GetHexCheck :\ Get <end> or <length>
1020 LDX #0:PLP:BEQ SizeLength
1030 :
1040 SEC
1050 .SizeEndLp
1060 LDA ctrl+14,X:SBC ctrl+10,X:STA num,X :\ num=<end>-<start>
1070 INX:TXA:EOR #4:BNE SizeEndLp
1080 BEQ ScanAddrs
1090 .jmpSyntax
1100 JMP errSyntax
1110 :
1120 .SizeLength
1130 CLC
1140 .SizeLengthLp
1150 LDA ctrl+14,X:STA num,X :\ num=<length>
1160 ADC ctrl+10,X:STA ctrl+14,X :\ <end>=<start>+<length>
1170 INX:TXA:EOR #4:BNE SizeLengthLp
1180 :
1190 .ScanAddrs
1200 LDA (lptr),Y:CMP #13:BEQ Create :\ No <exec>
1210 LDX #6:JSR GetHexCheck:BEQ Create :\ Get <exec>
1220 LDX #2:JSR GetHexCheck:BNE jmpSyntax :\ Get <load>
1230 .Create
1240 LDX #9
1250 .SaveAddrs
1260 LDA ctrl+0,X:PHA:DEX:BPL SaveAddrs :\ Save addrs for later
1270 LDA #7:JSR CallOsfile :\ Create file
1280 LDX ctrl+0:LDY ctrl+1 :\ XY=>filename
1290 LDA #&80:JSR OSFIND:TAY:BEQ errCantOpen
1300 STY handle:JMP CopyData
1310 :
1320 .GetHexDigit
1330 LDA (lptr),Y
1340 CMP #ASC"0":BCC HexDigitBad
1350 CMP #ASC"9"+1:BCC HexDigitOk
1360 CMP #ASC"A":BCC HexDigitBad
1370 AND #&DF
1380 CMP #ASC"F"+1:BCS HexDigitBad
1390 SBC #7
1400 .HexDigitOk
1410 SBC #ASC"0"-1:RTS
1420 .HexDigitBad
1430 CLC:RTS
1440 :
1450 .GetHexCheck
1460 JSR GetHexDigit:BCC errSyntax
1470 LDA #0
1480 STA ctrl+0,X:STA ctrl+1,X
1490 STA ctrl+2,X:STA ctrl+3,X
1500 .GetHexNext
1510 JSR GetHexDigit:BCC SkipSpace
1520 PHA:TYA:PHA:LDY #4
1530 .GetHexMultiply
1540 ASL ctrl+0,X:ROL ctrl+1,X
1550 ROL ctrl+2,X:ROL ctrl+3,X
1560 DEY:BNE GetHexMultiply
1570 PLA:TAY:PLA
1580 ORA ctrl+0,X:STA ctrl+0,X
1590 INY:BNE GetHexNext
1600 :
1610 .SkipSpace1
1620 INY
1630 .SkipSpace
1640 LDA (lptr),Y:CMP #ASC" ":BEQ SkipSpace1
1650 CMP #13:RTS
1660 :
1670 .errCantOpen
1680 \BRK:\EQUB 192:\EQUS "Can't open file"
1690 .errSyntax
1700 BRK:EQUB 220:EQUS "Syntax: XSave <fsp> <start> <end>|+<length> (<exec> (<load>))":BRK
1710 :
1720 EQUS ver$
1730 :
1740 ]:NEXT
1750 IF (P%AND&FFFF)>&B00:PRINT"Code overrun":END
1760 IF buffer>&A00:PRINT"Buffer overrun":END
1770 PRINT"*SAVE XSave "+STR$~mcode%+" "+STR$~O%+" "+STR$~(start%OR&FFFF0000)+" "+STR$~(load%OR&FFFF0000)