Difference between revisions of "Relocating variables on the 6502 Second Processor"

From BeebWiki
Jump to: navigation, search
m (Fixed some spelling errors - please try to spell check submissions and try not to confuse lose and loose.)
 
m (.)
 
(4 intermediate revisions by one other user not shown)
Line 1: Line 1:
 
[[Category:BASIC]]
 
[[Category:BASIC]]
 
A 6502 second processor gives you an extra 64K of memory, but not all
 
A 6502 second processor gives you an extra 64K of memory, but not all
programs take advantage of this. When BASIC is entered, it is copied over
+
programs take advantage of this. When BASIC is entered, it is copied over to
to the second processor and runs from there. If you have HiBASIC, it is
+
the second processor and runs from there. If you have HiBASIC, it is
assembled to run at &B800 giving you memory from &800 to &B800 giving 44k
+
assembled to run at &B800 giving you memory from &800 to &B800 giving 44K
available. However, this means losing a ROM socket, as you need HiBASIC
+
available. However, this means losing a ROM socket, as you need HiBASIC to
to run on the second processor and BASIC (assembled to run at &8000) when
+
run on the second processor and BASIC (assembled to run at &8000) when
running with the second processor turned off. Most people end up using
+
running with the second processor turned off. Most people end up using BASIC
having BASIC at &8000 as this runs on both sides. Unfortunately, this
+
at &8000 as this runs on both sides of the Tube. Unfortunately, this means
means that the spare memory at &C000 to &F800 in the second processor is
+
that the spare memory at &C000 to &F800 in the second processor is wasted.
wasted.
 
  
 
     FFFF +-----------+                FFFF +-----------+
 
     FFFF +-----------+                FFFF +-----------+
Line 29: Line 28:
  
 
The second processor introductory guide suggests putting LOMEM=&C000 and
 
The second processor introductory guide suggests putting LOMEM=&C000 and
storing variables above BASIC. This works perfectly well, but it implies
+
storing variables above BASIC. This works perfectly well, but it implies
that your program is always going to be massively larger than the
+
that your program is always going to be massively larger than the variables
variables it uses. You have 30K for the program but only 14K for
+
it uses. You have 30K for the program but only 14K for variables. What would
variables. What would be more useful it to put the BASIC program above
+
be more useful it to put the BASIC program above BASIC at &C000 and put the
BASIC at &C000 and put the variables in &800-&8000, giving more space for
+
variables in &800-&8000, giving more space for variables than for the
variables than for the program.
+
program.
  
 
     FFFF +-----------+                FFFF +-----------+
 
     FFFF +-----------+                FFFF +-----------+
Line 58: Line 57:
 
     PAGE=&C000:LOMEM=&800
 
     PAGE=&C000:LOMEM=&800
  
Unfortunately, it is not quite that simple. BASIC gets confused if PAGE
+
Unfortunately, it is not quite that simple. BASIC gets confused if PAGE is
is higher than HIMEM, you keep getting 'No room' errors whenever you try
+
higher than HIMEM, you keep getting 'No room' errors whenever you try to do
to do something as simple as listing the program. Also, you can't just
+
something as simple as listing the program. Also, you can't just stick a
stick a PAGE move at the start of a program. The program has to be moved
+
PAGE move at the start of a program. The program has to be moved to the new
to the new PAGE position. The easiest way of doing this is to load it
+
PAGE position. The easiest way of doing this is to load it again. This then
again. This then raises the problem of knowing what the program was saved
+
raises the problem of knowing what the program was saved as.
as.
 
  
 
Most programs that reload themselves like this hard-wire the name into the
 
Most programs that reload themselves like this hard-wire the name into the
Line 75: Line 73:
 
       CHAIN "$.Users.Jim.Progs1.MyName"
 
       CHAIN "$.Users.Jim.Progs1.MyName"
  
