**BASIC ROM Routines**

MENU:Introduction/News Routines listed in Address order Routines listed by Category BASIC ROM documentation by Christopher Dewhurst BASIC4r32 |

==================

By Christopher Dewhurst

The Basic Rom is that dark and mysterious area of memory that lies beyond the screen, starting at &8000 and stretching up to &BFFF. It's the backstage department containing the machine code needed to interpret your Basic commands. But have you ever wondered if we can use some of that machine code in our own programs? Have you, for instance, struggled to write an assembler routine to print a number on the screen, when one must surely exist somewhere in the Basic Rom?

Well, wonder no more, because there is indeed such a routine, and in this article we'll be exploring that and a lot more besides.

Before we go any further, however, a word of caution. The best machine code is specific machine code written for a specific job; Basic Rom routines are general-purpose routines, and are not the answer to everything. Having said that, if speed is not your main priority, then the Rom routines are ideal. They make your programs smaller and smarter, provided you use them properly - and this usually involves some fairly tricky setting up - so listen carefully.

I learnt a lot about the Basic Rom by exploring around it and experimenting with it myself. I also picked up a few tips from 'The Advanced Basic Rom User Guide' by Colin Pharo (Cambridge Micro Centre, 1984). Roland Waddilove also presented a series of excellent articles on the subject in 'Electron User'; if you still have these paper beauties, dig out the November 1988 for a rundown on mathematical Rom routines. However, I will be concentrating on routines which print numbers in hex or decimal, the random number generator, and printing strings of text.

In case you're wondering, BBC Master owners won't be left out of the discussion this time. I have done quite a bit of disassembling of the Basic 4 Rom to find out where equivalent routines to Basic 2 reside. Basic 2 is the Rom fitted in the BBC B and Electron, and Basic 4 is the one fitted in the Master. (Like the Plus 2, for some reason Basic 3 never was.)

When I talk about a Rom routine, I will specify both the Basic 2 and Basic 4 addresses - together with examples and commentaries on how to use them - so it is up to you to use the correct one depending on which computer you have.

If you experience any difficulties - or if you have additional hints and tips - just email me.

Right, down to business. We must first get to know what is called the Integer Work Area, or IWA for short. This is just a sequence of four bytes in zero page, located at &2A-&2D. Before Basic can work on an integer variable, be it adding a number to it or printing it out, it must be put into the IWA. Fortunately, life is made easier with the help of a couple of routines which copy an integer variable, either from zero page or from the main memory, to the IWA:

1. Routine: Copy 4-byte integer from zero page to the IWA

Basic 2 address: &AF56

Basic 4 address: &AA80

Entry: X = zero page offset at which the integer to be copied is located.

Exit: IWA contains the integer.

Ex.: LDX #&70 \integer at &70-3

JSR &AF56 \copy to IWA

2. Routine: Copy 4-byte integer from memory to the IWA

Basic 2: &B336

Basic 4: &B1AA

Entry: &2A/&2B contain address of the integer.

Exit: IWA contains the integer.

Ex.: LDA #integer MOD 256

STA &2A

LDA #integer DIV 256

STA &2B

JSR &B336

...

.integer EQUD &12345678

There are also two routines which do the opposite of above. The one at &BE44 (Basic 2)/&BDC6 (Basic 4) copies the IWA to a zero-page location, X being set to the zero page location on entry. The routine at &B4C6 (Basic 2)/ &B347 (Basic 4) copies the IWA to a location in main memory whose address is held in &37/&38.

3. Routine: Print a string

Basic 2: &BFCF

Basic 4: &BECF

Entry: The string must follow the JSR &BFCF instruction, and be terminated
by a byte of value &80 or greater.

Ex.: JSR &BFCF

EQUS "Hello there.":NOP

...

Notice how I've used the NOP instruction to terminate the string. The NOP opcode has a value of &EA, which satisfies the condition of being &80 or greater. The important point to remember is that program execution continues AFTER that NOP instruction. In machine code, every time a JSR instruction is executed the current address is pushed onto the stack. Basic pulls this address from the stack, stores it in zero page locations &37/&38, and uses indirect addressing to get the bytes of the string. By the time the string has been printed, &37/&38 contains the address of the next instruction after the string in the program that called the routine. The disadvantage of this routine, however, is that while you can include control codes (to turn off the cursor for instance) you can't print out a string of graphics characters because they have an ASCII value of &80 or above which, as we said, is used to terminate the string.

4. Routine: Print A in hex

Basic 2: &B545

Basic 4: &BD6C

Entry: The Accumulator contains the byte to be printed in hex

Ex.: LDA #&CD

JSR &B545

This one can be quickly demonstrated from Basic, if you really want, by typing A%=&CD:CALL&B545.

5. Routine: Print 16-bit number in decimal

Basic 2: &991F

Basic 4: &A081

