
CORSO DI ASSEMBLER - LESSON 12

Author: Fabio Ciucci

In this lesson you will read the techniques to obtain the COMPATIBILITY of the 
code, a very important thing: think how important it is that the game or the 
demo you program works on all Amiga models !!!

This is by no means difficult, although it is known that a lot of old games and 
demos do not work on kick 2.0, 68020, a1200, or even there are things that only 
work on the unexpanded A500 1.3, just insert the fast ram, or the new kickstart 
and they no longer work.

All these problems come from a few causes, the same stupid causes, in fact 99% 
of the code of a demo or a game that only goes on A500 1.3 would work on all 
Amigas, if it weren't for those 2 or 3 lines of "DIRTY" code that get 
everything nailed down, usually at boot.

Personally I have always tried to understand all these BUGs, that is 
programming errors visible only on machines higher than 500 1.3, and very often 
I have managed to FIX, that is to "repair" the games or demos that did not 
work, simply by disassembling the code and modifying the recurring errors.

In this way I combined business with pleasure: on the one hand I made the game 
or demo friends always liked so much when they had the 500 1.3 work when they 
got A1200 or other computers, on the other hand, I got a good education on the 
causes of the "nailing" with spectacular gurus, and I found that they are 
always the same few programming "flaws", which I will list.

Because of these flaws, most of the software written in direct assembler on the 
Amiga hardware has earned the reputation of being incompatible and unsafe, so 
the assembler itself has been blamed for being an insecure language, especially 
the "hardware direct" language, the "metalbashing".

All these problems however can be easily avoided, just DON'T DO certain things, 
and surely the game / demo will run on all Amigas.

In fact, all the listings in this course, for example, work on both amiga 500 
1.3 and amiga 4000/040, and of course they also work on all other intermediate 
computers, with any configuration.

Of course I can't guarantee compatibility with hypothetical amiga models with 
RISC or AAA chipset, but in that case NOT EVEN A GAME would work, much less 
programs like the protracker. In fact, I hope that the 680x0 series and the 
downward compatibility with the ECS (at least) is maintained, or we would find 
ourselves with PCs, called "Amiga", but not compatible with MSDOS.

I would rather make computers based on 68060 at 150Mhz, which has nothing to 
envy a RISC, and a "local bus" for the chipram, ie a speeding up of access to 
this type of memory, which is too slow on current models (cursed C= engineering 
in the last hour).

I decided to deal with this topic just now, because in order to make mistakes, 
at least you need to know how to program, so it was not logical to insert this 
lesson before having explained the basics of programming.

After this lesson, you may be able to get your old incompatible game to work!

In the course you have seen "HOW TO PROGRAM", with all the right procedures, so 
ignore the bullshit that was done years ago.

Here is a list of the "paperissima" errors that I have found around programs 
that do not work on all machines: (test on A4000 / 040)

******************************************************************************
 PART 1: ERRORS CONCERNING THE $dffXXX REGISTERS, i.e. COPPERLIST and BLITTER
******************************************************************************

1) Among the "remediable" errors we find the least serious, which is not a 
   mistake in the old productions, since they could not have known about the AGA:
   is to "forget" to reset the AGA with these 3 instructions after pointing the 
   copperlist: (Not before!)

	lea	$dff000,a5	; CUSTOM base address in A5 for offsets
	move.l	#copper,$80(a5)	; COP1LC - Point the copperlist
	move.w	d0,$88(a5)	; COPJMP1 - start the copperlist

;	let's disable AGA:

	move.w	#0,$1fc(a5)	; reset sprites wide and DISABLE 64 bit burst
	MOVE.W	#$c00,$106(A5)	; reset AGA palette, sprite resolution
				; and double playfield palette
	MOVE.W	#$11,$10c(A5)	; reset AGA sprite palette

   This error can be remedied by booting the computer by pressing both mouse 
   buttons, and selecting the old chipset emulation.
   
   On the other hand, the copper problems do not end here, in fact it is enough 
   to forget to define any of the COPPER registers, for the error to appear! In 
   fact I found many copperlists of old demos / games that did not define the 
   modulos, so the VALUES of the system copperlist REMAIN, and you never know 
   what they are. In fact the most common mistake is not to set the modulos 
   ($108 and $10a), assuming they are cleared. THIS WAS TRUE FOR KICKSTART 
   1.3!!! BUT SINCE 2.0 THE MODULO IS NOT ZERO!!! So the intros / demos / games 
   are "crawled" unless you load kickstart 1.3. The same goes for DiwStart / 
   DiwStop etc. ALWAYS remember to set all the registers in the copperlist, 
   even if they are cleared, to avoid that the uncertain values of the 
   operating system remain!!!

	dc.w	$108,0		; Bpl1Mod
	dc.w	$10a,0		; Bpl2Mod
	dc.w	$8e,$2c81	; DiwStrt
	dc.w	$90,$2cc1	; DiwStop
	dc.w	$92,$38		; DdfStart
	dc.w	$94,$d0		; DdfStop
	dc.w	$102,0		; BplCon1
	dc.w	$104,0		; BplCon2

   I also found other silly errors by sifting through old copperlists.
   Some, instead of NOT SETTING the registers, mysteriously SET TOO MANY!!! In 
   fact, you NEVER NEED TO ACCESS AN UNKNOWN REGISTER, OR NOT YET USED, NOR 
   NEED TO SET OR RESET RESERVED OR NO FUNCTION BITS IN KNOWN REGISTERS, in 
   this way you risk activating strange functions in future chipsets.
   
   For now we have had the biggest evolution, from ECS to AGA, and there are 
   more "blind setup" errors than I expected. For example, I found this oddity 
   in the copperlist of a very old Ackerlight intro:

	....
	dc.w	$100,$5000	; BPLCON0
	dc.w	$0092,$30	; DDFSTRT
