A TSR (Sorry) TEXT FILE VIEWER ****************************** Programming suggestion by Silas Brown
In 8BS (I'm not sure whether it's 27 or
28) it was suggested that one could use
a printout of my Envelopes article
while experimenting with Chris
Richardson's Envelope Editor. I haven't
got access to a printer, and I don't
know how many other people have, so I
thought that this would be an idea for
a program that enabled you to switch
between the foreground task and viewing
a text file with hot keys.
I haven't got much access to a Beeb, so
if I was to write this program it would
take ages to do it and you'll all have
to be up to your knees in dusty old
disks to try and find out what on earth
(or not) I'm talking about! However, I
can describe how to go about writing
such a program, so that somebody is
more likely to actually do it.
To start with, I hope everybody's
guessed that it'll have to be in
machine code (well some of it, at
least, but it's a lot easier to keep it
in one hunk of code, rather than
accessing the BASIC ROM, etc., etc.),
and it would be impractical to do it in
main memory (it's not just the code,
it's also the old screen display that's
got to be stored somewhere) so the best
place is Sideways RAM (RAM not ROM
because we'll need write access).
That's another excuse for me not doing
it - when I DO have access to a BBC it
hasn't got any Sideways RAM (Sniff!).
Thus you'll have to know about
programming in machine code, and
several other things:-
- Writing a Sideways ROM Image
- Vectors and how to get them into
Sideways RAM
- Detecting hot keys
- Preserving the screen display
WRITING A SIDEWAYS ROM IMAGE
If I gave a really detailed
explaination of any one of these
subheadings I'd be still typing away
in about 2000, I think. So here's what
you should need to get this particular
ROM off the ground. Assemble your code
in main memory, but set O% to &8000
when you set P%, and add 4 to your OPT
statements.
The first thing you need to assemble is
BRK:EQUW0 ½ No language
½ entry point
JMP service ½ Jump to service
½ entry point
EQUB&82 ½ Service ROM
EQUBoffset MOD 256 ½ LSB of
½ identifier
EQUB&93 ½ Binary version
½ number
.title ½ Start of ROM
½ title
EQUS "Text Viewer" ½ Text of ROM
½ title
.offset
BRK:EQUS"(C)":BRK ½ Identifier
½(string to make
½sure that
½ it really is a
½ROM not any old
½ data)
.service
The entry starts at .service.
The ROM should then preserve the
Accumulator (we'll need it for later).
Why not store it at a location within
the Sideways RAM itself; thus sparing a
bit of that precious main memory? After
doing this, it should stack all
registers (including P) and restore the
Accumulator.
Service ROMS are called for lots of
reasons, but, to my knowledge, what we
want isn't amongst them. So, we'll have
to treat this ROM as an ordinary '*
command' ROM, with things like '* KEYS
ON', etc.. There are two calls which
such a ROM should respond to: A=4 (Star
command issued. X = ROM number AND
(&82),Y = first character of command),
and A=9 (*HELP issued. Registers as
A=4, but (&82),Y point to first
character after *HELP.).
So, have a routine to CMP#4:BEQ
starcom:CMP#9:BNE exit followed by the
Help routine.
When *HELP is issued, four things can
happen. Firstly, *HELP on its own is
issued. If this happens, (&82),Y would
point to a Character 13. The ROM should
then print its title, and, on the next
line, indented by 3 spaces, a keyword,
in capitals.
Secondly, *HELP followed by a full stop
is issued. (&82),Y would then point to
a full stop character (IMPORTANT! When
interperting commands like this, be
sure to have a routine to skip spaces,
stars and don't be case sensitive!).
The ROM should then, after printing its
title and keyword as before, print a
list of star commands, each indented 6
spaces.
Thirdly, *HELP followed by the keyword
is issued. The ROM should then respond
as with *HELP followed by a full stop.
And also, *HELP followed by something
else is issued. The ROM should then
keep quiet.
After a *HELP call, or an unrecognised
call, the ROM should exit with all
registers intact.
The star command processing routine
should look for recognised commands. If
the string at (&82),Y is not
recognised, it should exit with all
registers intact. If it is, it should
process it and exit with all registers
intact except A, which should exit with
0. This tells all the other ROMS that
the command has already been dealt with
and they needn't bother.
Suitable * commands may be *HOTKEYS ON
or *HOTKEYS OFF. The command
interpreter could look for HOTKEYS,
then, if ON or OFF isn't specified, it
prints a Syntax: message, and still
exits with A = 0.
Now, you'll be needing something that
would actually turn the hotkeys on or
off.
VECTORS AND HOW TO GET THEM INTO SIDEWAYS RAM There are a few addresses that contain
MSB - LSB addresses to jump to. What?
Well, if you've got a disassembler, try
disassembling the three addresses
&FFF7, &FFF8 and &FFF9. You should find
JMP (&208). This means that when a
program does a JSR &FFF7 to execute a
star command, the 6502 looks at &208
and &209 and jumps to the address
there. In OS 1.2, this is normally &89
and &DF, so the address is &DF89. Now,
if you were to preserve the old
contents of the vector, change it to
point to your own code, and when it
does get to your own code you do
something and jump to the previous
contents, you can intercept the star
command call. And, no matter how many
programs that there are intercepting
the call, as long as they all follow
the rules, everything should be fine.
We're not thinking of intercepting the
star command vector, as the MOS kindly
makes that easy for us using the system
above. However, it's by vectors that
our hot keys can work. Problem: if you
intercept this vector, your ROM may
just not be the active one when it
jumps inside it. What does it do? Start
executing instructions, or even data,
from any old position within the
currently paged-in ROM. Chaos! (And you
don't get your hotkeys, either).
So, what do you do? Point the vector to
the address &FF00+(3*Vector number).
This jumps to a routine which handles
ROMs. Next, put the LSB - MSB address
of the entry point, followed by the ROM
number (which should be in the X
register if you've preserved it), at 3
bytes beginning at &9DF+3*Vector
number.
So far so good. Which vector number are
we going to use, and how are we going
to use it?
DETECTING HOT KEYS Vector number 16 is rather different.
The addresses are &220 and &221, and it
is called the Event Vector. There are
several events, like the following:-
Event
Number Description
0 Output buffer empty
1 Input buffer full
2 Character enters in buffer
3 ADC finishes a conversion
4 Start of vertical SYNC pulse
5 Interval timer is 0
6 Escape pressed
7 RS423 error
8 Something to do with Econet
9 User
To enable a particular event, OSBYTE
(&FFF4) must be called with A = 14, X =
event number and Y = 0. To disable it,
OSBYTE must be called with A = 13, X =
event number and Y = 0.
Whenever an event occurs, the BBC stops
what it's doing, loads the event number
into the Accumulator, and does a JSR
(&220). So, our event routine must
first push the processor flag, check
the accumulator for the event number
that the routine is responding to, and,
if necessary, do something. IT SHOULD
ALWAYS EXIT WITH ALL REGISTERS INTACT.
Don't forget to jump to the old
contents of (&220), otherwise another
program might not get a chance.
One thing. There should be a routine
that checks that, when * HOTKEYS ON is
issued, the event handler is not
already installed. If it is, don't
install it again, otherwise, when it
looks for the old address to go to, it
will jump back to itself, and so on,
creating a crash. Similarly, make sure
that the hotkeys ARE on before *
HOTKEYS OFF is executed.
Suppose that the event is ours. We then
have to check if the hotkeys are being
pressed. Event number 4 is kind of
nice; it happens 50 times a second;
every time that the screen is updated.
There are two 0 - page locations that
are constantly being updated with the
keys which are currently being held
down. It is better to use these than
OSBYTEs, as they take rather a long
time and would slow down the foreground
task quite noticably. A few OS calls
are not self re-enterable, which means
that if the foreground task is calling
them at the time of the event, the
machine will crash if the background
task calls them as well. Stick to
direct memory access as much as
possible. Isn't it a structured
programmer's nightmare?!
Type this line of BASIC, press your hot
- keys (in order) and hold, and read
what ?&EC and ?&ED would be if your hot
- keys are being pressed.
REP.:P.?&EC,?&ED:U.0
Now all you have to do is write a check
for our hot - keys being pressed,
exiting if they're not.
If they are, directly poke (yugh!) a
bit of code that says JMP exit after it
has been confirmed that the event was
ours. Don't forget to restore it
afterwards! This is to make sure that
the code doesn't re - enter itself at
the next event and so on, crashing. It
is best not to disable the event since
something else may be using it, and it
is best not to redirect the vector
because some other bit of code may have
hooked on to it since the code was
installed. That's another thing for the
* HOTKEYS OFF code to check for. It
can't uninstall if the vector doesn't
point to it, otherwise all the programs
which have been installed after it will
be deactivated (and we don't want that,
do we?). This is getting a bit like
writing a PC TSR, you know.
Now all you have to write is the code
executed when the hot - keys are
pressed. But you're not on your own yet
(snarl)!
PRESERVING THE SCREEN DISPLAY You may be thinking "Does that Brown
know what he's on about? Preserve a
screen that may be over 20K in a puny
16K sideways RAM that's already half
eaten up with code and data anyway?
Crazy!"
However, if you display the text in
Mode 7, you only need to preserve the
overlap between Mode 7 and the other
MODEs, and that's only 1K (&7C00 to
&7FFF). Mind you, how on earth (or not)
are you going to change MODE back
without clearing the screen? And what
MODE to change back to anyway?
On the BBC A, B, B+ and Master (but not
Tube or Electron), the mode change
routine begins at &CB1D. If you do
LDA#7:JSR&CB1D, the screen display will
switch to mode 7, for example. This is
quicker than calling the VDU drivers.
All they do is interpret the numbers
that you send them, and then call this
routine.
If the routine were to stop at &CBEF,
the screen would not be cleared (it
first changes mode and then clears the
screen). Rather than copy the code from
&CB1D to &CBEF at assembly time, why
not make it part of the * HOTKEYS ON
initalization? That way, danger is
avoided about your code having to be
reassembled on every different machine
that it goes on. Remember to do all
this BEFORE setting and enabling the
vectors. Alter the vector at &220 LAST,
except for doing the *FX call to enable
it. Otherwise, your code may be called
before you're ready for it!
Well, what screen mode to change to,
anyway? OSBYTE 135 returns the current
screen mode in the Y register, but
there's all the other VDU status to
preserve as well. Stuff to preserve is
&300 to &3FF, for a start, and a few
zero page locations as well (try
preserving &D0 to &DF). Of course, ALL
memory should come out of the routine
as it went in. Experiment!
One reminder: Use direct screen poking
rather than OSWRCH, as it is not self -
re-entering.
BEEBUG READING LIST Vol.10 No.5 October 1991 p.34 "First
Course" - reading current MODE
Vol.4 No.9 March 1986 p.25 "Preserving
Screens"
Vol.10 No.6 November 1991 p.38 "A Timed
Interrupt Routine" - detecting hot keys
Vol.7 No.6 November 1988 p.62 "Using
Assembler" - events
Vol.6 No.1 May 1987 p.46 "Vectored
entry into Sideways RAM"
Vol.11 No.6 November 1992 p.38 "Using
Sideways RAM (Part 1)"
Vol.11 No.7 December 1992 p.46 "Using
Sideways RAM (Part 2)"
Vol.11 No.8 January/Feburary 1993 p.51
"Sideways ROMs (Part 3)" (which should
really be "Using Sideways RAM (Part
3)"!)