Fortunately, there's a way of finding this out. When BASIC does a CHAIN,
+
Fortunately, there's a way of finding this out. When BASIC does a CHAIN, it
it passes a string to OSFILE to load the program. This string is stored
+
passes a string to OSFILE to load the program. This string is stored in
in BASIC's string buffer which is at &0600 in 6502-BASIC, and if you don't
+
BASIC's string buffer which is at &0600 in 6502 BASIC, and if you don't do
do any string operations that file name will still be there. Caution -
+
any string operations that file name will still be there. Caution - string
string operations include printing numbers! So, to reload the current
+
operations include printing numbers! So, to reload the current program, we
program, we can do something like:
+
can do something like:
  
 
       CHAIN $&600
 
       CHAIN $&600
Line 89: Line 87:
 
below its code.
 
below its code.
  
To start with, we need to see if we are running 6502-BASIC on a second
+
To start with, we need to see if we are running 6502 BASIC on a second
processor, and change PAGE, HIMEM and LOMEM to suit and reload if
+
processor, and change PAGE, HIMEM and LOMEM to suit and reload if necessary.
necessary. HIMEM cannot be changed inside a PROCedure or FuNction as you
+
HIMEM cannot be changed inside a PROCedure or FuNction as you lose the
lose the return address, so the best thing to do is to return a value to
+
return address, so the best thing to do is to return a value to set HIMEM to
set HIMEM to at the start of the program:
+
at the start of the program:
  
 
       HIMEM=FNhimem0
 
       HIMEM=FNhimem0
  
 
If the program needs to end with the program still in a condition where it
 
If the program needs to end with the program still in a condition where it
can be LISTed, or you want to load another program in the same space,
+
can be LISTed, or you want to load another program in the same space, HIMEM
HIMEM needs to be set above PAGE otherwise 'No room' errors will occur:
+
needs to be set above PAGE otherwise 'No room' errors will occur:
  
 
       DEFPROCend:HIMEM=FNhimem1:END
 
       DEFPROCend:HIMEM=FNhimem1:END
Line 112: Line 110:
  
 
As LOMEM will not be at the end of the program, TOP should not be used to
 
As LOMEM will not be at the end of the program, TOP should not be used to
find out how much memory is available - this is sloppy programming
+
find out how much memory is available - this is sloppy programming practice
practice anyway. The following should be used in all cases anyway:
+
anyway. The following should be used in all cases:
  
 
       maxmem%=HIMEM-LOMEM
 
       maxmem%=HIMEM-LOMEM
  
Now that the structure of the program has been described, here are the
+
Now that the structure of the program has been described, here are the three
three support functions. They are in the 'HiBASIC' BASIC Library.
+
support functions. They are in the 'HiBASIC' BASIC Library.
  
 
==FNhimem0==
 
==FNhimem0==
 
Called to initialise the memory arrangement and reload the program if
 
Called to initialise the memory arrangement and reload the program if
necessary. This function will normally actually be called twice on the
+
necessary. This function will normally actually be called twice on the
second processor, first with PAGE set to &800 when it reloads the program
+
second processor, first with PAGE set to &800 when it reloads the program to
to &C000, and then with PAGE set to &C000 when LOMEM and HIMEM are
+
&C000, and then with PAGE set to &C000 when LOMEM and HIMEM are re-arranged.
re-arranged.
 
  
 
     DEFFNhimem0:A%=130:IF((USR&FFF4)AND&FFFF00)=&FFFF00:=HIMEM
 
     DEFFNhimem0:A%=130:IF((USR&FFF4)AND&FFFF00)=&FFFF00:=HIMEM
         REM If not running in i/o, just return current HIMEM
+
         REM If running in I/O memory, just return current HIMEM
 
     IF?&FFF7<>&6C OR HIMEM=&B800:=HIMEM
 
     IF?&FFF7<>&6C OR HIMEM=&B800:=HIMEM
 
         REM If not running on a 6502, or we are running HiBASIC,
 
         REM If not running on a 6502, or we are running HiBASIC,
Line 142: Line 139:
  
 
==FNhimem1==
 