;--->	dc.w	$106,$FE5	; Why did they add a register that did not 
				; exist at that time? On the AGA he distorts 
				; the palette
	dc.w	$102,$CC	; BPLCON1
	dc.w	$108,$A8	; BPL1MOD
	dc.w	$10A,$A8	; BPL2MOD
	....

   This error went unnoticed until the AGAs came out, and beware that this 
   error is ABSOLUTELY INELIMINABLE, in fact, before disassembling the material 
   that doesn't work, I do all possible tests to understand the problem: I 
   remove the fastmemory, load kick 1.3, disable caches, MMU, reset VBR etc ... 
   This error showed up anyway: the colors were wrong, everything else worked.
   
   In fact, the only way to correct these errors is to find them and remove 
   them in the code. I replaced that $106,$fe5 with a $92,$30, ie I replicated 
   the previous line, and everything worked out beautifully.
   
   Maybe the coder didn't even notice that move, it may be a typo, maybe he 
   wanted to write $108 and not $106, who knows, but be careful to only work on 
   known registers or bits, or you will be disappointed to see your production 
   not work on your grandchildren's Amiga 9000, and that for one stupid line in 
   the copperlist.
   
   Also pay attention to the sprite structures, since already from OCS to ECS 
   there are some extra bits in the fourth control byte: many sprites that 
   appear "distorted", or stretched to the end of the screen are in that state 
   ONLY on ECS / AGA machines, and not on old A500 / A2000, because the sprite 
   management routine left it set instead of resetting that bit.
   
   The designers recommend to leave the unused bits of the known registers 
   CLEARED, and NOT TO ACCESS the ones not yet known at all. I strongly advise 
   you not to forget it.

	-	-	-	-	-	-	-	-	-

2) Do not use the CLR instruction on $dffXXX registers, because this
   instruction has a different behavior on 68000 and 68020/30/40 processors, in 
   fact on 68000 it causes one read and one write, i.e. 2 accesses, while on 
   68020/30/40 it causes only one access.

   To avoid different results on different processors, remember to access the 
   STROBE registers in another way (COPJMP1 = $dff080, COPJMP2 = $dff088 etc.). 
   One way can be a MOVE.W d0,$dff080. I have also encountered problems when 
   using the CLR on other $dffXXX registers, for example $dff064 (BLTAMOD)

Example1:
	MOVE.W	#0,$DFF088	; never do CLR.W $dff088!
or:
	MOVE.W	d0,$dff088
	...

Example2:
	MOVE.W	#0,$DFF064	; never do CLR.W $dff064!
or:
	MOVEQ	#0,d0
	MOVE.W	d0,$dff064
	...

   For safety, therefore, I recommend that you access through registers or 
   direct values (#0,$dffxxx), never use a CLR on a $dffxxx register.

	-	-	-	-	-	-	-	-	-

3) In 1988-1989 a rather stupid way of pointing copperlists was in use, which 
   later turned out to be incompatible with versions of the operating system 
   from 2.0 onwards, given that system structures were taken for granted which, 
   of course, not being documented from Commodore, they changed, leaving "the 
   smart ones" with their sources that no longer pointed the copperlists on 
   A500+ and A600. Unfortunately, someone inexperienced in programming has 
   continued to "steal" pieces of old listings that they found around, 
   containing this ridiculous copperlist pointing code, so even some demos from 
   1990-91 require kickstart 1.3 in order to work due to this levity.
   Here is the painful code invented by some "clever" coding pioneer:

	move.l  4.w,a6		; execbase
	move.l  (a6),a6		; ???
	move.l  (a6),a6		; HAHAHA! GFXBASE??? Only in kick1.3!
	move.l	$26(a6),OLDCOP	; HAHAHA! SAVE OLD COPLIST???
	move.l	#MYCOP,$32(a6)	; DOUBLE HAHAHAHA! POINT COPLIST???
	...

   Unfortunately, this piece of code is very common in the old listings around 
   in the databases, and in the intros (eg that of the ORACLE).
   
   ALWAYS remember not to make the DOUBLE mistake contained in these 4 lines 
   of VERY DIRTY code: First of all, the GFXBASE can be found by opening the 
   graphics.library as it is done in the examples of the course, and certainly 
   not by doing twice "move.l (a6), a6 ", this happens randomly only in 
   kickstart 1.2 and 1.3 due to the particular structure of the old library.
   
   The second mistake is to point the copperlist by putting its address in the 
   GFXBASE structure instead of in the $dff080 register, this causes endless 
   disasters, always point the copperlist with a nice:

	MOVE.L	#Copperlist,$dff080	; COP1LC
	move.w	d0,$dff088		; COPJMP1

	-	-	-	-	-	-	-	-	-

4) A custom of the older generation of coders was also not to wait for the end 
   of a blittata before making another. This is very easily found in code 
   written before 1990, but there are some that still continue to gloss over 
   WaitBlit routines.

   Indeed, when the Amigas only had the 68000 as a processor there were cases 
   in which, between one blitt and the other, the processor needed so much 
   time to do the necessary operations that the blitter had already finished.
   
   Demo programmers (and unfortunately also game programmers) often thought 
   that it was useless to wait for the blitter if, even without putting the 
   Wait routine, everything worked. An example is the PANG game ...
   
   But if they saved 2 lines of code in their demo / game, not only did they 
   not increase the speed of execution (I'm sure a couple of "btst #6,$dff002" 
   isn't enough to slow down ...), but they didn't consider that on faster 
   processors the time between blitts is shortened, since it is known that the 
   68020 is faster than the 68000, so the crash is total.
   
   Unfortunately, the blitter remained at the same slow speed on ECS and AGA 
   (on accelerated A4000 or a1200 it is even slower than normal!).
   
   To solve the problems produced by such "lightness" of programming, 
   sometimes it is enough to remove the caches and the fast ram, so even a 
   68020, if it works on chip ram with caches disabled, it sometimes slows 
   down enough to avoid a blitt on top of another already running
   
   The bad thing is that, for hardware synchronization reasons, on computers 
   like A4000 or A1200 accelerated with 68030, the blitter is SLOWER THAN IN 
   THE OLD A500, so even if we manage to slow down the processor to a basic 
   68000, it's the slower blitter which makes the crash inevitable and 
   certainly not welcome.
   
   Among other things, keep in mind that even if you have all the routines 
   that wait for the end of the blitt before making another one, the blitter, 
   being slower on the A4000, can cause horrendous "jerkiness" in games or 
   demos that run smoothly (at 50 frames per second) on an A500, making the 
   incredulous owner of the A4000 nervous, who had seen it run faster earlier.
   
   So ALWAYS and HOWSOEVER wait for the blitter to finish:

	LEA	$dff000,a5
WaitBlit0:
	BTST.B	#6,2(a5)
WaitBlit1:
	BTST.B	#6,2(a5)	; check 2 times due to an error in the A1000
	BNE.S	WaitBlit1

   P.S: Sometimes you can find "btst #14,$dff002" instead of "btst 
	#6,$dff002", but the effect is the same, since bit 6 is always tested. 
	In fact, 6 + 8 is 14. The BTST works on bytes only and tests the sixth 
	bit anyway. For aesthetic (and logical) reasons we prefer to use btst 
	#6 and not btst #14!!

    To give you an idea of how much the blitter slows down on accelerated 
    machines, consider that, for example, a routine that blits 14 bobs per 
    frame on a basic A1200 would only blitt 12 on an A4000 and only 9 on an 
    A1200 with a GVP 68030 accelerator card at 40Mhz!!!!
    
    Therefore, consider that, when possible, it is better to use the processor 
    than the blitter.
    
    Furthermore, it is always good to leave some "free" raster lines, rather 
    than blit until the last millisecond. In fact, in the latter case, with 
    the slowdown of the blitter on accelerated A4000 or a1200 it would no 
    longer make it in one frame, and everything would become mega jerky.

****************************************************************************
 PART 2: ERRORS CONCERNING CIAA / CIAB - KEYBOARD, TIMERS, TRACKLOADERS
****************************************************************************

5) Routines that make the caps lock key LED blink do not work on A1200 because 
   it has a cheap keyboard different from the standard ones.
   These routines are present in certain demos for "beauty", here is one 
   below, try it and you will notice the flash on A500 / A2000 / A3000 / 
   A4000, and, instead, a nice reset on an A1200:

