>Z80CONV  Z80 BBCBASIC Converter " By J.G.Harston, (C)1989-2009 ( 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 ? 01/03/09 v1.26: ROM header checks Tube after Tube startup : )<>&3B00:"Wrong version of BASIC": +test%=:VER$="1.26":DATE$="01 Mar 2009" 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: 6ChkEscape=&366E @: J P=0 1 T3P%=addr(&011B):[OPT P*3 :\ Pass command to ^JP oscli:] h: r6P%=addr(&0143):[OPT P*3 :\ Get host environment | 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 D OSWRCH:] N: X2P%=addr(&0DAE):[OPT P*3 :\ Check for Escape b ChkEscape:] l: v>P%=addr(&0D06):[OPT P*3 :\ Check for #= as well as #= W 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 44CP 5:JP Z,PrReal :\ &05=real, &04=int >+LD A,&40: &328E :\ &40=Integer H PutH1: PutL1 R PutH2: PutL2 \4POP BC:JR addr(&0F84) :\ Loop for next item f: p .PrString zLD H,&38:LD L,E:POP DE *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 8POP IX:POP AF BPUSH DE: &15EA:POP DE L' prompt ]:chk(&132A) : /P%=addr(&13A3):[OPT P*3 :\ No before JR addr(&13A8):] : .P%=addr(&13AF):[OPT P*3 :\ Input a line  RdLine:]  : 6 P%=FNaddr(&1460):[OPT P*3 :\ Do NL before REPORT = CALL report:] :REM Compatible with ?(TOP-2) (: 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: OSBPUT :\ Initial CR / SaveWriteLp :\ Save with BPUTs XOR A:JP OSFIND : ;.SaveWriteLp :\ Save program using BPUTs &LD A,(DE): A :\ Get HLD A,&FF:JP Z,OSBPUT :\ If &00, output end-of-prog and return .INC DE:INC DE :\ Point to HI & PutByte :\ Put HI && PutByteDec :\ Put LO 0% PutByteDec :\ Put :0INC DE:INC DE:INC DE :\ Point to text D.SaveWrite N- PutByte:INC DE :\ Put each byte XCP 13:JR NZ,SaveWrite bJR SaveWriteLp l: v.PutByteDec  DEC DE  .PutByte LD A,(DE):JP OSBPUT :  .report / &FFE7:JP &0CE2 :\ Put NL before : 2NOP:NOP :\ Two spare bytes : 9]:chk(&31BB):[OPT P*3 :\ Load BASIC program =&31BB ;LD (ctrl),HL:LD (ctrl+2),DE :\ HL=>filename, 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  IINC DE :\ 6502-style program has to be reordered *9.LoadLoop :\ HL=>+1, DE=>+1 4*INC DE:INC DE:LD A,(DE):LD (HL),A:\ >&DEC DE:LD A,(DE):LD B,A :\ LO H&DEC DE:LD A,(DE) :\ HI R&INC HL:LD (HL),B :\ LO \&INC HL:LD (HL),A :\ HI f7INC A:JR Z,LoadEnd :\ End of BASIC program p:INC H:JR Z,LoadEnd :\ Gone past top of memory zDEC 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 $JR LoadLoop .: 8 .LoadEnd BDEC HL:DEC HL:LD (HL),0 L .LoadOk V3SCF:RET :\ Flag OK and exit `: j;.SaveCreate :\ Try to create empty file tLD (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 : .CallOsfile (LD HL,ctrl:JP OSFILE 2: < .CantOpen F-LD A,192: &07D6:M "Can't save file":NOP P: Z.openin :LD A,&40:JR open d.openout:LD A,&80:JR open n$.openup :LD A,&C0:.open:JP &218E x: .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 "CP &F6:JP Z,FuncRdReport ,HJP &2186 :\ Unrecognised extra function - Mistake 6: @.FuncRdEnd JLD HL,(&3AE2):JP &2186 T: ^(]:chk(&32AB):[OPT P*3 :\ =&32AB hPUSH HL:LD H,E: OSBGET rPOP 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  .ReadArgs &LD HL,0:LD (ctrl+2),HL 0 CallArgsHL :LD HL,(ctrl) DLD DE,(ctrl+2):RET N: X .OsByte00 b/LD HL,0:JP OSBYTE :\ Osbyte A,0,0 l: v)]: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  &2FC0:JP &21B2 * .GetDate 4 INC IY >LD HL,&3800:LD (HL),0 HLD A,14: &FFF1 RLD A,(HL):LD E,A \ A:JR Z,GetDate2 fLD E,24:.GetDate2 pLD D,&38:LD A,&80:RET z: (.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 : $%.FuncRdReport :\ = .LD A,(IY+0):CP "$" 8JR NZ,RdReport:INC IY B .RdReport LLD HL,(&3AEE):LD DE,&3800 V.RdReportLp `LD A,(HL):LD (DE),A jINC DE:INC HL tBIT 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 ".PutH2:EXX:LD A,H:EXX:JP &328E (".PutL2:EXX:LD A,L:EXX:JP &328E 2: <.InpStrEnd FPOP AF:LD L,A P"POP IX:POP AF:PUSH DE:EX DE,HL Z &1611:POP DE:JP &12D7 d .InpReal n GetL2: GetH2 x 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 ".SaveName3 ,%LD A,(HL):LD (DE),A:INC DE:INC HL 6CP 13:JR NZ,SaveName3:RET @.SaveSkipSpc J+LD A,(HL):INC HL:CP 32:JR Z,SaveSkipSpc TRET ^: h.SaveTooBig rPOP 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  .CopyLine &LD A,(DE):LD (HL),A:INC HL 0INC DE:CP 13:JR NZ,CopyLine :JR SaveCopy D.CopyEndZero NDEC HL:DEC HL XLD (HL),&FF:INC HL b .CopyEnd l!LD DE,(&3AE2):LD (ctrl+10),DE v!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  3LD HL,Error1:LD (&FFFA),HL :\ Init BRKV vector *LD A,&E5: OsByte00 4LD A,&E6: OsByte00 >- GetCmdLine:PUSH AF :\ Save cmd flag H0LD A,&84: OSBYTE:EX DE,HL :\ DE=MEMTOP -> R.LD A,&83: &FFF4 :\ HL=MEMBOT -> \=LD L,0:LD A,H:CP &3B :\ Check if MEMBOT is too low fJR NC,InitPageOk p9LD H,&3B :\ Force MEMBOT=end of ws z.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 $7EX DE,HL:LD (HL),13:RET :\ Put terminating .: 8;.SpareStart :\ Where spare space starts B] L: V2P%=addr(&0E3D):[OPT P*3:JP CheckCommand:NOP:] `% Check for functions as commands j2 Must be done last, or interpreter falls over t: ~"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 B &4C:W &8000+ServStart ( B &E8 2B copy-start < B VER$ FM "Z80 BASIC" PB 0 ZM VER$+" ("+DATE$+")" d .copy nB 0 xM "(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  .RunExec2 H:JP Z,&F4A9 :\ Check ROM header - not Z80 code R6CP A:LD HL,(&FCA8): :\ Z=1, flag stack as balanced \%JP (HL) :\ Enter code f .PatchEnd p: z1JP 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