To directly access the sideways ROMs you need to use 2 memory locations 0xFE30 and 0xF4 (or &FE30 and &F4 in BBC parlance). The 0xFE30 address is write only and allows you to page a 16K ROM into the memory space of 0x8000 to 0xBFFF. The 0xF4 address maintains a readable copy of the currently paged in ROM, which in practice is usually the BASIC ROM.
The sockets in the BBC are banks 12, 13, 14, and 15. The BBC can handle more but this requires extra hardware to implement. On my BBC (an aside: if you remember the old BBC computer TV programmes then you may remember Ian McNaught Davis' catch phrase of 'On my BBC', at least to me it was his catch phrase anyway!) I have built my own ZIF socket kit which is connected to ROM socket 12, and so the example code will reflect this but should be easy to modify if you are using a different socket.
So if I want to peek the first byte from ROM 12 then surely I could do the following in BASIC?
20 PRINT PEEK &8000
So this switched to bank 12 and then prints the value in location 0x8000 and then switches the bank back to 15 which is the BASIC EPROM (on my BBC at least). Well no actually it doesn't, in fact it will likely crash the BBC and the reason? Well quite simply to perform any BASIC commands after line 10 the BBC would need the BASIC ROM paged in, but we paged another ROM in! So how
we going to peek the values from another ROM?
Machine Code is the only universal language the BBC can run irrespective of the ROM paged in so we will need to use machine code to page the ROM in read the byte and then page the BASIC ROM back in before returning back to BASIC. Luckily BBC BASIC includes an in-line assembler which is one of the many reasons to me it is a programmer's machine.
10 MODE 1
20 DIM ROMPEEK% 20
40 REM **ROMPEEK**
50 REM A=ROMNO
60 REM PAGE 0 &70,&71 CONTAIN ADDR
70 REM RETURNS PEEK VALUE IN A
90 LDY &F4
100 STA &FE30
110 LDX #0
120 LDA (&70,X)
130 STY &FE30
170 FOR A=0 TO &3FFF
175 IF A MOD 1024=0 THEN PRINT A
210 NEXT A
220 PRINT CHK;" ";~CHK
240 DEF FNROMPEEK(ROMNO,BYTE)
260 ?&70=(BYTE+&8000) MOD 256
270 ?&71=(BYTE+&8000) DIV 256
280 =USR(ROMPEEK%) AND 255
290 END FN
What I like about the above code is it highlights lots of interesting features about BBC development.
I will try to explain it step by step:
line 10 simply changes screen mode.
lines 20 and 30 reserve some memory for the machine code, P% is used by the BBC's inline assembler as a program counter, this is incremented as the assembler does its business.
Lines 80 to 150 are the assembly code, the square brackets tell BBC BASIC that this is assembler, note that in MODE 7 these show up as arrows (one of the reasons why I set MODE 1).
The REM statements briefly allude to what you need to pass to this machine code and what it returns, basically you need to populate memory locations 0x70 and 0x71 with the 16-bit memory location that you wish to PEEK and pass the ROM Bank number in the accumulator (which is mapped to A%).
This brings me onto another slightly unusual feature of BBC BASIC and that is that variables A% to Z% are always initialised whereas we can add our own variables at any time and they will be added into memory the A% to Z% variables are already there. Also A% is not the same as A and some of the single letter variables are used by the Machine Code system. We have already seen that P% is used as a program counter, but also A%, X%, and Y% are also mapped to the 6502 registers A, X and Y. This allows us to pass 8-Bit values to the Machine Code routine, and also All the registers and flags are passed back to BBC BASIC in the A% variable.
In my example however I am only passing a single value using the accumulator because it is an 8-bit value, a memory address however is a 16-bit value and hence is not very easy to pass via the registers, I could have split the value between X and Y but as these values would then have to be placed directly into memory anyway it seemed easier to just let BASIC do it directly.
You are probably wondering why I am using memory locations 0x70 and 0x71? Well if you look at how the 6502 addresses memory you can read from any 16-bit address if you specify it explicitly in the code, eg:
This will load the value in memory location 0x8000 into the accumulator, however if I want to specify the address instead of &8000 then I need to store the address in 2 memory locations and ask the machine to read this address and then fetch the value pointed to by this address. The only way this can be done on the 6502 is using what is called indirect addressing and the only area of memory the 6502 can use for indirect addressing is page zero or the first 256 bytes of memory. Hence on 6502 machines this area of memory is very heavily contested by various parts of the Operating System. By looking in the BBC Guide and the Advanced User Guide it says that addresses &70 to &8F are free for user routines in BASIC.
The other thing to consider is the 6502 does not have a simple instruction like:
instead it has:
These are in fact not just indirect addressing but indirect indexed addressing, the first simply means add X to &70 and then load the accumulator from the 16-bit address found there. This is the most common of the 2 and allows you to simply setup lookup tables. The second means load the accumulator from the 16-bit address found in &70 and then add Y to this value.
I don't need either of these I simply want the indirect addressing but as that instruction doesn't exist I have to set X to 0 and use the LDA (&70,X) instruction.
So to summarise the assembly, the Y register stores the current paged ROM number (BASIC), I page in the ROM specified in A, 'PEEK' the value and then page back in the ROM number that was preserved in Y.
Lines 240 to 290 highlight a powerful feature of BBC BASIC, the ability to define a function, I have taken advantage of this to make a ROMPEEK function which you can pass the ROM number to and the byte number in the ROM which you want to PEEK. Line 280 returns the value, I'm performing a logic And of 255 so that only the least significant 8 bits are returned which is the accumulator. The value returned from a USR statement is a 32-bit value which is composed of all the 6502 registers and flags.
So this just leaves lines 160 to 230 which are simply the checksumming loop, line 175 is a simple progress indicator which outputs every Kilobyte that has been processed. Line 220 outputs the checksum in decimal and hex (~).
Now if this code is run it will produce a checksum for the ROM in slot 12 which matches the checksum generated by a Dataman S4 EPROM programmer, but there is one small problem, and that is it is slow.
This is likely due to several factors; one, BASIC is controlling the loop, and two the ROM is paged in and out for each byte, so the paging is happening 16384 times. How do I speed this up? Well I need to write the complete checksum code in Machine Code...