Entry: &2B/&2C (the 2 least significant bytes of the IWA) should contain
the number to be printed.

Ex.: LDA #1023 MOD 256 \put the number 1023

STA &2B

LDA #1023 DIV 256 \onto the two lsb's of the IWA

STA &2C

JSR &991F

This is the routine which I promised we would discuss at the beginning of this article, so let's take some time going through it in detail. If you have a disassembler then you could look at the actual machine code, which in English goes something like this. You first of all see how many times 10,000 can be subtracted from the given number before it becomes negative. For example, you can subtract 10,000 six times from the number 60,000. This is the 10,000s count. Then you see how many times 1,000 can be subtracted from the remainder, then how many times 100 can be taken away from the remainder of that, and so on down to the 1s count. In order to do this, we need a table of two-byte values: 10,000, 1,000, 100, 10 and 1. There are two tables in Rom; the first table contains the low bytes, and the second table contains the high bytes:

Basic 2 Low bytes: High bytes

&996B [&01] &99B9 [&00] &0001 = 1

&996C [&0A] &99BA [&00] &000A = 10

&996D [&64] &99BB [&00] &0064 = 100

&996E [&E8] &99BC [&03] &03E8 = 1000

&996F [&10] &99BD [&27] &2710 =

10000

Basic 4 Low bytes High bytes

&8026 [&01] &8021 [&00] &0001 = 1

&8027 [&0A] &8022 [&00] &000A = 10

&8028 [&64] &8023 [&00] &0064 = 100

&8029 [&E8] &8024 [&03] &03E8 = 1000

&802A [&10] &8025 [&27] &2710 = 10000

If you haven't got a disassembler, then the program below demonstrates how it works:

10 FORI%=0TO2STEP2

20 P%=&900

30 [OPTI%

40 LDX #&50 \copy integer from zero page

50 JSR &AF56 \to IWA

60

70 LDX #4

80 .loop LDA#0

90 STA &3F,X

100 SEC

110 .loop2 LDA&2A

120 SBC &996B,X \&8026 for BBC M

130 TAY

140 LDA &2B

150 SBC &99B9,X \&8021 for Basic 4

160 BCC skip

170 STA &2B

180 STY &2A

190 INC &3F,X

200 BNE loop2

210 .skip DEX

220 BPL loop

270

280 LDX #5 \suppress leading zeroes

290 .loop3 DEX \by indexing to first

300 BEQ print \non-zero number

310 LDA &3F,X

320 BEQ loop3

330 .print LDA &3F,X

340 ORA #&30

350 JSR &FFEE

360 DEX

370 BPL print

380 RTS

390 ]

400 NEXT

410 INPUT !&50

420 CALL &900

The section of code from line 280 to 320 suppresses leading zeroes. This just means that if you had the number 234, then it will be printed as 234 and not 00234. Sometimes you might not care for leading zero suppression. In most games, for instance, your score is displayed as 00000 at the start, then changes to 00010 when you score some points and so on. In this case, you can dispense with lines 290-320 in the above program and replace line 280 with LDX #4.

6. Routine: Convert number in IWA to ASCII decimal or hexadecimal

Basic 2: &9EFF

Basic 4: &A138

Entry: IWA should contain number to be converted location &15 = &FF for hexadecimal ASCII or &15 = 0 for decimal ASCII

The previous routine only allowed 16-bit numbers (numbers in the range 0-65535) to be printed. This routine helps you print 32-bit numbers in decimal or hex. When we speak of "hexadecimal" or "decimal ASCII", it means that a string containing ASCII codes is made for the given number. For example, the four ASCII decimal codes for &9EFF are 57, 69, 70, and 70 (ignoring the ampersand which Basic doesn't print anyway). Basic puts these ASCII codes into the String Work Area, or SWA for short. We can then use another routine to print out the contents of the SWA:

7. Routine: Print the SWA

Basic 2: &8E12

Basic 4: &921B

Entry: location &30 must contain the length of the string the SWA must contain the string location &A must = 0.

Ex.: LDX #&50 \copy integer at &50-3

JSR &AF56 \to SWA

LDA #&FF:STA &15 \number to be in hex

JSR &9EFF \convert IWA to ASCII codes

LDA #0:STA &A

JMP &8E12 \and print the number

8. Routine: Random number generator

Basic 2: &AF51

Basic 4: &AA7B

No entry requirements. On exit, the IWA contains a 4-byte random integer.

Ex.: JSR &AF51

LDA &2A

JSR alien

I find this Rom routine extremely useful for getting random numbers in games, and it's the only decent way of getting fairly unpredictable numbers in machine code.

Conclusion

If you use any of the above routines, don't forget to use the correct address
for your version of Basic. Now that you've seen what the Basic Rom can do, hopefully
you'll want to start exploring other parts. If you find anything useful, do
write in and let us know!

Christopher Dewhurst

24 September, 2000