CAPSLOCK:
	LEA	$BFE000,A2
	MOVEQ	#6,D1		; bit 6 of $bfee01-input-output bit of $bfec01
	CLR.B	$801(A2)	; reset TODLO - bit 7-0 of 50-60hz timer
	CLR.B	$C01(A2)	; CLear the SDR (synchronous serial shift
				; connected to the keyboard)
DOFLASH:
	BSET	D1,$E01(A2)	; Output
	BCLR	D1,$E01(A2)	; Input
	CMPI.B	#50,$801(A2)	; Wait 50 blanks (CIA timer) 
	BGE.S	DONE
	BSET	D1,$E01(A2)	; Output
	BCLR	D1,$E01(A2)	; Input
	MOVE.W	$DFF01E,D0	; Intreqr in d0
	ANDI.W	#%00000010,D0	; checks I/O PORTS
	BEQ.S	DOFLASH
DONE:
	RTS

   The amiga 1200 has a cheap keyboard controller, try this test to verify: 
   press the "r" key, leave it pressed, and hit another key, for example the 
   "u". Nothing happens on an a1200, while on another computer the "u" appears 
   on the screen.
   
   So don't mess with the routines that manage the keyboard!
   One of the demos that don't work on the A1200 because of this routine is 
   ODISSEY.

   As for the routines that "move" the heads of the drives, the fundamental 
   issue is to make a mistake in the synchronization routines, doing them with 
   simple "empty" loops or series of NOPs which, on faster processors, are 
   performed too quickly to wait long enough. Time with VBLANK or with the CIA!

****************************************************************************
 PART 3: ERRORS CONCERNING THE 68010/20/30/40/60 PROCESSORS
****************************************************************************

6) First of all, we must also look for errors in the utilities we use, and not 
   only in our executable. In fact, I often made demos or intros work simply 
   by unpacking and recompacting them with a modern cruncher, for example 
   Powerpacker or StoneCracker 4.
   
   In fact many of the old absolute address compactors (crunchers) do not work 
   on 68010+, so even if the demo itself works, the mere fact of being 
   compacted with an old ByteKiller or TetraPacker makes it go into guru, 
   before starting, during the decrunch.
   
   First of all, therefore, do not compress your program with old crunchers, 
   use Stone Cracker4, PowerPacker or Titan Cruncher.
   
   Furthermore, it is always better to make relocatable code than code with 
   absolute addresses!!!

	-	-	-	-	-	-	-	-	-

7) Address errors:
   Some old productions contain accesses to the ROM addresses, for example:

	JSR	$fce220

   Well, kickstart 1.2 / 1.3 is localized, in older Amigas, to memory 
   locations $fc0000, up to $ffffff, for a total of 256k.
   
   It is obvious that in these kickstarts each routine has its own address: as 
   we have already seen there is a "JMP table" at the Execbase address, ie we 
   know that, for example, having the execbase in a6, we will find, $84 bytes 
   earlier, the JMP that jumps to ROM to execute the Forbid:

	jsr	-$84(a6)	; Forbid, disable multitasking

   for instance in kickstart 3.0 (Version 39.106) this is the JMP table of the 
   execbase (a disassembled part of it):

	...
	JMP	$00F815CC	; ...
	JMP	$00F815A2	; -$96(a6)
	JMP	$00F81586	; -$90(a6)
	JMP	$00F8286C	; -$8a(a6) - routine of the permit
