10 REM > XSave/src v0.10 18-Feb-2015 J.G.Harston
   20 REM Source for *XSAVE - save from page-wide extended RAM in JIM
   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)