==FNhimem1==
Called to ensure HIMEM is above the program in memory. This is needed if
+
Called to ensure HIMEM is above the program in memory. This is needed if you
you want to END and LIST the program, or load another program over the
+
want to END and LIST the program, or load another program over the current
current program without overwriting any data in the variable space.
+
program without overwriting any data in the variable space.
  
 
     DEFFNhimem1:IFHIMEM<PAGE:=&F800 ELSE =HIMEM
 
     DEFFNhimem1:IFHIMEM<PAGE:=&F800 ELSE =HIMEM
Line 152: Line 149:
 
==FNhimem2==
 
==FNhimem2==
 
Called whenever you want the memory arrangement returned to 'normal', i.e.
 
Called whenever you want the memory arrangement returned to 'normal', i.e.
to chain another program at the normal memory position. This program will
+
to chain another program at the normal memory position. This program will be
be loaded over whatever data is in the variable area.
+
loaded over whatever data is in the variable area.
  
 
     DEFFNhimem2:IFHIMEM<PAGE:PAGE=&800:=HIMEM ELSE =HIMEM
 
     DEFFNhimem2:IFHIMEM<PAGE:PAGE=&800:=HIMEM ELSE =HIMEM
Line 162: Line 159:
 
I have tested these functions on the following BASICs:
 
I have tested these functions on the following BASICs:
  
     BASIC 1, BASIC 2, BASIC 4 (Master), BASIC 40 (Compact), BASIC(Z80),
+
     BASIC 1, BASIC 2, BASIC 4 (Master), BASIC 40 (Compact), Z80 BASIC,
 
     BASIC 5 (Archimedes)
 
     BASIC 5 (Archimedes)
  
 
and on the following hardware:
 
and on the following hardware:
  
     BBC B i/o, BBC B+6502Tube, BBC B+Z80Tube, Master Compact,
+
     BBC B I/O, BBC B+6502Tube, BBC B+Z80Tube, Master Compact,
 
     Master 128 i/o, Master 128+6502Tube, !Z80Tube, !65Host, !65Tube,
 
     Master 128 i/o, Master 128+6502Tube, !Z80Tube, !65Host, !65Tube,
     Archimedes
+
     RISC OS
  
  
 
==Demonstration Program==
 
==Demonstration Program==
 
The accompanying program HiDemo (listed below) shows these functions in
 
The accompanying program HiDemo (listed below) shows these functions in
operation. They are also used in real applications in the JGH-PD library
+
operation. They are also used in real applications in the JGH-PD library in
in FileIndexer and FormList (on JGH-012) amongst others.
+
FileIndexer and FormList (on JGH-012) amongst others.
  
 
     REM > HiDemo
 
     REM > HiDemo
Line 181: Line 178:
 
     :
 
     :
 
     HIMEM=FNhimem0
 
     HIMEM=FNhimem0
     ONERRORIFFNerr:PROCend
+
     ON ERROR IF FNerr:PROCend
     PRINT"Program is at    &";~PAGE
+
     PRINT "Program is at    &";~PAGE
     PRINT"Data space is at &";~LOMEM;" to &";~HIMEM
+
     PRINT "Data space is at &";~LOMEM;" to &";~HIMEM
     PRINT"Available memory &";~HIMEM-LOMEM;" (";HIMEM-LOMEM;" bytes)"
+
     PRINT "Available memory &";~HIMEM-LOMEM;" (";HIMEM-LOMEM;" bytes)"
     REPEATUNTILFNmenu:PROCend:END
+
     REPEAT UNTIL FNmenu:PROCend:END
 
     :
 
     :
 
     DEFPROCend:HIMEM=FNhimem1:END
 
     DEFPROCend:HIMEM=FNhimem1:END
Line 199: Line 196:
 
     :
 
     :
 
     DEFFNmenu
 
     DEFFNmenu
     PRINT"M: Return to 8BS Menu"
+
     PRINT "M: Return to 8BS Menu"
     PRINT"H: Chain a program in high memory"
+
     PRINT "H: Chain a program in high memory"
     PRINT"L: Chain a program in low memory"
