Common Program Errors ~~~~~~~~~~~~~~~~~~~~~ This article is dedicated to the hard nosed metal bashers whose work I often admire for being creative and for pushing back the limits of the Amiga,but also curse for not using the system taking no notice of the users needs and not being debugged. The following is a sort of hit list of program errors,some you may agree with and some you may not.Either way I hope that my comments may spur some programmers on to take a little more time and care over their programs. Perhaps most of all he will spare a little consideration for the poor end user.Here's the list,you might like to add some of your own,or you may feel that I'm being petty,but I feel that I am acting in the interests of the Amiga and the Amiga community. Programs which break when used with expanded systems. The main cause of his is problems with memory allocation.For those who don't already know,there are two basic types of memory available on the Amiga. First there is the Chip memory.Chip memory can be accessed by all the Amiga's custom chips,so all sound and graphics data must reside here.The setback here is that all the Audio,graphics,Blitter,sprite etc DMA processes will slow down the processor by making it wait for control of the address and data busses when accessing this type of memory.All Amigas will have some Chip memory. Secondly there is the Fast memory.This is called fast memory because the processor is not slowed down by DMA processes while reading this type of memory.However fast memory can't be used to store any data to be accessed by the custom chips.Not all Amigas have Fast memory. This leaves us with one last question.Whats Public memory then ?. Public memory isn't as some books would have us believe a way of getting either chip or fast memory it just means that we have set a flag to say that this block of memory cannot be relocated mapped out or made non addressable.The current Amigas cannot do any of this as it requires an MMU (Memory Management Unit) so setting this flag will have no effect.The Public memory flag is there for upward compatability.Commodore obviously were looking ahead to the day when the Amiga is fitted with an MMU .The 68030 processor has an MMU built in.Public memory should be specified for any system structures and message ports or any blocks of memory that you may want other tasks to have access to.Public memory may be specified with either chip or fast memory. The slow/fast memory found on the A500/A2000 machines should be trated as fast memory (this type of memory will go away as more Amiga's are fitted with the new Agnus chip as this turns the memory into chip memory). If no memory type is specified then the system will try to allocate private (not public) fast memory,if none is available then it will try to allocate private chip memory.It is not recommended that you specify fast memory as your program will fail on all 512k machines and even some 1meg machines if they are fitted with the new Agnus chip. If the memory is allocated within your program as variables or constants using the DC.L type directives then set the code or data sections to the type of memory required with the SECTION directive.If your assembler doesn't allow multiple sections within a program then ditch it and buy a good assembler like Devpac V2.14.It is this type of error that programs like Fixhunk attempts to repair.One last point that a few programmers seem to ignore, always check to see if the memory you asked for was in fact given to you,and if it isn't deal with the error properly. Programs which hog the entire system when it's not necessary.Only games or demos with fast moving graphics need to do this.There are a few other cases but we needn't go into that at the moment. Programs which run into problems if the user boots up with a different font to the one you use.If you want Topaz 8 then either use the graphics library routine SetFont or tell intuition when you open a screen by pointing ns_Font in your newscreen structure to a textfont structure discribing Topaz 8. Programs which set up their own copper list just to open a fairly standard sort of screen.Not going through intuition stops the user from pulling another screens to the front.The first Soundtrackers broke both these last two rules.It may seem that I don't like Soundtracker during the next few paragraphs but nothing could be further from the truth.It's just that this program shows up most of the errors up quite well. Programs which do not redirect system requesters to it's own screen. Don't you find it infuriating when the workbench pops to the front every time a system requester is called.It is not at all difficult to do this and it saves the user having to keep pushing the workbench screen to the back. All you have to do is find the address of your own task control block (with the exec call FindTask) then put the address of the window structure of any window open on the custom screen in Task + 184 (pr_WindowPtr).To be safe you should first save the old address from the control block,install your window pointer,then when you have finished restore the old address BEFORE closing the window. Programs which hard code directory paths.Isn't it a pain having to name all your Soundtracker module disks ST-00: and to create directories modules and songs.Give the user the choice to name his/her disks and directories by whatever names they wish.I can see no reason programming wise why some programmers do this.It is very easy to give the user a choice. Programs which use strange keyboard shortcuts.It may seem logical to you to press Ctrl Alt F1 to load a program or HELP to quit a program but the user,not being telepathic may not find these keystrokes quite so logical or friendly.You should give the user a fighting chance of understanding your program even if the docs get lost,he/she should not have to keep refering to the docs file. Programs which don't use file requesters.Without a requester the user can feel lost and it's a pain having to remember pathnames or (if the program will allow you) have to go back to cli and use the dir command. There is no excuse for not including a file requester as there is the code available in the public domain (an early C.Heath one) or a very good one is included in the ARP library.The ARP one is very easy to use but does require that the arp.library is in the users LIBS: directory. Programs which don't use menus.These are a friendly way for the user to tell your program what he wants to do.Menus can be a bitch to program and the slightest mistake in the menu structures will result in a system crash, usually with no guru message.It is also desirable that you use the COMMSEQ flag in the menu structure to give keyboard shortcuts.Please stick to the standard shortcut keys (ie Right Amiga L for load etc.).If you aren't sure which these are then copy the shortcuts used on other programs like Notepad. Using these will make the user feel at comfortable with your program very quickly.Menus requesters etc are of course only applicable in larger programs and not small utilities or cli commands. Programs which open up a full size window on the workbench without sizing or depth gadgets.This is very unfriendly and stops the user from using programs already running on the workbench.It also effectivly disables the workbench.Even if depth gadgets are used it will still stop the user from opening new disk icons. Programs which don't have a quit option (often my favourite!).This is made even worse with programs that disable multitasking.What if the user had something important in memory when he/she decided to try out that new PD program.The original (commercial) version of Soundtracker was a bitch for this.Usually you are left with a soft reset as your only way out. Programs which constantly tell you how great they are,and what a superb programmer the author is.A good program will speak for itself and,if it is good we will all know how clever the programmer is. Programs which wait around in busy loops.This isn't a nice thing to do as it steals cpu time from other tasks.Is it really nescasary to check for the right or left mouse button thousands of times per second ?.Make your task sleep if it isn't doing anything useful.Programs that don't use intuition gadgets will have to keep checking the left mouse button and pointer position to simulate gadgets.An intuition program can just go to sleep until someone does something which you are interested in like clicking on a gadget or selecting a menu item. Programs which don't free all resources like memory,locks,devices etc. This is bad manners and should be considered a bug.Also many programs change the current directory.If you do this change it back to the directory that was current when your program started. Programs which just start using memory without allocating it (in the good old fashioned wild coding manner).There is no excuse for stomping all over the users (possibly valuable) data.If your program must have memory at a particular address (though I can see no reason why you should need to) then use the Exec call AllocAbs. Programs which can't run from workbench.Stange as it may seem quite a few users still prefer to use workbench,and are afraid of the mysterious CLI. If your code doesn't have a proper WB startup and the user creates an icon for your program then runs it he/she can expect a visit from the guru or they can say goodbye to a chunk of memory.As workbench never gets it's message replied to it will assume that the program is still running and will never UnLoadSeg it.Early Soundtracker playroutines like those used on 17 Bit Disk 14 caused this problem. Programs which simply quit when you try to run them.Give the user some sort of error message telling him/her why your program can't run.If there isn't enough memory or a file can't be found then tell the user.Don't just quit! Above all keep your code legal,at the very least hardware legal.That is if you don't want to rewrite your code for each new version of the OS.If you must jump into ROM code (what's this! I don't believe I just said that) then only jump into proper documented rom routines and get their address legally. That is open the library and then get the address of the routine from the libraries jump table and store it in an address register.You only then need to do jsr (An) where n is the number of the register containing the routine address.It is also necessary to have the base address of the library in reg A6,even though you aren't using it to calculate the jump address,as library routines often use this and expect to find the base address there.This method can be useful for squeezing that little extra from a code loop.Don't try this unless you really know what you're doing.To be quite frank the benifits of doing this are only very small. Non Optimisation of Code ~~~~~~~~~~~~~~~~~~~~~~~~ This is one of my pet hates.It is suprising just how much memory and CPU time can be wasted with sloppy coding.It is not at all unusual to gain about 200 bytes from a program that is only 2k long.As the Amiga is multitasking the programmer has a duty to keep his code size down and make his program sleep where possible.On NewsFlash 9 was a very good article based on an article by J A Toebes III in a German magazine,but which also appeared in this country in Transactor Vol2 Issue 1.There is not a lot that can be added to such a good article but here are a few optimizations either not covered or not fully exploited. PC Relative ~~~~~~~~~~~ Any reference to a label within the same code section should use pc relative addressing.ie Move.l _DOSBase(pc),a6 4 bytes 16 cycles This is both smaller and faster than Move.l _DOSBase,a6 6 bytes 20 cycles Adding And Subtracting From Address Registers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To add or subtract values greater than 8 (that can't use addq or subq)we can use the LEA instruction,yes thats right the LEA instruction.This ones a little sneaky but it's a great way to add or subtract from an address register.The trick is to get LEA to calculate the register address with displacement ie Lea $200(a1),a1 4 bytes 8 cycles adds $200 to a1 and is twice as fast as Add.l #$200,a1 6 bytes 16 cycles Similary Lea can be used to load constants less than $ffff into address registers ie. Lea $5000.w,a1 4 bytes 8 cycles instead of Move.l #$5000,a1 6 bytes 12 cycles Keep track of condition codes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ I think I've lost track of the times that I've seen something like CALLEXEC OpenLibrary Tst.l d0 Beq Liberror Move.l d0,LibBase This can be more effectivly programed as CALLEXEC OpenLibrary Move.l d0,LibBase Beq Liberror Giving you a saving of 2 bytes and 4 cycles.(the Tst.l d0).Incidentally the condition codes are set on return from the OpenLibrary routine but this is something that you should NOT rely upon. Use Subroutines ~~~~~~~~~~~~~~~ If a piece of code or code very similar is repeated a number of times throughout a program,consider making it a subroutine.If the code is similar see if it can be modified to both jobs.The larger the section of code the greater the benifits.Common routines are those which allocate memory,open and close files,open and close libraries etc.Usually you can make one subroutine and just pass parameters (like say the address of the library name) telling what you want to do. Over Use of Macros ~~~~~~~~~~~~~~~~~~ The big culprit here is the CALL macros like CALLGRAF.Each time this macro is called it not only generates the code for the subroutine jump but also generates the code to load register A6 with the library base address.Examine the code and if A6 already contains the right library base address then use the macro CALLSYS.This macro can be found in asmsupp.i which is available in the public domain.CALLSYS doesn't load A6 with the base address but just generates the code for the subroutine jump.As a rule of thumb start at a label then work your way down the code.After the first CALLGRAF macro (or whatever library your using) all further calls to the graphics library may be changed to CALLSYS until you encounter a call to another library or you reach a label.The reasons for this are that a call to another library will put it's base address into A6 and a label means that another section of code may jump or branch to this label with another library base address in A6.This may sound a little confusing at first but if you give it a little thought it should become clear.Each time you replace a CALL you will save 6 bytes and 20 cycles.It is possible to save a lot of memory with some programs and speed up the code into the bargain.For those who haven't already got or can't get asmsupp.i here is the listing for the CALLSYS macro.Note! I have altered this macro so that it doesn't need the CALLLIB macro (found in exec/libraries.i).The first lines are to protect the programmer (if there were no aruments for the macro) and may be deleted if they cause problems for your particular assembler. CALLSYS MACRO IFGT NARG-1 if your assembler doesn't FAIL !!! support these lines you ENDC may delete them JSR _LVO\1(A6) ENDM Seka users will have to change the \1 into the non-standard ?1. Saving Registers ~~~~~~~~~~~~~~~~ By convention all subroutines should restore all data registers numbered 2 to 7 and address registers 2 to 6.Note A7 isn't restored because it contains the subroutine return address.The saving of registers is a good idea especially if the routine is a general purpose one that may be re-used.It saves you the worry of remembering which registers are safe to use and makes adding the subroutine to another program a much smoother and easier process.If the subroutine is going to be used only for the program that you are working on you can forget about restoring the registers.For subroutines that you want to re-use only restore modified registers.Some programmers restore all registers even if they haven't been modified.Take care to choose your registers to keep the number of modified registers down to a minimum.The fewer registers there are to restore the faster the movem.l instruction works.In fact if you only have one register to restore then use move.l instead as this works faster still.In the worst possible case movem.l can use up to 140 cycles,however it is highly unlikely that you will want to restore 14 registers. Whilst we are talking about the MOVEM instruction I'd like to make a few comments on the Movememory routine by Olli Wagner from Newsflash 8.Firstly the routine isn't as fast as <> led us to believe.I make it about 5.17 cycles per byte (OK so there's no such thing as .17 of a cycle).Using move.l gives us a copy rate of 8 cycles per byte.Note that the dbra timing is for the false branch only. 1: Move.l (a0)+,(a1)+ 2 bytes 20 cycles Dbra d1,1 2 bytes 12 cycles or adding another move.l speeds it up to 6.5 cycles per byte. 1: Move.l (a0)+,(a1)+ 2 bytes 20 cycles Move.l (a0)+,(a1)+ 2 bytes 20 cycles Dbra d1,1 2 bytes 12 cycles So Movem is still a little faster,but it does suffer one major drawback (which makes me wonder why other programmers agree with Olli),and blows it right out of the water,it is illegal code.The line MOVEM.l d1-d7/a2-a6,(a1)+ Should generate an illegal effective address error from your assembler.To quote from the Motorola 68000 Pocket Reference Guide (given away with Devpac) "If the effective address is in the postincrement mode,only a memory to register operation is allowed".This of course only refers to Movem.The exec call to CopyMemQuick uses a similar approach to the movememory routine except that the destination address is incremented with an add instruction (I peeked) and is much more elegant.To be fair the movememory routine may work as some 68000 cpu's may support this addressing mode (like the 6502 had some extra unofficial instructions whic worked on some cpu's,like STZ store zero), but it isn't something that you can rely upon.So don't do it. As to the blitter being very slow,well OK it's not that fast,but it is capable of moving memory just over two and a half times faster than the 7.09 MHz 68000,provided it isn't slowed down by DMA contention.If the blitter is being slowed then it is possible that the 68000 is also being slowed down. The blitter also supports rectangular memory areas (the basis for Bobs).To simulate this the 68000 would have to have two nested loops with an add.l instruction to add the modulo value to the destination address slowing things further.The blitter doesn't completely block the machine as stated in the article.It can block the 68000 some,perhaps almost completely if it is in "blitter nasty" mode,that is if the DMAF_BLITHOG bit in register DMACON is set.If DMAF_BLITHOG is clear then the 68000 will get a go at least every fourth cycle depending on what the blitter is doing.So the 68000 is only slowed down to at worst 25% of it's normal speed.The blitter really comes into it's own when doing things like logical operations (256 are available) on the data it is moving allowing it to combine,mask etc data from up to three sources to form the destination data.The blitter also has a barrel shifter (something the ST's blitter hasn't got,which is one of the reasons why it is near useless).This is used to shift data to overcome problems with only being able to blit words onto a graphics screen and have the image aligned on the screen to single pixel resolution. The blitter also has an area fill mode and a line draw mode,both of which are very fast and easily outperform the 68000 at these tasks.So whats does all this add up to then.Well if you're moving a small block of contiguous then stick with the cpu as the set up time for the blitter will offset any gain in speed.For slightly larger blocks use the exec routines.If you wish to copy chip memory to chip memory in large chunks,as rectangular memory or wish to perform logical operations upon the data being moved then consider using the blitter.For more information about the blitter consult the Hardware Manual (Addison Wesley),System Programmers Guide (Abacus) or perhaps get hold of a copy of Blitlab by Tomas Rokicki (Fish Disk 191).The program and the documents are very informative.One last tip I believe the credit for this should go to Jez San.For large blits wait until the start of the vertical blank before starting the blit as no screen data is being gathered and the blitter will operate at twice the speed. At the end of all this you can see that I don't agree with removing the blitter from our machine.To remove the blitter we would have to remove Agnus who also controls DMA,Audio,memory refresh and the copper.Without these we only need cut a few pins from Denise (or Daphne if you've known her for a long time) then smash your monitor so you have to buy a new one and presto! an ST simulator which is capable of running most ST games and demos :-) Steve Marshall, Sheffield, Uk.