|
|
1. sor: |
1. sor: |
| AMIGA MACHINE LANGUAGE | | 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]]) |
| + | |
| + | * [[Amiga_Machine_Language_(Chapter_1)|Chapter 1 - Introduction]] |
| + | * [[Amiga_Machine_Language_(Chapter_2)|Chapter 2 - The MC68000 processor]] |
| + | * [[Amiga_Machine_Language_(Chapter_3)|Chapter 3 - Working with assemblers]] |
| + | * [[Amiga_Machine_Language_(Chapter_4)|Chapter 4 - Our first programs]] |
| + | * [[Amiga_Machine_Language_(Chapter_5)|Chapter 5 - Hardware registers]] |
| + | * [[Amiga_Machine_Language_(Chapter_6)|Chapter 6 - The Amiga operating system]] |
| + | * [[Amiga_Machine_Language_(Chapter_7)|Chapter 7 - Working with Intuition]] |
| + | * [[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]] |
| | | |
| | | |
- | Table of Contents.
| + | Original contents table, will be gone as chapters will get Wiki formatting: |
- | ------------------
| + | |
- | 1. Introduction
| + | |
- | 1.1 Why machine code?
| + | |
- | 1.2 A look into the Amiga's memory
| + | |
- | 1.2.1 RAM,ROM,hardware register
| + | |
- | 1.2.2 Bits,bytes and words
| + | |
- | 1.2.3 Number systems
| + | |
- | 1.3 Inside the Amiga
| + | |
- | 1.3.1 Components and libraries
| + | |
- | 1.3.2 Memory
| + | |
- | 1.3.3 Multi-tasking
| + | |
| | | |
| 2 The MC68000 processor | | 2 The MC68000 processor |
91. sor: |
93. sor: |
| Overview of library functions | | Overview of library functions |
| Overview of the MC68000 Instructions | | 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
| |