diff options
Diffstat (limited to 'docs/os12/mdfs.net/Docs/Comp/BBC/OS1-20/Microbase')
-rw-r--r-- | docs/os12/mdfs.net/Docs/Comp/BBC/OS1-20/Microbase | 1682 |
1 files changed, 1682 insertions, 0 deletions
diff --git a/docs/os12/mdfs.net/Docs/Comp/BBC/OS1-20/Microbase b/docs/os12/mdfs.net/Docs/Comp/BBC/OS1-20/Microbase new file mode 100644 index 0000000..3251a42 --- /dev/null +++ b/docs/os12/mdfs.net/Docs/Comp/BBC/OS1-20/Microbase @@ -0,0 +1,1682 @@ +BBC MICROBASE SERIES +BBC 6502 Machine Code by Geoff Cox +---------------------------------- +This series was first published on Micronet +between April and October 1991 + +The BBC Micro Operating System +Part One: The moving electron writes +------------------------------------ +In the last series we reviewed the basics of machine code programming +using the 6502. You will have noticed that not too many examples of +programming were given. This is because there are two levels of +programming on any machine, machine level and operating system level. + +Operating Systems +----------------- +An operating system is basically a group of routines that sit between +the user and the electronics of the computer. To illustrate what an +operating system does we have to turn briefly from the path of the +series. + +We'll imagine that you want to write the letter A on the screen of +your monitor. First we have to work out the shape of the character and +slice it horizontally into eight sections, one for each of eight +screen scanning lines on the monitor. + +Now we need to detect when the frame synchronising pulse for the +monitor is sent by the computer. Next we need to count the line +synchronising pulses to find the one corresponding to the start of the +first line of the character. + +Then we must time from this pulse to the start of the first line of +the character, turn on the appropriate electron guns in the monitor +and turn them off at the correct time for the end of the of the +character. Finally we have a few microseconds to do it all again for +the next line. That's more or less what happens fifty times a second +on your monitor screen. + +Mapped Screens +-------------- +This makes even the easiest task very complex. We can make life easier +by storing a picture or map of the screen in memory and writing the +character shape to the appropriate map locations. + +The map can then be scanned in synchronisation with the electron beam +on the monitor. This can either be via a clever piece of electronics +or a software routine. + +To illustrate the BBC map, type the following program on any 6502- +based BBC without screen RAM shadowing. + +10 MODE 0 +20 ?&7D00=65 + +You should see two little white dots towards the bottom right of the +screen. Now two dots are not the letter A so if we want to write A we +have to designthe character and write it to the map. This program does +just that. + +10 MODE 0 +20 FOR A=&7D00 TO &7D07 +30 READ B +40 ?A=B +50 NEXT +60 DATA &3C, &66, &66, &7E +70 DATA &66, &66, &66, &00 + +The sharp-eyed among you will have spotted something a little odd +here. The screen seems to be arranged in blocks of eight bytes in this +mode. Don't worry about this - it simply makes character table design +simpler. + +Character Tables +---------------- +We can make life even easier by designing a set of characters and +putting them in an area of memory where they can be looked up. In the +BBC B this area is in ROM starting at &C000. + +A quick diversion here. If we have to have a series of character +designs in memory it helps to have a standard method of accessing +them. + +The standard character ordering is called ASCII. A space has ASCII +number 32 and this is the first "printable" character in the set. The +last is 127 (Delete). There are eight bytes in each character matrix +so the design for any character is at &C000+(8*(ASCII - 32)). + +This program examines the ROM character table and shows how characters +are arranged. + +10 MODE 0 +20 PRINT "CHARACTER ?" +30 A$=GET$ +40 PRINT A$ +50 START=(8(ASC(A$)-32))+&C000 +60 FIN=START+7 +70 FOR BYTE=START TO FIN +80 PEEK=?BYTE +90 PROC_BIN +100 PRINT~BYTE;TAB(15);~?BYTE;TAB(20); +110 NEXT +120 END +130 DEFPROC_BIN +140 A$="" +150 FOR BIT=7 TO 0 STEP-1 +160 X=(PEEK AND 2^BIT) +170 IF X>1 THEN A$=A$+"" ELSE A$=A$+"." +180 NEXT +190 ENDPROC + +As you will often want to write characters on the screen it makes a +lot of sense to have a little routine to take a character from a +register and print it on the screen. + +The routine will look up a character in the table and print it to the +appropriate position on the screen. There will also need to be a +record of the cursor position on the screen. + +Routines for printing and moving a cursor on the screen will also be +useful. You could, of course, write all of these routines yourself as +part of every program but as the computer manufacturer has to provide +them in order to tell you the machine is working he usually leaves an +access point for you to use the routines yourself. + +Incidentally if you rewrite the first program but change line 10 to +read MODE 7 you will see a letter A on the screen. + +This is an example of clever electronics. A chip on the BBC board +contains a character generator. Instead of the character design being +stored in the screen map the character's ASCII value is stored. This +is read by the electronics and used to generate characters directly on +the screen. This allows a 40 x 80 text and block graphics screen to be +generated in just 1K of RAM. + +Using the Operating System +-------------------------- +The screen handling routines and several others make up an operating +system. In the BBC micro the series of routines that write a character +on the screen can be accessed though a point called OSWRCH at &FFFE. + +So instead of having to design characters, time lines and do all of +the other things, you can output a letter A to the screen simply +using: + +LDA #65 +JSR &FFEE ;OSWRCH + +and leave the operating system to do the rest. + + +BBC 6502 Machine Code +Part Two: In the beginning +-------------------------- +Last week we looked at the reasons for an operating system and how it +can simplify the programmer's task. Unfortunately you will not always +be able to use the operating system. There may not be a suitable +routine or it may be too slow. In these cases you have to write to the +device itself. So to continue our look at machine code we need to +examine programming with device handling and the operating system. + +This series will attempt to kill two birds with one stone by taking +avery close look at Acorn's OS 1.20 used on Model Bs. + +This changed little for the B+ and Master so while the routines may be +in a slightly different place the basic system will be the same. The +Master uses 65C02 code which has a few extra instructions so there may +be some differences in the length of the code. + +What you will need +------------------ +A disassembler or machine code monitor that can handle 6502 or 65C02 +codes. If If you don't have either you can use: + +PRINT ~?address + +to get the hexadecimal codes which you can then look up in the back of +the BBC User Guide to get the relevant command. + +Suitable Monitors are EXMON, BBC Monitor or the SYSTEM monitor. (I use +"Maxim" - Ed.) If you have a single- stepping monitor like one of +these, you will be able to trace some of the routines for yourself. + +For this series we will use standard 6502 mnemonics except that DB and +DW will be used to show byte assignments rather than EQUS, EQUW and +EQUB. This is because the disassembler I am using produces these codes +and it's a lot easier to follow than convoluted BBC-type statements. + +First things first +------------------ +Remember that an operating system is not a program in the usual sense. +Normal programs have a defined entry and exit routines. An operating +system can have a large number of entry and exit points as well as +interlocking routines. So to examine the operating system we need a +starting point. + +The 6502 regards memory as a series of 256-byte pages 0 to &FF (255). +Any address can be considered to be a page number plus an offset +within the page. Both figures can be represented by a single byte. So +address &FF01 is on Page &FF offset 01. The concept of offsets is very +useful if you ever get involved in 80n86 programming. + +The BBC Manual gives a series of system entry points on page FF. Most +of these are indirected through Page 2 and as we cannot guarantee what +the contents of Page 2 should be (the vectors can be and are changed) +these are useless as starting points. This leaves three sensible entry +points. + +6502 Vectors +FFFA DW &0D00 ;NMI address +FFFC DW &D9CD ;RESET address +FFFE DW &DC1C ;IRQ address + +The NMI address is in RAM so no joy there, but the other two look +fine. The best is RESET as this is where the machine starts when it is +turned on or BREAK is pressed. In the case of Model B and OS 1.20 that +address is &D9CD, so what happens? + +In the beginning +---------------- +Reset can be effected by turning on the computer or pressing BREAK. If +it is a power-up then the system VIA and processor are reset +electronically. + +If this is a power on situation then nothing has been set up. The +first thing that happens when power is turned on is that the 6522 +VIAs, the processor and the floppy disc controller are reset. This is +done by means of one of three printed circuit tracks. The tracks are +RSTA, RST and NOTRESET. + +RSTA is only connected to the system 6522 Versatile Interface adaptor +(VIA). This operates through a little resistor/capacitor circuit that +only works when the power is turned on. The effect of this is that the +6522 System VIA Interrupt Enable Register (IER) bits 0 to 6 will be +clear (0) only if the reset is caused by a power on condition. + +If the Reset is caused by BREAK being pressed then the machine must +have been on and therefore one or more of the System VIA IER bits will +be set (to 1). If one or more bits are set then bit 7 of the VIA will +also be set. This is used to determine the type of Reset. So let's +look at the operating system more closely. + +D9CD LDA #&40 ;set NMI first instruction to RTI +D9CF STA &0D00 ;NMI RAM start + +RESET is the ultimate Act of God as far as the machine is concerned. +Anything could be happening so the operating system has to clean up +the system as its first act. + +These first instructions just make sure that if a disc is running no +more information will be read or written from or to the disc. This +illustrates why you shouldn't press BREAK when a disc is being +accessed! + +The next section sets up the stack: + ++ +D9D2 SEI ;disable interrupts just in case +D9D3 CLD ;clear decimal flag +D9D4 LDX #&FF ;reset stack to where it should be +D9D6 TXS ;(&1FF) + +Next find out if a power-up reset or a BREAK press by examining the +System VIA IER register. + +D9D7 LDA &FE4E ;read interrupt enable register of the system VIA +D9DA ASL ;shift bit 7 into carry +D9DB PHA ;save what's left +D9DC BEQ &D9E7 ;if Power up A=0 so go to D9E7 to clear memory + +That's probably enough for this time. Don't worry! I don't intend to +do a complete disassembly of the operating system in this series but +we will follow through the power-on sequence to the end because a lot +of interesting things happen at this time. + +We'll take a look at D9E7 and the next routine in this sequence (D9DE) +in the next part. + + +BBC 6502 Machine Code +Part Three: Cleaning up the mess +-------------------------------- +In the last part we looked at what happens when you press BREAK or +switch on the machine. We'll now continue with a look at an +undocumented (at least officially) routine. + +The byte at &258 can be used to contain information about what the +machine should do if BREAK is pressed. FX200,n is used to set this +byte. If n=2 or n=3 then the memory must be cleared. This is often +used in program protection. + +D9DE LDA &0258 ;else if BREAK pressed read BREAK Action flags (set by FX200,n) +D9E1 LSR ;divide by 2 +D9E2 CMP #&01 ;if &0258 <> 2 or 3 +D9E4 BNE &DA03 ;then Goto &DA03 +D9E6 LSR ;divide A by 2 again (A=0 if FX200,2/3 else A=n/4 + +Pages 4-&7F are cleared by a simple loop if &258=2 or 3 or it is a +power on reset. Look out for the clever way of avoiding problems on +16K machines. + +D9E7 LDX #&04 ;get page to start clearance from (4) +D9E9 STX &01 ;store it in ZP 01 +D9EB STA &00 ;store A at 00 +D9ED TAY ;and in Y to set loop counter + ;LOOP STARTS +D9EE STA (&00),Y ;clear RAM +D9F0 CMP &01 ;until page address (in &01) =0 +D9F2 BEQ &D9FD ; +D9F4 INY ;increment pointer +D9F5 BNE &D9EE ;if not zero loop round again +D9F7 INY ;else increment again (Y=1) this avoids overwriting the RTI + ;instruction at &D00 +D9F8 INX ;increment X +D9F9 INC &01 ;increment &01 +D9FB BPL &D9EE ;loop until Page (in 01)=&80 then exit + +Note that RAM addressing for 16K loops around to &4000=&00 hence the +checking of &01 for 00. This avoids overwriting zero page on BREAK +which would cause the machine to crash! + +D9FD STX &028E ;writes marker for available RAM 40 =16K,80=32 +DA00 STX &0284 ;write soft key consistency flag + +This routine shows the basic structure of a loop. Those of you who +program in BASIC will recognise it as a very simple structure: + +10 A=A+1 +20 IF A<20 GOTO 10 + +The loop uses zero page addressing with the target address in 00 and +01 (Page) and the index in Y. + +The loop is exited when the value in 01 becomes negative. Remember +that all values between 0 and &7F are considered to be positive, so +the BPL instruction can be used to exit the loop at page &80, the +first negative number. This is the first of the useful loop techniques +we'll see in this series. + +Notice that the first byte of each page is left unchanged. This is +useful if you want information to survive a BREAK of this type. This +clearing of memory is not normally carried out. + +Next week we'll have a look at the normal RESET path. + +BBC 6502 Machine Code +Part Four: Cleaning up even more mess +------------------------------------- +As we saw last week, a normal warm reset avoids the memory clearance +and proceeds to set up the System VIA. + +DA03 LDX #&0F ;set PORT B data direction register to output on bits + ;0-3 and input bits 4-7 +DA05 STX &FE42 ; + +The next bit is a little more complicated and is intimately bound up +with hardware. The function is to set up the addressable latch IC 32 +for peripherals via PORT B. + +The latch value is written by writing the value to &FE40 bits 0 to 2 +and either a 1 or 0 to bit 3. + +Writing the value + 8 therefore writes a 1 to the latched address, +otherwise a 0 is written. + +Value Peripheral Effect ++ 0 8 + +0 Sound chip Enabled Disabled + Speech Chip +1 (RS) Low High +2 (WS) Low High +2 (WS) Low High +3 Keyboard + Write Disabled Enabled +4 C0 address + modifier Low High +5 C1 address + modifier Low High +6 Caps LED On Off +7 Shift LED On Off + +C0 and C1 are involved with hardware scroll screen address. + + ;X=&F on entry +DA08 DEX ;loop start +DA09 STX &FE40 ;Write latch IC32 +DA0C CPX #&09 ;Is it 9? +DA0E BCS &DA08 ;If not go back and do it again + ;X=8 at this point + ;Caps Lock On, SHIFT Lock undetermined + ;Keyboard Autoscan on + ;Sound disabled (may still sound) + +Next the keyboard is scanned to determine the values of the keyboard +links and whether a Ctrl-Break has been performed. + +Remember that although we have spent a lot of time reading this, we +are probably less than 200 microseconds after BREAK was pressed. + +The check for Ctrl-Break is effectively looking for simultaneous +keypresses. + +DA10 INX ;X=9 +DA11 TXA ;A=X +DA12 JSR &F02A ;Interrogate keyboard +DA15 CPX #&80 ;for keyboard links 9-2 and CTRL key (1) +DA17 ROR &FC ;rotate MSB into bit 7 of &FC + +DA19 TAX ;Get back value of X for loop +DA1A DEX ;Decrement it +DA1B BNE &DA11 ;and if >0 do loop again + ;On exit if Carry set link 3 is made + ;link 2 = bit 0 of &FC and so on + ;If CTRL pressed bit 7 of &FC=1 X=0 +DA1D STX &028D ;Clear last BREAK flag +DA20 ROL &FC ;CTRL is now in carry &FC is keyboard links +DA22 JSR &EEEB ;Set LEDs + ;Carry set on entry is in bit 7 of A on exit +DA25 ROR ;Get carry back into carry flag + +To review what the operating system has done so far, about 400 +microseconds after a BREAK press or about 2 milliseconds from a power +on. Memory may have been cleared, NMIs have been short circuited, IRQs +disabled. The keyboard has been scanned for made links and for Ctrl +being pressed. + +We have also located two important and undocumented subroutines: &F02A +to scan the keyboard and &EEEB to set the keyboard LEDs. + +The F02A routine scans for the key whose code is in X being pressed: + +F02A LDY #&03 ;Stop Auto scan +F02C STY &FE40 ;by writing to system VIA +F02F LDY #&7F ;Set bits 0 to 6 of port A to input on bit 7. + ;Output on bits 0 to 6 +F031 STY &FE43 ; +F034 STX &FE4F ;Write X to Port A system VIA (key to check) +F037 LDX &FE4F ;Read back &80 if key pressed (M set) +F03A RTS ;And return + +The routine at &EEEB switches on the selected keyboard lights. + +EEEB PHP ;Save flags +EEEC LDA &025A ;Read keyboard status + ;Bit 7=1 shift enabled + ;Bit 6=1 control pressed + ;Bit 5 =0 shift lock + ;Bit 4 =0 Caps lock + ;Bit 3 =1 shift pressed +EEEF LSR ;Shift Caps bit into bit 3 +EEF0 AND #&18 ;Mask out all but 4 and 3 +EEF2 ORA #&06 ;Returns 6 if caps lock OFF &E if on. + ;Remember add 8 to the value for the addressable + ;latch to send a 1. +EEF4 STA &FE40 ;Turn on or off caps light if required +EEF7 LSR ;Bring shift bit into bit 3 +EEF8 ORA #&07 ; +EEFA STA &FE40 ;Turn on or off shift lock light +EEFD JSR &F12E ;Set keyboard counter +EF00 PLA ;Get back flags into A +EF01 RTS ;Return + +In this part we've had a look at subroutines using JSR and RTS, the +machine code equivalent of GOSUB, PROC or FN. Subroutines are often +used in machine code to perform such frequently needed functions as +scanning a keyboard or turning on and off lights. + +We've also discovered that the byte at &25A contains the keyboard +status. Try changing it for yourself. You can therefore use OR and AND +to set the shift and Caps lock status of the machine for a particular +program. + +Next week we'll examine setting up the default vector table in memory. + +BBC 6502 Machine Code +Part Five: Vectors Victor +------------------------- +The next stage is to set up the vectors on page 2. + +DA26 LDX #&9C ; +DA28 LDY #&8D ; +DA2A PLA ;Get back A from &D9DB +DA2B BEQ &DA36 ;If A=0 power up reset so go to DA36 with X=&9C + ;Y=&8D +DA2D LDY #&7E ;else let Y=&7E +DA2F BCC &DA42 ;and if not CTRL- BREAK go to DA42 for a WARM RESET +DA31 LDY #&87 ;else Y=&87 COLD RESET +DA33 INC &028D ;&28D=1 +DA36 INC &028D ;&28D=&28D+1 +DA39 LDA &FC ;Get keyboard links set +DA3B EOR #&FF ;Invert +DA3D STA &028F ;and store at &28F +DA40 LDX #&90 ;X=&90 + +What we have done is to set up the high water marks for the reset of +vectors. + +&28D=0 Warm reset, X=&9C, Y=&7E +&28D=1 Power up , X=&90, Y=&8D +&28D=2 Cold reset, X=&9C, Y=&87 + +DA42 LDA #&00 ;A=0 +DA44 CPX #&CE ;zero &200+X to &2CD +DA46 BCC &DA4A ; +DA48 LDA #&FF ;then set &2CE to &2FF to &FF +DA4A STA &0200,X ; +DA4D INX ; +DA4E BNE &DA44 ; + ;A=&FF X=0 + +This is another IF-GOTO loop, but in this case it is a double function +loop. The test at DA44 to DA46 means that A is 0 only for values of X +between the high water mark and &CD. Above this value A is set to &FF +by the instruction at &DA48. This saves a few bytes of space, +essential when writing a tightly-filled ROM. + +The next instructions set up the printer port. The only reason for +doing this now is to save two bytes. A must be &FF at this point so it +is used to set up the User VIA for outputs as the printer port. + +DA50 STA &FE63 ;Set port A of user VIA to all outputs (printer out) +DA53 TXA ;A=0 +DA54 LDX #&E2 ;X=&E2 + +START OF LOOP +DA56 STA &00,X ;set zero page addresses &E2 to &FF to zero +DA58 INX ; +DA59 BNE &DA56 ;X=0 + +Now set up the vectors in page 2 from the table at &D940: + +DA5B LDA &D93F,Y ;copy data from &D93F+Y +DA5E STA &01FF,Y ;to &1FF+Y +DA61 DEY ;until +DA62 BNE &DA5B ;1FF+Y=&200 + +Note that this is a decrementing loop which, for loops ending when an +index register reaches zero, is faster and shorter because no compare +is needed. More space saved! + +Now the RS423 port is set up via a subroutine affecting the ACIA. +(Asynchronous Communications Interface Adaptor) + +DA64 LDA #&62 ;A=&62 +DA66 STA &ED ;store in &ED +DA68 JSR &FB0A ;set up ACIA ;X=0 + +Now Acorn clears the interrupt and enable registers of both VIAs. + +DA6B LDA #&7F ;bit 7 is 0! +DA6D INX ; +DA6E STA &FE4D,X ; +DA71 STA &FE6D,X ; +DA74 DEX ; +DA75 BPL &DA6E ; + ;This loop only has two passes as X=0 on entry. +DA77 CLI ;Briefly allow interrupts to clear anything + ;pending +DA78 SEI ;Disallow again NB: all VIA IRQs are disabled +DA79 BIT &FC ;If bit 6=1 then JSR &F055 as there must be a + ;hardware interrupt! +DA7B BVC &DA80 ;else DA80 +DA7D JSR &F055 ; + +What have we here? Another undocumented routine. If bit 6 of &FC is +set there must have been a hardware interrupt when the SEI occurred. + +From the circuit diagram the only place that this IRQ could have come +from is the 1MHz bus - let's have a look at the routine at &F055. + +F055 JMP (&FDFE) ;Jim paged entry vector + +So we jump to some piece of hardware on the 1MHz bus. This would +probably be a ROM which would take over the system at power on and +Break. This has some very interesting applications. It was designed by +Acorn to provide a crude Econet facility to allow a batch of machines +to be functionally tested without the need to install a full Econet +kit. + +Next week we shall examine the VIA bus. + +BBC 6502 Machine Code +Part Six: The VIA bus +--------------------- +The next interesting routine we find in the BBC operating system is +the one that sets up the system VIA interrupts. It is located at +&DA80. Refer to the manual for the meanings of Sheila addresses. + +DA80 LDX #&F2 ;Enable interrupts 1,4,5,6 of system VIA +DA82 STX &FE4E ; + ;0 Keyboard enabled as needed + ;1 Frame sync pulse + ;4 End of A/D conversion + ;5 T2 counter (for speech) + ;6 T1 counter (10 mSec intervals) + +DA85 LDX #&04 ;set system VIA PCR +DA87 STX &FE4C ; + ;CA1 Interrupt on negative edge (Frame sync) + ;CA2 Handshake output for keyboard + ;CB1 Interrupt on negative edge (end of conversion) + ;CB2 Negative edge (Light pen strobe) +DA8A LDA #&60 ;Set system VIA ACR +DA8C STA &FE4B ; + ;Disable latching + ;Disable shift register + ;T1 counter continuous interrupts + ;T2 counter timed interrupt + +DA8F LDA #&0E ;Set system VIA T1 counter (low) +DA91 STA &FE46 ; + ;This becomes effective when T1 hi set +DA94 STA &FE6C ;Set user VIA PCR + ;CA1 interrupt on -ve edge (Printer Acknowledge) +DA80 LDX #&F2 ;enable interrupts + ;CA2 High output (printer strobe) + ;CB1 Interrupt on -ve edge (user port) + ;CB2 Negative edge (user port) +DA97 STA &FEC0 ;Set up A/D converter Bits 0 and 1 determine + ;channel selected + ;If Bit 3=0 it is set for an 8-bit conversion. + ;If bit 3=1 12-bit conversion. + +Now although the machine now knows how much RAM it has it still +doesn't know if it's a Model A or Model B, so it does not know if a +user VIA is present at &FE60-FE6F. + +The next routine tests for the presence of a user VIA. The system +timers are then set up to interrupt every 10mSec. Sound channels are +cleared and the serial ULA is set up. Then the function keys are +reset. + +Now we need a catalogue of sideways ROMS. This is not a catalogue in +the conventional sense as the ROM title is always at the same place in +the ROM itself and can be read from there. It is a catalogue of the +ROM types and positions. + +There is a ROM latch at &FE30. Writing a number between 0 and 15 to +this switches the corresponding ROM into the area between &8000 and +&BFFF. A short subroutine does this and maintains a copy of the +current ROM in zero page at location &F4. + + ;on entry X=required ROM number +DC16 STX &F4 ;RAM copy of ROM latch +DC18 STX &FE30 ;Write to ROM latch +DC1B RTS ;and return + +You should use this subroutine if you want to switch ROMs. Now we can +look at the ROM cataloguing routines; + +A ROM is considered to be valid if it contains a string identical to +astring at location &DF0C in the Operating System ROM. + +DF0C DB ')C(' ; +DF0F DB 0 ; + +The location of this string is pointed to by an offset byte located at +&8007. + +;X=0 on entry +DABD JSR &DC16 ;Set up ROM latch and RAM copy to X +DAC0 LDX #&03 ;Set X to point to offset in table +DA80 LDX #&F2 ;Enable interrupts +DAC2 LDY &8007 ;Get copyright offset from ROM +DAC5 LDA &8000,Y ;Get first byte +DAC8 CMP &DF0C,X ;Compare it with table byte +DACB BNE &DAFB ;If not the same then goto DAFB +DACD INY ;Point to next byte +DACE DEX ;(s) +DACF BPL &DAC5 ;and if still +ve go back to check next byte. + ;This point is reached if 4 bytes indicate + ;valid ROM + +Next the first 1K of each ROM is checked against higher priority ROMs +to ensure that there are no matches. If a match is found, the lower +priority ROM is ignored. + +A ROM type byte is located at &8006. A catalogue of these bytes is +held at &2A1-&2B0. If bit 7 of this byte is 0 then the ROM is BASIC. +The position of this ROM is stored at &24B. + +Now the ROMs are catalogued it is time to set up the speech system and +screen. More about that next week. + +BBC 6502 Machine Code +Part Seven: Talk to me +---------------------- +The operating system start-up routines next checks the SPEECH system. +At this point the X register is set to 16 (&10) by previous routines. + +This is one of the reasons why this routine is inserted here. Setting +X to the required value would use two more bytes. This is not much +space but it can make the difference between all of the OS fitting +into a single ROM and a complete hardware or software redesign. + +DB11 BIT &FE40 ;If bit 7 low then we have speech system fitted +DB14 BMI &DB27 ;else goto DB27 for screen set up routine. +DB16 DEC &027B ;(027B)=&FF a RAM flag that indicates that a speech + ;chip is present. +DB19 LDY #&FF ;Y=&FF +DB1B JSR &EE7F ;Initialise speech generator +DB1E DEX ;via this +DB1F BNE &DB19 ;loop + +Now X = 0 so: + +DB21 STX &FE48 ;Set T2 timer for speech +DB24 STX &FE49 ; + +Screen set-up +------------- +X=0 on entry to this routine which gets the default screen mode and +then goes off to the screen setup routine. + +DB27 LDA &028F ;Get back start up options (mode) +DB2A JSR &C300 ;then jump to initialise screen. + +One of the things that I wondered when I got a BBC was how the RESET +key could possibly act as a soft key. As we all know BREAK acts as +soft key 10. But the keyboard buffer is cleared by the Reset. Tucked +away is the five-byte routine that makes the BREAK key act as soft +key 10. + +Soft keys work by inserting a byte greater than 127 into the keyboard +buffer. &CA is the code for key 10. + +DB2D LDY #&CA ;Y=&CA +DB2F JSR &E4F1 ;to enter this value in the keyboard buffer + +Simple isn't it? You can use the routine yourself although further +investigation will show that E4F1 is part of an OSbyte call. Remember +that the keyboard buffer is buffer 0. + +E4F1 LDX #&00 ;X=0 keyboard buffer + +************************************** +* * +* OSBYTE 153 Put byte in input * +* Buffer checking for ESCAPE * +* * +************************************** + +On entry X = buffer number which is either 0 or 1. If it's 0 then the +keyboard buffer is selected. If it's 1 then it is the RS423 buffer. + +Notice that the JSR to EF41 ensures that ONLY the keyboard buffer can +be selected. Once again we are looking at coding economy, in this case +with a specific keyboard buffer entry routine. Y contains the +character to be written. + +E4F3 TXA ;A=buffer number +E4F4 AND &0245 ;and with RS423 mode (0 treat as keyboard 1 ignore + ;Escapes no events no soft keys) +E4F7 BNE &E4AF ;so if RS423 buffer AND RS423 in normal mode (1) E4AF + ; +E4F9 TYA ;else Y=A character to write +E4FA EOR &026C ;compare with current escape ASCII code (0=match) +E4FD ORA &0275 ;or with current ESCAPE status (0=ESC, 1=ASCII) +E500 BNE &E4A8 ;if ASCII or no match E4A8 to enter byte in buffer +E502 LDA &0258 ;else get ESCAPE / BREAK action byte +E505 ROR ;Rotate to get ESCAPE bit into carry +E506 TYA ;get character back in A +E507 BCS &E513 ;and if escape disabled exit with carry clear +E509 LDY #&06 ;else signal EVENT 6 Escape pressed +E50B JSR &E494 ; +E50E BCC &E513 ;if event handles ESCAPE then exit with carry clear +E510 JSR &E674 ;else set ESCAPE flag +E513 CLC ;clear carry +E514 RTS ;and exit + +This routine will normally be accessed by assembly language +programmers by OSbyte 138 which calls EF43. + +BBC 6502 Machine Code +Part Eight: Breaker Break +------------------------- +One of the 'secret' features of the BBC Micro OS 1.20 when it was +arrived was the BREAK intercept. This is a useful method of taking +over the machine and is sometimes used by ROM software. + +There are two entry points, entered with the carry flag reset to 0 and +set to 1 respectively. The first call comes before sideways ROM calls. + +Enter BREAK intercept with Carry Clear + +DB32 JSR &EAD9 ;check to see if BOOT address is set up if so + ;JMP to it + +The address &287 is written by OSbyte 247 and the jump addresses in +&288 and &289 by OSbytes 248 and 249. The machine code for JMP is &4C. + +EAD9 LDA &0287 ;get BREAK vector code +EADC EOR #&4C ;produces 0 if JP (4C) not in &287 +EADE BNE &EAF3 ;if not goto EAF3 +EAE0 JMP &0287 ;else jump to use BREAK code +EAF3 RTS ;Return + +The RTS at the end of another routine is used because it saves code. + +Frequently you will find machine code routines where a lot of branches +go to a single RTS for just this reason. If you are writing your own +code remember that the RTS must be within range of the branch. One of +the most common assembler errors is a branch out of range that in turn +causes more errors when you add an extra RTS. + +Obviously at this point the machine could be totally in your control. +You can return control to the OS with an RTS or just continue on your +merry way. + +Remember that the sideways ROMs don't have any workspace yet and you +can't really run BASIC or any other language as the workspace will not +exist. But, assuming that you don't want to do any of this, let's go +back to the OS routines after testing for BREAK intercept. + +DB35 JSR &F140 ;set up cassette options +DB38 LDA #&81 ;test for tube to FIFO buffer 1 +DB3A STA &FEE0 ; +DB3D LDA &FEE0 ; +DB40 ROR ;put bit 0 into carry +DB41 BCC &DB4D ;if no tube then DB4D +DB43 LDX #&FF ;else +DB45 JSR &F168 ;issue ROM service call &FF to initialise TUBE system +DB48 BNE &DB4D ;if not 0 on exit (tube not initialised) DB4D +DB4A DEC &027A ;else set tube flag to show its active + +Now the Tube is flagged as active, or not as the case may be. We +continue next week, with the setup routines for the sideways ROMs. + +BBC 6502 Machine Code +Part Nine: A ROM with a view +----------------------------- +Now we nearly have a working system, we are, perhaps, 400 milliseconds +into the Power up routine. Now is the time to set up all of those nice +sideways ROMs we catalogued earlier. + +First we set up workspace and hence the value of BASIC's PAGE +variable. The call to ROMs is made via F168. This is available to the +programmer as OSBYTE 143. + +A ROM can have a number between 0 and 15 and will have two entry +points - a Service entry at &8003 and a Language entry at &8000. If +the ROM does not contain language code it will not have a language +entry. + +ROMs are paged into main memory by writing the ROM number to a latch +at &FE30. Hardware could be arranged to allow 256 ROMs although the +operating system does not support this. + +The Break Intercept code could be used to make drastic hardware +modifications like this. + +************************************** +* * +* OSBYTE 143 * +* Pass service commands * +* to sideways ROMs * +* * +************************************** + ;on entry X=command number +F168 LDA &F4 ;get current ROM number +F16A PHA ;store it +F16B TXA ;command in A +F16C LDX #&0F ;set X=15 + +The next bit of code is a countdown loop to send the command code to +each enabled ROM in turn. The Map at &2A1 is used to decide which ROMs +are active. Note the use of a countdown loop. This gives code economy +and explains why the highest ROM number has priority. + +F16E INC &02A1,X ;read bit 7 on ROM map (no ROM has type 254 &FE) +F171 DEC &02A1,X ; +F174 BPL &F183 ;if not set (+ve result) +F176 STX &F4 ;else store ROM number in &F4 +F178 STX &FE30 ;switch in paged ROM +F17B JSR &8003 ;and jump to service entry +F17E TAX ;on exit put A in X +F17F BEQ &F186 ;if 0 (command recognised by ROM) reset ROMs & exit +F181 LDX &F4 ;else point to next lower ROM +F183 DEX ; +F184 BPL &F16E ;and go round loop again +F186 PLA ;get back original ROM number +F187 STA &F4 ;store it in RAM copy +F189 STA &FE30 ;select original page +F18C TXA ;put X back in A +F18D RTS ;and return + +Couldn't be easier! So we can now return to the main body of the +routine. + +DB4D LDY #&0E ;set current value of PAGE +DB4F LDX #&01 ;issue call to claim absolute workspace +DB51 JSR &F168 ;via F168 +DB54 LDX #&02 ;send private workspace claim call +DB56 JSR &F168 ;via F168 + +OSHWM is OS High Water Mark. The highest address used by the operating +system. + +DB59 STY &0243 ;set primary OSHWM +DB5C STY &0244 ;set current OSHWM +DB5F LDX #&FE ;issue call for Tube to explode character set etc. +DB61 LDY &027A ;Y=FF if tube present else Y=0 +DB64 JSR &F168 ;and make call via F168 + +We now have the machine set up to enter a language, all the filing +systems have been set up and the sideways ROMs activated. + +Next week we finally start the screen messages. + +BBC 6502 Machine Code +Part Ten: Stringing it along +----------------------------- +The next routine shows why the Machine start up message is not always +seen on third-party kit. + +DB67 AND &0267 ;if A=&FE and bit 7 of 0267 is set then continue +DB6A BPL &DB87 ;else ignore start up message +DB6C LDY #&02 ;output to screen +DB6E JSR &DEA9 ;'BBC Computer ' message + +Looking at the routine in DEA9 we find a very useful string printing +routine. Remember that Y = 2 on entry. + +DEA9 LDA #&C3 ;point to start &C300 +DEAB STA &FE ;store it +DEAD LDA #&00 ;point to lo byte +DEAF STA &FD ;store it and start loop with Y=2 +DEB1 INY ;print character in string +DEB2 LDA (&FD),Y ;pointed to by &FD/E +Y +DEB4 JSR OSASCI ;print it expanding Carriage returns +DEB7 TAX ;store A in X +DEB8 BNE &DEB1 ;and loop again if not =0 +DEBA RTS ;else exit + +Here is the string delimited by BRK. The code for BRK is 00. Y is 3 +when the first character is read so its address is &C303. + +C303 DB 13 ;Carriage Return +C304 DB 'BBC Computer ' +C311 BRK + +Notice that the routine uses TAX to set the zero flag which marks the +end of the string. This is a useful tip. + +The next part of the Operating system deals with printing correct +messages on the screen. + +DB71 LDA &028D ;0=warm reset, If a cold reset continue +DB74 BEQ &DB82 ; +DB76 LDY #&16 ;by checking length of RAM +DB78 BIT &028E ; +DB7B BMI &DB7F ;and either +DB7D LDY #&11 ; +DB7F JSR &DEA9 ;finishing message with '16K' or '32K' +DB82 LDY #&1B ;and two new lines +DB84 JSR &DEA9 ; + +Notice that Y is used to pick the appropriate message. + +C312 DB '16K' +C315 DB 7 ;Bell +C316 BRK +C317 DB '32K' +C31A DB 7 ;Bell +C31B BRK +C31C DB 08,0D,0D + +Notice the BBC Beep at this point indicates that nearly all set up +procedures have been finished. + +The hum is generated by the Sound channel which is reset as part of +the start routine. Hence the HUM-BEEP start up. If the machine does +not start properly the sound signals give a strong clue to the nature +of the problem. Having got this far the OS gives us another chance to +take control. + +Enter BREAK INTERCEPT ROUTINE WITH CARRY SET (call 1) + +DB87 SEC ; +DB88 JSR &EAD9 ;look for break intercept jump + ;SEE EARLIER PART + +Next we set up the keyboard lights + +DB8B JSR &E9D9 ;set up LEDs in accordance with keyboard status + +This is another 'undocumented' OSBYTE call. + +************************************** +* * +* OSBYTE &76 (118) * +* SET LEDs to Keyboard Status * +* * +************************************** +;osbyte entry with carry set +E9D9 PHP ;PUSH P +E9DA SEI ;DISABLE INTERRUPTS +E9DB LDA #&40 ;switch on CAPS and SHIFT lock lights +E9DD JSR &E9EA ;via subroutine +E9E0 BMI &E9E7 ;if ESCAPE exists (M set) E9E7 +E9E2 CLC ;else clear V and C +E9E3 CLV ;before calling main keyboard routine to +E9E4 JSR &F068 ;switch on lights as required +E9E7 PLP ;get back flags +E9E8 ROL ;and rotate carry into bit 0 +E9E9 RTS ;Return to calling routine + ; +* Turn on keyboard lights and +* Test Escape flag + ; +E9EA BCC &E9F5 ;if carry clear +E9EC LDY #&07 ;switch on shift lock light +E9EE STY &FE40 ; +E9F1 DEY ;Y=6 +E9F2 STY &FE40 ;switch on Caps lock light +E9F5 BIT &FF ;set minus flag if bit 7 of &00FF is set indicating +E9F7 RTS ;that ESCAPE condition exists, then return + +The Keyboard routine continues via the KEYV. This is a little long to +include here so we'll leave it until a later part. So back to the +Start up routine next week with the cassette system. + +BBC 6502 Machine Code +Part Eleven: Language! +---------------------- +Having got the keyboard nicely set up the machine proceeds to +initialise a filing system and run a !BOOT file if one exists. The +start up options are already read from the keyboard links. + +DB8E PHP ;save flags +DB8F PLA ;and get back in A +DB90 LSR ;zero bits 4-7 and bits 0-2 bit 4 which was bit 7 +DB91 LSR ;may be set +DB92 LSR ; +DB93 LSR ; +DB94 EOR &028F ;EOR with start up options which may or may not +DB97 AND #&08 ;invert bit 4 +DB99 TAY ;Y=A +DB9A LDX #&03 ;make initialisation call, if Y=0 on entry +DB9C JSR &F168 ;RUN, EXEC or LOAD !BOOT file from a filing system. +DB9F BEQ &DBBE ;if a ROM accepts this call then DBBE +DBA1 TYA ;else put Y in A +DBA2 BNE &DBB8 ;if Y<>0 DBB8 +DBA4 LDA #&8D ;else set up standard cassette baud rates +DBA6 JSR &F135 ;via &F135 which is OSBYTE 140. +DBA9 LDX #&D2 ; +DBAB LDY #&EA ; +DBAD DEC &0267 ;decrement ignore start up message flag +DBB0 JSR OSCLI ;and execute /!BOOT +DBB3 INC &0267 ;restore start up message flag +DBB6 BNE &DBBE ;if not zero then DBBE +DBB8 LDA #&00 ;else A=0 +DBBA TAX ;X=0 +DBBB JSR &F137 ;set tape speed via OSBYTE 140. + +We now have an active filing system. The next job is to preserve the +current language on soft RESET. + +DBBE LDA &028D ;get last RESET Type +DBC1 BNE &DBC8 ;if not soft reset DBC8 +DBC3 LDX &028C ;else get current language ROM address +DBC6 BPL &DBE6 ;if +ve (language available) then skip search + ;routine +For a cold break we search for the language with the highest priority. + +DBC8 LDX #&0F ;set pointer to highest available ROM +DBCA LDA &02A1,X ;get ROM type from map +DBCD ROL ;put hi-bit into carry, bit 6 into bit 7 +DBCE BMI &DBE6 ;if bit 7 set then ROM has a language entry so DBE6 +DBD0 DEX ;else search for language until X=&ff + +Check for Tube if no language found. + +DBD1 BPL &DBCA ;check if tube present +DBD3 LDA #&00 ;if bit 7 of tube flag is set BMI succeeds +DBD5 BIT &027A ;and TUBE is connected else +DBD8 BMI &DC08 ;make error + +No language error + +DBDA BRK ; +DBDB DB &F9 ;error number +DBDC DB 'Language?' ;message +DBE5 BRK ; + +This might seem odd as BRK is handled by the current language BRK +handler, but we don't have a language! We need to investigate further +in another part. + +DBE6 CLC ; + +OSBYTE 142 enter Language ROM at &8000 X=ROM number. Carry is set if +this is an OSBYTE call and clear if this is an initialisation routine. + +DBE7 PHP ;save flags +DBE8 STX &028C ;put X in current ROM page +DBEB JSR &DC16 ;select that ROM +DBEE LDA #&80 ;A=128 +DBF0 LDY #&08 ;Y=8 +DBF2 JSR &DEAB ;display text string held in ROM at &8008,Y +DBF5 STY &FD ;save Y on exit (end of language string) +DBF7 JSR OSNEWL ;two line feeds +DBFA JSR OSNEWL ;are output +DBFD PLP ;then get back flags +DBFE LDA #&01 ;A=1 required for language entry +DC00 BIT &027A ;check if tube exists +DC03 BMI &DC08 ;and goto DC08 if it does +DC05 JMP &8000 ;else enter language at &8000 + +TUBE FOUND enter tube software + +DC08 JMP &0400 ;enter tube environment + +The Tube initialisation would have read the language across to the +TUBE usually but it could be loaded by a !BOOT file from the filing +system initialisation. + +The operating system now stops general control of the system and hands +this to the language which looks after command lines etc. The OS +however still handles the screen, keyboard and much else. + +Notice how every possible eventuality was taken into account during +the initialisation routine. This is one of the things that made the +Beeb a very powerful machine. + +Next week we'll have a look at the Interrupt code. + +BBC 6502 Machine Code +Part Twelve: Pardon me! +----------------------- +We finished the last part at the point where the operating systems +power up routine handed over control to the language. We'll write our +own language later in the series but for now let's dive into another +entry point. + +When the processor's IRQ pin (4) goes low (0V) the processor finishes +off the current instruction and then goes off to run some microcode of +its own. This checks that the RDY (2) pin is high and that the +interrupt flag in the status register is 0 (not set). If it is set the +interrupt is ignored and the processor goes to the next instruction. +This continues when the IRQ pin is low. + +If the flag is clear then the processor stores the program counter and +status register on the stack and sets the interrupt flag. The 6502 +then gets the address stored in &FFFE and &FFFF and executes this +instruction next. + +If a BRK instruction is found in executing code then the processor +performs exactly the same actions except that it does not check the +status register for the interrupt flag, it does set a flag in the +status register, the BRK flag. + +The main entry point for IRQ (and BRK) for OS 1.20 is &DC51. + +MAIN IRQ Entry point + +;ON ENTRY STACK contains STATUS REGISTER,PCH,PCL +DC1C STA &FC ;save A +DC1E PLA ;get back status (flags) +DC1F PHA ;and save again +DC20 AND #&10 ;check if BRK flag set +DC22 BNE &DC27 ;if so goto DC27 +DC24 JMP (&0204) ;else JUMP through IRQ1V + +That's pretty straightforward so far. As you can see IRQ1V allows you +to put your own hardware at a higher priority than anything else in +the machine. + +You can also write your own hardware interrupt handler if you wish. +This is the flexibility that made the BBC machine so remarkably +successful among knowledgeable users. + +Let's look at the BRK handler now. + +* BRK handling routine * +DC27 TXA ;save X on stack +DC28 PHA ; +DC29 TSX ;get status pointer +DC2A LDA &0103,X ;get Program Counter low byte +DC2D CLD ; +DC2E SEC ;set carry +DC2F SBC #&01 ;subtract 2 (1+carry) +DC31 STA &FD ;and store it in &FD +DC33 LDA &0104,X ;get hi byte +DC36 SBC #&00 ;subtract 1 if necessary +DC38 STA &FE ;and store in &FE +DC3A LDA &F4 ;get currently active ROM +DC3C STA &024A ;and store it in &24A +DC3F STX &F0 ;store stack pointer in &F0 +DC41 LDX #&06 ;and issue ROM service call 6 +DC43 JSR &F168 ;(User BRK) to ROMs + ;now &FD/E points to byte after BRK + ;ROMS may use BRK for their own purposes + ;and many do! + +It's interesting to see what happens with the ROM handler. This is +also an entry point for OSBYTE 143 so you can use this in your own +code. + +* OSBYTE 143 * +*Pass service commands to sideways ROMs * + ;on entry X=command number +F168 LDA &F4 ;get current ROM number +F16A PHA ;store it +F16B TXA ;command in A +F16C LDX #&0F ;set X=15 + ;send commands loop +F16E INC &02A1,X ;read bit 7 on ROM map (no ROM has + ;type 254 &FE) +F171 DEC &02A1,X ; +F174 BPL &F183 ;if not set (+ve result) +F176 STX &F4 ;else store ROM number in &F4 +F178 STX &FE30 ;switch in paged ROM +F17B JSR &8003 ;and jump to service entry +F17E TAX ;on exit put A in X +F17F BEQ &F186 ;if 0 (command recognised by ROM) + ;reset ROMs & exit +F181 LDX &F4 ;else point to next lower ROM +F183 DEX ; +F184 BPL &F16E ;and go round loop again +F186 PLA ;get back original ROM number +F187 STA &F4 ;store it in RAM copy +F189 STA &FE30 ;select original page +F18C TXA ;put X back in A +F18D RTS ;and return + +Useful little routine that. So back to the BRK handler. + +DC46 LDX &028C ;get current language +DC49 JSR &DC16 ;and activate it +DC4C PLA ;get back original value of X +DC4D TAX ; +DC4E LDA &FC ;get back original value of A +DC50 CLI ;allow interrupts +DC51 JMP (&0202) ;and JUMP via BRKV (normally into current language) + +Next week we'll carry on by taking a look at the BRK handler. + +BBC 6502 Machine Code +Part Thirteen: Give us a BRK +---------------------------- +BRK is usually handled by the default language (or by a Sideways ROM). +However, it may be that you are running a machine code program before +a current language is set up or perhaps your language doesn't handle +BRK (it should but you never know). + +That's when a default BRK handler takes over. + +* DEFAULT BRK HANDLER * + +DC54 LDY #&00 ;Y=0 to point to byte after BRK +DC56 JSR &DEB1 ;print message + +Let's have a look at the print routine. Remember that the error- +handling layout is: + +BRK +Error Number (1 byte) +Message +BRK + +Y plus the address in &FD &FE points to the error message on entry. + +DEB1 INY ;point to first ;character in string +DEB2 LDA (&FD),Y +DEB4 JSR OSASCI ;print it + ;expanding + ;Carriage + ;returns +DEB7 TAX ;store A in X to change flags +DEB8 BNE &DEB1 ;and loop again if not =0 +DEBA RTS ;else exit + +A standard print routine, nothing out of the ordinary but nice and +compact. + +You can use this in your own print routines by changing the zero page +values. Back to the default BRK handler and an interesting bit of +code. + +DC59 LDA &0267 ;if BIT 0 set and DISK EXEC error +DC5C ROR ;occurs +DC5D BCS &DC5D ;hang up machine! + +Nasty! But the machine has to be in a pretty unusual configuration for +this to happen. Mind you, setting 0267 then doing a JSR to DC59 would +confuse the average user. + +DC5F JSR OSNEWL ;else print two newlines +DC62 JSR OSNEWL ; +DC65 JMP &DBB8 ;and set tape speed before entering the current + ;language +DBB8 LDA #&00 ;else A=0 +DBBA TAX ;X=0 +DBBB JSR &F137 ;set tape speed via OSBYTE 141. + +There's the end of the BRK handling code. As I said before this is +generally handled by the default language but you can arrange for your +own code or a Sideways ROM to handle it. + +Next week we'll return to the interrupt system with a look at the +default entry point for IRQ1. + +BBC 6502 Machine Code +Part Fourteen: The story so far... +---------------------------------- +We left the interrupt-handling routine just after it had gone off to +the IRQ1V vector. If you don't change the vector the code continues +from DC93. + +One very important thing to remember about an interrupt-driven machine +like the BBC is that the interrupt flag is not set for too long. If it +is the machine could crash. This means that interrupt routines are +short and snappy. + +* Main IRQ Handling routines, default IRQIV destination * + +DC93 CLD ;clear decimal flag +DC94 LDA &FC ;get original value of A +DC96 PHA ;save it +DC97 TXA ;save X +DC98 PHA ; +DC99 TYA ;and Y +DC9A PHA ;on the stack + ;note the pre-CMOS code! +DC9B LDA #&DE ;A=&DE +DC9D PHA ;store it +DC9E LDA #&81 ;save &81 +DCA0 PHA ;store it (a RTS will now jump to DE82) + +This is quite a useful technique as we will see later. If we now use +JMP to go to an OS routine we can ensure that the routine, which ends +with an RTS, causes execution to go to a specified point. + +This saves a lot of code as it can be arranged that the first device +found that called the interrupt will be the only one handled. This, in +turn, saves time! + +We now poll the hardware looking for who caused it. The first routine +deals with the serial/tape system. + +DCA1 CLV ;clear V flag +DCA2 LDA &FE08 ;get value of status register of ACIA +DCA5 BVS &DCA9 ;if this was source then DCA9 to process +DCA7 BPL &DD06 ;else if no interrupt requested DD06 +DCA9 LDX &EA ;read RS423 timeout counter +DCAB DEX ;decrement it +DCAC BMI &DCDE ;and if <0 DCDE +DCAE BVS &DCDD ;else if >&40 DCDD (RTS to DE82) +DCB0 JMP &F588 ;else read ACIA via F588 + ;RTS ends routine!! +DCB3 LDY &FE09 ;read ACIA data +DCB6 ROL ; +DCB7 ASL ; +DCB8 TAX ;X=A +DCB9 TYA ;A=Y +DCBA LDY #&07 ;Y=07 +DCBC JMP &E494 ;check and service EVENT 7 RS423 error +DCBF LDX #&02 ;read RS423 output buffer +DCC1 JSR &E460 ; +DCC4 BCC &DCD6 ;if C=0 buffer is not empty goto DCD6 +DCC6 LDA &0285 ;else read printer destination +DCC9 CMP #&02 ;is it serial printer?? +DCCB BNE &DC68 ;if not DC68 +DCCD INX ;else X=3 +DCCE JSR &E460 ;read printer buffer +DCD1 ROR &02D2 ;rotate to pass carry into bit 7 +DCD4 BMI &DC68 ;if set then DC68 +DCD6 STA &FE09 ;pass either printer or RS423 data to ACIA +DCD9 LDA #&E7 ;set timeout counter to stored value +DCDB STA &EA ; +DCDD RTS ;and exit (to DE82) + + ;A contains ACIA status +DCDE AND &0278 ;AND with ACIA bit mask (normally FF) +DCE1 LSR ;rotate right to put bit 0 in carry +DCE2 BCC &DCEB ;if carry clear receive register not full so DCEB +DCE4 BVS &DCEB ;if V is set then DCEB +DCE6 LDY &0250 ;else Y=ACIA control setting +DCE9 BMI &DC7D ;if bit 7 set receive interrupt is enabled so DC7D + +DCEB LSR ;put BIT 2 of ACIA status into +DCEC ROR ;carry if set then Data Carrier Detected applies +DCED BCS &DCB3 ;jump to DCB3 + +DCEF BMI &DCBF ;if original bit 1 is set TDR is empty so DCBF +DCF1 BVS &DCDD ;if V is set then exit to DE82 + +DCF3 LDX #&05 ;X=5 +DCF5 JSR &F168 ;issue ROM call 5 'unrecognised ;interrupt' + +We've seen this ROM service routine call before. + +DCF8 BEQ &DCDD ;if a ROM recognises it then exit to DE82 +DCFA PLA ;otherwise get back DE82 address from stack +DCFB PLA ; +DCFC PLA ;and get back X, Y and A +DCFD TAY ; +DCFE PLA ; +DCFF TAX ; +DD00 PLA ; +DD01 STA &FC ;&FC=A +DD03 JMP (&0206) ;and offer to the user via IRQ2V + +That was a little convoluted, to say the least. Next week we look at how the +VIAs are dealt with. + +BBC 6502 Machine Code +Part Fifteen: Hardware VIA interrupts +------------------------------------- +After deciding that it wasn't the ACIA that caused the interrupt, the +VIAs are the next port of inquisition. + +* VIA INTERRUPTS ROUTINES * + +DD06 LDA &FE4D ;read system VIA interrupt flag register +DD09 BPL &DD47 ;if bit 7=0 the VIA has not caused interrupt goto DD47 + +DD0B AND &0279 ;mask with VIA bit mask +DD0E AND &FE4E ;and interrupt enable register +DD11 ROR ;rotate right twice to ;check for IRQ 1 (frame sync) + +DD12 ROR ; +DD13 BCC &DD69 ;if carry clear then no IRQ 1, else IRQ 1 means + ;interrupt request 1. This is different from the + ;vector IRQ1. + +DD15 DEC &0240 ;decrement vertical sync counter +DD18 LDA &EA ;A=RS423 Timeout counter +DD1A BPL &DD1E ;if +ve then DD1E +DD1C INC &EA ;else increment it +DD1E LDA &0251 ;load flash character counter +DD21 BEQ &DD3D ;if 0 then flash system is not in use, ignore it +DD23 DEC &0251 ;else decrement counter +DD26 BNE &DD3D ;and if not 0 go on past reset routine + +This routine resets the flashing character system. + +DD28 LDX &0252 ;get mark period count in X +DD2B LDA &0248 ;current VIDEO ULA control setting in A +DD2E LSR ;shift bit 0 into C to ;check if first colour +DD2F BCC &DD34 ;is effective if so C=0. Jump to DD34 +DD31 LDX &0253 ;else get space period count in X +DD34 ROL ;restore bit +DD35 EOR #&01 ;and invert it +DD37 JSR &EA00 ;then change colour + +DD3A STX &0251 ;&0251=X resetting the counter + +DD3D LDY #&04 ;Y=4 and call E494 to check and implement vertical +DD3F JSR &E494 ;sync event (4) if necessary +DD42 LDA #&02 ;A=2 +DD44 JMP &DE6E ;clear interrupt 1 and exit + +Remember the RTS routine last time? + +* PRINTER INTERRUPT USER VIA 1 * + +DD47 LDA &FE6D ;Check USER VIA interrupt flags register +DD4A BPL &DCF3 ;if +ve USER VIA did not call interrupt +DD4C AND &0277 ;else check for USER IRQ 1 printer interrupt. +DD4F AND &FE6E ; +DD52 ROR ; +DD53 ROR ; +DD54 BCC &DCF3 ;if bit 1=0 then no ;interrupt 1 so DCF3 +DD56 LDY &0285 ;else get printer type +DD59 DEY ;decrement +DD5A BNE &DCF3 ;if not parallel then :CF3 +DD5C LDA #&02 ;reset interrupt 1 flag +DD5E STA &FE6D ; +DD61 STA &FE6E ;disable interrupt 1 +DD64 LDX #&03 ;and output data to parallel printer +DD66 JMP &E13A ;and exit via RTS + +* SYSTEM INTERRUPT 5 Speech * + +DD69 ROL ;get bit 5 into bit 7 +DD6A ROL ; +DD6B ROL ; +DD6C ROL ; +DD6D BPL &DDCA ;if not set this is not ;a speech interrupt so DDCA +DD6F LDA #&20 ; +DD71 LDX #&00 ; +DD73 STA &FE4D ; +DD76 STX &FE49 ;and zero high byte of Timer t2 +DD79 LDX #&08 ;&FB=8 +DD7B STX &FB ; +DD7D JSR &E45B ;and examine buffer 8 +DD80 ROR &02D7 ;shift carry into bit 7 +DD83 BMI &DDC9 ;and if set buffer is empty so exit +DD85 TAY ;else Y=A +DD86 BEQ &DD8D ; +DD88 JSR &EE6D ;control speech chip +DD8B BMI &DDC9 ;if negative exit +DD8D JSR &E460 ;else get a byte from buffer +DD90 STA &F5 ;store it to indicate speech or file ROM +DD92 JSR &E460 ;get another byte +DD95 STA &F7 ;store it +DD97 JSR &E460 ;and another +DD9A STA &F6 ;giving address to be accessed in paged ROM +DD9C LDY &F5 ;Y=&F5 +DD9E BEQ &DDBB ;and if =0 then DDBB +DDA0 BPL &DDB8 ;else if +ve DDB8 +DDA2 BIT &F5 ;if bit 6 of F5 =1 (&F5)>&40 +DDA4 BVS &DDAB ;then DDAB +DDA6 JSR &EEBB ;else continue for more speech processing +DDA9 BVC &DDB2 ;if bit 6 clear then DDB2 +DDAB ASL &F6 ;else double address in &F6/7 +DDAD ROL &F7 ; +DDAF JSR &EE3B ;and call EE3B +DDB2 LDY &0261 ;get speech enable/disable flag into Y +DDB5 JMP &EE7F ;and JMP to EE7F + +DDB8 JSR &EE7F ;Call EE7F +DDBB LDY &F6 ;get address pointer in Y +DDBD JSR &EE7F ; +DDC0 LDY &F7 ;get address pointer high in Y +DDC2 JSR &EE7F ; +DDC5 LSR &FB ; +DDC7 BNE &DD7D ; +DDC9 RTS ;and exit + +Next week we continue with a look at the remaining System Interrupts. + +BBC 6502 Machine Code +Part Sixteen: Timers and Keyboard Interrupts +-------------------------------------------- +The last part showed how the BBC Micro handles some of the system +interrupt calls. Most of these are pretty routine so we won't continue +with an interminable list. + +The next interesting routines concern how the timers and keyboard +interrupts are handled. + +* SYSTEM INTERRUPT 6 10mS Clock * + +DDCA BCC &DE47 ;bit 6 is in carry so if clear there is no 6 so go + ;on to DE47 +DDCC LDA #&40 ;Clear interrupt 6 +DDCE STA &FE4D ; + +This is the start of the update timers routine, This is interesting +because of the way that the timer information is stored. It's very +clever. There are two timer stores, &292-6 and &297-B. These are +updated by adding 1 to the current timer and storing the result in the +other, the direction of transfer being changed each time of update. + +This ensures that at least one timer is valid at any call as the +current timer only is read. Other methods would cause inaccuracies if +a timer was read while being updated. + +DDD1 LDA &0283 ;get current system clock store pointer (5,or 10) +DDD4 TAX ;put A in X +DDD5 EOR #&0F ;and invert lo nybble (5becomes 10 and vv) +DDD7 PHA ;store A +DDD8 TAY ;put A in Y. Carry is always set at this point +DDD9 LDA &0291,X ;get timer value +DDDC ADC #&00 ;update it +DDDE STA &0291,Y ;store result in alternate +DDE1 DEX ;decrement X +DDE2 BEQ &DDE7 ;if 0 exit +DDE4 DEY ;else decrement Y +DDE5 BNE &DDD9 ;and go back and do next byte +DDE7 PLA ;get back A +DDE8 STA &0283 ;and store back in clock pointer (ie. inverse + ;previous contents) +DDEB LDX #&05 ;set loop pointer for countdown timer +DDED INC &029B,X ;increment byte and +DDF0 BNE &DDFA ;if not 0 then DDFA +DDF2 DEX ;else decrement pointer +DDF3 BNE &DDED ;and if not 0 do it again +DDF5 LDY #&05 ;process EVENT 5 interrupt timer +DDF7 JSR &E494 ; +DDFA LDA &02B1 ;get byte of inkey countdown timer +DDFD BNE &DE07 ;if not 0 then DE07 +DDFF LDA &02B2 ;else get next byte +DE02 BEQ &DE0A ;if 0 DE0A +DE04 DEC &02B2 ;decrement 2B2 +DE07 DEC &02B1 ;and 2B1 +DE0A BIT &02CE ;read bit 7 of envelope processing byte +DE0D BPL &DE1A ;if 0 then DE1A +DE0F INC &02CE ;else increment to 0 +DE12 CLI ;allow interrupts +DE13 JSR &EB47 ;and do routine sound processes +DE16 SEI ;bar interrupts +DE17 DEC &02CE ;DEC envelope processing byte back to 0 +DE1A BIT &02D7 ;read speech buffer busy flag +DE1D BMI &DE2B ;if set speech buffer is empty, skip routine +DE1F JSR &EE6D ;update speech system variables +DE22 EOR #&A0 ; +DE24 CMP #&60 ; +DE26 BCC &DE2B ;if result >=&60 DE2B +DE28 JSR &DD79 ;else more speech work +DE2B BIT &D9B7 ;set V and C +DE2E JSR &DCA2 ;check if ACIA needs attention +DE31 LDA &EC ;check if key has been pressed +DE33 ORA &ED ; +DE35 AND &0242 ;(this is 0 if keyboard is to be ignored, else + ;&FF) +DE38 BEQ &DE3E ;if 0 ignore keyboard +DE3A SEC ;else set carry +DE3B JSR &F065 ;and call keyboard +DE3E JSR &E19B ;check for data in use defined printer channel +DE41 BIT &FEC0 ;if ADC bit 6 is set ADC is not busy +DE44 BVS &DE4A ;so DE4A +DE46 RTS ;else return + +* SYSTEM INTERRUPT 4 ADC end of conversion * + +DE47 ROL ;put original bit 4 from FE4D into bit 7 of A +DE48 BPL &DE72 ;if not set DE72 +DE4A LDX &024C ;else get current ADC channel +DE4D BEQ &DE6C ;if 0 DE6C +DE4F LDA &FEC2 ;read low data byte +DE52 STA &02B5,X ;store it in &2B6,7,8 or 9 +DE55 LDA &FEC1 ;get high data byte +DE58 STA &02B9,X ;and store it in hi byte +DE5B STX &02BE ;store in Analogue system flag marking last channel +DE5E LDY #&03 ;handle event 3 conversion complete +DE60 JSR &E494 ; +DE63 DEX ;decrement X +DE64 BNE &DE69 ;if X=0 +DE66 LDX &024D ;get highest ADC channel present +DE69 JSR &DE8F ;and start new conversion +DE6C LDA #&10 ;reset interrupt 4 +DE6E STA &FE4D ; +DE71 RTS ;and return + +* SYSTEM INTERRUPT 0 Keyboard * + +DE72 ROL ;get original bit 0 in bit 7 position +DE73 ROL ; +DE74 ROL ; +DE75 ROL ; +DE76 BPL &DE7F ;if bit 7 clear not a keyboard interrupt +DE78 JSR &F065 ;else scan keyboard +DE7B LDA #&01 ;A=1 +DE7D BNE &DE6E ;and off to reset interrupt and exit +DE7F JMP &DCF3 ;and again a subroutine to exit. + +Now we come to the point you've all been waiting for. This mystery +RTSreturns all subroutines to &DE82. + +************** exit routine +DE82 PLA ;restore registers +DE83 TAY ; +DE84 PLA ; +DE85 TAX ; +DE86 PLA ; +DE87 STA &FC ;store A + +* IRQ2V default entry * + +DE89 LDA &FC ;get back original value of A +DE8B RTI ;and return to calling routine. + +NEXT WEEK: OSBYTE entry. + +BBC 6502 Machine Code +Part Seventeen: The BBC Operating System +---------------------------------------- +We've been examining the BBC operating system in some detail over the +last few weeks. Unfortunately the demise of Micronet means that we +cannot finish completely, as we hoped. So we've put together the next +twenty weeks' articles in the form of a completely commented +disassembly of OS 1.20. + +This is an excellent example of BBC programming and is full of tips. + +Just to remind you of the main points of the software. Entry points +are pointed to by a jump table in the last six bytes of the ROM. + +The font characters are located from &C000 to &C2FF. + +OK, so here it is all commented and ready for you to peruse. + +Ed says: I have uploaded the series of disassembly articles as ten + short TSW files. Look on Micronet on 700100239 (before it's + too late!) + + *********** THE END ********** + |