---»	JMP	$00F82864	; -$84(a6) - routine of the FORBID
	JMP	$00F817F8	; -$7e(a6)
	JMP	$00F817EA	; ...
	...

   On a computer with kickstart V39.106, a FORBID can be obtained with a:

	JSR	$F82864		; Forbid on kickstart V39.106 of the A1200/A4000

   But if, for example, the V39.106 kickstart is loaded via software and not 
   in ROM, the JMP table will point to the RAM addresses where the kick was 
   loaded. Therefore NEVER, NEVER log into kickstart this way, or your 
   production will only run on your computer.
   
   With this example, however, you can guess that you can modify the JMP 
   tables by replacing the ROM address with our own, in order to run our 
   modified routines. This is how programs that modify the operating system 
   work, such as utilities that add a gadget to windows or that add options to 
   the workbench.
   
   it is for this "RELATIVITY" of the operating system that you NEVER have to 
   jump into the ROM. The only fixed address of the Amiga operating system is 
   $0004, which is the EXECBASE, which contains the address from which to 
   make the offsets.
   
   So, if you want to deal with the operating system, always follow the 
   standard instructions. Even if you don't want to deal with it!
   
   This type of error is lethal, so much so that many very old demos made on 
   Amiga500 Kick1.2 do not work on Amiga500 Kick1.3, or higher, not even by 
   loading kickstart 1.2 via software.

   Other slightly less serious address errors are those that assume fast ram 
   is at $c00000.
   Originally Amiga had 512k of CHIP RAM, then the internal expansion 
   increased to 1MB, and it is known that the additional 512k of fast ram is 
   from $c00000 to $c80000.
   
   Even the demos and games then began to be designed to fill the entire 
   megabyte of memory, and since at that time the vast majority of programs 
   were at absolute addresses, the coders thought of assembling the program in 
   fast ram, in locations from $c00000 to $c80000, and to load the graphics 
   and music into chip ram, from $00000 to $80000. So the program, in addition 
   to being unpacked to the absolute addresses $c00000, had the instructions 
   allocated for that zone:

	...
	MOVE.L	#$c23b40,d0
	jsr	$c32100
	...

   These demos or games worked on the A500 internally expanded with the 
   classic RAM expansion card, but when the A500 plus came out, equipped with 
   1MB of fixed memory, but only with CHIP, all those programs became 
   unusable. This happened because with 1MB of chip, the memory is arranged in 
   this way: the first 512k are always from $00000 to $80000, but the second 
   512k are then from $80000 to $100000!!!
   
   So a "JSR $c32100" leads nowhere, however it sure does lead to a 
   spectacular crash with on-screen fireworks.

   Following this, succesive games and demos used different methods to 
   leverage memory beyond the first 512k.
   
   One of these is to completely abandon absolute addressing, forgetting the 
   ORG and LOAD commands, and also the autoboot programs, since those, with 
   their own loader, must necessarily be placed at absolute addresses.
   
   While many subsequent demo / games loadable via DOS became 100% relocatable 
   via SECTIONS, without parts loaded at fixed addresses, others did not want 
   to give up autoboot and fixed addresses, up to the last byte of memory. The 
   latter solved the problem in a couple of ways: one is to assemble two main 
   programs, one fixed with ORG and LOAD at $c00000, if it was determined that 
   the computer had half a mega CHIP and half a FAST, and another fixed at 
   $80000, to be loaded instead in case the computer has 1MB or more of CHIP. 
   In this way at boot a routine checks which case we are in, and loads one or 
   the other main program at the right address, while data such as graphics 
   and sounds are then loaded later by the main program. This system has the 
   disadvantage that it requires the waste of disk space for the two versions 
   of the main program.
   
   Others more "clever" have instead programmed a small operating system, 
   which at boot takes note of which memory segments are present in the 
   computer, and through their own allocation routine reallocates the various 
   parts of the program to the address where they find the FAST RAM.
   
   This is certainly the best way to make an AUTOBOOT program, even if it is 
   rather difficult, and the advantages are these: imagine loading a demo or a 
   game with the system of the two main programs on an A4000:
   
   at boot the program recognizes the memory and, realizing that there is no 
   memory at $c00000, loads the code in CHIP RAM at $80000.
   
   The demo / game itself, on the other hand, is modified to load with the 
   mini operating system: at boot it recognizes that there are two memory 
   blocks, the CHIP one from $000000 to $200000 and the FAST one from $7c00000 
   to $7ffffff, consequently relocates all the parts of code in FAST RAM and 
   loads graphics and sound into CHIP RAM; as you know the code in FAST RAM is 
   much faster than in CHIP RAM, especially on TURBO processors like the 
   68040, consequently the demo or the game will go much faster with the code 
   relocated in FAST.
   
   However, it should be noted that those who have used their operating 
   systems have seen their demo crash with the advent of the 68040, or even 
   with the advent of the simple 68020, because Motorola guarantees complete 
   compatibility downwards ONLY in usermode, and not in supervisor mode: in 
   fact the 68040 has its own instructions for the supervisor mode, and also 
   the 68060 is 100% compatible only in usermode...
   
   Let alone processors or computers of the future, which maybe will emulate 
   the 680x0 ... NEVER GO TO SUPERVISOR MODE AND DO NOT MAKE OPERATING 
   SYSTEMS, to give you an idea, the beautiful WOC 92 demo by Sanity, due to 
   its operating system, does not work on 68040 .... and the same happened to 
   the Italian demo IT CAN'T BE DONE, by a friend of mine, and in this last 
   case it was me who found the error: the supervisor routine!!!
   
   All in all, I think it is easier and SAFER to use the SECTIONS to make 
   executable code, also because there is the advantage of being able to 
   install it on HardDisk, and surely in the next few years Amiga will have to 
   become more and more competitive with the MSDOS, and with this I mean that 
   the BASE computer must have the HardDisk and the FAST RAM, otherwise we 
   will be left with the few to enjoy the 1MB games that load from the disk in 
   autoboot, and that do not even exploit the processor speed by not loading 
   the code in FAST RAM!!

   Up to here I have indicated how it is better to make relocatable code, but 
   what happens if we use absolute addresses for an executable file that can 
   be loaded from dos? Well the introduction of the A500+, with 1MB of CHIP 
   has led to not running many of the "MIXED" code productions, that is with 
   relocatable code, created with the "SECTION", but with the use of buffers 
   for graphics NOT allocated through Section BSS or AllocMem, but arbitrarily 
   established:

	lea	$30000,a0	; Bitplane buffer address
	bsr.s	PrintText	; Print text at $30000

   In this case it is not the code that is not relocatable, but the graphics 
   buffer. Consequently there is not even the copperlist bitplane pointing 
   routine, because the value $30000 is put directly by the programmer: 
   (HORROR !!!)

	...
	dc.w	$e0,$0003	; bpl0pth
	dc.w	$e2,$0000	; bpl0ptl
	...

   Let's see what happens on old computers, those with 512k of CHIP and 512k 
   of FAST: assuming that the intro has a CODE section of 20k and a CHIP of 
   40k (containing the FONT of the characters and the music), the first 
   section is loaded in FAST and the second in CHIP, so you don't touch the 
   $30000 lease, but, say, $2000. In this case everything works, as long as 
   this intro is the first thing that is loaded from the dos.
   
   On the most recent machines, with 1MB or 2MB of chip ONLY, since there is 
   no FAST MEM, everything is loaded in CHIP, both the CODE section and the 
   other, in this way the last Kb of code (or music, graphics, etc. ) are 
   located beyond the address $30000. Imagine what a nice CRASH will happen 
   when the routine prints the characters above the code!
   
   The desperation of the "lightweight" coders, at the release of A500+ and 
   A600, was also that they could not correct the listings on these computers, 
   as the ASMONE itself was loaded into CHIP RAM, exceeding the $30000 or 
   $40000 used as a absolute buffer, so the JMP could after assembling could 
   work (IN SOME CASES), but at the exit the ASMONE found himself PERFORATED 
   by the routines and the CRASH was inevitable.
   
   This also taught intro makers to make a nice relocatable buffer:

	SECTION	BufferOK,BSS_C

	ds.b	10000

   To conclude the series of errors concerning the addresses, I now report 
   some errors that you would hardly have made, since they are illogical 
   actions, but for safety it is good to know that:

   - Some clever coders have sometimes used the high byte of the addresses to 
   write messages or simply for the sake of writing to us.
   You should know that 16-bit CPUs like 68000 or 68010 ignore the high byte 
   of an address, so do:

	JSR	$00005a00
	JSR	$00120d00
	JSR	$00c152b0
	JSR	$00013cd0

   it is equivalent to writing

	JSR	$C0005a00
	JSR	$DE120d00
	JSR	$FEc152b0
	JSR	$DE013cd0

  A "C0DE-FEDE" can be clearly read in the first bytes, which may be a message 
  left by an Emilio Fede coder of many years ago, who signed himself in this 
  way. Note that with hexadecimals many words can be formed (A, B, C, D, E, F, 
  and 0 as an "O"), for example:
  
  FEDE (faith), AFA (muggy), ABAC0 (abacus), FACCE (faces), F0CA (seal), CACCA 
  (poop), CADE (timber!), C0DE, ...
  
  So these smart guys left whole messages, poems, love letters in the high 
  byte of the addresses in the series of subroutines or in other places, where 
  those who disassembled could also read insults!
  
  This game lasted very short, fortunately, but the intro / demos that have 
  them do not work on 32-bit processors, in fact on these processors the 
  maximum addressing is increased, so the JSR really looks for those strange 
  locations. Among other things, you will have noticed how the FAST RAM of the 
  old A500 is at $00c00000, while that of the A4000 is at $07c00000, ie 
  outside the addressing range of a 68000.

  The last of the address errors, and no less unusual than the previous one, 
  is that of the 512k CHIP memory, which is "repeated" four times in the 
  address bus, in the sense that despite the fact that this is from $00000 to 
  $7ffff, It can also be accessed by trading on $80000-$FFFFF, or 
  $100000-$17ffff, or $180000-$1FFFFF.
  
  In practice, the $80000 bytes (512k), is 1/4 of the $200000 (2MB) of the 
  bus, and on OCS machines (old Amigas that could only address 512k of CHIP, 
  the rest FAST), each byte of CHIP memory is accessible by four different 
  addresses, spaced from each other by 512kb.
  
  This of course is a property that has been lost on ECS and AGA machines, ie 
  those that can address 1MB or more of memory.
  
  Let's take an example: if we write the value $12345678 at location $0, we 
  can "fish out" that value also from $0 + $80000, $0 + $80000 * 2, as well as 
  $0 + $80000 * 3 and $0 + $80000 * 4. Let's see a listing:

	move.l #$12345678,$0	; let's put this value in the first 4 bytes

	move.l	$80000,d0	; d0 = $12345678
	move.l	$100000,d1	; d1 = $12345678
	move.l	$180000,d2	; d2 = $12345678

   Reading from $80000, $100000 and $180000 is like reading from $0!!!!
   Unfortunately some fool has used this strange property for their routines, 
   and this causes the A500+ and a600 to malfunction in various ways, and mind 
   you that although the processor is still the 68000, the error also occurs 
   with kickstart 1.3 in ROM.

   So now you know all the addressing errors that have been made in the past. 
   See that you don't make them and don't invent new ones!!!!

	-	-	-	-	-	-	-	-	-

