M/C Part 6 ROM routines part 2 ===================== In issue 10, I explained how to use some of the routines hidden away inside your coupe's ROM by calling addresses within a jump table. This is okay if you only want to use it as an extension to your own programs. However, the reverse can also be true when the ROM runs by itself and you wish to extend it using your routines. This is done by altering values in the system variables (page 0) to the start addresses of your routines. Below is a list of some of these VECTORS, together with uses and any special entry or exit requirements: * please note - the addresses of the vectors are for RAM 0 in the page after ROM 0. Also, you will only intercept the ROM if the most significant byte of your new value is non-zero. Another problem you might encounter in the future with add-ons is incompatibility due to a use of a vector in one of your proggies. To counter this you should add an initialisation routine which will not only change the vector to your new value, but will also store the existing value. The Vectors:- ------------- NMIV Non-maskable interrupt. This is the address called when 5AE0 the BREAK button is pressed, and normally points to an error handler. The speccy emulators change this value to use it as a snapshot button. On entry, AF and HL are pushed onto the existing stack, SVARs are paged in and the original LMPR status is held in NMILSP (5AD9). A temporary stack is provided and the original SP value is stored in NMISP (5AD7). FRAMIV Called by the frame interrupt every 50th of a second, just after the screen has been transferred to your TV, and just at the start of the scan flyback. Oh, and all the main registers can be used without any problems. LINIV Hmmm. Must admit, I couldn't get this one to work, but 5AE4 I'll tell you what's supposed to happen. When the value in the line interrupt port (249) matches the scan (ie. Y coordinate) just about to occur, this address is called. You can use all the main registers. If you output any value greater than 191 to the port, the interrupt is disabled. MIPV After the MIDI input interrupt, this is called with A 5AE8 having been read from the MIDI IN port. All main registers etc. MOPV Called by MIDI output interrupt. (More on interrupts in 5AEA a future article) RST28V This called by the floating point calculator with A=next 5A0F code to execute (I gave you some of the codes last month and some are listed below). DE points to the end of the FPC stack on entry, and on exit IX should remain unchanged, with DE pointing to the end of the stack. (You might have changed this if entries have been added or discarded) RST30V This vector can be used as a user RST for anything you 5AF2 want. Note, though, that, unlike other vectors, your routine is jumped to - not called. This means that at the RET in your vector routine, the program counter jumps back to where your RST30 was. CMDV Called with A=code of character about to be executed or 5AF4 syntax checked. You can use this to add new commands or provide some other action(s) for existing tokens. EVALUV Called with A=current character in expression to 5AF6 evaluate. You can use this to add new functions. MTOKV Called if the ROM does not recognise a potential 5AFA spelled-out keyword. On entry, DE points to the word. On exit, the zero flag should indicate whether or not the keyword is valid (set=invalid). A=the token value. HL points to the start of the word, DE points just past its end. KURV Called before the BASIC editor's cursor is printed. ROM1 5AFE is paged in. You could use this to supply a different type of cursor (eg. flashing/transparent). If you want to do this, you should junk the return address, before returning. OK? Now, here's an example of a use of a vector: Suppose you had some code, or a BASIC program that wanted numbers rounded off to the nearest integer, not just with the fractional part chopped off, as happens with INT. Well, you could just add a half and use INT, but the following code will change INT to do this automatically. A couple of points - the code is located in the "System Heap", an area of about 2.7k in page 0 which you can use when you only know that RAM0 will be paged in, as in this case. Also, to enable the new function call INIT (#4000). ORG #4000 INIT LD HL,ROUND_OFF LD (RST28V),HL RET ; ROUND_OFF CP #44 ; Is the command INT? RET NZ ; If not, return POP BC ; Junk the return address LD (IXSTORE),IX ; IX must exit unchanged CALL JSTKFETCH ; Get the number to round LD HL,(FPSBOT) LD (FPSBOT_STR),HL ; This bit just makes sure we LD HL,(STKEND) ; use a different stack in case LD (STKEND_STR),HL ; we corrupt it LD HL,TEMP_FPCS LD (FPSBOT),HL LD (STKEND),HL CALL JSTKSTORE ; Put the number onto our new ; stack RST #28 ; Call the FPC DB STKHALF DB ADDN ; Add a half and chop fractions DB TRUNC DB EXIT CALL JSTKFETCH ; Get the rounded value LD HL,(FPSBOT_STR) LD (FPSBOT),HL ; Restore the original stack LD HL,(STKEND_STR) LD (STKEND),HL CALL JSTKSTORE ; Stack on our new value EX DE,HL ; Set DE to end of stack LD IX,(IXSTORE) ; Restore IX RET IXSTORE DW 0 FPSBOT_STR DW 0 STKEND_STR DW 0 TEMP_FPCS DW 0,0,0,0,0,0,0,0,0 ; RST28V EQU #5AF0 JSTKFETCH EQU #0124 ; ROM Jump table to routines JSTKSTORE EQU #0127 ; FPSBOT EQU #5BCC ; System variables STKEND EQU #5C65 ; ; STKHALF EQU #E0 ; FPCS command codes ADDN EQU #01 ; (see last issue) TRUNC EQU #30 ; EXIT EQU #33 ;