>Z80CONV  Z80BBCBASIC Converter " By J.G.Harston, (C)1989-2006 ( 70 Camm Street 2 SHEFFIELD < S6 3TR F Public Domain P$ 16/04/91: Uses extended *Basic Z 23/05/91: PRINT# corrected d+ 16/06/93: Load checks for non-65BASIC n- 23/02/97 v1.19: Errors don't do CLOSE#0 x1 27/02/97 v1.20: Module-compliant rom header ? 15/04/97 v1.22: load_block moved to common block at &374D D 14/07/97 v1.23: Reordered in code order, better variable names E 23/11/03 v1.24: Compacted code to help with Spectrum conversion H 24/04/06 v1.25: Load changed as ANFS doesn't return correct length : )<>&3B00:"Wrong version of BASIC": +test%=:VER$="1.25":DATE$="24 Mar 2006" assm1:assm2  : assm1 *OSFILE=&FFDD:OSFIND=&FFCE:OSBPUT=&FFD4 7OSBGET=&FFD7:OSARGS=&FFDA:OSGBPB=&FFD1:OSRDCH=&FFE0 6OSWRCH=&FFEE:OSWORD=&FFF1:OSBYTE=&FFF4:oscli=&FFF7 ctrl=&374D : ", Point to old routines before starting: ,ChkEscape=&366E 6: @ P=0 1 J3P%=addr(&011B):[OPT P*3 :\ Pass command to TJP oscli:] ^: h6P%=addr(&0143):[OPT P*3 :\ Get host environment r InitEnv:] |: 2P%=addr(&019D):[OPT P*3 :\ Print '>' prompt  PrPrompt:] : .P%=addr(&01D6):[OPT P*3 :\ Input a line  RdLine:] : 2P%=addr(&06A1):[OPT P*3 :\ Check for Escape  ChkEscape:] : 8P%=addr(&07A6):[OPT P*3 :\ Look for Save filename  SaveFindName:] : 2P%=addr(&07FE):[OPT P*3 :\ In error handler NOP:NOP:NOP:] P%=addr(&080A):[OPT P*3 NOP:NOP:NOP:NOP:NOP:] &: 0,P%=addr(&095A):[OPT P*2 :\ Print char : OSWRCH:] D: N2P%=addr(&0DAE):[OPT P*3 :\ Check for Escape X ChkEscape:] b: l>P%=addr(&0D06):[OPT P*3 :\ Check for #= as well as #= vW SetExtPtr:] : 0P%=addr(&0DDC):[OPT P*3 :\ Don't #0 on NOP:NOP:NOP:] : #P%=addr(&0E18):[OPT P*3 :\  oscli:] : *P%=addr(&0E20):[OPT P*3 :\ *command  oscli:] : 2P%=addr(&0E63):[OPT P*3 :\ Check for Escape  ChkEscape:] : $P%=addr(&0F8E):[OPT P*3 :\ #  PUSH DE: &1E46:EX AF,AF' JP M,PrString  POP DE:PUSH BC *4CP 5:JP Z,PrReal :\ &05=real, &04=int 4+LD A,&40: &328E :\ &40=Integer > PutH1: PutL1 H PutH2: PutL2 R4POP BC:JR addr(&0F84) :\ Loop for next item \: f .PrString pLD H,&38:LD L,E:POP DE z*XOR A: &328E :\ &00=String &LD A,L: &328E :\ Length .PrStringLp !INC L:DEC L:JR Z,addr(&0F84) $DEC L:LD A,(HL): &328E :\ Char JR PrStringLp : 3NOP:NOP:NOP:NOP :\ Four spare bytes ]:chk(&0FCC) : $P%=addr(&12E9):[OPT P*3 :\ # PUSH AF:PUSH HL  A:JP M,InpString  &32AB:CP &FF:JP Z,InpReal  GetH1: GetL1  GetH2: GetL2  LD C,0 $.AssignVal .POP IX:POP AF 8PUSH DE: &15EA:POP DE B' prompt : ]:chk(&132A) : /P%=addr(&13A3):[OPT P*3 :\ No before JR addr(&13A8):] : .P%=addr(&13AF):[OPT P*3 :\ Input a line  RdLine:]  : 0P%=addr(&1460):[OPT P*3 :\ Do NL before  report:] (: 2*P%=addr(&149B):[OPT P*3 :\ #=, #= <JP WriteArgs:] F: P.P%=addr(&14B0):[OPT P*3 :\ Check =/$= ZJP SetTime:] d: n&P%=addr(&1521):[OPT P*3 :\ x, x OSWRCH:] : &P%=addr(&1530):[OPT P*3 :\ x;  OSWRCH:] : 0P%=addr(&1D8A):[OPT P*3 :\ Separate // W openin:] P%=addr(&1DC8):[OPT P*3 W openup:W openout:] : CP%=addr(&1FDC):[OPT P*3 :\ Check for functions in &C6+ range JP NC,CheckFunction:] : P%=addr(&2192):[OPT P*3 ;LD A,13:LD (DE),A:POP AF :\ CR-terminate OPEN string 3LD HL,&3800: OSFIND :\ Call OSFIND to open ] ": ,.P%=addr(&21AF):[OPT P*3 :\ Check =/=$ 6JP GetTime:] @: J&P%=addr(&264C):[OPT P*3 :\ =/ T OSRDCH:] ^: hP%=addr(&3169):[OPT P*3 r.OsfileHigh |2LD A,&82: OSBYTE :\ Get addr high word LD (ctrl+4),HL:RET : 2]:chk(&3172):[OPT P*3 :\ Save Basic program 0 SaveTryOsfile:RET NC :\ Try using OSFILE 4 SaveCreate :\ Create an empty file 4LD A,&80: OSFIND :\ Open file for output  A:JP Z,CantOpen -LD H,A:LD A,13 :\ Initial CR /DEC DE: SaveWrite :\ Save with BPUTs XOR A:JP OSFIND : ;.SaveWriteLp :\ Save program using BPUTs LD A,(DE): A 9LD A,&FF:JR Z,SaveWriteFF :\ If =0, end of program .INC DE:INC DE :\ Point to HI .SaveWriteFF & PutByte 0filename, DE=address  OsfileHigh  XOR A:LD (ctrl+6),A 2DEC A: CallOsfile :\ Load to (was +1)  0PUSH DE:POP HL :\ DE=load, HL= *:LD A,(DE):CP 13:JR NZ,LoadOk:\ Z80-style Basic program 4IINC DE :\ 6502-style program has to be reordered >9.LoadLoop :\ HL=>+1, DE=>+1 H*INC DE:INC DE:LD A,(DE):LD (HL),A:\ R&DEC DE:LD A,(DE):LD B,A :\ LO \&DEC DE:LD A,(DE) :\ HI f&INC HL:LD (HL),B :\ LO p&INC HL:LD (HL),A :\ HI z7INC A:JR Z,LoadEnd :\ End of BASIC program :INC H:JR Z,LoadEnd :\ Gone past top of memory DEC H:JR LoadLine : (]:chk(&31EB):[OPT P*3 :\ =&31EB XOR A:LD H,E:JP OSFIND : 9.LoadLine :\ HL=>HI(80), DE=>HI(65) /INC DE:INC DE:INC DE :\ DE=>TEXT(65) ,DEC HL:DEC HL :\ HL=>(80) .LoadLineSpc &LD A,(DE):CP 32:JR NZ,LoadLineText ;DEC (HL):INC DE:JR LoadLineSpc:\ Skip space, dec length .LoadLineText /INC HL:INC HL:INC HL :\ HL=>TEXT(80) .LoadTextLoop ,LD A,(DE):LD (HL),A :\ Copy text $INC HL:INC DE .9CP 13:JR NZ,LoadTextLoop :\ HL=>(CR)+1, DE=>(CR)+1 8JR LoadLoop B: L .LoadEnd VDEC HL:DEC HL:LD (HL),0 ` .LoadOk j3SCF:RET :\ Flag OK and exit t: ~;.SaveCreate :\ Try to create empty file LD (ctrl),HL:PUSH HL !PUSH DE:XOR A:LD E,A: OSARGS 4CP 4:JR C,SaveCreateTape :\ CFS, can't create POP DE:PUSH DE:INC DE LD (ctrl+10),DE EX DE,HL:INC HL:ADD HL,BC .SaveOsfile LD (ctrl+14),HL: OsfileHigh #LD (ctrl+12),HL:LD (ctrl+16),HL LD HL,&FFFF !LD (ctrl+4),HL:LD (ctrl+8),HL LD HL,&FB00:LD (ctrl+2),HL LD H,0:LD (ctrl+6),HL XOR A: CallOsfile: A .SaveCreateTape APOP DE:POP HL:RET :\ On return, C=CFS, NC=DISK, etc (: 2.CallOsfile <LD HL,ctrl:JP OSFILE F: P .CantOpen Z-LD A,214: &07D6:M "Can't open file":NOP d: n.openin :LD A,&40:JR open x.openout:LD A,&80:JR open $.openup :LD A,&C0:.open:JP &218E : .WriteArgs APOP BC :\ BC=0/18 /, A=chn, DEHL=value LD (ctrl+2),DE:LD E,A /LD A,C: A:LD A,1 :\ If C<>0, do = -JR NZ,WriteArgs2:LD A,3 :\ C=0, do = .WriteArgs2  CallArgsHL:JP &0DAA NOP : (]:chk(&328E):[OPT P*3 :\ =&328E PUSH HL:LD H,E: OSBPUT POP HL:RET : .CheckFunction "CP &EB:JR Z,FuncRdMode ,CP &E0:JR Z,FuncRdEnd 6CP &F6:JP Z,FuncRdReport @HJP &2186 :\ Unrecognised extra function - Mistake J: T.FuncRdEnd ^LD HL,(&3AE2):JP &2186 h: r(]:chk(&32AB):[OPT P*3 :\ =&32AB |PUSH HL:LD H,E: OSBGET POP HL:RET : .FuncRdMode LD A,&87: &FFF4 LD L,H:JP &2184 : .EofEnd LD A,127:LD L,E  OSBYTE:LD A,L CPL: A:RET )]:chk(&32C5):[OPT P*3 :\ = =&32C5 JR EofEnd : 3NOP:NOP:NOP:NOP:NOP :\ five spare bytes : )]:chk(&32CC):[OPT P*3 :\ = =&32CC & LD A,2 0 .ReadArgs :LD HL,0:LD (ctrl+2),HL D CallArgsHL NLD HL,(ctrl) XLD DE,(ctrl+2):RET b: l .OsByte00 v/LD HL,0:JP OSBYTE :\ Osbyte A,0,0 : )]:chk(&32E5):[OPT P*3 :\ = =&32E5 XOR A:JR ReadArgs : .CheckCommand 2CP &95:JP Z,&0E5F :\ Enter assembler )CP &DC:JP NZ,&0E41 :\ Not = 2LD C,0 :\ Flag this is = .SetExtPtr 5PUSH BC:JP &148C :\ Save cmd, evaluate : .CallArgsHL LD (ctrl),HL LD HL,ctrl:JP OSARGS : (.GetTime :\ =$/ LD A,(IY+0):CP "$" *JR Z,GetDate 4 &2FC0:JP &21B2 > .GetDate H INC IY RLD HL,&3800:LD (HL),0 \LD A,14: &FFF1 fLD A,(HL):LD E,A p A:JR Z,GetDate2 zLD E,24:.GetDate2 LD D,&38:LD A,&80:RET : (.SetTime :\ $/= LD A,(IY+0):CP "$" JR Z,SetDate  &1758:JP &14B3 .SetDate INC IY: &1758  &1F5F:\ get string LD A,E: A:JR Z,SetDateNull PUSH DE:LD H,D:LD L,E LD C,E:LD B,0:DEC HL LDDR:POP DE .SetDateNull LD HL,&3800:LD (HL),E LD A,15: &FFF1 $ JP &0DAA .: 8%.FuncRdReport :\ = BLD A,(IY+0):CP "$" LJR NZ,RdReport:INC IY V .RdReport `LD HL,(&3AEE):LD DE,&3800 j.RdReportLp tLD A,(HL):LD (DE),A ~INC DE:INC HL BIT 7,E:JR NZ,RdReportEnd  A:JR NZ,RdReportLp  DEC E .RdReportEnd LD A,&80:RET :  .PrReal (LD A,&FF: &328E :\ &FF=real  PutL2: PutH2  PutL1: PutH1 POP BC:LD A,C: A >JR Z,PrRealExp:INC A :\ Put exponent into 6502 form .PrRealExp   &328E:JP &0F84 : .PutH1:LD A,H:JP &328E (.PutL1:LD A,L:JP &328E 2".PutH2:EXX:LD A,H:EXX:JP &328E <".PutL2:EXX:LD A,L:EXX:JP &328E F: P.InpStrEnd ZPOP AF:LD L,A d"POP IX:POP AF:PUSH DE:EX DE,HL n &1611:POP DE:JP &12D7 x .InpReal  GetL2: GetH2  GetL1: GetH1  &32AB:LD C,A CP 2:JP C,AssignVal  DEC C JP AssignVal : .GetH1: &32AB:LD H,A:RET .GetL1: &32AB:LD L,A:RET %.GetH2: &32AB:EXX:LD H,A:EXX:RET %.GetL2: &32AB:EXX:LD L,A:EXX:RET : .SaveFindName !LD A,(IY+0):CP 13:JP NZ,&1F5F &LD HL,(&3ADC):INC HL:INC HL:INC HL $ SaveSkipSpc:CP &F4:JP NZ,&1F5F "% SaveSkipSpc:CP ">":JP NZ,&1F5F ,LD DE,&3800 6.SaveName3 @%LD A,(HL):LD (DE),A:INC DE:INC HL JCP 13:JR NZ,SaveName3:RET T.SaveSkipSpc ^+LD A,(HL):INC HL:CP 32:JR Z,SaveSkipSpc hRET r: |.SaveTooBig POP HL:SCF:RET : 9.SaveTryOsfile :\ Try saving with OSFILE $PUSH HL:LD HL,(&3AE2) :\ ADD HL,BC:JR C,SaveTooBig LD A,H:INC A LD HL,0:ADD HL,SP CP H:JR NC,SaveTooBig POP HL:LD (ctrl),HL #LD HL,(&3AE2):LD (HL),13:INC HL  .SaveCopy ,INC DE:INC DE:LD A,(DE):LD (HL),A:INC HL CP &FF:JR Z,CopyEnd %DEC DE:LD A,(DE):LD (HL),A:INC HL DEC DE:LD A,(DE):LD (HL),A  A:JR Z,CopyEndZero &INC HL:INC DE:INC DE:INC DE 0 .CopyLine :LD A,(DE):LD (HL),A:INC HL DINC DE:CP 13:JR NZ,CopyLine NJR SaveCopy X.CopyEndZero bDEC HL:DEC HL lLD (HL),&FF:INC HL v .CopyEnd !LD DE,(&3AE2):LD (ctrl+10),DE !PUSH HL:PUSH HL:JP SaveOsfile :  .Error1 5LD HL,(&FF82):PUSH HL :\ Push FAULT pointer  .Error2 3POP HL:LD A,(HL):INC HL :\ Get error number 1PUSH HL:JP &07D6 :\ Register error : .ChkEscape 1LD A,(&FF80): A:RET P :\ No Escape state  .MkEscape LD A,&7E: OSBYTE 8JP &0E84 :\ Generate Escape error  :  .InitEnv  6LD A,&C3:LD (&38),A :\ Init RST &38 vector *LD HL,Error2:LD (&39),HL 43LD HL,Error1:LD (&FFFA),HL :\ Init BRKV vector >LD A,&E5: OsByte00 HLD A,&E6: OsByte00 R- GetCmdLine:PUSH AF :\ Save cmd flag \0LD A,&84: OSBYTE:EX DE,HL :\ DE=MEMTOP -> f.LD A,&83: &FFF4 :\ HL=MEMBOT -> p=LD L,0:LD A,H:CP &3B :\ Check if MEMBOT is too low zJR NC,InitPageOk 9LD H,&3B :\ Force MEMBOT=end of ws .InitPageOk 3POP AF:RET :\ NZ=param present : 7.RdLine :\ Read a line of input LD IX,&374D LD (IX+0),L:LD (IX+1),H 0LD (IX+2),&FF :\ max=255 chars 0LD (IX+3),&20:LD (IX+4),&FF :\ chars &20-&FF 4LD A,&DA: OsByte00 :\ Clear scroll counter +XOR A:LD HL,&374D: OSWORD :\ Read line @JR C,MkEscape:XOR A:RET :\ Generate Escape if escape set : <.GetCmdLine :\ Final code, so expandable 7XOR A:LD DE,&3800:LD HL,&80 :\ DE=strbuf, HL=cmdbuf 1LD C,(HL):INC HL :\ Get cmd length $?CP C:JR Z,GetNoCmd:LDIR :\ Copy command pars if present . .GetNoCmd 87EX DE,HL:LD (HL),13:RET :\ Put terminating B: L;.SpareStart :\ Where spare space starts V] `: j2P%=addr(&0E3D):[OPT P*3:JP CheckCommand:NOP:] t% Check for functions as commands ~2 Must be done last, or interpreter falls over : "Saving Z80BAS" 0 test% "**SAVE Z80BAS 100 "+~SpareStart  (ݤaddr(A%): test% =A%+&A000 =A% Ichk(A%):P%-(test% &A000)<>A%:"Misalignment at ";~A%;" (";~P%;")"  :  assm2  mcode% &11F:start=&B000 ServStart=SpareStart !Patch=&F5AE : Patch area 4ExecPatch=&F823 : Address of RunExec patch area  P=0 1 P%=start:O%=mcode% [OPT P*3+4 (JR LangZ80:B &B0 2B &4C:W &8000+ServStart < B &E8 FB copy-start P B VER$ ZM "Z80 BASIC" dB 0 nM VER$+" ("+DATE$+")" x .copy B 0 M "(C)J.G.Harston" B 0 W start:W 0 B 0:\ Align to &803A: \ $\ This point NEEDS BE at &803A %\ For 6502/Z80 lang entry to work  .LangZ80 LD HL,start+&100:LD DE,&100  .RunExec1 H:POP HL :\ Enter pure code - balance stack R .RunExec2 \:JP Z,&F4A9 :\ Check rom header - not Z80 code f6CP A:LD HL,(&FCA8): :\ Z=1, flag stack as balanced p%JP (HL) :\ Enter code z .PatchEnd : 1JP RunExec2-PatchStart+Patch:\ Check rom code 0JP RunExec1-PatchStart+Patch:\ Balance stack : .GBPBTable B 0:W &80:W 0 8\W 0:\W 0:\W 0:\W 0:\ This can be trimmed to fit : M &B100-P%,0) : \ Must be within &B100 ] "'"Saving Z80 BASIC base code" H test% "**SAVE Z80R1 "+~mcode%+" "+~O%+" "+~start+" "+~start