8) Issues with SR in 68010 and superior processors:
   
   One of the most frequent incompatibility problems between 68010 processors 
   and higher and 68000 is that of "MOVE SR,dest" instructions such as "MOVE 
   SR,d0" or "MOVE SR,$1234" or "MOVE SR,LABEL".
   
   In fact these instructions can normally be used on basic 68000 in user 
   mode,  like any other instruction: programs such as emulators (PC 
   Transformer, C64 Emulator etc.) do not work on 68010+ precisely because 
   they perform this operation in USER mode, which on 68010+ is no longer 
   possible and causes a "Privilege Violation" guru.
   
   Many games and demos also get stuck with the presence of this instruction 
   at the beginning of the routines that take over the system.
   
   Motorola decided to add to processors from 68010 onwards the possibility of 
   simulating the operation of new operating systems for machines not yet 
   available, this entailed the need to make the MOVE SR instruction, dest 
   privileged, ie executable only in supervisor mode. Otherwise the result is 
   a "Privilege Violation" GURU.

   To access the SR in user mode, however, the Motorola designers added the 
   instruction MOVE CCR,dest to be used instead of MOVE SR,dest, from 68010 
   processors onwards, which however was not available on 68000, so some 
   programs for the Amiga 68000 was written using the MOVE SR,dest instruction 
   in user mode, and now we verify this when a game or demo crashes on boot 
   with a sinister GURU MEDITATION or SOFTWARE FAILURE message on A1200 / 
   A3000 / A4000 or accelerated A2000.
   
   In reality, Motorola made the "mistake", as the unsuspecting who 
   confidently used MOVE SR,dest in user mode (USER) certainly did not expect 
   it to become an instruction to be executed only in supervisor mode! (after 
   a TRAP or an EXCEPTION of the processor).
   
   However, the important thing is to know about this, and now we can be sure 
   not to run into the problem, and this is possible by remembering to always 
   execute the instruction MOVE SR,dest in SUPERVISOR mode, that is after a 
   TRAP, or in an INTERRUPT, etc. This way the instruction will work on all 
   processors. Another solution could be to check what processor is on the 
   machine, and execute the appropriate code, ie a "MOVE SR,dest" on 68000, or 
   a "MOVE CCR,dest" on 68020, both in USER mode to avoid having to run them 
   in SUPERVISOR mode, but I think the fastest and most reasonable solution is 
   to always run the "MOVE SR,dest" in SUPERVISOR mode. Summarized:

	CPU		USER Mode		SUPERVISOR Mode

	68000		MOVE SR,dest		MOVE SR,dest
	68010/20/30/40	MOVE CCR,dest		MOVE SR,dest

   You will agree that it is better to always run the old "MOVE SR, dest" in 
   supervisor mode, this saves time and code.
   
   If instead the game / program / demo you are programming is intended only 
   for 68010+ processors, for example if the demo is AGA only, you can use the 
   new MOVE CCR,dest in user mode, since you are on a 68020+, but remember 
   also that such an instruction does not exist on 68000, so it is not 
   assembled by base 68000 assemblers, like this TRASH'M-One; in order to 
   assemble 68010+ instructions like this you must use the ASMONE TFA or the 
   DEVPAC 3.
   
   On the other hand, I really advise you to NEVER USE THIS INSTRUCTION, and 
   NEVER GO THE SUPERVISOR WAY ... what is it for? To take a risk?
   
   When an error of this type occurs, the number of the SOFTWARE FAILURE from 
   the operating system is # 80000008, it is not difficult to identify it.
   
   However, to program demos or games, acting on the SR register has no 
   fundamental use, so I HIGHLY recommend you NEVER access this register, also 
   because its bits are different from processor to processor, and it is very 
   easy to cause incompatibility problems.

	-	-	-	-	-	-	-	-	-

