Q7 HADFS programmingq Qexamplesq   This chapter gives some examples of calling various HADFS functions, and provides some useful BASIC functions and procedures for incorporating into programs. Some of the functions need some memory reserved for control block and data. Where a control block is used, it must be set by the main program before using the functions, and X% and Y% must be set to point to it with the following code: B DIM ctrl% 31:X%=ctrl%:Y%=X%DIV256b Where a function requires other memory, it is shown before the function definition, such as: B DIM name% 80b In the listings given, no line numbers are shown, as none are needed. Where a line is too long to fit on the page, it is continued on the next line indented. Unless otherwise stated, these functions will work on all the following varieties of BBC BASIC: 6502 BBC BASIC on I/O processor, Tube, RISC OS 65Host and 65Tube emulators 32000 BBC BASIC on the 32016 second processor ARM BBC BASIC V on RISC OS and ARM second processor BBC BASIC(Z80) using BBC I/O on Z80 Tube or Archimedes Tube emulator Most will also work on BBC BASIC(86) and BBC BASIC(Win) for PCs and BBC BASIC(X) for UNIX. These functions are all supplied in the ILibrary.BLibi directory on the System Startup disk. DGeneral functions - BLib.MOSd OSBYTE Calls BFNbyteb returns the single byte in the X register for the specified call. BFNfxb returns the two-byte value held in XY. B DEFFNbyte(A%,X%,Y%)=(USR&FFF4 AND &FF00)DIV256 DEFFNfx(A%,X%):LOCAL Y%:Y%=X%DIV256:=(USR&FFF4 AND &FFFF00)DIV256b As an example, BFNfx(133,mode%)b returns HIMEM for the specified mode, and BFNbyte(135,0,0)b returns the character under the cursor. BFNfx(135,0)DIV256b returns the current screen mode number. OSFILE Calls - BLib.FileIO This call performs an OSFILE function, returning the object type from the A register. B DIM name% 127 DEFFNfile(A$,A%):$name%=A$:?X%=name%:X%?1=name%DIV256:=(USR&FFDD)AND&FFb BFNfile("FRED",5)b would read the catalogue information for the file FRED. The catalogue information is put into memory with X%!2=load, X%!6=exec, X%!10=length and X%!14=attributes. OSGBPB Calls - BLib.FileIO These procedures and functions call OSGBPB. BPROCgbpbb makes a general call. Calling BFNgbpbb with A% passed as 5, 6 or 7 returns the name of the current disk, the current directory or the current library respectively. BFNgbpb8b reads a filename from the current directory, returning a null string if at the end. The updated pointer is returned in the control block. B DIM name% 31 DEFPROCgbpb(A%,chn%,addr%,num%,ptr%) ?X%=chn%:X%!1=addr%:X%!5=num%:X%!9=ptr%:CALL &FFD1:ENDPROC DEFFNgbpb(A%):X%!1=name%:CALL &FFD1:A%=name%+((1+?name%)AND((A%AND-2)=6)) A%?(1+?A%)=13:=$(A%+1) DEFFNgbpb8(ptr%):X%!1=name%:X%!5=1:X%!9=ptr%:A%=8:CALL&FFD1:IFX%!5=1:="" A%=name%+1:A%!(A%?-1)=&D20:A%?(INSTR($A%," ")-1)=13:=$A%b After calling BFNgbpb8()b, X%!9 holds the ptr% to be used on the next call. After calling BFNgbpb()b with A%=6 or 7 using B name%?(1+?name%)=13:Drive$=$(name%+1)b will set Drive$ to the ASCII drive identifier for that directory. OSARGS Calls - BLib.FileIO These two functions allow you to make OSARGS calls. BFNargsAb returns the value from the A register, ignoring the zero page data. BFNargsb sets and returns data passed in zero page. When reading a value, the zero page parameter is ignored, so you can set it to zero on entry. When writing a value, some calls return a value, often the same as the value just written. B DEFFNargsA(A%):IFHIMEM<&10000:LOCAL X%,Y%,E%,!&70:X%=&70:=(USR&FFDA)AND&FF SYS"OS_Args",A% TO A%:=A% DEFFNargs(A%,Y%,ptr%):LOCAL X%,E%:IF?(TOP-3)=0:E%=Y%:Y%=0 IFHIMEM<&10000:LOCAL !&70:X%=&70:!X%=ptr%:CALL&FFDA:=!X% SYS"OS_Args",A%,Y%,ptr% TO ,,ptr%:=ptr% used%=FNargs(4,0,0) :REM Get disk space used free%=FNargs(5,0,0) :REM Get disk free space size%=free%+used% :REM Find disk size dummy%=FNargs(1,chn%,num%) :REM identical to PTR#chn%=num%b BFNfsb returns the current filing system number. B DEFFNfs:LOCAL A%,Y%,E%:=(USR&FFDA)AND&FFb Time and Date - BLib.Time BASIC IV (on the Master) and V (on the Archimedes) have a =BTIME$b function to read the real-time clock (RTC). HADFS provides a RTC function, but BASIC II (on the B/B+) does not have a function to read it. Also, the 6502 second processor only reads 24 bytes instead of 25, and so loses the terminator. The function BFNtimeb will read the RTC regardless of what machine it is running on, and will correct for years after 1996 and 1999, or return a null string if no time is available. The 'Acorn Era' is defined as starting in 1981, so the century range is 1981-2080. B DEFFNtime:?X%=0:A%=14:CALL&FFF1:IF?X%=0:="" X%?25=13:A%=VAL$(X%+4):$(X%+4)=RIGHT$("0"+STR$(A%AND31),2) $(X%+11)=STR$(INTVAL$(X%+11)+(A%AND&E0)DIV2-100*(INTVAL$(X%+11)<1981)) X%?6=32:X%?15=46:=$X%b The function BFNTimeb checks for any onboard RTC and if none is found will also check for any fileserver clock. B DEFFNTime:!X%=1:A%=14:CALL&FFF1:FORA%=0TO7:X%?A%=VALSTR$~X%?A%:NEXT ?X%=?X%+((X%?2)AND&E0)DIV2:X%?2=X%?2 AND31:IF?X%<81:?X%=?X%+100 IF!X%=101:!X%=&10000700:X%!4=0:A%=20:CALL&FFF1:!X%=0:IFX%!4:?X%=81+(X%?5 ... DIV16)+((X%?4 AND&E0)DIV2):X%?1=X%?5 AND15:X%?2=X%?4 AND31:X%!4=X%!6 IFX%?3=0:IFX%?2:X%?3=FNDate_DayOfWeek(X%?2,X%?1,1900+X%?0) =FNDay(X%?3)+","+FNd0(X%?2,2)+" "+FNMon(X%?1)+" "+FNd0(1900+ ... X%?0,4)+"."+FNd0(X%?4,2)+":"+FNd0(X%?5,2)+":"+FNd0(X%?6,2)b The function BFNdateb returns the current date in the format &yyyymmdd. So, if it is called with BT%=FNdateb, then BT%DIV65536b would give the current year. B DEFFNdate:A%=14:!X%=1:CALL&FFF1:IF!X%=1:=0 ?X%=(VALSTR$~?X%+(((VALSTR$~X%?2)DIV2)AND&F0))MOD100 =(VALSTR$~X%?2 AND31)+256*VALSTR$~X%?1+65536*(6400+?X%-&700*(?X%<128))b Time and Date - BLib.Date The functions BFNDayb and BFNMonb return three-character string containing the specified day or month. BFNDayb and BFNMonb are also in the ITimei library. B DEFFNDay(A%):=MID$("000SunMonTueWedThuFriSat",A%*3+1,3) DEFFNMon(A%):=MID$("000JanFebMarAprMayJunJulAugSepOctNovDecDDDEEEFFF", ... A%*3+1,3)b The function BFNDate_DayOfWeekb returns the day of the week for the given date, month and year. The day number is 1..7 for Sun..Sat and can be converted to the name of the day with BFNDayb. BFNDate_DayOfWeekb is also in the ITimei library. DEFFNDate_DayOfWeek(d%,m%,y%):y%=y%MOD400 =(y%*365.25+m%*30+d%+VALMID$("120112234455",m%,1)+((y%MOD4)=0)- ... ((y%-1)DIV100)-(m%>2AND((y%MOD4)=0AND(y%MOD100)<>0ORy%=0))+3)MOD7+1 BPROCDate_FromOrdb converts the given time and date to a 5-byte centisecond count from 00:00:00 on 1st Jan 1900, as used for RISC OS time-stamps. B DEFPROCDate_FromOrd(mem%,d%,m%,y%,hr%,mn%,sc%,cs%):y%=y%MOD400 d%=y%*365.25+m%*30+d%+VALMID$("120112234455",m%,1)+((y%MOD4)=0)- ... ((y%-1)DIV100)-(m%>2AND((y%MOD4)=0AND(y%MOD100)<>0ORy%=0))+36493 IFd%>146066:d%=d%-146097 d%=d%*&41EB:mem%!1=d%+d%:d%=((hr%*60+mn%)*60+sc%)*100+cs% ?mem%=d%:mem%!1=mem%!1+d%DIV256:ENDPROCb On entry: mem%->five bytes of memory day, month, year of the date hours, minutes, seconds, centiseconds of the time On exit: mem% to mem%+4 containes five-byte centisecond time since 00:00:00 on 1-Jan-1900. The d%+d% is deliberate. It prevents a 'Too big' error happening when &7Fxxxxxxxx overflows to &80xxxxxxxx. The five-byte time range ends at 06:57:57.75, 04/06/2248. The five-byte time can be used to set the load and exec addresses on a file with OSFILE with B PROCDate_FromOrd(X%+6,.......) X%!2=X%?10 OR type%*&100 OR &FFF00000b where X% is an OSFILE control block, or: B PROCDate_FromOrd(mem%,.......) exec%=!mem%:load%=mem%?4 OR type%*&100 OR &FFF00000b to get load and execution addresses separately. BPROCDate_ToOrdb converts a 5-byte centisecond count into a time and date. B DEFPROCDate_ToOrd(mem%):LOCAL A%,B%,C%,D% year%=0:month%=0:day%=0:hour%=0:minute%=0:second%=0:centi%=0 IFmem%!1<0:ENDPROC:REM Problems with negatives ATM D%=mem%!1DIV&83D6+2447065:C%=mem%?0+256*(mem%!1MOD&83D6):centi%=C%MOD100 C%=C%DIV100:second%=C%MOD60:C%=C%DIV60:minute%=C%MOD60:hour%=C%DIV60 B%=((D%*4+3)MOD146097AND-4)+3:C%=B%MOD1461DIV4*5+2:D%=D%*4+3 A%=C%DIV153+2:day%=C%MOD153DIV5+1:month%=A%MOD12+1 year%=D%DIV146097*100+B%DIV1461+A%DIV12-4800 ENDPROCb On entry: mem%->five bytes of memory containing the centisecond count On exit: year%, month%, day% are set to the date hour%, minute%, second%, centisecond% are set to the time BFNDate_Sinceb returns the number of days since a past date. On entry td%, tm% and ty% should be set to today's date, month and year and pd%, pm% and py% should be set to the past date, month and year. B DEFFNDate_Since(td%,tm%,ty%,pd%,pm%,py%):LOCAL past% PROCDate_FromOrd(X%,pd%,pm%,py%,0,0,0,0):past%=X%!1 PROCDate_FromOrd(X%,td%,tm%,ty%,0,0,0,0):=(X%!1-past%)DIV&83D6b Number output - BLib.Number The function BFNh0b converts the number A% to a N%-digit hexadecimal string padded with zeros. BFNd0b does the same, returning a decimal string. BFNhb and BFNdb return a N%-digit hexadecimal and decimal strings, padded with spaces. B DEFFNh0(A%,N%)=RIGHT$("0000000"+STR$~A%,N%) DEFFNh(A%,N%)=RIGHT$(" "+STR$~A%,N%) DEFFNd0(A%,N%)=RIGHT$("000000000"+STR$A%,N%) DEFFNd(A%,N%)=RIGHT$(" "+STR$A%,N%)b HADFS can support up to 32 drives, numbered from 0 to 9 and A to V. The function BFNdrvb gives the drive character "0" to "V" for the specified drive number 0 to 31. BFNDrvb converts the other way, giving the drive number from the given drive character. B DEFFNdrv(A%)=CHR$(48+A%-7*(A%>9)) DEFFNDrv(A$)=ASCA$-48+7*(A$>"9")AND31b BFNIntToTxtb converts the number A% to the English text string representing that number. B DEFFNIntToTxt(A%):LOCAL A$,B$ IF A%<0 AND A%<>-A%:="minus "+FNIntToTxt(-A%) ELSE IF A%<0: ... ="minus two thousand "+FNIntToTxt(147483648) IF A%=0:="zero" IF A%>999999:A$=FNIntToTxt(A% DIV 1000000)+" million": ... A%=A% MOD 1000000 IF A%>999 AND A$<>"":A$=A$+" " IF A%>999:A$=A$+FNIntToTxt(A% DIV 1000)+" thousand":A%=A% MOD 1000 IF A%>99 AND A$<>"":A$=A$+" " IF A%>99:A$=A$+FNIntToTxt(A% DIV 100)+" hundred":A%=A% MOD 100 IF A%=0:=A$ ELSE IF A$<>"":A$=A$+" and " IF A%>12 AND A%<20: ... B$=MID$("thir four fif six seveneigh nine",(A%-12)*5-4,5): ... =A$+LEFT$(B$,INSTR(B$+" "," ")-1)+"teen" IF A%>10 AND A%<13:=A$+MID$("eleventwelve",(A%-10)*6-5,6) B$=MID$("ten twenty thirty fourty fifty sixty seventyeighty ... ninety",(A%DIV10)*7-6,7):B$=LEFT$(B$,INSTR(B$+" "," ")-1) A$=A$+B$:IF A%>20 AND A%MOD10<>0 AND A$<>"":A$=A$+"-" A%=A%MOD10:B$=MID$("one two threefour five six seveneightnine", ... A%*5-4,5):=A$+LEFT$(B$,INSTR(B$+" "," ")-1)b Directory pathname - BLib.Pathname The function BFNPath_Nameb returns the pathname of the current directory, prefixed with the disk number. The function BFNPath_Fullb is almost the same, but it returns the disk name instead of the disk number. B DIM name% 31 DEFFNPath_Name:A%=6 DEFFNPath_Full:A%=&106 LOCAL n$,p$:REPEAT:X%!1=name%:CALL &FFD1 ?(name%+2+?name%+?(name%+?name%+1))=13 n$=$(name%+2+?name%):*DIR ^ n$=LEFT$(n$,INSTR(n$+" "," ")-1) p$=n$+"."+p$:UNTILn$="$"ORn$="&" p$=LEFT$(p$,LENp$-1):OSCLI"DIR "+p$ IFA%=6:X%!1=name%:CALL &FFD1:?(name%+1+?name%)=13:n$=$(name%+1):IF ... n$<>"":=":"+n$+"."+p$ X%!1=name%:A%=5:CALL &FFD1 ?(name%+1+?name%)=13:n$=$(name%+1) n$=LEFT$(n$,INSTR(n$+" "," ")-1):=":"+n$+"."+p$b The functions BFNPath_Libb, BFNPath_LibFullb, BFNPath_URDb and BFNPath_URDFullb use the BFNPath_Nameb and BFNPath_Fullb functions to give the pathnames of the library and user root directories. B DEFFNPath_Lib LOCAL a$,b$:a$=FNPath_Name:*DIR % b$=FNPath_Name:OSCLI"DIR "+a$:=b$ : DEFFNPath_LibFull LOCAL a$,b$:a$=FNPath_Name:*DIR % b$=FNPath_Full:OSCLI"DIR "+a$:=b$ : DEFFNPath_URD LOCAL a$,b$:a$=FNPath_Name:*DIR & b$=FNPath_Name:OSCLI"DIR "+a$:=b$ : DEFFNPath_URDFull LOCAL a$,b$:a$=FNPath_Name:*DIR & b$=FNPath_Full:OSCLI"DIR "+a$:=b$b This is an example of what would be returned by the functions: BFNPath_Nameb would return :0.$.Progs.Temp BFNPath_Fullb would return :ProgramDisk.$.Progs.Temp BFNPath_Libb would return :1.$.Library BFNPath_LibFullb would return :UtilsDisk.$.Library BFNPath_URDb would return :0.$.User1 BFNPath_URDFullb would return :MyDisk.$.User1 These functions will work on any Acorn machine, including Archimedes/RISC OS, and on any filing system that recognises the I^i directory. The short name functions (ie BFNPath_Nameb, BFNPath_Libb, BFNPath_URDb) are suitable for using as a pathname, eg for B*CATb or B*DIRb. The path returned by the full path functions that include the diskname can only be used for filing operations on filing systems that recognise named disks, ie NFS and RISC OS filing systems. DProgram Environment Functionsd Command line tail HADFS, RISC OS and the B*MCodeb command allow BASIC programs to be run as B*bcommands. This function allows the command tail to be retrieved, and also gets the host type and the command that started the program. So if a BASIC program IPROGi was run with B*PROG Hellob, then BFNOS_GetEnvb would return "Hello" and run$ would be set to "*PROG" if MCoded or "PROG" if CHAINed or "". B DEFFNOS_GetEnv:LOCALA$,A%,X%,Y%:X%=1:os%=((USR&FFF4)AND&FF00)DIV256:DIMX%-1 IFos%=32:IFPAGE>&FFFF:SYS"GetModuleFileName",0,X%,255:A$=$$X%:run$=A$:=@cmd$ IFos%=32:A$=$&100 IFLENA$=0:IFHIMEM>&FFFF:run$=$&8100:SYS"OS_GetEnv"TOA$,,A%:SYS"OS_WriteEnv","",A%: ... A$=MID$(A$,1+INSTR(A$+" "," ",1+INSTR(A$," "))):IFLENA$=0:A$=run$ IFLENA$=0:IF?(TOP-3):A$=$&600 ELSE IFLENA$=0:A$=$(PAGE-&300) A%=INSTR(A$+" "," "):run$=LEFT$(A$,A%-1):IFrun$<>"":=MID$(A$,A%+1) Y%=X%DIV256:A%=9:?X%=0:X%!1=X%+16:X%!16=0:CALL&FFD1:A%=X%+16:IF!A%AND?A%+A%?2<>8: ... A%?(A%+1)=13:=$(A%+1) =""b This function works on the BBC/Master I/O processor, the 6502, Z80 and 32016 CoProcessors, Arthur, RISC OS, BBC BASIC(86) and BBC BASIC(Win). It needs 270 free bytes above the end of variable space for temporary workspace. As BFNOS_GetEnvb is usually called before most variables are set there should be sufficient space. On an Arthur and RISC OS it gets the command tail using the BSYSb call OS_GetEnv. On pre-BASIC V, the BSYSb token is listed and can be entered as BLOADATNb with no spaces between BLOADb and BATNb. Next, any command line passed with BBC BASIC for Windows is looked for. Then the command line passed on BBC BASIC(86), BBC BASIC(65) and BBC BASIC(80) is looked for; and if there is no run name, it finally checks to see if there is an HADFS or UNIX command tail available. If no command tail can be found anywhere, then a null string is returned. This function is used in programs like IETreei and ITreeCopyi. The following function can be used to run a BASIC program or B*bcommand, passing it a command line: B DEFPROCos(A$):IFA$=""ORASCA$=42:OSCLIA$:ENDPROC ELSE CHAINA$b If the passed command starts with a 'B*b', it is called as a command. If not, it is CHAINed as a BASIC program. DHADFS functionsd HADFS Version number BFNhadfs_verb returns the HADFS binary version number. This is the version number*100 for versions before 0.52 and version number*100 for versions 5.20 onwards, ie HADFS 0.48 returns 48 and HADFS 5.20 returns 52. B DEFFNhadfs_ver:LOCAL A%,X%,Y%,E% A%=&FD:X%=&70:A%=(USR&FFDA)AND&FF:=((A%+3)AND&FF)-3b The +3 and -3 are so that early version of HADFS, or non-HADFS filing systems return a value of -3 so easy comparisons can be made, eg B IF FNhadfs_ver<40 THEN PRINT "Must be HADFS 0.40 or later":ENDb Preserving directory context OSARGS &FE can be used to read HADFS's Currently Selected Directory (CSD), so that it can be set to something else using B*DIRb, and then restored later with OSRAG &FD. The LIB and URD can also be preserved and later restored in this way. B csd%=FNargs(&FE,25,0):REM Read CSD *DIR somewhere : : Do something here : : csd%=FNargs(&FD,25,csd%): Restore CSDb The B*Disksb command uses this method to switch between disks without losing where it was to start with. Adding extra drives with OSWORD 90 Extra drives can be added by intercepting the OSWORD 90 call. The following is an example listing of some code to do just this by creating a RAM-disk in JIM. JIM is the area of memory at &FD00 which is reserved for access to multiple pages of extra memory. A paging selector is accessed at address &FCFF in FRED. The RAM-disk would have to be initialised with B*INSTALL d $ Name DxxxKb where Bxxxb is the amount of memory available, and Bdb is the drive number. Setting drive=27 gives drive R. The file I$.Extras.JimDisk/si contains the code for intercepting via WORDV. This efficiency of this listing is not what it could be, as it is programed for clarity rather than for speed. The code in IIntern/si is highly optimised, but is thereby much harder to read. BRAM-based intercepting WORDV ROM-based using service call 8 addr=&A8:sect=addr+4:ptr=&AE addr=&A8:sect=addr+4:ptr=&F0 drive=27:REM drive R drive=27:REM drive R Redirect WORDV to point to OswordCall, .Service8:\ Unknown OSWORD with old WORDV preserved in OldWord. LDA &EF:CMP #90:BEQ Osword90 .NotForMe .NotForMe LDX ptr:LDY ptr+1:LDA #90 LDX &F4:LDA #8:LDY ptr+1 .NotOsword90 RTS JMP (OldWord) : .OswordCall CMP #90:BNE NotOsword90 : \ Routine common to RAM or ROM-based: .Osword90 LDY #0:LDA (ptr),Y:BNE NotForMe :\ First check &0600 ID marker INY:LDA (ptr),Y:CMP #6:BNE NotForMe LDY #9:LDA (ptr),Y:CMP drive:BNE NotForMe :\ Check for drive number LDY #11:LDA (ptr),Y:BMI NotForMe:BEQ NotForMe :\ bit7 commands dealt by HADFS CMP #3:BCS NotForMe :\ This only does Read and Write : \ Control block now holds: \ 2..5=addr \ 6..8=sector \ 9 =drive (already checked) \ 10 =number of sectors \ 11 =type 1=write, 2=read : LDX #7 :\ Preserve the memory about to be used .SaveLoop LDA addr,X:PHA:DEX:BPL SaveLoop LDY #2 :\ Get address & sector .AddrLoop LDA (ptr),Y:STA addr-2,Y \ RAM-based routine :\ ROM-based routine: INY:CPY #8:BNE AddrLoop:INY:INY INY:CPY #10:BNE AddrLoop : INY:LDA (ptr),Y:TAX :\ Get number of sectors : INY:LDA (ptr),Y :\ Get Read/Write CMP #1:BEQ Write LDA &27A:BPL ReadLoop LDA addr+2:AND addr+3:CMP #&FF BNE TubeRead : .ReadLoop :\ Read to I/O memory JSR SetSector .ReadLoop2 LDA &FD00,Y:STA (addr),Y:INY BNE ReadLoop2 JSR NextAddress BNE ReadLoop:BEQ IOEnd : .TubeRead :\ Read to Tube memory TXA:PHA :\ Save number LDA #1:JSR ClaimTube PLA:TAX :\ Restore number .TubeReadLoop JSR SetSector .TubeReadLoop2 LDA &FD00,Y:STA &FEE5:INY JSR TubeDelay:JSR TubeDelay BNE TubeReadLoop2 JSR NextAddress BNE TubeReadLoop:BEQ TubeEnd : .Write LDA &27A:BPL WriteLoop LDA addr+2:AND addr+3:CMP #&FF BNE TubeWrite : .WriteLoop :\ Write from I/O memory JSR SetSector .WriteLoop2 LDA (addr),Y:STA &FD00,Y:INY BNE WriteLoop2 JSR NextAddress BNE WriteLoop:BEQ IOEnd : .TubeWrite :\ Write from Tube memory TXA:PHA :\ Save number LDA #0:JSR ClaimTube JSR TubeDelay:JSR TubeDelay :\ Write needs starting delay PLA:TAX :\ Restore number .TubeWriteLoop JSR SetSector .TubeWriteLoop2 LDA &FEE5:STA &FD00,Y:INY JSR TubeDelay:JSR TubeDelay BNE TubeWriteLoop2 JSR NextAddress BNE TubeWriteLoop .TubeEnd JSR TubeRelease .IOEnd LDX #0 :\ Restore memory .LoadLoop PLA:STA addr,X:INX:CPX #8 BNE LoadLoop LDY #11:LDA #0:STA (ptr),Y :\ Claim call RTS : .SetSector :\ Set the JIM page select latch LDA sect+1:STA &FCFE :\ Select hi byte LDA sect:STA &FCFF :\ Select lo byte LDY #0:RTS : .NextAddress :\ Increment sector and addr bytes 1/2/3 INC sect:BNE NextAd2:INC Sect+1 .NextAd2 INC addr+1:BNE NextAd3 INC addr+2:BNE NextAd3 INC addr+3 .NextAd3 DEX RTS : .ClaimTube :\ Tube claim and release routines PHA .ClaimLoop LDA #&C0+36:JSR &406:BCC ClaimLoop PLA LDX #addr AND 255:LDY #addr DIV 256 JMP &406 : .ReleaseTube LDA #&80+36:JMP &406 : .TubeDelay :\ Delays by 12us JSR TubeD2 .TubeD2 RTS b DBBC BASIC on different machinesd BBC BASIC has been implemented on many machines, and it is often useful to find out exactly what hardware a program is running on. On all Acorn hardware, this is simple enough, as BINKEY-256b and OSBYTE 0,1 give unique values for the machine you are using. However, some machines do not implement negative INKEYs, and so would just wait about 12 minutes for you to press a key, and some do not let you do an OSBYTE call. There are ways around this though. In this discussion, the following BASICs will be refered to: BBC BASIC(2/4) 6502 BASIC, as supplied in the B and Master BBC BASIC(32) 32000 BASIC, as supplied with the 32016 CoPro BBC BASIC(V) ARM BASIC, as supplied in the Archimedes or ARM CoProcessors BBC BASIC(Z80) Z80 BASIC, for Acorn CPM, Torch CPN, Tatung Einstein, Cambridge Z88, Amstrad Notepad, or modified for Tube with BBC I/O, Sinclair Spectrum or Amstrad CPC. BBC BASIC(86) 80x86 BASIC, for PC machines BBC BASIC(Win) 80x86 BASIC for PCs running Windows BBC BASIC(X) C-coded BASIC, for UNIX platforms Brandy BASIC Brandy BASIC BBC BASIC(2/4) and BBC BASIC(V) could also be called BBC BASIC(65) and BBC BASIC(ARM). Some of the above can also be run on RISC OS under emulation. BASIC(65), BASIC(32) and BASIC(ARM) store the program in memory starting from PAGE with a CR, and ending with xx, CR, &FF where xx is the last character of the final line. BASIC(Z80), BASIC(86) and BASIC(Win) store the program ending with CR, 00, &FF, &FF. BASIC(65), BASIC(32) and BASIC(ARM) always have access to the MOS and FILE calls and allow INKEY-256. This allows us to use ?(TOP-3)=0 to indicate Z80/86/Win or 65/32000/ARM, and to indicate whether we can make a call to &FFxx or do INKEY-256. OSBYTE 0,1 will return 6 indicating an RISC OS/Archimedes when running on an emulator. BBC BASIC(ARM) running native always has PAGE>&8000. This can be used to indicate native. BASIC(86) starts with HIMEM>&FFFF, and BBC BASIC(Z80) always has HIMEM<&FFFF, so this check can be used to distinguish between Z80 and 80x86. Additionally, Windows always has PAGE>&FFFF and DOS has PAGE<&10000. BBC BASIC(86) allows only three MOS calls, OSBYTE 0,1 which returns 32, OSBYTE 135 to read the character at the cursor position and OSWORD 10 to read character definitions BASIC(Z80) may run under CPM or not under CPM. All CPM calls go through a jump vector at address &0005. In some BASIC(Z80) environments, the &0005 call returns zero to indicate non-CPM mode. In other environments, there isn't a jump vector there at all. If we first test to see if there is a jump vector there and if there is call it with C% set to 12 to ask for the CPM version number, we can find out if we are running under CPM. If the call returns zero, then we are running in a non-CPM environment that allows MOS and FILE calls. If we are running in a non-CPM environment we are probably running on a Cambridge Z88 or an NC100. The Z88 has its reset routine at location 0 starting with a &F3 byte. This distinguishes between Z88 and NC100, neither of which have any MOS or FILE calls. B REM Works on I/O,T6502,Arc,TZ80bbc,PC,CPM,Unix,NC,Z88,Einstein DEFFNOS_GetEnv:LOCAL A%,X%,Y%,A$ cpm%=0:dos%=0:win%=0:bbc%=0:os%=-1:arc%=0:unix%=0:z88%=0:nc%=0 bbc%=?(TOP-3)<>0:IFNOTbbc%:dos%=HIMEM>&FFFF: ... IFNOTdos%:cpm%=?5=&C3:IFcpm%:C%=12:IFUSR5=0:bbc%=TRUE:cpm%=0 IFbbc%ORdos%:A%=0:X%=1:os%=(USR&FFF4 AND&FF00)DIV256 IFNOTbbc%ANDNOTdos%ANDNOTcpm%:z88%=?0=&F3:nc%=NOTz88%:A$=$&A000 arc%=(os%=6 AND PAGE>&8000):unix%=os%=8 IFarc%:LOADATN"OS_GetEnv"TOA$:A$=MID$(A$,1+INSTR(A$," ",1+INSTR(A$," "))) DIMX%-1:IFos%=32:IFPAGE>&FFFF:win%=TRUE:SYS"GetModuleFileName",0,X%,255: ... X%?255=13:A$=LEFT$($X%,INSTR($X%,CHR$0)-1):=@cmd$ ELSE IFos%=32:A$=$&100 IFLENA$=0:IFbbc%:A$=$&600 ELSE IFLENA$=0:A$=$&3800 A%=INSTR(A$+" "," "):run$=LEFT$(A$,A%-1):IFrun$<>"":=MID$(A$,A%+1) DIMX%-1:Y%=X%DIV256:A%=9:?X%=0:X%!1=X%+16:X%!16=0:CALL&FFD1:A%=X%+16: ... IF!A%AND?A%+A%?2<>8:A%?(1+?A%)=13:=$(A%+1) =""b The above routine returns any command line parameters and sets the following global variables: run$ is set to the command used to start the program, os% contains the host operating system value (see below); cpm%, dos%, win%, bbc%, unix%, arc%, z88% and nc100% are flags indicating what hardware is running. These can be used to indicate the following: os% Host os value (see below) run$ Pathname used to start the program bbc% Calls to &FFxx and INKEY-256 ok, names are :d.$.dirname.leafname cpm% Calls to &0005 allowed, filenames are d:filename.xtn dos% filenames are d:\dirname\filename.xtn win% running on 32-bit Windows, long filenames and SYS available unix% filenames are /dirname/filename arc% extended BASIC functions allowed nc% running on an Amstrad NC100/150/200 z88% running on a Sinclair Z88 Host OS values returned in os% include the following: -1 : Unknown 6 : Archimedes/Acorn 0 : Electron 7 : Springboard 1 : BBC 8 : Unix 2 : BBC B+ 3 : Master 128 30 : Amstrad CPC 4 : Master ET 31 : Spectrum 5 : Master Compact 32 : IBM-type PC