8-Bit Software Online Conversion

            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)"!)                                    