9) With the 68010, in addition to the MOVE CCR,SR command that we have seen, 
   other innovations have been introduced, which if not known can cause 
   incompatibility errors. This is the VBR, ie the VECTOR BASE REGISTER. We 
   saw this register in the lesson on interrupts, in fact we know that when an 
   interrupt or a trap occurs (instruction TRAP #xx), the processor INTERRUPTS 
   the reading of the program it was executing in USER mode, switches to 
   SUPERVISOR mode and executes the routine at address found in the specific 
   VECTOR, which can be one of the interrupt levels, or one of the TRAPs, and 
   so on.

   In the new processors, in addition to using vectors that had no function on 
   the 68000 (see, for example, $18 and $1c), the possibility of moving the 
   BASE of these vectors has been implemented.
   
   While on a 68000 we are sure that the VBLANK interrupt is always at $6c, 
   on a 68010 or higher we cannot be sure.
   
   This is because the base of these OFFSETs may no longer be $000000.
   
   In fact it is enough to execute, in supervisor, a "MOVEC d0,VBR", and 
   everything changes.
   
   Of course, at boot time the VBR is reset, so the vectors are all in the 
   same place as the 68000. It is the AmigaDos SetPatch command that moves the 
   VBR, usually to FAST RAM, copying the vector addresses to the new address. 
   Or the "move" is done by other utilities.
   
   In fact, therefore, once the WorkBench is loaded on a computer with 68010+ 
   it is very likely that the VBR is not at zero, so the old demos and games 
   (not just the old ones!), if loaded from the Shell or the WorkBench, often 
   they don't have the music or just hang, because they put interrupt routines 
   in $6c, when they should put it in VBR + $6c. So you should NEVER do such a 
   thing:

	MOVE.L	#IntRoutine,$6c

   First, you could "optimize" to:

	MOVE.L	#IntRoutine,$6c.w

   But the most important thing is that it only works if loaded at boot, 
   before running SetPatch or other utilities.
   
   To remedy the problem, just a few lines of code are enough to check if 
   there is a 68000 or a 68010+, and in the latter case they read the VBR 
   value to perform the necessary offsets.
   
   At the end of the program, just remember to do the same to fix the old 
   interrupt.
   
   This trick is present in the startup2.s used in the advanced course 
   listings. Note that the MOVEC VBR,A1 instruction, being 68010+, is not 
   assembled by all assemblers (including this ASMONE), so it is better to 
   insert it through its hexadecimal equivalent.
   
   In fact, nobody prevents you from writing "dc.w $4e75" instead of RTS!

	-	-	-	-	-	-	-	-	-

10)Now let's see the programming imprudences that have been made evident with 
   the introduction of INSTRUCTION CACHE on processors from 68020 onwards. By 
   "made evident" I mean that such errors lead to a scary system crash. 
   Fortunately, many of these errors can be resolved by disabling CACHE via 
   software utilities.

   Let's see briefly what the CACHE is: it is a very fast memory area that is 
   located INSIDE the processor rather than outside, in contrast to the CHIP 
   or FAST ram, which to be reached must pass through the bus.
   
   We have already seen the data and address REGISTERS, which are nothing but 
   LONGWORDs of internal memory to the processor, which we can read and write. 
   Well the CACHEs are similar memory banks, which however we cannot read or 
   write with instructions, they are read and written automatically by the 
   processor through a special hardware.
   
   The purpose of the caches is to speed up the LOOPs, that is the routines 
   that are cyclically executed many times. I state that on 68020 and 68030 
   the INSTRUCTION cache is 256 bytes, while on 68040 it is 4096 bytes.
   
   On 68060 I think it is at 8192, and in the future who knows...
   
   Well, imagine this loop:

	...
	MOVEQ	#100,d0
Loop1:
	move.w	LABEL1(PC),d2
	add.w	d3,d2
	....
	altre istruzioni
	....
	DBRA	d0,loop1
	...

   Even increasing the processor speed, this loop requires reading the 
   instructions between the label "loop1:" and the "DBRA d0,loop1" each cycle, 
   and reading from RAM, especially if it is CHIP RAM, is very slow.
   
   The Motorola designers then came up with this trick: "What if we 
   automatically cache the last 256 bytes that were executed?? We would get 
   that when a loop smaller than 256 bytes occurs, all the instructions in the 
   loop are in the CACHE and the processor can read it the remaining times 
   from the fast CACHE memory instead of RAM!". This is more or less how 
   Instruction CACHE works.
   
   The first loop would be read from the RAM only the first time then, once 
   reached the DBRA, the processor "realizes" that Loop1: is close enough to 
   always be contained in the CACHE, and the remaining 99 times the reading 
   time of the instructions is lowered notably being executed from CACHE 
   instead of CHIP / FAST RAM.

   You will ask yourself: but then what errors can occur?????
   The most common is that of those who have "timed" certain routines based on 
   the time it takes for the base 68000 to do a certain number of "empty" 
   loops (now they have a listing to throw away).
   
   Let's see how the "smart" ones managed to "waste time" waiting, for 
   example, for the heads of the disk drive to move, or to time a music, or 
   more:

	....
	MOVE.W	#2500,d0
Aspettatempo:
	dbra	d0,Aspettatempo
	...

   These examples of awkward and approximate programming are, unfortunately, 
   very frequent in track loaders and routines that play music. The "music.s" 
   routine in the course originally had a couple of these "empty loops", which 
   I promptly replaced with reliable "time wasters" routines, which we will 
   now look at.
   
   If you have replay routines for noisetracker / protracker you will almost 
   certainly find stupid loops of this type, which cause some notes to be lost 
   while listening to the tortured music.
   
   A note: also in the listings of Gerardo Proia's course there are stupid 
   loops of this type, I hope you have not taken them as an example!
   
   Look in the listings you find lying around for these damn loops, and if you 
   find them, throw out those crappy sources or replace those parts with 
   routines that use CIA or VBLANK, for timing.
   
   The operating principle of a no-load loop is that the processor must read, 
   in this case 2500 times, the DBRA instruction from memory, subtract #1 from 
   d0 and jump back to "Wait:".
   
   On a computer with active cache it can be read up to 50,000 times, but, 
   having finished in CACHE, the DBRA will run in a fraction of a second 
   anyway, consequently the drive does not read the tracks, and the music 
   "cuts" the notes. Besides, even the 68010 actually has a small 3 word CACHE 
   to speed up small DBRA loops like this one, so these loops only "work" on 
   68000 at 7Mhz.
   
   Since in this course we have NEVER learned to time with no-load loops, I 
   hope that no one will start doing such *CAZZATE* (not able to translate) on 
   their own.
   
   In general it can be said that NEVER take the speed of execution of 
   instructions by the 680x0 CPU as a reference, since it varies from 
   processor to processor, and even depending on what type of memory is read. 
   The only certainties, from the point of view of timing, are THE REFRESH 
   RATE OF VIDEO VBLANK, which in standard PAL will always be performed 50 
   times per second, and the timers of the CIA, which is a chip equal to all 
   Amigas, for which 1 millisecond is the same on an A500 as on an A4000. Be 
   careful not to rely on the speed of the blitter either, as it varies in 
   speed depending on the computer processor.

   Here's how to time trackloaders, signals for external peripherals or robot 
   arms connected to the parallel port:

   First let's see how to wait for some "raster lines" with VBLANK:

	LEA	$DFF000,A5		; Custom register base in a5
