Intuition : Part2 ================= Preamble ======== Wow! a 1170 line tutorial, must be a record! Last month I covered Intuition windows, this month I will explain how to get text and graphics data into a window. I will also explain how to load in a disc based font. Disc based fonts require functions from libraries not yet covered in this series of tutorials, do not worry! The functions are explained. After this tutorial you should be able to open a window and set up a visually pleasing display in it. Next month I will move on to user interaction ...... Gadgets. To start with let me cover a skeleton routine that will form the basis of all future examples in this series. The Start.s Program =================== From experience, I have found that the majority of Intuition programs all revolve around a similar skeleton routine that can be detailed as follows: 1/ Open all libraries 2/ Initialise any required data 3/ Open a window 4/ Wait for an Intuition Message 5/ React to the message 6/ If message was not quit, go back to step 3/ 7/ Close window 8/ Free any resources ( from Initialise routine ) 9/ Close all libraries 10/ Finish I have supplied just such a skeleton routine for you to work with. You will find that this skeleton routine will be a suitable starting point for most Intuition programs you write. I use a variation of it for all the programs I write these days, making final adjustments as required. To use this program is simplicity itself. You write all the support code in a seperate file and just include this into the skeleton routine when you wish to assemble it. How is this possible? Well the technique I am going to show you was picked up from Steve Marshall. All gadgets, menus etc. are handled by jumping to subroutines. The address of the subroutine to call will be supplied ( sometimes indirectly ) by the Intuition Message. The code used to extract the address of the appropriate subroutine is already in the skeleton program. If the window you open does not contain any gadgets or menus, this code will not be executed so you need not worry about it ( it adds very little overhead to the Message loop ). If you do add a file to this skeleton code that contains gadgets, menus and the subroutines that support them, they will be dealt with appropriately. The only parts of the skeleton code you will need to alter are: The Initialise routine The Message Loop ( possibly, at a later date ) The Free resources routine The window data structure ( to display the window you require ) The Usage text Programs written using this skeleton routine can be run from the WorkBench providing you give them an icon. As an extra bonus, the programs can also be run from the CLI, if the standard ? follows the program name then usage text will be displayed in the CLI window. This little feature was outlined in a previous tutorial. When the window is opened the program saves some useful pointers. These are detailed below: window.ptr This holds the address of the Window structure. This address is required for most Intuition functions. window.up This holds the address of the windows UserPort structure. window.rp This holds the address of the windows RastPort structure. The skeleton code also comes equiped with a number of very useful subroutines ( which you can delete if you do not require them ). The label and calling details are outlined below: DosMsg Entry: A0 must hold the address of a 0 terminated text string Exit: All registers preserved Function: Will display the text in the CLI ( or Console ) whos handle is stored at label STD_OUT. The initialisation routines will store the handle of the CLI at this label if the program was run from one. If this label contains 0, then no text is displayed. You may open a console window and store it's handle at this address if you wish. DosMsg will then display the text in this console. Notes: Do not remove this subroutine. It is used to display the usage text should the user require it ( from CLI only ). FileLen Entry: A0 must hold the address of a filename, 0 terminated. Exit: D0 will hold the length of the file ( in bytes ) or 0 if the file could not be found. A0 corrupted. All other registers preserved. Function: This routine will determine the length of a specified file. Useful for allocating memory if you need to read the file in. Notes: Make sure you supply the FULL pathname of the file. eg. 'df1:docs/readme',0 This routine may be removed. PointerOn Entry: None required. Exit: All registers preserved. Function: Will display a custom 'sleepy' pointer to show the user that the program is busy. Notes: Calling this routine while before your program does some lengthy task, such as read in a file, makes the program more user friendly. Restore the normal Intuition pointer by calling PointerOff This routine may be removed. PointerOff Entry: None required. Exit: All registers preserved. Function: Restores the standard Intuition pointer. Notes: Call this routine after your program has finished a lengthy task before which a call to PointerOn was made. This routine may be removed. Find Entry: a0 must hold address of string to search for d0 must hold length of this string a1 must hold address to start searching at d1 must hold maximum number of bytes to search Exit: d0 will hold address of first occurence of string or 0 if no match was found. All other registers preserved. Function: This is a case sensative search routine. Notes: This routine may be removed. Ucase Entry: a0 must hold the address of a 0 terminated text string. Exit: a0 holds the address of the 0 byte following string. Function: Converts all lower case letters in the string to upper case. Non alphabetic characters are ignored. Notes: This routine may be removed. Local labels have been used where possible to remove restrictions for you. NOTE: ALL GADGET AND MENU SERVICE SUBROUTINES SHOULD SET REGISTER D2 TO ZERO BEFORE RETURNING TO THE MESSAGE LOOP: moveq.l #0,d2 rts IF D2 IS SET TO CLOSEWINDOW, THE PROGRAM WILL FINISH: move.l #CLOSEWINDOW,d2 rts IF D2 IS SET TO SOME OTHER VALUE THE RESULT IS UNPREDICTABLE. THIS WARNING WILL BE REPEATED IN FUTURE TUTORIALS AS APPROPRIATE. The above warning may not make much sense at present, but all will be revealed in future tutorials! As you can see, this skeleton code is the ideal starting point for a whole variety of programs. You will become very familiar with it after working through this and future tutorials. You may use this code in your own programs if you so wish. The skeleton routine can be assembled and run as it stands. It will open a blank window and wait for the close gadget to be hit. I suggest you assemble and run it now, just to be sure. The source for the skeleton routine is heavily commented. There is not much in it that we have not already covered in earlier tutorials. Take a good look at the source and make sure you understand what each part does. Do not worry to much about the subroutines, they are there should you need them. I will call on them in later tutorials when we will write a couple of utils. Lets move on to printing text, we will get to using the skeleton code very soon! Text ==== You are not going to believe what is involved getting your name into a window! To print text in a window the Intuition function 'PrintIText' is used. This function requires four parameters to be initialised prior to being called: PrintIText ( RastPort, IText, Left, Top ) A0 A1 D0 D1 RastPort -------- Register A0 should contain the address of the RastPort attached to this window. You should recall that the address of the RastPort structure can be obtained from the Window structure. A suitable fragment of code to open a window and obtain this address would be: CALLINT OpenWindow open the window move.l d0,window.ptr save Window addr beq error quit if failure move.l d0,a0 a0-> Window move.l wd_RPort(a0),window.rp save Rastport addr As with the address of the UserPort, I always copy the address of the RastPort into window.rp for consistency. You need not worry about what information a RastPort structure contains at this stage. IText ----- Register A1 should contain the address of the IText structure that describes the text you wish to print. Lets take a look at this structure: Field Size Comment it_FrontPen BYTE Colour register (0 to 31) for foreground it_BackPen BYTE Colour register (0 to 31) for background it_DrawMode BYTE Drawing mode:JAM1, JAM2, COMPLEMENT or INVERSE it_KludgeFill00 BYTE 0 byte to allign following word data it_LeftEdge WORD Number of pixels from left border to start printing it_TopEdge WORD Number of pixels from top border to start printing it_ITextFont LONG Address of structure defining font to use or 0 it_IText LONG Address of the 0 terminated text to print it_NextText LONG Address of the next IText structure to print Looks complicated doesn't it. It is infact quite simple to set up once you understand what each field requires. Lets look at the individual fields: it_FrontPen This byte sets the background colour to use. It should contain the colour register number that you require. Colour register 0 is always transparent. If you are working on the WorkBench screen, you can select from register 0,1,2 or 3. it_BackPen This byte sets the colour the text will actually be printed in. Again only 0,1,2 or 3 are available on the WorkBench screen. it_DrawMode This byte describes how Intuition should render the text on the screen. Four draw modes are supported: JAM1 : FrontPen is used to draw the text, but the background is left unchanged. JAM2 : FrontPen is used to draw the text, the background is changed to the colour specified in BackPen. COMPLEMENT : The text is drawn in the complement of the background colours. INVERSVID : The text is drawn in background colour, the background is filled with FrontPen. it_KludgeFill00 This byte just pads out the structure, keeping the word data that follows evenly alligned. it_LeftEdge This specifies the starting position of the text from the left border of the container. it_TopEdge This specifies the starting position of the text from the top border of the container. it_ITextFont A pointer to a Font structure. Set this field to 0 if you wish to use the default font ( usualy Topaz 80 ). it_IText The address of the text to print. Note the text MUST be 0 terminated or strange things will happen. it_NextText The address of another IText structure defining more text to be displayed. This field can be used to link a number of texts together all of which will be printed by just one call to PrintIText (). Lets look at an example structure ( which will shortly be used ) that defines a line of text to be printed: MyName dc.b 0 Transparent background dc.b 1 White foreground dc.b RP_JAM2 draw mode dc.b 0 KludgeFill00 dc.w 5 5 pixels from left dc.w 10 10 pixels down dc.l 0 use default font dc.l MyNameText address of text dc.l 0 no more text MyNameText dc.b 'Mark Meany.',0 0 terminated text string Left ---- In register D0 you should set the offset from the left window border that text should start. You may be thinking that you have already specified this in the IText structure, in a way you have. The it_LeftEdge field lets you specify how many pixels from the left border to start printing. Left has the effect of moving the border to some position inside the window. This could be used to format text output into colums, such as a news paper. Left would be set with the column offset, it_LeftEdge would hold the number of pixels from the left of any particular column. Top --- In register D1 you should set the offset from the top window border. Again it_TopEdge is taken as an offset from this positiuon. If you are only using PrintIText to display odd lines of text, it is a good idea to set the it_LeftEdge and it_TopEdge fields to zero. The text position in the window can then be alterd by setting Left and Top ( D0 and D1 ). IText structures are quite important as they are used to build up the text used in menus and gadgets. For this reason I urge you to play around with the following examples, changing the pen colours and draw mode so you have first hand experience of what is available. Time for an example and the first delve into Start.s Example1: Start.s contains an IText structure ( gosh! I've thought of everything ). When the window is first opened, the text described by this structure is printed into the window. Initally, the text printed consists of just one space, so you do not actually see anything. Line 132 is the start of the code that prints the text, this section is shown below: ;-------------- Display basic usage text for user move.l window.rp,a0 a0->windows RastPort lea WinText,a1 a1->IText structure moveq.l #10,d0 X offset moveq.l #15,d1 Y offset CALLINT PrintIText print this text Line 453 contains the actual IText structure. As I said, this defines only a single space. Notice that the position of the text is set in the above code and not in the structure itself. This is because the structure will be re-used later, so it seemed a good idea to adopt this technique. WinText dc.b 1 FrontPen dc.b 0 BackPen dc.b RP_JAM2 DrawMode dc.b 0 KludgeFill00 dc.w 0 x position dc.w 0 y position dc.l 0 font dc.l .Text address of text to print dc.l 0 no more text .Text dc.b ' ',0 the text itself even Load the start.s program into Devpac and alter the text to be printed, for example: .Text dc.b 'Mark Meany, June 1991.',0 Now assemble to Memory and then Run the program. When the window opens you will see the text printed 10 pixels across and 15 pixels down from the top left corner of the window. Example 2: Now lets print two lines of text using only th one call to PrintIText. To do this we must link a second IText structure to the first. Hopefully, you still have the last example still loaded into Devpac, if not reload it. Mark the IText structure as a block. This can be accomplished by moving the cursor to the start of Line 452 and pressing F1 ( function key 1 ), next move the cursor to the start of Line 465 and press F2. The IText structure should appear in orange ( if you are using standard colours ). Now duplicate the text by pressing F4 with the cursor still positioned at the start of Line 465. Press F1 and then F2 to remove the horrible orange highlight. You should now have two identical IText structures, one after the other. Change the label of the second structure from WinText to WinText2. Change the label of the second text string from .Text to .Text2, note you must also alter the reference to this text in the IText structure. The second IText structure should now look like the one below: WinText2 dc.b 1 FrontPen dc.b 0 BackPen dc.b RP_JAM2 DrawMode dc.b 0 KludgeFill00 dc.w 0 x position dc.w 0 y position dc.l 0 font dc.l .Text2 address of text to print dc.l 0 no more text .Text2 dc.b 'Mark Meany, June 1991',0 the text itself even Change the FrontPen colour to 0 and the BackPen colour to 2. Now change the y position to 10 and alter the text to some other message. All that now remains is linking the two IText structures together, this is done by entering the label of the second structure into the last long word of the first. When you have done this, your data should look like that shown below: WinText dc.b 1 FrontPen dc.b 0 BackPen dc.b RP_JAM2 DrawMode dc.b 0 KludgeFill00 dc.w 0 x position dc.w 0 y position dc.l 0 font dc.l .Text address of text to print dc.l WinText2 no more text .Text dc.b 'Mark Meany',0 the text itself even WinText2 dc.b 0 FrontPen dc.b 2 BackPen dc.b RP_JAM2 DrawMode dc.b 0 KludgeFill00 dc.w 0 x position dc.w 10 y position dc.l 0 font dc.l .Text2 address of text to print dc.l 0 no more text .Text2 dc.b 'Two for One Eg.',0 the text itself even Assemble the program to memory and then Run it. Get the idea? The first line of text is printed as before, the second line is printed below it in other colours. You could link even more IText structures to the ones already done if you wish. Time for you to experiment with different combinations of pen colours and draw modes. To get a feel for draw modes, get the second string to be partially printed over the first string and try different draw modes out on the second string. Disc 11 contained a tutorial I wrote covering a function in the Exec library called RawDoFmt(). If you never read this I suggest you do so now, this function is essential for text formatting. It will even convert numbers from word or long word values into text characters for printing. For those of you who do not have disc 11 I have included the Includes and Autodocs defenition of RawDoFmt() at the end of this file ( well Dave Edwards version of it! ). Aw, to hell with it! I've included the rdf.doc file on this disc as well, you will have to 'cut' the example program out of the file to assemble it! Lets edit start.s so that when the window opens, it displays the amount of free memory. To generate the string we wish to print RawDoFmt() will be used. To determine the amount of free memory, we will use a AvailMem from the exec library ( this function was covered in an earlier tutorial ). First off we must alter Line 460 to read as follows: OurText dc.l Text address of text to print next we need to alter line 463 to read as follows: Text ds.b 100 buffer for text itself This sets up a buffer that RawDoFmt() will build the text string in, the local label, '.Text', was replaced with a global label,'Text', so it may be referenced from anywhere in the program. Now move to Line 465 and the following lines: Template dc.b 'Free memory : %dl',0 even DataStream dc.l 0 These lines set up other required information for RawDoFmt(). Now move to line 113, which reads: .ok moveq.l #1,d0 no errors We will add the code that determines how much free memory is available and calls RawDoFmt() here. Enter the following lines: .ok move.l #MEMF_CHIP,d1 type of mem CALLEXEC AvailMem how much is free? move.l d0,d7 store free chip move.l #MEMF_FAST,d1 type of mem CALLEXEC AvailMem how much is free? add.l d7,d0 add free chip to this move.l d0,DataStream and store value lea Template,a0 format string lea DataStream,a1 data lea PutChar,a2 subroutine lea Text,a3 buffer CALLEXEC RawDoFmt create text string moveq.l #1,d0 no errors .error rts PutChar move.b d0,(a3)+ save next character rts and return That's it. An explanation then. Two calls are made to AvailMem to determine the amount of CHIP and FAST memory available to the system. The two values returned are added together and the result stored in memory. RawDoFmt() takes the result and the template string and builds the required text in the text buffer. The text will be displayed as normal in the Openwin subroutine. This example is saved on the disc as is called 'Int_EG1.s'. As an exersise, try creating a chain of 3 IText structures and modifying the above example so that it will print the amount of CHIP, FAST and total memory available to the system. I have included a version that does just that, 'Int_EG2.s', but you should try to do this yourself. No explanation accompanies 'Int_EG2.s', you will have to follow the source. I will just add that I changed the usage text for this example as well. Borders ======= A border is just a line! The line is draw from a set of points defined in a structure, which we will look at shortly. You can use a border to highlight an area of the display or if you are really dedicated to produce a picture. A border is renderd into a window using the following function: DrawBorder ( RastPort, Border, LeftOffset, TopOffset ) A0 A1 D0 D1 RastPort -------- Yep! the address of the rastport structure attached to the window. This value is stored in 'window.rp' by start.s Border ------ The Border structure that defines the line to be drawn: bd_LeftEdge WORD offset from left edge of container bd_TopEdge WORD offset from top edge of container bd_FrontPen BYTE colour of foreground pen bd_BackPen BYTE colour of background pen bd_DrawMode BYTE drawing mode to use bd_Count BYTE the number of coordinates defining the line bd_XY LONG address of coordinates bd_NextBorder LONG address of next border structure As you would expect LeftEdge, TopEdge, FronPen, BackPen and DrawMode are all set in the same way as in an IText structure. I will not repeat that lot again, refer to the IText defenition given above! As you have probably guessed from the bd_NextBorder entry in the structure, borders can be linked together. This gives you quite a powerfull line drawing facility. The bd_Count field holds the number of coordinate pairs that define your line and bd_XY the address of these coordinates. The origin that all coordinates refer to is the top left corner of the container the border is being drawn in ( borders can be drawn in windows, gadgets and other Intuition objects ). LeftEdge and TopEdge move the origin to the position (LeftEdge,TopEdge) and all coordinates will then be relative to this new orogin. Here is an attempt at an illustration detailing this: __________________________________________________________ <-- container | | | . (LeftEdge,TopEdge) | | .(5,3) | |\ | | \ | | \ | | \ | | \ | (5,50) |_____\(50,50) | | The coordinates all refer to the orogin (LeftEdge,TopEdge). Here is an example structure detailing this border: MyBorder dc.w 5 LeftEdge dc.w 5 TopEdge dc.b 1 FrontPen dc.b 0 BackPen dc.b RP_JAM1 DrawMode dc.b 4 Count ( 3 pairs of coordinates ) dc.l .Vectors address of coordinates dc.l 0 no further borders .Vectors dc.w 5,3 dc.w 50,50 dc.w 5,50 dc.w 5,3 Notice that 4 coordinates are required to draw three lines. LeftOffset ---------- Another means of shifting the origin. The value passed in d0 specifies an offset in pixels from the left of the window that the LeftEdge of the container is to be. TopOffset --------- As for LeftOffset, but the number of pixels from the top border of the window. So calling DrawBorder() with the same structure, but different LeftOffset and TopOffset values will cause the same object to be drawn at different positions on the screen. An example or two then. First we will display the border detailed above. To do this we need to add a few lines to start.s, so load start.s again. Move the cursor to Line 139 and enter the following lines of code, this will display the border after the text has been printed in the Openwin subroutine: move.l window.rp,a0 a0->windows RastPort lea MyBorder,a1 a1->Border Sructure moveq.l #10,d0 X offset moveq.l #25,d1 Y offset CALLINT DrawBorder Draw this border Now we need to enter the border structure, so move to line 473 and enter the following data: MyBorder dc.w 5 LeftEdge dc.w 5 TopEdge dc.b 1 FrontPen dc.b 0 BackPen dc.b RP_JAM1 DrawMode dc.b 4 Count ( 3 pairs of coordinates ) dc.l .Vectors address of coordinates dc.l 0 no further borders .Vectors dc.w 5,3 dc.w 50,50 dc.w 5,50 dc.w 5,3 Assemble this to Memory and run it. Fairly straight forward realy isn't it. On the disc you will find an example very similar to this, but with more points in the border, this is 'Int_EG3.s'. Take a look at it! Images ====== Borders are ok, they allow areas to be framed and can be used to draw line diagrams. If you want to put a proper picture on the screen, you will need to use an Image. An image is basicaly a picture drawn in two bitplanes ( this means you are restricted to only 4 colours ). It's time to dig out disc 12b ( the supplement disc that came with issue 12 ). In the Utils drawer you will find a program called 'BrushCon'. This wonderfull little utility will convert an iff brush into source code. As well as this it also adds an image structure on the beginning ( well you knew it was coming! ). It is possible to create images manually by typing in binary data, but you'd have to be a real header to do this! To display an image in a window you use the following Intuition function: DrawImage ( RastPort, Image, LeftOffset, TopOffset ) A0 A1 D0 D1 Look familiar? The only difference between this function and DrawBorder() is the structure supplied. So I will skip all the boring details on RastPort, LeftOffset and TopOffset. Lets look at the Image structure: ig_LeftEdge WORD offset from left border of container ig_TopEdge WORD offset from top border of container ig_Width WORD width of image in pixels ig_Height WORD height of image in pixels ig_Depth WORD Number of bitplanes ( 0,1 or 2) in image ig_ImageData LONG address of the image data ig_PlanePick BYTE which planes of container to render into ig_PlaneOnOff BYTE what to do with unused planes LeftEdge -------- The number of pixels offset from the left border of the container. TopEdge ------- The number of pixels offset from the top border of the container. Width ----- The width of the image in pixels. Note this is required as the data is stored as WORD value, so an image 23 pixels wide will require 2 words per vertical line. The extra 9 bits in the second word should be set to 0. Height ------ The height of the image in pixels. Depth ----- The number of bitplanes to the image ImageData --------- The address of the data that will build the picture. PlanePick --------- Bitwise representation of which bitplanes to render the image into. For example, if you have a 2 bitplane image and you are going to render it into a 4 bitplane display, you can specify which of the 4 planes actually get used: %0110 use planes 2 and 1 %1010 use planes 3 and 1 %1001 use planes 3 and 0 If the display were 5 bitplanes deep: %00110 use planes 2 and 1 %01010 use planes 3 and 1 %01001 use planes 3 and 0 %10010 use planes 4 and 1 The planes the image is rendered into effect it colour. If you are using the WorkBench screen then you only have 2 bitplanes to display the image in, hence the image will always appear in all of the Workbench default colours. PlaneOnOff ---------- If your image has less bitplanes than the display then you can decide what to do with planes the image will NOT be printed in. If you want to all the pixels in an unused plane to be set ( 1's ) set the corresponding bit in PlaneOnOff, if you want the bits cleared ( 0's ) clear the corresponding bit in PlaneOnOff. The image data that is to be displayed MUST BE IN CHIP MEMORY. The image structure does not. Here is a simple smiley face example ( ACID! ): MyImage dc.w 0 LeftEdge dc.w 0 TopEdge dc.w 16 Width ( 16 pixels ) dc.w 9 Height ( 9 pixels ) dc.w 1 Depth ( only 1 bitplane ) dc.l ImDat Image Data dc.b %01 PlanePick ( display in plane 0 ) dc.b %00 PlaneOnOff ( reset all points in plane 1 ) section smiley,data_c ImDat dc.w %0000000110000000 dc.w %0000011001100000 dc.w %0001100000011000 dc.w %0010001001000100 dc.w %0010000000000100 dc.w %0010001111000100 dc.w %0001100000011000 dc.w %0000011001100000 dc.w %0000000110000000 Ok, I admit I'm no artist. Can you see the shape of the image I have designed? You may be wondering what the directive 'section smiley,data_c' does. Well the section directive is used to force either code or data into a specific memory area. The format is as follows: section , a name that you assign to a section for the time being I will only introduce 4 of the available types: data_f data into FAST mem data_c data into CHIP mem code_f code into FAST mem code_c code into CHIP mem Everything following a section directive is forced into the memory specified until the next section directive or the end of the program. Lets display the image we created above. Load start.s and goto Line 519. This is where you will enter the image data, there is no need to type in a section directive as this is already a data_c section ( defined on Line 492 ). Type in the data. Now move the cursor to Line 466 and type in the Image structure shown above ( including the label 'MyImage' ). All that is now required is the call to DrawImage(). We will put this at the same place we put the call to DrawBorder(), so move the cursor to Line 139 and enter the following: move.l window.rp,a0 Rastport lea MyImage,a1 Image structure moveq.l #30,d0 X offset moveq.l #30,d1 Y offset CALLINT DrawImage and disply the image moveq.l #1,d0 no errors The last line is required to stop the program from quitting without closing the window! Now assemble and run the example. Time for you to experiment with PlanePick and PlaneOnOff. I suggest you try as many combinations as possible. On the disc you will find an Image example, it uses an image created using BrushCon. The image consists of two bitplanes, the data for the second plane follows the data for the first. Assemble and run this example. Fonts ===== Time to look at a rather grey area avoided by most common assembly language text books, fonts. Fonts are not that difficult to use and can improve the presentation of a program no end. I have included a font on the source disc which the following example uses. It is called Balloon and is a BIG font. Before you can use a font it must be open, just like a library, window or screen. Unfortunately, the routines for opening disc resident fonts are not in the Intuition library, they are infact in the diskfont.library (suprise, suprise! ). This means we are going to have to open the diskfont library before we can open a font. What a bummer, a programmers life is never an easy one! Here is the code to open the diskfont library: lea diskfname,a1 lib name moveq.l #0,d0 any version CALLEXEC OpenLibrary as always move.l d0,_DiskFontBase save base pointer beq error quit if error < rest of program > diskfname dc.b 'diskfont.library',0 even _DiskFontBase dc.l 0 This is the same routine used to open any other library and you should be quite familiar with it by now. To call functions from the diskfont library we use the following macro: CALLDISKFONT This macro is defined in the file 'include/libraries/diskfont_lib.i', as are both of this libraries functions. I am only going to look at one of these functions, OpenDiskFont (). As you may have guessed, this opens a disc based font so the system can use it. Here is a description of this function. OpenDiskFont ( textAttr ) A0 If the font opens, a handle is returned in register D0. This should be saved so that you can close the font later. If the font failed to open for some reason, D0 will contain a zero. Do not try and use a font that failed to open or the Guru will visit. textAttr is a structure ( moan! ) that defines the font to be opened. The address of this structure is passed in register A0. Lets look at the textAttr structure, it is defined in the file 'include/graphics/text.i': ta_Name LONG Address of the name of the font ta_YSize WORD the height of the font in pixels ta_Style BYTE style of font required ta_Flags BYTE type of font to load ta_Name ------- This must hold the address of a 0 terminated string that names the font. You can either specify the full pathname of the font ie 'Source:fonts/balloon.font' or you can just specify the name of the font, ie 'balloon.font' If you only specify the name of the font, your Amy will assume the font can be found in the FONTS: directory. Note that all font names must end with .font ta_YSize -------- This specifies the height of the font you want to open. The height of the font can be determined from the disk the font is on. Look in the font directory, you will find entries such as: 8 9 20 These are all the sizes of that particular font that are available. The balloon font supplied comes in just one size, 30 pixels high. ta_Style -------- Since this will be treated as a normal font, you can specify any of the styles ( unexpected results may occur on some fonts ). FS_NORMAL all characters are displayed normally FSB_ITALIC all characters are displayed in italics ( slanted ) FSB_BOLD all characters are displayed in bold type FSB_UNDERLINED all characters are underlined ta_Flags -------- The flags define details about the font. There are more flags than I am going to cover, the reason is I've never used them and am not sure of their function! anyone care to expand on this please write to me. FPB_DISKFONT font is disc based FPB_ROMFONT font is in ROM ( either Topaz 6 or 8 ) FPB_PROPORTIONAL font is a proportional font FPB_DESIGNED font is of fixed width The Balloon font on the source disk is a proportional font, 30 pixels high, so a suitable textAttr structure to define it would be: balloonAttr dc.l .fontname dc.w 30 dc.b FS_NORMAL dc.b FPB_PROPORTIONAL!FPB_DISKFONT .fontname dc.b 'source:fonts/balloon.font',0 even A further example should help, to define the 8 pixel ROM version of Topaz, but in italics: topazAttr dc.l .fontname dc.w 8 dc.b FS_ITALIC dc.b FPB_DESIGNED!FPB_ROMFONT .fontname 'topaz.font',0 even Topaz 8 is a designed font, ie all characters are designed in a grid of set size: 8x8. ( Almost made a cock-up! To open a ROM font, you must use OpenFont () this is almost identical to OpenDiskFont ). Well it is now a simple matter opening a font. Just set up a textAttr structure and call OpenDiskFont: lea balloonAttr,a0 addr of textAttr CALLDISKFONT OpenDiskFont open the font move.l d0,font.ptr save handle beq error quit if error Once the font is open, it must be attached to the window we are using. This is done using one of the functions from the graphics library called SetFont (): SetFont ( RastPort, font ) A1 A0 RastPort should be the address of the rastport attached to the window that the program opens. Start.s stores this pointer in 'window.rp'. Notice that the window must be open before we can call this function! font should be the handle supplied by OpenDiskFont(). Before bringing all this together in an example, I'd better explain how to close a font. Would you believe it? the function to close a font is in the graphics library. Good old Commodore! Well the start.s program opens the graphics library for us, so I will not go into that here. The function we require is CloseFont (): CloseFont ( font ) A1 We must supply the handle returned by OpenDiskFont in regeiter A1. There is no useful return from this function. move.l font.ptr,a1 font handle CALLGRAF CloseFont and close it The example then. This example will serve three purposes. First it demonstrates modular programming in that we know that start.s works and we are now going to write an extra module of code to attach to it. Secondly it shows how to make minor alterations to start.s to achieve the program we require and finaly it shows how to display text using a custom font. This example will be created by editing the start.s I suggest you set the copy protection bit of start.s to make sure you do not accidentally save over it. For those amongst you who would rather just read and then assemble, I have included the final version in the Tutorial_Source directory. It is called font_eg1.s. First we will have to define some data, the textAttr structure for example. Load start.s and get the cursor to Line 463, this is the text we are going to print. Edit this so it will display your name: .Text dc.b 'MARK MEANY.',0 The text you are going to print MUST be in upper case as balloon.font does not support lower case letters, sorry! The actual code that handles the font is going to be written in a seperate file. It will consist of two subroutines, one to open the diskfont library and attach the font to the window and one to close the library. I have named these routines FontAdd and FontFree respectively. FontFree should be called from the DeInit routine in start.s because it is freeing resources that the program has used. Go to Line 183 and edit it to read: DeInit bsr FontFree release font As I mentioned above, the font must be added after the window has been opened. It must also be added before the text is printed ( obvious ). Move the cursor into the Openwin subroutine at Line 131 and add the following line: bsr FontAdd set new font All that now remains is the command to Devpac to draw in the extra source module that will contain these subroutines, move the cursor to the last line of the file ( Right Amiga key + B ). Add the following line: include ram:font.i This will tell Devpac to read in the file font.i from ram. This file will soon contain the required subroutines and the data they require. Save this modified version of start.s, do not forget to rename it ( I called it font_eg1.s ). Now clear the editor ready to enter the required subroutines. The first subroutine we will write is FontAdd. This must preform the following three tasks: 1/ Open the DiskFont library 2/ Open the balloon font 3/ Attach the balloon font to the window start.s has opened Because the subroutines in this file access addittional libraries and also call on specific areas of the graphics libraries we will need to start the file off with some instructions to Devpac: include libraries/diskfont_lib.i include graphics/text.i This makes sure all the relevant equates are set up for our use. On to writing FontAdd then. I have already explained each of the three steps above, so here is the routine in full: *************** Extra Initalisations required for this program ; Open the diskfont library FontAdd lea diskfname,a1 a1->lib name moveq.l #0,d0 any version CALLEXEC OpenLibrary and open it move.l d0,_DiskfontBase save base pointer beq .error quit if error ; Open balloon font lea balloonAttr,a0 addr of font struct CALLDISKFONT OpenDiskFont and open font move.l d0,font.ptr save handle beq .error quit if error ; Attach the font to the window start.s has opened move.l d0,a0 font handle move.l window.rp,a1 rastport CALLGRAF SetFont attach font .error rts *************** Well that was quite straight forward. Now the FontFree routine. Some care is needed here, we must not assume that either the font, or the diskfont library have opened correctly. Attempting to close either if they are not open will summon your friendly nieghbourhood Guru. Before each is released, the following routine checks it exsists first: *************** Extra resource releasing required by this prog. FontFree tst.l font.ptr font loaded ? beq.s .no_font if not skip next bit ; Close font. move.l font.ptr,a1 get font handle CALLGRAF CloseFont and close it ; Close diskfont library .no_font tst.l _DiskfontBase lib open? beq.s .no_lib if not skip next bit move.l _DiskfontBase,a1 get lib base pointer CALLEXEC CloseLibrary and close .no_lib rts and return *************** The above two subroutines both require data that has not been defined in 'start.s', so we must define it in this file. The data required is: *************** Data required by this extension. diskfname dc.b 'diskfont.library',0 even _DiskfontBase dc.l 0 font.ptr dc.l 0 balloonAttr dc.l .fontname dc.w 30 dc.b FS_NORMAL dc.b FPF_PROPORTIONAL!FPF_DISKFONT .fontname dc.b 'Source:fonts/balloon.font',0 even *************** Notice the filename I have given the font. You may like to copy the font into ram: and edit this filename accordingly. When the file is complete, save it to disc for future reference. Next save it to ram: and call it 'font.i', remember that font_eg1.s will be looking for this file! Load font_eg1.s, assemble and run it. Your name displayed in a custom font in an intuition window. Well that was quite involved and if you managed to stay with me this far I suggest you now have a refreshing cup of tea, I'm going to! I will admit that when I was starting this tutorial I had intended to link the font to the IText structure via the it_ITextFont field. I failed and do not know why!