+
     PRINT "L: Chain a program in low memory"
     PRINT"Q: Quit"
+
     PRINT "Q: Quit"
 
     :
 
     :
 
     REPEAT
 
     REPEAT
     A$=GET$:IFA$="*":REPEATINPUTLINE"*"A$:OSCLIA$:UNTILA$="":PRINT":";:A$="*"
+
     A$=GET$:IF A$="*":REPEAT INPUTLINE"*"A$:OSCLI A$:UNTIL A$="":PRINT":";:A$="*"
     A$=CHR$(ASCA$AND(&DF OR(A$<"`"))):UNTILINSTR("MHLQ",A$):P.A$
+
     A$=CHR$(ASCA$AND(&DF OR(A$<"`"))):UNTIL INSTR("MHLQ",A$):PRINT A$
 
     :
 
     :
     IFA$="M":HIMEM=FNhimem2:CHAIN"$.!Boot"
+
     IF A$="M":HIMEM=FNhimem2:CHAIN"$.!Boot"
     IFA$="H":INPUT"File to load to high memory: "F$:HIMEM=FNhimem1:CHAINF$
+
     IF A$="H":INPUT "File to load to high memory: "F$:HIMEM=FNhimem1:CHAINF$
     IFA$="L":INPUT"File to load to low memory: "F$:HIMEM=FNhimem2:CHAINF$
+
     IF A$="L":INPUT "File to load to low memory: "F$:HIMEM=FNhimem2:CHAINF$
 
     :
 
     :
 
     =A$="Q"
 
     =A$="Q"
  
 
This also demonstrates a good way of prompting for and getting a key-press
 
This also demonstrates a good way of prompting for and getting a key-press
from a menu while also allowing multiple '*' commands. It can get very
+
from a menu while also allowing multiple '*' commands. It can get very
annoying when programs that only allow you to issue one '*' command and
+
annoying when programs that only allow you to issue one '*' command and then
then force you back to the menu before you can do anything else. That
+
force you back to the menu before you can do anything else. That means you
means you can't do something like '*.' to see what's there, then '*Info
+
can't do something like '*.' to see what's there, then '*Info xxxx' on
xxxx' on something.
+
something.
  
 
'''By JGH, Nov-1997'''
 
'''By JGH, Nov-1997'''

Latest revision as of 01:09, 31 January 2016

A 6502 second processor gives you an extra 64K of memory, but not all programs take advantage of this. When BASIC is entered, it is copied over to the second processor and runs from there. If you have HiBASIC, it is assembled to run at &B800 giving you memory from &800 to &B800 giving 44K available. However, this means losing a ROM socket, as you need HiBASIC to run on the second processor and BASIC (assembled to run at &8000) when running with the second processor turned off. Most people end up using BASIC at &8000 as this runs on both sides of the Tube. Unfortunately, this means that the spare memory at &C000 to &F800 in the second processor is wasted.

    FFFF +-----------+                FFFF +-----------+
         | CoPro MOS |                     | CoPro MOS |
    F800 +-----------+                F800 +-----------+
         |  HiBASIC  |                     | Spare RAM |
    B800 +-----------+ HIMEM          C000 +-----------+
         |           |                     |   BASIC   |
         |           |                8000 +-----------+ HIMEM
         |           |                     |           |
         |/\/\/\/\/\/|                     |/\/\/\/\/\/|
         | Variables |                     | Variables |
         +-----------+ LOMEM=TOP           +-----------+ LOMEM=TOP
         |   BASIC   |                     |   BASIC   |
         |  program  |                     |  program  |
    0800 +-----------+ PAGE           0800 +-----------+ PAGE
                             
  6502 CoPro running HiBASIC         6502 CoPro running BASIC