PerdiTempo:
	MOVE.w	#LINEE-1,D1	; NUMBER OF LINES to WAIT (304=1 frame)
VBWAITY:
	MOVE.B	6(A5),D0	; $dff006, VHPOSR.
WBLAN1:
	CMP.B	6(A5),D0	; VHPOSR
	BEQ.S	WBLAN1
WBLAN2:
	DBRA	D1,VBWAITY

   If you are not using the CIA timers for other routines, then you could use 
   them, although it is best to touch them as little as possible, since the 
   operating system uses them. In lesson 11 you will find sources on the 
   subject.
   
   When using the CIA timers, consider that the operating system also uses 
   some of them for certain purposes: (best to use the CIAB!)

   CIAA, timer A	Used for interfacing with the keyboard

   CIAA, timer B	Used by the exec to switch tasks etc.

   CIAA, TOD		50/60 Hz timer used by the Timer.device

*  CIAB, timer A	Not used, available for programs

*  CIAB, timer B	Not used, available for programs

   CIAB, TOD		Used by the graphics.library to follow the positions 
			of the electron brush.

   If you must use timers that also serve the operating system, do so only if 
   you have disabled multitasking and system interrupts, that is, if you have 
   taken complete control of the system. However, it is always more dangerous 
   to use the CIA than the vblank, because at the exit from our production if 
   we have busted a timer who knows what could happen.

		-		-		-		-

   In addition to the problem of timing loops, there is also that given by 
   another programming flaw, which however has almost disappeared nowadays, 
   fortunately. It is the legendary and mysterious "SELF-MODIFYING" code: this 
   type of code is called self-modifying precisely because it modifies itself. 
   In fact, it is possible to make "creatures" that, in addition to modifying 
   data, also modify their instructions during execution.
   
   Unfortunately, this type of programming has been used since ancient times, 
   probably because it seemed like a faster or more powerful way to write 
   code. In reality, what is done with self-modifying code can be rewritten 
   with 100% normal code and, at times, you can gain speed. So forget to write 
   code of this type, if not for experimental purposes, because this code does 
   not work with the active 68020/30/40/60 CACHE. Anyone who has an A1200 
   certainly realizes how many games and demos are not working just because of 
   the caches! In fact, I believe most errors in old software are of this 
   type. To recognize a game or demo that has self-modifying code, just try if 
   it works by removing the caches (without loading old kicks), then try it 
   again with the caches enabled. If that doesn't work at this point, it's 
   obvious that it's only active caches that are causing the problem, and 
   caches only cause two types of errors: that of the cancellation of the DBRA 
   delay cycles, which we have already seen, and those due to the 
   self-modifying code.
   
   Here's how a listing containing self-modifying code can be presented:

	...
	divu.w	#3,d0
MYLABEL:
	moveq	#0,d0
	...

   Which is assembled in memory like this:

	...
	dc.l	$80FC0003	; DIVU.W #$0003,D0
