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:
DIM ctrl% 31:X%=ctrl%:Y%=X%DIV256
Where a function requires other memory, it is shown before the function definition, such as:
DIM name% 80
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 Library.BLib directory on the System Startup disk.
FNbyte
returns the single byte in the X register for the specified call.
FNfx
returns the two-byte value held in XY.
DEFFNbyte(A%,X%,Y%)=(USR&FFF4 AND &FF00)DIV256
DEFFNfx(A%,X%):LOCAL Y%:Y%=X%DIV256:=(USR&FFF4 AND &FFFF00)DIV256
As an example, FNfx(133,mode%)
returns HIMEM for the specified mode, and
FNbyte(135,0,0)
returns the character under the cursor. FNfx(135,0)DIV256
returns the current screen mode number.
OSFILE Calls - BLib.FileIO This call performs an OSFILE function, returning the object type from the A register.
DEFFNfile(A$,A%):$name%=A$:?X%=name%:X%?1=name%DIV256:=(USR&FFDD)AND&FF DIM name% 127
FNfile("FRED",5)
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. PROCgbpb
makes a general call.
Calling FNgbpb
with A% passed as 5, 6 or 7 returns the name of the current
disk, the current directory or the current library respectively. FNgbpb8
reads a filename from the current directory, returning a null string if at
the end. The updated pointer is returned in the control block.
DEFPROCgbpb(A%,chn%,addr%,num%,ptr%)
DEFFNgbpb(A%):X%!1=name%:CALL &FFD1:A%=name%+((1+?name%)AND((A%AND-2)=6))
DEFFNgbpb8(ptr%):X%!1=name%:X%!5=1:X%!9=ptr%:A%=8:CALL&FFD1:IFX%!5=1:="" DIM name% 31
?X%=chn%:X%!1=addr%:X%!5=num%:X%!9=ptr%:CALL &FFD1:ENDPROC
A%?(1+?A%)=13:=$(A%+1)
A%=name%+1:A%!(A%?-1)=&D20:A%?(INSTR($A%," ")-1)=13:=$A%
After calling FNgbpb8()
, X%!9 holds the ptr% to be used on the next call.
After calling FNgbpb()
with A%=6 or 7 using
name%?(1+?name%)=13:Drive$=$(name%+1)
will set Drive$ to the ASCII drive identifier for that directory.
OSARGS Calls - BLib.FileIO
These two functions allow you to make OSARGS calls. FNargsA
returns the
value from the A register, ignoring the zero page data. FNargs
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.
DEFFNargs(A%,Y%,ptr%):LOCAL X%,E%:IF?(TOP-3)=0:E%=Y%:Y%=0
used%=FNargs(4,0,0) :REM Get disk space used DEFFNargsA(A%):IFHIMEM<&10000:LOCAL X%,Y%,E%,!&70:X%=&70:=(USR&FFDA)AND&FF
SYS"OS_Args",A% TO A%:=A%
IFHIMEM<&10000:LOCAL !&70:X%=&70:!X%=ptr%:CALL&FFDA:=!X%
SYS"OS_Args",A%,Y%,ptr% TO ,,ptr%:=ptr%
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%
FNfs
returns the current filing system number.
DEFFNfs:LOCAL A%,Y%,E%:=(USR&FFDA)AND&FF
Time and Date - BLib.Time
BASIC IV (on the Master) and V (on the Archimedes) have a =TIME$
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 FNtime
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.
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%
The function FNTime
checks for any onboard RTC and if none is found will
also check for any fileserver clock.
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)
The function FNdate
returns the current date in the format &yyyymmdd. So,
if it is called with T%=FNdate
, then T%DIV65536
would give the current
year.
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))
Time and Date - BLib.Date
The functions FNDay
and FNMon
return three-character string containing the
specified day or month. FNDay
and FNMon
are also in the Time library.
DEFFNDay(A%):=MID$("000SunMonTueWedThuFriSat",A%*3+1,3)
DEFFNMon(A%):=MID$("000JanFebMarAprMayJunJulAugSepOctNovDecDDDEEEFFF",
... A%*3+1,3)
The function FNDate_DayOfWeek
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 FNDay
. FNDate_DayOfWeek
is also in
the Time 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
PROCDate_FromOrd
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.
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:ENDPROC
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
PROCDate_FromOrd(X%+6,.......)
X%!2=X%?10 OR type%*&100 OR &FFF00000
where X% is an OSFILE control block, or:
PROCDate_FromOrd(mem%,.......)
exec%=!mem%:load%=mem%?4 OR type%*&100 OR &FFF00000
to get load and execution addresses separately.
PROCDate_ToOrd
converts a 5-byte centisecond count into a time and date.
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
ENDPROC
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
FNDate_Since
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.
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&83D6
Number output - BLib.Number
The function FNh0
converts the number A% to a N%-digit hexadecimal string
padded with zeros. FNd0
does the same, returning a decimal string. FNh
and
FNd
return a N%-digit hexadecimal and decimal strings, padded with spaces.
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%)
HADFS can support up to 32 drives, numbered from 0 to 9 and A to V. The
function FNdrv
gives the drive character "0" to "V" for the specified
drive number 0 to 31. FNDrv
converts the other way, giving the drive
number from the given drive character.
DEFFNdrv(A%)=CHR$(48+A%-7*(A%>9))
DEFFNDrv(A$)=ASCA$-48+7*(A$>"9")AND31
FNIntToTxt
converts the number A% to the English text string representing
that number.
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)
Directory pathname - BLib.Pathname
The function FNPath_Name
returns the pathname of the current directory,
prefixed with the disk number. The function FNPath_Full
is almost the
same, but it returns the disk name instead of the disk number.
DEFFNPath_Name:A%=6 DIM name% 31
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$
The functions FNPath_Lib
, FNPath_LibFull
, FNPath_URD
and FNPath_URDFull
use the FNPath_Name
and FNPath_Full
functions to give the pathnames of the
library and user root directories.
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$
This is an example of what would be returned by the functions:
FNPath_Name
would return :0.$.Progs.Temp
FNPath_Full
would return :ProgramDisk.$.Progs.Temp
FNPath_Lib
would return :1.$.Library
FNPath_LibFull
would return :UtilsDisk.$.Library
FNPath_URD
would return :0.$.User1
FNPath_URDFull
would return :MyDisk.$.User1
These functions will work on any Acorn machine, including Archimedes/RISC
OS, and on any filing system that recognises the ^ directory. The short
name functions (ie FNPath_Name
, FNPath_Lib
, FNPath_URD
) are suitable for
using as a pathname, eg for *CAT
or *DIR
. 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.
*MCode
command allow BASIC programs to be run as
*
commands. 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 PROG was run with *PROG Hello
, then FNOS_GetEnv
would return
"Hello" and run$ would be set to "*PROG" if MCoded or "PROG" if CHAINed or
"".
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)
=""
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 FNOS_GetEnv
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 SYS
call
OS_GetEnv. On pre-BASIC V, the SYS
token is listed and can be entered as
LOADATN
with no spaces between LOAD
and ATN
. 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 ETree and
TreeCopy.
The following function can be used to run a BASIC program or *
command,
passing it a command line:
DEFPROCos(A$):IFA$=""ORASCA$=42:OSCLIA$:ENDPROC ELSE CHAINA$
If the passed command starts with a '*
', it is called as a command. If
not, it is CHAINed as a BASIC program.
FNhadfs_ver
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.
DEFFNhadfs_ver:LOCAL A%,X%,Y%,E%
A%=&FD:X%=&70:A%=(USR&FFDA)AND&FF:=((A%+3)AND&FF)-3
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
IF FNhadfs_ver<40 THEN PRINT "Must be HADFS 0.40 or later":END
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 *DIR
, and then restored
later with OSRAG &FD. The LIB and URD can also be preserved and later
restored in this way.
csd%=FNargs(&FE,25,0):REM Read CSD
*DIR somewhere
:
:
Do something here
:
:
csd%=FNargs(&FD,25,csd%): Restore CSD
The *Disks
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
*INSTALL d $ Name DxxxK
where xxx
is the amount of memory available, and d
is the drive number. Setting drive=27 gives drive R.
The file $.Extras.JimDisk/s 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 Intern/s is highly optimised, but is thereby much harder to read.
addr=&A8:sect=addr+4:ptr=&AE addr=&A8:sect=addr+4:ptr=&F0
Redirect WORDV to point to OswordCall, .Service8:\ Unknown OSWORD
.NotForMe .NotForMe
JSR NextAddress
:RAM-based intercepting WORDV ROM-based using service call 8
drive=27:REM drive R drive=27:REM drive R
with old WORDV preserved in OldWord. LDA &EF:CMP #90:BEQ Osword90
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
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
INKEY-256
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.
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)
=""
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