The second processor introductory guide suggests putting LOMEM=&C000 and storing variables above BASIC. This works perfectly well, but it implies that your program is always going to be massively larger than the variables it uses. You have 30K for the program but only 14K for variables. What would be more useful it to put the BASIC program above BASIC at &C000 and put the variables in &800-&8000, giving more space for variables than for the program.

    FFFF +-----------+                FFFF +-----------+
         | CoPro MOS |                     | CoPro MOS |
    F800 +-----------+ HIMEM          F800 +-----------+
         |           |                     |           |
         |           |                     +-----------+ TOP
         |/\/\/\/\/\/|                     |   BASIC   |
         | Variables |                     |  program  |
    B800 +-----------+ LOMEM          C000 +-----------+ PAGE
         |   BASIC   |                     |   BASIC   |
    8000 +-----------+                8000 +-----------+ HIMEM
         |           |                     |           |
         +-----------+ TOP                 |           |
         |   BASIC   |                     |/\/\/\/\/\/|
         |  program  |                     | Variables |
    0800 +-----------+ PAGE           0800 +-----------+ LOMEM
                             
    Variables in high memory           Program in high memory

Initially, it would seem that you would do this with:

    PAGE=&C000:LOMEM=&800

Unfortunately, it is not quite that simple. BASIC gets confused if PAGE is higher than HIMEM, you keep getting 'No room' errors whenever you try to do something as simple as listing the program. Also, you can't just stick a PAGE move at the start of a program. The program has to be moved to the new PAGE position. The easiest way of doing this is to load it again. This then raises the problem of knowing what the program was saved as.

Most programs that reload themselves like this hard-wire the name into the program giving something like this:

     PAGE=&xxxx:CHAIN "MyName"

However, this doesn't work if you have called it with something like

     CHAIN "$.Users.Jim.Progs1.MyName"

Fortunately, there's a way of finding this out. When BASIC does a CHAIN, it passes a string to OSFILE to load the program. This string is stored in BASIC's string buffer which is at &0600 in 6502 BASIC, and if you don't do any string operations that file name will still be there. Caution - string operations include printing numbers! So, to reload the current program, we can do something like:

     CHAIN $&600

as long as we haven't done anything to disturb the string buffer.

So, bearing this in mind, we can construct a program to hold its variables below its code.

To start with, we need to see if we are running 6502 BASIC on a second processor, and change PAGE, HIMEM and LOMEM to suit and reload if necessary. HIMEM cannot be changed inside a PROCedure or FuNction as you lose the return address, so the best thing to do is to return a value to set HIMEM to at the start of the program:

     HIMEM=FNhimem0

If the program needs to end with the program still in a condition where it can be LISTed, or you want to load another program in the same space, HIMEM needs to be set above PAGE otherwise 'No room' errors will occur:

     DEFPROCend:HIMEM=FNhimem1:END

and

     HIMEM=FNhimem1:CHAIN "Program"

And finally, you may want to exit or chain a program into the 'normal' memory arrangement:

     HIMEM=FNhimem2:CHAIN "Program"

As LOMEM will not be at the end of the program, TOP should not be used to find out how much memory is available - this is sloppy programming practice anyway. The following should be used in all cases:

     maxmem%=HIMEM-LOMEM

Now that the structure of the program has been described, here are the three support functions. They are in the 'HiBASIC' BASIC Library.

FNhimem0

Called to initialise the memory arrangement and reload the program if necessary. This function will normally actually be called twice on the second processor, first with PAGE set to &800 when it reloads the program to &C000, and then with PAGE set to &C000 when LOMEM and HIMEM are re-arranged.

   DEFFNhimem0:A%=130:IF((USR&FFF4)AND&FFFF00)=&FFFF00:=HIMEM
       REM If running in I/O memory, just return current HIMEM
   IF?&FFF7<>&6C OR HIMEM=&B800:=HIMEM
       REM If not running on a 6502, or we are running HiBASIC,
       REM return current HIMEM
   IFPAGE=&C000:LOMEM=&800:=&8000
       REM If we've just been loaded above BASIC, set LOMEM to
       REM bottom of memory and return to set HIMEM to bottom of
       REM BASIC ROM code
   PAGE=&C000:HIMEM=&F800:CHAIN$&600
       REM At this point we must be running in 'normal' memory, so
       REM move PAGE up, put HIMEM above it and reCHAIN the name
       REM stored in the string buffer