MYLABEL:
	dc.w	$7000		; MOVEQ	 #$00,D0
	...

   For now we have modified some data in memory, in the copperlists, we have 
   put values in the custom registers $ dffXXX, but we have never acted INSIDE 
   an instruction!!! This is precisely because it is an INCOMPATIBLE thing.
   
   (We actually modified a JMP at the end of an interrupt in the DOS loading 
   listing in Lesson 11.txt, but that's the only "useful" case!).
   
   Imagine that, later in the listing, there is this instruction:

	...
	move.w	#5,MYLABEL-2
	...

   What happens? The word before the MYLABEL label is the $0003 of the DIVU 
   #3,d0, which becomes $0005, so the DIVU #3,d0 becomes DIVU #5,d0!!!
   All other instructions can be changed in the same way. In another way you 
   can write:

	...
	divu.w	#3,d0
MYLABEL:	EQU	*-2
	moveq	#0,d0
	...

   Now to modify the DIVU number just do a MOVE.W #xxxx,MYLABEL, in fact 
   through the EQU *-2 you make MYLABEL correspond with the label-2.
   
   the asterisk can be translated into "this point", so "*-2" becomes "this 
   point minus 2 bytes". Now suppose we have this situation in a listing with 
   self-modifying code:

	...
	divu.w	#0,d0	; to be changed to the desired number
MYLABEL:
	EQU	*-2
	...

   Imagine what happens when CACHE is active: the instruction goes into the 
   cache as it is in memory, i.e. DIVU.W #0,d0, probably before it is 
   modified, so at the time this instruction is executed:

	move.w	#5,MYLABEL

  the DIVU instruction is changed in RAM, but not in CACHE! In fact this 
  instruction will be executed as it was, ie DIVU.W #0,d0, which causes a nice 
  system crash for DIVISION BY ZERO!!

	divu.w	#0,d0	; the value "0" will be replaced before this 
MYLABEL:		; instruction is executed, but with the CACHE active 
	EQU	*-2	; this instruction will be read from the cache as it
			; was originally, and a nice DIVISION BY ZERO guru
			; will stop our fun.
	
  Similarly a:

	JMP	0	; the address will be put in this point by a move, but 
MYLABEL:		; with the cache we will have a nice JUMP to 0! (guru!)
	EQU	*-6	; (EQU *-4, EQU *-8, depending on the size of the 
			; address or the stupid "modification".


   In some cases instead of a real GURU there are problems of malfunction of 
   the routines, for example a loop of this type:

	...
	MOVE.W	#100,d0
Loop1:
	...
	divu.w	#2,d2
MYLABEL:
	EQU	*-2
	....
	addq.w	#1,MYLABEL
	DBRA	d0,loop1
	...

   There is no system crash, but while the divu.w each loop should change to 
   DIVU.W #3,d2, DIVU.W #4,d2 etcetera, instead it always remains DIVU.W #2,d2 
   (being read from the CACHE and not from RAM).
   
   For example, if this was a routine that made a 3d solid move, rest assured 
   that the solid will stay still or won't even show up.

- HOW WE SHOULD DO IT:

   The same thing could have been done dividing by a LABEL or a REGISTER 
   instead of an absolute value (divu.w LABEL(PC),d2 instead of divu.w 
   #xxx,d2), and then the add could have been made on LABEL, maintaining 
   compatibility with caches and without losing speed.
   
   So DON'T BE STUPID: making self-modifying code is not something to boast 
   about, because it is neither difficult nor useful, but it is incompatible.
   
   Please don't use old listings that have self-modifying code.
   
   However, there is a way, in addition to disabling the caches, to make the 
   self-modifying code work: you can in fact CLEAR the cache with a specific 
   instruction, in this way the old instruction will be deleted from the CACHE 
   and the processor will have to read the modified one from RAM.
   
   If you want to make "bacterial" or artificial intelligence programs or 
   whatever kind, which self-modify, just put a "BSR.w CACHECLR" before 
   executing the modified instruction.
   
   On kickstart 2.0+ there is a special function of the ExecLib:

ClearMyCache:
	movem.l	d0-d7/a0-a6,-(SP)
	move.l	4.w,a6
	MOVE.W	$14(A6),D0	; lib version
	CMP.W	#37,D0		; is it V37+? (kick 2.0+)
	blo.s	nocaches	; If it is kick1.3, the problem is that it 
				; can't even know if it's a 68040, so it's 
				; risky .. and hopefully a fool who has a 
				; 68020+ on a kick1.3 also has caches disabled!
	jsr	-$27c(a6)	; cache cleaR U (for loads, modifications etc.)
nocaches:
	movem.l	(sp)+,d0-d7/a0-a6
	rts

   In startup2.s there is this subroutine, which can be executed.
   Note: if we are on kick 1.3 it comes out without doing the JSR, in this way 
   there are no problems on old computers.
   
   Also run this routine after loading data into memory with a trackloader, or 
   after making other changes to areas of memory containing code. Beware that 
   sometimes a program with self-modifying code can run by chance on 
   68020/68030, because there are more than 256 bytes of distance between the 
   modified instruction and the one it modifies, so the instruction is read 
   from RAM, but on A4000 the cache is 4096 bytes, and who knows how much can 
   be increased on future processors! So NEVER fall into self-modification, as 
   some A1200 demo coders continue to do, only getting their creatures not 
   working on A4000.

   In the 68030 and 68040 processors there is also the DATA CACHE, which does 
   the same thing as the INSTRUCTION CACHE, but on the data (such as tables), 
   but only if this data is in FAST RAM. The DATA cache does not act on CHIP 
   RAM.
   
   Errors with the DATA CACHE are less frequent, in fact it is difficult for 
   tables to be changed, for example. However for safety you can do a 
   CACHECLR, also because on 68040 there is copyback, an "enhancement" of the 
   DATA CACHE which is really bad, so much so that even some programs compiled 
   in the C language do not work.
   
   So get yourself a "ClearMyCache" every now and then, which doesn't hurt.

	-	-	-	-	-	-	-	-	-

11)Another incompatibility problem I encountered is that of interrupts on 
   A4000. I noticed that many demos, even AGA, which worked great on the 
   A1200, on the A4000 played music at double speed, jerky and sometimes 
   crashing the system. I realized that this was due to an oversight, which 
   like all oversights are noticed when the code runs on A4000.

   Take a look at this level 3 interrupt (the $6c so to speak):

INTERRUPT:
	MOVEM.L	D0-D7/A0-A6,-(SP)
	BSR.W	ROUTINE1
	BSR.W	ROUTINE2
	BSR.W	ROUTINE3
	BSR.W	ROUTINE4
	MOVEM.L	(SP)+,D0-D7/A0-A6
NOINT:
	move.w	#$20,$dff09c	; INTREQ - vertb (bit 5 - $20 = %100000)
	rte
	
   What's wrong?? Note that it has done well in VBR+$6c, so it runs smoothly.
   
   The solution is:	NO BIT TEST IN INTREQR!!!!

   Here's how it needs to be changed:

INTERRUPT:
	btst.b	#5,$dff01f	; INTREQR - vertb int? (bit5)
	beq.s	NOINT		; not a true VERTB interrupt
	MOVEM.L	D0-D7/A0-A6,-(SP)
	BSR.W	ROUTINE1
	BSR.W	ROUTINE2
	BSR.W	ROUTINE3
	BSR.W	ROUTINE4
	MOVEM.L	(SP)+,D0-D7/A0-A6
NOINT:
	move.w	#$20,$dff09c	; INTREQ - vertb (bit 5 - $20 = %100000)
	rte

   Actually very often on A500/A1200 the interrupt works well even without the 
   bit control in INTREQR, but on A4000 it must ALWAYS be put, otherwise the 
   interrupt is executed a billion times too many.
   
   So NEVER, NEVER forget to test the bits that generated an interrupt in 
   INTREQR! As we have already seen, for each interrupt level there is a bit 
   of the $dff01f (INTREQR) to test.

   A reminder:

   INT $64	LEVEL1	bits 0 (soft) ,1 (dskblk) ,2 (serial port tbe)

   INT $68	LEVEL2	bit 3 (ports)

   INT $6c	LEVEL3	bits 4 (copper) ,5 (verticalblank) ,6 (blitter)

   INT $70	LEVEL4	bits 7 (aud0) ,8 (aud1) ,9 (aud2) ,10 (aud3)

   INT $74	LEVEL5	bits 11 (serial port rbf) ,12 (disksyn)

   INT $78	LEVEL6	bit 13 (external int)

   Remember to always do the btst at the beginning of the interrupt, and in 
   case the bit is not set (beq) exit without running the routines.
   
   At the exit it is always necessary to act on the $dff09c to remove the 
   interrupt request, in practice we "signal" that the interrupt has been 
   executed.
   
   Be careful not to make the mistake, which I have made in the past, of 
   making, for example, a "BTST #11,$dff01f". In this case, bit 11-8 is 
   actually tested, ie bit 3 of the $dff01f. You will remember the story of 
   the waitblit, whereby writing btst "#6,$dff002" is the same as writing 
   "btst #14,$dff002". In fact, some assemblers also assemble BTST 
   instructions on addresses with a number of bits greater than 7, although 
   they are useless since it scales the number of such bits by 8.
   
   Other assemblers, such as Devpac 3, give errors and do not allow to 
   assemble such useless BTST.b. (NOTE the .BYTE! = 0 to 7 max!)

	-	-	-	-	-	-	-	-	-

- WOE TO ANYONE WHO MAKES ONE OF THE MISTAKES DESCRIBED IN THIS LESSON!!!!!!!!!

