M/C Part 27 Essentially, vectors transfer control from Driver to the application and the jump table transfers control from the application back to Driver. Simple, yeah? Oh shut your face. There are about 50 entries in version 1.0, at three byte intervals: 0. JINTERRUPT: Call every frame interrupt. 1. JNMI: Call to reset colours and break back to basic. Also useful for breakpoints when debugging code. 2. JMENU.INIT: Call to initialise menu list with HL = address of list. Remember that all addresses must have D15 set to seperate them from the Driver code, although they get accessed in lower memory. See last month for more information on this. The menu list data consists of the addresses (D15 set) of the menus in order, using 0 to end the list. 9. JVECTOR.INIT: Enter with HL = address of vector list. The list consists of the addresses for the vectors in turn (see last month), with 0 indicating that the vector is not used. 10. JAPPL.INIT: Application initialisation. Enter with HL = application name, DE pointing to the page table and BC = variable table. The first is obvious (end the name with FFh) and gets used for the desktop window. The others are explained later on. 11. JGRAPHIC.INIT: Graphics initialisation. Includes the character set being copied from the Driver data page, the arrow pointer being turned on and the screen being printed. 12. JPRINT.SCREEN: Call to print all the windows in turn, starting with the application desktop. The pointer and cursor are turned off beforehand and then restored afterwards - sleeper mode is also turned off. 13. JPR.PARTSCREEN: Call to print all the windows in turn, starting with the "A"th one displayed. For example, entry with A = 0 has the same effect as JPRINT.SCREEN, A = 1 will start with the first one after the desktop and so on. Pointer and cursor dealt with as above. 24: JPRINT.WINDOW: Prints window A on the screen. Make sure the pointer and cursor are turned off. 27: JPRINT.SCROLLS: Reprints the scroll bars and move gadget for window A. Useful when something in a window changes and they need to be adjusted. (Of course, each of the three gadgets is only printed is the window has it.) 30: JOPEN.WINDOW: Open window number A at coords DE, with size BC. H holds the type and IX points to its name. (The name ends with FFh and is centred when the window is printed. Even if the window has no name gadget you must assign something!) Alternatively, enter with A = 0 and a window number will be allocated and returned in A. CY is set if there are no windows available. The screen display is not changed. 33. JCLOSE.WINDOW: Enter with A = window number to close, including 0 to close the applications. The relevant vector (Close window or close application) will be called by the routine. Screen not updated. 34. JWINDOW.ICON: Prints an icon in window A, trimming it at the right and bottom to fit in. Enter with HL = address of data, DE = relative coords within window, BC = real size, B'C' = size to use after trimming top/left. (Normally the same as BC). Just to confuse you, if HL is >=8000h the Driver data page is used, otherwise the application is used. If the icon is to be trimmed top/left, DE would be 0 and B'C' would hold the size AFTER trimming. Any further trimming at right/bottom is dealt with. 42. JWINDOW.TEXT: Print some text in window A. DE holds the relative coords, and IX holds the address of the text. As above, trimming is done automatically at right/bottom, but you must enter with C = number of characters to skip (normally 0). To trim at the left, therefore, enter with E = 0 and C = no characters to skip over. Also enter with A' = paper colour (0-3), B = pen colour. 45. JPOINTER.ON: Enter with A = page, HL = address of pointer data. If A = H = 0, the existing pointer is used. 48. JPOINTER.OFF: Pointer is removed from screen and turned off. The vector JSLEEPER.ON is also called. 51. JCURSOR.ON: Cursor turned on at absolute coords DE. If the cursor is already on, it is removed from the screen and repositioned. However, the mode is not changed (this might mean calling JSLEEPER.OFF to change back to WIMP mode.) 54. JCURSOR.OFF: Cursor removed from screen and turned off. 57. Reserved. 60. Reserved. 63. JSET.MODE: Set Driver mode A. 66. JSLEEPER.ON: Stores current mode and sets mode 4 (sleeper), de-activating all gadgets. 69. JSLEEPER.OFF: Resets previous mode stored by JSLEEPER.ON. 72. JFPAGES: Returns number of free 16k pages in A. If more than 255 are free, 255 is returned. 75. JRESERVE.PAGE: Allocate and reserve page for application. Entries are made in application page table (see later), and the number of pages reserved is increased. If no page is available, the routine returns CY. 78. JRELEASE.PAGE: Releases page at top of page table and decreases page total. 81. JPAGE: Enter with A = logical page number (0-127). Returns physical page numbers in A and B. This is the standard representation of a data page: A represents the HMPR value, and B the LEPR (External page in section C) value. The routine also returns C = lepr, so that on return your application does: OUT (hmpr),A OUT (C),B 84. JRUNCLI: Enter with HL = address of BASIC line data in application. The line is run through the ROM and returns with A = error number or 0 with CY set for an error. 87. JBASIC: Return to BASIC. 90. JDESKTOP: Return to Driver Desktop. 93. Reserved. 96. JGETKEY: Returns key from head of queue in A (ascii code). A = 0 if no key was pressed. 99. JFLUSH: Flush out keyboard queue. 102. JSAVEAS: Opens the "Save as..." dialogue box. Entry is with HL pointing to the file data, which is altered on exit to suit the file details selected by the user. On return, A = 1 (OK) or 0 (CANCEL). File data consists of 15 bytes: 0/1 Disk number 2 Drive 3 Sub directory number 4 File type (not relevant to "Save as") 5-14 File name, including trailing spaces. 105. JOPENAS: Opens the "Open a file" dialogue box. Entry and exit are as for JSAVEAS. (This is where the file type becomes important). For opening a file, I would suggest using a default file name with a wildcard, like "*.icn" which would list only those files. This is what Icon Master does. 108. JSCROLLUP: Scroll window A up either a bit (C=0) or a lot (C=1). These correspond to clicking either the up arrow or the space above the scroll bar indicator. The Scroll Up vector is called, and the window(s) redrawn. 111. JSCROLLDOWN: As above, but scrolls down. 114. JSCROLLLEFT: As above. 117. JSCROLLRIGHT: As above. 120. JCUT: Cut BC bytes from HL onto the clipboard. BC must range from 1-256 and HL must be in the application page. CY is returned if the clipboard is full. (It can grow as long as memory allows) 123. JPASTE: Paste BC bytes from the clipboard to HL in the application page. BC must be from 1-256. CY is returned if there is no data left, with BC holding the number of bytes left unpasted. 126. JEMPTYCLIP: Empty clipboard, releasing all its pages, and reset the JCUT pointer to the start. 129. JRESETPASTE: Reset JPASTE pointer to the start of the clipboard. 132. JKEYCONTROL: Enter with A = 1 if the keyboard is to be shared between keyboard control of the pointer (if the user has no mouse) and something else, like entering text. Otherwise A = 0. The user toggles between the two modes using [SYMBOL]-[EDIT]. 135. JSELECT.SAW: Select window number A. The affected windows are redrawn on the screen. 138. JBLOCKS: Use HL as the base address for the window gadget blocks. (See Set Variables) 141. JCOPYCHARS: Copy character set from the Driver Data page to a space above the screen. Driver keeps a copy there to speed up printing text (the pointer is also stored above the screen), but some DOS routines might corrupt it. 144. JFREE.PAGE: Returns the top free page available in AB, or CY if none free. No entries are made in any table. 147. JALLOCATE: Allocate page AB in ROM/DOS tables, using value in C. If C is zero, the page is freed. The application page table is not affected. HOW TO USE THE JUMP TABLE Using the jump table is simple. Just page Driver into section C and CALL the relevant entry. The numbers given above are offsets - the table is at 8400h in the Driver page, although I'll probably change this at the last minute. Anyway, one of the the set variables (see the section a few pages on) contains the jump table base addess. Note, though, that interrupts are disabled on exit. The first couple of entries deserve examples. You should be running interrupts in mode 1, and your handler is called at 0038h: maskable.int PUSH AF IN A,(status) RRA JP NC,line.int RRA RRA RRA JP NC,frame.int maskint.end POP AF EI RET . . frame.int IN A,(hmpr) PUSH AF LD A,(driver.page) OUT (hmpr),A CALL jint POP AF OUT (hmpr),A POP AF EI RET The non-maskable interrupt is called at 0066h: nmi LD SP,stack CALL driver_in JP jnmi This should make it clear that you can run additional routines off the frame interrupt, or a line interrupt. It is possible to run such an interrupt about 20 lines into the screen, and change both the mode and palette. You might want to change the graphics for both the window gadget blocks and the pointer to make them compatible with mode 4. Modes 1 and 2 should be avoided, though. Uses? Art applications, of course. MEMORY MANAGEMENT As I mentioned last month, Driver applications run in lower memory, paging data, graphics, the screen and Driver itself into sections C and D. This is to let you use external RAM which can only be paged there. The external RAM is paged using HMPR and another two ports - LEPR (128 dec) which controls section C, and HEPR (129 dec) for section D. HMPR has D7 set to access the extra memory. So, we can represent any page with two numbers - the HMPR value (0-31 for internal memory, 128 for external) in A and the LEPR value (0-255) in B. All the extra pages used by your application are held in a table somewhere inside it (PAGETAB). This is 257 bytes long: the first byte represents the number of data pages (0-128), and then the pages are listed using the above format (HMPR for page 0, LEPR for page 0; HMPR for page 1, LEPR for page 1 etc..) JPAGE will return the two values for a logical page number 0-127, although I found it easier to do it myself. You tell Driver the location of the table via JAPPL.INIT (see above). JRESERVE.PAGE will find a free page (using external memory if possible) and add it into PAGETAB, updating the number of pages. JRELEASE.PAGE does the opposite, freeing the top page. The problem with this (and about the only programming problem for applications), is that the application data has to be treated in 16k blocks, and the pages won't be concurrent. Not very easy to manipulate, especially for things like word processed documents where you need to keep shuffling memory about. I'll tell you how I got round this drawback with Notepad next month. (Incidentally, you can write documents on Notepad up to 2 MEG BIG!!! - A nice result after having to cope with such a nasty problem!) There are advantages, though: you don't have to worry about how much memory the machine has, and more than one application can reside and run at the same time. Then the user can cut and paste bits of data between documents without having to load and save. (More on clipboarding next month.) The one further drawback with the paging is loading and saving. Apart from having to do so using machine code, you can't just use the DOS's usual load and save routines. Instead, you read and write blocks of data at a time. Again, I'll detail this more next month. One final point: If your application is only going to use 2 data pages (for example, in an art program), you could alway use code like this to page them into either section C or D. Enter with HL = address (8000h-BFFFh for page 0, C000h-FFFFh for page 1): data_in PUSH AF PUSH BC PUSH HL LD HL,pagetab+1 LD C,(HL) ; Page 0 hmpr INC HL LD A,(HL) ; Page 0 lepr OUT (lepr),A INC HL LD B,(HL) ; Page 1 hmpr INC HL LD A,(HL) OUT (hepr),A ; Page 1 hepr POP HL LD A,B ; Use page 0 BIT 6,H JR Z,di1 LD A,C ; Use page 1 DEC A ; Take 1 off for hmpr XOR C AND 31 ; Merge D7, in case of XMEM XOR C di1 OUT (hmpr),A ; Page it POP BC POP AF RET You could modify this to use four pages, with all the memory addressed by a single 16-bit word. If the application needs to use more than that, you'll find yourself having to address the memory using 3 bytes. Nasty! ** IMPORTANT ** I must stress, once again, that you have to SET BIT 15 on any address that you give Driver to do with your application. The ONLY exception to this is when dealing with icons. Despite this, your application always runs in lower memory. DRIVER VARIABLES To keep your application notified, certain Driver variables are copied into a table during every Driver Interrupt. You define the table with JAPPL.INIT: 0. Pointer x coordinate (absolute 0-255) 1. Pointer y coordinate (0-191, 0 is at the top) 2. Mode (0-4) 3. Shift flag. 1 if [SHIFT] is being held, else 0. 4. Mouse button (D0 set when left held, D1 = 0, D2 set when right held.) 5. Pointer flag. 6. Cursor flag. 7. Cursor x coordinate (as for pointer). 8. Cursor y coordinate (as for pointer). 9. Number of windows open, excluding desktop. (0-255) 10. Active SAW number. (1-255, or 0 if none selected) 11. Number of pages reserved by clipboard (0 if none). After these twelve bytes, leave space for another 10 or so, to ensure compatibility with future versions. SET ADDRESSES Inside your application, there are several addresses that must be set aside for specific purposes. 0000 Jumped to when the application is opened for the second time (or later) 0003 Jumped to when the application is opened for the first time only. 0006 Called when Driver is being closed. You must return the address of PAGETAB (with D15 set, of course) in DE. 0009 Reserved for a future "Kickstart" program. 00F0 Name of the application (15 characters max), ending with FFh. This is centred by the Driver Desktop under the application's icon. 0100 Icon. 16x24 fat pixels in the usual format (192 bytes long). Used by both the Driver Desktop and File Manager. In the first four jumps, the stack is in upper memory, so you need to provide your own. After this, Driver keeps track of the application stack so that every time a vector transfers control from Driver to the application the stack is correct. One "problem" is the "Driver Closing" jump. Only the currently open application's stack and page table are monitored, so when Driver is closed (and all the applications with it), the stack is in section C and you must return the PAGETAB in DE. You would probably also want to run a dialogue box to save any changes to a file, and your own stack would be needed: 0006 JP driver_closing . . . driver_closing POP HL ; Return address LD SP,stack PUSH HL CALL close_resave CALL driver_in LD DE,pagetab+8000h RET As you can see, Driver will revert to its own stack once you return. "driver_in" simply pages Driver into upper memory. An application vector, "close application", is called when the application itself is closed and the fiddling with the stack isn't necessay there. Incidentally, Driver's page is stored in System Variable 5C97h, and the Driver Data page is at 5C98h. You might like to copy these at the start. (The idea of the Kickstart thing is that you can put applications in a folder and they will be installed when Driver is loaded.) FIXED VARIABLES As I mentioned earlier, at the start of the Driver code there are variables at fixed addresses: 0010 PRTOKV.STORE 0012 CMDV.STORE 0014 MTOKV.STORE Stores for ROM vectors to do with the keyword. 0016 KEYWORD.FLAG 0 if keyword installed, else 1. 0017 DVAR.OFFSET Offset of DVAR 0 within the DOS. 0019 Version number of Driver, times 10. 001A Settings Offset of preference settings within Driver code. 001C ATAB.ADDRESS Offset of application table within Driver code. 001E EDITOR.FLAG Normally FFh. Use 1 when writing an application. 001F BLOCKS Address of window gadget blocks. To customise the blocks (for example, when using a different screen mode) change this with D15 set. 0021 Reserved 0023 Clock vector See below. 0026 MTask vector See below. 0029 SEL.APPL Address of selected application within application table. 002B MAX.APPLS Maximum number of applications (normally 12). 002C JTAB Jump table address. MULTI-TASKING Now, although when I discussed multi-tasking a few months back I mentioned that I'd discounted the idea for Driver, I had a slight change of heart. Although applications are mutually independant when it comes to windows and menus, you can have them running "invisibly" in the background. This is acheived using the MTask vector mentioned above - this isn't a normal application vector at all. Instead, you specify a page and an address, and after the Driver interrupt has done graphical stuff the vector is called in UPPER MEMORY. And, like the "Driver closing" vector, the stack is left in Driver. To let more than one application use the vector, another vector is stored three bytes after the vectored address, and is run after the first one. Three bytes after this, the third vector is stored and then run. Confused? Good. In practice, a "chain" of vectors looks something like this: Driver page, 0026h 18h 0027h 8010h ; Page and address of first vector. Page 18h, 8010h JP mtask 8013h 16h 8014h 8123h ; Page and address of second vector. Page 16h, 8123h JP mtask 8126h 15h 8127h 8200h ; Page and address of third vector. Page 15h, 8200h JP mtask 8203 00h 8204 0000h ; No more vectors. Of course, is there aren't any vectors, MTask vector will be 0,0,0. To set a vector, the following routine (run in lower memory), will be useful: Entry: IX = vector address with D15 set (eg. 8026h for Mtask vector) HL = address of routine with D15 set. (Eg. 8010h) set.vector CALL driver_in PUSH HL ; Copy HL into IY POP IY LD A,(IX+0) ; Store the existing vector LD (IY+0),A LD A,(IX+1) LD (IY+1),A LD A,(IX+2) LD (IY+2),A IN A,(lmpr) ; Store the new vector AND 31 LD (IX+0),A LD (IX+1),L LD (IX+2),H RET And, at 0010h: 0010h JP mtask 0013h DB 0,0,0 When you want to disconnect the vector, we have to search through the chain to find it. Then we take the vector from 0013h (or wherever you've put it) and copy it over. Essentially, we've taken the link out of the chain and connected the two loose ends together. Entry as before. remove.vector CALL driver_in ; Driver into upper memory. rv1 IN A,(lmpr) AND 31 CP (IX+0) ; Check the vector. JR Z,rv.found LD E,(IX+1) ; Get the address of the wrong one. LD D,(IX+2) INC DE INC DE INC DE OUT (hmpr),A ; Page it in. PUSH DE POP IX ; Copy it into IX. JR rv1 ; Loop until we find it. rv.found INC HL INC HL INC HL LD A,(HL) ; Take the link out of the chain. LD (IX+0),A INC HL LD A,(HL) LD (IX+1),A INC HL LD A,(HL) LD (IX+2),A RET You might notice that in both routines, the entry conditions include the vector address. This lets the routines also work on ANOTHER vector (gee, I'm too good to you guys). I mentioned that the MTask vectors are called after the Driver Interrupt has done its graphical stuff. This "graphical stuff" has to done at the start of the interrupt, during the frame flyback to avoid shear. Essentially, the routine looks like this: . . Remove pointer from screen Remove cursor from screen Call clock vector Call application graphic vector Add cursor Add pointer . . The application graphic vector (number 17), which I omitted from last month, is called in WIMP and sleeper modes only, with no entry or return conditions. The clock vector is similar to the MTask vector and can be found at 0023h. The reasons that I seperated the two are obvious: Firstly, if the screen display is going to be changed, the pointer and cursor must be removed first. Secondly, we want to minimise the time taken by the graphical vectors, or the pointer (and cursor) will be added back on after the screen scan has passed them, making them partly or completely invisible! I called it the Clock vector because, originally, this was the only use I saw for it; running a clock at the top left of the screen. In fact, such clock application will (hopefully!) be released in one form or another shortly after Driver comes out. To minimise the time the vector takes, I would suggest doing as much processing as possible in an accompanying MTask routine. The aforementioned clock application uses the MTask vector to "print" the time in a buffer space, which is copied to the screen by the next clock vector. Of course, dumping one rectangular graphic is faster than printing 5 smaller ones.