|
|
(3 közbenső módosítás ugyanattól a szerkesztőtől nincs mutatva) |
1. sor: |
1. sor: |
| AMIGA MACHINE LANGUAGE
| |
| Typed by DEE JAY | | Typed by DEE JAY |
|
| |
|
| | Wiki-ified by Charlie / Singular Crew, (a.k.a [[User:Chain-Q|Chain-Q]]) |
|
| |
|
| | == Disclaimer == |
|
| |
|
| Table of Contents.
| | '''IMPORTANT:''' ''Please note that this is a very old tutorial. Every statement in this text refers to [[AmigaOS]] 1.x, the [[OCS]] chipset and the plain 68000 [[CPU]]. It does not cover current [[AmigaOS]] versions, 68020 and newer processors, [[ECS]] and [[AGA]] chipsets. It might make statements and recommendations which are considered obsolete, wrong or even strictly forbidden on newer chipset revisions and newer OS releases.'' |
| ------------------
| | |
| 1. Introduction
| | == Contents == |
| 1.1 Why machine code?
| | |
| 1.2 A look into the Amiga's memory
| | * [[Amiga_Machine_Language_(Chapter_1)|Chapter 1 - Introduction]] |
| 1.2.1 RAM,ROM,hardware register
| | * [[Amiga_Machine_Language_(Chapter_2)|Chapter 2 - The MC68000 processor]] |
| 1.2.2 Bits,bytes and words
| | * [[Amiga_Machine_Language_(Chapter_3)|Chapter 3 - Working with assemblers]] |
| 1.2.3 Number systems
| | * [[Amiga_Machine_Language_(Chapter_4)|Chapter 4 - Our first programs]] |
| 1.3 Inside the Amiga
| | * [[Amiga_Machine_Language_(Chapter_5)|Chapter 5 - Hardware registers]] |
| 1.3.1 Components and libraries
| | * [[Amiga_Machine_Language_(Chapter_6)|Chapter 6 - The Amiga operating system]] |
| 1.3.2 Memory
| | * [[Amiga_Machine_Language_(Chapter_7)|Chapter 7 - Working with Intuition]] |
| 1.3.3 Multi-tasking
| | * [[Amiga_Machine_Language_(Chapter_8)|Chapter 8 - Advanced programming]] |
| | * [[Amiga_Machine_Language_(Appendix_A)|Appendix A - Overview of library functions]] |
| | * [[Amiga_Machine_Language_(Appendix_B)|Appendix B - Overview of the MC68000 Instructions]] |
| | |
| | |
| | Original contents table, will be gone as chapters will get Wiki formatting: |
|
| |
|
| 2 The MC68000 processor | | 2 The MC68000 processor |
87. sor: |
93. sor: |
| 8.1 Supervisor mode | | 8.1 Supervisor mode |
| 8.2 Exception programming | | 8.2 Exception programming |
|
| |
| Appendix
| |
| Overview of library functions
| |
| Overview of the MC68000 Instructions
| |
|
| |
|
| |
|
| |
|
| |
|
| |
| Chapter 2.
| |
| ---------
| |
| 2.The MC68000 Processor.
| |
| -----------------------
| |
| The Amiga`s MC68000 processor is a 16/32 bit processor,which means
| |
| that while it can process data of 32 bits,it"only"has a 16 bit
| |
| data bus and a 24 bit address bus.Thus,it can access 2^24=16777216
| |
| bytes(or 16 Mbytes)of memory directly.
| |
| 7.1 Megaherz;
| |
| The Amiga 68000 processor,running at 7.1 megaherz,is quite fast,
| |
| which is required for a computor with a workload as heavy as the
| |
| Amiga`s.The Amiga also processes a number of custom chips that
| |
| greatly ease the workload of the processor.These custom chips
| |
| manage sound in/output,graphics and animation,thus freeing the
| |
| processor for calculations.
| |
|
| |
| 2.1.Registers.
| |
| -------------
| |
| In addition to the standard RAM,the processor contains internal
| |
| memory called registers.There are eight data registers(D0-D7),
| |
| eight address registers(A0-A7),a status register(SR),two stack
| |
| pointers,a user stack pointer,a system stack pointer(USP and SSP)
| |
| and the program counter(PC).
| |
| Register Sizes;
| |
| The data registers,the address registers,and the program counter
| |
| are all 32 bits,while the status register is 16 bits.These
| |
| registers are located dirctly in the processor so they are not
| |
| accessed the same way memory would be accessed.There are special
| |
| instructions for accessing these registers.
| |
| Data Registers;
| |
| The data registers are used for all kinds of data.They can handle
| |
| operations with bytes(8 bits)words(16 bits)and longwords(32bits).
| |
| Address Registers;
| |
| The address registers are used for storing and processing
| |
| addresses.This way they can be used as pointers to tables,in which
| |
| case only words and longwords operations are possible.
| |
| Stack Pointer;
| |
| The address register A7 plays a special role:this register is
| |
| utilized as the Stack Pointer(SR)by the processor,and thus is not
| |
| recommended for normal use by the programmer.Which of the two
| |
| possible stacks is being pointed to depends on the present mode of
| |
| the processor,but more about that later.
| |
| The stack,to whose actual position the stack pointer is pointing,
| |
| is used to store temporary internal data.The stack works similar
| |
| to a stack of notes on your desk:the note that was added to the
| |
| stack last is the first one to come off the stack.This type of
| |
| stack is known as LIFO(Last in,First out).There is another type of
| |
| stack,the FIFO(First in,First out)which is not used by the
| |
| processor itself.
| |
| How these registers and the SP can be manipulated,or how to work
| |
| with the stack,is presented in the next chapter.Lets continue with
| |
| the registers for now.
| |
| Status Register;
| |
| The status register plays an important role in machine language
| |
| programming.This 16-bit quality(word)contains important
| |
| information about the processor status in 10 of its bits.The word
| |
| is divided into two bytes,the lower byte(the user byte)and the
| |
| upper byte(the system byte).The bits that signify that certain
| |
| conditions are refered to as flags.This means that when a certain
| |
| condition is present,a particular bit is set.
| |
| The user byte contains five flags,which have the following meaning
| |
|
| |
| Bit Name Meaning
| |
| -----------------------------------------------
| |
| 0 (C,Carry) Carry bit,modified by math
| |
| calculation,and shift instructions.
| |
| 1 (V,Overflow) Similar to carry,indicates a change
| |
| of sign,in other words,a carry from
| |
| bit six to bit seven.
| |
| 2 (Z,Zero) Bit is set when the result of an
| |
| operation is zero.
| |
| 3 (N,Negative) Is set when the result of an
| |
| operation is negative.
| |
| 4 (X,Extended) Like carry,is set for arithmetic
| |
| operations.
| |
| 5-7 Not used.
| |
|
| |
| The system byte contains five significant bits:
| |
|
| |
| Bit Nane Meaning
| |
| -----------------------------------------------
| |
| 8 I0 Interupt mask.Activates interupt
| |
| 9 I1 levels 0 to 7,where 0 is the lowest
| |
| 10 I2 and 7 is the highest priority.
| |
| 11 not used.
| |
| 12 not used.
| |
| 13 (S,Supervisor) This bit indicates the actual
| |
| pocessor mode(0=User,1=Supervisor
| |
| mode).
| |
| 14 not used.
| |
| 15 (T,Trace) If this bit is set,the processor is
| |
| in single step mode.
| |
|
| |
| Here's an overview of the status word;
| |
|
| |
| bit : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
| |
| name : T - S - - I2 I1 I0 - - - X N Z V C
| |
|
| |
| Don't let the new terms,like mode and interupt confuse you.We'll
| |
| talk about these in greater detail in the chapter dealing with the
| |
| operating conditions of the processor.
| |
|
| |
| 2.2.Addressing Memory.
| |
| ---------------------
| |
| In the standard Amiga 500's and 1000's,the processor has over 512k
| |
| of RAM available.The Amiga 2000 has one mega-byte of RAM that can
| |
| be accessed by the processor.How does the processor access all
| |
| this memory?
| |
| If you're programming in BASIC you don't have to worry about
| |
| memory management.You can simply enter MARKER%=1,and the value is
| |
| stored in memory by the BASIC interpreter.
| |
| In assembler,there are two ways of accomplishing this task:
| |
| 1) Store the value in one of the data or address registers,or
| |
| 2)Write it directly into a memory location.
| |
| To demonstrate these two methods let's get a little ahead and
| |
| introduce a machine language instruction,which is probably the
| |
| most common:MOVE.As its name states,this instruction moves values.
| |
| Two parameters are required with the instruction:source and
| |
| destination.
| |
| Lets see what the example from above would look like if you
| |
| utilize the MOVE instruction...
| |
| 1) MOVE #1,D0
| |
| This instruction moves the value 1 into data register D0.As you
| |
| can see,the source value is always entered before the destination.
| |
| Thus,the instruction MOVE D0,#1 is not possible.
| |
| 2) MOVE #1,$1000
| |
| deposits the value 1 in the memory location at $1000.This address
| |
| was arbitrarily chosen.Usually addresses of this form won't be
| |
| used at all in assembler programs,since labels that point to a
| |
| certain address are used instead.Thus,the more common way of
| |
| writing this would be:
| |
| ...
| |
| MOVE #1,MARKER
| |
|
| |
| ...
| |
| MARKER:DC.W 1
| |
|
| |
| These are actually two pieces of program:the first part executes
| |
| the normal MOVE instruction whose destination is `MARKER`.This
| |
| label is usually defined at the end of a program and specifies the
| |
| address at which the value is stored.
| |
| The paraneter DC.W 1 is a pseudo op,apseudo operation.This means
| |
| that this isn`t an instruction for the processor,but an
| |
| instruction for the assembler.The letters DC stand for`DeClare`and
| |
| the suffix .W indicates that the data is a Word.The other two
| |
| suffix alternatives would be .B for a byte(8 bits)and .L for a
| |
| long word(32 bits).
| |
| This suffix(.B.W or.L)is used with most machine language
| |
| instructions.If the suffix is omitted,the assembler uses .W(word)
| |
| as the default parameter.If you wanted a long word,you`d use an
| |
| instruction that looks something like this:MOVE .L #$1234678,D0
| |
| where as an instruction like MOVE.B #$12,D0 would be used for a
| |
| byte of data.However,with this instruction there`s one thing you
| |
| must be aware of...
| |
| CAUTION:
| |
| If the memory is accessed by words or long words,the address must
| |
| be even(end digit must be 0,2,4,6,8,A,C,E)!
| |
| Assemblers usually have a pseudo-op,`EVEN`or`ALIGN`,depending on
| |
| the assembler,that aligns data to an even address.This becomes
| |
| necessary in situations similar to this:
| |
| ...
| |
| VALUE1: DC.B 1
| |
| VALUE2: DC.W 1
| |
|
| |
| If the VALUE1 is located at an even address,VALUE2 is automaticaly
| |
| located at an odd one.If an ALIGN(EVEN)is inserted here,a fill
| |
| byte(0)is inserted by the assembler,thus making the second address
| |
| even.
| |
| ...
| |
| VALUE1: DC.B 1
| |
| ALIGN
| |
| VALUE2: DC.W 1
| |
|
| |
| Back to the different ways of addressing.The variations listed
| |
| above are equivalent to the BASIC instruction MARKER%=1 where the
| |
| % symbol indicates an integer value.
| |
| Lets go a step further and translate the BASIC instruction MARKER
| |
| %=VALUE% into assembler.You`ve probably already guessed the answer
| |
| right?
| |
|
| |
| MOVE VALUE,MARKER
| |
| ...
| |
| ...
| |
| MARKER: DC.W 1
| |
| VALUE : DC.W 1
| |
|
| |
| In this case,the contents in the address at VALUE are moved into
| |
| the address at MARKER.
| |
| With the help of these simple examples,you`ve already become
| |
| familiar with four different ways of addressing,in other words,
| |
| ways that the processor can access memory.The first is
| |
| characterized by the number sign(#)and represents a direct value.
| |
| Thus,this method is also known as direct addressing,and is legal
| |
| only for the source parameter!
| |
| A further method in which a direct address(in our case,`MARKER`and
| |
| `VALUE`)can be specified is known as absolute addressing.This
| |
| method is legal for the source parameter as well as for the
| |
| destination parameter.
| |
| This method can be divided into two different types,between which
| |
| the programmer usually does'nt notice a difference.Depending on
| |
| whether the absolute address is smaller or larger than $FFFF,in
| |
| other words if it requires a long word,it is called absolute
| |
| addressing(for addresses above $FFFF)or otherwise absolute short
| |
| addressing.The assembler generally makes the distinction between
| |
| these two types,and thus,only general knowledge of absolute
| |
| addressing is required.
| |
| The fourth method of addressing that you've encountered so far is
| |
| known as DATA REGISTER DIRECT.It was the first one introduced(MOVE
| |
| #1,D0)in conjunction with direct addressing,the only difference
| |
| being that this type accesses a data register(such as D0).
| |
| These four methods aren't the only ones available to the MC68000
| |
| processor,in fact there are a total of 12.One other variation
| |
| called ADDRESS REGISTER DIRECT,is almost identical to data
| |
| register direct,except that it accesses the address register
| |
| instead of the data register.Thus,you can use MOVE.L #MARKER,A0 to
| |
| access the address register A0 directly.
| |
| You now know five ways to address memory with which quite a bit
| |
| can be accomplished.Now,lets tackle something more complicated and
| |
| more interesting.
| |
| Lets take another example from BASIC:
| |
|
| |
| 10 A=1000
| |
| 20 POKE A,1
| |
|
| |
| In this example the first line assigns the value 1000 to the
| |
| variable A.This can be done in assembler as well:MOVE.L #1000,A0.
| |
| In the assembler version the absolute value of 1000 ia stored in
| |
| the address register A0.
| |
| Line 20 doesn't assign a value to the variable A itself,but rather
| |
| to the memory location at the address stored in A.This is an
| |
| indirect access,which is quite easy to duplicate in assembler:
| |
|
| |
| MOVE.L #1000,A0 ;bring address in A0
| |
| MOVE #1,(A0) ;write 1 into this address
| |
|
| |
| The parentheses indicates an addressing known as ADDRESS REGISTER
| |
| INDIRECT.This method only works with address registers,since a
| |
| 'data register indirect' does not exist.
| |
| There are several variations of this method.For instance,a
| |
| distance value can be specified,which is added to the address
| |
| presently located in the address register before the register is
| |
| actually accessed.The instruction MOVE #1,4(A0),if applied to the
| |
| example above,writes the value 1 into the memory cell at 1000+4=
| |
| 1004.This distance value can be positive or negative.Thus,values
| |
| from -32768 to +32768 are accepted.This specific variation of
| |
| addressing is called ADDRESS REGISTER INDIRECT WITH A 16 BIT
| |
| DISPLACEMENT value.
| |
| There is another very similar variation:ADDRESS REGISTER INDIRECT
| |
| WITH AN 8 BIT INDEX value.While this variation is limited to 8
| |
| bits,it also brings another register into play.This second
| |
| register is also a distance value,except that it is a variable as
| |
| well.
| |
| We'll try to clarify this with an example.Lets assume that a
| |
| program includes a data list that is structured like this:
| |
|
| |
| ...
| |
| RECORD: DC.W 2 ;number of entries -1
| |
| DC.W 1,2,3 ;elements of list
| |
|
| |
| We'll use MOVE.L #RECORD,A0 to load the list into the address
| |
| register A0.Then you can use MOVE (A0),D0 to pull the number of
| |
| in the list into the data register.To access the last element of
| |
| the listonly one instruction is needed.The whole thing looks
| |
| something like this:
| |
|
| |
| CLR.L D0 ;erase D0 completely
| |
| MOVE.L #RECORD,A0 ;address of list in A0
| |
| MOVE (A0),D0 ;number of elements -1 in D0
| |
| MOVE 1(A0,D0),D1 ;last element in D1
| |
| ...
| |
| RECORD: DC.W 2 ;number of entries -1
| |
| DC.W 1,2,3 ;elements of list
| |
|
| |
| This last instruction accesses the byte that is located at 1+A0+D0
| |
| in other words,the record +1 where the data begins plus the
| |
| contents of D0(in this case 2).
| |
| This method of accessing is very useful.It works exquisitely for
| |
| the processing of tables and lists,as the example demonstrates.If
| |
| no distance value is needed,simply use a distance value of zero,
| |
| which some assemblers automatically insert as the default value if
| |
| for instance only MOVE (A0,D0)is entered.
| |
| The latter two methods have a third variation,which as its own
| |
| characteristic trait.It dosen't utilize an address register,but
| |
| uses the Program Counter(PC)instead.The program counter with
| |
| displacement method proves useful when a program must function
| |
| without any changes in all address ranges.The following two
| |
| statements(in the 15 bit limits)have the same effect:
| |
|
| |
| MOVE MARKER,D0
| |
|
| |
| and
| |
|
| |
| MOVE MARKER(PC),D0
| |
|
| |
| This method is actually rather imprecise,since the first
| |
| instruction specifies the actual address of the marker with MARKER
| |
| while the second line specifies the distance between the
| |
| instruction and the marker.However since it would be quite
| |
| cumbersome to constanly calculate the distance,the assembler takes
| |
| this task off our hands and calculates the actual value automatic.
| |
| Lets examine the difference between the two instructions.In a
| |
| program they'll accomplish the same thing,although they are
| |
| interpreted as two completely different things by the assembler.
| |
| You'll assume a program being at the address $1000 and the marker
| |
| is located at $1100.The generated program code looks something
| |
| like this:
| |
|
| |
| $001000 30 39 00 00 11 00 MOVE MARKER,D1
| |
|
| |
| or
| |
|
| |
| $001000 30 3A 00 FE MOVE MARKER(PC),D1
| |
|
| |
| As you can see,the generated code of the second line is two bytes
| |
| shorter than the first line.In addition,if you were to shift this
| |
| code to the address $2000,the first version still accesses the
| |
| memory at $1100,while the second line using the PC indirect
| |
| addressing accesses memory at $2000 correctly.Thus,the program can
| |
| be transferred to almost any location.
| |
| This,then,is PROGRAM COUNTER WITH 16 BIT DISPACEMENT value.As we
| |
| mentioned,there is also PROGRAM COUNTER WITH 8 BIT INDEX value,
| |
| which permits a second register as a distance value,also known as
| |
| offset.
| |
| There are two addressing modes left.These two are based on
| |
| indirect addressing.They offer the capability of automatically
| |
| raising or lowering the address register by one when memory is
| |
| accessed with address register indirect.
| |
| To automatically increase the register,you'd use ADDRESS REGISTER
| |
| DIRECT WITH POST-INCREMENT.The address register raises this by the
| |
| number of bytes used AFTER accessing memory.Thus if you write:
| |
|
| |
| MOVE.L #1000,A0
| |
| MOVE.B #1,(A0)+
| |
|
| |
| the 1 is written in the address $1000 and then A0 is raised by one
| |
| Instructions like this are very helpful when a memory range is to
| |
| be filled with a specific value(for instance when the screen is
| |
| cleared).For such purposes the instruction can be placed in a loop
| |
| ...which we'll get to later.
| |
| The counter part to post increment is ADDRESS REGISTER WITH PRE-
| |
| DECREMENT.In this case the specified address register is lowered
| |
| by one BEFORE the access to memory.The instructions:
| |
|
| |
| MOVE.L #1000,A0
| |
| MOVE.B #1,-(A0)
| |
|
| |
| writes 1 in the address 999,since the contents of A0 is first
| |
| decremented and the 1 is written afterwards.
| |
| These two methods of addressing are used to manage the stack
| |
| pointer(SP).Since the stack is filled from top to bottom,the
| |
| following is written to place a word(s.aD0)on the stack:
| |
|
| |
| MOVE.B D0,-(SP)
| |
|
| |
| and to remove it from the stack,again in D0:
| |
|
| |
| MOVE.B (SP)+,D0
| |
|
| |
| This way the stack pointer always points to the byte last
| |
| deposited on the stack.Here,again,you'll have to be careful that
| |
| an access to the stack with a word or a long word is always on an
| |
| even address.Thus,if you're going to deposit a byte on the stack,
| |
| either use a whole word or make sure the byte is not followed by a
| |
| JSR or BSR.The JSR or BSR instructions deposit the addresses from
| |
| which they stem on the stack in the form of a long word.
| |
| In the code above,the SP is generally replaced by the address
| |
| register A7 in the program code,since this register is always used
| |
| as the SP.Thus,you can write A7 as well as S,the resulting program
| |
| is the same.However,we recommend the use of SP,since this makes
| |
| the code somewhat easier to read.After all,quite often you'll want
| |
| to employ your own stacks,in which case the difference between the
| |
| processor stack and your own stacks become very important.
| |
| These are the twelve ways of addressing the MC68000 processor,Here
| |
| is a summary:
| |
|
| |
| No Name Format
| |
| -- ---- ------
| |
| 1 data register direct Dn
| |
| 2 address register direct An
| |
| 3 address register indirect (An)
| |
| 4 address register indirect with post-increment (An)+
| |
| 5 address register indirect with pre-decrement -(An)
| |
| 6 address register indirect with 16 bit displacement d16(An)
| |
| 7 address register indirect with 8 bit index value 8(An,Rn)
| |
| 8 absolute short xxxx.W
| |
| 9 absolute long xxxxxxxx.L
| |
| 10 direct #'data'
| |
| 11 program counter indirect with 16 bit displacement d16(PC)
| |
| 12 program counter indirect with 8 bit index value d8(PC,Rn)
| |
|
| |
| The abbreviations above have the following meanings:
| |
|
| |
| An address registers A0-A7
| |
| Dn data registers D0-D7
| |
| d16 16 bit value
| |
| d8 8 bit value
| |
| Rn register D0-D7,A0-A7
| |
| 'data' up to 32 bit value,(either .B .W .L)
| |
|
| |
| These are the addressing modes used by the MC68000 processor.The
| |
| bigger brother of this processor,the 32 bit MC68020,has six more
| |
| methods which we won't discuss here.
| |
| Next you're going to see under what conditions the processor can
| |
| operate.
| |
|
| |
| 2.3.Operating Modes.
| |
| -------------------
| |
| In the previous section about registers you encountered the Status
| |
| Register(SR).The individual bits of this register reflect the
| |
| present operating condition of the processor.You differentiated
| |
| between the system byte(bits 8-15)and the user byte(bits 0-7).Now,
| |
| lets take a closer look at the system byte and its effects upon
| |
| the operation of the processor.
| |
|
| |
| 2.3.1.User and Supervisor Modes.
| |
| -------------------------------
| |
| Isn't it rather strange that the processor classifies you either
| |
| as a'user'or a 'supervisor'?Both of these operating modes are
| |
| possible,the user mode being the more common mode.In this mode it
| |
| is impossible to issue some instructions,and that in your own
| |
| computor!
| |
| Don't worry though,you can get around that,as well.The Amiga's
| |
| operating system contains a function that allows us to switch the
| |
| processor from one mode to the other.
| |
| The mode is determined by bit 13 of the Status Register.Usually
| |
| this bit is cleared(0),indicating that the processor is in user
| |
| mode.It is possible to write directly into the status register,
| |
| although this is a privileged instruction that can only be
| |
| executed from the supervisor mode.Thus,this instructioncould only
| |
| be used to switch from the supervisor mode into the user mode,by
| |
| using AND #$DFFF,SR to clear the supervisor bit.However,it is
| |
| quite preferable to let the operating system perform the switch
| |
| between these two modes.
| |
| Now what differentiates these two modes in terms of application?
| |
| Well,we already mentioned the first difference:some instructions,
| |
| such as MOVE xx,SR, are privileged and can only be executed from
| |
| the supervisor mode.An attempt to do this in user mode would
| |
| result in an exception and interruption of the program.Exceptions
| |
| are the only way of switching to the supervisor mode,but more
| |
| about later.
| |
| A further difference is in the stack range used.Although A7 is
| |
| still used as the stack pointer,another memory range is used for
| |
| the stack itself.Thus,the SP is changed rach time you switch from
| |
| one mode to the other.Because of this you differentiate between
| |
| the User(USP)and the Supervisor SP(SSP).
| |
| Accessing memory can also depend on these two modes.During such
| |
| accessing,the processor sends signals to the peripheral components
| |
| informing them of the current processor moce.This way a MC68000
| |
| computor can protect(privilege)certain memory ranges so they can't
| |
| be accessed by the user.
| |
| In the supervisor mode it is possible to execute all instructions
| |
| and access all areas of memory.Because of this,operating systems
| |
| usually run in the supervisor mode.This is accomplished through
| |
| the use of Exceptions.
| |
|
| |
| 2.3.2.Exceptions.
| |
| ----------------
| |
| Exceptions are similar to interupts on 6500 computors.This allows
| |
| stopping a program,running a sub-program,and then restarting the
| |
| stopped program.When an exception occurs the following seps are
| |
| taken:
| |
|
| |
| 1) The status register is saved.
| |
| 2) The S bit in SR is set(supervisor mode)and the T bit is
| |
| cleared(no trace).
| |
| 3) The program counter and the user SP are saved.
| |
| 4) The exception vector,which points to the needed exception
| |
| routine,is retrieved.
| |
| 5) The routine is executed.
| |
|
| |
| The vectors mentioned,which contain the starting addresses for the
| |
| various routines,are located at the very beginning of the memory.
| |
| Here is an overview of the vectors and their respective addresses:
| |
|
| |
| Number Address Used for
| |
| ----- ------ --------
| |
| 0 $000 RESET:starting SSP
| |
| 1 $004 RESET:starting PC
| |
| 2 $008 bus error
| |
| 3 $00C address error
| |
| 4 $010 illegal instruction
| |
| 5 $014 division by zero
| |
| 6 $018 CHK instruction
| |
| 7 $01C TRAPV instruction
| |
| 8 $020 privilege violation
| |
| 9 $024 trace
| |
| 10 $028 Axxx-instruction emulation
| |
| 11 $02C Fxxx-instruction emulation
| |
| $030-$038 reserved
| |
| 15 $03C uninitialed interrupt
| |
| $040-$05F reserved
| |
| 24 $060 unjustified interrupt
| |
| 25-31 $064-$083 level 1-7 interrupt
| |
| 32-47 $080-$0BF TRAP instructions
| |
| $0C0-$0FF reserved
| |
| 64-255 $100-$3FF user interrupt vectors
| |
|
| |
| The individual entries in the table above need detailed
| |
| explanation.So lets go through them one by one...
| |
| RESET:staring SSP;
| |
| At reset,the long word stored at this location is used as the
| |
| stack pointer for the supervisor mode(SSP).This way you can
| |
| specify the stack for the RESET routine.
| |
| RESET:starting PC;
| |
| Again at reset,the value at this location is used as the program
| |
| counter.In other words,the RESET routine is started at the address
| |
| stored here.
| |
| Bus Error;
| |
| This exception is activated by a co-processor when,for instance,a
| |
| reserved or non-existant memory range is accessed.
| |
| Address Error;
| |
| This error occurs when a word or long word access is atempted at
| |
| an odd address.
| |
| Illegal Instruction;
| |
| Since all MC68000 instructions consist of one word,a total 65536
| |
| different instructions are possible.However,since the processor
| |
| doesn't that many instructions,there are a number of words that
| |
| are invalid instructions.Should such a word occur,the exception is
| |
| prompted.
| |
| Division by Zero;
| |
| Since the processor has a division function,and the division of
| |
| anything by zero is mathematically undefined and thus illegal,this
| |
| exception occurs when such an operation is attempted.
| |
| CHK Instruction;
| |
| This exception only occurs with the CHK instruction.This
| |
| instruction tests that a data registers contents are within a
| |
| certain limit.If this is not the case,the exception is activated.
| |
| TRAPV Instruction;
| |
| If the TRAPV instruction is executed and the V bit(bit 1)in the
| |
| status word is set,this exception is prompted.
| |
| Privilege Violation;
| |
| If a privileged instruction is called from the user mode,this
| |
| exception is activated.
| |
| Trace;
| |
| If the trace bit(bit 15)in the status word is set,this exception
| |
| is activated after each instruction that is executed.This method
| |
| allows you to employ a step by step execution of machine programs.
| |
| Axxx-Instruction Emulation;
| |
| Fxxx-Instruction Emulation;
| |
| These two vectors can be used for a quite interesting trick.If an
| |
| instruction beginning with $A or $F(such as $A010 or $F200)is
| |
| called the,the routine to which the corresponding vector is
| |
| pointing is accessed.In these routines you can create chains of
| |
| other instructions,in effect expanding the processors instruction
| |
| vocaulary!
| |
| Reserved;
| |
| These vectors are not used.
| |
| Uninitialized Interrupt;
| |
| This exception is activated when a peripheral component that was
| |
| not initialized sends an interrupt.
| |
| Unassigned Interrupt;
| |
| Is activated when a BUS error occurs during the interrupt
| |
| verification of the activating component.However,the interrupt is
| |
| usually only by some type of disturbance.
| |
| Level 1-7 Interrupt;
| |
| These seven vectors point to the interrupt routines of the
| |
| corresponding priority levels.If the level indicated in the status
| |
| word is higher than the level of the occuring interrupt,the
| |
| interrupt is simply ignored.
| |
| TRAP Instructions;
| |
| These 16 vectors are used when a corresponding TRAP instruction
| |
| occurs.Thus,TRAP instructions from TRAP #0 to TRAP #15 are
| |
| possible.
| |
| User Interrupt Vectors;
| |
| These vectors are used for interrupts which are activated by
| |
| several peripheral components that generate their own vector
| |
| number.
| |
|
| |
| At this point you don't want to delve any deeper into the secrets
| |
| of exceptions,since we'd be expanding this book beyond its frame
| |
| work.However,there's one last thing to say about exceptions:the
| |
| exception routines are ended with the RTE(ReTurn from Exception)
| |
| instruction,with which the original status is restored and the
| |
| user program is continued.
| |
|
| |
| 2.3.3.Interrupts.
| |
| ----------------
| |
| Interrupts are processed similarly to exceptions.They are breaks
| |
| (or interruptions)in the program which are activated through
| |
| hardware(such as a peripherical component or an external trigger).
| |
| The interrupt level is stored in bits 8-10 of the status register.
| |
| A value between 0 and 7 indicates the interrupt level.There are
| |
| eight possible interrupts,each of which as a different priority.If
| |
| the level of this interrupt happens to be higher than the value in
| |
| the status register,the interrupt is executed,or else ignored.
| |
| When a valid interrupt occurs,the computor branches to the
| |
| corresponding routine whose address is indicated in the exception
| |
| vector table above.
| |
| The interrupts are very important if you're trying to synchronize
| |
| a program with connected hardware.In this manner,a trigger(s.a the
| |
| keyboard)which is to feed the computor data,can signal the request
| |
| for a legal value using an interrupt.The interrupt routine then
| |
| simply takes the value directly.This method is also employed for
| |
| the operation of the serial interface(RS232).
| |
| We'll talk about the use of interrupts at a later time.The last
| |
| thing we want to mention about interrupts at this time is that,
| |
| like exceptions,interrupt routines are terminated with the RTE
| |
| instruction.
| |
|
| |
| 2.3.4.Condition Codes.
| |
| ---------------------
| |
| When you write a program in any language,the need for a
| |
| conditional operation arises quite often.For instance,in a BASIC
| |
| program
| |
|
| |
| IF D1=2 THEN D2=0
| |
|
| |
| represents a conditional operation.To write the equivalent in
| |
| machine language,you first need to make the comparison:
| |
|
| |
| CMP #2,D1
| |
|
| |
| CMP stands for compare and compares two operands,in this case D1
| |
| and D2.How is this operation evaluated?
| |
| For this purpose you have condition codes(CC's),which exist in the
| |
| branch instructions of machine language.Because of this,you must
| |
| specify when and where the program is to branch.
| |
| The simplest variation of the branch instruction is an
| |
| unconditional branch.The corresponding instruction is 'BRA address
| |
| ',although this won't help you here.After all,you need a
| |
| conditional branch.
| |
| To retain the result of the operation,in this case a comparrison
| |
| (CMP),several bits are reserved in the status word.Lets look at
| |
| bit 2 first,which is the zero flag.This flag is set when the
| |
| result of the previous operation was zero.
| |
| To explain the relationship between CMP and the Z flag,you must
| |
| first clarify the function of the CMP instruction.Actually this
| |
| instruction performs the subtraction of the source operand from
| |
| the destination operand.In the example above,the number 2 is
| |
| subtracted from the content of the memory cell at D1.Before the
| |
| result of this subtraction is discarded,the corresponding flags
| |
| are set.
| |
| If the contents of D1 in our example above happened to be 2,the
| |
| result of the subtraction would be 0.Thus,the Z flag would be set,
| |
| which can then be evaluated through a corresponding branch
| |
| instruction.Our example would then look like this:
| |
|
| |
| ...
| |
| CMP #2,D1 ;comparison,or subtraction
| |
| BNE UNEQUAL ;branch,if not equal(Z flag not set)
| |
| MOVE #0,D2 ;otherwise execute D2=0
| |
|
| |
| UNEQUAL:
| |
|
| |
| ... ;program branches to here
| |
|
| |
| BNE stands for Branch if Not Equal.This means,that if the Z flag
| |
| was cleared(=0)by the previous CMP,the program branches to the
| |
| address specified by BNE(here represented by UNEQUAL).The counter
| |
| part to the BNE instruction is the BEQ(Branch if EQual)instruction
| |
| which is executed if Z=1.
| |
| Here's a list of condition codes,which allow you to form
| |
| conditional branches using the Bcc(cc=condition code)format:
| |
|
| |
| cc Condition Bits
| |
| ---------------------------------------------------
| |
| T true,corresponds to BRA
| |
| F false,never branches
| |
| HI higher than C'* Z'
| |
| LS lower or same C + Z
| |
| CC,HS carry clear,higher or same C'
| |
| CS,LO carry set,lower C
| |
| NE not equal Z'
| |
| EQ equal Z
| |
| VC overflow clear V'
| |
| VS overflow set V
| |
| PL plus,positive
| |
| MI minus,negative
| |
| GE greater or equal N*V+N'*V'
| |
| LT less than N*V'+N'*V
| |
| GT greater than N*V*Z'+N'*V'*Z'
| |
| LE less or equal Z + N*V' + N'*V
| |
|
| |
| *=logic AND, +=logic OR, '=logic NOT
| |
|
| |
| Here are a few examples to demonstrate how these numerous
| |
| conditions can be utilized:
| |
|
| |
| CMP #2,D1
| |
| BLS SMALLER_EQUAL
| |
|
| |
| This branches if the contents of D1<=2,whether D1 is 0,1 or 2.In
| |
| this example,the BLE instruction would allow the program to branch
| |
| even if D1 is negative.You can tell this by the fact that the V
| |
| bit is used in the evaluation of this expression(see chart above).
| |
| When the sign is changed during the operation,this V bit is
| |
| compared with the N bit.Should both bits be cleared(N bit=0 and V
| |
| bit=0)after the CMP subtraction(D1-2),the result has remained
| |
| positive:the condition as not been met.
| |
| The conditions EQ and NE are quite important for other uses,as
| |
| well.For instance,they can be used to determine if particular bits
| |
| in a data word are set,by writing the following sequence...
| |
|
| |
| ...
| |
| AND #%00001111,D1 ;masks bits out
| |
| BEQ SMALLER ;branches when none of the four
| |
| ; ;lower bits is set
| |
| CMP #%00001111,D1
| |
| BEQ ALL ;branches when all four bits set
| |
|
| |
| The AND instruction causes all bits of D1 to be compared with the
| |
| bits of the parameter(in this case #%00001111).If the same bits
| |
| are set in both bytes,the corresponding bits are also set in the
| |
| result.If one bit of the pair is cleared,the resulting bit is zero
| |
| as well.Thus,in the result,the only bits that are set are those
| |
| bits of the lowest four that were set in D1.
| |
| The technique is known as masking.In the example above,only the
| |
| lowest four bits were masked out,which meansthat in the resulting
| |
| byte,only the lowest four appear in their original condition.All
| |
| other bits are cleared with the AND operand.Of course you can use
| |
| any bit combination with this method.
| |
| If no bit at all is set in the result,the zeroflag is set,thus
| |
| fullfilling the BEQ condition and branching the program.Otherwise,
| |
| the next instruction is processed,in which D1 is compared with
| |
| %00001111.When both are equal,at leastall of the four lowest bits
| |
| of the original byte have been set,in which case the following BEQ
| |
| instruction branches.
| |
| Aside from CMP,the CC and CS conditions can also be used to
| |
| determine whether a HI bit was pushed out of the data word during
| |
| data rotation with the ROL and ROR instructions.
| |
| Before you move on the instruction vocabulary of the MC68000,we'd
| |
| like to give you another tip:
| |
| The AssemPro assembler makes it quite easy to try every command in
| |
| posible situations.Take the CMP command which we've been talking
| |
| about,for example.To test this command with various values and to
| |
| receive the results of the comparisons directly via the flags,try
| |
| the following.
| |
| Type the following into the Editor.
| |
|
| |
| run:
| |
| cmp $10,d1
| |
| bra run
| |
| end
| |
|
| |
| Assemble it,save the resulting code and enter the debugger.After
| |
| reloading the code you can then single step through the program
| |
| observing the results the program has on the flags.Try changing
| |
| the values in the register D1 and see how higher and lower values
| |
| affect the flag.
| |
| By the way,using the start command at this time causes it to run
| |
| forever.Well,at least until reset is hit,which isn't exactly
| |
| desirable,either...
| |
| This procedure isn't limited to just the CMP instruction.You can
| |
| use it to try any other instructions you're interested in.
| |
|
| |
| 2.4.The 68000 Instructions.
| |
| --------------------------
| |
| Its about time to explain the MC68000 instructions.You don't have
| |
| room for an in-depth discussion of each instruction in this book;
| |
| for that purpose we recommend PROGRAMMING THE 68000 from Sybex by
| |
| Steve Williams.
| |
| The following tables show the required parameters and arguments
| |
| for each instruction.AssemPro have access to built in help tables
| |
| covering effective addressing modes and many of the Amiga
| |
| Intuition calls.The following notation is used for arguments:
| |
|
| |
| Label a label or address
| |
| Reg register
| |
| An address register n
| |
| Dn data register n
| |
| Source source operand
| |
| Dest destination operand
| |
| <ea> address or register
| |
| #n direct value
| |
|
| |
| Here is a short list of the instructions for the MC68000 processor
| |
| AssemPro owners can simply place the cursor at the beginning of
| |
| the instruction and press the help key to see the addressing modes
| |
| allowed:
| |
|
| |
| Mnemonic Meaning
| |
| ---------------------------------------------------
| |
| Bcc Label conditional branch,depends on condition
| |
| BRA Label unconditional branch(similar to JMP)
| |
| BSR Label branch to subprogram.Return address is
| |
| deposited on stack,RTS causes return to that
| |
| address.
| |
| CHK <ea>,Dx check data register for limits,activate the
| |
| CHK instruction exception.
| |
| DBcc Reg,Label check condition,decrement and branch
| |
| JMP Label jump to address(similar to BRA)
| |
| JSR Label jump to subroutine.Return address is
| |
| deposited on stack,RTS causes return to that
| |
| address.
| |
| NOP no operation
| |
| RESET reset peripherals(caution!)
| |
| RTE return from exception
| |
| RTR return with loading of flags
| |
| RTS return from subroutine(after BSR and JSR)
| |
| Scc <ea> set a byte to -1 when condition is met
| |
| STOP stop processing(caution!)
| |
| TRAP #n jump to an exception
| |
| TRAPV check overflow flag,the TRAPV exception
| |
|
| |
| Here are a few important notes...
| |
| When a program jumps(JSR)or branches(BSR)to subroutine,the return
| |
| address to which the program is to return is placed on the stack.
| |
| At the RTS instruction,the address is pulled back off the stack,
| |
| and the program jumps to that point.
| |
| Lets experiment a little with this procedure.Please enter the
| |
| following short program:
| |
|
| |
| run:
| |
| pea subprogram ;address on the stack
| |
| jsr subprogram ;subprogram called
| |
| move.l (sp)+,d1 ;get a long word from stack
| |
| ; illegal ;for assemblers without
| |
| ;debuggers
| |
|
| |
| subprogram:
| |
| move.l (sp),d0 ;return address in D0
| |
| rts ;and return
| |
| end
| |
|
| |
| The first instruction,PEA,places the address of the subprogram on
| |
| the stack.Next,the JSR instruction jumps to the subprogram.The
| |
| return address,or the address at which the main program is to
| |
| continue after the completion of the subprogram,is also deposited
| |
| on the stack at this point.
| |
| In the subprogram,the long word pointed to by the stack pointer is
| |
| now loaded into the data register D0.After that,the RTS
| |
| instruction pulls the return address from the stack,and the
| |
| program jumps to that address.
| |
| Back in the main program,the long word which is on the top of the
| |
| stack,is pulled from the stack and written in D1.Assemblers that
| |
| do not have the debugging features of AssemPro may need the
| |
| ILLEGAL instruction so they can break the program and allow you to
| |
| view the register contents.
| |
| Assemble the program and load the resulting code into the debugger
| |
| Single step thru the program and examine the register contents.
| |
| Here you can see that D0 contains the address at which the program
| |
| is to continue after the RTS command.Also,D1 contains the address
| |
| of the subprogram which you can verify by comparing the debugger
| |
| listing.
| |
| The STOP and RESET instructions are so powerful that they can only
| |
| be used in supervisor mode.Even if you do switch to the supervisor
| |
| mode,you should NOT use these instructions if there is any data in
| |
| memory that has not been saved and you wish to retain.
| |
| The TRAP instruction receives a number between 0 and $F,which
| |
| determines the particular TRAP vector(addresses $0080-$00BF)and
| |
| thus the corresponding exception routine.Several operating systems
| |
| for the 68000 utilize this instruction to call operating system
| |
| functions.You'll deal more with this instruction later.
| |
| In the short sample program that compared two numbers,the CMP
| |
| instruction performed an arithmetic function,namely a subtraction.
| |
| This subtraction could be performed with an actual result as well
| |
| using the SUB instruction.The counterpart to this is in addition,
| |
| for which the ADD instruction is used.In 8 bit processors,like the
| |
| 6502,these arithmetic functions are the only mathematical
| |
| operations.The MC68000 can also multiply,divide,and perform these
| |
| operations with a variety of data sizes.
| |
| Most of the functions require two parameters.For instance the ADD
| |
| instruction...
| |
|
| |
| ADD source,destination
| |
|
| |
| where source and destination can be registers or memory addresses.
| |
| Source can also be a direct value(#n).The result of the opoeration
| |
| is placed in the destination register or the destination address.
| |
| This is same for all operation of this type.These instructions can
| |
| be tried out with the AssemPro assembler.In this case we recommend
| |
| the use of a register as the destination.
| |
| Heres an overview of the arithmetic operations with whole numbers:
| |
|
| |
| Mnemonic Meaning
| |
| --------------------------------------------------------------
| |
| ADD source,dest binary addition
| |
| ADDA source,An binary addition to a address register
| |
| ADDI #n,<ea> addition with a constant
| |
| ADDQ #n,<ea> fast addition of a constant which can
| |
| be only from 1-8
| |
| ADDX source,dest addition with transfer in X flag
| |
| CLR <ea> clear an operand
| |
| CMP source,dest comparison of two operands
| |
| CMPA <ea>,An comparison with an address register
| |
| CMPI #n,<ea> comparison with a constant
| |
| CMPM source,dest comparison of two memory operands
| |
| DIVS source,dest sign-true division of a 32 bit
| |
| destination by a 16 bit source operand.
| |
| The result of the division is stored in
| |
| the LO word of the destination,the
| |
| remainder in the HI word.
| |
| DIVU source,dest division without regard to sign,similar
| |
| to DIVS
| |
| EXT Dn sign-true expansion to twice original
| |
| size(width)data unit
| |
| MULS source,dest sign-true multiplication of two words
| |
| into one long word
| |
| MULU source,dest multiplication without regard to sign,
| |
| similar to MULS
| |
| NEG <ea> negation of an operand(twos complement)
| |
| NEGX <ea> negation of an operand with transfer
| |
| SUB source,dest binary subtraction
| |
| SUBA <ea>,An binary subtraction from an address
| |
| register
| |
| SUBI #n,<ea> subtraction of a constant
| |
| SUBQ #n,<ea> fast subtraction of a 3 bit constant
| |
| SUBX source,dest subtraction with transfer in X flag
| |
| TST <ea> test an operand and set N and Z flag
| |
|
| |
| For the processing of whole numbers,the processor can operate with
| |
| BCD numbers.These are Binary Coded Decimal numbers,which means
| |
| that the processor is working with decimals.In this method,each
| |
| halfbyte contains only numbers from 0 to 9,so that these numbers
| |
| can be easily processed.For this method,the following instructions
| |
| are available:
| |
|
| |
| Mnemonic Meaning
| |
| -----------------------------------------------------------------
| |
| ABCD source,dest addition of two BCD numbers
| |
| NBCD source,dest negation of a BCD number(nine
| |
| complement)
| |
| SBCD source,dest subtraction of two BCD numbers
| |
|
| |
| Again,we recommend that you try this out yourself.Although
| |
| handling the BCD numbers is relatively easy,it can be rather
| |
| awkward at first.Be sure that you enter only BCD numbers for
| |
| source and destination,since the results are not correct
| |
| otherwise.
| |
| Next are the logical operations,which you might know from BASIC.
| |
| With these functions,you can operate on binary numbers bit for
| |
| bit.
| |
|
| |
| Mnemonic Meaning
| |
| -----------------------------------------------------------------
| |
| AND source,dest logic AND
| |
| ANDI #n,<ea> logic AND with a constant
| |
| EOR source,dest exclusive OR
| |
| EORI #n,<ea> exclusive OR with a constant
| |
| NOT <ea> inversion of an operand
| |
| OR source,dest logic OR
| |
| ORI #n,<ea> logic OR wuth a constant
| |
| TAS <ea> check a byte and set bit 7
| |
|
| |
| Single bits can also be manipulated by the following set of
| |
| instructions:
| |
|
| |
| Mnemonic Meaning
| |
| ----------------------------------------------------------------
| |
| BCHG #n,<ea> change bit n(0 is changed to 1 and vice
| |
| versa)
| |
| BCLR #n,<ea> clear bit n
| |
| BSET #n,<ea> set bit n
| |
| BTST #n,<ea> test bit n,result is displayed in Z
| |
| flag
| |
|
| |
| These instructions are particularly important from the
| |
| manipulation and evaluation of data from peripherals.After all,in
| |
| this type of data,single bits are often very significant.You'll
| |
| come across this more in later chapters.
| |
| The processor can also shift and rotate an operand within itself
| |
| ('n'indicates a register,'#'indicates a direct value which
| |
| specifies the number of shiftings)...
| |
|
| |
| Mnemonic Meaning
| |
| ----------------------------------------------------------------
| |
| AS n,<ea> arithmetic shift to the left(*2^n)
| |
| ASR n,<ea> arithmetic shift to the right(/2^n)
| |
| LSL n,<ea> logic shift to the left
| |
| LSR n,<ea> logic shift to the right
| |
| ROL n,<ea> rotation left
| |
| ROR n,<ea> rotation right
| |
| ROXL n,<ea> rotation left with transfer in X flag
| |
| ROXR n,<ea> rotation right with transfer in X flag
| |
|
| |
| All these instructions allow you to shift a byte,a word or a long
| |
| word to the left or right.Its not too surprising that this is the
| |
| equivalentof multipling(dividing)the number by a power of 2.Here's
| |
| a little example to demonstrate why
| |
| Lets take a byte containing the value 16 as an example.In binary,
| |
| it looks like this:
| |
|
| |
| %00010000 =16
| |
|
| |
| Now,if you shift the byte to the left by inserting a 0 at the
| |
| right,you'll get the following result...
| |
|
| |
| %00010000 shifted to the left equals
| |
| %00100000 =32,in effect 16*2
| |
|
| |
| Repeated shifting results in repeated doubling of the number.Thus
| |
| if you shift the number n times,the number is multiplied by 2^n.
| |
| The same goes for shifting to the right.However,this operation as
| |
| a slight quirk:here's a sample byte with the value 5:
| |
|
| |
| %00000101 =5,shifted once to the right equals
| |
| %00000010 =2
| |
|
| |
| The answer in this case is not 2.5 as you might expect.The result
| |
| such a division is always a whole number,since any decimal places
| |
| are discarded.If you use the DIV instruction instead of shifting,
| |
| you'll retain the digits to the right of the decimal point.However
| |
| shifting is somewhat faster,and shifting can also receive long
| |
| words as results.
| |
| After explaining the principle of shifting,you still need to know
| |
| why more than two instructions are required for the procedure.Well
| |
| this is because there are several different types of shifting.
| |
| First,you must differentiate between shifting and rotating.In
| |
| shifting,the bit that is added to the left or the right side is
| |
| always a zero.In rotating,it is always a specific value that is
| |
| inserted.This means that with the ROR or the ROL instructions,the
| |
| bit that is taken out on one side is the one that is inserted on
| |
| the other.With the ROXR and the ROXL instructions this bit takes a
| |
| slight detour to the X flag.Thus,the content of the flag is
| |
| inserted into the new bit,while the old bit is loaded into the
| |
| flag.
| |
| Shifting as well,has two variations:arithmetic and logical
| |
| shifting.You've already dealt with logical shifting.In this
| |
| variation,the inserted bit is always a zero,and the extracted bit
| |
| is deposited in the C flag and in the X flag.
| |
| Although the highest bit,which always represents the sign,is
| |
| shifted in arithmetic shifting,the sign is still retained by ASR.
| |
| This has the advantage that when these instructions are used for
| |
| division,the operation retains the correct sign(-10/2 equals-5).
| |
| However,should an overflow or underflow cause the sign to change,
| |
| this change is noted in the V flag,which always indicates a change
| |
| in sign.With logical shifting this flag is always cleared.
| |
| Now to the instructions that allow you to move data.These are
| |
| actually the most important instructions for any processor,for how
| |
| else could you process data?
| |
|
| |
| Mnemonic Meaning
| |
| ------------------------------------------------------------------
| |
| EXG Rn,Rn exchange of two register contents(don't
| |
| confuse with swap)
| |
| LEA <ea>,An load an effective address in address
| |
| register An
| |
| LINK An,#n build stack range
| |
| MOVE source,dest carry value over from source to dest
| |
| MOVE SR,<ea> transfer the status register contents
| |
| MOVE <ea>,SR transfer the status register contents
| |
| MOVE <ea>,CCR load flags
| |
| MOVE USP,<ea> transfer the user stack point
| |
| MOVE <ea>,USP transfer the user stack point
| |
| MOVEA <ea>,An transfer a value to the address
| |
| register An
| |
| MOVEM Regs,<ea> transfer several registers at once
| |
| MOVEM <ea>,Regs transfer several registers at once
| |
| MOVEP source,dest transfer data to peripherals
| |
| MOVEQ #n,Dn quickly transfer a 8 bit constant to
| |
| the data register Dn
| |
| PEA <ea> deposit an address on the stack
| |
| SWAP Dn swap the halves of the register(the
| |
| upper 16 bits with the lower)
| |
| UNLK An unlink the stack
| |
|
| |
| The LEA or PEA instructions are often used to deposit addresses in
| |
| an address register or on the stack.The instruction
| |
|
| |
| LEA label,A0
| |
|
| |
| loads the address of the label'label' into the address register
| |
| A0.In practice,this corresponds to
| |
|
| |
| MOVE.L #label,A0
| |
|
| |
| which is equivalent to
| |
|
| |
| PEA label
| |
|
| |
| All these instructions deposit the address of 'label' on the stack
| |
| The following instruction also does this:
| |
|
| |
| MOVE.L #label,-(SP)
| |
|
| |
| The LEA instruction becomes much more interesting when the label
| |
| is replaced by indirect addressing.Here's an example:
| |
|
| |
| LEA 1(A0,D0),A1
| |
|
| |
| The address that's produced by the addition of 1(direct value
| |
| offset)+A0+D0 is located in A1.To duplicate this instruction with
| |
| MOVE would be quite cumbersome.Take a look:
| |
|
| |
| MOVE.L A0,A1
| |
| ADD.L D0,A1
| |
| ADDQ.L #1,A1
| |
|
| |
| As you can see,the LEA instruction offers you quite some
| |
| interesting possibilities.
| |
| Those are all the instructions of the MC68000.Through their
| |
| combination using the diverse methods of addressing,you can create
| |
| a great number of different instructions,in order to make a
| |
| program as efficent as possible.
| |
| The following table is an overview of all MC68000 instructions
| |
| along with their possible addressing types and the influence of
| |
| flags.The following abbreviations are used:
| |
|
| |
| x=legal s=source only d=destination only
| |
| -=not effected 0=cleared *=modified accordingly
| |
| 1=set u=undermined P=privileged
| |
|
| |
| Mnemonic 1 2 3 4 5 6 7 8 9 10 11 12 X N Z V C P
| |
| -----------------------------------------------------------------
| |
| ABCD x
| |
| ADD s s x x x x x x x s s s * * * * *
| |
| ADDA x x x x x x x x x x x x - - - - -
| |
| ADDI x x x x x x x x * * * * *
| |
| ADDQ x x x x x x x x x * * * * *
| |
| ADDX x x * * * * *
| |
| AND s x x x x x x x s s s - * * 0 0
| |
| ANDI x x x x x x x x - * * 0 0
| |
| ASL, ASR x x x x x x x x * * * * *
| |
| Bcc - - - - -
| |
| BCHG x x x x x x x x - - * - -
| |
| BCLR x x x x x x x x - - * - -
| |
| BRA - - - - -
| |
| BSET x x x x x x x x - - * - -
| |
| BSR - - - - -
| |
| BTST x x x x x x x x z x x - - * - -
| |
| CHK x x x x x x x x x x x - * u u u
| |
| CLR x x x x x x x x - 0 1 0 0
| |
| CMP x x x x x x x x x x x x - * * * *
| |
| CMPA x x x x x x x x x x x x - * * * *
| |
| CMPI x x x x x x x x - * * * *
| |
| CMPM x x x x x x x - * * *
| |
| cpGEN - - - - -
| |
| DBcc - - - - -
| |
| DIVS x x x x x x x x x x x - * * * 0
| |
| DIVU x x x x x x x x x x x - * * * 0
| |
| EOR x x x x x x x x - * * 0 0
| |
| EORI x x x x x x x x - * * 0 0
| |
| EORI CCR * * * * *
| |
| EORI SR * * * * *
| |
| EXG - - - - -
| |
| EXT - * * 0 0
| |
| EXTB - * * 0 0
| |
| ILLEGAL - - - - -
| |
| JMP x x x x x x x - - - - -
| |
| JSR x x x x x x x - - - - -
| |
| LEA x x x x x x x - - - - -
| |
| LINK x - - - - -
| |
| LSL, LSR x x x x x x x * * * 0 *
| |
| MOVE x s x x x x x x x s s s - * * 0 0
| |
| MOVEA x x x x x x x x x x x x - - - - -
| |
| MOVE to CCR x x x x x x x x x x x * * * * *
| |
| MOVE from SR x x x x x x x x - - - - - P
| |
| MOVE to SR x x x x x x x x x x x * * * * * P
| |
| MOVE USP x - - - - - P
| |
| MOVEM x s d x x x x s s - - - - -
| |
| MOVEP s d - - - - -
| |
| MOVEQ d - * * 0 0
| |
| MULS x x x x x x x x x x x - * * 0 0
| |
| MULU x x x x x x x x x x x - * * 0 0
| |
| NBCD x x x x x x x x * u * u *
| |
| NEG x x x x x x x x * * * * *
| |
| NEGX x x x x x x x x * * * * *
| |
| NOP - - - - -
| |
| NOT x x x x x x x x - * * 0 0
| |
| OR s x x x x x x x s s s - * * 0 0
| |
| ORI x x x x x x x x - * * 0 0
| |
| PEA x x x x x x x - - - - -
| |
| RESET - - - - - P
| |
| ROL, ROR x x x x x x x - * * 0 *
| |
| ROXL, ROXR x x x x x x x - * * 0 *
| |
| RTE - - - - - P
| |
| RTR * * * * *
| |
| RTS - - - - -
| |
| SBCD x x * u * u *
| |
| Scc x x x x x x x x - - - - -
| |
| STOP x - - - - -
| |
| SUB s s x x x x x x x s s s * * * * *
| |
| SUBA x x x x x x x x x x x x - - - - -
| |
| SUBI x x x x x x x x * * * * *
| |
| SUBQ x x x x x x x x x * * * * *
| |
| SUBX x x * * * * *
| |
| SWAP x - * * 0 0
| |
| TAS x x x x x x x x - * * 0 0
| |
| TRAP x - - - - -
| |
| TRAPV - - - - -
| |
| TST x x x x x x x x - * * 0 0
| |
| UNLK x - - - - -
| |
|
| |
|
| |
|
| |
|
| |
| Chapter 3.
| |
| ---------
| |
| 3.Working With Assemblers.
| |
| -------------------------
| |
| The instructions that you've learned so far are incomprehensible
| |
| to the MC68000 processor.The letters MOVE mean absolutely nothing
| |
| to the processor-it needs the instructions in binary form.Every
| |
| instruction must be coded in a word-which normally takes a lot of
| |
| work.
| |
| An assembler does this work for you.An assembler is a program that
| |
| translates the instructions from text into the coresponding binary
| |
| instructions.The text that is translated is called Mnemonic or
| |
| Memcode.Its a lot easier working with text instructions-or does
| |
| $4280 mean more to you than CLR.L D0?
| |
| This chapter is about working with assemblers.We'll describe the
| |
| following three:
| |
| ASSEM;
| |
| This is the assembler from the Amiga's development package.This
| |
| assembler is quite powerful,but it is clearly inferior to its two
| |
| fellow compilers in some areas.
| |
| AssemPro;
| |
| This is the Abacus assembler.It has a debugger in addition to the
| |
| assembler.This lets you test and correct programs.In our opinion,
| |
| it is the best one to use for writing and testing practice
| |
| programs.For this reason,we wrote the programs in this book with
| |
| this assembler.
| |
| KUMA-SEKA;
| |
| This is a popular assembler which also has a debugger.
| |
|
| |
| All assemblers perform a similar task-they translate memcode,so
| |
| that you can write a runable program to disk.To test the program
| |
| directly,you need a debugger which is something Assem doesn't
| |
| have.
| |
|
| |
| 3.1.The Development Assembler.
| |
| -----------------------------
| |
| This assembler is a plain disk assembler.That means that it can
| |
| only assemble text files that are on disk and write the result
| |
| back to disk.You can't make direct input or test run the new
| |
| program.
| |
| You can call ASSEM from the CLI by typing ASSEM followed by
| |
| parameters that specify what you wish the assembler to do.
| |
| In the simplest case,you call it like this:
| |
|
| |
| ASSEM Source -O Destination
| |
|
| |
| Source is the filename of the file containing the program text.
| |
| Destination is the name of the file that contains the results of
| |
| assembling after the process is over.The "-O"means that the
| |
| following name is used for the object file.
| |
| There are several other parameters that can be passed.These are
| |
| written with their option(ie-O),so that the assembler knows what
| |
| to do with the file that you've told it to use.The following
| |
| possible options must be followed by a filename.
| |
|
| |
| -O Object file
| |
| -V Error messages that occur during assembling are written
| |
| to a file.If this isn't given,the error messages appear
| |
| in the CLI window.
| |
| -L The output of the assembled program lines are sent to
| |
| this file.You can also use"PRT:"to have it printed.
| |
| -H This file is read in at the beginning of the assembled
| |
| file and assembled along with it.
| |
| -E A file is created that contains lines which have EQU
| |
| instructions.
| |
| -C This option isn't followed by a filename but by another
| |
| option.You can also use OPT to do this.The following
| |
| options are available:
| |
|
| |
| OPT S A symbol table is created which contains all the
| |
| labels and their values.
| |
| OPT X A cross reference list is created(where labels
| |
| are used).
| |
| OPT W A number must follow this option.It sets the
| |
| ammount of workspace to be reserved.
| |
|
| |
| The assembler creates an object file.This is not runnable.To make
| |
| it runnable,you need to call the linker,ALINK.This program can
| |
| link several assembled or compiled object files together to make a
| |
| runnable program.In the simplest case,you enter the following
| |
| instruction in the CLI:
| |
|
| |
| ALINK Source TO Destination
| |
|
| |
| Source is the object file produced by the assembler.Destination is
| |
| the name of the program.It can be started directly.
| |
|
| |
| 3.2.AssemPro.
| |
| ------------
| |
| Abacus's AssemPro is a package which combines editor,assembler and
| |
| debugger in an easy to use package.
| |
| The AssemPro Program is divided into several windows-one for the
| |
| assembler,the editor,the debugger and several help functions.
| |
| Producing a program is very easy:
| |
|
| |
| 1) Write the program with the editor and then store it to disk.
| |
| 2) Start the assembler,so that the program is translated.
| |
| 3) If desired,start the debugger,load the program and test it.
| |
|
| |
| Within the debugger you can work through parts of the program or
| |
| even through single commands.As after each one of these steps the
| |
| debugger shows you the state of the registers and flags in the
| |
| status register,you can easily try the programs presented in this
| |
| book.
| |
| You need to load AssemPro only once when working with machine
| |
| language programs.Thus you don't need to save back and to between
| |
| editor,assembler,linker and debugger.
| |
| AssemPro has an especially interesting function:the reassembler.
| |
| With this debugger function you are able to convert runnable
| |
| programs into the source text of the program.Once you have made
| |
| the source text,you can edit the program using the editor and
| |
| assemble it again.AssemPro is equipped with functions other
| |
| assemblers miss.There are however,some differences you should know
| |
| about.As many programs you see were written for the K-SEKA,be
| |
| aware of one difference:the EVEN command.AssemPro uses the ALIGN
| |
| instruction.
| |
| Note,that when entering and assembling one of the programs in
| |
| AssemPro you must make sure that you place an END directive at the
| |
| end of the source text.
| |
| The following is an introduction into working with AssemPro and
| |
| the programs in this book.
| |
|
| |
| Start AssemPro normally,next click on the editor window and start
| |
| typing in your program.If the program is on disk already,load it
| |
| by selecting the appropriate menu or by using the key combination
| |
| right <AMIGA> key and <o>.To do this you only need to ckick on the
| |
| filename in the displayed requester and click on the OK gadget.
| |
| Once you have typed in or loaded the program into the editor,you
| |
| can assemble it.It is best to save your source before assembling.
| |
| You assemble your program by clicking on the assembler window
| |
| displayed above the editor window and pressing <AMIGA> and <a>.You
| |
| can then choose how to locate your program in the memory.Remember
| |
| that data used by the co-processors must be located in CHIP RAM.
| |
| By clicking OK you start the assembler process.If you additionally
| |
| select "breakable",you can cancel the process by pressing both
| |
| shift keys.If any error occurs during assembling,AssemPro uses a
| |
| window to tell you this.Use this window to correct the error and
| |
| continue with "Save and try again".
| |
| Now the runnable program is located in the Amiga's memory.Use the
| |
| menu item "Save as"to save it on disk.If you want to store it on
| |
| RAM disk,click the given filename and enter RAM: in front of this
| |
| name.In addition you can click on the menu item "ICON"and choose
| |
| if you only want the program itself on disk but the icon too.Use
| |
| this icon to start the program at a later time from Workbench.
| |
| To test-run the program,you move the debugger window to the
| |
| foreground of the screen(for instance by clicking on the back
| |
| gadget).Use "Load"in the debugger menu or <AMIGA> <o> to call the
| |
| select file window,where you select the saved program.The program
| |
| is then loaded into the memory and its shown disassembled.
| |
| The highlighted line(orange)represents the current state of the
| |
| program counter.This is the line where the processor reads its
| |
| next instruction,provided you tell the processor so.There are
| |
| three ways to do so.
| |
| The first one is to start the program with "Start".This
| |
| alternative does not enable you to stop the program if anything
| |
| goes wrong.
| |
| The second possibility,"Start breakable"is better in this respect.
| |
| After the program starts,it continuously displays the registers
| |
| contents on the left side of the window.In addition to that you
| |
| can cancel the process by pressing <ESC>.Note that this only works
| |
| if your program doesn't use the <ESC>key itself.
| |
| The third possibility enables you to only partly run your program.
| |
| You can do this by stepping through the program or by placing
| |
| breakpoints throughout the program.You place these by clicking on
| |
| the desired address and then pressing <AMIGA> <b>."BREAKPOINT"is
| |
| displayed where the command was displayed before.If you start the
| |
| program now,it stops whenever it comes across any breakpoints.
| |
| You can start a small part of the program by moving the mouse
| |
| pointer to the orange line,clicking the left button and holding it
| |
| down while you drag the mouse pointer downward.If you release the
| |
| button,the processor works through this part of the program,
| |
| stopping at the line,where you positioned the mouse pointer.This
| |
| is a very useful method to step by step test a program.
| |
| AssemPro as another helpful window:the Table.This window lists the
| |
| valid address methods for instructions and the parameters of Amiga
| |
| functions.This is extremely helpful whenever you are not sure
| |
| about one of the instructions.
| |
|
| |
| 3.3.The K-SEKA Assembler.
| |
| ------------------------
| |
| The SEKA assembler,from KUMA,has a simple text editor and a
| |
| debugger in addition to the assembler.This program is controlled
| |
| by simple instructions and it is easy to use.It is also multi-
| |
| functional and quick,so it is great for small test and example
| |
| programs.You can use it to write bigger programs once you've got
| |
| use to the editor.Now lets look at the editor.
| |
| To load a program as source code(text)into the editor,enter"r"
| |
| (read).The program asks you for the name of the file with the
| |
| "<FILENAME>"prompt.You then enter the name of the text file.If you
| |
| created the file with SEKA,the file is stored on disk with".s" on
| |
| the end of its name.You don't need to include the".s"when you load
| |
| the file.Thats taken care of automatically.("s"stands for source.)
| |
| You can store programs you've just written or modified by using
| |
| the"w"instruction.The program asks you for the name.If you enter
| |
| "Test",the file is written to the disk with"Test.s"as its name.
| |
| This is a normal text file in ASCII format.
| |
| There are two ways to enter or change a programs:using the line
| |
| editor or the screen editor.You can enter the second by hitting
| |
| the <ESC>key.The upper screen section is then reserved for the
| |
| editor.You can move with the cursor keys and change the text
| |
| easily.The lines that you enter are inserted into the existing
| |
| text and automatically numbered.By hitting the <ESC>key again,you
| |
| leave the screen editor.
| |
| There's really not much to say about this editor.It's really just
| |
| for simple insertions and changes.Other functions are called in
| |
| normal instruction mode,the mode in which">"is the input prompt.
| |
| The following instructions are available to you for text editing
| |
| (<n>stands for a number.The meaning of the instructions is in
| |
| parenthesis.)
| |
|
| |
| Instruction Function
| |
| ----------------------------------------------------------------
| |
| t(Target) Puts the cursor on the highest line in the
| |
| text.
| |
| t<n> Puts the cursor on line n.
| |
| b(Bottom) Puts the cursor on the last line in the text.
| |
| u(Up) Go up one line.
| |
| u<n> Go up n lines.
| |
| d(Down) Go down one line.
| |
| d<n> Go down n lines.
| |
| z(Zap) Deletes the current line.
| |
| z<n> Deletes n lines starting at the cursor line.
| |
| e(Edit) Lets you edit the current line(and only that
| |
| line).
| |
| e<n> Edit from line n.
| |
| ftext(Find) Searches for the text entered starting at the
| |
| current line.The case of a letter makes a
| |
| difference,so make sure to enter it correctly.
| |
| Blanks that appear after the f are looked for
| |
| as well!
| |
| f Continues searching beyond the text that was
| |
| previously given.
| |
| i(Insert) Starts the line editor.Now you can enter a
| |
| program line by line.However,you can't use the
| |
| cursor to move into another line.Line numbers
| |
| are generated automatically.The lines that
| |
| follow are moved down,not erased.
| |
| ks(Kill Source) The source text is deleted if you answer"y"
| |
| when the program asks if you are sure.Otherwise
| |
| nothing happens.
| |
| o(Old) Cancels the "ks"function and saves the old text
| |
| p(Print) Prints the current line.
| |
| p<n> Prints n lines starting at cursor line.
| |
|
| |
| Those are the K-SEKA's editor functions.In combination with the
| |
| screen editor,they allow for simple text editing.You can,for
| |
| example,delete the current line(and other lines)while working in
| |
| the screen editor by hitting <ESC> to get into instruction mode
| |
| and then entering"z"(or "z<n>").
| |
| If you'd like to edit all lines that contain "trap",for example,
| |
| you can do the following:
| |
|
| |
| -Jump to the beginning of the text using "t"
| |
| -Search for a "trap"instruction by entering "ftrap" in the
| |
| first line.
| |
| -Press <ESC> and edit the line.
| |
| -Press <ESC> again to get into instruction mode.
| |
| -Search using "f",<ESC>,etc.until you get to the end of the
| |
| text.
| |
|
| |
| This sounds very clumsy,but in practise it works quite well and
| |
| goes quickly.Experiment with the editor bit,so you can get use to
| |
| it.
| |
| Now here are the instructions for working with disks:
| |
|
| |
| Instruction Function
| |
| -----------------------------------------------------------------
| |
| v(View files) Look at the disk's directory.You can also
| |
| include the disk drive or subdirectory
| |
| that interests you.For example,"vc"causes
| |
| the "c"subdirectory to be listed and makes
| |
| it the current directory.
| |
| kf(Kill file) The program asks for the name of the file.
| |
| The file is deleted(and you aren't asked
| |
| if your sure either-so be careful).
| |
| r(Read) After inputting this instruction,you'll be
| |
| asked which file to load(FILENAME>).The
| |
| file that you specify is then loaded.If
| |
| only "r"is entered,a text file is loaded
| |
| in the editor.
| |
| ri(Read Image) Loads a file into memory.After you've
| |
| entered the filename,SEKA asks for the
| |
| address the file should begin at in memory
| |
| (BEGIN>)and the highest address that
| |
| should be used for the file(END>).
| |
| rx(Read from Auxillary) This works just like the "ri"function
| |
| except that it reads from the serial port
| |
| instead of from disk(You don't need a file
| |
| name).
| |
| rl(Read Link file) This instruction reads in a SEKA created
| |
| link file.First you'll be asked if you are
| |
| sure,because the text buffer is erased
| |
| when the link file is loaded.
| |
| w(Write) After entering this instruction,you'll be
| |
| asked for the name of the file the text
| |
| should be written to.A".s"is automatically
| |
| appended to the name,so that it can be
| |
| recognized as a SEKA file.
| |
| wi(Write Image) Stores a block of memory to disk after the
| |
| name,beginning and end are entered.
| |
| wx(Write to Auxillary) This is similar to"wi";the only difference
| |
| is that the output is to the serial inter-
| |
| face.
| |
| wl(Write Link file) Asks for the name and then stores a link
| |
| file that was assembled with the"I"option
| |
| to disk.If this isn't available,the
| |
| message "* * Link option not specified"
| |
| appears.
| |
|
| |
| Once you've typed in or loaded a program,you can call the
| |
| assembler and have the program translated.Just enter"a"to do so.
| |
| You'll then be asked which options you want to use.If you enter a
| |
| <RETURN>,the program is assembled normally-ie the results of
| |
| translating a program in memory is stored in memory.Then the
| |
| program can be executed straight away.
| |
| You can enter one or more of the following options,however:
| |
|
| |
| v The output of the results goes to the screen.
| |
| p or
| |
| e goes to the printer with a title line.
| |
| h The output stops after every page and waits for a key
| |
| stroke.This is useful for controlling output to the screen
| |
| or for putting new sheets of paper in the printer.
| |
| o This option allows the assembler to optimize all possible
| |
| branch instructions.This allows the program code to be
| |
| shorter than it would otherwise be.Several messages appear
| |
| but you can ignore them.
| |
| l This option causes linkable code to be produced.You can
| |
| save it with the"wl"instruction and read it with the "rl"
| |
| instruction.
| |
|
| |
| A symbol table is included at the end of the listing if desired.
| |
| The table contains all labels and their values.It also contains
| |
| macro names.A macro allows several instructions to be combined in
| |
| to a single instruction.
| |
| For example,suppose you wrote a routinethat outputs the text that
| |
| register A0 points to.Every time you need to use the routine,you
| |
| must type:
| |
|
| |
| lea text,a0 ;pointer to text in A0
| |
| bsr pline ;output text
| |
|
| |
| You can simplify this by defining a macro for this function.To do
| |
| this,put the following at the beginning of the program:
| |
|
| |
| print:macro ;Macro with the name "Print"
| |
| lea ?1,a0 ;Parameter in A0
| |
| bsr pmsg ;Output text
| |
| endm ;End of macro
| |
|
| |
| Now,you can simply write the following in your program:
| |
|
| |
| print text ;Output text
| |
|
| |
| This line is replaced using the macro during assembly.The
| |
| parameter "text"is inserted where "?1"appears in the macro.You can
| |
| have several parameters in a macro.You give them names like "?2",
| |
| "?3",etc...
| |
| You can also decide whether you'd like to see the macros in the
| |
| output listing of the assembler.This is one of the pseudo-ops that
| |
| are available in the assembler.The SEKA assembler has the
| |
| following pseudo-ops:
| |
|
| |
| dc Defines one or more data items that should appear in
| |
| this location in the program.The word length can be
| |
| specified with .B,.W,or .L-and if this is left off, .B
| |
| is used.Text can be entered in question marks or
| |
| apostrophes.For example:dc.b "Hello",10,13,0
| |
| blk Reserves a number of bytes,words or long words,depending
| |
| on whether .B,.W,or .L is chosen.The first parameter
| |
| specifies the number of words to be reserved.The second
| |
| (which is optional)is used to fill the memory area.For
| |
| example:blk.w 10,0
| |
| org The parameter that follows the org instruction is the
| |
| address from which the (absolute) program should be
| |
| assembled.For example: org $40000
| |
| code Causes the program to be assembled in relative mode,the
| |
| mode in which a program is assembled starting at address
| |
| 0.The Amiga takes care of the new addressing after the
| |
| program is loaded.
| |
| data This means that from here on only data appear.This can
| |
| be left out.
| |
| even Makes the current address even by sometimes inserting a
| |
| fill byte.
| |
| odd The opposite of even-it makes the address odd.
| |
| end Assembling ends here.
| |
| equ or Used for establishing the value of a label
| |
| = For example: Value=123 or Value:equ 123
| |
| list Turns the output on again(after nlist).You can use the
| |
| following parameters to influence the output:
| |
| c Macro calls
| |
| d Macro definitions
| |
| e Macro expansion of the program
| |
| x Code expansions
| |
| For example: list e
| |
| nlist Turns off output.You can use the same parameters here as
| |
| with "list".
| |
| page Causes the printer to execute a page feed,so that you'll
| |
| start a new page.
| |
| if The following parameter decides whether you should
| |
| continue assembling.If it is zero,you won't continue
| |
| assembling.
| |
| else If the "if"parameter is zero,you'll begin assembling
| |
| here.
| |
| endif End of conditional assembling.
| |
| macro Start of a macro definition.
| |
| endm End of macro definition.
| |
| ?n The text in the macro that is replaced by the nth
| |
| parameter in the calling line.
| |
| ?0 Generates a new three digit number for each macro call-
| |
| this is very useful for local labels.
| |
| For example: x?0:bsr pmsg
| |
| illegal Produces an illegal machine language instruction.
| |
| globl Defines the following label as globel when the "I"option
| |
| of the assembler is chosen.
| |
|
| |
| Once you've assembled your program,the program code is in memory.
| |
| Using the "h"instruction,you can find out how large the program is
| |
| and where it is located in memory.The beginning and end address is
| |
| given in hex and the length in decimal(according to the last
| |
| executed operations):
| |
|
| |
| Work The memory area defined in the beginning
| |
| Src Text in memory
| |
| RelC Relocation table of the program
| |
| RelD Relocation table of the memory area
| |
| Code Program code produced
| |
| Data The program's memory area
| |
|
| |
| You'll find program in memory at the location given by Code.It's a
| |
| pain to have to enter this address whenever you want to start the
| |
| program.It make's good sense to mark the beginning of the program
| |
| with a label(for example,"run:").You can use the "g"instruction to
| |
| run the program as follows:
| |
|
| |
| g run
| |
|
| |
| The"g"(GO)instruction is one of SEKA's debugger instrucions.Heres
| |
| an overview:
| |
|
| |
| x Output all registers.
| |
| xr Output and change of registers(ie xd0)
| |
| gn Jump to address n.You`ll be asked for break points,addresses
| |
| at which the program should be terminate.
| |
| jn This is similar to the one above-a JSR is used to jump into
| |
| the program.The program must end with a RTS instruction.
| |
| qn Output the memory starting at address n.You can also specify
| |
| the word length.For example: q.w $10000
| |
| nn Disassembled output starting at address n.
| |
| an Direct assembling starting at address n.Direct program
| |
| instructions are entered.
| |
| nn Modify the contents of memory starting at address n.Here too
| |
| the word length can be given.You can terminate input with
| |
| the <ESC> key.
| |
| sn Executes the program instruction that the PC points to.After
| |
| you enter this instruction,n program steps are executed.
| |
| f Fill a memory area.You can choose the word width.All the
| |
| needed parameters are asked for individually.
| |
| c Copies one memory area to another.All the needed parameters
| |
| are asked for individually.
| |
| ? Outputs the value of an expression or a label.
| |
| For example: ?run+$1000-256
| |
| a Sets an instruction sequence that is passed to the program
| |
| when it starts as if it were started from CLI with this
| |
| sequence.
| |
| ! Leaves the SEKA assembler after being asked if your sure.
| |
|
| |
| You saw some of the calculations like SEKA can make in the "?"
| |
| example.You can also use them in programming.The folowing
| |
| operations work in SEKA:
| |
|
| |
| + Addition
| |
| - Subtraction
| |
| * Multiplication
| |
| / Division
| |
| & Logic AND
| |
| ! Logic OR
| |
| ~ EXclusive OR (XOR)
| |
|
| |
| These operations can also be combined.You can choose the counting
| |
| system.A "$"stands for hexadecimal,"@"for octal,and "%"for binary.
| |
| If these symbols aren`t used,the number is interpreted as a
| |
| decimal number.
| |
| Lets go back to the debugger.As mentioned,after entering "g
| |
| address",you`ll be asked for break points.You can enter up to 16
| |
| addresses at which the program halts.If you don`t enter break
| |
| points,but instead hit <RETURN>,the program must end with an
| |
| ILLEGAL instruction.If it ends instead with a RTS,the next return
| |
| address from the stack is retrieved and jumped to.This is usually
| |
| address 4 which causes SEKA to come back with "**Illegal
| |
| Instruction at $000004",but theres no guarantee that it will.Your
| |
| computor can end up so confused that it can`t function.
| |
| The SEKA program puts an ILLEGAL instruction in the place
| |
| specified as break points after saving the contents of these
| |
| locations.If the processor hits an illegal instruction,it jumps
| |
| back to the debugger by using the illegal instruction vector that
| |
| SEKA set up earlier.Then SEKA repairs the modified memory
| |
| locations and then displays the status line.Here you can find out
| |
| where the program terminated.
| |
| Using break points is a good technique for finding errors in the
| |
| program.You can,for example,put a break point in front of a
| |
| routine that you`re not sure about and start the program.When the
| |
| program aborts at this spot,you can go through the routine step by
| |
| step using the "s"option.Then you can watch what happens to the
| |
| status line after each instruction and find the mistake.
| |
| Program errors are called bugs.That`s why the program that finds
| |
| them is called a debugger.
| |
|
| |
|
| |
|
| |
|
| |
| Chapter 4.
| |
| ---------
| |
| 4.Our First Programs.
| |
| --------------------
| |
| You`re getting pretty far along in your knowledge of machine
| |
| language programming.In fact,you`re to the point where you can
| |
| write programs,and not just programs for demonstration purposes,
| |
| but ones that serve a real function.We`re assuming that you have
| |
| the AssemPro assembler and have loaded it.
| |
| If you`re using a different assembler,a few things must be done
| |
| differently.We covered those differences already in the chapter on
| |
| the different assemblers.
| |
| We`ve written the example programs as subroutines so they can be
| |
| tried out directly and used later.After assembling the program,you
| |
| can put the desired values in the register.Then you can either
| |
| single-step thru the programs or run the larger programs and
| |
| observe the results in the registers directly.(using the SEKA
| |
| assembler you can type "j program_name"to start the program.Then
| |
| you can read the results from the register directly,or use "q
| |
| address"to read it from memory.)
| |
| Lets start with an easy example,adding numbers in a table.
| |
|
| |
| 4.1.Adding Tables.
| |
| -----------------
| |
| Imagine that you have numbers in memory that you'd like to add.
| |
| Lets assume that you have five numbers whose length is one word
| |
| each.You want their sum to be written in register D0.The easiest
| |
| way to do this is:
| |
|
| |
| ;(4.1A)
| |
| adding1:
| |
| clr.l D0 ;Erase D0 (=0)
| |
| move table,d0 ;First entry in D0
| |
| add table+2,d0 ;Add second entry
| |
| add table+4,d0 ;Add third entry
| |
| add table+6,d0 ;Add fourth entry
| |
| add table+8,d0 ;add fifth entry
| |
| rts ;Return to main program
| |
| table: dc.w 2,4,6,8,10,
| |
| end
| |
|
| |
| Try out the program using the debugger by single stepping thru the
| |
| program until you get to the RTS instruction(left Amiga T).(SEKA
| |
| owners use "j adding1").You see that data register D0 really
| |
| contains the sum of the values.
| |
| The method above only reads and adds numbers from a particular set
| |
| of addresses.The Amigas processor has lots of different sorts of
| |
| addressing modes that give us a shorter and more elegant solution.
| |
| Lets add a variable to the address of the table,so that the
| |
| program can add different tables.
| |
| Lets put the addresses of the table in an address register(for
| |
| example, A0)instead.This register can be used as a pointer to the
| |
| table.You must use move.l since only long words are relocatable.By
| |
| using a pointer to a table you can use indirect addressing.You can
| |
| change the expression "table+x" to"x(A0)".
| |
|
| |
| ;(4.1B)
| |
| adding1:
| |
| clr.l D0 ;Erase D0 (=0)
| |
| move.l #table,a0 ;Put table addresses in A0
| |
| move 0(a0),d0 ;Put first entry in d0
| |
| add 2(a0),d0 ;Add second entry
| |
| add 4(a0),d0 ;Add third entry
| |
| add 6(a0),d0 ;Add forth entry
| |
| add 8(a0),d0 ;Add fifth entry
| |
| rts ;Return to main program]
| |
| table: dc.w 2,4,6,8,10
| |
| end
| |
|
| |
| Assemble this program,load it into the debugger.Then single step
| |
| (left Amiga T)thru this program and you'll see that this program
| |
| adds five numbers in order just like the last one.The reason you
| |
| used a step size of two for the ofset is that words are two bytes
| |
| long.AssemPro also defaults to relocate code so that you must move
| |
| #table as a long word.
| |
| Lets improve the program more by using "(a0)+"instead of "x(a)".
| |
| This way,every time you access elements of the table,the address
| |
| register A0 is automatically incremented by the number of bytes
| |
| that are read(in this case two).The difference between this and
| |
| the last example is that here the registers contents are modified.
| |
| The pointer is to the next unused byte or word in memory.
| |
| Lets make it even better.Lets make the number of words to be added
| |
| to a variable.You'll pass the number in register D1.Now you need
| |
| to do a different sort of programming,since you can't do it with
| |
| the old methods.
| |
| Lets use a loop.You need to add D1 words.You can use(a0)+ as the
| |
| addressing method(address register indirect with post increment),
| |
| since this automatically gets you to the next word.
| |
| Now for the loop.You'll have D1 decremented by one every time the
| |
| contents of the pointer are added.If D1 is zero,then you're done.
| |
| Otherwise,you need another addition.The program looks like this:
| |
|
| |
| ;(4.1C)
| |
| adding2:
| |
| clr.l d0 ;Erase D0
| |
| move.l #table,a0 ;Put table addresses in A0
| |
| move #$5,d1 ;Put number of entries in D1
| |
|
| |
| loop: ;Label for loop beginning
| |
| add (a0)+,d0 ;Add a word
| |
| subq #1,d1 ;Decrement counter
| |
| bne loop ;Continue if non-zero
| |
| rts ;Else done
| |
|
| |
| table: dc.w 2,4,6,8,10
| |
| end
| |
|
| |
| Lets take a close look at this program.Load the pointer A0 with
| |
| the addresses of the data and the counter D1 with the number of
| |
| elements.Then you can single step thru the program and watch the
| |
| results.Make sure not to run the final command,the RTS command,
| |
| because otherwise a return address is popped from the stack,and
| |
| the results of this are unpredictable.(SEKA owners can use"x pc"
| |
| to point the program counter to "adding2".You can then step thru
| |
| the program by using the "s"command and watch the results).
| |
| To finish up this example,your assigning a little homework.Write
| |
| the program so that it adds single bytes or long words.Try to
| |
| write a program that takes the first value in a table and
| |
| subtracts the following values.For example,the table
| |
|
| |
| table: dc.w 50,8,4,6
| |
|
| |
| should return the value 50-8-4-6, ie 32 ($20).
| |
|
| |
| 4.2.Sorting a Table.
| |
| -------------------
| |
| Lets keep working with tables.You don't want to just read data
| |
| from one this time.You want to change it.You'll assort the table
| |
| in assending order.
| |
| You need to decide how to do the sorting.The simplest method is to
| |
| do the following.
| |
| Compare the first and the second value.If the second value is
| |
| larger than the first one,things are OK so far.Do the next step,
| |
| compare the second and third values,and so on.If you come to the
| |
| final pair and in each case the preceding value was smaller than
| |
| the following value,then the sorting is done(it was unnescessary).
| |
| If you find a pair where the second value is smaller than the
| |
| first,the two values are exchanged.You then get a flag(here let's
| |
| use a register)that is checked once you're done going thru the
| |
| table.If it is set,the table probably isn't completely sorted.You
| |
| then erase the flag and start again from the beginning.If the flag
| |
| is still zero at the end,the sorting is complete.
| |
| Now let's write a program to do this.First let's figure out the
| |
| variables you need.You'll use registers for the variables.You need
| |
| a pointer to the table you're sorting(A0),a counter(D0)and a flag
| |
| (D1).While the program is running,change these values,so you'll
| |
| need two more registers to store the starting values(address and
| |
| the number of the table entries).You'll use A1 and D2.
| |
| Let's start writing the program,each section will be written and
| |
| then explained.Then the complete program will be given.You put the
| |
| tables address in A1 and the number of entries in D2.
| |
|
| |
| ;(4.2A) part of sort routine
| |
| sort: ;Start address of the program
| |
| move.l #table,a1 ;Load pointer with address
| |
| move.l a1,a0 ;Copy pointer to working register
| |
| move.l #5,d2 ;Number in the counter
| |
| move.l d2,d0 ;Copy number of elements
| |
| subq #2,d0 ;Correct conter value
| |
| clr d1 ;Erase flag
| |
|
| |
| table: dc.w 3,6,9,5
| |
|
| |
| end
| |
|
| |
| Now the preparations are complete.The pointer and the counter are
| |
| ready and the flag is cleared.The counter is decremented by two
| |
| because you want to use the DBRA command(take one off)and only X-1
| |
| comparrisons are needed for X numbers(take off one more).
| |
| Next let's write the loop that compares the values.You compare one
| |
| word with another.It looks like this:
| |
|
| |
| loop:
| |
| move 2(a0),d3 ;Next value in register D3
| |
| cmp (a0),d3 ;Compare values
| |
|
| |
| You need to use register D3 because CMP (A0),2(A0) isn't a legal
| |
| choice.If the second value is greater than or equal to the first
| |
| value,you can skip an exchange.
| |
|
| |
| bcc noswap ;Branch if greater than or equal
| |
| ;to
| |
|
| |
| Now you need to do the exchanging(unfortunatly you can't use EXC
| |
| 2(a0),(a0) since this form of addressing does not exist).
| |
|
| |
| doswap:
| |
| move (a0),d1 ;Save first valus
| |
| move 2(a0),(a0) ;Copy second into first word
| |
| move d1,2(a0) ;Move first into second
| |
| moveq #1,d1 ;Set flag
| |
| noswap:
| |
|
| |
| Now increment the counter and continue with the next pair.You do
| |
| this until the counter is negative.
| |
|
| |
| addq.l #2,a0 ;Pointer+2
| |
| dbra d0,loop ;Continuing looping until the end
| |
|
| |
| Now you'll see if the flag is set.You start again at the beginning
| |
| if it is.
| |
|
| |
| tst d1 ;Test flag
| |
| bne sort ;Not finished sorting yet!
| |
| rts ;Otherwise done.Return
| |
|
| |
| If the flag is zero,you're done and the subroutine ends.You jump
| |
| back to the main program using the RTS command.
| |
| Now a quick overview of the complete program.
| |
|
| |
| ;(4.2B)
| |
| sort: ;Start address of the program
| |
| move.l #table,a1 ;Load pointer with address
| |
| move.l a1,a0 ;Copy pointer to working register
| |
| move.l #5,d2 ;Number in the counter
| |
| move.l d2,d0 ;Copy number of elements
| |
| subq #2,d0 ;Correct counter value
| |
| clr d1 ;Erase flag
| |
|
| |
| loop:
| |
| move 2(a0),d3 ;Next value in register D3
| |
| cmp (a0),d3 ;Compare values
| |
| bcc noswap ;Branch if greater than or equal to
| |
|
| |
| doswap:
| |
| move (a0),d1 ;Save first value
| |
| move 2(a0),(a0) ;Copy second into first word
| |
| move d1,2(a0) ;Move first into second
| |
| moveq #1,d1 ;Set flag
| |
|
| |
| noswap:
| |
| addq.l #2,a0 ;Pointer+2
| |
| dbra do,loop ;Continue looping until the end
| |
| tst d1 ;Test flag
| |
| bne sort ;Not finished sorting yet!
| |
| rts ;Otherwise done.Return
| |
|
| |
| table:
| |
| dc.w 10,8,6,4,2 ;When finished acceding
| |
|
| |
| end
| |
|
| |
| To test this subroutine,assemble the routine with AssemPro,save it
| |
| and then load it into the debugger.The table is directly after the
| |
| RTS,notice its order.Set a breakpoint at the RTS,select the
| |
| address with the mouse and press left-Amiga-B sets a breakpoint in
| |
| AssemPro.Start the program the redisplay the screen by selecting
| |
| "Parameter-Display-Dissassem-bled"and examine the order of the
| |
| numbers in the table,they should now be ascending order.
| |
| You use several registers in this example for storing values.
| |
| Usually in machine language programming your subroutines cannot
| |
| change any register or can only change certain registers.For this
| |
| reason,there is a machine language command to push several
| |
| registers onto the stack at the same time.This is the MOVEM ("MOVE
| |
| multiple")command.If you insert this command twice in your program
| |
| then you can have the registers return to the main program with
| |
| the values they had when the subroutine was called.To do this,you
| |
| need one more label.Let's call it"start";the subroutine is started
| |
| from here.
| |
|
| |
| start:
| |
| movem.l d0-d7/a0-a6,-(sp) ;save registers
| |
|
| |
| sort:
| |
| etc...
| |
| ...
| |
| ...
| |
| bne sort ;not finished sorting yet!
| |
|
| |
| movem.l (sp)+,d0-d7/a0-a6 ;retrieve registers
| |
| rts ;finished
| |
|
| |
| The powerful command moves several registers at the same time.You
| |
| can specify which registers should be moved.If you want to move
| |
| the D1,D2,D3,D7,A2 and A3 registers,just write
| |
|
| |
| movem.l d1-d3/d7/a2-a3,-(sp)
| |
|
| |
| Before you quit sorting,do one little homework assignment.Modify
| |
| the program,so that it sorts the elements in descending order.
| |
|
| |
| 4.3.Converting Number Systems.
| |
| -----------------------------
| |
| As we mentioned in the chapter on number systems,converting
| |
| numbers from one base to another can be rather difficult.There is
| |
| another form of numeric representation-as a string that can be
| |
| entered via the keyboard or output on the screen.
| |
| You want to look at some of the many conversions possible and
| |
| write programs to handle the task.You'll start by converting a hex
| |
| number into a string using binary numbers and then print the value
| |
| in hex.
| |
|
| |
| 4.3.1.Converting Hex To ASCII.
| |
| -----------------------------
| |
| First you need to set the start and finish conditions.In this
| |
| example,let's assume that data register D1 contains a long word
| |
| that should be converted into a 8-digit long string of ASCII
| |
| characters.You'll write it to a particular memory location so that
| |
| you can output it later.
| |
| The advantage of using hex instead of decimal is pretty clear in
| |
| this example.To find out the hexadecimal digit for a particular
| |
| spot in the number,you just need to take the corresponting 4 bits
| |
| (half byte)and do some work on it.A half byte(also called a
| |
| nibble)contains one hex digit.
| |
| You'll work on a half byte in D2.To convert this to a printable
| |
| character,you need to use the correct ASCII code.The codes of the
| |
| 16 characters that are used as hex digits are the following:
| |
|
| |
| 0 1 2 3 4 5 6 7 8 9 A B C D E F
| |
|
| |
| $30 $31 $32 $33 $34 $35 $36 $37 $38 $39 $41 $42 $43 $44 $45 $46
| |
|
| |
| To convert the digits 0-9,you just need to add $30.For the letters
| |
| A-F that correspond to the values 10-15,you need to add $37.The
| |
| program to evaluate a half byte must make a destinction between
| |
| values between 0 and 9 and those between A and F and add either
| |
| $30 or $37.
| |
| Now let's write a machine language subroutine that you'll call for
| |
| each digit in the long words hex representation.
| |
|
| |
| nibble:
| |
| and #$0f,d2 ;just keep low byte
| |
| add #$30,d2 ;add $30
| |
| cmp #$3a,d2 ;was it a digit?
| |
| bcs ok ;yes:done
| |
| add #7,d2 ;else add 7
| |
|
| |
| ok:
| |
| rts ;done
| |
|
| |
| This routine converts the nibble in D2 to an ASCII character that
| |
| corresponds to the hex value of the nibble.To convert an entire
| |
| byte,you need to call the routine twice.Here is a program to do
| |
| this.The program assumes that A0 contains the address of the
| |
| buffer that the characters are to be put in and that D1 contains
| |
| the byte that is converted.
| |
|
| |
| ;(4.3.1a) bin-hex
| |
| ; ;your program
| |
| lea buffer,a0 ;pointer to buffer
| |
| move #$4a,d1 ;byte to be converted(example)
| |
| bsr byte ;and convert
| |
| rts
| |
| ; ... ;more of your program
| |
| byte:
| |
| move d1,d2 ;move value into d2
| |
| lsr #4,d2 ;move upper nibble into lower nibble
| |
| bsr nibble ;convert d2
| |
| move.b d2,(a0)+ ;put character into buffer
| |
| move d1,d2 ;value in d2
| |
| bsr nibble ;convert lower nibble
| |
| move.b d2,(a0)+ ;and put it in buffer
| |
| rts ;done
| |
| nibble:
| |
| and #$0f,d2 ;just keep low byte
| |
| add #$30,d2 ;add $30
| |
| cmp #$3a,d2 ;was it a digit?
| |
| bcs ok ;yes:done
| |
| add #7,d2 ;else add 7
| |
| ok:
| |
| rts ;done
| |
| buffer:
| |
| blk.b 9,0 ;space for long word data
| |
|
| |
| end
| |
|
| |
| To test this subroutine,use AssemPro to assemble the routine,save
| |
| the program and load it intoi the debugger.Next set a breakpoint
| |
| at the first RTS,to set the breakpoint in AssemPro select the
| |
| correct address with the mouse and press the right-Amiga-B keys.
| |
| Start the program and watch the contents of D2,it is first $34
| |
| (ASCII 4)and finally $41(ASCII A).Select "Parameter-Display-HEX-
| |
| Dump"and you'll see that 4A has been moved into the buffer.
| |
| This is how the routine operates.First,you move the value that you
| |
| wish to convert into D2.Then you shift the register four times to
| |
| the right to move the upper nibble into the lower four bits.After
| |
| the subroutine call,you use "move.b d2,(a0)+"to put the HI nibble
| |
| in the buffer.Then the original byte is put in D2 again.It is
| |
| converted.This gives us the LO nibble as an ASCII character in D2.
| |
| You put this in the next byte of the buffer.
| |
| The buffer is long enough to hold a long word in characters and
| |
| closing the null byte.The null byte is usually required by screen
| |
| output routines.Screen output will be discussed in a later
| |
| chapter.Now let's worry about converting a long word.
| |
| When converting a long word,you need to be sure to deal with the
| |
| nibbles in the right order.Before calling the "nibble"routine for
| |
| the first time,you need to move the upper nibble into the lower 4
| |
| bits of the long word.You need to do this without losing anything.
| |
| The LSR command isn't very good for this application.If you use it
| |
| you'll lose bits.Its better to use the rotation commands like ROR
| |
| or ROL,since they move the bits that are shifted out,back in on
| |
| the other side.
| |
| If you shift the original long word in D1 four times to the left,
| |
| the upper four bits are shifted into the lower four bits.Now you
| |
| can use our "nibble"routine to evaluate it and then put the
| |
| resulting ASCII character in the buffer.You repeat this eight
| |
| times and the whole long word has been converted.You even have D1
| |
| looking exactly the way it did before the conversion process began
| |
|
| |
| ;(4.3.1B) bin-hex-2
| |
| hexlong:
| |
| lea buffer,a0 ;pointer to the buffer
| |
| move.l #$12345678,d1 ;data to convert
| |
| move #7,d3 ;counter for the nibbles:8-1
| |
|
| |
| loop:
| |
| rol #4,d1 ;move upper nibble into lower
| |
| move d1,d2 ;write in d2
| |
| bsr nibble ;and convert it
| |
| move.b d2,(a0)+ ;character in buffer
| |
| dbra d3,loop ;repeat 8 times
| |
| rts ;finished!
| |
|
| |
| nibble:
| |
| and #$0f,d2 ;just keep low byte
| |
| add #$30,d2 ;add $30
| |
| cmp #$3a,d2 ;was it a digit?
| |
| bcs ok ;yes:done
| |
| add #7,d2 ;else add 7
| |
|
| |
| ok:
| |
| rts ;done
| |
|
| |
| buffer:
| |
| blk.b 9,0 ;space for long word,null byte
| |
|
| |
| end
| |
|
| |
| To test this subroutine,use AssemPro to assemble the routine,save
| |
| the program and load it into the debugger.Next set a breakpoint at
| |
| the first RTS,to set the breakpoint in AssemPro select the correct
| |
| address with the mouse and press the right-Amiga-B keys.Start the
| |
| program and when it is finished redisplay the output by selecting
| |
| "Parameter-Display-HEX-dump"so you can examine the new buffer
| |
| contents.
| |
| You'll find that there's an error in the program-the buffer
| |
| contains the digits "56785678"instead of "12345678".Try to find
| |
| the error.
| |
| Have you found it?This is the sort of error that causes you to
| |
| start pulling your hair out.This sort is hard to find.The
| |
| assembler assumes that the rotation operation should be done on a
| |
| word,because the ".l"was left off.As a result,only the lower word
| |
| of D1 was rotated-so you get the same value twice.If you change
| |
| the "rol.l",things work just right.
| |
| The error shows how easy it is to convert the program above into
| |
| one that converts four digit hex numbers into ASCII characters.
| |
| Just leave off the ".l"on the "rol"command and change the counter
| |
| from seven to three.The program is done.
| |
| Now for a little homework:change the program so that it can handle
| |
| six digit hex numbers(D1 doesn't nescessarily have to stay the
| |
| same...)!
| |
| Now lets look at a different conversion problem:converting a four
| |
| digit decimal number.
| |
|
| |
| 4.3.2.Converting Decimal To ASCII.
| |
| ---------------------------------
| |
| It's not quite as easy to convert decimal as hex.You can't group
| |
| the bits to form individual digits.You need to use another method.
| |
| Lets look at how a decimal number is constructed.In a four digit
| |
| number,the highest place is in the thousands place,the next is the
| |
| hundreds place,etc...
| |
| If you have the value in a register and divide by 1000,you'll get
| |
| the value that goes in the highest place in the decimal number.
| |
| Since the machine language command DIV not only gives us the
| |
| result of the division but also gives us the remainder,you can
| |
| work out the remainder quite easily.You divide the remainder by
| |
| 100 to find the hundreds place,divide the remainder by 10 and get
| |
| the tens place,and the final remainder is the ones place.
| |
| This isn't so hard after all!Heres the program that follows the
| |
| steps above to fill the buffer with D1's ASCII value.
| |
|
| |
| main:
| |
| lea buffer,a0 ;pointer to the buffer
| |
| move #1234,d1 ;number to convert
| |
| jsr deci_4 ;test subroutine
| |
| illegal ;room for breakpoint
| |
|
| |
| deci_4: ;subroutine-four digit numbers
| |
|
| |
| divu #1000,d1 ;divide by 1000
| |
| bsr digit ;evaluate result-move remainder
| |
|
| |
| divu #100,d1 ;divide by 100
| |
| bsr digit ;evaluate result and move
| |
|
| |
| divu #10,d1 ;divide by 10
| |
| bsr digit ;evaluate result-move remainder
| |
|
| |
| ;evaluate the remainder directly
| |
|
| |
| digit:
| |
| add #$30,d1 ;convert result into ASCII
| |
| move.b d1,(a0)+ ;move it into buffer
| |
| clr d1 ;erase lower word
| |
| swap d1 ;move the remainder down
| |
| rts ;return
| |
|
| |
| buffer:blk.b 5,0 ;reserve bytes for result
| |
|
| |
| end
| |
|
| |
| To test this subroutine,use AssemPro to assemble the routine,save
| |
| the program and load it into the debugger.Next set a breakpoint at
| |
| the illegal instruction.To set the breakpoint in AssemPro select
| |
| the correct address with the mouse and press the right-Amiga-B
| |
| keys.This breakpoint stops the program.Start the program and when
| |
| it is finished redisplay the output by selecting"Parameter-Display
| |
| -HEX-dump"so you can examine the ASCII values now in the buffer.
| |
| You use a little trick in this program that is typical for machine
| |
| language programming.After calling"digit"three times from the sub-
| |
| routine"deci_4",you go right into the"digit"subroutine.You don't
| |
| use a BSR or JSR command.Once the processor hits the RTS command,
| |
| it returns to the main program,not the "deci_4"subroutine.Doing
| |
| this,you save a fourth "bsr digit"command and an "rts"command for
| |
| the "deci_4"routine.
| |
| Try the program out.Make sure that you use values that are smaller
| |
| than 9999,because otherwise strange things can happen.
| |
| Now let's reverse what you've been doing and convert strings into
| |
| binary numbers.
| |
|
| |
| 4.3.3.Converting ASCII To Hex.
| |
| -----------------------------
| |
| In a string,each hex digit represents a half byte.You just need to
| |
| write a program that exactly reverses what the hex conversion
| |
| program did.
| |
| You have two choices
| |
|
| |
| 1. The number of hex digits is known in advance
| |
| 2. The number is unknown
| |
|
| |
| The first is easier to program,but has the disadvantage that if,
| |
| you assume the strings are four digits in length and want to enter
| |
| the value 1,you must enter 0001.That is rather awkward,so you'll
| |
| use the second method.
| |
| Let's convert a single digit first.You'll pass a pointer to this
| |
| digit in address register A0.You want the binary value to come
| |
| back in data register D0.
| |
| The program looks like this:
| |
|
| |
| move.l #string,a0 ;this example
| |
| jsr nibblein ;test routine
| |
| nop ;set breakpoint here
| |
|
| |
| nibblein: ;*convert the nibble from (A0)
| |
| clr.l d0 ;erase D0
| |
| move.b (a0)+,d0 ;get digit,increment A0
| |
| sub #'A',d0 ;subtract $41
| |
| bcc ischar ;no problem:in the range A-F
| |
|
| |
| add #7,d0 ;else correct value
| |
| ischar:
| |
| add #10,d0 ;correct value
| |
| rts
| |
|
| |
| string:dc.b 'B',0 ;character to convert
| |
|
| |
| end
| |
|
| |
| To test this subroutine,use AssemPro to assemble the routine,save
| |
| the program and load it into the debugger.Next set a breakpoint at
| |
| the first NOP,to set the breakpoint in AssemPro select the correct
| |
| address with the mouse and press the right-Amiga-B keys.Start the
| |
| program and watch the contents of D0.
| |
| Let's see how the program works.A0 points to a memory location
| |
| that contains the character "B"that is represented by the ASCII
| |
| value $42.This number is loaded into D0 right after this register
| |
| is erased.
| |
| After subtracting $41,you end up with the value $1.Now you're
| |
| almost done.Before returning to the main program,you add 10 to get
| |
| the correct value 11,$B.
| |
| If the buffer has a digit in it,the subtraction causes the
| |
| register to become negative.The C flag is set.Let's take the digit
| |
| 5 as an example.
| |
| The ASCII value of 5 is $35.After subtracting $41,you end up with
| |
| -12 and the C flag is set.In this case,you won't branch with the
| |
| BCC command.Instead you'll add 7 to get -5.Then 10 is added,and
| |
| you end up with 5.Done!
| |
| The routine as a disadvantage.If an illegal character is given,one
| |
| that doesn't represent a hex digit,you'll get some nonsense result
| |
| Let's ignore error checking for the moment though.
| |
| Let's go on to multi-digit hex numbers.The first digit that you
| |
| convert has the highest value and thus represents the highest
| |
| nibble.To allow for this and to allow for an arbitrarily long
| |
| number(actually not arbitrarily long,the number should fit in a
| |
| long word-so it can only be eight digits long),you'll use a trick.
| |
| Take a look at the whole program.It handles the calculations and
| |
| puts the result in D1.It assumes that A0 is a pointer to a string
| |
| and that this string is ended by null byte.
| |
|
| |
| hexin: ;converting a hex number
| |
| clr.l d1 ;first erase D1
| |
| move.l #string,a0 ;address of the string in A0
| |
| jsr hexinloop ;test subroutine
| |
| nop ;set breakpoint here
| |
|
| |
| hexinloop:
| |
| tst.b (a0) ;test digit
| |
| beq hexinok ;zero,then done
| |
| bsr nibblein ;convert digit
| |
| lsl.l #4,d1 ;shift result
| |
| or.b d0,d1 ;insert nibble
| |
| bra hexinloop ;and continue
| |
|
| |
| hexinok:
| |
| rts
| |
|
| |
| nibblein:
| |
| clr.l d0 ;convert the nibble from (A0)
| |
| move.b (a0)+,d0 ;get digit,increment A0
| |
| sub #'A',d0 ;subtract $41
| |
| bcc ischar ;no problem:in range A-F
| |
| add #7,d0 ;else correct value
| |
|
| |
| ischar:
| |
| add #10,d0 ;correct value
| |
| rts
| |
|
| |
| string:DC.B "56789ABC',00 ;eight digit string,null byte
| |
| ;to be converted
| |
| end
| |
|
| |
| To test this subroutine,use AssemPro to assemble the routine,save
| |
| the program and load it into the debugger.Next set a breakpoint at
| |
| the NOP,to set the breakpoint in AssemPro select the correct
| |
| address with the mouse and press the right-Amiga-B keys.Start the
| |
| program and watch the contents of D1,the hex value is placed in
| |
| this register.
| |
| The trick is to shift left four times,to shift one nibble.In this
| |
| way,the place of the last digit is incremented by one and there is
| |
| room for the nibble that comes back from the "nibblein"routine.The
| |
| program uses the TST.B instruction to check for the null byte at
| |
| the end of the string,when it encounters the null byte the program
| |
| ends.The result is in the D1 long word already!
| |
| To do some error checking,you need to make some changes in the
| |
| program.You'll do this right after you come back from the"nibblin"
| |
| routine with the value of the current character.
| |
| If the value in D0 is bigger than $F,there is an error.You can
| |
| detect this in several ways.You chose the simplest one-you'll use
| |
| CMP #$10,D0 to compare D0 with $10.If it smaller,then the C flag
| |
| is set(since CMP uses subtraction)and everything is fine.If C is
| |
| zero,there is an error.
| |
| You can use this trick to skip the test for a null byte,since its
| |
| an invalid character as well.The program looks like this:
| |
|
| |
| ;(4_3_3C) hex-conv2 optional disk name
| |
| hexin: ;converting a hex number
| |
| clr.l d1 ;first erase D1
| |
| move.l #string,a0 ;address of string in A0
| |
| jsr hexinloop ;test subroutine
| |
| nop ;set breakpoint here
| |
|
| |
|
| |
| hexinloop:
| |
| bsr nibblein ;convert digit
| |
| cmp $10,d0 ;test if good
| |
| bcc hexinok ;no,then done
| |
| lsl.l #4,d1 ;shift result
| |
| or.b d0,d1 ;insert nibble
| |
| bra hexinloop ;and continue
| |
|
| |
| hexinok:
| |
| rts
| |
|
| |
| nibblein: ;convert the nibble from (A0)
| |
| clr.l d0 ;erase D0
| |
| move.b (a0)+,d0 ;get digit,increment A0
| |
| sub #'A',d0 ;subtract $41
| |
| bcc ischar ;no problem:in the range A-F
| |
| add #7,d0 ;else correct value
| |
|
| |
| ischar:
| |
| add #10,d0 ;correct value
| |
| rts
| |
|
| |
| string:DC.B "56789ABC',00 ;8 digit string ending with a
| |
| ;null byte to be converted
| |
| end
| |
|
| |
| To test this subroutine,use AssemPro to assemble the routine,save
| |
| the program and load it into the debugger.Next set a breakpoint at
| |
| the NOP,to set the breakpoint in AssemPro select the correct
| |
| address with the mouse and press the right-Amiga-B keys.Start the
| |
| program and watch the contents of D1,the hex value is placed in
| |
| this register.
| |
| This is the method for converting hex to binary.If you convert
| |
| decimal to binary,the conversion is not much harder.
| |
|
| |
| 4.3.4.Converting ASCII To Decimal.
| |
| ---------------------------------
| |
| You can use a very similar method to the one used above.Since you
| |
| are not sure how many digits there are,you'll use a similar method
| |
| for putting digits of a number in the next place up.You can't do
| |
| this with shifting,but you can multiply by 10 and add the value of
| |
| the digit.
| |
| Heres the program for converting decimal numbers.
| |
|
| |
| decin: ;converting a decimal number
| |
| clr.l d1 ;first erase D1
| |
| move.l #string,a0 ;the string to convert
| |
| jsr decinloop ;test subroutine
| |
| nop ;breakpoint here
| |
|
| |
| decinloop:
| |
| bsr digitin ;convert digit
| |
| cmp #10,d0 ;test,if valid
| |
| bcc decinok ;no,then done
| |
| mulu #10,d1 ;shift result
| |
| add d0,d1 ;insert nibble
| |
| bra decinloop ;and continue
| |
|
| |
| decinok:
| |
| rts ;end of conversion
| |
|
| |
| digitin: ;converting the nibble from (A0)
| |
|
| |
| clr.l d0 ;erase D0
| |
| move.b (a0)+,d0 ;get digit,increment A0
| |
| sub #'0',d0 ;subtract $30
| |
| rts
| |
|
| |
| string:dc.b '123456' ;ASCII decimal string to convert
| |
|
| |
|
| |
| end
| |
|
| |
| To test this subroutine,use AssemPro to assemble the routine,save
| |
| the program and load it into the debugger.Next set a breakpoint at
| |
| the NOP,to set the breakpoint in AssemPro select the correct
| |
| address with the mouse and press thr right-Amiga-B keys.Select
| |
| "Parameter-Output-numbers-Decimal"so the registers are displayed
| |
| as decimal numbers.Then start the program and watch the contents
| |
| of D1,the decimal value is placed in this register.
| |
| This program can ONLY convert numbers upto 655350,although the hex
| |
| conversion routine can go higher.Thats because the MULU command
| |
| can only multiply 16-bit words.The last multiplication that can be
| |
| done correctly is $FFFF*10--65535*10,which gives us the value
| |
| 655350.Normally this is a large enough range,so you won't
| |
| complicate the program further.
| |
|
| |
| CHAPTER 5.
| |
| ---------
| |
| 5.Hardware Registers.
| |
| --------------------
| |
| You can get information about hardware functions without using
| |
| library functions.You can use the hardware registers instead.These
| |
| are memory locations at particular addresses that are neither in
| |
| RAM nor in ROM.They are direct interfaces between the processor
| |
| and its peripheral devices.
| |
| Each device has a number of hardware registers that the processor
| |
| accesses to control graphics,sound and input/output.There are lots
| |
| of possibilities for assembly language programmers.We'll only be
| |
| able to go into a few examples.
| |
| The registers are generally used in byte-wise fashion.You'll find
| |
| an example in the next chapter.
| |
|
| |
| 5.1.Checking For Special Keys.
| |
| -----------------------------
| |
| Load AssemPro and enter the debugger,select "Parameter-Display-
| |
| From-Address"and enter $BFEC00.Next select "Parameter-Display-Hex
| |
| -Dump"to display the memory.(To use the SEKA assembler or a similar
| |
| monitor program,enter "q $bfec00".)
| |
| Yoy'll see a byte-wise listing of the addresses starting at
| |
| $BFEC00 in which two bytes always repeat.These two bytes represent
| |
| the status of the two hardware registers.
| |
| The mirroring occurs because not all the address bits are used in
| |
| decoding the address.In addressing this register,only the upper
| |
| two bytes of the address and the low bit,bit 0,are used.The
| |
| address of the two registers goes like this:$BFECxx,where the
| |
| lower address byte xx doesn't contain any information in bits 1-7.
| |
| Only bit 0 contains information about the desired register.You'll
| |
| find this odd form of addressing with most hardware registers.
| |
| Let's look at the information in these registers.Let's look at the
| |
| second register,$BFEC01.Hold down the<ALT>key and select"Parameter
| |
| -Display-HEX-Dump"to redisplay the screen.(SEKA owners must enter
| |
| "q $bfec00"and press the<ALT>key right after pressing the<Return>
| |
| key.)You'll see that contents of every two bytes($BFEC01,$BFEC03,
| |
| etc...)have been changed to $37.This is the status of the special
| |
| keys.This is also true for the other special keys.The following
| |
| keys produce the bytes:
| |
|
| |
| Shift left $3F
| |
| Shift right $3D
| |
| Control $39
| |
| Alternate $37
| |
| Amiga left $33
| |
| Amiga right $31
| |
|
| |
| You can use this register to have a machine language program check
| |
| if one of these keys was pressed and then respond by calling or
| |
| ending a function.A program section might look like this:
| |
|
| |
| skeys=$bfec01
| |
| ...
| |
| cmp.b #$37,skeys ;Alternate pressed?
| |
| beq function1 ;Yes!
| |
| cmp.b #$31,skeys ;or right Amiga?
| |
| beq function2 ;Yes!
| |
| ... ;and so on...
| |
|
| |
| 5.2.Timing.
| |
| ----------
| |
| If you want to find out how much time elapsed between two events,
| |
| you can use a hardware register to keep track of time quickly and
| |
| precisely.The Amiga contains just such a timekeeper:the I/O port
| |
| componant.The chip has a 24 bit wide counter that has a 60 Hertz
| |
| clock.
| |
| These 24 bits can't be read at once,for instance with a MOVE.L
| |
| command,because the register is divided into three bytes.The low
| |
| byte is at address $BFE801,the middle at $BFE901,and the high byte
| |
| with bits 16-23 at $BFEA01.
| |
| Here's an example of a way to use this register:finding out how
| |
| long a subroutine takes to run.
| |
|
| |
| test:
| |
| bsr gettime ;put current time in D7
| |
| move.l d7,d6 ;save it in D6
| |
| bsr routine ;routine to be timed
| |
| bsr gettime ;get the time again
| |
| sub.l d6,d7 ;elapsed time in
| |
| ... ;1/50 seconds in D7!
| |
| nop ;set break point here to stop
| |
|
| |
| routine: ;test routine
| |
| move #500,d0 ;delay counter
| |
|
| |
| loop:
| |
| dbra d0,loop ;count down
| |
| rts
| |
|
| |
| gettime:
| |
| move.b $bfea01,d7 ;hi-byte in D0
| |
| lsl.l #4,d7 ;shift twice by 4 bits
| |
| lsl.l #4,d7 ;(8 bits shifted)
| |
| move.b $bfe901,d7 ;get mid-byte
| |
| lsl.l #4,d7
| |
| lsl.l #4,d7 ;shift again
| |
| move.b $bfe801,d7 ;get the lo-byte
| |
| rts ;done
| |
|
| |
| 5.3.Reading The Mouse-Joystick.
| |
| -------------------------------
| |
| There are two hardware registers for the mouse and the joystick.
| |
| They contain the state(or the position)of these input devices.Its
| |
| interesting that the same port is used with both the mouse and the
| |
| joystick even through they work completely different.
| |
| The joystick as four switches that are closed during movement and
| |
| give off a potential(-)that is related to the movement of the
| |
| joystick/mouse.The mouses movements give off lots of quick signals
| |
| -two for horizontal and two for vertical movements.
| |
| The computor must keep an eye on the ports so that it can evaluate
| |
| the signals and calculate the new mouse position.This isn't the
| |
| work of the processor though;it already has too much to do.
| |
| You find the status of the mouse/joystick port at address $DFF00A
| |
| for port 1 and $DFF00C for port 2.The information in these words
| |
| is for vertical mouse movement in the lower byte and for
| |
| horizontal movement in the upper byte.
| |
| AssemPro owners be careful!Don't read these addresses,because for
| |
| some reason that causes the computor to crash.This looks
| |
| interesting(the screen begins to dance)but you can only recover by
| |
| pressing <RESET>and losing all your data.
| |
| To read this register,lets write a short program:
| |
|
| |
| ;(5.3A) mouse
| |
|
| |
| test:
| |
| jsr run ;test subroutine
| |
| jmp test ;continue until broken
| |
| nop ;breakpoint here
| |
|
| |
| joy= $dff00a
| |
|
| |
| run:
| |
| move joy,d6 ;data item 1 in D6
| |
| move joy+2,d7 ;data item 2 in D7
| |
| jmp run ;rts for SEKA and other
| |
|
| |
| end
| |
|
| |
| If you assemble the program and start breakable in the debugger
| |
| (SEKA-"j run"),D6 and D7 contain the contents of the two registers
| |
| Move the mouse a bit and watch the register contents.
| |
| As you see,the value in D6 is different.If you just move the mouse
| |
| horizontaly,only the upper bytes value is different,if just moved
| |
| vertically only the upper byte is different.
| |
| You are not getting the absolute position of the mouse pointer on
| |
| the screen.You can see that easily by moving the mouse in the
| |
| upper left corner,then reading the value by restarting the program
| |
| and moving the mouse left again.As you can see,the registers
| |
| contents are always relative.
| |
| Change the program as follows:
| |
|
| |
| ;(5.3B) mouse difference
| |
|
| |
| test:
| |
| jsr run ;test subroutine
| |
| jmp test ;continue until broken
| |
| nop ;breakpoint here
| |
|
| |
| joy= $dff00a
| |
|
| |
| run:
| |
| move d7,d6 ;old position in D6
| |
| move joy,d7 ;new position in D7
| |
| sub d7,d6 ;difference in D6
| |
| jmp run ;rts for SEKA and other
| |
|
| |
| end
| |
|
| |
| Start Breakable(right-Amiga-A)in the AssemPro debugger and watch
| |
| D6,the result is zero or D7.(SEKA owners have to start the program
| |
| two times.The result in D6 is zero.)If you move the mouse,D6
| |
| contains the difference between the old and new positions since
| |
| the start.You'll find the vertical and horizontal positions of the
| |
| mouse relative to the last time you looked.In this way,you can use
| |
| this register to find the relative mouse movement between two
| |
| checks.
| |
| Now to check the joysticks.Put a joystick in port 2 and change the
| |
| address $DFF00A to $DFF00C in the program.Start Breakable in the
| |
| AssemPro debugger and watch D6,the result is zero or D7.(SEKA
| |
| owners have to start the program two times.The result in D6 is
| |
| zero.)
| |
| Move the joystick up.You'll get the value $FF00.One was subtracted
| |
| from the upper byte.Let the joystick loose.This time you get the
| |
| value $100-one is added.You'll get the same effect if you move the
| |
| joystick left-after you let go,one is subtracted.
| |
| The individual movements and their effects on the joystick program
| |
| are:
| |
|
| |
| UP $FF00 HI-BYTE -1
| |
| DOWN $FFFF LO-BYTE -1
| |
| LEFT $0100 HI-BYTE +1
| |
| RIGHT $0001 LO-BYTE +1
| |
|
| |
| These values aren't terribly reliable.If you move the joystick a
| |
| lot and then look at the value,you'll find a crazy value in D6.
| |
| This is because the input driver thinks that a mouse is attached.
| |
| Nevertheless,this is the quickest way to read a joystick.In this
| |
| way,an external device that gives off evaluatable TTL signals can
| |
| be connected to the port and watched by a machine language
| |
| program.
| |
| Now you just need to find out whether the fire button has been
| |
| pressed,and you'll know how to get all the information you need
| |
| from the joystick.The buttons state is in bit 7 of the byte that
| |
| is in memory location $BFE001.If the bit is set,the button was'nt
| |
| pressed.That's true for the joystick connected to port 2.Bit 6 of
| |
| this byte contains the buttons state when the joystick is in port
| |
| 1 or the state of the left mouse button.
| |
| Let's stay on port 2.You can test bit 7 to execute a function when
| |
| the joystick button is pressed without any problems.Bit 7 is the
| |
| sign bit.You can use this program segment:
| |
|
| |
| tst.b $bfe001 ;was fire button 2 hit?
| |
| bpl fire ;yes!branch
| |
|
| |
| The TST.B instruction tests the addressed byte and sets the Z and
| |
| the N flag.If the N flag is set,you know that bit 7 of the tested
| |
| byte is set.Since the fire button turns on LO potential,the bit is
| |
| erased when the button is pressed.The N flag works that way with
| |
| the TST command as well.The BPL command in the program above
| |
| branches if the button was pressed.The PL stands for plus and is
| |
| set when the sign bit is cleared.
| |
| Here is the complete program to check the fire button and joystick
| |
| difference:
| |
|
| |
| ;(5.3C) fire button and joy difference
| |
|
| |
| test:
| |
| jsr run ;test subroutine
| |
| tst.b $bfe001 ;was fire button 2 hit?
| |
| bpl fire ;yes! branch
| |
| jmp test ;continue until broken
| |
|
| |
| joy = $dff00a
| |
| run:
| |
| move d7,d6 ;old position in D6
| |
| move joy,d7 ;new position in D7
| |
| sub d7,d6 ;difference in D6
| |
| jmp run ;rts for SEKA and other
| |
|
| |
| fire:
| |
| nop ;breakpoint here
| |
|
| |
| end
| |
|
| |
| 5.4.Tone Production.
| |
| -------------------
| |
| It's fun to make noises and sounds.The Amiga lets you use Audio
| |
| Devices and various I\O structures to play tones,noises and/or
| |
| music pieces in the background.You'll leave this method to C or
| |
| Basic programmers,since you can use short machine language
| |
| programs to directly program the audio hardware.
| |
| The Paula chip has all the capabilities needed for tone production
| |
| This chip can be accessed using the hardware registers of the
| |
| processor.No library of any high level language can do more than
| |
| you can-program the chip.
| |
| How does it work?Since the disk uses Direct Memory Access(DMA)to
| |
| get information,you just need to tell it where to look for the
| |
| tone or tone sequences that you would like played.You also need to
| |
| tell it how to interpret the data.
| |
| Lets start with the easiest case-producing a constant tone.A tone
| |
| like this consists of a single oscillation that is repeated over
| |
| and over.If you make a diagram of the oscillation,you see the wave
| |
| form of the oscillation.There are several standard waves: sine,
| |
| square,triangle and saw tooth.The simplest is the square wave.
| |
| To produce a square wave,you just need to turn the loud speaker on
| |
| and off.The frequency that occurs here is the frequency of the
| |
| tone.
| |
| You want to produce such a tone using the Amiga.First you need to
| |
| make a table that contains the amplitude of the tone you wish to
| |
| produce.For a square wave,you only need two entries in the table,a
| |
| large and a small value.Since the sound chip in the Amiga has
| |
| amplitude values between -128 and +127,our table looks like this:
| |
|
| |
| soundtab:
| |
| dc.b -100,100
| |
|
| |
| You need to give the address of the table to the sound chip.You
| |
| have four choices,since the Amiga as four sound channels.The
| |
| address of the hardware register in which the table address for
| |
| channel 0 must be written is $DFF0A0;for channel 1 it is $DFF0B0;
| |
| for channel 2 its $DFF0C0;for channel 3 its $DFF0D0.For stereo
| |
| output,channels 0 and 3 control the left loud speaker.Channels 1
| |
| and 2 control the right loud speaker.For example,choose channel 0
| |
| and write the following:
| |
|
| |
| move.l #soundtab,$DFF0A0 ;address of the table
| |
|
| |
| Next you need to tell the sound chip how many items there are in
| |
| the table.The data is read from beginning to end and sent to the
| |
| loud speaker.Once it reaches the end,it starts over at the
| |
| beginning.Since the sound chip gets this one word at a time,even
| |
| though the data is in bytes,the table must always have an even
| |
| number of bytes.The length that you give it is the number of words
| |
| the number of bytes/2.
| |
| You put the length for channel 0 in the register at address
| |
| $DFF0A4(for channel x just add x*$10!):
| |
|
| |
| move #1,$dff0a4 ;length of table in words
| |
|
| |
| Now you have to tell it how quickly to read the data and output it
| |
| to the loud speaker.This word determines the frequency.However,it
| |
| does this "backwards".The larger the value,the lower the frequency
| |
| Choose the value 600 for this example:
| |
|
| |
| move #600,$dff0a6 ;read in rate
| |
|
| |
| Now you need to decide the loudness level for the tone or noise.
| |
| You have 65 different levels to choose from.Lets choose the middle
| |
| value 40 for our example:
| |
|
| |
| move #40,$dff0a8 ;loudness level
| |
|
| |
| Thats the data that the sound chip needs to produce the tone.
| |
| However nothing happens yet.What next?The chip can't tell if the
| |
| data thats in the registers is valid,so it doesn't know if it
| |
| should use the data.
| |
| You need to work with the DMA control register at address $DFF096
| |
| to let it know.You only need six bits of this word for your
| |
| purposes:
| |
|
| |
| Bit 15 ($8000) If this bit is set,every bit that is written to
| |
| this internal register is set.Otherwise the bits
| |
| are erased.Zero bits aren't affected.This is very
| |
| useful because this word also contains DMA
| |
| information for disk operations that should'nt be
| |
| changed.
| |
|
| |
| Bit 9 ($200) This bit makes it posible for the chip to access
| |
| DMA memory.If you want to start playing the tone,
| |
| you need to set this bit.
| |
|
| |
| Bit 0-3 Turn channel 0-3 on when bits are set.
| |
|
| |
| You'll start your tone by setting bits 15,9 and 0:
| |
|
| |
| move #$8000+$200+1,$dff096 ;start DMA
| |
|
| |
| Heres an example of tone production-this time with tone using a
| |
| sine wave:
| |
|
| |
| ;**Sound Generation using hardware registers** (5.5A)
| |
|
| |
| ctlw = $dff096 ;DMA control
| |
| cothi = $dff0a0 ;table address HI
| |
| c0tlo = $c0thi+2 ;table address LO
| |
| c0tl = $c0thi+4 ;table length
| |
| c0per = $c0thi+6 ;read in rate
| |
| c0vol = $c0thi+8 ;loudness level
| |
|
| |
| run: ;*Produce a simple tone
| |
| move.l #table,c0thi ;table beginning
| |
| move #8,c0tl ;table length--8 words
| |
| move #400,c0per ;read in rate
| |
| move #40,c0vol ;loudness level (volume)
| |
| move #$8201,ctlw ;DMA/Start sound
| |
| rts
| |
|
| |
| data ;>500K place in CHIP memory
| |
| table: ;sound table:sine
| |
| dc.b -40,-70,-40,0,40,70,40,0
| |
|
| |
| end
| |
|
| |
| To test this subroutine,use AssemPro to assemble the routine,save
| |
| the program and load it into the debugger.Next set a breakpoint at
| |
| the RTS,to set the breakpoint in AssemPro select the correct
| |
| address with the mouse and press the right-Amiga-B keys.Start the
| |
| program and listen to the tone.You need another routine to turn
| |
| the tone off,turn your sound down for now.
| |
| To turn the tone off,you just need to erase bit 0 of the DMA
| |
| control register.To do this,you just need to write a 0 in bit 15
| |
| and all the set bits in this register are erased.To erase bit 0,
| |
| just write a one to the memory location:bit 15=0=> bit 0 is erased
| |
| Heres a small routine to stop the tone coming from channel 0:
| |
|
| |
| still: ;*turn off tone
| |
| move #1,ctlw ;turn off channel 1
| |
| rts
| |
|
| |
| Now lets use the routine in a program to produce a short peep tone
| |
| that you culd,for instance,use as a key click:
| |
|
| |
| ;** Producing a Peep Tone **
| |
| ctlw = $dff096 ;DMA control
| |
| c0thi = $dff0a0 ;HI table address
| |
| c0tlo = $c0thi+2 ;LO table address
| |
| c0tl = $c0thi+4 ;table length
| |
| c0per = $c0thi+6 ;read in rate
| |
| c0vol = $c0thi+8 ;volume
| |
|
| |
| beep: ;*Produce a short peep tone
| |
| move.l #table,c0thi ;table beginning
| |
| move #8,c0tl ;table length
| |
| move #400,c0per ;read in rate
| |
| move #65,c0vol ;volume
| |
| move #$8201,ctlw ;Start DMA (sound)
| |
| move.l #20000,d0 ;delay counter
| |
|
| |
| loop:
| |
| dbra d0,loop ;count down
| |
|
| |
| still:
| |
| move #1,ctlw ;turn off tone
| |
| rts
| |
|
| |
| table:
| |
| dc.b 40,70,90,100,90,70,40,0,-4,0
| |
| end
| |
|
| |
| You can play upto four tones at the same time in such a way that
| |
| they are independant of each other.The Amiga also offers another
| |
| method of making the sound more interesting:you can modulate the
| |
| tone.
| |
| Lets produce a siren tone.You could do this by figuring out the
| |
| entire sequence and programming it.However,as you can well imagine
| |
| thats a lot of work.
| |
| Its much easier to use two tone channels.Lets use channel 1 for
| |
| the bass tone and channel 0 for its modulation.Channel 0 needs to
| |
| hold the envelope of the siren tone.It needs to give the expanding
| |
| and contracting of the tone at the right speed.
| |
| You then have two ways that you can have channel zero work with
| |
| channel one.You can control the volume via channel 0,the read in
| |
| rate(frequency),or both.For our example,you'll use frequency
| |
| modulation.
| |
|
| |
| Change the program as follows:
| |
|
| |
| ;** Modulated sound generation via hardware registers **
| |
| ctlw = $dff096 ;DMA control
| |
| adcon = $dff09e ;Audio/Disk control
| |
| c0thi = $dff0a0 ;HI table address
| |
| c0tlo = c0thi+2 ;LO table address
| |
| c0tl = c0thi+4 ;table length
| |
| c0per = c0thi+6 ;read in rate
| |
| c0vol = c0thi+8 ;volume
| |
|
| |
| run:
| |
| move.l #table,c0thi+16 ;table start for channel 1
| |
| move #8,c0tl+16 ;table length--8 words
| |
| move #300,c0per+16 ;read in rate
| |
| move #40,c0vol+16 ;volume
| |
|
| |
| move.l #table2,c0thi ;table start for channel 0
| |
| move #8,c0tl ;table length
| |
| move #60000,c0per ;read in rate
| |
| move #30,c0vol ;volume
| |
|
| |
| move #$8010,adcon ;modulation mode:FM
| |
| move #$8203,ctlw ;start DMA
| |
| rts
| |
|
| |
| still: ;*Turn Off Tone
| |
| move #$10,adcon ;no more modulations
| |
| move #3,ctlw ;turn off channels
| |
| rts
| |
|
| |
| table: ;data for basic tone
| |
| dc.b -40,-70,-90,-100,-90,-70,-40,0
| |
| dc.b 40,70,90,100,90,70,40,0
| |
|
| |
| table2: ;data for modulation
| |
| dc.w 400,430,470,500,530,500,470,430
| |
|
| |
| end
| |
|
| |
| When you start the program,you'll here a siren.You can change this
| |
| tone to your hearts content.
| |
| Did you notice the added "adcon"register.This register controls
| |
| the modulation of the audio channel as well as handling disk
| |
| functions.The same technique is used here as for the DMA control
| |
| register,bits can only be set if bit 15 is.As a result,you don't
| |
| have to worry about the disk bits.I'd recommend against
| |
| experimentation.
| |
| Control bit 15 isn't the only one of interest to you.You can also
| |
| use bits 0-7,because they determine which audio channel modulates
| |
| another channel.There is a restriction,though.A channel can only
| |
| modulate the next higher numbered channel.For this reason you use
| |
| channel 1 for the basic tone and channel 0 for the modulation in
| |
| the example.You can't for example,modulate channel three with
| |
| channel zero.Channel 3 can't be used to modulate any other
| |
| channel.
| |
|
| |
| Here is an overview of bits 0-7 of the "adcon"register.
| |
|
| |
| Bit Function
| |
| -----------------------------------------------------------------
| |
| 0 Channel 0 modulates the volume of channel 1
| |
| 1 Channel 1 modulates the volume of channel 2
| |
| 2 Channel 2 modulates the volume of channel 3
| |
| 3 Turn of channel 3
| |
| 4 Channel 0 modulates the frequency of channel 1
| |
| 5 Channel 1 modulates the frequency of channel 2
| |
| 6 Channel 2 modulates the frequency of channel 3
| |
| 7 Turn off channel 3
| |
|
| |
| In the example,you set bit 4,which put channel 0 in charge of
| |
| channel one's frequency modulations.
| |
| When you've chosen a channel for use in modulating another channel
| |
| some of the parameters of the channel change.You don't need to
| |
| give volume for this channel,so you can omit it.Now the tables
| |
| data is looked at as words instead of as bytes.These words are
| |
| read into the register of the modulated register at a
| |
| predetermined rate.The Read in Rate Register determines the rate.
| |
| If you want to modulate the frequency and the volume of another
| |
| channel,(In the example,set bits 0 and 4 of "adcon"),the data is
| |
| interpreted a little differently.The first word in the table is
| |
| the volume,the second is the read in rate,and so on.It alternates
| |
| back and forth.In this way,you can for instance,produce the siren
| |
| tone.
| |
|
| |
| 5.5.Hardware Registers Overview.
| |
| -------------------------------
| |
| The following tables should give you an overview of the most
| |
| important hardware registers.Theres not enough room to describe
| |
| each register,so I'd recommend getting a hold of the appropriate
| |
| literature.If you experiment with these registers,you should keep
| |
| in mind that this can cause the computor to crash.Save your data
| |
| to disk and then take the disk out of the drive,because you might
| |
| cause the disk drive to execute some wierd functions.
| |
| Lets start with the PIA's.This covers the PIA type 8520.You should
| |
| keep in mind that some functions and connection of the 8520 are
| |
| integrated into the Amiga and so there are limitations on what you
| |
| can do with the PIA's.
| |
|
| |
| PIA A PIA B Registers Meaning
| |
| ------------------------------------------------------------------
| |
| BFE001 BFE000 Data register A
| |
| BFE101 BFE100 Data register B
| |
| BFE201 BFE200 Data direction register A
| |
| BFE301 BFE300 Data direction register B
| |
| BFE401 BFE400 Timer A LO
| |
| BFE501 BFE500 Timer A HI
| |
| BFE601 BFE600 Timer B LO
| |
| BFE701 BFE700 Timer B HI
| |
| BFE801 BFE800 Event register Bits 0-7
| |
| BFE901 BFE900 Event register Bits 8-15
| |
| BFEA01 BFEA00 Event register Bits 16-23
| |
| BFEB01 BFEB00 Unused
| |
| BFEC01 BFEC00 Serial data register
| |
| BFED01 BFED00 Interrupt control register
| |
| BFEE01 BFEE00 Control register A
| |
| BFEF01 BFEF00 Control register B
| |
|
| |
| Some internal meanings:
| |
|
| |
| $BFE101 Data register for parallel interface
| |
| $BFE301 Data direction register for the parallel interface
| |
| $BFEC01 State of the keyboard,contains the last special key
| |
| pressed(Shift,Alternate,Control,Amiga)
| |
|
| |
| Now come the registers that are used for tone production.The first
| |
| two registers should be treated especially carefully-if they are
| |
| used wrong,very nasty effects can occur.
| |
| These registers can be either read or written only.This
| |
| information is included under R/W in the table.
| |
|
| |
| Address R/W Meaning
| |
| ------------------------------------------------------------------
| |
| DFF096 W Write DMA Control
| |
| DFF002 R Read DMA Control and Blitter Status
| |
| --Audio Channel 0--
| |
| DFF0AA W Data register
| |
| DFF0A0 W Pointer to table beginning Bits 16-18
| |
| DFF0A2 W Pointer to table beginning Bits 0-15
| |
| DFF0A4 W Table length
| |
| DFF0A6 W Read in Rate
| |
| DFF0A8 W Volume
| |
| --Audio Channel 1--
| |
| DFF0BA W Data register
| |
| DFF0B0 W Pointer to table beginning Bits 16-18
| |
| DFF0B2 W Pointer to table beginning Bits 0-15
| |
| DFF0B4 W Table length
| |
| DFF0B6 W Read in Rate
| |
| DFF0B8 W Volume
| |
| --Audio Channel 3--
| |
| DFF0CA W Data register
| |
| DFF0C0 W Pointer to table beginning Bits 16-18
| |
| DFF0C2 W Pointer to table beginning Bits 0-15
| |
| DFF0C4 W Table length
| |
| DFF0C6 W Read in Rate
| |
| DFF0C8 W Volume
| |
| --Audio Channel 4--
| |
| DFF0DA W Data register
| |
| DFF0D0 W Pointer to table beginning Bits 16-18
| |
| DFF0D2 W Pointer to table beginning Bits 0-15
| |
| DFF0D4 W Table length
| |
| DFF0D6 W Read in Rate
| |
| DFF0D8 W Volume
| |
|
| |
| Now for the registers that contain information about the joystick,
| |
| mouse or potentiometer.These addresses have been gone over in part
| |
| previously.
| |
|
| |
| Address R/W Meaning
| |
| ------------------------------------------------------------------
| |
| DFF00A R Joystick/Mouse Port 1
| |
| DFF00C R Joystick/Mouse Port 2
| |
| DFF012 R Potentiometer pair 1 Counter
| |
| DFF014 R Potentiometer pair 2 Counter
| |
| DFF018 R Potentiometer connection
| |
| DFF034 W Potentiometer port direction
| |
|
| |
|
| |
|
| |
|
| |
| Chapter 6
| |
| ---------
| |
| 6.The Operating System.
| |
| -----------------------
| |
| Now lets take a step forward in your ability to write assembly
| |
| language programs.Its not enough to put a piece of text in memory
| |
| someplace.You want to be able to put it on the screen.Do you know
| |
| how to write a character on the screen?Do you know how to draw a
| |
| window on the screen that can be modified by the mouse?Actually
| |
| you don't have to have terribly precise knowledge about such
| |
| topics.
| |
| Fortunately,the Amigas operating system supplies routines that
| |
| take care of common tasks like this.It can seem quite complicated
| |
| due to the number of routines necessary.These routines are in
| |
| libraries.We'll look at the libraries in some depth now.
| |
|
| |
| 6.1.Load Libraries.
| |
| -------------------
| |
| Before you can use a library,it must be available.It has to be
| |
| loaded into memory.Unfortunately,the whole library must be loaded,
| |
| even if you only need one of the functions.
| |
| First you need to decide what the program must be able to do,so
| |
| you can see which libraries you'll need.For simple I/O text,you
| |
| don't need a library that contains routines for moving graphics!
| |
| There are a number of libraries onj a normal Workbench disk.Heres
| |
| an overview of the names and the sort of functions they do:
| |
|
| |
| Exec.Library;
| |
| This library is needed to load the other libraries.It is already
| |
| in memory and doesn't need to be loaded.Its in charge of basic
| |
| functions like reserving memory and working with I/O channels.
| |
|
| |
| Dos.Library;
| |
| Contains all the functions for normal I/O operations,for instance
| |
| screen or disk access.
| |
|
| |
|
| |
| Intuition.Library;
| |
| Used for working with screens,windows,menus,etc...
| |
|
| |
| Clist.Library;
| |
| This contains routines for working with the Copper lists that are
| |
| used for controlling the screen.
| |
|
| |
| Console.Library;
| |
| Contains graphics routines for text output in console windows.
| |
|
| |
| Diskfont.Library;
| |
| Used for working with the character fonts that are stored on the
| |
| disk.
| |
|
| |
| Graphics.Library;
| |
| This library contains functions to control the Blitter(or graphics
| |
| )chip.Its used for basic graphics functions.
| |
|
| |
| Icon.Library;
| |
| Used in the development and use of workbench symbols(icons).
| |
|
| |
| Layers.Library;
| |
| Used for working with screen memory (layers).
| |
|
| |
| Mathffp.Library;
| |
| Contains basic math floating point operations.
| |
|
| |
| Mathieeedoubbas.Library;
| |
| Contains basic math functions for integers.
| |
|
| |
| Mathtrans.Library;
| |
| Contains higher level mathmatical functions.
| |
|
| |
| Potgo.Library;
| |
| Used for evaluating analog input to the Amiga.
| |
|
| |
| Timer.Library;
| |
| Contains routines for time critical programs.They can be used to
| |
| program exact time intervals.
| |
|
| |
| Translator.Library;
| |
| Contains the single function "Translate",that translates normal
| |
| text written phonetically for the narrator,the speech synthesisor.
| |
|
| |
| You can open(load)all these libraries of course.You should
| |
| remember that this takes time and memory.For this reason,you
| |
| should always think about which functions you need and which
| |
| libraries they are in.
| |
| For example,lets say you want to write a program that does text
| |
| input/output.You need the "Dos.Library",so it can be loaded.
| |
| The "exec.library"is in charge of loading.This library contains
| |
| the OpenLib function that can be called once you've passed the
| |
| needed parameters.AssemPro Amiga includes all the libraries
| |
| necessary for the Amiga,it also includes files that contain the
| |
| offsets for the operating system calls.The macros contained in
| |
| AssemPro ease assembly language programming considerably.To make
| |
| the programs in this book useful to the largest audience the
| |
| following examples are written for generic assemblers and do not
| |
| include AssemPro's macros.We have used the AssemPro ILABEL and the
| |
| macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can start the
| |
| programs from the desktop.(If you are using a different assembler
| |
| check your documentation for instructions on linking programs).
| |
|
| |
| 6.2.Calling Functions.
| |
| ----------------------
| |
| Since this chapter is rather complex we'll first describe the
| |
| fundamental routines necessary to use the Amiga's operating system
| |
| after a description a complete program is listed.Every library
| |
| begins in memory with a number of JMP commands.These JMPs branch
| |
| to the routines that are in the library.To call a function,you
| |
| need to find the beginning of this JMP table and call function x
| |
| by going to the xth JMP command.Usually you use an offset to get
| |
| to the right JMP command.Normally,you don't start at the beginning
| |
| but at the end of the JMP table,so use negative offsets.
| |
| It works out very easily.Now lets open the "dos.library"by using
| |
| "exec.library's"base address.This address is $000004.To call a
| |
| fuction from another library,you need to use another base address.
| |
| Now you need the offset for the function that you want.You want
| |
| the OpenLib function that has -408 as an offset.You'll find a list
| |
| of function offsets in the appendix.
| |
| You need a pointer to the name of the library you are loading for
| |
| the OpenLib function(in this case "dos.library")and a long word in
| |
| memory that you can use to store the base address of the DOS
| |
| library.You get this back from the OpenLib function.You need to be
| |
| sure to write the library name in lower case letters(dos.library),
| |
| otherwise you can't open it.I entered a name in capitol letters
| |
| once and spent a lot of time finding this error.
| |
|
| |
| The routine looks like this:
| |
|
| |
| ;** Load the DOS library 'dos.library' (6.2A) **
| |
| Execbase = 4 ;base address of the EXEC library
| |
| OpenLib = -408 ;offset for the OpenLib function
| |
|
| |
| IoErr = -132 ;offset for IoErr information
| |
|
| |
| init:
| |
| move.l Execbase,a6 ;base address in A6
| |
| lea dosname,a1 ;address of library name
| |
| moveq #0,d0 ;version number
| |
| jsr OpenLib(a6) ;open DOS library
| |
| move.l d0,dosbase ;save DOS base address
| |
| beq error ;if zero,then error!
| |
| ... ;your program goes here
| |
| ... ;more program...
| |
|
| |
|
| |
| error: ;error
| |
| move.l dosbase,a6 ;address of library name
| |
| jsr IoErr(a6) ;call IoErr for error info
| |
| move.l d0,d5
| |
| ... ;your error routine goes here
| |
| rts
| |
|
| |
|
| |
| dosname: ;name of library to open
| |
| dc.b 'dos.library',0,0
| |
| align ;SEKA uses-even
| |
|
| |
| dosbase: ;storage for DOS base address
| |
| blk.l 1
| |
|
| |
| end
| |
|
| |
| This is the way to load the DOS library so that you can use it.All
| |
| library functions are called this way.Parameters are put in
| |
| registers and passed to the function.When there is an error,when
| |
| the function doesn't run correctly,a zero is usually put in data
| |
| register D0.
| |
| Once your program is done with its work,you need to close the
| |
| libraries that are still open before you return to the CLI or
| |
| Workbench.The CloseLib function (offset -414)takes care of this
| |
| job.This function is in the EXEC library just like the OpenLib.The
| |
| only parameter it needs is the base address of the library that is
| |
| closed.To close "dos.library",do the following:
| |
|
| |
| CloseLib = -414 ; (6.2B)
| |
| ...
| |
| move.l Execbase,a6 ;EXEC base address
| |
| move.l dosbase,a1 ;DOS base address
| |
| jsr CloseLib(a6) ;close library
| |
|
| |
| 6.3.Program Initialization.
| |
| ---------------------------
| |
| Before you can start a program,you need to initialize many things
| |
| so that the program can run.
| |
| Lets take an example program that does some text editing.A program
| |
| like this must be able to store text,so it needs to be able to
| |
| access memory.It also needs to be able to accept keyboard input
| |
| and do screen output,so it needs an output window.
| |
| To do this,you need to open one or more of the libraries that we
| |
| talked about earlier.Lets assume that you've loaded the DOS
| |
| library,so that you can do the next steps.
| |
|
| |
| 6.3.1.Reserve Memory.
| |
| ---------------------
| |
| There are several ways to get the operating system to assign you a
| |
| chunk of memory.You need to use one of them,so that during multi-
| |
| tasking,you don't have one program overwriting another programs
| |
| memory area.
| |
| Lets look at the function that is normally used.This function is
| |
| in the resident EXEC library and has the name AllocMem (offset
| |
| -$c6).It reserves a memory area,using the value in D0 as the
| |
| length.The address that the memory area begins at is returned in
| |
| the D0 data register.If it returns zero,the program could'nt give
| |
| you that much memory.
| |
| You can also use a mode word in D1 to determine whether the memory
| |
| area that is reserved should be erased or not.
| |
| The routine looks like this:
| |
|
| |
| ExecBase = 4 ; (6.3.1A)
| |
| AllocMem = -$c6
| |
| ...
| |
| move.l #number,d0 ;number of bytes to reserve
| |
| move #mode,a6 ;mode word
| |
| move.l ExecBase,a6 ;DOS base address in A6
| |
| jsr AllocMem(a6) ;call function
| |
| move.l d0,address ;save memory's start address
| |
| beq error ;memory not reserved
| |
| ...
| |
|
| |
| The second way to reserve memory is to use the AllocAbs function
| |
| (offset -$CC).This function in contrast to the AllocMem function
| |
| reserves a particular memory area.The D0 register contains the
| |
| number of bytes that should be reserved.Address register A1
| |
| contains the desired start address.This function returns a zero in
| |
| D0 if the memory area can't be reserved.
| |
|
| |
| ExecBase = 4 ; (6.3.1B)
| |
| AllocAbs = -$cc
| |
| ...
| |
| move.l #number,d0 ;number of bytes to reserve
| |
| lea address,a1 ;desired start address
| |
| move.l execbase,a6 ;EXEC base address
| |
| jsr AllocAbs(a6) ;reserve memory
| |
| tst.l d0 ;everything ok?
| |
| beq error ;no!
| |
| ...
| |
|
| |
| When the program has done its work and must return to the CLI or
| |
| the Workbench,it needs to return the memory it as reserved to the
| |
| system.The FreeMem function (offset -$D2) handles this.
| |
| The function works like AllocAbs in that the number of bytes is
| |
| put in D0 and the start address of the memory area is put in A1.
| |
| If you try to free up a memory area that was'nt reserved,you'll
| |
| usually crash the computor.
| |
| The routine to free up a memory area looks like this:
| |
|
| |
| ExexBase = 4 ; (6.3.1C)
| |
| FreeMem = -$d2
| |
| ...
| |
| move.l #number,d0 ;number of bytes released
| |
| lea address,a1 ;start address from AllocAbs
| |
| move.l ExecBase,a6 ;ExecBase address
| |
| jsr FreeMem(a6) ;free up memory
| |
| tst.l d0 ;everything ok?
| |
| beq error ;no!
| |
| ...
| |
|
| |
| 6.3.2.Opening a Simple Window.
| |
| ------------------------------
| |
| The title of this chapter may sound a bit strange.However,the
| |
| differences between the two different methods of opening a window
| |
| are so great that they should be handled in seperate chapters.
| |
| The method of opening a window presented here is very simple,but
| |
| it doesn't allow you to work with all the gadgets.These gadgets
| |
| include the close symbol in the upper left corner of a window and
| |
| the size symbol in the lower left corner.
| |
| If you open the window in the simple manner,almost all the gadgets
| |
| are present.However,the close symbol is not.As a result,this
| |
| method isn't appropriate for every application.Now lets look at
| |
| the method.
| |
| To open a window,use a function from the DOS library,so you need
| |
| to open the library first (see the section "Load Library").This
| |
| open function is an all purpose function that can be used for many
| |
| things.For this reason,it makes good sense to put a "open"
| |
| subroutine in your program.You can use it a lot.Lets do the basic
| |
| steps:
| |
|
| |
| ;** Load the DOS Library 'dos.library' (6.3.2A) **
| |
| ExecBase = 4 ;base addres of the EXEC library
| |
| OpenLib = -408 ;offset of OpenLib function
| |
| Open = -30 ;Offset of the DOS function OPEN
| |
|
| |
| init:
| |
| move.l ExecBase,a6 ;base address in A6
| |
| lea dosname(pc),a1 ;address of library name
| |
| move.q #0,d0 ;version number:unimportant
| |
| jsr OpenLib(a6) ;call the function
| |
| move.l d0,dosbase ;save DOS base address
| |
| beq error ;if zero,then error!
| |
| ... ;more of your program
| |
| ... ;now open window,etc...
| |
|
| |
|
| |
| error:
| |
| ... ;error occured
| |
| ... ;your error routine
| |
|
| |
|
| |
| openfile: ;general open function
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| jsr Open(a6) ;call OPEN function
| |
| tst.l d0 ;test if ok
| |
| rts ;done,evaluate test later
| |
|
| |
| dosname: ;name of library to be opened
| |
| dc.b 'dos.library',0,0
| |
| align ;even
| |
|
| |
| dosbase: ;spot for DOS base address
| |
| blk.l 1
| |
|
| |
| You call the Openfile routine,because the label "Open"is already
| |
| being used for the offset.This routine calls the Open function
| |
| that is in the DOS library.
| |
| This isn't everything.The function must be given some parameters
| |
| so that it knows what to open.The parameters are sent in registers
| |
| D1 and D2.D1 points to a definition block what specifies what
| |
| should be opened.You need to have a filename ended with a null
| |
| byte there.D1 must be passed as a long word like all addresses.D2
| |
| contains the mode that the function should run in.There is an old
| |
| (1005) and a new (1006) mode.This number must be passed in D2's
| |
| long word.
| |
| Heres an overview of how windows are opened.Fortunately,AmigaDos
| |
| allows you to use input and output channels in the same way.The
| |
| standard channels are disk files,the console (keyboard and screen)
| |
| the printer interface and the serial RS232 interface.
| |
| The console input/output is what you'll work with now.When you
| |
| specify the console as the filename of the channel to be opened,a
| |
| window is opened automatically.
| |
| The name must begin with CON:to do this.Its similar to DF0:for
| |
| disk operations.A little more infotmation about the window is
| |
| still needed.
| |
| You need to specify the X and Y coordinates of the upper left and
| |
| lower right corners of the window as well as the name that should
| |
| appear in the title line of the window.A complete definition block
| |
| for a window like this would appear like the following line:
| |
|
| |
| consolname: dc.b 'CON:0/100/640/100/**Window**',0
| |
|
| |
| To open this window,the line above needs to be inserted in the
| |
| following program:
| |
|
| |
| mode_old = 1005
| |
|
| |
| lea consolname(pc),a1 ;consol definition
| |
| move.l #mode_old,d0 ;mode
| |
| bsr openfile ;console open
| |
| beq error ;didn't work
| |
| move.l d0,conhandle
| |
|
| |
| rts
| |
| ...
| |
| conhandle: dc.l 1 ;space for handle
| |
|
| |
| There are two points to clear up yet.
| |
| You should use mode_old as the the mode when you open a window.
| |
| Logically the window doesn't exist before opening so this seems
| |
| wierd but it doesn't hurt anything.
| |
| The parameter that returns from "openfile"in D0 is zero in the
| |
| case of an error,in the case that opening didn't work.Otherwise
| |
| the value is the identification number (handle number) of the
| |
| opened channel.You need to store it away,because every function
| |
| that wants to use this channel must give the handle number.In the
| |
| example,you stored this number in the "conhandle"long word.
| |
| As mentioned,the window you've opened doesn't have a close symbol
| |
| but it can be made bigger and smaller and moved forward and back.
| |
| The manipulations that are carried out using the mouse are
| |
| completely taken care of by the Amiga (in contrast to the ATARI ST
| |
| where the programmer has to take care of these things).
| |
| An important function that uses the handle number is the one that
| |
| closes the channel (in your case the window).This function is also
| |
| in the DOS library and is called "Close".Its offset is -36 and it
| |
| only needs one parameter;the handle number of the channel that is
| |
| closed must be in the D1 register.
| |
| After your work is done,you need to put the following lines in
| |
| your program to close the window:
| |
|
| |
| Close = -36 ; (6.3.2C)
| |
| ...
| |
| move.l conhandle,d1 ;handle number in D1
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| jsr Close(a6) ;close channel!
| |
|
| |
| The window disappears!
| |
|
| |
| Now for a few remarks about opening and closing the window in this
| |
| way.If you open several windows in the same way,you'll get several
| |
| windows and thus several handle numbers.In this way,you can put as
| |
| many windows on the screen as you like.You can do your work with
| |
| them and close them individually.
| |
| Here is the complete program to open and close a simple window in
| |
| AssemPro format (We have used the AssemPro ILABEL and the macros
| |
| INIT_AMIGA and EXIT_AMIGA so AssemPro owners can start the program
| |
| from desktop.If you are using a different assembler check your
| |
| documentation for instructions on starting and exiting programs):
| |
|
| |
| ;***** 6.3.2 S.D *****
| |
|
| |
| OpenLib =-30-378
| |
| closelib =-414
| |
| ;execbase =4 ;defined in AssemPro macros
| |
|
| |
|
| |
| *calls to Amiga DOS:
| |
|
| |
| open =-30
| |
| close =-30-6
| |
| IoErr =-132
| |
| mode_old = 1005
| |
| alloc_abs =-$cc
| |
|
| |
| ILABEL AssemPro:includes/Amiga.l ;AssemPro only
| |
|
| |
| INIT_AMIGA ;AssemPro only
| |
|
| |
| run:
| |
| bsr init ;initialization
| |
| bra test ;system-test
| |
|
| |
| init: ;system initialization and open
| |
| move.l execbase,a6 ;number of execute-library
| |
| lea dosname(pc),a1
| |
| moveq #0,d0
| |
| jsr openlib(a6) ;open DOS-Library
| |
| move.l d0,dosbase
| |
| beq error
| |
|
| |
| lea consolname(pc),a1 ;consol definition
| |
| move.l #mode_old,d0
| |
| bsr openfile ;consol open
| |
| beq error
| |
| move.l d0,conhandle
| |
|
| |
| rts
| |
|
| |
| test:
| |
|
| |
| bra qu ;quit and exit
| |
|
| |
|
| |
| error:
| |
| move.l dosbase,a6
| |
| jsr IoErr(a6)
| |
| move.l d0,d5
| |
|
| |
| move.l #-1,d7 ;flag
| |
|
| |
| qu:
| |
| move.l conhandle,d1 ;window close
| |
| move.l dosbase,a6
| |
| jsr close(a6)
| |
| move.l dosbase,a1 ;DOS.Lib close
| |
| move.l execbase,a6
| |
| jsr closelib(a6)
| |
|
| |
| EXIT_AMIGA ;AssemPro only
| |
|
| |
| openfile: ;open file
| |
| move.l a1,d1 ;pointer to I/O-Definition-Text
| |
| move.l d0,d2
| |
| move.l dosbase,a6
| |
| jsr open(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| dosname: dc.b 'dos.library',0,0
| |
| Align.w
| |
|
| |
| dosbase: dc.l 0
| |
|
| |
| consolname: dc.b 'CON:0/100/640/100/**CLI-Test**',0
| |
| Align.w
| |
|
| |
| conhandle: dc.l 0
| |
|
| |
|
| |
| end
| |
|
| |
| There is another way to open a window easily.Just use RAW:instead
| |
| of CON:as the channel designator.All the other parameters and
| |
| operations remain the same.
| |
| If you try them both out,you won't see any differences between the
| |
| two windows.They both look the same and can be worked with in the
| |
| same way with the mouse.The difference comes when you input to the
| |
| window.In the RAW:window,the cursor keys are ignored.In the CON:
| |
| window and in CLI,they do work.
| |
|
| |
| 6.4.Input/Output.
| |
| -----------------
| |
| Besides managing and making calculations with data,the most
| |
| important work of a program is to input and output the data.There
| |
| are many methods of data transfer in and out of the computor,for
| |
| instance screen or printer output,keyboard input,using the serial
| |
| or the parallel interface,tone or speech output and finally disk
| |
| operations.
| |
| You want to learn about all these methods of data input and output
| |
| for programming and applications.We've written some programs as
| |
| subroutines that should be useful for later programs.It makes good
| |
| sense to make a library of these subroutines that can either be
| |
| directly integrated in a new program or linked to a program.At the
| |
| end of the sections there is a list of a complete program so you
| |
| can see how the subroutines are used.
| |
| To prepare for input/output,you need to have data to output and
| |
| space to input data.To get this ready,you need a correct program
| |
| beginning in which the EXEC and DOS libraries are opened and
| |
| memory is reserved.After this,you begin most programs by outputing
| |
| some text.The text can be a program title or the instruction to
| |
| input data over the keyboard.Lets start looking at screen output.
| |
|
| |
| 6.4.1.Screen Output.
| |
| --------------------
| |
| For a computor like the Amiga the first question is where should
| |
| the screen output be sent?The answer is simple for many computors;
| |
| they only have one screen,and output goes there.You need to
| |
| specify which window to write to when you use the Amiga,however.
| |
|
| |
| There are two possibilites:
| |
|
| |
| 1.Output to CLI
| |
|
| |
| 2.Output to another window
| |
|
| |
| The first posibillity only exists if the program that makes the
| |
| output was started from CLI.If not,you need to open your own
| |
| custom window for your program.If so,you can use the window that
| |
| was opened by the CLI for output.
| |
| If you use the second method,you need to open a window.As you've
| |
| already seen,there are three methods.For simple text and character
| |
| output,the difference between the three sorts of windows isn't
| |
| very great.Here you have a free hand in determining which sort of
| |
| window to use.Lets open a CON:window and put its handle number in
| |
| "conhandle".
| |
| You've opened your window and want to output a title.You choose
| |
| text to output and then put it in memory using a code segment like
| |
| this:
| |
|
| |
| title: dc.b "** Welcome to this Program! **"
| |
| titleend:
| |
| align ;even
| |
|
| |
| The "align"(even) is a pseudo-op that should follow text when it
| |
| is followed by either word data or program lines.It causes the
| |
| assembler to insert a null byte if necessary to make the address
| |
| even.
| |
| To output this text you need another DOS function:Write.This has
| |
| an offset of -48 and needs three parameters:
| |
|
| |
| In D1 the handle of an opened output channel that should be
| |
| written to (in your case,this is the handle number that
| |
| you go back from the Open command when you opened your
| |
| window.).
| |
| In D2 the address of the text to be output (in the example,the
| |
| address "title").
| |
| In D3 the number of characters to be output in bytes.
| |
|
| |
| To find the number of bytes to output,you need to count the number
| |
| of characters in your text.Use "titleend"to calculate this.Using
| |
| this label,the assembler can calculate the length of your text for
| |
| itself (after all,why should you count when you have a computor?)
| |
| if you write:
| |
|
| |
| move.l #titleend-title,d3
| |
|
| |
| The advantage of specifying the length is that you can put control
| |
| characters between the beginning and end of the text.In this way,
| |
| you can execute certain functions using text output.You'll learn
| |
| about the control characters in a bit.
| |
|
| |
| Heres the routine:
| |
|
| |
| Write = -48 ; (6.4.1A)
| |
| ... ;open window
| |
| ...
| |
| move.l dosbase,a6 ;DOS base address
| |
| move.l conhandle,d1 ;pass handle
| |
| move.l #title,d2 ;text address
| |
| move.l #titleend-title,d3 ;and length
| |
| jsr Write(a6) ;call function
| |
| ...
| |
|
| |
|
| |
| title: dc.b "** Welcome to this Program! **"
| |
|
| |
| titleend:
| |
|
| |
| align ;event
| |
|
| |
| end
| |
|
| |
| You'll certainly use this function a lot.You'll often want to
| |
| output just one character though.To allow you to do this and
| |
| similar text related tasks,there are four subroutines,each of
| |
| which do a different sort of output:
| |
|
| |
| Pmsg;
| |
| Outputs the text from (D2) to the first null byte.
| |
|
| |
| Pline;
| |
| Is the same as the routine above except that the text is
| |
| automatically followed by a CR,the cursor is positioned at the
| |
| beginning of the next line.
| |
|
| |
| Pchar;
| |
| Outputs the character in D0
| |
|
| |
| Pcrlf;
| |
| Puts the cursor at the beginning of the next line.
| |
|
| |
| Heres the subroutine package:
| |
|
| |
| Write = -48 ; (6.4.1B)
| |
| ...
| |
|
| |
| pline: ;*output line and then a CR
| |
| bsr pmsg ;output line
| |
|
| |
| pcrlf:
| |
| move #10,d0 ;line feed
| |
| bsr pchar ;output
| |
| move #13,d0 ;and CR
| |
|
| |
| pchar:
| |
| move.b d0,outline ;character in output buffer
| |
| move.l #outline,d2 ;address of the character
| |
|
| |
| pmsg: ;*output line (D2) upto null
| |
| move.l d2,a0 ;address in A0
| |
| clr d3 ;length = 0
| |
|
| |
| ploop:
| |
| tst.b (a0)+ ;null byte ?
| |
| beq pmsg2 ;yes:length found
| |
| addq.l #1,d3 ;else length + 1
| |
| bra ploop ;and continue looking
| |
|
| |
| pmsg2:
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| move.l conhandle,d1 ;our window handle
| |
| jsr Write(a6) ;call write function
| |
| rts ;done!
| |
|
| |
| outline: dc.w 0 ;output buffer for 'pchar'
| |
|
| |
| conhandle: dc.l 0 ;windows handle
| |
|
| |
| Here is an example program to open and close a simple window and
| |
| output a text message in AssemPro format (We have used the
| |
| AssemPro macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can
| |
| start the program from desktop.If you are using a different
| |
| assembler check your documentation for instructions on starting
| |
| and exiting programs.):
| |
|
| |
| Here is the complete program in AssemPro format:
| |
|
| |
| ;***** 6.4.1C.asm S.D. *****
| |
|
| |
| Openlib =-30-378
| |
| closelib =-414
| |
| ;execbase = 4 ;Defined in AssemPro
| |
| ;Macros
| |
|
| |
| * calls to Amiga Dos:
| |
|
| |
| open =-30
| |
| close =-30-6
| |
| write =-48
| |
| IoErr =-132
| |
| mode_old = 1005
| |
| alloc_abs =-$cc
| |
|
| |
| ILABEL AssemPro:include/Amiga.l ;AssemPro only
| |
|
| |
| INIT_AMIGA ;AssemPro only
| |
|
| |
| run:
| |
| bsr init ;initialization
| |
| bsr test ;system test
| |
| nop
| |
| bra qu ;quit and exit
| |
|
| |
| test:
| |
| move.l #title,d0
| |
| bsr pmsg
| |
| bsr pcrlf
| |
| bsr pcrlf
| |
|
| |
| rts
| |
|
| |
| init: ;system initialization and
| |
| ;open
| |
| move.l execbase,a6 ;number of execute-library
| |
| lea dosname(pc),a1
| |
| moveq #0,d0
| |
| jsr openlib(a6) ;open DOS-library
| |
| move.l d0,dosname
| |
| beq error
| |
|
| |
| lea consolname(pc),a1 ;console definition
| |
| move.l #mode_old,d0
| |
| bsr openfile ;console open
| |
| beq error
| |
| move.l d0,conhandle
| |
|
| |
| rts
| |
|
| |
| pmsg: ;print message (D0)
| |
| movem.l d0-d7/a0-a6,-(sp)
| |
| move.l d0,a0
| |
| move.l a0,d2
| |
| clr.l d3
| |
|
| |
| ploop:
| |
| tst.b (a0)+
| |
| beq pmsg2
| |
| addq.l #1,d3
| |
| bra ploop ;length calculate
| |
|
| |
| pmsg2:
| |
| move.l conhandle,d1
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6
| |
| rts
| |
|
| |
| pcrlf:
| |
| move #10,d0
| |
| bsr pchar
| |
| move #13,d0
| |
|
| |
| pchar: ;output char in D0
| |
| movem.l d0-d7/a0-a6,-(sp) ;save all
| |
| move.l conhandle,d1
| |
|
| |
| pch1:
| |
| lea outline,a1
| |
| move.b d0,(a1)
| |
| move.l a1,d2
| |
| move.l #1,d3 ;1 letter
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6 ;restore all
| |
|
| |
| error:
| |
| move.l dosbase,a6
| |
| jsr IoErr(a6)
| |
| move.l d0,d5
| |
|
| |
| move.l #-1,d7 ;flag
| |
|
| |
| qu:
| |
| move.l conhandle,d1 ;window close
| |
| move.l dosbase,a6
| |
| jsr close(a6)
| |
|
| |
| move.l dosbase,a1 ;DOS.Lib close
| |
| move.l execbase,a6
| |
| jsr closelib(a6)
| |
|
| |
| EXIT_AMIGA ;AssemPro only
| |
|
| |
| openfile: ;open file
| |
| move.l a1,d1 ;pointer to I/O-definition-
| |
| ;text
| |
| move.l d0,d2
| |
| move.l dosbase,a6
| |
| jsr open(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| dosname: dc.b 'dos.library',0,0
| |
| align.w
| |
|
| |
| dosbase: dc.l 0
| |
|
| |
| consolname: dc.b 'CON:0/100/640/100/** CLI-Test **',0
| |
| align.w
| |
|
| |
| conhandle: dc.l 0
| |
|
| |
| title: dc.b '** Welcome to this Program! **'
| |
|
| |
| titleend:
| |
| align
| |
|
| |
| outline: dc.w 0 ;output buffer for char
| |
|
| |
| end
| |
|
| |
| Using this program,you can very easily put whatever you want in
| |
| the CON:window.These functions also work in RAW:window.You should
| |
| rename "conhandle"as "rawhandle",so that you don't get things
| |
| mixed up later.
| |
| Lets stay with the CON:window.As mentioned earlier,you can output
| |
| special characters that execute functions or change parameters for
| |
| output.These characters are called control characters.
| |
| You've already learned about one of these control characters,Line
| |
| Feed ($A).This character isn't just output;instead,it calls a
| |
| function that moves the cursor into the next line and moves the
| |
| screen up.This is very useful,but there are much more interesting
| |
| control characters.
| |
| Here's a list of control characters that execute functions.These
| |
| characters are given in hex.
| |
|
| |
| Control Sequence;
| |
|
| |
| Sequence Function
| |
| ------------------------------------------------------------------
| |
| 08 Backspace
| |
| 0A Line Feed,Cursor down
| |
| 0B Move Cursor up a line
| |
| 0C Clear screen
| |
| 0D Carrige return,cursor in the first column
| |
| 0E Turn on normal characters (Cancel Of Effects)
| |
| 0F Turn on special characters
| |
| 1B Escape
| |
|
| |
| The following sequences begin with $9B,the CSI (Control Sequence
| |
| Introducer).The characters that follow execute a function.The
| |
| values in square brackets can be left off.The n's you see
| |
| represent one or more digit decimal numbers given using ASCII
| |
| characters.The value that is used when n is left off,is given in
| |
| the parenthesis that follow n in the description of the function
| |
| in the table.
| |
|
| |
| Control Sequence Introducer;
| |
|
| |
| Sequence Function
| |
| ------------------------------------------------------------------
| |
| 9B[n]40 Insert n blanks
| |
| 9B[n]41 Move cursor n (1) lines up
| |
| 9B[n]42 Move cursor n (1) lines down
| |
| 9B[n]43 Move cursor n (1) characters to the right
| |
| 9B[n]44 Move cursor n (1) characters to the left
| |
| 9B[n]45 Move cursor down n (1) lines into column 1
| |
| 9B[n]46 Move cursor up n (1) lines and into column 1
| |
| 9B[n][3B n]48 Cursor in line;Set column
| |
| 9B 4A Erase screen from cursor
| |
| 9B 4B Erase line from the cursor
| |
| 9B 4C Insert line
| |
| 9B 4D Delete line
| |
| 9B[n]50 Delete n characters starting at cursor
| |
| 9B[n]53 Move up n lines
| |
| 9B[n]54 Move down n lines
| |
| 9B 32 30 68 Line feed => Line feed + return
| |
| 9B 32 30 6C Line feed => just Line feed
| |
| 9B 6E Sends the cursor position!A string of the following
| |
| form is returned:
| |
| 9B (line) 3B (column) 52
| |
| 9B(style);(foreground colour);(Background Colour)6D
| |
| The three parameters are decimal numbers in ASCII
| |
| format.They mean:
| |
| Style: 0 = normal
| |
| 1 = bold
| |
| 3 = italic
| |
| 4 = underline
| |
| 7 = inverse
| |
| Foreground colour: 30-37
| |
| Colour 0-7 for Text
| |
| Background colour: 40-47
| |
| Colour 0-7 for background
| |
| 9B(length)74 sets the maximum number of lines to be displayed
| |
| 9B(width)75 sets the maximum line length
| |
| 9B(distance)78 defines the distance in pixels from the left border
| |
| of the window to the place where output should
| |
| begin
| |
| 9B(distance)79 defines the distance in pixels from the upper
| |
| border of the window to the place where output
| |
| should begin
| |
| The last four functions yield the normal values if
| |
| you leave off the parameters.
| |
|
| |
| 9B 30 20 70 Make cursor invisible
| |
| 9B 20 70 Make cursor visible
| |
| 9B 71 Sends window construction.A string of the following
| |
| form is returned:
| |
| 9B 31 3B 31 3B (lines) 3B (columns) 73
| |
|
| |
| To see how the control characters work,have "pmsg"output this text
| |
| to your window:
| |
|
| |
| mytext: dc.b $9b,"4;31;40m" ; (6.3.2D)
| |
| dc.b "underline"
| |
| dc.b $9b,"3;33;40m",$9b,"5;20H"
| |
| dc.b "** Hello World! **",0
| |
|
| |
| The parameters for the control sequence are put in quotation marks
| |
| so they are treated as an ASCII string.Now you see,just how easy
| |
| it is to do text output!
| |
| Here is the complete program to open and output the text and
| |
| control codes to your window in AssemPro format (We have used the
| |
| AssemPro macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can
| |
| start the programs from desktop.If you are using a different
| |
| assembler check your documentation for instructions on starting
| |
| and exiting programs):
| |
|
| |
| ; ***** 6.4.1D.ASM S.D. *****
| |
|
| |
|
| |
| openlib =-30-378
| |
| closelib =-414
| |
| ;execbase = 4 ;defined in AssemPro macros
| |
|
| |
| * calls to Amiga Dos:
| |
|
| |
| open =-30
| |
| close =-30-6
| |
| write =-48
| |
| IoErr =-132
| |
| mode_old = 1005
| |
| alloc_abs =-$cc
| |
|
| |
| ILABEL AssemPro:includes/Amiga.l ;AssemPro only
| |
|
| |
| INIT_AMIGA ;AssemPro only
| |
|
| |
| run:
| |
| bsr init ;initialization
| |
| bsr test ;system test
| |
| nop
| |
| bra qu ;quit and exit
| |
|
| |
| test:
| |
| move.l #mytext,d0
| |
| bsr pmsg
| |
| bsr pcrlf
| |
| bsr pcrlf
| |
|
| |
| rts
| |
|
| |
| init: ;system initialization and open
| |
| move.l execbase,a6 ;number of execute-library
| |
| lea dosname(pc),a1
| |
| moveq #0,d0
| |
| jsr openlib(a6) ;open DOS-Library
| |
| move.l d0,dosbase
| |
| beq error
| |
|
| |
| lea consolname(pc),a1 ;console definition
| |
| move.l #mode_old,d0
| |
| bsr openfile ;console open
| |
| beq error
| |
| move.l d0,conhandle
| |
|
| |
| rts
| |
|
| |
| pmsg: ;print message (D0)
| |
| movem.l d0-d7/a0-a6,-(sp)
| |
| move.l d0,a0
| |
| move.l a0,d2
| |
| clr.l d3
| |
|
| |
| ploop:
| |
| tst.b (a0)+
| |
| beq pmsg2
| |
| addq.l #1,d3
| |
| bra ploop
| |
|
| |
| pmsg2:
| |
| move.l conhandle,d1
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6
| |
| rts
| |
|
| |
| pcrlf:
| |
| move #10,d0
| |
| bsr pchar
| |
| move #13,d0
| |
|
| |
| pchar: ;output char in D0
| |
| movem.l d0-d7/a0-a6,-(sp) ;save all
| |
| move.l conhandle,d1
| |
|
| |
| pch1:
| |
| lea outline,a1
| |
| move.b d0,(a1)
| |
| move.l a1,d2
| |
| move.l #1,d3 ;one letter
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6 ;restore all
| |
| rts
| |
|
| |
| error:
| |
| move.l dosbase,a6
| |
| jsr IoErr(a6)
| |
| move.l d0,d5
| |
|
| |
| move.l #-1,d7 ;flag
| |
|
| |
| qu:
| |
| move.l conhandle,d1 ;window close
| |
| move.l dosbase,a6
| |
| jsr close(a6)
| |
|
| |
| move.l dosbase,a1 ;DOS.Lib close
| |
| move.l execbase,a6
| |
| jsr closelib(a6)
| |
|
| |
| EXIT_AMIGA ;AssemPro only
| |
|
| |
| openfile: ;open file
| |
| move.l a1,d1 ;pointer to I/O-definition-
| |
| ;text
| |
| move.l d0,d2
| |
| move.l dosbase,a6
| |
| jsr open(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| dosname: dc.b 'dos.library',0,0
| |
| align.w
| |
|
| |
| dosbase: dc.l 0
| |
|
| |
| consolname: dc.b 'CON:0/100/640/100/ ** CLI-Test **',0
| |
| align.w
| |
|
| |
| conhandle: dc.l 0
| |
|
| |
| mytext:
| |
| dc.b $9b,'4;31;40m'
| |
| dc.b 'underline'
| |
| dc.b $9b,'3;33;40m',$9b,'5;20H'
| |
| dc.b '** Hello World !! **',0
| |
|
| |
| align
| |
|
| |
| outline: dc.w 0 ;output buffer for pchar
| |
|
| |
| end
| |
|
| |
| Now that you've done text and character output,its time to move on
| |
| to text input.
| |
|
| |
| 6.4.2.Keyboard Input.
| |
| ---------------------
| |
| You can read keyboard input very easily.You just need to open the
| |
| I/O channel of the CON:window and read from it.You need the read
| |
| function from the DOS library to do this.Its offset is -42.
| |
| The function has three parameters just like the WRITE function.
| |
|
| |
| In D1 the handle number that you get from the WRITE function.
| |
| In D2 the address that the data read in is to start.
| |
| In D3 the number of bytes to read.
| |
|
| |
| Here is a subroutine that reads the number of characters from the
| |
| keyboard that it finds in D3.It puts them in a buffer.
| |
|
| |
| read = -42 ; (6.4.2A)
| |
| ...
| |
|
| |
| getchr: ;* Get (D3) characters from the
| |
| ;keyboard
| |
| move.l #inbuff,d2 ;address of buffer in D2
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| move.l conhandle,d1 ;our window handle
| |
| jsr read(a6) ;call read function
| |
| rts ;done!
| |
|
| |
| inbuff: blk.b 80,0 ;buffer for keyboard input
| |
|
| |
| This routine returns to the main program when <Return> is entered.
| |
| If more than D3 characters are entered,"inbuff"only gets the first
| |
| characters.The routine gets the remaining characters when called a
| |
| second time.
| |
| This sort of input is fairly easy.You can backspace,because only
| |
| the characters that should be there are put in the memory block
| |
| starting at "inbuff".The number of characters moved into "inbuff"
| |
| is put in D0.
| |
|
| |
| Try the program out as follows:
| |
|
| |
| After opening the CON:window,put the following lines in the main
| |
| program:
| |
|
| |
| move #80,d3 ;read 80 characters (6.4.2B)
| |
| bsr readchr ;get line from keyboard
| |
| lea inline,a0 ;address of line in A0
| |
| clr.b 0(a0,d0) ;null byte on the end
| |
| bsr pmsg ;output line again
| |
|
| |
| bp:
| |
|
| |
| After this comes the code segment that closes the window again.
| |
| After loading the program into the AssemPro debugger,make "bp"a
| |
| breakpoint and start the program.(SEKA users start the program
| |
| with "g run"and enter "bp"as the breakpoint).The program quits at
| |
| the breakpoint and you can take a look at the results on the
| |
| screen.Then you can continue the program (SEKA with "j bp") and
| |
| let the window close.
| |
| After starting the program and opening the window,the cursor
| |
| appears in the upper left corner of the window.Enter some text and
| |
| press <Return>.The string that you just entered is output again on
| |
| the screen.
| |
| You use the "pmsg"routine from the previous chapter to do this
| |
| output.This routine needs a null byte at the end of the text to be
| |
| output.You put a null byte there by putting the address of the
| |
| input buffer in A0 and then erasing the byte at A0+D0 using the
| |
| CLR.B command.Since D0 contains the number of characters that were
| |
| entered,this byte is the first unused byte.
| |
| Since you're in the debugger you can redisplay the disassembled
| |
| output when the program ends to see what "getchr"put in "inbuff"
| |
| (SEKA owners can use "q inbuff"when the program ends to see what
| |
| "getchr"put there.)You'll find the characters that you typed plus
| |
| a closing $A.The $A stands for the <Return> key and its counted
| |
| too,so if you entered a 12 and then hit <Return>,for example,D0
| |
| will contain a 3.
| |
| Try this again with a RAW:window.Change the window definition from
| |
| CON: to RAW:and reassemble the program.You'll notice the diference
| |
| right away.After you've entered one character,a return is executed
| |
| D0 always as one bit in it.
| |
| The advantage of this form of input is that cursor and function
| |
| keys can be recognized.Using your own routine,you can repeatedly
| |
| accept input of characters using "getchr"and then work with the
| |
| special characters.
| |
| Theres another form of keyboard input:checking for a single key.
| |
| This is important when a program is about to execute an important
| |
| function and the user must say he wants it executed by entering
| |
| "Y"for yes.This can be treated as normal input,but in some cases,
| |
| there is a better method.
| |
| There is a function in the DOS library that waits a certain
| |
| specified length of time for a key to be pressed,and returns a
| |
| zero (FALSE) if no key was hit in this time period.It returns a -1
| |
| ($FFFFFFFF = TRUE) if one was.To find out which key it takes
| |
| another function.The WaitForChar function,is only good for tasks
| |
| like waiting for the user to let the program know that it can
| |
| continue scrolling text.
| |
|
| |
| The function needs two parameters:
| |
|
| |
| In D1 the handle number of the window or file from which the
| |
| character should be read.It can also wait for a character
| |
| from an interface.
| |
| In D2 you pass the length of time in microseconds that you
| |
| should wait for a key stroke.
| |
|
| |
| To wait one second for one key to be hit,you can use the following
| |
| routine:
| |
|
| |
| WaitForCh=-30-174 ; (6.4.2C)
| |
| ...
| |
|
| |
| scankey: ;* Wait for a key stroke
| |
| move.l conhandle,d1 ;in our window
| |
| move.l #1000000,d2 ;waiting time 1 second
| |
| move.l dosbase,a6 ;DOS base address
| |
| jsr waitforch(a6) ;wait...
| |
| tst.l d0 ;test result
| |
| rts
| |
|
| |
| The TST command at the end of the program allows the calling
| |
| routine to use a BEQ or BNE command to evaluate the results of the
| |
| routine-BEQ branches if no key was hit.BNE doesn't.
| |
| Heres an example program in AssemPro format covering what you have
| |
| learned so far.Opening and closing a window,displaying text in the
| |
| window and inputting text:
| |
|
| |
| ;***** 6.4.2A.ASM S.D *****
| |
|
| |
| openlib =-30-378
| |
| closelib =-414
| |
| ;execbase =4 ;defined in AssemPro
| |
| ;Macros
| |
|
| |
| * call to Amiga.Dos:
| |
|
| |
| open =-30
| |
| close =-30-6
| |
| read =-42
| |
| write =-48
| |
| IoErr =-132
| |
| mode_old =1005
| |
| alloc_abs =-$cc
| |
|
| |
| ILABEL AssemPro:include/Amiga.l ;AssemPro only
| |
|
| |
| INIT_AMIGA ;AssemPro only
| |
|
| |
| run:
| |
| bsr init ;initialization
| |
| bsr test ;system test
| |
| nop
| |
| bra qu ;quit and exit
| |
|
| |
| test:
| |
| move.l #mytext,d0
| |
| bsr pmsg
| |
| bsr pcrlf
| |
| bsr pcrlf
| |
| move.l #80,d3 ;80 characters to read in (D3)
| |
| bsr getchr ;get character
| |
| bsr pmsg ;output line
| |
|
| |
| rts
| |
|
| |
| init: ;system initialization and open
| |
| move.l execbase,a6 ;number of execute-library
| |
| lea dosname(pc),a1
| |
| moveq #0,d0
| |
| jsr openlib ;open DOS-Library
| |
| move.l d0,dosbase
| |
| beq error
| |
|
| |
| lea consolname(pc),a1 ;console definition
| |
| move.l #mode_old,d0
| |
| bsr openfile ;console open
| |
| beq error
| |
| move.l d0,conhandle
| |
|
| |
| rts
| |
|
| |
| pmsg: ;print message (D0)
| |
| movem.l d0-d7/a0-a6,-(sp)
| |
| move.l d0,a0
| |
| move.l a0,d2
| |
| clr.l d3
| |
|
| |
| ploop:
| |
| tst.b (a0)+
| |
| beq pmsg2
| |
| addq.l #1,d3
| |
| bra ploop ;check length
| |
|
| |
| pmsg2:
| |
| move.l conhandle,d1
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6
| |
| rts
| |
|
| |
| pcrlf:
| |
| move #10,d0
| |
| bsr pchar
| |
| move #13,d0
| |
|
| |
| pchar: ;character in D0 output
| |
| movem.l d0-d7/a0-a6,-(sp) ;save all
| |
| move.l conhandle,d1
| |
|
| |
| pch1:
| |
| lea outline,a1
| |
| move.b d0,(a1)
| |
| move.l a1,d2
| |
| move.l #1,d3 ;1 letter
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6 ;restore all
| |
| rts
| |
|
| |
| getchr: ;get character for keyboard
| |
| move.l #1,d3 ;1 character
| |
| move.l conhandle,d1
| |
| lea inbuff,a1 ;buffer address
| |
| move.l a1,d2
| |
| move.l dosbase,a6
| |
| jsr read(a6)
| |
| clr.l d0
| |
| move.b inbuff,d0
| |
| rts
| |
|
| |
| error:
| |
| move.l dosbase,a6
| |
| jsr IoErr(a6)
| |
| move.l d0,d5
| |
|
| |
| move.l #-1,d7 ;flag
| |
|
| |
| qu:
| |
| move.l conhandle,d1 ;window close
| |
| move.l dosbase,a6
| |
| jsr close(a6)
| |
|
| |
| move.l dosbase,a1 ;DOS.Lib close
| |
| move.l execbase,a6 jsr ;close lib (A6)
| |
|
| |
| EXIT_AMIGA ;AssemPro only
| |
|
| |
|
| |
| openfile: ;open file
| |
| move.l a1,d1 ;pointer to I/O-Definition-
| |
| ;Text
| |
| move.l d0,d2
| |
| move.l dosbase,a6
| |
| jsr open(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| dosname: dc.b 'dos.library',0,0
| |
| align.w
| |
|
| |
| dosbase: dc.l 0
| |
|
| |
| consolname: dc.b 'CON:0/100/640/100/** CLI-TEST **',0
| |
| align.w
| |
|
| |
| conhandle: dc.l 0
| |
|
| |
| mytext: dc.b '** Hello World !! **',0
| |
|
| |
| align
| |
|
| |
| outline: dc.w 0 ;output buffer for pchar
| |
|
| |
| inbuff: blk.b 8 ;input buffer
| |
|
| |
| end
| |
|
| |
|
| |
| 6.4.3.Printer Control.
| |
| ----------------------
| |
| Now that you've looked at console I/O,lets look at outputting data
| |
| from the computor.The first device that we'll discuss is the
| |
| printer.
| |
| Its very easy to use the printer.You just need to open another
| |
| channel.It goes just the way you learned it with CON: and RAW:
| |
| windows;the only difference is you enter PRT:instead.
| |
| You open this channel using the same lines that you used above for
| |
| the window except that the pointer is to the channel name PRT:in
| |
| D1.You pass the mode "new"(1006) in D2 in the "do_open"routine as
| |
| well.Save the handle number that comes back at a label called
| |
| "prthandle".
| |
| Now you can use the same output routines that you used with the
| |
| windows to send text to the printer.You need to put "prthandle"
| |
| instead of "conhandle"in the line with the "move.l conhandle,d1"
| |
| command.
| |
| Actually it would be better to eliminate this line from the
| |
| routine totally.Then you can use the same routine for window and
| |
| printer output.The calling procedure would then need to put
| |
| "conhandle"in D1 for window output.It would put "prthandle" in D1
| |
| for printer output.This is a very flexible output routine that can
| |
| be used for window and printer output now.You can't accept input
| |
| from the printer,because the printer doesn't send data.It just
| |
| accepts it and prints it.
| |
|
| |
| 6.4.4.Serial I/O.
| |
| -----------------
| |
| Its just as easy to use the serial interface as the printer.Just
| |
| enter SER:as the filename.Now you can use the DOS functions READ
| |
| and WRITE just as before to do I/O channels you've just opened.
| |
| You can set the parameters for the interface (like Hand shake and
| |
| Transfer rate) with the Preferences program.
| |
|
| |
| 6.4.5.Speech Output.
| |
| --------------------
| |
| The Amiga has a speech synthesizer built in.This isn't quite as
| |
| easy to program as the I/O devices discussed earlier,however.You
| |
| use the "narrator.device"to do this.
| |
| This device requires several program steps to install it and then
| |
| causes it to speak.You need to open the device,start the I/O,etc..
| |
| Lets look at how to translate the text into the proper form and
| |
| then output the text.
| |
| First we need to do some initialization.Lets define the constants
| |
| now.Some of them are new.
| |
|
| |
| ;***** Narrator Basic Functions 3/87 S.D ***** (6.4.5A)
| |
|
| |
| openlib =-408
| |
| closelib =-414
| |
| execbase = 4
| |
|
| |
| open =-30 ;open file
| |
| close =-36 ;close file
| |
| mode_old = 1005 ;old mode
| |
|
| |
| opendevice =-444 ;open device
| |
| closedev =-450 ;close device
| |
|
| |
| sendIo =-462 ;start I/O
| |
| abortIO =-480 ;abort I/O
| |
|
| |
| translate =-30 ;translate text
| |
|
| |
| ;The initialization routine follows:
| |
|
| |
| init: ;initialize and open system
| |
|
| |
| ;* open DOS library *
| |
|
| |
| move.l execbase,a6 ;pointer to execbase
| |
| lea dosname,a1 ;pointer to DOS name
| |
| moveq #0,d0 ;version unimportant
| |
| jsr openlib(a6) ;open DOS library
| |
| move.l d0,dosbase ;save handle
| |
| beq error ;error handle
| |
|
| |
| ;* Open translator.library *
| |
|
| |
| lea transname,a1 ;pointer to translator name
| |
| clr.l d0
| |
| jsr openlib(a6) ;open translator
| |
| move.l d0,transbase ;save handle
| |
| beq error ;error handling
| |
|
| |
| ;* Set up I/O area for Narrator *
| |
|
| |
| lea talkio,a1 ;pointer to I/O area in A1
| |
| move.l #nwrrep,14(a1) ;enter port address
| |
| move.l #amaps,48+8(a1) ;pointer to audio mask
| |
| move #4,48+12(a1) ;number of the mask
| |
| move.l #512,36(a1) ;length of the output area
| |
| move #3,28(a1) ;command:write
| |
| move.l #outtext,40(a1) ;address of output area
| |
|
| |
| ;* Open Narrator device *
| |
|
| |
| clr.l d0 ;number 0
| |
| clr.l d1 ;no flags
| |
| lea nardevice,a0 ;pointer to device name
| |
| jsr opendevice(a6) ;open narrator.device
| |
| tst.l d0 :error?
| |
| bne error ;Yes!
| |
|
| |
| ;* Open Window *
| |
|
| |
| move.l #consolname,d1 ;console definition
| |
| move.l #mode_old,d2 ;old mode
| |
| move.l dosbase,a6 ;DOS base address
| |
| jsr open(a6) ;open window
| |
| tst.l d0 ;error?
| |
| beq error ;Yes!
| |
| move.l d0,conhandle ;else save handle
| |
|
| |
| After you've done this initialization,you can have the computor
| |
| save the text you have prepared for it.To see what the Amiga is
| |
| saying,use the "pmsg"function to have the text written to the
| |
| window:
| |
|
| |
|
| |
| move.l #intext,d2 ;text for the Amiga to say
| |
| bsr pmsg ;output in window also
| |
|
| |
| sayit: ;have the text said
| |
|
| |
| ;*Translate the text into a form that the computor can use *
| |
|
| |
| lea intext,a0 ;address of the text
| |
| move.l #outtext-intext,d0 ;length of the text
| |
| lea outtext,a1 ;address of output area
| |
| move.l #512,d1 ;length of output area
| |
| move.l tranbase,a6 ;translator base address
| |
| jsr translate(a6) ;translate text
| |
|
| |
| ;* Speech output *
| |
|
| |
| lea talkio,a1 ;address of I/O structure
| |
| move.l #512,36(a1) ;length of output area
| |
| move.l execbase,a6 ;EXEC base address
| |
| jsr sendIO(a6) ;start I/O (speech output)
| |
|
| |
|
| |
| Once the program ends,the I/O stops as well,so you need to put in
| |
| something that keeps the program going longer.You'll use the
| |
| "getchr"function that you programmed earlier to take care of this:
| |
|
| |
|
| |
| bsr getchr ;wait for keyboard input
| |
|
| |
| The computor waits until the <Return> key is pressed.Now you can
| |
| listen to what the Amiga as to say.Once the <Return> key is
| |
| pressed,the program stops.
| |
|
| |
|
| |
| qu: ; (6.4.5C)
| |
| move.l execbase,a6 ;EXEC base address
| |
| lea talkio,a1 ;pointer to I/O area
| |
| jsr abortio(a6) ;stop the I/O
| |
|
| |
| move.l conhandle,d1
| |
| move.l dosbase,a6
| |
| jsr close(a6) ;close window
| |
|
| |
| move.l dosbase,d1
| |
| move.l execbase,a6
| |
| jsr closelib(a6) ;close DOS library
| |
|
| |
| lea talkio,a1
| |
| jsr closedev(a6) ;close narrator.device
| |
|
| |
| move.l tranbase,a1
| |
| jsr closelib(a6) ;close translator library
| |
|
| |
| rts ;* end of program
| |
|
| |
|
| |
| Now comes the data that you need for the program above:
| |
|
| |
|
| |
| mytext: dc.b 'This is a test text !',10,13,10,13,0,0
| |
| dosmame: dc.b 'dos.library',0,0
| |
| transname: dc.b "translator.library",0
| |
| consolname: dc.b 'RAW:0/100/640/100/** Test window',0
| |
| nardevice dc.b 'narrator.device',0
| |
| align
| |
| dosbase: dc.l 0
| |
| tranbase dc.l 0
| |
| amaps: dc.b 3,5,10,12
| |
| align
| |
| conhandle: dc.l 0
| |
| talkio: blk.l 20,0
| |
| nwrrep: blk.l 8,0
| |
| intext: dc.b 'hello,i am the amiga talking to you',0
| |
| align
| |
| outtext: blk.b 512,0
| |
|
| |
|
| |
| This is quite a bit of work,but its worth it because it opens so
| |
| many possibilities for you.There are a lot of variations possible
| |
| if you modify parameters.These parameters are entries in the I/O
| |
| area starting at the "talkio"label.The area is built as follows:
| |
|
| |
| Offset Length Meaning
| |
| ----------------------------------------------------------------
| |
| ** Port Data **
| |
| 0 L Pointer to next block
| |
| 4 L Pointer to last block
| |
| 8 B I/O type
| |
| 9 B Priority
| |
| 10 L Pointer to I/O name
| |
| 14 L Pointer to port
| |
| 18 W Length
| |
| ** I/O Data **
| |
| 20 L Pointer to device
| |
| 24 L Pointer to device unit
| |
| 28 W Command word
| |
| 30 B I/O flags
| |
| 31 B I/O status
| |
| 32 L I/O pointer
| |
| 36 L I/O length
| |
| 40 L Pointer to Data
| |
| 44 L I/O offset
| |
| ** Narrator data items **
| |
| 48 W Speech speed
| |
| 50 W Highness of voice
| |
| 52 W Speech mode
| |
| 54 W Sex (male/female voice)
| |
| 56 L Pointer to audio mask
| |
| 60 W Number of mask
| |
| 62 W Volume
| |
| 64 W Read in rate
| |
| 66 B Flag for producing graphics (0=off)
| |
| 67 B Actual mask (internal use)
| |
| 68 B Channel used (internal use)
| |
|
| |
| We would'nt recommend experimenting with the data in the first two
| |
| blocks.If you do,you can easily cause a system crash.You can use
| |
| the last entries of the structure to produce some interesting
| |
| effects though.
| |
| Heres an overview of the parameters you can use to vary the speech
| |
| output.The value in parenthesis is the standard value,the value
| |
| set when narrator.device is opened.
| |
|
| |
| Speech speed (150);
| |
| You can use this to set the speed of speech.The pitch of the voice
| |
| is not affected by this value.
| |
|
| |
| Pitch of voice (110);
| |
| You can choose a value between 65 and 320 for the pitch (from
| |
| Goofy to Mickey Mouse).
| |
|
| |
| Speech mode (0);
| |
| The zero gives half-way naturel speech.A one lets the Amiga speak
| |
| in monotone like a robot.
| |
|
| |
| Sex (0);
| |
| A zero means masculine and a one means feminine (more or less..)
| |
|
| |
| Volume (64);
| |
| The volume can range from 0 to 64.The standard value is the
| |
| loudest possible.
| |
|
| |
| Read in rate (22200);
| |
| By lowering this value,the voice is lowered.If you change this
| |
| very much,you'll get some wierd voices!
| |
|
| |
| You can experiment a bit until you find a interesting voice.Have
| |
| fun!
| |
| Here is a complete talking program in AssemPro format:
| |
|
| |
| ;***** Speech output S.D. *****
| |
|
| |
| openlib =-30-378
| |
| closelib =-414
| |
| ;execbase =4 ;defined by AssemPro
| |
|
| |
| * calls to Amiga Dos:
| |
|
| |
| open =-30
| |
| close =-30-6
| |
| opendevice =-444
| |
| closedev =-450
| |
| addport =-354
| |
| remport =-360
| |
| ;DoIo =-456
| |
| sendIo =-462
| |
| abortIo =-480
| |
| read =-30-12
| |
| write =-30-18
| |
| ;myinput =-30-24
| |
| ;output =-30-30
| |
| ;currdir =-30-96
| |
| ;exit =-30-114
| |
| waitforch =-30-174
| |
| findtask =-294
| |
| translate =-30
| |
| mode_old = 1005
| |
| ;mode_new = 1006
| |
| ;alloc_abs =-$cc
| |
| ;free_mem =-$d2
| |
|
| |
| ;!!!when>500KB !!! or place in chip memory
| |
| ;org $40000
| |
| ;load $40000
| |
| ;!!!!!!!!!!!!!!!!!!!!!!!
| |
|
| |
|
| |
| ILABEL AssemPro:includes/Amiga.l ;AssemPro only
| |
|
| |
| INIT_AMIGA ;AssemPro only
| |
|
| |
| run:
| |
| bsr init ;initialization
| |
| bra test ;system-test
| |
|
| |
| init: ;system initialization and
| |
| ;open
| |
| move.l execbase,a6 ;pointer to exec library
| |
| lea dosname(pc),a1 ;pointer to dos name
| |
| moveq #0,d0 ;version:not important
| |
| jsr openlib(a6) ;open DOS-Library
| |
| move.l d0,dosbase ;save handle
| |
| beq error ;error routine
| |
|
| |
| ;* ;open translator library
| |
| move.l execbase,a6 ;pointer to exec library
| |
| lea transname,a1 ;pointer to translator library
| |
| clr.l d0
| |
| jsr openlib(a6) ;open translator
| |
| move.l d0,tranbase ;save handle
| |
| beq error ;error routine
| |
|
| |
| ;* ;set up
| |
| sub.l a1,a1
| |
| move.l execbase,a6
| |
| jsr findtask(a6) ;find task
| |
| move.l d0,nwrrep+2
| |
|
| |
| lea nwrrep,a1
| |
| jsr addport(a6) ;add port
| |
|
| |
| ;* ;open narrator device
| |
| lea talkio,a1 ;pointer to I/O area in A1
| |
| move.l #nwrrep,14(a1) ;enter port address
| |
| clr.l d0 ;number 0
| |
| clr.l d1 ;no flags
| |
| lea nardevice,a0 ;pointer to device name
| |
| jsr opendevice(a6) ;open narrator.device
| |
| tst.l d0 ;error?
| |
| bne error ;Yes!
| |
|
| |
| ;* ;set up I/O for narrator
| |
| ;device
| |
|
| |
| bp:
| |
| lea talkio,a1 ;pointer to I/O in A1
| |
| move.l #nwrrep,14(a1) ;enter port address
| |
| move.l #amaps,48+8(a1) ;pointer to audio mask
| |
| move #4,48+12(a1) ;size of mask
| |
|
| |
| lea consolname(pc),a1 ;console-definition
| |
| move.l #mode_old,d0
| |
| bsr openfile ;console open
| |
| beq error
| |
| move.l d0,conhandle
| |
|
| |
| rts
| |
|
| |
| test:
| |
| move.l #mytext,d0
| |
| bsr pmsg ;test-text output
| |
|
| |
| bsr sayit ;say text
| |
|
| |
| bsr readin ;input
| |
| move #10,d0
| |
| bsr pchar ;LF output
| |
| move.l #inline+2,d0
| |
| bsr pmsg ;and again
| |
| bsr pcrlf
| |
| bra qu
| |
|
| |
| error:
| |
| move.l #-1,d7 ;flag
| |
|
| |
| qu:
| |
| move.l execbase,a6
| |
| lea talkio,a1
| |
| jsr abortio(a6)
| |
|
| |
| move.l conhandle,d1 ;window close
| |
| move.l dosbase,a6
| |
| jsr close(a6)
| |
|
| |
| move.l dosbase,a1 ;DOS.Lib close
| |
| move.l execbase,a6
| |
| jsr closelib(a6)
| |
|
| |
| lea nwrrep,a1
| |
| jsr remport(a6) ;remove port
| |
| lea talkio,a1
| |
| jsr closedev(a6) ;close narrator device
| |
| move.l tranbase,a1
| |
| jsr closelib(a6) ;close translator library
| |
|
| |
| EXIT_AMIGA ;AssemPro only
| |
|
| |
| openfile: ;open file
| |
| move.l a1,d1 ;pointer to I/O definition-
| |
| ;text
| |
| move.l d0,d2
| |
| move.l dosbase,a6
| |
| jsr open(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| pmsg: ;print message (D0)
| |
| movem.l d0-d7/a0-a6,-(sp)
| |
| move.l d0,a0
| |
| move.l a0,d2
| |
| clr.l d3
| |
|
| |
| mess1:
| |
| tst.b (a0)+
| |
| beq mess2
| |
| addq.l #1,d3
| |
| bra mess1 ;length calculate
| |
|
| |
| mess2:
| |
| move.l conhandle,d1
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6
| |
| rts
| |
|
| |
| pcrlf:
| |
| move #10,d0
| |
| bsr pchar
| |
| move #13,d0
| |
|
| |
| pchar: ;output characters in D0
| |
| movem.l d0-d7/a0-a6,-(sp) ;save all
| |
| move.l conhandle,d1
| |
|
| |
| pch1:
| |
| lea chbuff,a1
| |
| move.b d0,(a1)
| |
| move.l a1,d2
| |
| move.l #1,d3 ;1 letter
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6 ;restore all
| |
| rts
| |
|
| |
| scankey: ;test key
| |
| move.l conhandle,d1
| |
| move.l #500,d2 ;wait value
| |
| move.l dosbase,a6
| |
| jsr waitforch(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| readin: ;input from keyboard
| |
| movem.l d0-d7/a0-a6,-(sp) ;save registers
| |
| lea inline+2,a2 ;pointer to input buffer
| |
| clr.l (a2)
| |
|
| |
| inplop:
| |
| bsr getchr
| |
| cmp.b #8,d0
| |
| beq backspace
| |
| cmp.b #127,d0 ;delete?
| |
| beq backspace
| |
| bsr pchar ;character output
| |
| cmp.b #13,d0
| |
| beq inputx
| |
| move.b d0,(a2)+
| |
| bra inplop
| |
|
| |
| inputx:
| |
| clr.b (a2)+
| |
| sub.l #inline,a2
| |
| move a2,inline ;length in lines+1
| |
| movem.l (sp)+,d0-d7/a0-a6 ;registers
| |
| rts
| |
|
| |
| backspace:
| |
| cmp.l #inline,a2 ;at the beginning?
| |
| beq inplop ;yes
| |
| move.b #8,d0
| |
| bsr pchar ;backspace
| |
| move #32,d0
| |
| bsr pchar ;blank
| |
| move #8,d0
| |
| bsr pchar ;backspace
| |
| clr.b (a2)
| |
| subq.l #1,a2
| |
| bra inplop
| |
|
| |
| getchr: ;get one character from
| |
| ;keyboard
| |
| move.l #1,d3 ;one character
| |
| move.l conhandle,d1
| |
| lea inbuff,a1 ;buffer address
| |
| move.l a1,d2
| |
| move.l dosbase,a6
| |
| jsr read(a6)
| |
| clr.l d0
| |
| move.b inbuff,d0
| |
| rts
| |
|
| |
| sayit:
| |
| lea intext,a0
| |
| move.l #outtext-intext,d0
| |
| lea outtext,a1
| |
| move.l #512,d1
| |
| move.l tranbase,a6
| |
| jsr translate(a6)
| |
|
| |
| p:
| |
| lea talkio,a1
| |
| move #3,28(a1) ;??
| |
| move.l #512,36(a1)
| |
| move.l #outtext,40(a1)
| |
| move.l execbase,a6
| |
| jsr sendio(a6)
| |
|
| |
| rts
| |
|
| |
| mytext: dc.b 'This is our Test-Text !',10,13,10,13,0,0
| |
|
| |
| dosname: dc.b 'dos.library',0,0
| |
|
| |
| transname: dc.b "translator.library",0
| |
| align.w
| |
|
| |
| dosbase: dc.l 0
| |
|
| |
| tranbase: dc.l 0
| |
|
| |
| consolname: dc.b 'CON:0/100/640/100/* Speech-Test S.D.* ',0
| |
|
| |
| nardevice: dc.b 'narrator.device',0
| |
|
| |
| amaps: dc.b 3,5,10,12,0,0
| |
| align.w
| |
|
| |
| conhandle: dc.l 0
| |
|
| |
| inbuff: blk.b 8
| |
|
| |
| inline: blk.b 180,0
| |
|
| |
| chbuff: blk.b 82,0
| |
|
| |
| narread: blk.l 20,0
| |
|
| |
| talkio: blk.l 20,0
| |
|
| |
| nwrrep: blk.l 8,0
| |
|
| |
| intext: dc.b 'hello,i am the amiga computor',0
| |
| align.w
| |
|
| |
| outtext: blk.l 128,0
| |
|
| |
|
| |
| end
| |
|
| |
| 6.5.Disk Operations.
| |
| --------------------
| |
| The most important peripheral device for a computor like the Amiga
| |
| is the disk drive.You use it to save data,so that you don't lose
| |
| it when you turn off the computor.We'll look at saving and
| |
| retrieving data in this chapter.
| |
| Lets first look at the simple disk operations that are used for
| |
| data management.To gain access to a file,you must open it first.
| |
| This is done using the OPEN function from the DOS library,a
| |
| function that you're already familiar with.I'll assume in the
| |
| following examples,that you've already opened the DOS library.
| |
|
| |
| 6.5.1.Open Files.
| |
| -----------------
| |
| The open function needs a parameter for the mode.The mode has a
| |
| particular meaning.If the file is opened for reading,it must
| |
| already exist.The mode for the OPEN function must be "old"(1005)
| |
| in this case.
| |
| If you want to produce a file,you must open it first.Since it does
| |
| not exist,you use the "new"(1006) mode.If a file is opened for
| |
| writing using this mode even though a file with this name already
| |
| exists,the old file with this name is erased and replaced.To avoid
| |
| loss of data,you should check if a file by that name already
| |
| exists and then output an error message if it does.
| |
| You're going to start with a subroutine that opens a file.Lets
| |
| assume that the filename starts at the label "filename",and that
| |
| it is closed with a null byte.You just need to pass the mode in
| |
| register D2.
| |
| The routine puts the file handle number in "filehd"and returns to
| |
| the main program.Since the operation with the handle is the last
| |
| one performed by the subroutine,the status of the operation can be
| |
| evaluated once the return has been executed.If the operation went
| |
| smoothly and the file is opened,the handle number has a non-zero
| |
| value.If it is zero and "bsr openfile"is followed by "beq error",
| |
| you can branch to an error handling routine when problems occur.
| |
|
| |
| Here is a subroutine for opening and closing a file:
| |
|
| |
| open =-30 ; (6.5.1A)
| |
| close =-36
| |
| mode_old = 1005
| |
| mode_new = 1006
| |
| ...
| |
| openfile: ;*open file,mode in D0
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| move.l #filename,d1 ;pointer to filename
| |
| jsr open(a6) ;open file
| |
| move.l d0,filehd ;save handle
| |
| rts
| |
|
| |
| closefile: ;*close file
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| move.l filehd,d1 ;file handle in D1
| |
| jsr close(a6) ;close file
| |
| rts
| |
|
| |
| filehd: dc.l 0 ;storage for file handle
| |
| filename: dc.b "filename",0 ;file to be opened
| |
| align ;even
| |
|
| |
| To use these subroutines,you must look at how you can load and
| |
| save data.
| |
|
| |
| 6.5.2.Reading and Writing Data.
| |
| -------------------------------
| |
| Lets write a new file.To start,write the following lines:
| |
|
| |
| move.l #mode_new,d2 ;open new file (6.5.2A)
| |
| bsr openfile ;open file
| |
| beq error ;did'nt work!
| |
|
| |
| For the filename,write a name like "Testfile"in the line labelled
| |
| "filename".After calling the "openfile"routine,a file with this
| |
| name is created on the disk.If one existed already,it is erased.
| |
|
| |
| Lets assume you want to write a short text file.For the example
| |
| lets use:
| |
|
| |
| text: dc.b "This is a test text for the Testfile",0
| |
| textend:
| |
|
| |
| The "textend"label is used so that you can calculate the number of
| |
| data bytes by subtracting "text".
| |
| You want to write this text in the file.Use the WRITE function
| |
| which needs three parameters:
| |
|
| |
| In D1 the file handle that you got back from the OPEN function.
| |
| In D2 a pointer to the data that should be written.
| |
| In D3 the number of bytes to be written.
| |
|
| |
| For the example,you'll need another segment of code to put the
| |
| pointer to the data in D2 and the number of bytes in D3:
| |
|
| |
| write =-48 ; (6.5.2B)
| |
| ...
| |
| writedata: ;*write data in the file
| |
| move.l dosbase,a6 ;DOS base address
| |
| move.l filehd,d1 ;file handle in D1
| |
| jsr write(a6) ;write data
| |
| rts
| |
|
| |
| After opening the file,you can call the subroutine from the main
| |
| program with the following lines:
| |
|
| |
| move.l #text,d2 ;pointer to data
| |
| move.l #textend-text,d3 ;number of bytes
| |
| bsr writedata ;write data in the file
| |
|
| |
| Then close the file with:
| |
|
| |
| bsr closefile ;close file
| |
| bra end ;end program
| |
|
| |
| After running the program,look at the directory of the diskette,
| |
| you should find the file "testfile".It is just as long as your
| |
| text.You want to read this file in,to make sure it contains the
| |
| right data.
| |
| You need the DOS function READ,which needs the same parameters as
| |
| the WRITE function.You can use parameters for the number of bytes
| |
| to read just part of the file.If you give a larger number than the
| |
| file contains,the whole file is loaded.You'll find the number of
| |
| bytes read in D0.
| |
| Lets set up a field that as enough space for the data you want to
| |
| read.You can do this with the following line:
| |
|
| |
| field: blk.b 100 ;reserve 100 bytes
| |
|
| |
| For the example data,this is plenty.If you want to load another
| |
| file,you may need to reserve more space.
| |
| Now lets write a subroutine to read the data.You always want to
| |
| load whole files.You just need to pass the address of the buffer
| |
| so the data is loaded into the subroutine.In the example,its the
| |
| address "field".
| |
| Heres the subroutine that reads the entire opened file into the
| |
| memory area pointed to by D2:
| |
|
| |
| read = -42 ; (6.5.2C)
| |
| ...
| |
| readdata: ;*read file
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| move.l filehd,d1 ;file handle in D1
| |
| move.l #$ffffff,d3 ;read an arbitrary number of bytes
| |
| jsr read(a6) ;read data
| |
| rts
| |
|
| |
| To use this routine to load the file into the buffer "field",use
| |
| the following main program:
| |
|
| |
| move,l #mode_old,d2 ;old file
| |
| bsr openfile ;open file
| |
| beq error ;did'nt work!
| |
| move.l #field,d2 ;pointer to data buffer
| |
| bsr readdata ;read file
| |
| move.l d0,d6 ;save number of bytes in D6
| |
| bsr closefile ;close file
| |
| bra end ;program end
| |
|
| |
| After assembling and starting this program,you can use the
| |
| debugger to look at the data buffer that you filled with data from
| |
| the file.In D6,you'll find the number of bytes that were read from
| |
| the file.
| |
|
| |
| 6.5.3.Erase Files.
| |
| ------------------
| |
| Once you've experimented enough with the program above,you'll
| |
| certainly want to erase the "Testfile"file.The DELETEFILE function
| |
| in the DOS library has an offset of -72.It only needs 1 parameter.
| |
| The parameter is passed in D1.The parameter is a pointer to the
| |
| filename.The name must be closed with a null byte.
| |
|
| |
| To erase "Testfile",use the following lines:
| |
|
| |
| deletefile =-72 ; (6.5.3)
| |
| ...
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| move.l #filename,d1 ;pointer to filename in D1
| |
| jsr deletefile(a6) ;erase file
| |
|
| |
| The file is deleted.You can't save the file with normal methods if
| |
| you accidently erase it!You can use a trick that saves the data.
| |
| We'll take a look at this trick later.Its used in lots of programs
| |
|
| |
| 6.5.4.Rename Files.
| |
| -------------------
| |
| When a text editing program writes a text that as be altered back
| |
| to the disk,the old file usually isn't erased.Often the old file
| |
| is renamed.For example,it might get the name "Backup".The new file
| |
| is written to disk with the old name.
| |
| The function in the DOS library that allows you to change the
| |
| names of programs is called RENAME and has -78 as an offset.You
| |
| need to pass two parameters-D1 as a pointer to the old name and D2
| |
| as a pointer to the new name of the file.
| |
|
| |
| To rename "Testfile"as "Backup"(before you erase it),use the
| |
| following lines:
| |
|
| |
| rename =-78
| |
| ...
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| move.l #oldname,d1 ;pointer to old name in D1
| |
| move.l #newname,d2 ;pointer to new name in D2
| |
| jsr rename(a6) ;rename file
| |
| ...
| |
| oldname: dc.b "testfile",0
| |
| newname: dc.b "backup",0
| |
|
| |
| 6.5.5.CLI Directory.
| |
| --------------------
| |
| Lets pretend you've programmed a text editor and started it.Now
| |
| you want to load a text from disk and edit it-but whats the name
| |
| of that file?
| |
| You need a function to read and display the directory of the disk.
| |
| There are several ways to do this.First lets use the easiest
| |
| method.It doesn't require much programming and can be quite
| |
| useful.
| |
| The trick is to call the Dir or List programs that are in the C
| |
| directory.You'll use the CLI commands.The DOS library contains a
| |
| command called "Execute"with offset -222 that allows you to
| |
| execute CLI commands.
| |
| The function needs three parameters:
| |
|
| |
| In D1 a pointer to a string closed with a zero that contains the
| |
| name of the command to be executed.This string must
| |
| contain the same command that you would give in the CLI.It
| |
| can be a null pointer as well.
| |
| In D2 the input file is determined.Normally theres a zero here.
| |
| If however,you give the file handle of a text file,though,
| |
| this file is read and interpreted as a command sequence.If
| |
| you define a window as the input medium,you've programmed
| |
| a new CLI window!
| |
| In D3 the output file is determined.If there a zero here,the
| |
| output of the commands (for example,DIR output) is sent to
| |
| the standard CLI window.
| |
|
| |
| To try this out,insert this subroutine in a program that has
| |
| already opened the DOS library and a window.
| |
|
| |
| execute = -222 ; (6.5.5)
| |
| ...
| |
| dir:
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| move.l #command,d1 ;pointer to command line
| |
| clr.l d2 ;no input (CLI window)
| |
| move.l conhandle,d3 ;output in our window
| |
| jsr execute(a6) ;execute command
| |
| rts
| |
|
| |
| command:
| |
| dc.b "dir",0
| |
|
| |
| This program works with the List command as well.The disadvantage
| |
| of this method is that the disk that the Workbench is loaded from
| |
| must be in the drive or the system requests you to put it in.The
| |
| Dir command is just a program,and the Amiga must load it before it
| |
| can run.
| |
| The disadvantage isn't too great.The program is short,and it
| |
| allows you to use any CLI command in a program.
| |
| Here is the complete program in AssemPro format that calls the dir
| |
| program:
| |
|
| |
| ;***** 6.5.5A DIR.ASM S.D.*****
| |
|
| |
| openlib =-408
| |
| closelib =-414
| |
| ;execbase = 4 ;defined in AssemPro
| |
| ;macros
| |
|
| |
|
| |
| *calls to Amiga Dos:
| |
|
| |
| open =-30
| |
| close =-36
| |
| execute =-222
| |
| IoErr =-132
| |
| mode_old = 1005
| |
| alloc_abs =-$cc
| |
|
| |
| ILABEL AssemPro:includes/Amiga.l ;AssemPro only
| |
|
| |
| INIT_AMIGA ;AssemPro only
| |
|
| |
| run:
| |
| bsr init ;initialization
| |
| bra test ;system test
| |
|
| |
| init: ;system initialization and
| |
| ;open
| |
| move.l execbase,a6 ;number of execute-library
| |
| lea dosname(pc),a1
| |
| moveq #0,d0
| |
| jsr openlib(a6) ;open DOS-library
| |
| move.l d0,dosbase
| |
| beq error
| |
|
| |
| lea consolname(pc),a1 ;console definition
| |
| move.l #mode_old,d0
| |
| bsr openfile ;console open
| |
| beq error
| |
| move.l d0,conhandle
| |
|
| |
| rts
| |
|
| |
| test:
| |
| bsr dir ;do directory
| |
| bra qu ;quit and exit
| |
|
| |
| dir:
| |
| move.l dosbase,a6 ;DOS base address in A6
| |
| move.l #command,d1 ;pointer to command line
| |
| clr.l d2 ;no input (CLI window)
| |
| move.l conhandle,d3 ;output in our window
| |
| jsr execute(a6) ;execute command
| |
| rts
| |
|
| |
| error:
| |
| move.l dosbase,a6
| |
| jsr IoErr(a6)
| |
| move.l d0,d5
| |
|
| |
| move.l #-1,d7 ;flag
| |
|
| |
| qu:
| |
| move.l conhandle,d1 ;window close
| |
| move.l dosbase,a6
| |
| jsr close(a6)
| |
|
| |
| move.l dosbase,a1 ;DOS.Lib close
| |
| move.l execbase,a6
| |
| jsr closelib(a6)
| |
|
| |
| EXIT_AMIGA ;AssemPro only
| |
|
| |
| openfile: ;open file
| |
| move.l a1,d1 ;pointer to I/O-Definition-
| |
| ;text
| |
| move.l d0,d2
| |
| move.l dosbase,a6
| |
| jsr open(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| dosname: dc.b 'dos.library',0,0
| |
| align.w
| |
| dosbase: dc.l 0
| |
| consolname: dc.b 'CON:0/100/640/100/** CLI-Test **',0
| |
| align.w
| |
| conhandle: dc.l 0
| |
| command:
| |
| dc.b "dir",0
| |
|
| |
| end
| |
|
| |
| 6.5.6.Read Directory.
| |
| ---------------------
| |
| Now,lets look at another method that doesn't need the CLI.In this
| |
| way,you can read the directory of any disk without having to play
| |
| Disk Jockey.
| |
| You need to writ a program that does what CLI's Dir program does.
| |
| There are several steps.
| |
| First you must give the system a key to the desired directory.That
| |
| means you must call DOS'Lock function.It needs two parameters:
| |
|
| |
| In D1 pass a pointer to a text that contains the name of the
| |
| directory you wish to read.If,for example,you want to
| |
| read the contents of the RAM disk,the text would be
| |
| 'RAM:',0.
| |
| In D2 put the mode that determines whether to read or write.Let
| |
| us use the "Read"(-2) mode.
| |
|
| |
| You call the Lock function (offset -84) and get either a point to
| |
| the key or a zero returned to you in the D0 register.If you get a
| |
| zero,the call did'nt work,the file was'nt found.This function can
| |
| be used to find if a file is on the disk.You use this function
| |
| with the name and see if D0 comes back zero.If not,the file
| |
| exists.
| |
| Lets assume the file or path exists.You need to save the value
| |
| that came back in D0.You'll need it for both functions that you'll
| |
| call.
| |
| The next function you need is called Examine.You use it to search
| |
| the disk for an acceptable entry.It returns parameters like name,
| |
| length and date that correspond to the entry.You need to reserve a
| |
| memory block for this information and put the beginning of the
| |
| block in D2 before calling the Examine function.Put the key you
| |
| got from the Lock function in the D1 register.
| |
| The memory area that is filled with information is called a
| |
| FileInfoBlock.Its 260 bytes long and contains information about
| |
| the file.The name starts in the 9th byte and ends with a null byte
| |
| so you can easily print it with our "pmsg"routine.The information
| |
| that Examine gives isn't about a particular file,but about the
| |
| disk.The name in FileInfoBlock is the disk name.
| |
| The Examine function sends the status back in the D0 register.
| |
| Since the Lock function already tested if the file existed,evalua-
| |
| ting the status really isn't necessary.
| |
| Now to the function that you can use to read individual files from
| |
| the directory.The function is called ExNext (Examine Next).This
| |
| function searches for the next entry that fits the key every time
| |
| it is called.ExNext gets the same parameters as Examine gets.
| |
| However,the return parameter in D0 is more important here.
| |
| The ExNext function is always called in the same way.It always
| |
| gets the next entry of the directory.If no more entries exist in
| |
| the directory,ExNext puts a zero in the D0 register.
| |
| You need to continue performing this operation until there aren't
| |
| any more entries.You can find this using the IoErr function from
| |
| the DOS library.
| |
| This function doesn't need any parameters.It returns the status of
| |
| the last I/O operation that was performed in the D0 register.After
| |
| the last ExNext,this value is 232,which means no_more_Entries.
| |
|
| |
| Heres a complete routine for reading the directory of the disk in
| |
| DFO:and displaying the contents in the window.
| |
|
| |
| ; 6.5.5B.ASM
| |
| ;***** DOS-Sample function 3/87 S.D. *****
| |
|
| |
| openlib =-30-378
| |
| closelib =-414
| |
| exbase =4
| |
|
| |
| * calls to amiga dos:
| |
|
| |
| open =-30
| |
| close =-30-6
| |
| read =-30-12
| |
| write =-30-18
| |
| myinput =-30-24
| |
| output =-30-30
| |
| currdir =-30-96
| |
| lock =-30-54
| |
| examine =-30-72
| |
| exnext =-30-78
| |
| exit =-30-114
| |
| IoErr =-30-102
| |
| waitforch =-30-174
| |
| mode = 0
| |
| mode_old = 1005
| |
| mode_new = 1006
| |
| alloc_abs =-$cc
| |
| free_mem =-$d2
| |
|
| |
| ILABEL AssemPro:includes/Amiga.l ;AssemPro only
| |
|
| |
| INIT_AMIGA ;AssemPro only
| |
|
| |
| run:
| |
| bsr init ;initialization
| |
| bra test ;system-test
| |
|
| |
| init: ;system initialization and
| |
| ;open
| |
| move.l exbase,a6 ;pointer to exec.library
| |
| lea dosname(pc),a1
| |
| moveq #0,d0
| |
| jsr openlib(a6) ;open dos-library
| |
| move.l do,dosbase
| |
| beq error
| |
|
| |
| lea consolname(pc),a1 ;console definition
| |
| move.l #mode_old,d0
| |
| bsr openfile ;console open
| |
| beq error
| |
| moveq d0,conhandle
| |
|
| |
| rts
| |
|
| |
| test:
| |
| move.l #mytext,d0
| |
| bsr pmsg ;test-text output
| |
|
| |
| move.l dosbase,a6
| |
| move.l #name,d1
| |
| move.l #-2,d2
| |
| jsr lock(a6)
| |
| move.l d0,d5
| |
| tst.l d0
| |
| beq error
| |
| move.l d0,locksav
| |
|
| |
| move.l dosbase,a6
| |
| move.l locksav,d1
| |
| move.l #fileinfo,d2
| |
| jsr examine(a6)
| |
| move.l d0,d6
| |
| tst.l d0
| |
| beq error
| |
|
| |
| loop:
| |
| move.l dosbase,a6
| |
| move.l locksav,d1
| |
| move.l #fileinfo,d2
| |
| jsr exnext(a6)
| |
| tst.l d0
| |
| beq error
| |
|
| |
| move.l #fileinfo+8,d0
| |
| bsr pmsg
| |
| bsr pcrlf
| |
| bra loop
| |
|
| |
| error:
| |
| move.l dosbase,a6
| |
| jsr ioerr(a6)
| |
| move.l d0,d6
| |
|
| |
| move.l #presskey,d0
| |
| bsr pmsg
| |
| bsr getch
| |
| move.l #-1,d7 ;flag
| |
|
| |
| qu:
| |
| move.l conhandle,d1 ;window close
| |
| move.l dosbase,a6
| |
| jsr close(a6)
| |
|
| |
| move.l dosbase,a1 ;dos.lib close
| |
| move.l exbase,a6
| |
| jsr closelib(a6)
| |
|
| |
| EXIT_AMIGA ;AssemPro only
| |
|
| |
| openfile: ;open file
| |
| move.l a1,d1 ;pointer to I/O-Definition-
| |
| ;Text
| |
| move.l d0,d2
| |
| move.l dosbase,a6
| |
| jsr open(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| pmsg: ;print message (D0)
| |
| movem.l d0-d7/a0-a6,-(sp)
| |
| move.l d0,a0
| |
| move.l a0,d2
| |
| clr.l d3
| |
|
| |
| mess1:
| |
| tst.b (a0)+
| |
| beq mess2
| |
| addq.l #1,d3
| |
| bra mess1
| |
|
| |
| mess2:
| |
| move.l conhandle,d1
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6
| |
| rts
| |
|
| |
| pcrlf:
| |
| move #10,d0
| |
| bsr pchar
| |
| move #13,d0
| |
|
| |
| pchar: ;character in D0 output
| |
| movem.l d0-d7/a0-a6,-(sp) ;save all
| |
| move.l conhandle,d1
| |
|
| |
| pch1:
| |
| lea chbuff,a1
| |
| move.b d0,(a1)
| |
| move.l a1,d2
| |
| move.l #1,d3
| |
| move.l dosbase,a6
| |
| jsr write(a6)
| |
| movem.l (sp)+,d0-d7/a0-a6 ;restore all
| |
| rts
| |
|
| |
| scankey: ;test key
| |
| move.l conhandle,d1
| |
| move.l #500,d2 ;wait value
| |
| move.l dosbase,a6
| |
| jsr waitforch(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| readin: ;input from keyboard
| |
| movem.l d0-d7/a0-a6,-(sp) ;registers
| |
| lea inline+2,a2 ;pointer to input buffer
| |
| clr.l (a2)
| |
|
| |
| inplop:
| |
| bsr getchr
| |
| cmp.b #8,d0
| |
| beq backspace
| |
| cmp.b #127,d0 ;delete?
| |
| beq backspace
| |
| bsr pchar ;character output
| |
| cmp.b #13,d0
| |
| beq inputx
| |
| move.b d0,(a2)+
| |
| bra inplop
| |
|
| |
| input:
| |
| clr.b (a2)+
| |
| sub.l #inline,a2
| |
| move a2,inline ;length in inline+1
| |
| movem.l (sp)+,d0-d7/a0-a6 ;registers
| |
| rts
| |
|
| |
| backspace:
| |
| cmp.l #inline,a2 ;at beginning?
| |
| beq inplop ;yes
| |
| move.b #8,d0
| |
| bsr pchar ;backspace
| |
| move #32,d0
| |
| bsr pchar ;blank
| |
| move #8,d0
| |
| bsr pchar ;backspace
| |
| clr.b (a2)
| |
| subq.l #1,a2
| |
| bra inplop
| |
|
| |
| getchr: ;get 1 character from keyboard
| |
| move.l #1,d3 ;1 character
| |
| move.l conhandle,d1
| |
| lea inbuff,a1 ;buffer-address
| |
| move.l a1,d2
| |
| move.l dosbase,a6
| |
| jsr read(a6)
| |
| clr.l d0
| |
| move.b inbuff,d0
| |
| rts
| |
|
| |
|
| |
| mytext: dc.b 'Directory of Diskette: DFO:',10,13,10,13,0,0
| |
| dosname: dc.b 'dos.library',0,0
| |
| presskey: dc.b 'Press thr Return key!!',0
| |
| align.w
| |
| dosbase: dc.l 0
| |
| consolname: dc.b 'CON:0/100/640/100/** Directory-Test **',0
| |
| name: dc.b 'DFO:',0
| |
| align.w
| |
| locksav: dc.l 0
| |
| fileinfo: ds.l 20
| |
| conhandle: dc.l 0
| |
| inbuff: DS.B 8
| |
| inline: DS.B 180
| |
| chbuff DS.B 82
| |
|
| |
| end
| |
|
| |
| The FileInfoBlock contains the following entries:
| |
|
| |
| Offset Name Meaning
| |
| ----------------------------------------------------------------
| |
| 0 DiskKey.L Disk Number
| |
| 4 DieEntryType.L Entry Type (+=Directory,-=File)
| |
| 8 FileName 108 bytes with the filename
| |
| 116 Protection.L File Protected?
| |
| 120 EntryType.L Entry type
| |
| 124 Size.L Length of file in bytes
| |
| 128 NumBlocks.L Number of blocks
| |
| 132 Days.L Creation day
| |
| 136 Minute.L Creation time
| |
| 140 Tick.L Creation time
| |
| 144 Comment 116 bytes with comments
| |
|
| |
| If you want to have the program output the file length as well,you
| |
| can read the length with "move.l fileinfo+124,d0"and then use a
| |
| conversion routine to produce a decimal number.You can output this
| |
| result with the name.
| |
|
| |
| 6.5.7.Direct Access To Disk.
| |
| ----------------------------
| |
| There isn't a simple function in the library for accessing single
| |
| disk sectors.Here,you must work with a device just like you did
| |
| with speech output.This time you'll be working with the trackdisk.
| |
| device.
| |
| You want to work with this device to directly program the disk
| |
| drives.Once you've built up the necessary program machinery,you
| |
| can experiment with various commands for disk access.Remember that
| |
| an error can cause the disk to be modified and thus unusable.Make
| |
| sure you're using a non-essential disk.Don't use one which
| |
| contains your only copy of something.
| |
| The initialization here is similar to that for speech output.Here
| |
| is the initialization routine for your program:
| |
|
| |
| ;** Direct disk access via trackdisk.device ** (6.5.7)
| |
|
| |
| openlib =-408
| |
| closelib =-414
| |
| execbase = 4
| |
| open =-30
| |
| close =-36
| |
| opendevice =-444
| |
| closedev =-450
| |
| sendIo =-462
| |
| read =-30-12
| |
| write =-30-18
| |
| waitforch =-30-174
| |
| mode_old = 1005
| |
|
| |
| run:
| |
| bsr init ;initialization
| |
| bra test ;system-test
| |
|
| |
| init: ;initialize and open system
| |
| move.l execbase,a6 ;pointer to exec.library
| |
| lea dosname,a1
| |
| moveq #0,d0
| |
| jsr openlib(a6) ;open dos.library
| |
| move.l d0,dosbase
| |
| beq error
| |
| lea diskio,a1 ;pointer to disk I/O area
| |
| move.l #diskrep,14(a1) ;pointer to port
| |
| clr.l d0 ;drive 0 (built in)
| |
| clr.l d1 ;no flags
| |
| lea trddevice,a0 ;pointer to device name
| |
| jsr opendevice(a6) ;open trackdisk.device
| |
| tst.l d0 ;error?
| |
| bne error ;yes!
| |
| move.l #consolname(pc),d1 ;console definition
| |
| move.l #mode_old,d2 ;old mode
| |
| move.l dosbase,a6 ;dos base address
| |
| jsr open(a6) ;open window
| |
| tst.l d0 ;error?
| |
| beq error ;yes!
| |
| move.l d0,conhandle ;else save handle
| |
| rts ;done
| |
|
| |
| test: ;place for test routine
| |
|
| |
|
| |
| And now for the functions that take care of the various messages
| |
| at the end of the program.
| |
|
| |
|
| |
| error:
| |
| move.l #-1,d7 ;flag for error (for SEKA)
| |
|
| |
| qu:
| |
| move.l execbase,a6 ;exec base address
| |
| lea diskio,a1 ;pointer to disk I/O
| |
| move.l 32(a1),d7 ;IO_ACTUAL in D7 (for testing)
| |
| move #9,28(a1) ;command motor on/off
| |
| move.l #0,36(a1) ;0=off,1=on,so turn motor
| |
| jsr sendio(a6) ;off
| |
| move.l conhandle,d1 ;close window
| |
| move.l dosbase,a6
| |
| jsr close(a6)
| |
| move.l dosbase,d1 ;close dos.lib
| |
| move.l execbase,a6
| |
| jsr closelib(a6)
| |
| lea diskio,a1
| |
| jsr closedev(a6) ;close trackdisk.device
| |
| rts
| |
|
| |
|
| |
| Lets not forget the routine that waits for the user to press
| |
| <Return>,so that you can watch the effects of the test function in
| |
| peace:
| |
|
| |
| getchr: ;get a character from keyboard
| |
| move.l #1,d3 ;1 character
| |
| move.l conhandle,d1 ;window handle
| |
| move.l #inbuff,d2 ;buffer address
| |
| move.l dosbase,a6 ;dos base address
| |
| jsr read(a6) ;read character
| |
| rts ;thats it
| |
|
| |
|
| |
| The last thing you need is the section of code that declares the
| |
| text and data fields that your program needs:
| |
|
| |
| dosname: dc.b 'dos.library',0
| |
| align
| |
| consolname: dc.b 'RAW:0/100/640/50/** Wait window',0
| |
| align
| |
| trddevice: dc.b 'trackdisk.device',0
| |
| align
| |
| dosbase: dc.l 0 ;dos base address
| |
| conhandle: dc.l 0 ;window handle
| |
| inbuff: blk.b 80,0 ;keyboard buffer
| |
| diskio: blk.l 20,0 ;I/O structure
| |
| diskrep: blk.l 8,0 ;I/O port
| |
| diskbuff: blk.b 512*2,0 ;place for 2 sectors
| |
|
| |
| There,now you've done with the set up work.Lets look at how you
| |
| can give commands to the disk drives.The first and easiest command
| |
| is the one for turning the drive motor on and off.You've already
| |
| seen this command in the program.This is command number nine.This
| |
| number goes in the command word of the I/O structure (bytes 28 and
| |
| 29 of the structure).
| |
| You need to pass a parameter that lets the computor know whether
| |
| to turn the motor off or on.This information goes in the I/O long
| |
| word that starts at byte 36:its zero for off,and one for on.
| |
| You already chose the motor that should be turned on or off when
| |
| you opened the device.You put the number of the chosen disk drive
| |
| in D0-in your case you put a zero there because you are using the
| |
| DFO:disk drive.
| |
| Heres an overview of the commands you can use to access
| |
| information on the disk:
| |
|
| |
| No Name Function
| |
| -----------------------------------------------------------------
| |
| 2 READ Read one or more sectors
| |
| 3 WRITE Write sectors
| |
| 4 UPDATE Update the track buffer
| |
| 5 CLEAR Erase track buffer
| |
| 9 MOTOR Turn motor on/off
| |
| 10 SEEK Search for a track
| |
| 11 FORMAT Format tracks
| |
| 12 REMOVE Initialize routine that is called when you
| |
| remove the disk
| |
| 13 CHANGENUM Find out number of disk changes
| |
| 14 CHANGESTATE Test if disk is in drive
| |
| 15 PROTSTATUS Test if disk is write protected
| |
|
| |
| You've already learned about command number nine.Lets look at the
| |
| three commands you can use to make tests.These are the last three
| |
| commands.They put a return value in the long word that begins in
| |
| the 32nd byte in the I/O structure.This value was written in D7 in
| |
| the program above for testing purposes.You can read its contents
| |
| directly if you ran the program with AssemPro.
| |
| Here is a simple routine that you can use to run one of these
| |
| commands with:
| |
|
| |
| test: ; (6.5.7B)
| |
| lea diskio,a1 ;pointer to I/O structure
| |
| move #13,28(a1) ;pass command (for example 13)
| |
| move.l execbase,a6 ;execbase address in A6
| |
| jsr sendio(a6) ;call function
| |
|
| |
| If CHANGENUM (command 13) is executed,in D7 you'll get the number
| |
| of times a disk was taken out and put in the drive.If you call the
| |
| program,you'll get a value back.If you take the disk out and put
| |
| it back in,the number is two higher the next time you call the
| |
| program.
| |
| The CHANGESTATE command (command 14) tells whether a disk is in
| |
| the drive or not.If one is,a zero comes back.Otherwise,a $FF is
| |
| returned.
| |
| You get the same values back from the PROTSTATUS function (command
| |
| 15).Here a zero means that the disk isn't write protected,while
| |
| $FF means that it is.
| |
| Now lets look at the READ and WRITE functions.These operations
| |
| need a few more parameters than the status functions.You need to
| |
| pass the following parameters:
| |
|
| |
| The address of the I/O buffer in the data pointer,the number of
| |
| bytes to be transfered in I/O length,and the data address on the
| |
| disk in I/O offset.
| |
|
| |
| The number of data bytes must be a multiple of 512,since every
| |
| sector is 512 bytes,and only whole sectors can be read.
| |
|
| |
| The data address is the number of the first byte in the sector.If
| |
| you want to use the first sector,the offset is zero.For the second
| |
| sector,its 512,etc...The formula is:
| |
|
| |
| offset = (sector_number -1) *512
| |
|
| |
| Here is a routine that loads the first two sectors of the disk
| |
| into the buffer:
| |
|
| |
| test: (6.5.7C)
| |
| lea diskio,a1
| |
| move #2,28(a1) ;command:READ
| |
| move.l #diskbuff,40(a1) ;buffer
| |
| move.l #2*512,36(a1) ;length:2 sectors
| |
| move.l #0*512,44(a1) ;offset:0 sectors
| |
| move.l execbase,a6 ;exec base address
| |
| jsr sendio(a6) ;start function
| |
|
| |
| Start the program from the debugger and then look at the buffers
| |
| contents after the program ends.You can find out the format of the
| |
| disk here.If you want to read a sector thats being used,change the
| |
| 0 in the offset definition to 700 and start again.Its highly
| |
| probable that theres some data there.
| |
| To modify and write back the data that you've read from the disk,
| |
| you need command number three,the WRITE command.The parameters are
| |
| the same.
| |
| If you've executed the WRITE commandyou're probably wondering why
| |
| the disk light did'nt go on.Thats because the Amiga writes a track
| |
| that as been read into a buffer on its own.It WRITE's data there
| |
| as well.It won't write the data to disk until another track is
| |
| accessed.
| |
| You can have the data updated directly as well using command four,
| |
| the UPDATE command.
| |
| Command 11,the FORMAT command,is also quite interesting.This
| |
| command needs a data field that is 11*512=5632 bytes long-the
| |
| length of a track.The offset must be a multiple of this number so
| |
| that you start at the beginning of a track.
| |
| The length must be a multiple of 5632 as a result.If several
| |
| tracks are formatted,each track is filled with the same data.
| |
| You can use this function to easy write a disk copy program.You
| |
| READ the source disk and then FORMAT the corresponding track on
| |
| the destination disk.Thats how the DiskCopy program works-it
| |
| reformats the destination disk.
| |
| Command ten,the SEEK command,just needs the offset.It moves the
| |
| Read/Write head of the drive to the position specified without
| |
| making a disk access or testing if its at the right position.
| |
| Command 12,the REMOVE command,is used to install an interrupt
| |
| routine that is called when the disk is removed from the disk
| |
| drive.The address of the interrupt structure is passed in the data
| |
| pointer of the I/O structure.If theres a zero here,the interrupt
| |
| routine is turned off.
| |
|
| |
| Heres a complete example program in AssemPro format:
| |
|
| |
| ;***** Track disk-Basic function 10/86 S.D. *****
| |
|
| |
| ILABEL ASSEMPRO:includes/Amiga.l :AssemPro only
| |
|
| |
| openlib =-30-378
| |
| closelib =-414
| |
| ;execbase = 4 ;defined in INIT_AMIGA
| |
|
| |
| * calls to amiga dos:
| |
|
| |
| open =-30
| |
| close =-30-6
| |
| opendevice =-444
| |
| closedev =-450
| |
| sendIo =-462
| |
| read =-30-12
| |
| write =-30-18
| |
| waitforch =-30-174
| |
| mode_old = 1005
| |
|
| |
| INIT_AMIGA ;AssemPro only
| |
|
| |
| run:
| |
| bsr init ;initialization
| |
| bra test ;system test
| |
|
| |
| init: ;system initialization and
| |
| ;open
| |
| move.l execbase,a6 ;pointer to exec-library
| |
| lea dosname,a1
| |
| moveq #0,d0
| |
| jsr openlib(a6) ;open dos-library
| |
| move.l d0,dosbase
| |
| beq error
| |
|
| |
| lea diskio,a1
| |
| move.l #diskrep,14(a1)
| |
| clr.l d0
| |
| clr.l d1
| |
| lea trddevice,a0
| |
| jsr opendevice(a6) ;open trackdisk.device
| |
| tst.l d0
| |
| bne error
| |
|
| |
| bp:
| |
| lea consolname(pc),a1 ;console-definition
| |
| move.l #mode_old,d0
| |
| bsr openfile ;console open
| |
| beq error
| |
| move.l d0,conhandle
| |
|
| |
| rts
| |
|
| |
| test:
| |
| bsr accdisk
| |
|
| |
| bsr getchr ;wait for character
| |
| bra qu
| |
|
| |
| error:
| |
| move.l #-1,d7 ;flag
| |
|
| |
| qu:
| |
| move.l execbase,a6
| |
| lea diskio,a1
| |
| move #9,28(a1)
| |
| move.l #0,36(a1)
| |
| jsr sendio(a6)
| |
|
| |
| move.l conhandle,d1 ;window close
| |
| move.l dosbase,a6
| |
| jsr close(a6)
| |
|
| |
| move.l dosbase,a1 ;dos.lib close
| |
| move.l execbase,a6
| |
| jsr closelib(a6)
| |
|
| |
| lea diskio,a1
| |
| move.l 32(a1),d7
| |
| jsr closedev(a6)
| |
|
| |
| EXIT_AMIGA ;AssemPro only
| |
|
| |
| openfile: ;open file
| |
| move.l a1,d1 ;pointer to the I/O-definition
| |
| ;text
| |
| move.l d0,d2
| |
| move.l dosbase,a6
| |
| jsr open(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| scankey: ;test for key
| |
| move.l conhandle,d1
| |
| move.l #500,d2 ;wait value
| |
| move.l dosbase,a6
| |
| jsr waitforch(a6)
| |
| tst.l d0
| |
| rts
| |
|
| |
| getchr: ;get one character from
| |
| ;keyboard
| |
| move.l #1,d3 ;1 character
| |
| move.l conhandle,d1
| |
| lea inbuff,a1 ;buffer-address
| |
| move.l a1,d2
| |
| move.l dosbase,a6
| |
| jsr read(a6)
| |
| clr.l d0
| |
| move.b inbuff,d0
| |
| rts
| |
|
| |
| accdisk:
| |
| lea diskio,a1
| |
| move #2,28(a1) ;command:READ
| |
| move.l #diskbuff,40(a1) ;buffer
| |
| move.l #2*512,36(a1) ;length:2 sectors
| |
| move.l #20*512,44(a1) ;offset: n sectors
| |
| move.l execbase,a6
| |
| jsr sendio(a6)
| |
| rts
| |
|
| |
| dosname: dc.b 'dos.library',0,0
| |
| align.w
| |
| dosbase: dc.l 0
| |
| consolname: dc.b 'RAW:0/100/640/100/** Test-Window S.D.V0.1',0
| |
| trddevice: dc.b 'trackdisk.device',0
| |
| align.w
| |
| conhandle dc.l 0
| |
| inbuff: ds.b 8
| |
| diskio: ds.l 20,0
| |
| diskrep: ds.l 8,0
| |
| diskbuff: ds.b 512*2,0
| |
|
| |
| end
| |
|
| |
| Chapter 7.
| |
| ----------
| |
| 7.Working With Intuition.
| |
| -------------------------
| |
| Now that you've learned so much about machine language,lets look
| |
| at the special features of the Amiga.Lets look at the operating
| |
| system Intuition that is in charge of windows,screens,the mouse
| |
| and lots of other things.Beforetaking a look at these beautiful
| |
| features,theres some bad news.
| |
| First,though,lets here the good news.Since Intuition has so many
| |
| functions,it allows you to be very creative in programming your
| |
| ideas.The disadvantage is that the flexibility means that you have
| |
| to use a lot of parameters,and that makes for a lot of tedious
| |
| work.
| |
| However,this is no grounds for panic.Once you've built up the
| |
| necessary routines,the programming and experimentation becomes
| |
| increasingly interesting.Before you try out new program variations
| |
| you should save your source code to disk,because Intuition gets
| |
| fairly upset about bad parameters and often responds by crashing
| |
| the system.
| |
| Now lets get to work.To start working with Intuition,you need the
| |
| Intuition library.You can load it with the OpenLibrary function
| |
| from the EXEC library.Heres the subroutine that takes care of
| |
| initialization.
| |
|
| |
| openlib =-408
| |
| execbase = 4
| |
|
| |
| run:
| |
| bsr openint ;load intuition library
| |
| ...
| |
|
| |
| openint: ;*initialize and open system
| |
| move.l execbase,a6 ;exec base address
| |
| lea intname,a1 ;name of intuition library
| |
| jsr openlib(a6) ;open intuition
| |
| move.l d0,intbase ;save intuition base address
| |
| rts
| |
|
| |
| intname: dc.b "intuition.library",0
| |
| align
| |
| intbase: dc.l 0 ;base address of intuition
| |
|
| |
| When your program is finished,you need to close the screens,the
| |
| window and the library.To do this,use the CloseLibrary function
| |
| from the EXEC library.It has an offset of -414.
| |
| Heres the subroutine:
| |
|
| |
| closelibrary =-414
| |
| ...
| |
| closeint: ;*close intuition
| |
| move.l execbase,a6 ;exec base address in A6
| |
| move.l intbase,a1 ;intuition base address in A1
| |
| jsr closelibrary(a6) ;close intuition
| |
| rts ;done
| |
|
| |
| Now that you've got that taken care of,you can finally start
| |
| working with Intuition.
| |
|
| |
| 7.1.Open Screen.
| |
| ----------------
| |
| Intuition is a graphics operating system.For this reason,you'll be
| |
| working with the screen.Its even more interesting to work with
| |
| several screens at the same time.However,you only have one monitor
| |
| on the Amiga.
| |
| You can open as many screens as you like (at least,as long as
| |
| theres some memory available).You can open a window,display menus
| |
| and do I/O's there.The individual screens are fully independant.
| |
| You can work with all of them simultaneously on the monitor.
| |
| You can move individual screens forward and back to your hearts
| |
| content.You can also press the left <Amiga> key and then an "m"to
| |
| return to the workbench screen after getting into the different
| |
| screens.
| |
| You want to begin programming Intuition by setting up a screen.You
| |
| have already loaded the Intuition library,so you can use the Open-
| |
| Screen function.
| |
| Wait a minute!What should the screen look like,where should it go,
| |
| and what form should it have?You need to look at the options for
| |
| the form of the screen you have available.
| |
| The input to the screen is in the form of a table that has 13
| |
| entries.Lets take a look at the parameters that you need for our
| |
| screen.
| |
| You'll start the table with the label "screen_defs"which must be
| |
| at an even address:
| |
|
| |
| align
| |
| screen_defs: ;The screen table begins here
| |
|
| |
| The first bit of information that the screen needs is the position
| |
| and size.Lets have it start in the upper left corner and fill the
| |
| entire screen.You'll use the positions X=0 and Y=0,the width 320
| |
| and the height 200.This means that your screen is the maximum
| |
| size.
| |
|
| |
| x_pos: dc.w 0 ;X-Position
| |
| y_pos: dc.w 0 ;Y-Position
| |
| width: dc.w 320 ;width
| |
| height: dc.w 200 ;height
| |
|
| |
| Next you need to decide which colors should be displayed.That
| |
| depends on the number of bitplanes,on the depth.Lets choose two.
| |
| That means you have 2^2 (4) colours available.Lets choose two,
| |
| since four colours is usually plenty.
| |
|
| |
| depth: dc.w 2 ;number of bitplanes
| |
|
| |
| Next you need to choose the colour of the title line and the
| |
| function symbols.Give the number of the colour register:
| |
|
| |
| detail_pen: dc.b 0 ;colour of text,etc...
| |
|
| |
| Now for the colour of the text background:
| |
|
| |
| block_pen dc.b 1 ;background colour
| |
|
| |
| Make sure that these two inputs fit in a byte.The colours are
| |
| normally the following (if the standard values have'nt been
| |
| changed).You'll notice that the number of colours depends on the
| |
| number of bit maps.
| |
|
| |
| Pen Colour
| |
| ---------------------------------------------------------
| |
| 0 Background (blue)
| |
| 1 White
| |
| for two bit planes
| |
| 2 Black
| |
| 3 Red
| |
| for three bit planes
| |
| 4 Blue
| |
| 5 Violet
| |
| 6 Turquoise
| |
| 7 White
| |
| for four bit planes
| |
| 8 Black
| |
| 9 Red
| |
| 10 Green
| |
| 11 Brown
| |
| 12 Blue
| |
| 13 Blue
| |
| 14 Green
| |
| 15 Green
| |
|
| |
| The next word contains the bits that describe the appearance of
| |
| the screen.The bits are:
| |
|
| |
| Bit Value Name Meaning
| |
| ---------------------------------------------------------------
| |
| 1 2 GENLOCK_VIDEO
| |
| 2 4 INTERLACE Puts the screen in Interlace
| |
| mode.The resolution and thus the
| |
| maximum screen size are doubled.
| |
| 6 $40 PFBA
| |
| 7 $80 EXTRA_HALFBRITE
| |
| 8 $100 GENLOCL_AUDIO
| |
| 10 $400 DBLPF Divides the screen into a border
| |
| and character area.
| |
| 11 $800 HOLDNMODIFY Turns on Hold-and-Modify mode.
| |
| 13 $2000 VP_HIDE
| |
| 14 $4000 SPRITES Allows sprites to be used.
| |
| 15 $8000 MODE_640 Turns on the highest resolution
| |
| graphics for the screen(640x400).
| |
|
| |
| Choose the value two (normal) for your example screen:
| |
|
| |
| view_modes: dc.w 2 ;representation mode
| |
|
| |
| The following word is constructed in such away that each bit as
| |
| its own meaning.Use this to set what sort of screen it is.Choose
| |
| 15 so the screen is a "Custom screen",which allows you all of the
| |
| options.
| |
|
| |
| screen_type: dc.w 15 ;screen type:custom screen
| |
|
| |
| Next theres a pointer to the character set to be used for all
| |
| output to the screen.If you don't want to install your own
| |
| character set,just put a zero here,and the standard character set
| |
| is used.
| |
|
| |
| font: dc.l 0 ;character set:standard
| |
|
| |
| Next theres a pointer to the text thats used as the name of the
| |
| screen.The text ends with a zero,just like window names must.
| |
|
| |
| title: dc.l name ;pointer to title text
| |
|
| |
| Next comes a long word that defines the gadgets.These gadgets
| |
| represent the functions,like "Bring forward",that can be accessed
| |
| via a mouse click in the screen.The long word in this table is a
| |
| pointer to a list which specifies the gadgets.These aren't the
| |
| system gadgets.However,you're only using system gadgets here,so
| |
| put a zero here.
| |
|
| |
| gadgets: dc.l 0 ;no gadgets
| |
|
| |
| Finally theres a long word that you only need if you want to use
| |
| the special bitmap just for your screen.Since this isn't the case,
| |
| just put a zero here.
| |
|
| |
| bitmap: dc.l 0 ;no bitmap
| |
|
| |
| Thats it for the list entries that you need to define the screen.
| |
| You still need the text for the name of the screen.Enter the
| |
| following:
| |
|
| |
| sname: dc.b 'Our Screen',0 ;screen title
| |
|
| |
| Heres a quick overview of the list:
| |
|
| |
| align
| |
| screen_defs: ;*The screen ta
| |
| x_pos: dc.w 0 ;X-position
| |
| y_pos: dc.w 0 ;Y-position
| |
| width: dc.w 320 ;width
| |
| height: dc.w 200 ;height
| |
| depth: dc.w 2 ;number of bitplanes
| |
| detail_pen: dc.b 0 ;colour of the text,etc...
| |
| block_pen: dc.b 1 ;background colour
| |
| view_modes: dc.w 2 ;representation mode
| |
| screen_type: dc.w 15 ;screen type:custom screen
| |
| font: dc.l 0 ;character set:standard
| |
| title: dc.l sname ;pointer to title text
| |
| gadgets: dc.l 0 ;no gadgets
| |
| bitmap: dc.l 0 ;no bit map
| |
| sname: dc.b 'Our Screen',0 ;screen title
| |
|
| |
| Once you've decided on the parameters,its very easy to open the
| |
| screen.You need Intuitions OpenScreen function.Its offset is -198
| |
| and it only needs one parameter,the address of the parameter
| |
| table.The program fragment looks like this:
| |
|
| |
| openscreen =-198
| |
| bsr openint ;open intuition
| |
| bsr scropen ;open screen
| |
| ...
| |
| scropen: ;*open screen
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| lea screen_defs,a0 ;pointer to table
| |
| jsr openscreen(a6) ;and open
| |
| move.l d0,screenhd ;save screen handle
| |
| rts ;return to main program
| |
| ...
| |
| screen_defs: ;table info follows
| |
|
| |
| Now the Amigas Workbench screen is covered by your screen.Now you
| |
| can do what you want with it until the program is done.Afterwards,
| |
| the screen must be closed again,so that you can see the Workbench
| |
| screen again.
| |
| Use the CloseScreen function (offset -66) to do this.The only
| |
| parameter it needs is the pointer to the screen structure you got
| |
| back from the OpenScreen function.
| |
|
| |
| closescreen =-66
| |
| ...
| |
| scrclose: ;* close screen
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l screenhd,a0 ;screen handle in A0
| |
| jsr closescreen(a6) ;clos screen
| |
| rts ;done
| |
|
| |
| The long word that OpenScreen returned to you is a pointer to a
| |
| screen structure that contains all the needed data about the
| |
| screen.Besides the data which was given,there is a pointer in the
| |
| screen area for individual bit planes,etc...
| |
| The form of this structure is fairly complicated and contains some
| |
| data that you can't use.Several of the parameters are interesting,
| |
| however.Heres a selection of usable parameters:
| |
|
| |
| No Name Function
| |
| ------------------------------------------------------------------
| |
| 0 (NextScreen.L) Pointer to next screen.
| |
| 4 (FirstWindow) Pointer to first window structure
| |
| 8 (LeftEdge.W)
| |
| $A (TopEdge.W) Position of screen
| |
| $C (Width.W) Width
| |
| $E (Height.W) Height
| |
| $10 (MouseY.W)
| |
| $12 (MouseX.W) Mouse position in the screen
| |
| $14 (Flags.W) Screen flags
| |
| $16 (Title.L) Pointer to title text
| |
| $1A (DefaultTitle) Pointer to normal title
| |
| $28 (Font.L) Pointer to character set
| |
| $C0 (Plane0.L) Pointer to bitplane 0
| |
| $C4 (Plane1.L) Pointer to bitplane 1
| |
| $C8 (Plane2.L) Pointer to bitplane 2
| |
| $CC (Plane3.L) Pointer to bitplane 3
| |
|
| |
| An example of an application for the plane pointer is writing and
| |
| using your own character routine.Next you want to move the address
| |
| of the plane into an address register as follows:
| |
|
| |
| move.l screenhd,a5 ;screen pointer in A5
| |
| move.l $c0(a5),a5 ;bitplane 0-pointer in A5
| |
|
| |
| If you want to try this,do the following:
| |
|
| |
| move.l screenhd,a5 ;screen pointer in A5
| |
| move.l $c0(a5),a5 ;bitplane 0-pointer in A5
| |
| move #$20,d0 ;Counter D0=$20
| |
|
| |
| lop1:
| |
| move d0,(a5) ;write counter bits in picture
| |
| add.l #80,a5 ;address+80,next line
| |
| dbra d0,lop1 ;continue until D0 < 0
| |
|
| |
| This program draws a white,square pattern that corresponds to the
| |
| bit pattern for the numbers $20 to 0.This isn't a particularly
| |
| useful program,but it shows how easy it is to write from a machine
| |
| language program directly to the screen.If you change the offset
| |
| in the second line to $C4,the pattern is read.
| |
| You can move the entire screen with the normal technique of moving
| |
| the mouse pointer into the upper border and moving it up and down
| |
| with the left mouse key depressed.You can do the same with a
| |
| program.
| |
| Lets move the screen without the mouse.Use the joystick for
| |
| demonstration purposes.Put the joystick in port two.As you saw in
| |
| the chapter on the hardware register,you can read memory location
| |
| $DFF00C to find information about the joystick.You can find the
| |
| direction the screen should be moved here.
| |
| Moving the screen requires another Intuition function.You use the
| |
| MoveScreen function which as an offset of -162 and needs three
| |
| parameters to do this.The parameters are:
| |
|
| |
| In A0 the pointer to the screen structure that you got back in
| |
| D0 when you opened the screen.(You saved it in "screenhd")
| |
| In D1 the desired movement in the Y-direction,the vertical
| |
| direction.
| |
| In D0 the horizontal movement in the X-direction.The varient
| |
| doesn't work so you can only move the screen vertically.
| |
|
| |
| Insert the following lines in your program:
| |
|
| |
| MoveScreen =-162
| |
| ...
| |
| scrmove: ;*move screen D0 to the right
| |
| ;and D1 down
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l screenhd,a0 ;screen handle in A0
| |
| clr.l d0 ;no horizontal movement
| |
| jsr movescreen(a6) ;move screen
| |
| rts ;done
| |
|
| |
| Now your looking at a complete program that goes through the
| |
| following steps:
| |
|
| |
| 1. Opens the Intuition library
| |
| 2. Opens the screen
| |
| 3. Moves the screen in the direction specified by the joystick in
| |
| port two
| |
| 4. Closes the screen when the fire button is hit
| |
| 5. Closes the Intuition library
| |
| 6. Ends
| |
|
| |
| Here is the complete program including the subroutines,so you'll
| |
| have it all in one spot:
| |
|
| |
| ;** Demo program to open and move a screen **
| |
|
| |
| movescreen =-162
| |
| openscreen =-198
| |
| closescreen =-66
| |
| closelibrary =-414
| |
| openlib =-408 ;open library
| |
| execbase = 4 ;exec base address
| |
| joy2 =$dff00c ;joystick 2 data
| |
| fire =$bfe001 ;firebutton 2:bit 7
| |
|
| |
| run:
| |
| bsr openint ;open intuition
| |
| bsr scropen ;open screen
| |
| move joy2,d6 ;save joystick info
| |
|
| |
| loop:
| |
| tst.b fire ;test fire button
| |
| bpl ende ;pressed down:done
| |
| move joy2,d0 ;basic info in D0
| |
| sub d6,d0 ;subtract new data
| |
| cmp #$0100,d0 ;up?
| |
| bne noup ;no
| |
| move.l #-1,d1 ;dy=-1 direction y
| |
| bsr scrmove ;move up
| |
| bra loop
| |
|
| |
| noup:
| |
| cmp #$0001,d0 ;down?
| |
| bne loop ;no
| |
| move.l #1,d1 ;dy=1
| |
| bsr scrmove ;move down
| |
| bra loop
| |
|
| |
| ende:
| |
| bsr scrclose ;close screen
| |
| bsr closeint ;close intuition
| |
| rts ;done!
| |
|
| |
| openint: ;*initialize and open system
| |
| move.l execbase,a6 ;exec base address
| |
| lea intname,a1 ;name of intuition library
| |
| jsr openlib(a6) ;open intuition
| |
| move.l d0,intbase ;save intuition base address
| |
| rts
| |
|
| |
| closeint: ;*close intuition
| |
| move.l execbase,a6 ;exec base address in A6
| |
| move.l intbase,a1 ;intuition base address in A1
| |
| jsr closelibrary(a6) ;close intuition
| |
| rts ;done
| |
|
| |
| scropen: ;*open screen
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| lea screen_defs,a0 ;pointer to table
| |
| jsr openscreen(a6) ;open
| |
| move.l d0,screenhd ;save screen handle
| |
| rts ;return to main program
| |
|
| |
| scrclose: ;*close screen
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l screenhd,a0 ;screen handle in A0
| |
| jsr closescreen(a6) ;close screen
| |
| rts ;done
| |
|
| |
| scrmove: ;move screen D0 right/D1 down
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l screenhd,a0 ;screen handle in A0
| |
| clr.l d0 ;no horizontal movement
| |
| jsr movescreen(a6) ;and move
| |
| rts ;done
| |
| align
| |
|
| |
| screen_defs: ;*screen table begins here
| |
| x_pos: dc.w 0 ;X-position
| |
| y_pos: dc.w 0 ;Y-position
| |
| width: dc.w 320 ;width
| |
| height: dc.w 200 ;height
| |
| depth: dc.w 2 ;number of bitplanes
| |
| detail_pen: dc.b 1 ;Text colour=white
| |
| block_pen: dc.b 3 ;background colour=red
| |
| view_modes: dc.w 2 ;representation mode
| |
| screen_type dc.w 15 ;screen type:custom screen
| |
| font: dc.l 0 ;standard character set
| |
| title: dc.l sname ;pointer to title text
| |
| gadgets: dc.l 0 ;no gadgets
| |
| bitmap: dc.l 0 ;no bit map
| |
| intbase: dc.l 0 ;base address of intuition
| |
| screenhd: dc.l 0 ;screen handle
| |
| intname: dc.b 'intuition.library',0
| |
| align
| |
| sname: dc.b 'Our Screen',0 ;Screen title
| |
| align
| |
| end
| |
|
| |
| From this example,you can see how easy scrolling actually is.
| |
| Another easy thing to do is to use the DisplayBeep function.It as
| |
| an offset -96;the only parameter it needs is the screen pointer
| |
| that you stored in the "screenhd"memory block.This function covers
| |
| the screen with an orange colour for a short while.The screen is
| |
| not changed.The beep function can be used as follows:
| |
|
| |
| DisplayBeep: =-96
| |
| ...
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l screenhd,a0 ;screen pointer in A0
| |
| jsr displaybeep(a6) ;light up screen
| |
|
| |
| If you put a zero instead of a screen pointer in A0,the whole
| |
| screen blinks.
| |
| Good,now you have your own screen that you can move up and down.
| |
| What good is it if you can't put anything on it?Lets open a window
| |
| on the screen!
| |
|
| |
| 7.2.Open Window.
| |
| ----------------
| |
| As you saw in the chapter on program initialization,its easy to
| |
| open a window with the DOS library.You can't use this method on
| |
| your own screen however.You need to use another method that can
| |
| open any window on any screen.
| |
| Intuition has a function called OpenWindow which handles this sort
| |
| of work.It has an offset of -204 and needs only one parameter,a
| |
| pointer to a window definition table.This pointer goes in register
| |
| A0.
| |
| This table is very similar to the one used to define the screen.
| |
| The first four values specify the X-and Y-positions,the width,and
| |
| the height of the window to be opened.Heres an example:
| |
|
| |
| align
| |
| window_defs:
| |
| dc.w 10 ;x-position
| |
| dc.w 20 ;y-position
| |
| dc.w 300 ;width
| |
| dc.w 150 ;height
| |
|
| |
| Next come two bytes that define the colour of the letters on the
| |
| background:
| |
|
| |
| dc.b 1 ;white letter colour
| |
| dc.b 3 ;on a red background
| |
|
| |
| The next long word contains the IDCMP flags in its bits.The bits
| |
| determine the circumstances under which Intuition sends a message
| |
| to the program.The bits have the following meanings:
| |
|
| |
| Bit Value Name Meaning
| |
| -----------------------------------------------------------------
| |
| 0 $000001 SIZEVERIFY
| |
| 1 $000002 NEWSIZE Window size changed
| |
| 2 $000004 REFRESHWINDOW
| |
| 3 $000008 MOUSEBUTTONS Mouse key hit
| |
| 4 $000010 MOUSEMOVE Mouse moved
| |
| 5 $000020 GADGETDOWN A special gadget chosen
| |
| 6 $000040 GADGETUP Same as above
| |
| 7 $000080 REQSET
| |
| 8 $000100 MENUPICK A menu item chosen
| |
| 9 $000200 CLOSEWINDOW A window closed
| |
| 10 $000400 RAWKEY A key pressed
| |
| 11 $000800 REQVERIFY
| |
| 12 $001000 REQCLEAR
| |
| 13 $002000 MENUVERIFY
| |
| 14 $004000 NEWPREFS Preferences modified
| |
| 15 $008000 DISKINSERTED A disk put in
| |
| 16 $010000 DISKREMOVED A disk taken out
| |
| 17 $020000 WBENCHMESSAGE
| |
| 18 $040000 ACTIVEWINDOW A window activated
| |
| 19 $080000 INACTIVEWINDOW A window deactivated
| |
| 20 $100000 DELTAMOVE Report relative mouse movement
| |
|
| |
| If you want your first window to respond only by clicking on the
| |
| close symbol,write the following:
| |
|
| |
| dc.l $200 ;IDCMP flags:CLOSEWINDOW
| |
|
| |
| Next comes a long word whose bits determine the windows type.You
| |
| can use this to construct a window to your exact specifications.
| |
| This is quite different from windows opened with the DOS function.
| |
| The bits mean:
| |
|
| |
| Bit Value Name Meaning
| |
| ------------------------------------------------------------------
| |
| 0 $0000001 WINDOWSIZING Window size is changeable
| |
| 1 $0000002 WINDOWDRAG Window is moveable
| |
| 2 $0000004 WINDOWDEPTH Window covering is posible
| |
| 3 $0000008 WINDOWCLOSE Window close symbol
| |
| 4 $0000010 SIZEBRIGHT
| |
| 5 $0000020 SIZEBOTTOM
| |
| 6 $0000040 SIMPLE_REFRESH New drawing manuel
| |
| 7 $0000080 SUPER_BITMAP Save the windows contents
| |
| 8 $0000100 BACKDROP Move window back
| |
| 9 $0000200 REPORTMOUSE Report mouse co-ordinates
| |
| 10 $0000400 GIMMEZEROZERO
| |
| 11 $0000800 BORDERLESS Window without border
| |
| 12 $0001000 ACTIVATE Window active
| |
| 13 $0002000 WINDOWACTIVATE
| |
| 14 $0004000 INREQUEST
| |
| 15 $0008000 MENUSTATE
| |
| 16 $0010000 RMBTRAP Right mouse key:no menu
| |
| 17 $0020000 NOCAREREFRESH No refresh message
| |
| 24 $1000000 WINDOWREFRESH
| |
| 25 $2000000 WBENCHWINDOW
| |
|
| |
| To refresh is to rebuild the window contents when necessary,for
| |
| instance when the windows size is changed.If none of the refresh
| |
| bits are set,you're in Smart-Refresh-Mode.In this case,Intuition
| |
| takes care of refreshing the window.This is the easiest method.
| |
| If you choose the value $100F as the type for your example window,
| |
| the window is active once its opened,and it has all the system
| |
| gadgets:
| |
|
| |
| dc.l $100F ;ACTIVATE and all gadgets
| |
|
| |
| The next long word in the list allows you to use your own gadgets
| |
| in the window.This long word is a pointer to the structure of a
| |
| your gadget.Since you don't want this,just put a zero here.
| |
|
| |
| dc.l 0 ;first gadget:no gadgets of our own
| |
|
| |
| The next long word is a pointer to a graphics structure so you can
| |
| design your own symbol for checking menu points.Put a zero here.
| |
| You'll use the standard sign:
| |
|
| |
| dc.l windowname ;pointer to window name
| |
|
| |
| The next long word is a pointer to the screen structure that you
| |
| got back after calling the OpenScreen function.The easiest way to
| |
| do this is to save the pointer to this location in the buffer:
| |
|
| |
| screenhd: dc.l 0 ;screen pointer
| |
|
| |
| The next long word is a pointer to a bit map if you want one of
| |
| your own for the window.Since you don't want one,put a zero here.
| |
|
| |
| dc.l 0 ;no bit map of our own
| |
|
| |
| Next come four values that set the maximum and minimum width and
| |
| height of the window:
| |
|
| |
| dc.w 150 ;smallest width
| |
| dc.w 50 ;smallest height
| |
| dc.w 320 ;maximum width
| |
| dc.w 200 ;maximum height
| |
|
| |
| The last value in the list is the screen type of the screen the
| |
| window is located in.Put a 15 here.You're using our screen as a
| |
| custom screen:
| |
|
| |
| dc.w 15 ;screen type:custom screen
| |
|
| |
| Heres a quick overview of the whole list:
| |
|
| |
| align
| |
| window_prefs:
| |
| dc.w 10 ;X-position
| |
| dc.w 20 ;Y-position
| |
| dc.w 300 ;width
| |
| dc.w 150 ;height
| |
| dc.b 1 ;white print colour
| |
| dc.b 3 ;on red background
| |
| dc.l $200 ;IDCMP flags:CLOSEWINDOW
| |
| dc.l $100f ;ACTIVATE and all gadgets
| |
| dc.l 0 ;first gadget:no gadgets of
| |
| ;our own
| |
| dc.l 0 ;checkmark:standard
| |
| dc.l windowname ;pointer to window name
| |
| screenhd: dc.l 0 ;screen pointer
| |
| dc.l 0 ;no bitmap of our own
| |
| dc.w 150 ;smallest width
| |
| dc.w 50 ;smallest height
| |
| dc.w 320 ;maximum width
| |
| dc.w 200 ;maximum height
| |
| dc.w 15 ;screen type:custom screen
| |
|
| |
| ;and here comes the window name:
| |
| windowname: dc.b 'Our Window',0
| |
| align
| |
|
| |
| Insert these lines in the program you listed above.Here are two
| |
| subroutines for opening and closing the window:
| |
|
| |
| openwindow =-204
| |
| closewindow =-72
| |
| ...
| |
| windopen:
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| lea windowdef,a0 ;pointer to window definition
| |
| jsr openwindow(a6) ;open window
| |
| move.l d0,windowhd ;save window handle
| |
| rts
| |
|
| |
| windclose:
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l windowhd,a0 ;window handle
| |
| jsr closewindow(a6) ;close window
| |
| rts
| |
| ...
| |
| windowhd: dc.l 0 ;window handle
| |
|
| |
| Now you can insert a "bsr windowopen"after the "bsr scropen"and a
| |
| "bsr windclose"before the "bsr scrclose"command.Once you've
| |
| started the program,move the window around in the screen.You'll
| |
| find that you can't move the window out of the screen with the
| |
| mouse.
| |
| The window in the example has the close gadget in the upper left
| |
| corner.Normally if you click it,the window is closed.Try clicking
| |
| it.You'll find that nothing happens.
| |
| The display of this and all other gadgets,as well as other events
| |
| must be programmed in,since Intuition doesn't know which action
| |
| causes which event.We'll take a look at how to handle this in the
| |
| next chapter.
| |
|
| |
| 7.3.Requesters.
| |
| ---------------
| |
| If you only have one disk drive,you've certainly seen the Amiga
| |
| message,"Please insert xxx in unit 0",a lot.This window is another
| |
| that has two fields for clicking.This sort of message with a
| |
| choice of options is called a requester.
| |
| You want to take a look at how to program a requester.First,you
| |
| need a window for the requester to appear in.You opened a window
| |
| of this sort in the example program.
| |
| To display a requester,use the Intuition function AutoRequest
| |
| (offset -348).It takes care of drawing and managing the requester.
| |
| This function needs the following parameters:
| |
|
| |
| In A0 The pointer to the window structure that you put in
| |
| "windowhd".
| |
| In A1 A pointer to the text structure that should stand over the
| |
| choice buttons.
| |
| In A2 Same as above for the text of the left button.
| |
| In A3 Same as above for the right button.
| |
| In D0 The IDCMP flag which lets you know what event should go
| |
| with the clicking of the left button.
| |
| In D1 Same as above for the right button.
| |
| In D2 The width of the whole requester.
| |
| In D3 The height of the requester.
| |
|
| |
| Insert the following lines in your program:
| |
|
| |
| autorequest =-348
| |
| ...
| |
| request:
| |
| move.l windowhd,a0 ;pointer to window structure
| |
| lea btext,a1
| |
| lea ltext,a2 ;pointer to text structure
| |
| lea rtext,a3
| |
| move.l #0,d0 ;left activates by clicking
| |
| move.l #0,d1 ;right activates by clicking
| |
| move.l #180,d2 ;width and
| |
| move.l #80,d3 ;height of requester
| |
| move.l intbase,a6 ;intuition base address
| |
| jsr autorequest(a6) ;display requester
| |
| rts
| |
|
| |
| The flags passed in D0 and D1 offer some interesting posibilites.
| |
| The system messages that tells you to enter a particular disk are
| |
| overlooked when the DISKINSERTED flag is similar.Putting a disk in
| |
| brings about the same responce as clicking the "Retry"button.
| |
| Whats new is the use of a text structure.Use three of them.Text
| |
| structures are lists that contain entries for the text that you
| |
| need.
| |
| These lists begin with two bytes that are used to define the
| |
| colour.The first byte is the colour of the text.The second is for
| |
| the background colour.Here this doesn't have any meaning.
| |
|
| |
| btext:
| |
| dc.b 2 ;black text colour
| |
| dc.b 0 ;background colour
| |
|
| |
| The next byte specifies the character mode.A zero means that the
| |
| text is output normally.A four means the text is output inverted.
| |
|
| |
| dc.b 0 ;normal text representation
| |
|
| |
| The next entries are words.For this reason the addresses must be
| |
| even,so you need to either insert another byte or use the "align"
| |
| pseudo-op.The following words are the X-and Y-position of the text
| |
| relative to the upper left corner of the requester.
| |
|
| |
| dc.w 10 ;X-position
| |
| dc.w 5 ;Y-position relative to upper
| |
| ;left corner
| |
|
| |
| Next,theres a pointer to the character set that is used.Put a zero
| |
| here to use the standard set.
| |
|
| |
| dc.l 0 ;standard character set
| |
|
| |
| Next you need to give the address of the text that should be
| |
| output.This text must be closed with a null byte.
| |
|
| |
| dc.l text ;pointer to text
| |
|
| |
| You need a long word at the end of the list that is either a
| |
| pointer to another text or a zero if no more text is needed.
| |
|
| |
| dc.l 0 ;no more text
| |
|
| |
| Here are the three text structures that you need for the example:
| |
|
| |
| btext: ;text structure for the title
| |
| dc.b 0,1 ;colour
| |
| dc.b 0 ;mode
| |
| align
| |
| dc.w 10,10 ;text position
| |
| dc.l 0 ;standard font
| |
| dc.l bodytxt ;pointer to text
| |
| dc.l 0 ;no more text
| |
|
| |
| bodytxt:
| |
| dc.b "Requester Text",0
| |
| align
| |
| ltext: ;text structure of left button
| |
| dc.b 0,1 ;colour
| |
| dc.b 0 ;mode
| |
| align
| |
| dc.w 5,3 ;text position
| |
| dc.l 0 ;standard font
| |
| dc.l lefttext ;pointer to text
| |
| dc.l 0 ;no more text
| |
|
| |
| lefttext:
| |
| dc.b "left",0
| |
| align
| |
|
| |
| rtext:
| |
| dc.b 0,1 ;colour
| |
| dc.b 0 ;mode
| |
| align
| |
| dc.w 5,3 ;text position
| |
| dc.l 0 ;standard font
| |
| dc.l righttext ;pointer to text
| |
| dc.l 0 ;no more text
| |
|
| |
| righttext:
| |
| dc.b "right",0
| |
| align
| |
|
| |
| After calling the requester,D0 contains the information about
| |
| which of the buttons were pressed,and in which button the event
| |
| took place.If D0 is zero,it was the right button.If it is one,it
| |
| was the left button.
| |
|
| |
| 7.4.Event Handling.
| |
| -------------------
| |
| Pretend you've opened a window that as a close symbol,and you want
| |
| the program to react to this symbol being clicked.You need a
| |
| signal from Intuition that lets you know that an event as taken
| |
| place.The signal is called a message.
| |
| The IDCMP flag of the window specifies which events should cause
| |
| Intuition to send a message.By setting the bits for WINDOWCLOSE,
| |
| you can allow a message to be sent when the close symbol is
| |
| clicked.
| |
| To get the message,you can use the EXEC function GetMsg (offset
| |
| -372).It needs the source address of the event as a parameter.Here
| |
| the source is the User port (which doesn't have anything to do
| |
| with the User port on old Commodore computors).
| |
| The User port contains a table which has entrieswhich specify the
| |
| events that have taken place and related things like mouse
| |
| position and time.
| |
| How do you find the User port?Use the pointer to the window
| |
| structure that you got back from the OpenWindow function and
| |
| stored in the "windowhd"memory block.
| |
| This pointer points to the window structure of this window.This
| |
| structure consists of a number of entries.Some are copies of the
| |
| parameters from our window definition table.We won't cover all the
| |
| entries,because most won't be interesting to you.You're more
| |
| interested in the pointer to the User port.Its in the window
| |
| structure.
| |
| You can find this in the long word that begins in the 86th byte of
| |
| the structure.You can get this long word with the following lines
| |
| of code:
| |
|
| |
| move.l windowhd,a0 ;pointer to structure in A0
| |
| move.l 86(a0),a0 ;user port pointer in A0
| |
|
| |
| You can call the GetMsg function with this pointer in A0 by using
| |
| the following lines of code in your program:
| |
|
| |
| GetMsg = -372
| |
| ...
| |
| move.l windowhd,a0 ;pointer to structure in A0
| |
| move.l 86(a0),a0 ;user port pointer in A0
| |
| move.l execbase,a6 ;exec base address in A6
| |
| jsr getmsg(a6) ;get message
| |
|
| |
| This function returns a value in the D0 register.This value is a
| |
| pointer to another structure,the Intuition Message Structure.If
| |
| theres a zero in D0,no event as taken place.
| |
| The long word that starts at the 20th byte in this structure
| |
| contains the information about which event took place.Evaluating
| |
| the information is easy,since the bits of this long word have the
| |
| same meaning as the IDCMP flag that you described when you looked
| |
| at opening windows.
| |
| Put the lines above after "loop"and then insert the following:
| |
|
| |
| move.l d0,a0 ;message pointer in A0
| |
| move.l 20(a0),d6 ;save event in D6
| |
| tst.l d0 ;did the event take place?
| |
| bne end ;yes!
| |
|
| |
| Now you can end this program by clicking the close symbol.This way
| |
| you can find out if an event as taken place.You can use D6 to
| |
| determine what event took place.In the example,D6 contains the
| |
| number $00000200,which means that the close symbol was clicked.
| |
| To see if this works with other events,change the $200 IDCMP flag
| |
| to $10200 in the window definition table.When you've assembled and
| |
| started this version,take the disk out of the drive-the program
| |
| terminates.
| |
| The IDCMP flags that you've got now cause the clicking of the
| |
| close symbol and the taking out of the disk (DISKREMOVED) to be
| |
| reported.If you want to find out which of the events took place,
| |
| you can look in D6.It has a $200 in it if the window is closed,a
| |
| $10000 if the disk was removed.
| |
|
| |
| 7.5.Menu Programming.
| |
| ---------------------
| |
| Now lets look at one of Intuitions more interesting capabillities:
| |
| menu programming.By using menus,you can make your programs very
| |
| user friendly.
| |
| There are a lot of ways for you to use menus.You can make menu
| |
| points unusable,output sub-menus,choose the type of menu entries
| |
| (allow text or pictures to be output),etc..To have lots of options
| |
| you need some parameters.
| |
| Lets produce a menu with the SetMenuStrip function (offset -264)
| |
| of Intuition.The function only needs two parameters,a pointer to
| |
| the menu structure of the window to be drawn and a pointer to the
| |
| window structure of the window in which the menu is to function.
| |
| Each window can have its own menu that is active when the window
| |
| is activated.
| |
| Heres the subroutine to set up the menu:
| |
|
| |
| SetMenuStrip =-264
| |
| ...
| |
| setmenu: ;* Initialize a menu
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l windowhd,a0 ;pointer to window structure
| |
| lea menu,a1 ;pointer to menu structure
| |
| jsr setmenustrip(a6) ;call function
| |
| rts
| |
|
| |
| Heres a routine to erase the menu:
| |
|
| |
| ClearMenuStrip =-54
| |
| ...
| |
| clearmenu:
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l windowhd,a0 ;pointer to window structure
| |
| jsr clearmenustrip(a6)
| |
| rts
| |
|
| |
| You've already got the pointer to the window structure.Lets look
| |
| at the menu structure you need for the menu.You need to build a
| |
| structure like this for each menu--for each menu title that
| |
| appears when you press the right mouse key.
| |
| This structure is a table with the following form:
| |
|
| |
| First there is a long word that points to the menu structure of
| |
| the next menu.If the current menu is the last one,a zero goes
| |
| here.
| |
|
| |
| align
| |
| menu:
| |
| dc.l menu1 ;pointer to the next menu
| |
|
| |
| Next come two words which contain tha X- and Y-position of the
| |
| menu title:
| |
|
| |
| dc.w 20 ;X-position
| |
| dc.w 0 ;Y-position
| |
|
| |
| Next,use two words to store the menu titles width and height in
| |
| pixels:
| |
|
| |
| dc.w 50 ;width
| |
| dc.w 10 ;height of menu title
| |
|
| |
| The next word contains the flag bit that determines whether the
| |
| menu is available or not.An unavailable menu either as grey
| |
| entries or they are drawn weakly.If the flag bit,bit 0,is set the
| |
| menu is available.Otherwise,it is not.
| |
|
| |
| dc.w 1 ;menu available
| |
|
| |
| Now comes a long word which functions as a pointer to the text
| |
| which is used as the menu title.Make sure the length isn't larger
| |
| than the width entry allows!Otherwise unpleasent things will
| |
| happen.
| |
|
| |
| dc.l menutext ;pointer to title text
| |
|
| |
| Next comes a long word which functions as a pointer to the
| |
| structure of the first menu entry of this menu.Each menu entry
| |
| needs its own structure.
| |
|
| |
| dc.l menuitem01 ;pointer to the first menu item
| |
|
| |
| The last entries in the table are four words that are reserved for
| |
| internal functions.They must be here.
| |
|
| |
| dc.w 0,0,0,0 ;reserved words
| |
|
| |
| Thats the structure of the first menu.This structures first long
| |
| word points to the next structure which has the same form.The
| |
| pointer is set to zero in the last menu.
| |
| You still need the structure of the menu entries.These structure
| |
| tables have the following form:
| |
|
| |
| They start with a pointer to the next menu item.This pointer is
| |
| set to zero for the last entry.
| |
|
| |
| align
| |
| menuitem01:
| |
| dc.l menuitem02 ;pointer to next menu item
| |
|
| |
| Next comes the four words:the X- and Y-position,the width and the
| |
| height of the box the menu entry goes in.The size becomes obvious
| |
| when the item is chosen by having the right mouse key clicked on
| |
| it.Then the box becomes visible.As you can see,the next word is
| |
| determined in the flags.First lets set the position and size of
| |
| the menu point,though:
| |
|
| |
| dc.w 0 ;X-position of entry
| |
| dc.w 0 ;Y-position
| |
| dc.w 90 ;width in pixels
| |
| dc.w 10 ;height in pixels
| |
|
| |
| The position entries are relative to the upper left corner of the
| |
| menu that is pulled down.
| |
| The following word was described above:it contains flags for
| |
| entries to this menu item.There are several interesting variations
| |
| possible.The following flag bits are contained in this word:
| |
|
| |
| Bit Value Name Meaning When Set
| |
| ------------------------------------------------------------------
| |
| 0 $0001 CHECKIT Point is checked when chosen
| |
| 1 $0002 ITEMTEXT Text menu item
| |
| 2 $0004 COMMSEQ Choice can be made with keys as well
| |
| 3 $0008 MENUTOGGLE Check turned on and off
| |
| 4 $0010 ITEMENABLED Menu item available
| |
| 6 $0040 HIGHCOMP Item inverted when chosen
| |
| 7 $0080 HIGHBOX Iten framed when chosen
| |
| 8 $0100 CHECKED Item is checked
| |
|
| |
| Heres a description of the bits:
| |
|
| |
| Name Description
| |
| ------------------------------------------------------------------
| |
| CHECKIT If this bit is set,a check or a user-defined
| |
| drawing is put in front of the text when the item
| |
| is chosen.The text should begin with two blanks.
| |
|
| |
| ITEMTEXT The menu item is a normal text if this bit is set.
| |
| Otherwise a drawing is output.
| |
|
| |
| COMMSEQ By setting this bit and entering a character,this
| |
| menu point can be chosen by pressing the right
| |
| <Amiga> key and the key that was input.The input
| |
| character is then displayed in the menu with the
| |
| Amiga symbol.There needs to be space available for
| |
| this.
| |
|
| |
| MENUTOGGLE If this bit is set and checking is allowed (bit 0),
| |
| the second time this point is chosen the check is
| |
| erased,the next time it is displayed again,etc...
| |
|
| |
| ITEMENABLED Erasing this bit makes the menu item available.
| |
|
| |
| HIGHCOMP If this bit is set,the box you've defined is
| |
| inverted when this menu item is chosen by the mouse
| |
| pointer.
| |
|
| |
| HIGHBOX In this mode,the box is framed whin its chosen.
| |
|
| |
| The two previous bits determine the mode of the chosen menu item.
| |
| The following combinations are possible:
| |
|
| |
| HIGHIMAGE If both bits are cleared,choosing the bit causes a
| |
| self-defined drawing to be output.
| |
|
| |
| HIGHNONE When both bits are set,there isn't any reaction to
| |
| choosing this item.
| |
|
| |
| CHECKED This bit can be set by either the program or
| |
| Intuition.It lets you know if the menu text has a
| |
| check next to it or not.You can use this to find
| |
| out if the item was checked by testing but eight.If
| |
| its set,the item was checked.You can also use it to
| |
| cause the item to be checked.
| |
|
| |
| You're choosing the mode CHECKIT,ITEMTEXT,COMMSEQ,MENUTOGGLE,ITEM-
| |
| ENABLED and HIGHBOX for the example:
| |
|
| |
| dc.w $10011111 ;mode flag
| |
|
| |
| Lets get back to the structure of the menu items.After the flag
| |
| word,there is a long word whose flag bits determine whether this
| |
| menu point can be turn off another one.Set this to zero:
| |
|
| |
| dc.l 0 ;no connection
| |
|
| |
| Now comes the pointer to the structure of the text that should be
| |
| displayed.If the ITEMTEXT bit isn't set,this pointer must point to
| |
| the structure of the drawing.If nothing should be shown,you can
| |
| set this to zero.Use a text in the example and write the
| |
| following:
| |
|
| |
| dc.l menu01text ;pointer to menu text structure
| |
|
| |
| The following long word only has a meaning if the HIGHIMAGE flag
| |
| is set.Then this long word points to the text or the drawing that
| |
| should be displayed when the menu items box is clicked.Otherwise
| |
| the long word is ignored,so insert a zero:
| |
|
| |
| dc.l 0 ;no drawing when clicked
| |
|
| |
| The next entry is a byte that is used for input of keyboard
| |
| characters,which together with the right <Amiga> key can be used
| |
| to choose the menu item.This only works if the COMMSEQ bit is set.
| |
| Place a character here:
| |
|
| |
| dc.b 'A' ;choose item using <Amiga>/'A'
| |
|
| |
| Since the next item is a long word,you need an "align"peudo-op
| |
| here.Next comes the long word that points to the menu item
| |
| structure or a submenu.The submenu is automatically shown when
| |
| this menu item is clicked.You can't nest them any deeper,however,
| |
| so this long word is ignored for submenus.
| |
| If you don't want a submenu to this item,put a zero here:
| |
|
| |
| align
| |
| dc.l 0 ;no submenu
| |
|
| |
| The next and final long word is written to by Intuition if you
| |
| choose several menu itens.In this case,the menu number of the next
| |
| menu item chosen goes here:
| |
|
| |
| dc.l 0 ;preparation
| |
|
| |
| Thats the structure for a menu item.You still need the text
| |
| structure for the text of the item.This isn't complicated,but it
| |
| makes you get into fine details about the form of the menu.You've
| |
| already learned about this text structure when you looked at
| |
| requesters,so we'll skip an explanation.
| |
| Heres the complete structure of an example menu.You can use two
| |
| menus,each with two subpoints.The second menu point of the left
| |
| menu has a submenu with two entries.You ought to type this program
| |
| in,so that you can experiment with it.You can also use this
| |
| example to evaluate the clicked menu item.
| |
|
| |
| ;**Complete menu structure foe example menu **
| |
| menu:
| |
| dc.l menu1 ;no next menu
| |
| dc.w 10,30 ;X/Y
| |
| dc.w 50,10 ;width/height
| |
| dc.w 1 ;menu enabled
| |
| dc.l menuname ;menu title
| |
| dc.l menuitem01 ;menu entry
| |
| menuname:
| |
| dc.b "Menu 1",0 ;first menu name
| |
| align
| |
| menu1:
| |
| dc.l 0 ;no further menu
| |
| dc.w 80,0 ;see above
| |
| dc.w 50,10
| |
| dc.w 1
| |
| dc.l menuname1
| |
| dc.l menuitem11
| |
| dc.w 0,0,0,0
| |
| menuname1:
| |
| dc.b "Menu 2",0 ;second menu name
| |
| align
| |
| menuitem01: ;first menu item
| |
| dc.l menuitem02 ;pointer to next entry
| |
| dc.w 0,0 ;X/Y
| |
| dc.w 130,12 ;width/height
| |
| dc.w $9f ;flags
| |
| dc.l 0 ;exclude
| |
| dc.l text01 ;pointer to text structure
| |
| dc.l 0 ;select fill
| |
| dc.b "1" ;command
| |
| align
| |
| dc.l 0 ;subitem:none
| |
| dc.w 0 ;next select:no
| |
| text01:
| |
| dc.b 0,1 ;colours
| |
| dc.b 0 ;mode:overwrite
| |
| align
| |
| dc.w 5,3 ;X/Y position
| |
| dc.l 0 ;standard character set
| |
| dc.l text01txt ;pointer to text
| |
| dc.l 0 ;no more text
| |
| text01txt:
| |
| dc.b "Point 0.1",0
| |
| align
| |
| menuitem02: ;second menu item
| |
| dc.l 0
| |
| dc.w 0,10
| |
| dc.w 130,12
| |
| dc.w $57
| |
| dc.l 0
| |
| dc.l text02
| |
| dc.l 0
| |
| dc.b "2" ;activate with <Amiga>/'2'
| |
| align
| |
| dc.l 0
| |
| dc.w 0
| |
| text02:
| |
| dc.b 0,1
| |
| dc.b 0
| |
| align
| |
| dc.w 5,3
| |
| dc.l 0
| |
| dc.l text02txt
| |
| dc.l 0
| |
| text02txt:
| |
| dc.b "Point 0.2",0
| |
| align
| |
| menuitem11: ;first menu point of the second menu
| |
| dc.l menuitem12 ;pointer to second menu point
| |
| dc.w 0,0
| |
| dc.w 90,12
| |
| dc.w $52
| |
| dc.l 0
| |
| dc.l text11
| |
| dc.l 0
| |
| dc.b 0
| |
| align
| |
| dc.l 0
| |
| dc.w 0
| |
| text11:
| |
| dc.b 0,1
| |
| dc.b 0
| |
| align
| |
| dc.w 5,3
| |
| dc.l 0
| |
| dc.l text11txt
| |
| dc.l 0
| |
| text11txt:
| |
| dc.b "Point 1.1",0
| |
| align
| |
| menuitem12: ;second menu item of second menu
| |
| dc.l 0 ;no more items
| |
| dc.w 0,10
| |
| dc.w 90,12
| |
| dc.w $92
| |
| dc.l 0
| |
| dc.l text12
| |
| dc.l 0
| |
| dc.b 0
| |
| align
| |
| dc.l submenu0 ;pointer to submenu
| |
| dc.w 0
| |
| text12:
| |
| dc.b 0,1
| |
| dc.b 0
| |
| align
| |
| dc.w 5,3
| |
| dc.l 0
| |
| dc.l text12txt
| |
| dc.l 0
| |
| text12txt:
| |
| dc.b "Point 1.2",0
| |
| align
| |
| submenu0: ;first point of submenu
| |
| dc.l submenu1 ;pointer to next point
| |
| dc.w 80,5
| |
| dc.w 90,12
| |
| dc.w $52
| |
| dc.l 0
| |
| dc.l texts0
| |
| dc.l 0
| |
| dc.b 0
| |
| align
| |
| dc.l 0
| |
| dc.w 0
| |
| texts0:
| |
| dc.b 0,1
| |
| dc.b 0
| |
| align
| |
| dc.w 5,3
| |
| dc.l 0,texts0txt,0
| |
| texts0txt:
| |
| dc.b "S Point 1",0
| |
| align
| |
| submenu1: ;submenu,second item
| |
| dc.l 0
| |
| dc.w 80,15
| |
| dc.w 90,12
| |
| dc.w $52
| |
| dc.l 0
| |
| dc.l texts1
| |
| dc.l 0
| |
| dc.b 0
| |
| align
| |
| dc.l 0
| |
| dc.w 0
| |
| texts1:
| |
| dc.b 0,1
| |
| dc.b 0
| |
| align
| |
| dc.w 5,3
| |
| dc.l 0
| |
| dc.l texts1txt
| |
| dc.l 0
| |
| texts1txt:
| |
| dc.b "S Point 2",0
| |
| align
| |
|
| |
| The menu items in this example have the following properties as a
| |
| result of their flags:
| |
|
| |
| Menu 1;
| |
| The first item,"Point 0.1",can be chosen using the right <Amiga>
| |
| key and the "1" key.This point alternates between checked and not
| |
| checked,which can easily be used to check out the key function.If
| |
| the item is checked and you hit both keys,the check disappears and
| |
| vice versa.The box at this point is framed when the mouse pointer
| |
| clicks on it.
| |
| The second item,"Point 0.2",can be chosen using the right <Amiga>
| |
| key and the "2"key.This item is checked the first time it is
| |
| chosen.However,in contrast to the item above,it can't be erased.
| |
| The box of this item is inverted when clicked.
| |
|
| |
| Menu 2;
| |
| These two points can't be chosen using keys.The box of the upper
| |
| item is inverted when clicked on:the lower one is framed.When you
| |
| click the second item,"Point 1.2",a submenu with two entries is
| |
| displayed.
| |
|
| |
| Experiment with this structure a little bit.Change some values and
| |
| see what happens.As you can see,menu programming isn't as bad as
| |
| you thought,and it offers a lot of options (but you'll have to do
| |
| lots of typing!).
| |
| When you've done experimenting,you'll want to produce your own
| |
| program with menus.How does the program find whether a menu item
| |
| in a menu has been clicked on?
| |
| You already looked at one way to find out the menus state.You can
| |
| test the CHECKED bit in the flag word of a menu item.If this is
| |
| set,the user clicked on this item with the mouse.
| |
| This only works if checking is allowed for the item being tested.
| |
| You could allow all the menu items to be checked,but this still
| |
| isn't a good solution--it requires testing all the flag bits of
| |
| all the menus one after the other.That makes very boring
| |
| programming.
| |
| You've already learned about finding about events from Intuition.
| |
| You've moved the message about which event took place into D6,and
| |
| you can look at it to find out what happend.
| |
| If you set the eight bit,the MENUPICK bit,of the IDCMP flag long
| |
| word in the window definition,the choice of the menu point is
| |
| reported.Put the following lines in your loop in the main program.
| |
|
| |
| loop:
| |
| move.l execbase,a6 ;exec base address in A6
| |
| move.l windowhd,a0 ;window structure pointer
| |
| move.l 86(a0),a0 ;user point pointer in A0
| |
| jsr getmsg(a6) ;get message
| |
| tst,l d0 ;whay happend?
| |
| beq loop ;nothing happend
| |
| move.l d0,a0 ;message pointer in A0
| |
| move.l $14(a0),d6 ;event in D6
| |
|
| |
| If the program makes it out of the loop,an event as taken place.
| |
| You have the events flag in the D6 register.You can evaluate the
| |
| event using CMP or BTST to find out which flag bits are set.You
| |
| can then execute the function corresponding to the set bit.You can
| |
| use lines like the following ones:
| |
|
| |
| cmp #$200,d6 ;WINDOWCLOSE?
| |
| beq ende ;yes:program end
| |
|
| |
| These lines terminate the program when the window is closed.
| |
|
| |
| If the user chose a menu item,there is a $100 in the D6 register.
| |
| You now need to determine which item it was.
| |
| You can find this information in a word that comes right after the
| |
| long word with the event flags in the message structure.Write:
| |
|
| |
| move $18(a0),d7
| |
|
| |
| You now have the code for the clicked menu item in the D7
| |
| register.If the user just pressed the right key and let it go
| |
| without choosing a menu item,you'll find a $FFFF here.This word
| |
| doesn't contain just one,but three pieces of information:
| |
|
| |
| Which menu was the item chosen from?
| |
| Which menu item?
| |
| Which submenu?
| |
|
| |
| The information is divided in three bit groups.The division is as
| |
| follows:
| |
|
| |
| Bits 0-4 Menu title number
| |
| Bits 5-10 Menu item number
| |
| Bits 11-15 Submenu item number
| |
|
| |
| The numbering begins with zero-ie the first menu point of the
| |
| first menu has the numbers 0 and 0.
| |
|
| |
| To try this out insert the following lines:
| |
|
| |
| move d7,d6 ;move code into D6
| |
| lsr #8,d7 ;shift right 11 times
| |
| lsr #3,d7 ;submenu item now in D7
| |
| clr.l d5
| |
| roxr #1,d6 ;bit 0 in X-flag
| |
| roxl #1,d5 ;menu number now in D5
| |
| and.l #$7f,d6 ;issolate lower bits
| |
| cmp #$7f,d6 ;no menu item?
| |
| beq loop ;no:continue
| |
| lsr #4,d6 ;else menu item in D6
| |
| ende
| |
|
| |
| By making a test run with AssemPro,you can easily see if this
| |
| works right-just look at the registers after the program is over.
| |
|
| |
| If you,for example,want to write a program with four menus with 10
| |
| menu items each,this sort of method is too much work-there are 44
| |
| tables.For this reason,lets look at a short program that takes
| |
| care of the necessary structure table itself.
| |
| The menu structure is built very simply-it doesn't offer submenus
| |
| or the option of choosing items via the keyboard.If you want these
| |
| extras,you can still use this program,but you'll have to use MOVE
| |
| commands to insert the desired flags and pointers.
| |
| The input that this program needs is a list of the menu names and
| |
| the items in each menu.The addresses of the menu texts go in a
| |
| table with the following simple form:
| |
|
| |
| dc.l Menu title 1
| |
| dc.l Point1,Point2,Point3,...,0
| |
| dc.l Menu title 2
| |
| dc.l Point1,Point2,Point3,...,0
| |
| dc.l Menu title 3 oder 0
| |
|
| |
| This program is set up in such a way that up to four menus can lie
| |
| next to each other (in normal screen resolution),which is often
| |
| plenty.The table above ends by putting a zero instead of a pointer
| |
| to the nxt menu title.As you can see,its pretty simple.
| |
| This program is inserted in your big program right behind the
| |
| "setmenu"label.After the "bsr setmenu"command is executed,the menu
| |
| structure is built and initialized at the same time.You don't need
| |
| to change the rest of the program,it'll be shorter that way.
| |
|
| |
| Heres the program fragment for the complete "setmenu"routine:
| |
|
| |
| setmenu: ;*initialize menu structure
| |
| lea mentab,a0 ;pointer to text pointer in A0
| |
| lea menu,a1 ;pointer to menu field in A1
| |
| move #10,d1 ;horizontal menu position=10
| |
|
| |
| menuloop:
| |
| clr.l d2 ;vertical menu position=0
| |
| move.l a1,a2 ;save address of pointer
| |
| tst.l (a0) ;another menu there?
| |
| beq setmenu1 ;no:quit
| |
| clr.l (a1)+ ;"no more menus"preperations
| |
| move d1,(a1)+ ;set X-position
| |
| add.l #70,d1 ;and increment
| |
| move.l #50,(a1)+ ;Y-position and width
| |
| move.l #$a0001,(a1)+ ;height and flag
| |
| move.l (a0)+,(a1)+ ;menu title
| |
| lea 12(a1),a3
| |
| move.l a3,(a1)+ ;pointer to menu item
| |
| clr.l (a1)+ ;reserved words
| |
| clr.l (a1)+
| |
|
| |
| itemloop:
| |
| tst.l (a0) ;last entry?
| |
| beq menuend ;yes:menu done
| |
| lea 54(a1),a3
| |
| move.l a3,(a1)+ ;pointer to next item
| |
| move.l d2,(a1)+ ;X- and Y-positions
| |
| add #10,d2 ;Y-position+10
| |
| move.l #$5a000a,(A1)+ ;WIDTH/HEIGHT
| |
| move #$52,(a1)+ ;flag:normal
| |
| clr.l (a1)+ ;no connection
| |
| lea 16(a1),a3
| |
| move.l a3,(a1)+ ;text structure pointer
| |
| clr.l (a1)+ ;no fill structure
| |
| clr.l (a1)+ ;no command,no submenu
| |
| clr.l (a1)+ ;and no continuation
| |
| move #$1,(a1)+ ;set text structure:colour
| |
| clr.l (a1)+ ;mode 0
| |
| move.l #$50003,(a1)+ ;X- and Y-position
| |
| clr.l (a1)+ ;standard character set
| |
| move.l (a0)+,(a1)+ ;text pointer
| |
| clr.l (a1)+ ;no continuation
| |
| bra itemloop ;next item...
| |
|
| |
| menuend: ;eventual transfer to next menu
| |
| clr.l -54(a1) ;erase pointer to next item
| |
| tst.l (a0)+ ;increment table pointer
| |
| tst.l (a0) ;another menu there?
| |
| beq setmenu1 ;no:done
| |
| move.l a1,(a2) ;pointer to next menu
| |
| bra menuloop ;and continue
| |
| setmenu1: ;*initialize menu (like before)
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move,l windowhd,a0 ;window structure in A0
| |
| lea menu,a1 ;pointer to menu structure
| |
| jsr setmenustrip(a6)
| |
| rts
| |
|
| |
| You need three things yet for this program:the memory to be used
| |
| for the structure,the table of text pointers and the text.Heres an
| |
| example:
| |
|
| |
| mentab:
| |
| dc.l menu1 ;first menu title
| |
| dc.l mp11,mp12,mp13 ;menu items
| |
| dc.l 0 ;end of menu 1
| |
| dc.l menu2 ;second menu title
| |
| dc.l mp21,mp22,mp23 ;menu items
| |
| dc.l 0 ;end of menu 2
| |
| dc,l 0 ;you're out of menus!
| |
|
| |
| ;** Menu Text **
| |
| menu1: dc.b "Menu 1",0
| |
| mp11: dc.b "Point11",0
| |
| mp12: dc.b "Point12",0
| |
| mp13: dc.b "Point13",0
| |
| menu2: dc.b "Menu 2",0
| |
| mp21: dc.b "Point21",0
| |
| mp22: dc.b "Point22",0
| |
| mp23: dc.b "Point23",0
| |
| align
| |
| ;** Storage space for menu structure **
| |
| menu: blk.w 500
| |
|
| |
| Make sure that the memory area reserved for the menu structure is
| |
| big enough and change the entry "blk.w 500"to the calculated
| |
| value.
| |
| If you use this program,and want to build some special features
| |
| into the menu (for instance key commands),you can make entries in
| |
| the menu structure table while the program is running.You can find
| |
| the word (or byte or long word) that interests you in the table as
| |
| follows:
| |
|
| |
| For example,to find the keyboard command byte of the second entry
| |
| in the first menu,calculate as follows:
| |
|
| |
| Address = Start_address+Menu*30+(entry-1)*54+26
| |
|
| |
| which in the example comes to:
| |
|
| |
| Address = menu+30+54+26
| |
| = menu+110
| |
|
| |
| The 26 is the distance from the beginning of the MenuItem
| |
| structure to the desired byte,the command byte.In this way,you can
| |
| calculate the addresses and use MOVE commands to modify the menu
| |
| to fit your wishes.By the way,in the example above,the correspond-
| |
| ing flag bit must be set as well,so that the keyboard command is
| |
| recognized.
| |
| Now lets get back to the window.Its nice to have a window that you
| |
| can change and close,but you really want to be able to output text
| |
| in a window!
| |
|
| |
| 7.6.Text Output.
| |
| ----------------
| |
| Its very easy to use Intuition's text output function.Use the
| |
| PrintIText function (offset -216).It needs four parameters.
| |
|
| |
| In A0 A pointer to the RastPort of the window.You can find this
| |
| in the window structure.
| |
| In A1 A pointer to the text structure of the text that should
| |
| be output.
| |
| In D0 The X-position.
| |
| In D1 The Y-position of the text in the window.
| |
|
| |
| Its very easy to enter the X- and Y-positions.You've already used
| |
| the text structure twice (for requesters and menus).
| |
| Whats new is accessing the windows RastPort.The RastPort is a
| |
| structure that describes the window.The address is needed by
| |
| several Intuition functions.
| |
| The pointer to the RastPort starts at the 50th byte in the window
| |
| structure.You can access it as follows:
| |
|
| |
| move.l windowhd,a0 ;address of window structure
| |
| move.l 50(a0),a0 ;RastPort address in A0
| |
|
| |
| Now you've got the address of the RastPort.Lets write a routine
| |
| that prints a text.The X- and Y-positions are in D0 and D1
| |
| respectively and the address of the text structure in A1 before
| |
| the routine is called:
| |
|
| |
| PrintIText = -216
| |
| ...
| |
| print:
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l windowhd,a0 ;address of window structure
| |
| move.l 50(a0),a0 ;rastport address in A0
| |
| jsr printitext(a6) ;call function
| |
| rts
| |
|
| |
| You can try out this routine by using the requesters text that is
| |
| still in a structure of the program.Write the following lines
| |
| before the "loop"label:
| |
|
| |
| lea btext,a1 ;pointer to text structure in A1
| |
| move.l #10,d0 ;X-position
| |
| move.l #30,d1 ;Y-position of text
| |
| bsr print ;output text
| |
|
| |
| Start the program and the text appears in the middle of the window
| |
| If this doesn't happen,check the colour of the text in the text
| |
| structure.Its probably zero.Just change it to three,and the text
| |
| appears in red the next time you start the program.
| |
|
| |
| 7.7.Images.
| |
| -----------
| |
| An image is a drawing that goes in a rectangular field and is
| |
| defined bitwise.The disk symbol of the Intuition screen and the
| |
| system gadgets in the screen and window borders are examples of
| |
| such Images.
| |
| The rectangle that the drawing goes in can be arbitrarily large,
| |
| but each pixel in the rectangle needs its own bit,so programming
| |
| screen-sized Images isn't advisable.You'll stick to an Image that
| |
| requires about 32x16 bits-an Image thats about 3x1cm.
| |
| You can make all sorts of images as you've seen looking at window
| |
| gadgets.There is an Intuition functionthat draws an Image:It is
| |
| the DrawImage function (offset -114) and it needs 4 parameters:
| |
|
| |
| In A0 The address of the rastport image is drawn in.You've
| |
| already learned how to access this address in the section
| |
| on the text function.
| |
| In A1 The structure address of the image to be drawn.
| |
| In D0 The relative X-position
| |
| In D1 The relative Y-position of the drawing.
| |
|
| |
| Lets draw this picture in your window.It justs takes a simple
| |
| routine.You just need to put the address of the image structure in
| |
| A1 and the position of the image in D0 and D1 before you call it.
| |
|
| |
| DrawImage =-114
| |
| ...
| |
| draw: ;*draw image
| |
| move.l intbase,a6 ;intuition base address in A6
| |
| move.l windowhd,a0 ;pointer to window structure
| |
| move.l 50(a0),a0 ;now,rastport address in A0
| |
| jsr drawimage(a6) ;draw image
| |
| rts
| |
|
| |
| Now you need the structure of the image.The structure contains
| |
| nine entries which have the following meanings:
| |
|
| |
| The first two entries are words which specify the distance in the
| |
| X- and Y-direction from the co-ordinates that were given to tell
| |
| where the image should be drawn.You'll just put two zeros here:
| |
|
| |
| image:
| |
| dc.w 0,0 ;X- and Y-position
| |
|
| |
| Next come two words which specify the width and height of the
| |
| image in pixels.Lets draw a 32x13 point image.Enter:
| |
|
| |
| dc.w 32,13 ;width and height of image
| |
|
| |
| The next word in the list specifies the number of planes in the
| |
| drawing.If its a simple image that only uses two colours,just
| |
| enter a one.For more colours,you'll need a correspondingly bigger
| |
| number.When more colurs are worked with,the bit pattern of the
| |
| image must have more data.Lets just have one bit plane:
| |
|
| |
| dc.w 1 ;one bitplane:2^1=2 colours
| |
|
| |
| Next comes a long word that points to the data of the image:
| |
|
| |
| dc.l imgdata ;pointer to image data
| |
|
| |
| The next two bytes are very interesting.The first byte,the
| |
| PlanePick byte,tells which plane of the window or screen the image
| |
| data should be written in.Since you only have one plane,you need
| |
| to enter the bit plane of the window.This information is found in
| |
| the bits of the byte-bit0 stands for plane 0,bit 1 for plane 1,etc
| |
| ..You also define the colour of the image with this input.If you
| |
| enter a two,every set bit of your image represents a red point.
| |
|
| |
| dc.b 2 ;drawing red:plane 1
| |
|
| |
| The second byte,the PlaneOnOff byte,is an interesting enhancement.
| |
| Each bit of the window bit plane corresponds to a whole number
| |
| here.The only bytes that are interesting though are the ones that
| |
| are cleared in the PlanePick byte.If the bit is set in PlaneOnOff,
| |
| evert bit of the image in the corresponding plane is set.Otherwise
| |
| they are cleared.To make sure that each bit of the image that
| |
| isn't set appears white,enter a one.All the bits of the image that
| |
| aren't set,are set in Plane 1 and appear white.
| |
|
| |
| dc.b 1 ;background:white
| |
|
| |
| The last entry of the structure is a lobg word that points to
| |
| another image.You don't need this,so set the long word to zero:
| |
|
| |
| dc.l 0 ;no more images
| |
|
| |
| Heres a quick overview of the image structure:
| |
|
| |
| image:
| |
| dc.w 0,0 ;X- and Y-positions
| |
| dc.w 32,13 ;width and height of image
| |
| dc.w 1 ;one bitplane:2^1=2 colours
| |
| dc.l imgdata ;pointer to image data
| |
| dc.b 2 ;drawing red:plane 1
| |
| dc.b 1 ;background:white
| |
| dc.l 0 ;no more images
| |
|
| |
| Now lets produce the image data.Each image row uses a word,long
| |
| word,or several of these represent the pattern.The set points of
| |
| the image correspond to the set bits.This is repeated as often as
| |
| the height of the image requires.The data on each line must begin
| |
| on a word border,on a even address.
| |
| For the example,its easy to decide on the data,since you're going
| |
| 32 points across-that corresponds to exacty one long word.Its
| |
| easiest to program the image using the binary representation of
| |
| the data.
| |
| Lets use,as an example,an image that represents a switch in "OFF"
| |
| mode.This form is chosen for a good reason,so you should type it
| |
| in.In the chapter on gadgets thats coming up,we'll show you how to
| |
| turn the switch on.Here is the example data for the switch image:
| |
|
| |
| imgdata: ;Data for switch in "OFF" mode
| |
| dc.l %00000000000000000000000000000000
| |
| dc.l %00000000000000000000111000000000
| |
| dc.l %00011101110111000001111100000000
| |
| dc.l %00010101000100000001111100000000
| |
| dc.l %00010101100110000001111000000000
| |
| dc.l %00011101000100000011100000000000
| |
| dc.l %00000000000000000111000000000000
| |
| dc.l %00000000000000001110000000000000
| |
| dc.l %00000000000111111111100000000000
| |
| dc.l %00000000001111111111110000000000
| |
| dc.l %00000000001111111111110000000000
| |
| dc.l %00000000000110000001100000000000
| |
| dc.l %00000000000000000000000000000000
| |
|
| |
| Once you've typed in this data,you can experiment with displaying
| |
| it on the screen.Enter the following lines before the "loop"label:
| |
|
| |
| move.l image,a1 ;pointer to image structure
| |
| move #30,d0 ;X-postion in window
| |
| move #50,d1 ;Y-position
| |
| bsr draw ;draw image
| |
|
| |
| How do you like the image on the screen?You'll run into this
| |
| switch again when we talk about putting the switch in the "ON"
| |
| state when discussing gadgets.You need to look at other methods of
| |
| drawing in the window first,though.
| |
|
| |
| 7.8.Borders.
| |
| ------------
| |
| A border is a collection of lines that are connected.They can be
| |
| of any length or at any angle.Intuition lets you draw borders to
| |
| do things like put frames around windows and screens.They are used
| |
| to put borders around pictures or text,especially for use with
| |
| string gadgets.We'll talk about that later,though.
| |
| Its easy to draw borders.Just use the Intuition function
| |
| DrawBorder (offset -108) which needs four parameters:
| |
|
| |
| In A0 The rastport address of the output medium the lines should
| |
| be drawn in.Use your window.
| |
| In A1 The address of the border structure.We'll look at the form
| |
| of this structure shortly.
| |
| In D0 The relative X-co-ordinate which is used with the X- and Y
| |
| co-ordinate list to calulate the actual line co-ordinates.
| |
| In D1 The relative Y-co-ordinates.Relative,here too,means that
| |
| this is relative to the upper left corner of the screen.
| |
|
| |
| Lets write a short routine that is called with three parameters.
| |
| The structure address in A1 and the X and Y co-ordinates are in D0
| |
| and D1 respectively when the routine is called.The border is drawn
| |
| in the window whose structure address is in "windowhd".
| |
|
| |
| DrawBorder =-108
| |
| ...
| |
| borderdraw: ;* draw several lines
| |
| move.l inbase,a6 ;intuition base address in A6
| |
| move.l windowhd,a0 ;pointer to window structure
| |
| move.l 50(a0),a0 ;now rastport address in A0
| |
| jsr drawborder(a6) ;draw lines
| |
| rts
| |
|
| |
| Now lets look at the border structure.The list needs the eight
| |
| following parameters:
| |
|
| |
| First,you need two words for the vertical and horizontal distance
| |
| from the co-ordinates given in the function call.To avoid losing
| |
| sight of some of the many distance entries,put two zeros here:
| |
|
| |
| border:
| |
| dc.w 0 ;horizontal distance
| |
| dc.w 0 ;vertical distance
| |
|
| |
| Next come two bytes that determine the colour.Use a red frame:
| |
|
| |
| dc.b 3 ;red frame
| |
| dc.b 0 ;background (unused)
| |
|
| |
| As you can see,the background colour isn't used.You have two modes
| |
| to choose between for drawing the lines.The following mode
| |
| determines the mode that is used.If it is zero,each line is drawn
| |
| in the colour chosen,no matter what was done before.This is the
| |
| JAM1 mode.The other mode is the XOR mode which ignores both colour
| |
| entries.In this mode,all the points that lie under the line have
| |
| their colour value inverted.As a result,a white point becomes
| |
| black,and a blue one becomes red.That is mode two.Lets use the
| |
| JAM1 mode for the example:
| |
|
| |
| dc.b 0 ;mode:JAM1 (2=XOR)
| |
|
| |
| The next entry specifies how many co-ordinate pairs there are in
| |
| the list.Since this word must be on an even address,you need to
| |
| use the "align"peudo-op first.Then enter the number of pairs.
| |
| Remember that you need three points to draw two lines:beginning,
| |
| corner and end point.To draw a rectangular frame,you need five
| |
| pairs:
| |
|
| |
| dc.b 5 ;5 X,Y pairs used together
| |
|
| |
| The next item is a pointer to the co-ordinate table that contains
| |
| a list of points to be connected:
| |
|
| |
| dc.l coord ;pointer to cordinate table
| |
|
| |
| The border structures final entry is a long word that can point to
| |
| another border structure.If you don't have any more structures to
| |
| be pointed to,just enter a zero here.The pointer is useful for
| |
| connecting two independant border structures-for example,to
| |
| produce a two coloured frame that really stands out.You don't need
| |
| this pointer in the example,though:
| |
|
| |
| dc.l 0 ;no more structures
| |
|
| |
| Thats the border structure.Now lets look at the co-ordinate list.
| |
| For the example,it consists of five pairs of numbers which
| |
| represent a rectangle.I recommend entering these values,because
| |
| you'll use them in example programs further down the line.
| |
|
| |
| coord: ;coordinates for rectangle frame
| |
| dc.w -2,-2
| |
| dc.w 80,-2
| |
| dc.w 80,9
| |
| dc.w -2,9
| |
| dc.w -2,-2
| |
|
| |
| Heres a quick overview of the border structure:
| |
|
| |
| border:
| |
| dc.w 0 ;horizontal distance
| |
| dc.w 0 ;vertical distance
| |
| dc.b 3 ;red frame
| |
| dc.b 0 ;background (unused)
| |
| dc.b 0 ;mode:JAM1 (2=XOR)
| |
| dc.b 5 ;5 X,Y pairs used together
| |
| dc.l coord ;pointer to cordinate table
| |
| dc.l 0 ;no more structures
| |
| coord: ;coordinates for rectangle frame
| |
| dc.w -2,-2
| |
| dc.w 80,-2
| |
| dc.w 80,9
| |
| dc.w -2,9
| |
| dc.w -2,-2
| |
|
| |
| Once you've typed this in,you can try the whole thing out.Type the
| |
| following lines before the "loop"label in the program:
| |
|
| |
| lea border,a1 ;address of the border structure
| |
| move #20,d0 ;X base position
| |
| move #80,d1 ;Y base position
| |
| bsr drawborder ;draw frame
| |
|
| |
| As you can see,using enough X and Y co-ordinates,you can draw the
| |
| Eiffel tower.Thats enough about simple drawings.You want to put
| |
| some life into your drawings and text.Lets manipulate them with
| |
| the mouse!
| |
|
| |
| 7.9.Gadgets.
| |
| ------------
| |
| We already talked a bit about gadgets when you looked at screen
| |
| construction.Looking at system gadgets like the window close
| |
| symbol,you can activate by clicking and causes a program function
| |
| to be executed.
| |
| You can make your own gadgets as well.Intuition allows you a lot
| |
| of interesting possibilities.
| |
|
| |
| There are four types of gadgets:
| |
|
| |
| Boolean gadgets are used in Yes/No situations.You can click and
| |
| activate it (Yes) or deactivate it (No).
| |
|
| |
| String gadgets are used to accept input of text of a specified
| |
| length.
| |
|
| |
| Integer gadgets are a special sort of string gadgets which accept
| |
| the input of a decimal number.Intuition converts the value into a
| |
| long word and sends it to the program.
| |
|
| |
| Proportional gadgets let you choose an analog value with the mouse
| |
| You can move these around with the mouse.
| |
|
| |
| 7.9.1.Boolean Gadgets.
| |
| ----------------------
| |
| Lets start with the simplest type,the boolean gadget.an example of
| |
| this sort of gadget is the close symbol of the window.The only
| |
| status it differenciates between are clicked and not clicked.Lets
| |
| develop a gadget of this type step by step.The flags and other
| |
| parameters are similar for the other gadgets.
| |
| Each gadget needs a structure containing fifteen entries.There is
| |
| a pointer to this structure in window,screen or requester that the
| |
| gadget is to appear in.Theres always a long word available for
| |
| this purpose.Up to this point,you've just put a zero there.If
| |
| there is an address of a gadget structure there,the gadget or
| |
| gadgets are displayed when the window is opened.
| |
|
| |
| A gadget structure as the following entries:
| |
|
| |
| The first long word is a pointer to the next gadget to be
| |
| installed.The gadgets are displayed in a row,like pearls on a
| |
| string.This pointer is the first gadget in this linked list of
| |
| gadgets.If you just want one gadget in your window,put a zero
| |
| here:
| |
|
| |
| gadget1:
| |
| dc.l 0 ;no more gadgets
| |
|
| |
| The next two words determine the position of the gadget in the
| |
| window.There are several ways to determine the position.Use flags
| |
| to access the various possibilities.Lets start with a gadget that
| |
| stays in one spot:
| |
|
| |
| dc.w 40 ;X and
| |
| dc.w 50 ;Y position of the gadget
| |
|
| |
| The next two words determine the size of the gadgets Hit box.This
| |
| box isn't the visible size of the gadget (that depends on the
| |
| image data).It is the size of the rectangle that Intuition should
| |
| watch.If the mouse pointer is moved into this box and the left
| |
| button pressed,the gadget is activated.Clicking on parts of the
| |
| gadget that are outside this box have no effect!
| |
|
| |
| dc.w 32 ;width and
| |
| dc.w 13 ;height of the hit box
| |
|
| |
| Next comes the word whose bits determine the properties of the
| |
| gadget.Bits 0 and 1 determine what should happen when this objects
| |
| hit box is clicked on.The meanings of the various values of these
| |
| bits go as follows:
| |
|
| |
| Bit 0 1 Value Name Meaning
| |
| ------------------------------------------------------------------
| |
| 0 0 0 GADGHCOMP The gadget inverted
| |
| 0 1 1 GADGHBOX The gadget framed
| |
| 1 0 2 GADGHIMAGE Another image appears
| |
| 1 1 3 GADGHNONE No reaction
| |
|
| |
| Bit 2 determines whether the gadget should consist of a drawing or
| |
| a border.If it is set(Value+4),it is treated as an image;otherwise
| |
| its treated like a border.
| |
| The next bit determines if the gadget should appear in the upper
| |
| or lower border of the frame.If it is set(Value+8).the position is
| |
| relative to the lower border;otherwise it is relative to the upper
| |
| border.The next bit as the same meaning for the horizontal
| |
| position.If set(Value+$10),it is a relative positioning.Otherwise,
| |
| it is an absolute positioning.
| |
| Notice that when you define a gadget to be relative,you must have
| |
| a negative value in the position input in the first word of the
| |
| structure.Since the desired position isn't under,but its over this
| |
| position!
| |
| In this way,you can choose either absolute or relative positioning
| |
| of the gadget.An example of a gadget that is positioned absolutely
| |
| is the system gadget,close window.An example of a relative gadget
| |
| is the symbol for changing the size.
| |
| The width and height of the gadgets hit box can also be relative
| |
| to the window size.Specify this by using bit 5 for width (Value +
| |
| $20)and bit 6 for the height (Value +$40).A set bit mens a
| |
| relative size.
| |
| Bit 7 (Value+$80)makes the object active as soon as the window is
| |
| opened.
| |
| Bit 8 (Value+$100)determines whether the gadget can be used or not
| |
| If this bit is set,the gadget can't be activated.
| |
| For the example,you'll use absolute positioning and size,the
| |
| inverted appearance for the activated gadget,and the
| |
| representation of the object as an image.That means you must use
| |
| the value four:
| |
|
| |
| dc.w 4 ;flags:image,invert
| |
|
| |
| Next comes a word whose bits are used as flags.This flag is called
| |
| the Activation Flag.It determines the functions of the gadget.The
| |
| bits,their values and meanings follow:
| |
|
| |
| Bit Value Name Meaning
| |
| ------------------------------------------------------------------
| |
| 0 1 RELVERIFY Causes the gadget to be activated
| |
| only when the left mouse key is let
| |
| loose over the gadget.
| |
| 1 2 GADGIMMEDIATE Lets the gadget be active as soon as
| |
| there is a click.
| |
| 2 4 ENDGADGET Lets you choose to end this choice
| |
| and have it disappear if this is a
| |
| requester gadget.
| |
| 3 8 FOLLOWMOUSE Lets the gadget know the mouse
| |
| position at regular intervals from
| |
| the time it is selected until the
| |
| time it is deselected.You can use
| |
| this to move the gadget with the
| |
| mouse when you want to change the
| |
| gadget position.
| |
| 4 $10 RIGHTBORDER This makes sure thay when borders
| |
| are used that the page is adjusted
| |
| to the size of the gadget so that it
| |
| fits in the border.
| |
| 5 $20 LEFTBORDER
| |
| 6 $40 TOPBORDER
| |
| 7 $80 BOTTOMBORDER
| |
| 8 $100 TOGGLESELECT Allows the objects state to change
| |
| every time it is clicked.If
| |
| activated,it becomes deactivated and
| |
| vice versa.
| |
| 9 $200 STRINGCENTRE For a string gadget,these two bits
| |
| determine whether the string should
| |
| appear centred or right justified.If
| |
| neither is set,the string is output
| |
| left justified.
| |
| 10 $400 STRINGRIGHT
| |
| 11 $800 LONGINT Turns a string gadget into a Integer
| |
| gadget (explanation later).
| |
| 12 $1000 ALTKEYMAP Causes another ketboard placement to
| |
| be in effect for string gadget input
| |
|
| |
| Thats it for the activation flags.Lets choose the TOGGLESELECT and
| |
| GADGETIMMEDIATED flags for example:
| |
|
| |
| dc.w $102 ;activation
| |
|
| |
| The next word of the gadget structure determines the gadget type.
| |
| Heres the meaning of the individual bits:
| |
|
| |
| Bit Value Name Meaning(report what circumstances)
| |
| ----------------------------------------------------------------
| |
| 0 1 BOOLGADGET This is a boolean gadget
| |
| 1 2 GADGET002
| |
| 2 4 STRGADGET String order Integer gadget
| |
| 0+1 3 PROPGADGET Proportional gadget
| |
|
| |
| System gadgets:
| |
| 4 $10 SIZING Size changing gadget
| |
| 5 $20 WDRAGGING Moving gadget for window
| |
| 4+5 $30 SDRAGGING Same for screen
| |
| 6 $40 WUPFRONT Gadget to move window forward
| |
| 6+4 $50 SUPFRONT Gadget to move screen forward
| |
| 6+5 $60 WDOWNBACK Move window back
| |
| 6+5+4 $70 SDOWNBACK Move screen back
| |
| 7 $80 CLOSE Window close gadget
| |
|
| |
| Type definitions:
| |
| 12 $1000 REQGADGET Requester gadget
| |
| 13 $2000 GZZGADGET Border gadget in GIMMEZEROZERO
| |
| window
| |
| 14 $4000 SCRGADGET Screen gadget when set
| |
| 15 $8000 SYSGADGET System gadget when set
| |
|
| |
| You want to use a simple boolean gadget for your example,so enter:
| |
|
| |
| dc.w 1 ;gadget type:boolean
| |
|
| |
| Next comes a pointer to the gadget structure.The first pointer
| |
| contains the address of the image or border structure which should
| |
| be used to represent the gadget.If no representation is needed,put
| |
| a zero here.You want to represent the gadget as an image,so put a
| |
| pointer to the image structure that you produced in the chapter
| |
| about images:
| |
|
| |
| dc.l image ;gadget image
| |
|
| |
| The next pointer is only used if the GADGHIMAGE flag in the flag
| |
| word of the structure is set.This is a pointer to another
| |
| structure that should be put on the screen when the object is
| |
| activated.If a border structure is used for the gadget represent-
| |
| ation,this must be a border structure as well.You won't use a
| |
| second image,so put a zero here:
| |
|
| |
| dc.l 0 ;no new gadget displayed
| |
|
| |
| The next pointer is to the text structure that should be output by
| |
| the gadget.If no text is needed,just put a zero here.You want to
| |
| use some text,however:
| |
|
| |
| dc.l ggtext ;gadget text
| |
|
| |
| Now comes a long word that determines which gadgets are
| |
| deactivated when this is activated.This function still doesn't
| |
| work right so put a zero here:
| |
|
| |
| dc.l 0 ;no exclude
| |
|
| |
| You'll set the next pointer to zero as well,because it is only
| |
| used for String and Proportional gadgets.For these gadgets,this is
| |
| a special structure to describe the characteristics of the gadget.
| |
| Its called SpecialInfo.
| |
|
| |
| dc.l 0 ;no SpecialInfo
| |
|
| |
| The next word contains the Gadget Identification (ID) number:
| |
|
| |
| dc.w 1 ;gadget ID
| |
|
| |
| Finally there is a long word that doesn't have any function,so put
| |
| a zero here:
| |
|
| |
| dc.l 0 ;user data (ignore)
| |
|
| |
| Thats it.Heres a quick overview of the gadget structure:
| |
|
| |
| gadget1:
| |
| dc.l 0 ;no more gadgets
| |
| dc.w 40 ;X and
| |
| dc.w 50 ;Y position of gadget
| |
| dc.w 32 ;width and
| |
| dc.w 13 ;height of hit box
| |
| dc.w 4 ;flags:image,invert
| |
| dc.w $102 ;activation flags
| |
| dc.w 1 ;gadget type:boolean
| |
| dc.l image ;gadget image
| |
| dc.l 0 ;no new gadget displayed
| |
| dc.l ggtext ;gadget text
| |
| dc.l 0 ;no exclude
| |
| dc.l 0 ;no SpecialInfo
| |
| dc.w 1 ;gadget ID
| |
| dc.l 0 ;user data (ignore)
| |
|
| |
| You've already prepared a structure that you can use for this
| |
| image.Now you need the text that appears under the gadget.
| |
| Since the gadget looks like a switch,label it "switch".The text
| |
| structure looks like this:
| |
|
| |
| ggtext:
| |
| dc.b 1,0 ;colours
| |
| dc.b 1 ;mode
| |
| align
| |
| dc.w -8,14 ;X and Y position
| |
| dc.l 0 ;standard font
| |
| dc.l swtext ;pointer to text
| |
| dc.l 0 ;no more text
| |
| swtext:
| |
| dc.b "switch",0
| |
| align
| |
|
| |
| Once you've typed this in,save it,assemble it and start again.You
| |
| can click the switch and cause it to be inverted.Click it again,
| |
| and it appears normal.
| |
| Now you can experriment with the structure.If you change the flag
| |
| from four to five,you can cause the gadget to be framed when it is
| |
| activated.Set the RELVERIFY bit(bit0:+1)in the Activation Flag
| |
| word.Then you can move the mouse pointer onto the object and press
| |
| the button.It is activated.Keep the mouse button pressed down and
| |
| move the mouse.Once you leave the hit box,the activation disapears
| |
| This way,you can avoid accidently activating a gadget.
| |
| Now you want to display the switch in an on state.This is easy.All
| |
| you need to do is produce another image structure,one for the on
| |
| state.You put this pointer in the long word right after the
| |
| pointer to the normal image structure.You can change the flag word
| |
| to six which causes a second image to be displayed when the gadget
| |
| is activated.
| |
| Here is the image structure for the switch in the one state.
| |
|
| |
| image2:
| |
| dc.w 0,0 ;no offset
| |
| dc.w 32,13 ;32x13 pixels
| |
| dc.w 1 ;mode 1
| |
| dc.l imgdata2 ;pointer to the data
| |
| dc.b 2,1 ;same colours as before
| |
| dc.l 0 ;nothing else
| |
|
| |
| imgdata2: ;data for switch in the On state
| |
| dc.l %00000000000000000000000000000000
| |
| dc.l %00000000011100000000000000000000
| |
| dc.l %00000000111110000011101001000000
| |
| dc.l %00000000111110000010101101000000
| |
| dc.l %00000000011110000010101011000000
| |
| dc.l %00000000000111000011101001000000
| |
| dc.l %00000000000011100000000000000000
| |
| dc.l %00000000000001110000000000000000
| |
| dc.l %00000000000111111111100000000000
| |
| dc.l %00000000001111111111110000000000
| |
| dc.l %00000000001111111111110000000000
| |
| dc.l %00000000000110000001100000000000
| |
| dc.l %00000000000000000000000000000000
| |
|
| |
| Now the state of the object can be determined by looking at the
| |
| picture.If the gadget is activated,the switch is on.If not,the
| |
| switch is off.
| |
| Thats it for boolean gadgets.You can learn about the things you
| |
| did'nt touch with some experimentation.You want to get to the
| |
| string gadgets that also do some interesting things.
| |
|
| |
| 7.9.2.String Gadgets.
| |
| ---------------------
| |
| Lets pretend you want a program to load data from the disk.To get
| |
| the user to enter the filename,you need to output text telling the
| |
| user to enter the name.Then you need to call an input routine to
| |
| evaluate the keyboard input.
| |
| Its easier and more elegant to use a String gadget.This function
| |
| allows for easy input and/or editingof short text.You have the
| |
| option of having the text framed.The Undo function can be used by
| |
| pressing the right <Amiga> key and a "Q",and the old contents of
| |
| the gadget,the old text are restored.
| |
| You can also vary the size of the text and the input field.If the
| |
| text is longer than the input field is wide,the text is moved back
| |
| and forth through the visible area when you move the cursor keys
| |
| or the normal input to the border.
| |
| You can also restrict input to just digits.This makes it posible
| |
| to accept numeric input.Intuition even converts the digit string
| |
| into a binary number.This saves the machine language programmer
| |
| some work.A specialized String gadget of this sort is called a
| |
| Integer gadget.
| |
| The structure is similar to the Boolean gadgets structure.There
| |
| are only two major differences:
| |
|
| |
| The type word of the structure must be a four to declare that this
| |
| is a String gadget (STRGADGET).
| |
|
| |
| The pointer to the SpecialInfo structure is needed.Put a pointer
| |
| to the StringInfo structure that you are going to design later
| |
| here.
| |
|
| |
| The width and height entries in the gadget structure have a
| |
| different meaning than they had previously.They do declare the
| |
| area in which you can bring the mouse pointer to activate the
| |
| String gadget.However,it is also used for representation of text.
| |
| These values determine the size of the box in which the text is
| |
| output.You should surround the box with a border using the Border
| |
| function,so that the user can see where it is.
| |
| If the text is longer than the box,only a portion of it is seen on
| |
| the screen.You can move through the area by entering text or using
| |
| the left/right cursor keys to move through the box.The characters
| |
| that are entered are inserted at the cursor position,so the rest
| |
| of the text is shifted by one character when you are on the right
| |
| edge of the input area.The following functions can be used for
| |
| editing this text:
| |
|
| |
| Cursor key left/right
| |
| Moves the cursor over the text thats already on hand.Moves
| |
| the text through the Container.
| |
|
| |
| Cursor keys with <Shift>
| |
| Puts the cursor on the beginning or the end of the text.
| |
|
| |
| <Del> Deletes the character under the cursor.
| |
|
| |
| <Backspace>
| |
| Deletes the character to the left of the cursor.
| |
|
| |
| <Return>
| |
| Ends text input.
| |
|
| |
| <Amiga>right+"Q"
| |
| This is the Undo function.It replaces the text with the
| |
| original contents.
| |
|
| |
| The StringInfo structure only has a few entries:
| |
|
| |
| First theres a pointer to the memory area that is used to store
| |
| the text that is input.The memory buffer must be big enough to
| |
| handle all the text entered.
| |
|
| |
| strinfo:
| |
| dc.l strpuffer ;pointer to text buffer
| |
|
| |
| Next comes the pointer to the Undo buffer.This pointer and this
| |
| buffer are only needed if you want the Undo function.If you do,you
| |
| must have a buffer that is at least as big as your text buffer.
| |
| Every time the string gadget function is called,the text buffers
| |
| contents are copied into this buffer.To get the old contents back,
| |
| just press the right <Amiga>key and the "Q"key.The contents of the
| |
| Undo buffer are copied back to the text buffer.If you use several
| |
| string gadgets in a program,you can use the same Undo buffer for
| |
| all of them,since only one string gadget is used at one time.
| |
|
| |
| dc.l undo ;pointer to undo buffer
| |
|
| |
| The following word contains the cursor position in the text.You
| |
| should set this word to zero,so that the user can see the
| |
| beginning of the text when the string gadget appears.
| |
|
| |
| dc.w 0 ;cursor position
| |
|
| |
| The next word contains the maximum number of characters that can
| |
| be input.If you type one more than this number of characters,the
| |
| screen blinks,to show that you can't enter a longer input string.
| |
| The number of characters and the reserved space for the input
| |
| field don't have to agree,since text can be scrolled by typing.
| |
|
| |
| dc.w 10 ;maximum # of characters
| |
|
| |
| The following word tells at which character of text in the buffer,
| |
| the output to the box should begin.You should put a zero here,so
| |
| that the user can see the beginning of the text.
| |
|
| |
| dc.w 0 ;output text from this character
| |
|
| |
| The next five words are used by Intuition,so you don't have to
| |
| initialize them.Just put zeros here.The words contain the
| |
| following information:
| |
|
| |
| dc.w 0 ;character position in undo buffer
| |
| dc.w 0 ;number of chars in text buffer
| |
| dc.w 0 ;number of chars visible in box
| |
| dc.w 0 ;horizontal box offset
| |
| dc.w 0 ;vertical box offset
| |
|
| |
| The next two long words are initialized by Intuition as well:
| |
|
| |
| dc.l 0 ;pointer to rastport
| |
| dc.l 0 ;long word with value of the input
| |
| ; ;(for integer gadgets)
| |
|
| |
| The final entry is a pointer to the keyboard table that is used if
| |
| the ALTKEYMAP flag of the gadget is set.
| |
|
| |
| dc.l 0 ;standard keyboard table
| |
|
| |
| Heres a quick overview of the StringInfo structure:
| |
|
| |
| strinfo:
| |
| dc.l strpuffer ;pointer to text buffer
| |
| dc.l undo ;pointer to undo buffer
| |
| dc.w 0 ;cursor position
| |
| dc.w 10 ;maximum # of characters
| |
| dc.w 0 ;output text from this character
| |
| dc.w 0 ;character position in undo buffer
| |
| dc.w 0 ;number of chars in text buffer
| |
| dc.w 0 ;number of chars visible in box
| |
| dc.w 0 ;horizontal box offset
| |
| dc.w 0 ;vertical box offset
| |
| dc.l 0 ;pointer to rastport
| |
| dc.l 0 ;long word with value of input
| |
| ; ;(for integer gadgets)
| |
| dc.l 0 ;standard keyboard table
| |
|
| |
| Here are the text and undo buffers:
| |
|
| |
| strpuffer:
| |
| dc.b "Hello!",0,0,0
| |
|
| |
| undo:
| |
| dc.l 0,0,0,0
| |
| align
| |
|
| |
| Once you've entered these lines,you can either alter the old
| |
| gadget structure or build a new one.We'd recommend building
| |
| another gadget structure so that you can have the switch and use
| |
| it later.Change the first pointer in the old structure from zero
| |
| to "gadget1"and insert this new structure.Here is an example
| |
| strucure for the string gadget.It as the following entries:
| |
|
| |
| gadget1: ;*structure for string gadget
| |
| dc.l 0 ;no more gadgets
| |
| dc.w 20,80 ;position
| |
| dc.w 80,10 ;width and height of box
| |
| dc.w 0 ;flags:normal
| |
| dc.w 2 ;activation($802 for long int)
| |
| dc.w 4 ;type:string gadget
| |
| dc.l border ;pointer to border
| |
| dc.l 0 ;no drawing selected
| |
| dc.l 0 ;no text
| |
| dc.l 0 ;no exclude
| |
| dc.l strinfo ;pointer to stringinfo structure
| |
| dc.w 2 ;gadget ID
| |
| dc.l 0 ;no user data
| |
|
| |
| border: ;*border for box frame
| |
| dc.w 0,0 ;no offset
| |
| dc.b 3,3 ;red colour
| |
| dc.b 0 ;mode:JAM1
| |
| dc.b 5 ;5 X,Y pairs
| |
| dc.l coord ;pointer to coordinates table
| |
| dc.l 0 ;no more structures
| |
|
| |
| coord: ;*coordinates for frame
| |
| dc.w -2,-2 ;start in upper left corner
| |
| dc.w 80,-2 ;upper right
| |
| dc.w 80,9 ;lower right
| |
| dc.w -2,9 ;lower left
| |
| dc.w -2,-2 ;back to beginning
| |
|
| |
| This data causes a red rectangle,the border,to appear around the
| |
| "Hello!"text.You can change the text by clicking in the field and
| |
| editing once the cursor appears.If you type something wrong,you
| |
| can use the undo function(the right <Amiga> key and the Q key),to
| |
| get "Hello!"back.
| |
| Once you've done some typing and deactivated the gadget by
| |
| pressing <Return> or by clicking outside the field (cursors
| |
| disapear),you can terminate the program.
| |
| Change the activation flag to $802 and the "strbuffer"to "dc.l
| |
| 0,0,0,0",assemble,and then start the program.You can type in the
| |
| string gadget once it has been activated,but you can only enter
| |
| digits.The screen blinks if you enter letters.
| |
| Enter a number,and then end the program after deactivating the
| |
| gadget.If you look at the stringinfo structure you can look at the
| |
| value of the number you input(in hex)in the eight long word.
| |
| After looking at boolean,text and numeric input to gadgets,lets
| |
| look at Proportional gadgets which allow the user to enter analog
| |
| values by moving a symbol.
| |
|
| |
| 7.9.3.Proportional Gadgets.
| |
| ---------------------------
| |
| You've seen the advantages of slider devices over knobs that you
| |
| turn,maybe on a hifi,maybe on a toaster,but certainly someplace.
| |
| Its easier to tell the state the item is in with a slider,
| |
| especially if several such devices are next to each other(for
| |
| example graphic equalizers).You can represent sliders on the
| |
| Amigas screen and work with them with the mouse.This offers a nice
| |
| way to represent information graphically in your programs.
| |
| You can do this with gadgets.Using Proportional gadgets,you can
| |
| put a symbol in a frame and move horzontally and/or vertically.The
| |
| size of the frame and the slider can be variable size,so that the
| |
| frame size is relative to the screen size so when the window
| |
| changes size,it will also.The slider can be set up so that its
| |
| size in the grows or shrinks.
| |
| These are best seen via example and experimentation.(The
| |
| posibilities mentioned do not form a complete list by any stretch
| |
| of the imagination.)You want to set up a simple Proportional
| |
| gadget that can be moved horizontally.
| |
| You need a gadget structure that as the same form as others.To
| |
| show the differences,heres a complete example structure for your
| |
| gadget.You can connect this gadget to the other one,by changing
| |
| the first long word in the last structure to "dc.l gadget2".
| |
|
| |
| gadget2: ;*structure for Proportional gadget
| |
| dc.l 0 ;no more gadgets
| |
| dc.w 150,30 ;position
| |
| dc.w 100,10 ;width and height of frame
| |
| dc.w 4 ;flags:GADGIMAGE
| |
| dc.w 2 ;activation:GADGIMMEDIATE
| |
| dc.w 3 ;type:proportional gadget
| |
| dc.l mover ;pointer to slider data
| |
| dc.l 0 ;no select structure
| |
| dc.l 0 ;no text
| |
| dc.l 0 ;no exclude
| |
| dc.l propinfo ;pointer to propinfo structure
| |
| dc.w 3 ;gadget ID
| |
| dc.l 0 ;no user data
| |
|
| |
| You see two special features.Use an image structure for the mover
| |
| and put a pointer to another structure in the spot for the Special
| |
| Info pointer.
| |
| First,lets look at the "mover"structure,the sliders image
| |
| structure.Heres an example of this structure:
| |
|
| |
| mover: ;*structure for slider image
| |
| dc.w 0,0 ;no offset
| |
| dc.w 16,7 ;16x7 pixels big
| |
| dc.w 1 ;one bit plane
| |
| dc.l moverdata ;pointer to image data
| |
| dc.b 1,0 ;colour:white
| |
| dc.l 0 ;don't continue
| |
|
| |
| moverdata: ;*image data for mover
| |
| dc.w %0111111111111110
| |
| dc.w %0101111111111010
| |
| dc.w %0101011111101010
| |
| dc.w %0101010110101010
| |
| dc.w %0101011111101010
| |
| dc.w %0101111111111010
| |
| dc.w %0111111111111110
| |
|
| |
| Up till now,there was'nt anything new.Now lets look at the
| |
| PropInfo structure that describes the properties of the
| |
| Proportional gadget.
| |
|
| |
| The structure starts with a flag word that contains the following
| |
| bits:
| |
|
| |
| Bits Value Name Meaning
| |
| -----------------------------------------------------------------
| |
| 0 1 AUTOKNOB Mover is set up automatically
| |
| 1 2 FREEHORIZ Allows horizontal movement
| |
| 2 4 FREEVERT Allows vertical movement
| |
| 3 8 PROPBORDERLESS Turns off automatic framing
| |
| 8 $100 KNOBHIT Set when the mover is touched
| |
|
| |
| You can set the first four bits to get the representation that you
| |
| want.Bit 8 is set by Intuition when the mover is clicked with the
| |
| mouse pointer.
| |
| Bit 0,AUTOKNOB,allos for the simplest sort of Proportional gadget.
| |
| If this bit is set,no move data are used for the mover image.
| |
| Instead,a white mover is generated that is adjusted to the size of
| |
| the box and the values to be represented.When you use this slider
| |
| to represent the displayed lines in a long text of a program,the
| |
| displayed lines are a percentage of the total text.The
| |
| relationship between the total number of lines and the lines shown
| |
| is represented by an AUTOKNOB as the relationship between the
| |
| frame and the slider.The bigger the percentage,the bigger the
| |
| slider is.You don't want to work with this though,even though it
| |
| is simple and interesting,because a simple white button isn't
| |
| particularly attractive.If you experiment with it,make sure that
| |
| the pointer to the image data points to a four word long buffer
| |
| that Intuition can use to store values.The buffer is of the
| |
| following form:
| |
|
| |
| buffer:
| |
| dc.w 0 ;X position of the slider in the box
| |
| dc.w 0 ;Y position in the box
| |
| dc.w 0 ;width of slider
| |
| dc.w 0 ;height of slider
| |
|
| |
| Leys look at the PropInfo structure.Since you're not using
| |
| AUTOKNOB and wish to allow horizontal movement only,put two in as
| |
| a flag:
| |
|
| |
| propinfo:
| |
| dc.w 2 ;flags:FREEHORIZ
| |
|
| |
| In the next two words of the structure,the horizontal (HorizPot)
| |
| and vertical (VertPot) position of sliders are stored.A value of
| |
| zero means left or upper,while the value $FFFF means right or
| |
| lower.The value that results from movement is in this range.You
| |
| set these values to zero at the start of the program.After moving
| |
| the mouse,there is different values here.
| |
|
| |
| dc.w 0,0 ;X and Y position of the slider
| |
|
| |
| Next come two words which determine the size of the AUTOKNOB or
| |
| the step size of the slider(this determines how far the slider
| |
| moves when you click in the box next to the slider).These words
| |
| are called HorizBody (horizontal movement) and VertBody (vertical
| |
| movement).
| |
|
| |
| dc.w $ffff/16 ;horizontal step size:1/16
| |
| dc.w 0 ;no vertical movement
| |
| ;The next six words are initialized by Intuition.
| |
| dc.w 0 ;box width
| |
| dc.w 0 ;box height
| |
| dc.w 0 ;absolute step size horizontal
| |
| dc.w 0 ;and vertical
| |
| dc.w 0 ;left border of box
| |
| dc.w 0 ;upper border of box
| |
|
| |
| Thats it.Heres a quick overview of the PropInfo structure:
| |
|
| |
| prpoinfo:
| |
| dc.w 2 ;flags:FREEHORIZ
| |
| dc.w 0,0 ;X and Y position of slider
| |
| dc.w $ffff/16 ;horizontal step size:1/16
| |
| dc.w 0 ;no vertical movement
| |
| dc.w 0 ;box width
| |
| dc.w 0 ;box height
| |
| dc.w 0 ;absolute step size horizontal
| |
| dc.w 0 ;and vertical
| |
| dc.w 0 ;left border of box
| |
| dc.w 0 ;upper border of box
| |
|
| |
| Once you've typed this in,you can start the program and try it
| |
| out.
| |
| You can also try vertical movement by setting the flag word equal
| |
| to six,the vertical step size to $FFFF/10,and the height of the
| |
| gadget to 80,for the example.To try out the AUTOKNOBs,change the
| |
| flag value to seven.
| |
|
| |
| 7.10.Example Program.
| |
| ---------------------
| |
| Here is a complete example program using what you have learned in
| |
| this chapter:
| |
|
| |
| ;7_Intuition.asm
| |
| ;** Demo-Program for working with Intuition **
| |
|
| |
| movescreen =-162
| |
| openscreen =-198
| |
| closescreen =-66
| |
| openwindow =-204
| |
| closewindow =-72
| |
| autorequest =-348
| |
| setmenustrip =-264
| |
| clearmenustrip=-54
| |
| printitext =-216
| |
| drawimage =-144
| |
| drawborder =-108
| |
| displaybeep =-96
| |
| closelibrary =-414
| |
| openlib =-408
| |
| execbase = 4
| |
| getmsg =-372
| |
|
| |
| joy2 =$dff0c
| |
| fire =$bfe001
| |
|
| |
| ;!!!when > 500kb !!!
| |
| ;org $40000
| |
| ;load $40000
| |
| ; or use AssemPro to place in CHIP RAM
| |
| ;!!!!!!!!!!!!!!!!!!!!!!!
| |
|
| |
| run:
| |
| bsr openint
| |
| bsr scropen
| |
| bsr windopen
| |
| bsr setmenu
| |
| bsr print
| |
|
| |
| lea border,a1
| |
| move #22,d0
| |
| move #30,d1
| |
| bsr borderdraw
| |
|
| |
| bsr draw
| |
|
| |
| bsr request
| |
|
| |
| loop:
| |
| move.l execbase,a6
| |
| move.l windowhd,a0
| |
| move.l 86(a0),a0 ;user port
| |
| jsr getmsg(a6)
| |
| tst.l d0
| |
| beq loop ;no event
| |
| move.l d0,a0
| |
| move.l $16(a0),msg ;event:LO=item,HI=Event
| |
| move.l msg,d6 ;to test
| |
| move.l d6,d7
| |
| lsr #8,d7
| |
| lsr #3,d7 ;sub menu point in D7
| |
| clr.l d5
| |
| roxr #1,d6
| |
| roxl #1,d5 ;menu number in D5
| |
| and.l #$7f,d6
| |
| cmp #$7f,d6 ;no menu point?
| |
| beq loop ;no:continue
| |
| lsr #4,d6 ;menu point in D6
| |
| cmp #1,d6 ;point 2?
| |
| bne no1
| |
| move,l intbase,a6
| |
| move.l screenhd,a0
| |
| jsr displaybeep(a6)
| |
|
| |
| no1:
| |
| cmp #0,d6
| |
| bne loop
| |
|
| |
| ende:
| |
| bsr clearmenu
| |
| bsr windclose
| |
| bsr scrclose
| |
| bsr closeint
| |
| rts
| |
|
| |
| openint:
| |
| move.l execbase,a6
| |
| lea intname,a1
| |
| jsr openlib(a6)
| |
| move.l d0,intbase
| |
| rts
| |
|
| |
| closeint:
| |
| move.l execbase,a6
| |
| move.l intbase,a1
| |
| jsr closelibrary(a6)
| |
| rts
| |
|
| |
| scropen:
| |
| move.l inbase,a6
| |
| lea screen_defs,a0
| |
| jsr openscreen(a6)
| |
| move.l d0,screenhd
| |
|
| |
| scrclose:
| |
| move.l inbase,a6
| |
| move.l screenhd,a0
| |
| jsr closescreen(a6)
| |
| rts
| |
|
| |
| scrmove:
| |
| move.l intbase,a6
| |
| move,l screenhd,a0
| |
| jsr movescreen(a6)
| |
| rts
| |
|
| |
| windopen:
| |
| move.l intbase,a6
| |
| lea windowdef,a0
| |
| jsr openwindow(a6)
| |
| move.l d0,windowhd
| |
| rts
| |
|
| |
| windclose:
| |
| move.l intbase,a6
| |
| move.l windowhd,a0
| |
| jsr closewindow(a6)
| |
| rts
| |
|
| |
| request:
| |
| move.l windowhd,a0
| |
| lea btext,a1
| |
| lea ltext,a2
| |
| lea rtext,a3
| |
| move.l #0,d0
| |
| move.l #0,d1
| |
| move.l #180,d2
| |
| move.l #80,d3
| |
| move.l intbase,a6
| |
| jsr autorequest(a6)
| |
| rts
| |
|
| |
| setmenu:
| |
| lea mentab,a0 ;pointer to text pointer in A0
| |
| lea menu,a1 ;pointer to menu field in A1
| |
| move #10,d1 ;menu position = 10
| |
|
| |
| menuloop:
| |
| clr.l d2 ;menu point-Y=0
| |
| move.l a1,a2 ;save pointer
| |
| tst.l (a0)
| |
| beq setmenu1 ;end
| |
| clr.l (a1)+
| |
| move d1,(a1)+
| |
| add.l #70,d1
| |
| move.l #50,(a1)+
| |
| move.l #$a0001,(a1)+
| |
| move.l (a0)+,(a1)+ ;menu title
| |
| lea 12(a1),a3
| |
| move.l a3,(a1)+ ;menu point
| |
| clr.l (a1)+
| |
| clr.l (a1)+
| |
|
| |
| itemloop:
| |
| tst.l (a0) ;last one?
| |
| beq menuend ;yes
| |
| lea 54(a1),a3
| |
| move.l a3,(a1)+ ;pointer to next point
| |
| move.l d2,(a1)+ ;X/Y
| |
| add #10,d2
| |
| move.l #$5a000a,(a1)+ ;width/height
| |
| move #$52,(a1)+
| |
| clr.l (a1)+
| |
| lea 16(a1),a3
| |
| move.l a3,(a1)+ ;text structor-pointer
| |
| clr.l (a1)+
| |
| clr.l (a1)+
| |
| clr.l (a1)+
| |
|
| |
| move #$1,(a1)+ ;text-structor set
| |
| clr (a1)+
| |
| move.l #$50003,(a1)+
| |
| clr.l (a1)+
| |
| move.l (a0)+,(a1)+ ;text pointer
| |
| clr.l (a1)+
| |
|
| |
| bra itemloop ;next point...
| |
|
| |
| menuend:
| |
| clr.l -54(a1)
| |
| tst.l (a0)+
| |
| tst.l (a0) ;still in menu?
| |
| beq setmenu1 ;no:ready
| |
| move.l a1,(a2) ;pointer to next menu
| |
| bra menuloop ;and continue
| |
|
| |
| setmenu1:
| |
| move.l intbase,a6
| |
| move.l windowhd,a0
| |
| lea menu,a1
| |
| jsr setmenustrip(a6)
| |
| rts
| |
|
| |
| clearmenu:
| |
| move.l intbase,a6
| |
| move.l windowhd,a0
| |
| jsr clearmenustrip(a6)
| |
| rts
| |
|
| |
| print:
| |
| move.l intbase,a6
| |
| move.l windowhd,a0
| |
| move.l 50(a0),a0
| |
| lea ggtext,a1
| |
| move.l #30,d0 ;X
| |
| move.l #16,d1 ;Y
| |
| jsr printitext(a6)
| |
| rts
| |
|
| |
| draw:
| |
| move.l intbase,a6
| |
| move.l windowhd,a0
| |
| move.l 50(a0),a0
| |
| lea image,a1
| |
| move.l #200,d0
| |
| move.l #100,d1
| |
| jsr drawimage(a6)
| |
| rts
| |
|
| |
| borderdraw:
| |
| move.l intbase,a6
| |
| move.l windowhd,a0
| |
| move.l 50(a0),a0
| |
| jsr drawborder(a6)
| |
| rts
| |
|
| |
| screen_defs:
| |
| dc.w 0,0
| |
| dc.w 640,200
| |
| dc.w 4
| |
| dc.b 0
| |
| dc.b 1
| |
| dc.w $800
| |
| dc.w 15
| |
| dc.l 0
| |
| dc.l tite1
| |
| dc.l 0
| |
| dc.l 0
| |
|
| |
| windowdef:
| |
| dc.w 10,20
| |
| dc.w 300,150
| |
| dc.b 0,1
| |
| dc.l $300
| |
| dc.l $100f
| |
| dc.l gadget
| |
| dc.l 0
| |
| dc.l windname
| |
|
| |
| screenhd:
| |
| dc.l 0
| |
| dc.l 0
| |
| dc.w 200,40,600,200
| |
| dc.w $f
| |
|
| |
| btext:
| |
| dc.b 3,3
| |
| dc.b 0
| |
| align
| |
| dc.w 10,10
| |
| dc.l 0
| |
| dc.l bodytxt
| |
| dc.l 0
| |
|
| |
| bodytxt:dc.b "Requester-Text",0
| |
| align
| |
|
| |
| ltext:
| |
| dc.b 3,1
| |
| dc.b 0
| |
| align dc.w 5,3
| |
| dc.l 0
| |
| dc.l lefttext
| |
| dc.l 0
| |
|
| |
| lefttext: dc.b "left",0
| |
| align
| |
|
| |
| rtext:
| |
| dc.b 0,1
| |
| dc.b 0
| |
| align dc.w 5,3
| |
| dc.l 0
| |
| dc.l righttext
| |
| dc.l 0
| |
|
| |
| righttext: dc.b "right",0
| |
| align
| |
| tite1: dc.b "User Screen",0
| |
| windname: dc.b "Window-Title",0
| |
| align windowhd: dc.l 0
| |
|
| |
| intbase:dc.l 0
| |
| intname:dc.b "intuition.library",0
| |
| align msg:dc.l 0
| |
|
| |
| mentab:
| |
| dc.l menu1
| |
| dc.l mp11,np12,mp13,mp14,mp15,mp16,mp17,mp18,mp19,0
| |
| dc.l menu2
| |
| dc.l mp21,mp22,mp23,0
| |
| dc.l menu3
| |
| dc.l mp31,mp32,0
| |
| dc.l menu4,mp41,0
| |
| dc.l 0
| |
|
| |
| menu1: dc.b "Menu 1",0
| |
| mp11: dc.b "Point 11",0
| |
| mp12: dc.b "Point 12",0
| |
| mp13: dc.b "Point 13",0
| |
| mp14: dc.b "Point 14",0
| |
| mp15: dc.b "Point 15",0
| |
| mp16: dc.b "Point 16",0
| |
| mp17: dc.b "Point 17",0
| |
| mp18: dc.b "Point 18",0
| |
| mp19: dc.b "Point 19",0
| |
|
| |
| menu2: dc.b "Menu 2",0
| |
| mp21: dc.b "End!",0
| |
| mp22: dc.b "Beep",0
| |
| mp23: dc.b "Point 23",0
| |
|
| |
| menu3: dc.b "Menu 3",0
| |
| mp31: dc.b "Point 31",0
| |
| mp32: dc.b "Point 32",0
| |
|
| |
| menu4: dc.b "Menu 4",0
| |
| mp41: dc.b "Point 41",0
| |
| align
| |
|
| |
| gadget:
| |
| dc.l gadget1
| |
| dc.w 20,80,80,10
| |
| dc.w 0
| |
| dc.w $2 ;activation,$802 for longint
| |
| dc.w 4
| |
| dc.l border
| |
| dc.l 0
| |
| dc.l 0
| |
| dc.l 0
| |
| dc.l strinfo
| |
| dc.w 2
| |
| dc.l 0
| |
|
| |
| border:
| |
| dc.w 0,0
| |
| dc.b 1,0,0
| |
| dc.d 5 ;XY-pair
| |
| dc.l koord
| |
| dc.l 0
| |
|
| |
| koord:
| |
| dc.w -2,-2,80,-2,80,9,-2,9,-2,-2
| |
|
| |
| strinfo:
| |
| dc.l strpuffer
| |
| dc.l undo
| |
| dc.w 0 ;cursor position
| |
| dc.w 10 ;max.char
| |
| dc.w 0
| |
| dc.w 0,0,0,0,0
| |
| dc.l 0,0,0
| |
|
| |
| strpuffer:
| |
| dc.b "Hello!",0,0,0
| |
|
| |
| undo: dc.l 0,0,0
| |
| align
| |
|
| |
| gadget1:
| |
| dc.l gadget2 ;more gadget
| |
| dc.w 40,50,32,13
| |
| dc.w $6 ;flags:invert
| |
| dc.w $103 ;activate
| |
| dc.w 1 ;gadget type
| |
| dc.l image ;gadget image
| |
| dc.l image2 ;select gadget
| |
| dc.l ggtext ;gadget text
| |
| dc.l 0 ;no exclude
| |
| dc.l 0 ;special info
| |
| dc.w 1 ;ID
| |
| dc.l 0 ;user data
| |
|
| |
| ggtext:
| |
| dc.b 1,0,1
| |
| align
| |
| dc.w -8,14
| |
| dc.l 0
| |
| dc.l swtext
| |
| dc,l 0
| |
|
| |
| swtext:
| |
| dc.b "Switch",0
| |
| align
| |
|
| |
| image:
| |
| dc.w 0,0
| |
| dc.w 32,13
| |
| dc.w 1
| |
| dc.l imgdata
| |
| dc.b 2,1
| |
| dc.l 0
| |
|
| |
| image2:
| |
| dc.w 0,0
| |
| dc.w 32,13
| |
| dc.w 1
| |
| dc.l imgdata2
| |
| dc.b 2,1
| |
| dc.l 0
| |
|
| |
| imgdata:
| |
| dc.l 0
| |
| dc.l %00000000011100000000000000000000
| |
| dc.l %00000000111110000011101001000000
| |
| dc.l %00000000111110000010101101000000
| |
| dc.l %00000000011110000010101011000000
| |
| dc.l %00000000000111000011101001000000
| |
| dc.l %00000000000011100000000000000000
| |
| dc.l %00000000000001110000000000000000
| |
| dc.l %00000000000111111111100000000000
| |
| dc.l %00000000001111111111110000000000
| |
| dc.l %00000000001111111111110000000000
| |
| dc.l %00000000000110000001100000000000
| |
| dc.l 0
| |
|
| |
| imgdata2:
| |
| dc.l 0
| |
| dc.l %00000000000000000000111000000000
| |
| dc.l %00011101110111000001111100000000
| |
| dc.l %00010101000100000001111100000000
| |
| dc.l %00010101100110000001111000000000
| |
| dc.l %00011101000100000011100000000000
| |
| dc.l %00000000000000000111000000000000
| |
| dc.l %00000000000000001110000000000000
| |
| dc.l %00000000000111111111100000000000
| |
| dc.l %00000000001111111111110000000000
| |
| dc.l %00000000001111111111110000000000
| |
| dc.l %00000000000110000001100000000000
| |
| dc.l 0
| |
|
| |
| gadget2:
| |
| dc.l 0
| |
| dc.w 150,30,100,50
| |
| dc.w 5
| |
| dc.w 2
| |
| dc.w 3 ;prop.gadet
| |
| dc.l mover ;border
| |
| dc.l 0,0,0
| |
| dc.l specinfo
| |
| dc.w 3
| |
| dc.l 0
| |
|
| |
| specinfo:
| |
| dc.w 6 ;flags:free horiz
| |
| dc.w 0,0
| |
| dc.w $ffff/10,$ffff/5
| |
| dc.w 0,0,0,0,0,0
| |
|
| |
| mover:
| |
| dc.w 0,0,16,7
| |
| dc.w 1
| |
| dc.l moverdata
| |
| dc.b 1,0
| |
| dc.l 0
| |
|
| |
| moverdata:
| |
| dc.w %0111111111111110
| |
| dc.w %0101111111111010
| |
| dc.w %0101011111101010
| |
| dc.w %0101010110101010
| |
| dc.w %0101011111101010
| |
| dc.w %0101111111111010
| |
| dc.w %0111111111111110
| |
|
| |
| menu:blk.w 500
| |
|
| |
| end
| |
|
| |
|
| |
|
| |
|
| |
| Chapter 8.
| |
| ----------
| |
| 8.Advanced Programming.
| |
| -----------------------
| |
| You've learned a lot about machine language programming on the
| |
| Amiga.What you need yet are a few routines that can be used as
| |
| programming tools.We'll work on that right now.They'll be easy to
| |
| use in your own program.The sky's the limit now!
| |
|
| |
| 8.1.Supervisor Mode.
| |
| --------------------
| |
| As mentioned in the chapter on the MC68000 processor,there are two
| |
| operating modes:the User and the Supervisor mode.It is often
| |
| necessary to move between the two modes.However,this isn't a
| |
| simple process.
| |
| The reason you want to do this,is that in User mode,you can't
| |
| access the Status register.If you write to one of them,an
| |
| Exception is executed which crashes the program.
| |
|
| |
| How can you get in Supervisor mode?
| |
|
| |
| No problem.The operating system of the Amiga contains a function
| |
| in the EXEC library that lets you get into supervisor mode.Its
| |
| called SuperState and it doesn't need any parameters.You can
| |
| easily call this program by using the following lines:
| |
|
| |
| execbase = 4 ;exec base address
| |
| superstate =-150 ;turn on function
| |
| ...
| |
| move.l execbase,a6 ;exec base address in A6
| |
| jsr superstate(a6) ;turn on supervisor mode
| |
| move.l d0,savesp ;save return value
| |
| ...
| |
| savesp:blk.l 1 ;space for sp value
| |
|
| |
| You get the value of the Stack Pointer(SP)back in the D0 register.
| |
| You'll also find it in register A7,but this register is changed
| |
| regularly.The reason is that in Supervisor mode,the Amiga works
| |
| with all the Interrupts and with the SP,and there are lots of
| |
| Interrupts for this computor.We'll talk about interrupts in a bit.
| |
|
| |
| After this call,you'll use the user stack instead of the
| |
| supervisor stack.In this way,you can access the old user stack.You
| |
| need to make sure that the user stack is large enough since the
| |
| interrupts must have enough room for their data on the stack.
| |
| You need to save the value returned in D0,because you'll need this
| |
| value later.You need to return to user mode sometime.Theres a
| |
| function for this in the exec library as well.It is called the
| |
| UserState function.It needs one parameter,the SP value that comes
| |
| back from the SuperState function.
| |
| Since you've saved this value in the long word starting at
| |
| "savesp",you can write the following:
| |
|
| |
| userstate =-156
| |
| ...
| |
| move.l execbase,a6 ;exec base address in A6
| |
| move.l savesp,d0 ;put old sp in D0
| |
| jsr userstate(a6) ;return to user mode
| |
|
| |
| Now you are back in the user mode.The user stack point(USP)is the
| |
| same as before.You can write functions that need to be run from
| |
| the supervisor mode as subroutines.First you call superstate,save
| |
| the return value,execute the desired function,call userstate,and
| |
| end with a RTS command.If the USP was changed,the RTS command
| |
| would'nt work right,and the computor would jump who knows where
| |
| and perhaps crash.Here it works though.
| |
| Now comes the question:how does the operating system get the
| |
| supervisor mode?Thats not too difficult;it goes like this:
| |
|
| |
| The superstate function attempts to access a Status Register.This
| |
| causes an Exception to occur and a routine is called whose address
| |
| begins at the long word starting at $20.It is the Exception Vector
| |
| for Privilege Violation.The routine that it branches to is called
| |
| in supervisor mode.Then it tests where this exception came from.If
| |
| the routine finds that the exception comes from the superstate
| |
| routine whose address it knows,the matter is settled.It just
| |
| branches to the routine without turning off the user mode.Thats
| |
| all there is to it.
| |
|
| |
| 8.2.Exception Programming.
| |
| --------------------------
| |
| The exceptions described in the processor chapter offer you a lot
| |
| of oppertunities to control the Amiga's functions.You can use them
| |
| to specify how errors should be handled and even list a crash
| |
| program.
| |
|
| |
| Here is a list of vectors that are used to jump to the exception
| |
| routines:
| |
|
| |
| Number Address Use with
| |
| ------------------------------------------------------------------
| |
| 2 $008 Bus error
| |
| 3 $00C Address eror
| |
| 4 $010 Illegal command
| |
| 5 $014 Division by zero
| |
| 6 $018 CHK command
| |
| 7 $01C TRAPV command
| |
| 8 $020 Privilege Violation
| |
| 9 $024 Trace
| |
| 10 $028 Axxx command emulation
| |
| 11 $02C Fxxx command emulation
| |
| $030-$038 Reserved
| |
| 15 $03C Uninitialized interrupt
| |
| $040-$05F Reserved
| |
| 24 $060 Unauthorised interrupt
| |
| 25-31 $064-$083 Level 1-7 interrupt
| |
| 32-47 $080-$0BF TRAP commands
| |
| $0C0-$0FF Reserved
| |
| 64-255 $100-$3FF User interrupt vector
| |
|
| |
| Lets look at the TRAP commands as an example.They aren't used in
| |
| the Amiga operating system.A TRAP command and a number between
| |
| zero and fifteen are used to call one of the 16 posible TRAP
| |
| routines.If the command TRAP #0 is executed,the processor (in
| |
| supervisor mode)branches to the routine whose address lies at $80
| |
| in memory.This routine must end with a RTE(ReTurn from Exception)
| |
| command.
| |
| Some operating systems,for example,the ATARI ST's TOS operating
| |
| systems,are completely callable via these TRAP's.Parameters are
| |
| put on the stack,and then a TRAP command is executed.The advantage
| |
| is that you don't have to know any of the operating systems
| |
| addresses.In the Amiga you must know the addresses(Execbase=4).
| |
|
| |
| Lets write your own TRAP routine to demonstrate the use of the
| |
| TRAP command.You'll need three program sections:
| |
|
| |
| 1.The initialization of the TRAP vector.
| |
| 2.The TRAP routine itself(It must end with RTE).
| |
| 3.A test routine that calls the TRAP command.
| |
|
| |
| Initialization is very short:
| |
|
| |
| init:
| |
| move.l #trap0,$80 ;set vector for TRAP #0
| |
| rts
| |
|
| |
| Now you need to write the trap0 routine.Lets use the example from
| |
| the hardware chapter that produced a beep.
| |
| Lets write this routine using as little effort as possible.Change
| |
| the RTS to a RTE at the end,erase the line in which the loop
| |
| counter D0 was loaded for the tone production,and change the loop
| |
| so that it works with long words.Now you can load the register
| |
| with an arbitrary value and have the TRAP #0 followed by a peep of
| |
| arbitrary duration.
| |
|
| |
| ;** beep tone production after a TRAP #0 **
| |
|
| |
| ctlw =$dff096 ;DMA control
| |
| c0thi =$dff0a0 ;HI table address
| |
| c0tlo =c0thi+2 ;LO table address
| |
| c0tl =c0thi+4 ;table length
| |
| c0per =c0thi+6 ;read in rate
| |
| c0vol =c0thi+8 ;volume
| |
|
| |
| trap0: ;* produce a short peep
| |
| move.l #table,c0thi ;table beginning
| |
| move #4,c0tl ;table length
| |
| move #300,c0per ;read in rate
| |
| move #40,c0vol ;volume
| |
| move #$8201,ctlw ;start DMA (sound)
| |
|
| |
| loop:
| |
| subq.l #1,d0 ;counter -1
| |
| bne loop ;count dwn to zero
| |
|
| |
| still:
| |
| move #1,ctlw ;turn on tone
| |
| rte ;exception end
| |
|
| |
| table: ;sound table
| |
| dc.b -40,-70,-40,0,40,70,40,0
| |
|
| |
| You need to make sure that "table"is in CHIP RAM($00000-$7FFFF),
| |
| otherwise the sound chip can't access the data!
| |
|
| |
| After entering this,you can test it out using the following
| |
| routine:
| |
|
| |
| test:
| |
| move.l #$2ffff,d0 ;pass tone length in D0
| |
| trap #0 ;carry out exception:peep
| |
| rts
| |
|
| |
| Now assemble both routines and start the initialization routine,
| |
| init.Nothing happens.
| |
| Start the second routine,test.A beep that lasts about one second
| |
| is output.
| |
| One thing you must keep in mind is that if you change the program
| |
| and reassemble it,the address of the trap0 routine can change.
| |
| Before you execute the TRAP command,you must repeat the initializ-
| |
| ation,so that the computor doesn't jump to the wrong location!
| |
|
| |
|
| |
|
| |
|
| |
| Appendices.
| |
| -----------
| |
| Overview of Library Functions.
| |
| ------------------------------
| |
| The following table gives you an overview of the available
| |
| libraries and their functions.Each sublist of functions is
| |
| preceded by the name of the library it is found in.
| |
| These functions are listed with their negative offset in hex and
| |
| decimal.Their name and their parameters are also specified.The
| |
| parameter names are in parenthesis behind the function name.The
| |
| second set of parenthesis includes a list of registers that
| |
| correspond to the parameter names.If no parameters are needed,we
| |
| put () to let you know.
| |
|
| |
| CLIST.LIBRARY
| |
| -$001E -30 InitCLPool (cLPool,size) (A0,D0)
| |
| -$0024 -36 AllocCList (cLPool) (A1)
| |
| -$002A -42 FreeCList (cList) (A0)
| |
| -$0030 -48 FlushCList (cList) (A0)
| |
| -$0036 -54 SizeCList (cList) (A0)
| |
| -$003C -60 PutCLChar (cList,byte) (A0,D0)
| |
| -$0042 -66 GetCLChar (cList) (A0)
| |
| -$0048 -72 UnGetCLChar (cList,byte) (A0,D0)
| |
| -$004E -78 UpPutCLChar (cList) (A0)
| |
| -$0054 -84 PutCLWord (cList,word) (A0,D0)
| |
| -$005A -90 GetCLWord (cList) (A0)
| |
| -$0060 -96 UnGetCLWord (cList,word) (A0,D0)
| |
| -$0066 -102 UnPutCLWord (cList) (A0)
| |
| -$006C -108 PutCLBuf (cList,buffer,length) (A0,A1,D1)
| |
| -$0072 -114 GetCLBuf (cList,buffer,maxLength) (A0,A1,D1)
| |
| -$0078 -120 MarkCList (cList,offset) (A0,D0)
| |
| -$007E -126 IncrCLMark (cList) (A0)
| |
| -$0084 -132 PeekCLMark (cList) (A0)
| |
| -$008A -138 SplitCList (cList) (A0)
| |
| -$0090 -144 CopyCList (cList) (A0)
| |
| -$0096 -150 SubCList (cList,index,length) (A0,D0.D1)
| |
| -$009C -156 ConcatCList (sourceCList,destCList) (A0,A1)
| |
|
| |
| CONSOLE.LIBRARY
| |
| -$002A -42 CDInputHandler (events,device) (A0,A1)
| |
| -$0030 -48 RawKeyConvert (events,buffer,length,keyMap)
| |
| (A0,A1,D1,A2)
| |
| DISKFONT.LIBRARY
| |
| -$001E -30 OpenDiskFont (textAttr) (A0)
| |
| -$0024 -36 AvailFonts (buffer,bufBytes,flags) (A0,D0,D1)
| |
|
| |
| DOS.LIBRARY
| |
| -$001e -30 Open (name,access mode)(d1,d2)
| |
| -$0024 -36 Close (file)(d1)
| |
| -$002a -42 Read (file,buffer,length)(d1,d2,d3)
| |
| -$0030 -48 Write (file,buffer,length)(d1,d2,d3)
| |
| -$0036 -54 Input()
| |
| -$003c -60 Output()
| |
| -$0042 -66 Seek(file,position,offset)(d1,d2,d3)
| |
| -$0048 -72 DeleteFile (name)(d1)
| |
| -$004e -78 Rename(oldname,newname)(d1,d2)
| |
| -$0054 -84 Lock(name,type)(d1,d2)
| |
| -$005a -90 UnLock(lock)(d1)
| |
| -$0060 -96 DupLock(lock)(d1)
| |
| -$0066 -102 Examine(lock,fileinfoblock)(d1,d2)
| |
| -$006c -108 ExNext(lock,fileinfoblock)(d1,d2)
| |
| -$0072 -114 Info(lock,parameterblock)(d1,d2)
| |
| -$0078 -120 CreateDir(name)(d1)
| |
| -$007e -126 CurrentDir(lock)(d1)
| |
| -$0084 -132 IoErr()
| |
| -$008a -138 CreateProc(name,pri,seglist,stacksize)(d1,d2,d3,d4)
| |
| -$0090 -144 Exit(returncode)(d1)
| |
| -$0096 -150 LoadSeg(filename)(d1)
| |
| -$009c -156 UnLoadSeg(segment)(d1)
| |
| -$00a2 -162 Getpacket(wait)(d1)
| |
| -$00a8 -168 Queuepacket(packet)(d1)
| |
| -$00ae -174 DeviceProc(name)(d1)
| |
| -$00be -180 SetComment(name,comment)(d1,d2)
| |
| -$ooba -186 SetProtection(name,mask)(d1,d2)
| |
| -$00c0 -192 DateStamp(date)(d1)
| |
| -$00c6 -198 Delay(timeout)(d1)
| |
| -$00cc -204 WaitForChar(file,timeout)(d1,d2)
| |
| -$00d2 -210 ParentDir(lock)(d1)
| |
| -$00d8 -216 IsInteractive(file)(d1)
| |
| -$00de -222 Execute(string,file,file)(d1,d2,d3)
| |
|
| |
| EXEC.LIBRARY
| |
| -$001e -30 Supervisor()
| |
| -$0024 -36 ExitIntr()
| |
| -$002a -42 Schedule()
| |
| -$0030 -48 Reschedule()
| |
| -$0036 -54 Switch()
| |
| -$003c -60 Dispatch()
| |
| -$0042 -66 Exception()
| |
| -$0048 -72 InitCode(startclass,version)(d0,d1)
| |
| -$004e -78 InitStruct(inittable,memory,size)(a1,a2,d0)
| |
| -$0054 -84 MakeLibrary(funcinit,structinit,libinit,datasize,
| |
| codesize)(a0,a1,a2,d0,d1)
| |
| -$005a -90 MakeFunctions(target,functionarray,funcdispbase)
| |
| (a0,a1,a2)
| |
| -$0060 -96 FindResident(name)(a1)
| |
| -$0066 -102 InitResident(resident,seglist)(a1,d1)
| |
| -$006c -108 Alert(alertnum,parameters)(d7,a5)
| |
| -$0072 -114 Debug()
| |
| -$0078 -120 Disable()
| |
| -$007e -126 Enable()
| |
| -$0084 -132 Forbid()
| |
| -$008a -138 Permit()
| |
| -$0090 -144 SetSR(newsr,mask)(d0,d1)
| |
| -$0096 -150 SuperState()
| |
| -$009c -156 UserState(sysstack)(d0)
| |
| -$00a2 -162 setIntVector(intnumber,interrupt)(d0,d1)
| |
| -$00a8 -168 AddIntServer(intnumber,interrupt)(d0,d1)
| |
| -$00ae -174 RemIntServer(intnumber,interrupt)(d0,d1)
| |
| -$00b4 -180 Cause(interrup)(a1)
| |
| -$00ba -186 Allocate(freelist,bytesize)(a0,d0)
| |
| -$00c0 -192 Deallocate(freelist,memoryblock,bytesize)(a0,a1,d0)
| |
| -$00c6 -198 AllocMem(bytesize,requirements)(d0,d1)
| |
| -$00cc -204 AlloAbs(bytesize,location)(d0,a1)
| |
| -$00d2 -210 FreeMem(memoryblock,bytesize)(a1,d0)
| |
| -$00d8 -216 AvailMem(requirements)(d1)
| |
| -$00de -222 AllocEntry(entry)(a0)
| |
| -$00e4 -228 FreeEntry(entry)(a0)
| |
| -$00ea -234 Insert(list,node,pred)(a0,a1,a2)
| |
| -$00f0 -240 AddHead(list,node)(a0,a1)
| |
| -$00f6 -246 AddTail(list,node)(a0,a1)
| |
| -$00fc -252 Remove(node)(a1)
| |
| -$0102 -258 RemHead(list)(a0)
| |
| -$0108 -264 RemTail(list)(a0)
| |
| -$010e -270 Enqueue(list,node)(a0,a1)
| |
| -$0114 -276 FindName(list,name)(a0,a1)
| |
| -$011a -282 AddTask(task,initpc,finalpc)(a1,a2,a3)
| |
| -$0120 -288 RemTask(task)(a1)
| |
| -$0126 -294 FindTask(name)(a1)
| |
| -$012c -300 SetTaskPri(task,prority)(a1,d0)
| |
| -$0132 -306 SetSignal(newsignals,signelset)(d0,d1)
| |
| -$0138 -312 SetExcept(newsignals,signalset)(d0,d1)
| |
| -$013e -318 Wait(signalset)(d0)
| |
| -$0144 -324 Signal(task,signalset)(a1,d0)
| |
| -$014a -330 AllocSignal(signalnum)(d0)
| |
| -$0150 -336 FreeSignal(signalnum)(d0)
| |
| -$0156 -342 AllocTrap(trapnum)(d0)
| |
| -$015c -348 FreeTrap(trapnum)(d0)
| |
| -$0162 -354 AddPort(port)(a1)
| |
| -$0168 -360 RemPort(port)(a1)
| |
| -$016e -366 PutMsg(port,message)(a0,a1)
| |
| -$0174 -372 GetMsg(port)(a0)
| |
| -$017a -378 ReplyMsg(message)(a1)
| |
| -$0180 -384 WaitPort(port)(a0)
| |
| -$0186 -390 FindPort(name)(a1)
| |
| -$018c -396 AddLibrary(library)(a1)
| |
| -$0192 -402 RemLibrary(library)(a1)
| |
| -$0198 -408 OldOpenLibrary(libname)(a1)
| |
| -$019e -414 CloseLibrary(library)(a1)
| |
| -$01a4 -420 Setfunction(library,funcoffset,funcentry)(a1,a0,d0)
| |
| -$01aa -426 SumLibrary(library)(a1)
| |
| -$01b0 -432 AddDevice(device)(a1)
| |
| -$01b6 -438 RemDevice(device)(a1)
| |
| -$01bc -444 OpenDevice(devname,unit,iorequest,flags)(a0,d0,a1
| |
| ,d1)
| |
| -$01c2 -450 CloseDevice(iorequest)(a1)
| |
| -$01c8 -456 DoIO(iorequest)(a1)
| |
| -$01ce -462 SendIO(iorequest)(a1)
| |
| -$01d4 -468 CheckIO(iorequest)(a1)
| |
| -$01da -474 WaitIO(iorequest)(a1)
| |
| -$01e0 -480 AbortIO(iorequest)(a1)
| |
| -$01e6 -486 AddResource(resource)(a1)
| |
| -$01ec -492 RemResource(resource)(a1)
| |
| -$01f2 -498 OpenResource(resname,version)(a1,d0)
| |
| -$01f8 -504 RawIOInit()
| |
| -$01fe -510 RawMayGetChar()
| |
| -$0204 -516 RawPutChar(char)(d0)
| |
| -$020a -522 RawDoFmt()(a0,a1,a2,a3)
| |
| -$0210 -528 GetCC()
| |
| -$0216 -534 TypeOfMem(address)(a1)
| |
| -$021c -540 Procedure(semaport,bidmsg)(a0,a1)
| |
| -$0222 -546 Vacate(semaport)(a0)
| |
| -$0228 -552 OpenLibrary(libname,version)(a1,d0)
| |
|
| |
| GRAPHICS.LIBRARY
| |
| -$001e -30 BltBitMap(scrbitmap,scrx,scry,destbitmap,destx,
| |
| desty,sizex,sizey,minterm,mask,tempa)
| |
| (a0,d0,d1,a1,d2,d3,d4,d5,d6,d7,a2)
| |
| -$0024 -36 BltTemplate(source,scrx,scrmod,destrastport,destx,
| |
| desty,sixex,sizey)(a0,d0,d1,a1,d2,d3,d4,d5)
| |
| -$002a -42 ClearEOL(rastport)(a1)
| |
| -$0030 -48 ClearScreen(rastport)(a1)
| |
| -$0036 -54 TextLength(rastport,string,count)(a1,a0,d0)
| |
| -$003c -60 Text(rastport,string,count)(a1,a0,d0)
| |
| -$0042 -66 SetFont(rastportid,textfont)(a1,a0)
| |
| -$0048 -72 OpenFont(textattr)(a0)
| |
| -$004e -78 CloseFont(textfont)(a1)
| |
| -$0054 -84 AskSoftStyle(rastport)(a1)
| |
| -$005a -90 SetSoftStyle(rastport,style,enable)(a1,d0,d1)
| |
| -$0060 -96 AddBob(bob,rastport)(a0,a1)
| |
| -$0066 -102 AddVSprite(vsprite,rastport)(a0,a1)
| |
| -$006c -108 DoCollision(rastport)(a1)
| |
| -$0072 -114 DrawGList(rastport,viewport)(a1,a0)
| |
| -$0078 -120 InitGels(dummyhead,dummytail,gelsinfo)(a0,a1,a2)
| |
| -$007e -126 InitMasks(vsprite)(a0)
| |
| -$0084 -132 RemIBob(bob,rastport,viewport)(a0,a1,a2)
| |
| -$008a -138 RemVSprite(vsprite)(a0)
| |
| -$0090 -144 SetCollision(type,routine,gelsinfo)(d0,a0,a1)
| |
| -$0096 -150 SortGList(rastport)(a1)
| |
| -$009c -156 AddAnimObj(obj,animationkey,rastport)(a0,a1,a2)
| |
| -$00a2 -162 Animate(animationkey,rastport)(a0,a1)
| |
| -$00a8 -168 etGBuffers(animationobj,rastport,doublebuffer)(a0,
| |
| a1,d0)
| |
| -$00ae -174 InitGMasks(animationobj)(a0)
| |
| -$00b4 -180 GelsFuncE()
| |
| -$00ba -186 GelsFuncF()
| |
| -$00c0 -192 LoadRGB4(viewport,colurs,count)(a0,a1,d0)
| |
| -$00c6 -198 InitRastPort(rastport)(a1)
| |
| -$00cc -204 InitVPort(viewport)(a0)
| |
| -$00d2 -210 MrgCop(view)(a1)
| |
| -$00D8 -216 MakeVPort (view,viewPort) (A0,A1)
| |
| -$00DE -222 LoadView (view) (A1)
| |
| -$00E4 -228 WaitBlit ()
| |
| -$00EA -234 SetRast (rastPort,color) (A1,D0)
| |
| -$00F0 -240 Move (rastPort,x,y) (A1,D0,D1)
| |
| -$00F6 -246 Draw (rastPort,x,y) (A1,D0,D1)
| |
| -$00FC -252 AreaMove (rastPort,x,y) (A1,D0,D1)
| |
| -$0102 -258 AreaDraw (rastPort,x,y) (A1,D0,D1)
| |
| -$0108 -264 AreaEnd (rastPort) (A1)
| |
| -$010E -270 WaitTOF ()
| |
| -$0114 -276 QBlit (blit) (A1)
| |
| -$011A -282 InitArea (areaInfo,vectorTable,vectorTableSize)
| |
| (A0,A1,D0)
| |
| -$0120 -288 SetRGB4 (viewPort,index,r,g,b) (A0,D0,D1,D2,D3)
| |
| -$0126 -294 QBSBlit (blit) (A1)
| |
| -$012C -300 BltClear (memory,size,flags) (A1,D0,D1)
| |
| -$0132 -306 RectFill (rastPort,xl,yl,xu,yu)
| |
| (A1,D0,D1,D2,D3)
| |
| -$0138 -312 BltPattern (rastPort,ras,xl,yl,maxX,maxY,
| |
| fillBytes) (A1,A0,D0,D1,D2,D3,D4)
| |
| -$013E -318 ReadPixel (rastPort,x,y) (A1,D0,D1)
| |
| -$0144 -324 WritePixel (rastPort,x,y) (A1,D0,D1)
| |
| -$014A -330 Flood (rastPort,mode,x,y) (A1,D2,D0,D1)
| |
| -$0150 -336 PolyDraw (rastPort,count,polyTable) (A1,D0,A0)
| |
| -$0156 -342 SetAPen (rastPort,pen) (A1,D0)
| |
| -$015C -348 SetBPen (rastPort,pen) (A1,D0)
| |
| -$0162 -354 SetDrMd (rastPort,drawMode) (A1,D0)
| |
| -$0168 -360 InitView (view) (A1)
| |
| -$016E -366 CBump (copperList) (A1)
| |
| -$0174 -372 CMove (copperList,destination,data) (A1,D0,D1)
| |
| -$017A -378 CWait (copperList,x,y) (A1,D0,D10
| |
| -$0180 -384 VBeamPos ()
| |
| -$0186 -390 InitBitMap (bitMap,depth,width,height)
| |
| (A0,D0,D1,D2)
| |
| -$018C -396 ScrollRaster (rastPort,dX,dY,minx,miny,maxx,
| |
| maxy) (A1,D0,D1,D2,D3,D4,D5)
| |
| -$0192 -402 WaitBOVP (viewPort) (A0)
| |
| -$0198 -408 GetSprite (simpleSprite,num) (A0,D0)
| |
| -$019E -414 FreeSprite (num) (D0)
| |
| -$01A4 -420 ChangeSprite (vp,simpleSprite,data) (A0,A1,A2)
| |
| -$01AA -426 MoveSprite (viewPort,simpleSprite,x,y)
| |
| (A0,A1,D0,D1)
| |
| -$01B0 -432 LockLayerRom (layer) (A5)
| |
| -$01B6 -438 UnlockLayerRom (layer) (A5)
| |
| -$01BC -444 SyncSBitMap (1) (A0)
| |
| -$01C2 -450 CopySBitMap (11,12) (A0,A1)
| |
| -$01C8 -456 OwnBlitter ()
| |
| -$01CE -462 DisownBlitter ()
| |
| -$01D4 -468 InitTmpRas (tmpras,buff,size) (A0,A1,D0)
| |
| -$01DA -474 AskFont (rastPort,textAttr) (A1,A0)
| |
| -$01E0 -480 AddFont (textFont) (A1)
| |
| -$01E6 -486 RemFont (textFont) (A1)
| |
| -$01EC -492 AllocRaster (width,height) (D0,D1)
| |
| -$01F2 -498 FreeRaster (planeptr,width,height) (A0,D0,D1)
| |
| -$01F8 -504 AndRectRegion (rgn,rect) (A0,A1)
| |
| -$01FE -510 OrRectRegion (rgn,rect) (A0,A1)
| |
| -$0204 -516 NewRegion ()
| |
| -$020A -522 ** reserved **
| |
| -$0210 -528 ClearRegion (rgn) (A0)
| |
| -$0216 -534 DisposeRegion (rgn) (A0)
| |
| -$021C -540 FreeVPortCopLists (viewPort) (A0)
| |
| -$0222 -546 FreeCopList (coplist) (A0)
| |
| -$0228 -552 ClipBlit (srcrp,srcX,srcY,destrp,destX,destY,
| |
| sizeX,sizeY,minterm) (A0,D0,D1,A1,D2,D3,D4,D5,D6)
| |
| -$022E -558 XorRectRegion (rgn,rect) (A0,A1)
| |
| -$0234 -564 FreeCprList (cprlist) (A0)
| |
| -$023A -570 GetColorMap (entries) (D0)
| |
| -$0240 -576 FreeColorMap (colormap) (A0)
| |
| -$0246 -582 GetRGB4 (colormap,entry) (A0,D0)
| |
| -$024C -588 ScrollVPort (vp) (A0)
| |
| -$0252 -594 UCopperListInit (copperlist,num) (A0,D0)
| |
| -$0258 -600 FreeGBuffers (animationObj,rastPort,
| |
| doubleBuffer) (A0,A1,D0)
| |
| -$025E -606 BltBitMapRastPort (srcbm,srcx,srcy,destrp,
| |
| destX,destY,sizeX,sizeY,minter)
| |
| (A0,D0,D1,A1,D2,D3,D4,D5,D6)
| |
|
| |
| ICON.LIBRARY
| |
| -$001E -30 GetWBObject (name) (A0)
| |
| -$0024 -36 PutWBObject (name,object) (A0,A1)
| |
| -$002A -42 GetIcon (name,icon,freelist) (A0,A1,A2)
| |
| -$0030 -48 PutIcon (name,icon) (A0,A1)
| |
| -$0036 -54 FreeFreeList (freelist) (A0)
| |
| -$003C -60 FreeWBOject (WBOject) (A0)
| |
| -$0042 -66 AllocWBOject ()
| |
| -$0048 -72 AddFreeList (freelist,mem,size) (A0,A1,A2)
| |
| -$004E -78 GetDiskObject (name) (A0)
| |
| -$0054 -84 PutDiskObject (name,diskobj) (A0,A1)
| |
| -$005A -90 FreeDiskObj (diskobj) (A0)
| |
| -$0060 -96 FindToolType (toolTypeArray,typeName) (A0.A1)
| |
| -$0066 -102 MatchToolValue (typeString,value) (A0,A1)
| |
| -$006C -108 BumbRevision (newname,oldname) (A0,A1)
| |
|
| |
| INTUITION.LIBRARY
| |
| -$001E -30 OpenIntuition ()
| |
| -$0024 -36 Intuition (ievent) (A0)
| |
| -$002A -42 AddGadget (AddPtr,Gadget,Position) (A0,A1,D0)
| |
| -$0030 -48 ClearDMRequest (Window) (A0)
| |
| -$0036 -54 ClearMenuStrip (Window) (A0)
| |
| -$003C -60 ClearPointer (Window) (A0)
| |
| -$0042 -66 CloseScreen (Screen) (A0)
| |
| -$0048 -72 CloseWindow (Window) (A0)
| |
| -$004E -78 CloseWorkbench ()
| |
| -$0054 -84 CurrentTime (Seconds,Micros) (A0,A1)
| |
| -$005A -90 DisplayAlert (AlertNumber,String,Height)
| |
| (D0,A0,D1)
| |
| -$0060 -96 DiplayBeep (Screen) (A0)
| |
| -$0066 -102 DoubleClick (sseconds,smicros,cseconds,
| |
| cmicros) (D0,D1,D2,D3)
| |
| -$006C -108 DrawBorder (Rport,Border,LeftOffset,TopOffset)
| |
| (A0,A1,D0,D1)
| |
| -$0072 -114 DrawImage (RPort,Image,LeftOffset,TopOffset)
| |
| (A0,A1,D0,D1)
| |
| -$0078 -120 EndRequest (requester,window) (A0,A1)
| |
| -$007E -126 GetDefPref (preferences,size) (A0,D0)
| |
| -$0084 -132 GetPrefs (preferences,size) (A0,D0)
| |
| -$008A -138 InitRequester (req) (A0)
| |
| -$0090 -144 ItemAddress (MenuStrip,MenuNumber) (A0,D0)
| |
| -$0096 -150 ModifyIDCMP (Window,Flags) (A0,D0)
| |
| -$009C -156 ModifyProp (Gadget,Ptr,Reg,Flags,HPos,VPos,
| |
| HBody,VBody) (A0,A1,A2,D0,D1,D2,D3,D4)
| |
| -$00A2 -162 MoveScreen (Screen,dx,dy) (A0,D0,D1)
| |
| -$00A8 -168 MoveWindow (Window,dx,dy) (A0,D0,D1)
| |
| -$00AE -174 OffGadget (Gadget,Ptr,Req) (A0,A1,A2)
| |
| -$00B4 -180 OffMenu (Window,MenuNumber) (A0,D0)
| |
| -$00BA -186 OnGadget (Gadget,Ptr,Req) (A0,A1,A2)
| |
| -$00C0 -192 OnMenu (Window,MenuNumber) (A0,D0)
| |
| -$00C6 -198 OpenScreen (OSArgs) (A0)
| |
| -$00CC -204 OpenWindow (OWArgs) (A0)
| |
| -$00D2 -210 OpenWorkBench ()
| |
| -$00D8 -216 PrintIText (rp,itext,left,top) (A0,A1,D0,D1)
| |
| -$00DE -222 RefreshGadgets (Gadgets,Ptr,Req) (A0,A1,A2)
| |
| -$00E4 -228 RemoveGadgets (RemPtr,Gadget) (A0,A1)
| |
| -$00EA -234 ReportMouse (Window,Boolean) (A0,D0)
| |
| -$00F0 -240 Request (Requester,Window) (A0,A1)
| |
| -$00F6 -246 ScreenToBack (Screen) (A0)
| |
| -$00FC -252 SCreenToFront (Screen) (A0)
| |
| -$0102 -258 SetDMRequest (Window,req) (A0,A1)
| |
| -$0108 -264 SetMenuStrip (Window,Menu) (A0,A1)
| |
| -$010E -270 SetPointer (Window,Pointer,Height,Width,
| |
| XOFFset, YOFFset) (A0,A1,D0,D1,D2,D3)
| |
| -$0114 -276 SetWindowTitles (Window,windowTitle,
| |
| screenTitle) (A0,A1,A2)
| |
| -$011A -282 ShowTitle (Screen,ShowIt) (A0,D0)
| |
| -$0120 -288 SizeWindow (Windowmdx,dy) (A0,D0,D1)
| |
| -$0126 -294 ViewAddress ()
| |
| -$012C -300 ViewPortAddress (Window) (A0)
| |
| -$0132 -306 WindowToBack (Window) (A0)
| |
| -$0138 -312 WindowToFront (Window) (A0)
| |
| -$013E -318 WindowLimits (Window,minwidth,minheight,
| |
| maxwidth, maxheight) (A0,D0,D1,D2,D3)
| |
| -$0144 -324 SetPrefs (preferences,size,flag) (A0,D0,D1)
| |
| -$014A -330 IntuiTextLength (itext) (A0)
| |
| -$0150 -336 WBenchToBack ()
| |
| -$0156 -342 WBenchToFront ()
| |
| -$015C -348 AutoRequest (Window,Body,PText,NText,PFlag,
| |
| NFlag,W,H) (A0,A1,A2,A3,D0,D1,D2,D3)
| |
| -$0162 -354 BeginRefresh (Window) (A0)
| |
| -$0168 -360 BuildSysRequest (Window,Body,PosText,NegText,
| |
| Flags,W,H) (A0,A1,A2,A3,D0,D1,D2)
| |
| -$016E -366 EndRefresh (Window,Complete) (A0,D0)
| |
| -$0174 -372 FreeSysRequest (Window) (A0)
| |
| -$017A -378 MakeScreen (Screen) (A0)
| |
| -$0180 -384 RemakeDisplay ()
| |
| -$0186 -390 RethinkDisplay ()
| |
| -$018C -396 AllocRemember (RememberKey,Size,Flags) (A0,D0,D1)
| |
| -$0192 -402 AlohaWorkBench (wbport) (A0)
| |
| -$0198 -408 FreeRemember (RememberKey,ReallyForgot) (A0,D0)
| |
| -$019E -414 LockIBase (dontknow) (D0)
| |
| -$01A4 -420 UnlockIBase (IBLock) (A0)
| |
|
| |
| LAYERS.LIBRARY
| |
| -$001E -30 InitLayers (li) (A0)
| |
| -$0024 -36 CreateUpfrontLayer (li,bm,x0,y0,xl,yl,flags,
| |
| bm2) A0,A1,D0,D1,D2,D3,D4,A2)
| |
| -$002A -42 CreateBehindLayer (li,bm,x0,y0,xl,yl,flags,
| |
| bm2) (A0,A1,D0,D1,D2,D3,D3,A2)
| |
| -$0030 -48 UpfrontLayer (li,layer) (A0,A1)
| |
| -$0036 -54 BehindLayer (li,layer) (A0,A1)
| |
| -$003C -60 MoveLayer (li,layer,dx,dy) (A0,A1,D0,D1)
| |
| -$0042 -66 SizeLayer (li,layer,dx,dy) (A0,A1,D0,D1)
| |
| -$0048 -72 ScrollLayer (li,layer,dx,dy) (A0,A1,D0,D1)
| |
| -$004E -78 BeginUpdate (layer) (A0)
| |
| -$0054 -84 EndUpdate (layer) (A0)
| |
| -$005A -90 DeleteLayer (li,layer) (A0,A1)
| |
| -$0060 -96 LockLayer (li,layer) (A0,A1)
| |
| -$0066 -102 UnlockLayer (li,layer) (A0,A1)
| |
| -$006C -108 LockLayers (li) (A0)
| |
| -$0072 -114 UnlockLayers (li) (A0)
| |
| -$0078 -120 LockLayerInfo (li) (A0)
| |
| -$007E -126 SwapBitRastPortClipRect (rp,cr) (A0,A1)
| |
| -$0084 -132 WhichLayer (li,x,y) (A0,D0,D1)
| |
| -$008A -138 UnlockLayerInfo (li) (A0)
| |
| -$0090 -144 NewLayerInfo ()
| |
| -$0096 -150 DisposeLayerInfo (li) (A0)
| |
| -$009C -156 FattenLayerInfo (li) (A0)
| |
| -$00A2 -162 ThinLayerInfo (li) (A0)
| |
| -$00A8 -168 MoveLayerInfrontOf (layer_to_move,
| |
| layer_to_be_in_front_of) (A0,A1)
| |
|
| |
| MATHFFP.LIBRARY
| |
| -$001E -30 SPFix (float) (D0)
| |
| -$0024 -36 SPFit (integer) (D0)
| |
| -$002A -42 SPCmp (leftFloat,right,Float) (D1,D0)
| |
| -$0030 -48 SPTst (float) (D1)
| |
| -$0036 -54 SPAbs (float) (D0)
| |
| -$003C -60 SPNeg (float) (D0)
| |
| -$0042 -66 SPAdd (leftFloat,rightFloat) (D1,D0)
| |
| -$0048 -72 SPSub (leftFloat,rightFloat) (D1,D0)
| |
| -$004E -78 SPMul (leftFloat,rightFloat) (D1,D0)
| |
| -$0054 -84 SPDiv (leftFloat,rightFloat) (D1,D0)
| |
|
| |
| MATHIEEEDOUBBAS.LIBRARY
| |
| -$001E -30 IEEEDPFix (integer,integer) (D0,D1)
| |
| -$0024 -36 IEEEDPFit (integer) (D0)
| |
| -$002A -42 IEEEDPCamp (integer,integer,integer,integer)
| |
| (D0,D1,D2,D3)
| |
| -$0030 -48 IEEEDPTst (integer,integer) (D0,D1)
| |
| -$0036 -54 IEEEDPAbs (integer,integer) (D0,D1)
| |
| -$003C -60 IEEEDPNeg (integer,integer) (D0,D1)
| |
| -$0042 -66 IEEEDPAdd (integer,integer,integer,integer)
| |
| (D0,D1,D2,D3)
| |
| -$0048 -72 IEEEDPSub (integer,integer,integer,integer)
| |
| (D0,D1,D2,D3)
| |
| -$004E -78 IEEEDPMul (integer,integer,integer,integer)
| |
| (D0,D1,D2,D3)
| |
| -$0054 -84 IEEEDPDiv (integer,integer,integer,integer)
| |
| (D0,D1,D2,D3)
| |
|
| |
| MATHTRANS.LIBRARY
| |
| -$001E -30 SPAtan (float) (D0)
| |
| -$0024 -36 SPSin (float) (D0)
| |
| -$002A -42 SPCos (float) (D0)
| |
| -$0030 -48 SPTan (float) (D0)
| |
| -$0036 -54 SPSincos (leftFloat,rightFloat) (D1,D0)
| |
| -$003C -60 SPSinh (float) (D0)
| |
| -$0042 -66 SPCosh (float) (D0)
| |
| -$0048 -72 SPTanh (float) (D0)
| |
| -$004E -78 SPExp (float) (D0)
| |
| -$0054 -84 SPLog (float) (D0)
| |
| -$005A -90 SPPow (leftFloat,rightFloat) (D1,D0)
| |
| -$0060 -96 SPSqrt (float) (D0)
| |
| -$0066 -102 SPTieee (float) (D0)
| |
| -$006C -108 SPFieee (float) (D0)
| |
| -$0072 -114 SPAsin (float) (D0)
| |
| -$0078 -120 SPAcos (float) (D0)
| |
| -$007E -126 SPLog10 (float) (D0)
| |
|
| |
| POTGO.LIBRARY
| |
| -$0006 -6 AllocPotBits (bits) (D0)
| |
| -$000C -12 FreePotBits (bits) (D0)
| |
| -$0012 -18 WritePotgo (word,mask) (D0,D1)
| |
|
| |
| TIMER.LIBRARY
| |
| -$002A -42 AddTime (dest,src) (A0,A1)
| |
| -$0030 -48 SubTime (dest,src) (A0,A1)
| |
| -$0036 -54 CmpTime (dest,src) (A0,A1)
| |
|
| |
| TRANSLATOR.LIBRARY
| |
| -$001E -30 Translate (inputString,inputLength,
| |
| outputBuffer,bufferSize) (A0,D0,A1,D1)
| |
|
| |
|
| |
| Abbreviations (symbols) used:
| |
|
| |
| label A label or address
| |
| reg Register
| |
| an Address register n
| |
| dn Data register n
| |
| source source operand
| |
| dest destination operand
| |
| <ea> address of register
| |
| #n direct value
| |
|
| |
|
| |
| GENERAL INSTRUCTIONS
| |
|
| |
| BCC label Conditional branch, depends on condition.
| |
| BRA label Unconditional branch (Similar to JMP).
| |
| BSR label Branch to subprogram. Return address is
| |
| deposited on the stack, RTS causes return to
| |
| that address.
| |
| CHK <ea>,dx Check data register for limits, activate the
| |
| CHK instruction exception.
| |
| DBCC reg,label Check condition, decrement on branch.
| |
| JMP label Jump to address (Similar to BRA).
| |
| JSR label Jump to a subroutine. Return address is deposited
| |
| on stack, RTS causes return to that address.
| |
| NOP No operation.
| |
| RESET Reset peripherals (Caution!).
| |
| RTE Return from exception.
| |
| RTR Return with loading of flags.
| |
| RTS Return from subroutine (After BSR or JSR).
| |
| SCC <ea> Set a byte to -1 when condition is met.
| |
| STOP Stop processing (Caution!).
| |
| TRAP #n Jump to an exception.
| |
| TRAPV Check overflow flag, then TRAPV exception.
| |
|
| |
|
| |
| ARITHMETIC OPERATIONS WITH WHOLE NUMBERS
| |
|
| |
| ADD source,dest Binary addition.
| |
| ADDA source,an Binary addition to an address register.
| |
| ADDI #n,<ea> Addition with a constant.
| |
| ADDQ #n,<ea> Fast addition of a constant which can be only
| |
| from 1 to 8.
| |
| ADDX source,dest Addition with transfer in X flag.
| |
| CLR <ea> Clear an operand.
| |
| CMP source,dest Comparison of two operands.
| |
| CMPA <ea>,an Comparison with an address register.
| |
| CMPI #n,<ea> Comparison with a constant.
| |
| CMPM source,dest Comparison of two memory operands.
| |
| DIVS source,dest Sign-true division of a 32 bit destination by
| |
| a 16 bit source operand. The result of the
| |
| division is stored in the LO word of the
| |
| destination, the remainder in the HI word.
| |
| DIVU source,dest Division without regard to sign, similar to DIVS.
| |
| EXT dn Sign-true expansion to twice original size
| |
| (width) data unit.
| |
| MULS source,dest Sign-true multiplication of two words into one
| |
| word.
| |
| MULU source,dest Multiplication without regard to sign, similar
| |
| to MULS.
| |
| NEG <ea> Negation of an operand (twos complement).
| |
| SUB source,dest Binary subtraction.
| |
| SUBA <ea>,an Binary subtraction from an address register.
| |
| SUBI #n,<ea> Subtraction of a constant.
| |
| SUBQ #n,<ea> Fast subtraction of a three bit constant.
| |
| SUBX source,dest Subtraction with transfer in X flag.
| |
| TST <ea> Test operand and set N and Z flag.
| |
|
| |
|
| |
| BINARY CODED DECIMAL NUMBERS
| |
|
| |
| ABCD source,dest Addition of two binary coded decimal numbers.
| |
| NBCD source,dest Negation of a binary coded decimal number
| |
| (nine complement).
| |
| SBCD source,dest Subtraction of two binary coded decimal numbers.
| |
|
| |
|
| |
| LOGICAL OPERATIONS
| |
|
| |
| AND source,dest Logic AND.
| |
| ANDI #n,<ea> Logic AND with a constant.
| |
| EOR source,dest Exclusive OR.
| |
| EORI #n,<ea> Exclusive OR with a constant.
| |
| NOT <ea> Inversion of an operand.
| |
| OR source,dest Logic OR.
| |
| ORI #n,<ea> Logic OR with a constant.
| |
| TAS <ea> Check a byte and set bit 7.
| |
|
| |
|
| |
| SINGLE BIT MANIPULATION
| |
|
| |
| BCHG #n,<ea> Change bit n(0 is changed to 1 and vice versa).
| |
| BCLR #n,<ea> Clear bit n.
| |
| BSET #n,<ea> Set bit n.
| |
| BTST #n,<ea> Test bit n, result is desplayed in Z flag.
| |
|
| |
|
| |
| SHIFT AND ROTATE OPERANDS
| |
|
| |
| NOTE: n indicates a register, # indicates a direct value which
| |
| specifies the number of shiftings.
| |
|
| |
| AS n,<ea> Arithmetic shift to the left (*2^n)
| |
| ASR n,<ea> Arithmetic shift to the right (/2^n)
| |
| LSL n,<ea> Logic shift to the left.
| |
| LSR n,<ea> Logic shift to the right.
| |
| ROL n,<ea> Rotation left.
| |
| ROR n,<ea> Rotation right.
| |
| ROXL n,<ea> Rotation left with transfer in X flag.
| |
| ROXR n,<ea> Rotation right with transfer in X flag.
| |
|
| |
|
| |
| MOVE DATA INSTRUCTIONS
| |
|
| |
| EXG rn,rn Exchange two register contents (don't confuse
| |
| with swap!).
| |
| LEA <ea>,an Load an effective address in address register an.
| |
| LINK an,#n Build stack range.
| |
| MOVE source,dest Carry value over from source to destination.
| |
| MOVE SR,<ea> Transfer the status register contents.
| |
| MOVE <ea>,SR Transfer the status register contents.
| |
| MOVE USP,<ea> Transfer the user stack pointer.
| |
| MOVE <ea>,USP Transfer the user stack pointer.
| |
| MOVEA <ea>,an Transfer a value to the address register an.
| |
| MOVEM regs,<ea> Transfer several registers at once.
| |
| MOVEM <ea>,regs Transfer several registers at once.
| |
| MOVEP source,dest Transfer data to peripherals.
| |
| MOVEQ #n,dn Quickly transfer an eight bit constant to the
| |
| data register dn.
| |
| PEA <ea> Deposit an address on the stack.
| |
| SWAP dn Swap the halves of the register (the upper 16 bit
| |
| with the lower).
| |
| UNLK an Unlink the stack.
| |