FNhimem1

Called to ensure HIMEM is above the program in memory. This is needed if you want to END and LIST the program, or load another program over the current program without overwriting any data in the variable space.

   DEFFNhimem1:IFHIMEM<PAGE:=&F800 ELSE =HIMEM
       REM If the program is above the variables, return HIMEM
       REM above the program, otherwise return the current HIMEM

FNhimem2

Called whenever you want the memory arrangement returned to 'normal', i.e. to chain another program at the normal memory position. This program will be loaded over whatever data is in the variable area.

   DEFFNhimem2:IFHIMEM<PAGE:PAGE=&800:=HIMEM ELSE =HIMEM
       REM If the program is above the variables, reset PAGE back
       REM to the bottom of memory, otherwise return the current
       REM HIMEM

I have tested these functions on the following BASICs:

   BASIC 1, BASIC 2, BASIC 4 (Master), BASIC 40 (Compact), Z80 BASIC,
   BASIC 5 (Archimedes)

and on the following hardware:

   BBC B I/O, BBC B+6502Tube, BBC B+Z80Tube, Master Compact,
   Master 128 i/o, Master 128+6502Tube, !Z80Tube, !65Host, !65Tube,
   RISC OS


Demonstration Program

The accompanying program HiDemo (listed below) shows these functions in operation. They are also used in real applications in the JGH-PD library in FileIndexer and FormList (on JGH-012) amongst others.

   REM > HiDemo
   REM Demonstrates running above BASIC on 6502Tube
   :
   HIMEM=FNhimem0
   ON ERROR IF FNerr:PROCend
   PRINT "Program is at    &";~PAGE
   PRINT "Data space is at &";~LOMEM;" to &";~HIMEM
   PRINT "Available memory &";~HIMEM-LOMEM;" (";HIMEM-LOMEM;" bytes)"
   REPEAT UNTIL FNmenu:PROCend:END
   :
   DEFPROCend:HIMEM=FNhimem1:END
   :
   DEFFNerr:REPORT:PRINT:=INKEY-1
   :
   DEFFNhimem0:A%=130:IF((USR&FFF4)AND&FFFF00)=&FFFF00:=HIMEM
   IF?&FFF7<>&6C OR HIMEM=&B800:=HIMEM
   IFPAGE=&C000:LOMEM=&800:=&8000
   PAGE=&C000:HIMEM=&F800:CHAIN$&600
   DEFFNhimem1:IFHIMEM<PAGE:=&F800 ELSE =HIMEM
   DEFFNhimem2:IFHIMEM<PAGE:PAGE=&800:=HIMEM ELSE =HIMEM
   :
   DEFFNmenu
   PRINT "M: Return to 8BS Menu"
   PRINT "H: Chain a program in high memory"
   PRINT "L: Chain a program in low memory"
   PRINT "Q: Quit"
   :
   REPEAT
   A$=GET$:IF A$="*":REPEAT INPUTLINE"*"A$:OSCLI A$:UNTIL A$="":PRINT":";:A$="*"
   A$=CHR$(ASCA$AND(&DF OR(A$<"`"))):UNTIL INSTR("MHLQ",A$):PRINT A$
   :
   IF A$="M":HIMEM=FNhimem2:CHAIN"$.!Boot"
   IF A$="H":INPUT "File to load to high memory: "F$:HIMEM=FNhimem1:CHAINF$
   IF A$="L":INPUT "File to load to low memory: "F$:HIMEM=FNhimem2:CHAINF$
   :
   =A$="Q"

This also demonstrates a good way of prompting for and getting a key-press from a menu while also allowing multiple '*' commands. It can get very annoying when programs that only allow you to issue one '*' command and then force you back to the menu before you can do anything else. That means you can't do something like '*.' to see what's there, then '*Info xxxx' on something.

By JGH, Nov-1997