Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 4: Advanced Assembly Programming

Similar presentations


Presentation on theme: "Chapter 4: Advanced Assembly Programming"— Presentation transcript:

1 Chapter 4: Advanced Assembly Programming
The HCS12 Microcontroller Han-Way Huang Minnesota State University, Mankato September 2009 1

2 Chapter Summery The same sequence of instructions often need to be executed in several places of the program. The subroutine mechanism enables the programmer to group the common instruction sequence into a subroutine. A subroutine can be called from many places. The processor saves the return address in the stack during a subroutine call. The subroutine restores the return address from the stack before it returns to the caller. The subroutine can receive parameters from the caller to perform operations and may return result to the caller. Top-down design with hierarchical refinement is the most effective and popular software development methodology.

3 Introduction - Program = data structures + algorithm
- Data structures to be discussed 1. stacks: a last-in-first-out data structure 2. arrays: a set of elements of the same type 3. strings: a sequence of characters terminated by a special character Stack 3

4 HCS12 Support for the Stack Data Structure
- A 16-bit stack pointer (SP) - Instructions and addressing mode 4

5 Example 4.1 Derive the stack contents after the execution of the following instruction
Sequence: lds #$15C0 ldaa #$20 psha ldab #40 pshb ldx #0 pshx Solution: 5

6 What is a Subroutine? A sequence of instructions that can be called from many places of the program The program flow during a subroutine call is shown in Figure 4.3. Saving and restoring return address is the key for the subroutine mechanism to work. 6

7 Instructions that Support Subroutine Call
bsr <sub>: branch subroutine <sub> is specified in relative addressing mode and is in the range of -128 to +127 bytes from the instruction immediately after the bsr <sub> instruction. The return address is pushed into the stack. jsr <sub>: jump subroutine <sub> is specified in direct, extended, indexed, and indexed indirect mode. The subroutine being called can be within the range of 64 kB. The return address is pushed into the stack. call <sub>: call subroutine The <sub> field specifies the page number and the address of the subroutine within the page to be called. The page number is to be loaded into the PPAGE register. rts: return from subroutine restrieve the return address from the stack and return to the caller. rtc: return from call This instruction retrieve the page number and the return address from the stack. 7

8 and remainder. The procedure is as follows: Step 1
Example 4.2 Write a subroutine that converts a 16-bit binary number into a BCD ASCII string. The 16-bit number is passed in D and the pointer to the buffer to hold the string is passed in Y. Solution: Let num, ptr, quo, and rem represent the number to be converted pointer, quotient, and remainder. The procedure is as follows: Step 1 Push a 0 into the stack. quo ¬ num. Step 2 rem ¬ quo % 10; quo ¬ quo / 10. Step 3 Push the sum of $30 and rem into the stack. Step 4 If (quo == 0) goto Step 5; else goto step 2. Step 5 Pull a byte from stack and save it in the location pointed to by ptr. Step 6 ptr ¬ ptr + 1. Step 7 If the byte pulled in Step 5 is NULL, stop; else goto Step 5. 8

9 bin2dec pshx ; save X in stack
movb #0,1,-SP ; push NULL character into stack divloop ldx #10 ; divide the number by 10 idiv ; “ addb #$30 ; convert the remainder to ASCII code pshb ; push it into the stack xgdx ; swap quotient to D cpd #0 ; if quotient is 0, then prepare to pull out beq revloop ; the decimal digit characters bra divloop ; quotient is not 0, continue to perform divide-by-10 revloop pula ; start to reverse the string from stack staa 1,y+ ; save ASCII string in the buffer cmpa #0 ; reach the NULL pushed previously? beq done ; if yes, then done bra revloop ; continue to pup done pulx ; restore the index register X rts

10 Issues related to Subroutine Calls 1. Parameter Passing
Use CPU registers Use stack Use global memory 2. Result Returning 3. Local Variable Allocation Allocated in the stack Use the instruction leas -n,SP to allocate n bytes of space in stack Use the instruction leas n, SP to deallocate n bytes from stack

11 Saving CPU Registers in Stack
The subroutine may use some CPU registers that were used by its caller. The subroutine must save these registers in order to make sure the caller program can obtain correct result The subroutine must restore the saved registers before returning to the caller. The order of register restoration must be reversed to that to which registers are saved. if the saving order is pshx pshy pshd then the restoring order is puld puly pulx

12 Stack Frame - The region in the stack that holds incoming parameters, the subroutine return address, local variables, and saved registers is referred to as stack frame. The stack frame is also called activation record. 12

13 leas –10,sp instruction is executed: ldd #$1234 pshd ldx #$4000 pshx
Example 4.3 Draw the stack frame for the following program segment after the leas –10,sp instruction is executed: ldd #$1234 pshd ldx #$4000 pshx jsr sub_xyz sub_xyz pshd pshy leas -10,sp Solution: The stack frame is shown Figure 4.10. 13

14 Subroutines with Local Variables
Example 4.4 Write a subroutine that can convert a BCD ASCII string to a binary number and leave the result in double accumulator D. The ASCII string represents a number in the range of -215 ~ 215 – 1. A pointer to the string is passed to this subroutine in X. Solution: The subroutine will return an error indication using the CY flag of the CCR register. Let in_ptr, sign, error, and number represent the pointer to the BCD string, sign flag, error flag, and the number represented by the BCD string. The algorithm is: Step 1 sign ¬ 0; error ¬ 0; number ¬ 0. Step 2 If the character pointed to by in_ptr is the NULL character, then go to step 4. else if the character is not a BCD digit, then: error ¬ 1; go to step 4; else number ¬ number × 10 + m[in_ptr] - $30; in_ptr ¬ in_ptr + 1; go to step 3;

15 Step 4 If sign = 1 and error = 0, then number ¬ two’s complement of number; else stop; This subroutine allows local variables in the stack and its stack is shown in Figure 4.6.

16 #include "c:\miniide\hcs12.inc"
minus equ $2D ; ASCII code of minus sign dummy equ 0 ; offset of dummy to hold a 0 pdVal equ 1 ; offset of present digit value from SP in stack val equ 2 ; offset of the 2-byte binary value of the BCD string ; from SP in stack sign equ 4 ; offset of the sign from SP in stack err equ 5 ; offset of error flag from SP in stack locVar equ 6 org $1000 StrBuf dc.b "-9889",0 ; input ASCII to be converted result ds.w 1 org $1500 start lds #$1500 ldx #StrBuf jsr bcd2bin std result swi ; ; This subroutine converts a BCD string into its equivalent binary value and also uses the ; CY flag to indicate error condition. The CY flag is set to 1 to indicate error. ;

17 bcd2bin pshy leas -locVar,SP ; allocate 4 bytes for local variables movw #0,val,SP ; initialize accumulated value to 0 movb #0,dummy,SP movb #0,sign,SP ; initialize sign to positive movb #0,err,SP ; clear error flag initially ldaa 0,x ; check the first character cmpa #minus ; is the first character a minus sign? bne GEZero ; branch if not minus movb #1,sign,SP ; set the sign to 1 inx ; move the pointer GEZero ldab 1,x+ ; is the current character a NULL character? lbeq done ; yes, we reach the end of the string cmpb #$30 ; is the character not between 0 to 9? blo inErr ; " cmpb #$39 ; " bhi inErr ; " subb #$30 ; convert to the BCD digit value stab pdVal,SP ; save the current digit value ldd val,SP ; get the accumulated value ldy #10 emul ; perform 16-bit by 16-bit multiplication

18 addd dummy,SP ; add the current digit value
std val,SP ; save the sum bra GEZero inErr movb #1,err,SP ; set the error flag to indicate error bra chkout done ldaa sign,SP ; check to see if the original number is negative beq chkout ldd #$FFFF ; convert to two's complement format subd val,SP ; if the number is negative addd #1 ; " std val,SP ; " chkout ldaa err,SP ; check the error flag beq clrErr ; go to clear CY flag before return sec ; clear the C flag bra dealloc clrErr clc ; set the C flag dealloc ldd val,SP leas locVar,SP ; deallocate local variables puly ; restore Y rts ; org $FFFE ; uncomment these two lines for CodeWarrior ; dc.w start ; “ end

19 Bubble Sort Sorting is useful for improving the searching speed when an array or a file needs to be searched many times. Bubble sort is a simple but inefficient sorting method. Example 4.13 Write a subroutine to implement the bubble sort algorithm and a sequence of instructions for testing the subroutine. Solution: The algorithm of bubble sort is shown in Figure 4.7. The bubble sort subroutine uses the following local variables: Buf: a buffer for swapping elements InOrder: a flag indicating whether the array is in order Inner: no of comparisons remained to be performed in the current iteration Iteration: number of iterations remained to be performed

20

21 Stack Frame for bubble Sort

22 arr equ 13 ; distance of the variable arrayX from stack top
arcnt equ 12 ; distance of the variable arcnt from stack top buf equ 3 ; distance of local variable buf from stack top inOrder equ 2 ; distance of local variable inOrder from stack top inner equ 1 ; distance of local variable inner from stack top iteration equ 0 ; distance of local variable iteration from stack top true equ 1 false equ 0 n equ 30 ; array count local equ 4 ; number of bytes used by local variables org $1000 ; test data arrayX dc.b 3,29,10,98,54,9,100,104,200,92,87,48,27,22,71 dc.b 1,62,67,83,89,101,190,187,167,134,121,20,31,34,54 org $1500 start lds #$1500 ; initialize stack pointer ldx #arrayX pshx ldaa #n psha jsr bubble leas 3,sp ; deallocate space used by outgoing parameters ; bra $ ; uncomment this instruction under CodeWarrior swi ; break to D-Bug12 monitor

23 bubble pshd pshy pshx leas -local,sp ; allocate space for local variables ldaa arcnt,sp ; compute the number of iterations to be performed deca ; " staa iteration,sp ; " ploop ldaa #true ; set array inOrder flag to true before any iteration staa inOrder,sp ; " ldx arr,sp ; use index register X as the array pointer ldaa iteration,sp ; initialize inner loop count for each iteration staa inner,sp ; " cloop ldaa 0,x ; compare two adjacent elements cmpa 1,x ; " bls looptst ; the following five instructions swap the two adjacent elements staa buf,sp ; swap two adjacent elements ldaa 1,x ; " staa 0,x ; " ldaa buf,sp ; " staa 1,x ; " ldaa #false ; reset the inOrder flag

24 looptst inx dec inner,sp bne cloop tst inOrder,sp ; test array inOrder flag after each iteration bne done dec iteration,sp bne ploop ; the following instruction deallocates local variables done leas local,sp ; de-allocate local variables pulx puly puld rts ; org $FFFE ; uncomment this line for CodeWarrior ; dc.w start ; uncomment this line for CodeWarrior end

25 Binary Search Subroutine
Divide the array into three parts: upper half, middle element, and lower half. Let min, man, mean be the minimum, maximum, and middle indices of the subarray to be searched. The algorithm of binary search is as follows: Step 1 min <- 0; max = n – 1; Step 2 If (max < min), stop. Step 3 Mean <- (min + max)/2 Step 4 If (key == arr[mean]), then key is found and exit. Step 5 If (key < arr[mean]), then set max to mean – 1 and go to Step 2. Step 6 if (key < arr[mean]), then set min to mean + 1 and go to Step 2.

26 n equ 30 ; array count srch equ 69 ; key to be searched mean equ 0 ; stack offset for local variable mean min equ 1 ; stack offset for local variable min max equ 2 ; stack offset for local variable max key equ 8 ; stack offset for local variable key arrCnt equ 9 ; stack offset for local variable arrCnt arrBas equ 10 ; stack offset for local variable arrBas locvar equ 3 ; number of bytes for local variables org $1000 result ds.b 1 ; search result

27 org $1500 lds #$1500 movw #arr,2,-SP ; pass array base address movb #n,1,-SP ; pass array count movb #srch,1,-SP ; pass key for search jsr binsearch leas 4,SP ; de-allocate space used in passing parameters staa result ; bra $ ; uncomment this instruction for CodeWarrior swi binSearch pshx ; save X in the stack pshb ; save B in the stack leas -locVar,SP ; allocate space for locVar variables movb #0,min,SP ; initialize min to 0 ldaa arrCnt,SP ; initialize max to arCnt - 1 deca ; " staa max,SP ; " ldx arrBas,SP ; use X as the pointer to the array loop ldab min,SP ; is search over yet? cmpb max,SP ; " lbhi notfound ; if min > max, then not found (unsigned comparison)

28 addb max,SP ; compute mean
lsrb ; " stab mean,SP ; save mean ldaa b,x ; get a copy of the element arr[mean] cmpa key,SP ; compare key with array[mean] beq found ; found it? bhi searchLO ; continue to search in lower half ldaa mean,SP ; prepare to search in upper half inca ; " staa min,SP ; " bra loop searchLO ldaa mean,SP ; set up indices range for searching in the deca ; lower half staa max,SP ; " found ldaa #1 bra exit notfound ldaa #0 exit leas locVar,SP pulb pulx rts

29 Subroutine that perform 32-bit division
arr db 1,3,6,9,11,20,30,45,48,60 db 61,63,64,65,67,69,72,74,76,79 db 80,83,85,88,90,110,113,114,120,123 ; org $FFFE ; uncomment this line for CodeWarrior ; dc.w start ; uncomment this line for CodeWarrior end Subroutine that perform 32-bit division

30 Algorithm for Multi-byte Division
Step 1 icnt ß n; R ß 0; Q ß dividend; P ß divisor. Step 2 Shift the register pair (R, Q) one bit to the left. Step 3 Subtract P from R, put the result back to R if the result is nonnegative. Step 4 If the result of Step 2 is negative, then set the lsb of Q to 0. Otherwise, set the lsb to 1. Step 5 icnt ß icnt – 1. Step 6 If (icnt == 0) then stop; else go to Step 2 Example 4.7 Write a subroutine to implement the division algorithm using the repeated subtraction method for a 32-bit unsigned dividend and divisor. The caller of the subroutine allocate space in the stack for this subroutine to return the quotient and remainder and then push the dividend and divisor into the stack before calling this subroutine.

31 buf equ 0 ; distance of buf from the top of the stack
i equ 4 ; distance of i from the top of the stack R equ 5 ; distance of R from the top of the stack Q equ 9 ; distance of Q from the top of the stack divisor equ 21 ; distance of divisor from the top of the stack dividend equ 25 ; distance of dividend from the top of the stack quo equ 29 ; distance of quo from the top of the stack rem equ 33 ; distance of rem from the top of the stack locVar equ 13 ; number of bytes for local variables

32 dvdendHI equ $42 ; dividend to be tested
dvdendLO equ $4c15 ; " dvsorHI equ $0 ; divisor to be tested dvsorLO equ $64 ; " org $1000 quotient ds.b 4 ; memory locations to hold the quo remain ds.b 4 ; memory locations to hold the remainder org $1500 ; starting address of the program start lds #$1500 ; initialize stack pointer leas -8,SP ; make a hole of 8 bytes to hold the result ldd #dvdendLO pshd ldd #dvdendHI ldd #dvsorLO ldd #dvsorHI jsr div32 ; call the divide subroutine leas 8,SP ; de-allocate space used by dividend & divisor movw 2,sp+,quotient ; pull upper half of quotient movw 2,sp+,quotient+2 ; pull lower half of quotient

33 movw 2,sp+, quotient ; pull upper half of the quotient from the stack
movw 2,sp+, quotient+2 ; pull lower half of the quotient from the stack bra $ div32 pshd pshx pshy leas -locVar,sp ; allocate space for local variables ldd #0 std R,sp ; initialize R to 0 std R+2,sp ; “ ldd dividend,sp ; place dividend in register Q std Q,sp ldd dividend+2,sp std Q+2,sp movb #32,i,sp ; initialize loop count dloop lsl Q+3,sp ; shift (R,Q) to the left by 1 bit rol Q+2,sp ; “ rol Q+1,sp ; “ rol Q,sp ; “ rol R+3,sp ; “ rol R+2,sp ; “ rol R+1,sp ; “ rol R,sp ; “

34 ldd R+2,sp subd divisor+2,sp std buf+2,sp ldaa R+1,sp sbca divisor+1,sp staa buf+1,sp ldaa R,sp sbca divisor,sp bcs smaller ; the following six instructions store the difference back to R register staa R,sp ldaa buf+1,sp staa R+1,sp ldd buf+2,sp std R+2,sp bset Q+3,sp,$01 ; set the least significant bit of Q register to 1 bra looptest smaller bclr Q+3,sp,$01 ; set the least significant bit of Q register to 0 looptest dec i,sp lbne loop ; the following four instructions copy the remainder into the hole in the stack ldd R,sp std rem,sp

35 ldd R+2,sp std rem+2,sp ; the following four instructions copy the quotient into the hole in the stack ldd Q,sp std quo,sp ldd Q+2,sp std quo+2,sp leas locVar,sp ; deallocate local variables puly pulx puld rts ; org $FFFE ; uncomment this line for CodeWarrior ; dc.w start ; uncomment this line for CodeWarrior end

36 Finding the Square Root
The successive approximation method can be used to find the square root of an integer. Step 1 sar ß 0; mask ß $8000; lpcnt ß 16; temp ß 0. Step 2 temp ß sar OR mask Step 3 If ((temp * temp) £ num) sar ¬ temp; Step 4 mask ß mask >> 1 (shift right one bit) Step 5 lpcnt ß lpcnt – 1 Step 6 If (lpcnt == 0) then stop; else go to Step 2. Example 4.8 Write a subroutine that implements the successive approximation method to find the square root of a 32-bit integer.

37 #include "c:\miniide\hcs12.inc"
mask equ 0 ; stack offset of the variable mask from SP sar equ 2 ; stack offset of the variable sar from SP temp equ 4 ; stack offset of the variable temp from SP lpcnt equ 6 ; stack offset of one-byte loop count from SP q_hi equ 13 ; stack offset of the upper and lower halves of the q_lo equ 15 ; number Q we want to find the square root from SP locVar equ 7 testHi equ $00 ; upper half of the test number (q) testLo equ $7BC4 ; lower half of the test number (q) org $1000 sqroot ds.w 1 ; square root

38 org $1500 start lds #$1500 movw #testLo,2,-SP ; push testHi into stack movw #testHi,2,-SP ; push testLo into stack jsr findsqr std sqroot ; save the returned square root leas 4,SP ; de-allocate the space used in passing parameters ; bra $ ; uncomment this line for CodeWarrior swi ; findsqr pshx pshy leas -locVar,SP ; allocate space for local variables movw #0,sar,SP ; initialize SAR to 0 movw #$8000,mask,SP ; initialize mask to $8000 movb #16,lpcnt,SP ; initialize loop count to 16 iloop ldd mask,SP ; get the mask oraa sar,SP ; set a bit in SAR to 1 orab sar+1,SP ; " std temp,SP ; save a copy of your guess tfr D,Y ; compute sar * sar emul ; "

39 cpy q_hi,SP ; is our guess correct?
bhi nextb ; " cpd q_lo,SP ; " ldd temp,SP ; yes, our guess is correct std sar,SP ; so, transfer temp to SAR nextb lsr mask,SP ; shift mask to the right one place ror mask+1,SP ; " dec lpcnt,SP bne iloop ldd sar,SP ; put sar in D as the approximated square root leas locVar,SP ; de-allocate local variables puly pulx rts ; org $FFFE ; uncomment this line for CodeWarrior ; dc.w start ; uncomment this line for CodeWarrior end The square root computed using this method tends to be smaller than the actual square root and hence may not be the closet approximation and the user should compare (sar + 1) and sar to find the closest approximation.

40 Subroutine for Prime Testing
The most efficient method for testing whether an integer is a prime is to divide the given number by all the prime numbers from 2 to the square root of the given number. Since the prime numbers from 2 to the square root of the given number are not available, we can test divide the given number by all the numbers from 2 to its square root. Example 4.9 Write a subroutine that can test whether an unsigned integer is a prime number. Solution: test_hi equ $0638 ; number to be tested for prime test_lo equ $F227 ; "   org $1000 isprime ds.b 1   org $1500 start lds #$1500 ; set up stack pointer movw #test_lo,2,-SP ; push the lower half of the test number movw #test_hi,2,-SP ; push the upper half of the test number jsr PrimeTest staa isprime ; bra $ ; uncomment this line for CodeWarrior swi

41 ii equ 0 ; stack offset from SP of loop index
tlimit equ 2 ; stack offset from SP of test limit pNumHi equ 10 ; stack offset from SP of upper half of test number pNumLo equ 12 ; stack offset from SP of lower half of test number pLocal equ 4 ; number of bytes used by local variables primeTest pshx pshy leas -pLocal,SP ; allocate space for local variables ldaa pNumLo+1,SP ; check if the number is even (if bit 0 is 0) anda #$01 ; " beq nonPRI ; " ldd pNumHi,SP cpd #0 bne testPR ; upper half nonzero, then enter normal test ldd pNumLo,SP ; if upper half is 0, then test lower half cpd #0 ; is lower half equal to 0? beq nonPri ; 0 is not a prime cpd #1 ; is lower half equal to 1 beq nonPri ; 1 is not a prime testPR ldd pNumLo,SP ; find the square root of Num ldx pNumHi,SP ; “ pshd ; “ pshx ; “

42 jsr findsqr ; “ leas 4,SP ; de-allocate space for passing parameters std tlimit,SP ; save returned value as the prime test limit movw #2,ii,SP ; initialize test divide number to 3 divLoop ldd ii,SP cpd tlimit,SP ; has test divided all numbers up to tlimit? bhi isPRI ; the number is prime ldd pNumLo,SP ; divide Num by ii ldx pNumHi,SP ; “ ldy ii,SP ; “ leas -8,SP ; “ pshd ; “ (push pNumLo) pshx ; “ (push pNumHi) pshy ; “ (push ii) movw #0,2,-SP ; “ (push 0 to the stack) jsr div32 ; “ (call the divide subroutine) leas 14,SP ; de-allocate the space used by outgoing parameters puld ; get the lower two bytes of the remainder cpd #0 ; is remainder equal to 0? beq nonPRI ; If remainder equals 0, then Num is not a prime ldd ii,SP ; test divide the next higher integer addd #1 ; “ std ii,SP ; “

43 bra divLoop isPRI ldaa #1 bra exitPT nonPRI ldaa #0 exitPT leas pLocal,SP puly pulx rts #include "c:\miniide\findsqr.asm" #include "c:\miniide\div32.asm" ; org $FFFE ; uncomment this line for CodeWarrior ; dc.w start ; uncomment this line for CodeWarrior end

44 Using the D-Bug12 Functions to Perform I/O Operations

45 - All functions listed in Table 4.2 are written in C language.
- The first parameter to the function is passed in accumulator D. The remaining parameters are pushed onto the stack in the reverse order they are listed in the function declaration. - Parameters of type char will occupy the lower order byte of a word pushed onto the stack. - Parameters pushed onto the stack before the function is called remain on the stack when the function returns. It is the responsibility of the caller to remove passed parameters from the stack. - All 8- and 16-bit values are returned in accumulator D. A returned value of type char is returned in accumulator B. int getchar (void) Pointer address: $EE84 Returned value: 8-bit character in accumulator B

46 Adding the following instruction sequence will read a character from SCI0 port:
getchar equ $EE84 jsr [getchar,PCR] int putchar(int) Pointer address: $EE86 Incoming parameter: character to be output in accumulator B Returned vale: the character that was sent (in B) - This function outputs a character to serial communication port SCI0. - The character to be output should be placed in accumulator B. - The following instruction sequence will output the character A to serial port SCI0 when the program is running on the Dragon12 demo board. putchar equ $EE86 ldab #$41 ; place the ASCII code of A in accumulator B jsr [putchar,PCR]

47 int printf(char *format,…)
Pointer address: $EE88 Incoming parameters: zero or more integer data to be output on the stack, D contains the address of the format string. The format string must be terminated with a zero. Returned value: number of characters printed in D. - This function is used to convert, format, and print its argument as standard output under the control of the format string pointed to by format. - All except floating-point data types are supported. - The format string contains two basic types of objects: 1. ASCII characters which will be copied directly to the display device. 2. Conversion specifications. Each conversion specification begins with a percent sign (%). 3. Optional formatting characters may appear between the percent sign and ends with a single conversion character in the following order: [-][<FieldWidth>][.][<Precision>][h | l]

48 The Meaning of Optional Characters

49 Formatting Characters Supported by the printf() function:
Example for outputting a message (Flight simulation): CR equ $0D LF equ $0A printf equ $EE88 ldd #prompt jsr [printf,PCR] prompt db “Flight simulation”,CR,LF,0

50 Example for outputting three numbers m, n, and gcd along with a message:
CR equ $0D LF equ $0A printf equ $F686 ldd gcd pshd ldd n ldd m ldd #prompt jsr [printf,PCR] leas 6,sp prompt db “The greatest common divisor of %d and %d is %d”,CR,LF,0

51 int far GetCmdLine(char *CmdLineStr, int CmdLineLen)
Pointer address: $EE8A Incoming parameters: a pointer to the buffer where the input string is to be stored and the maximum number of characters that will be accepted by this function. This function is used to obtain a line of input from the user. The reception of an ASCII carriage return ($0D) terminates the reception of characters from the user. The caller of this function normally would output a message so that the user knows to enter a message. The example in the next page illustrates this interaction.

52 printf equ $EE88 GetCmdLine equ $EE8A cmdlinelen equ 100 CR equ $0D LF equ $0A prompt db “Please enter a string: “,CR,LF,0 inbuf ds.b 100 ldd #prompt ; output a prompt to remind the user to jsr [printf,PCR] ; enter a string ldd #cmdlinelen ; push the CmdLineLen pshd ; “ ldd #inbuf call [GetCmdLine,PCR] ; read a string from the keyboard puld ; clean up the stack

53 Example 4.15 Write a program that invokes appropriate functions to find the prime number between 1000 and Output eight prime numbers in one line. To do this, you will need to 1.--   write a subroutine to test if an integer is a prime. 2.--   invoke the printf() function to output the prime number. 3.--   write a loop to test all the integers between 1000 and 2000. Solution: The logic structure of the program is Step 1 Output the message “The prime numbers between 1000 and 2000 are as follows:”. Step 2 For every number between 100 and 1000 do 1. -   call the test_prime() function to see if it is a prime. 2. -   output the number (call printf()) if it is a prime. 3. -   if there are already eight prime numbers in the current line, then also output a carriage return.

54 The algorithm of the test_prime()  
Step 1 Let num, i, and isprime represent the number to be tested, the loop index, and the flag to indicate if num is prime. Step 2 isprime ¬ 0; Step 3 For i = 2 to num/2 do if num % i = 0 then return; isprime ¬ 1; The Stack frame of the function test_prime ():

55 CR equ $0D LF equ $0A upper equ 2000 ; upper limit for testing prime lower equ 1000 ; lower limit for testing prime printf equ $EE88 ; location where the address of printf() is stored org $1000 out_buf ds.b 10 PRIcnt ds.b 1 ; prime number count k ds.b 2 tmp ds.b 2 org $1500 start ldx #upper stx tmp pshx ldx #lower stx k ; initialize k to 1000 for prime testing ldd #form0 jsr [printf,PCR] leas 4,sp clr PRIcnt again ldd k cpd #upper

56 bhi Pstop ; stop when k is greater than upper
pshd ldd #0 jsr primetest ; test if k is prime leas 4,sp ; de-allocate space used by outgoing parameters tsta beq next_k ; test next integer if k is not prime inc PRIcnt ; increment the prime count ldd k ldd #form1 jsr [printf,PCR] ; output k leas 2,sp ldaa PRIcnt cmpa #8 ; are there eight prime numbers in the current line? blo next_k ; output a CR, LF if there are already eight prime numbers in the current line ldd #form2 jsr [printf,PCR] clr PRIcnt next_k ldx k

57 inx stx k lbra again ; Pstop bra $ ; uncomment this line for CodeWarrior Pstop swi ; comment this line for CodeWarrior #include "c:\miniide\primetest.asm" #include "c:\miniide\div32.asm" #include "c:\miniide\findsqr.asm" form0 db CR,LF,"The prime numbers between %d and %d are as follows: " db CR,LF,CR,LF,0 form1 db " %d ",0 form2 db " ",CR,LF,0 ; org $FFFE ; uncomment this line for CodeWarrior ; dc.w start ; uncomment this line for CodeWarrior end

58 Program Execution Result
The prime numbers between 1000 and 2000 are as follows: User Bkpt Encountered PP PC SP X Y D = A:B CCR = SXHI NZVC D1 15DE :D xx: PSHX >

59 Subroutines for Creating Time Delays
Example 4.11 Write a subroutine that can create a time delay of 100 ms. Solution: Delay100ms pshx ldx #60000 ; 2 E cycles iloop psha ; 2 E cycles pula ; 3 E cycles psha ; 2 E cycles nop ; 1 E cycle dbne x,iloop pulx rts

60 100 ms as follows (the multiple is passed in Y):
The previous subroutine can be modified to create a time delay that is a multiple of 100 ms as follows (the multiple is passed in Y): delayby100ms pshx eloop3 ldx #60000 ; 2 E cycles iloop3 psha ; 2 E cycles pula ; 3 E cycles psha ; 2 E cycles nop ; 1 E cycle dbne x,iloop3 ; 3 E cycles dbne y,eloop3 ; 3 E cycles pulx rts

61 Introduction to Parallel I/O Port and Simple I/O Devices
A HCS12 device may have from 48 to 144 pins arranged in 3 to 12 I/O Ports. An I/O port consists of a set of I/O pins and the registers required to control its operation. An I/O pin can be configured for input or output. An I/O pin usually serves multiple functions. When it is not used as a peripheral function, it can be used as a general-purpose I/O pin. Addressing the I/O Port Data Register When inputting or outputting data from or to an I/O port, the user read from or write to the port data register. Each I/O port register is assigned to an address in the HCS12 memory space. For example, Port A data register is assigned to address 0, the next instruction output $35 to Port A: movb #$35,0 ; address 0 is Port A data register A name is also assigned to each register so that we can access a register by referring to its name: PTA equ 0 movb #$35,PTA ; output $35 to Port A The name and address association is established by including the HCS12.inc file to your program using the “#include c:\...\hcs12.inc” statement.

62 Configuring I/O Pin Direction
Each I/O port has a data direction register DDRx (x is the port name) that configures the direction of each pin. By setting a bit of the DDRx register to 1, the corresponding I/O pin is configured for output. By setting a bit of the DDRx register to 0, the corresponding I/O pin is configured for input. movb #$FF,DDRB ; configure Port A for output movb #0,DDRB ; configure Port B for input movb #$AA,DDRB ; configured Port A odd pins for output, even pins for input Input and Output Operations movb #$FF,DDRB ; configure Port B for output staa PTB ; output the contents of A to port B movb #$67,PTB ; output the value $67 to Port B movb #0,DDRC ; configure Port C for input movb PTC,ibuf ; read the contents of Port C and save it at the memory ; location represented by ibuf

63 The HCS12 I/O ports and Pin Names

64 Interfacing with LEDs An LED has an anode and cathode terminal.
The anode terminal must at a voltage at least 1.6 V above that of the cathode terminal (forward biased) in order for the LED to be lighted. The forward current required to light an LED is from a few to more than 10 mA. The I/O pin can drive an LED using one of the circuits shown in Figure 4.15. The resistors R1, R2, are R3 are called current-limiting resistors.

65 LED Circuit in the Draong12-Plus Demo Board
Example 4.12 Write a program to drive the LEDs shown in Figure 4.16 so that one LED is lighted at a time from top to bottom and then from bottom to top with each LED lighted for about 200 ms.

66 #include "C:\miniide\hcs12.inc"
org $1000 lpcnt ds.b 1 org $1500 start movb #$FF,DDRB ; configure port B for output bset DDRJ,$02 ; configure PJ1 pin for output bclr PTJ,$02 ; enable LEDs to light forever movb #16,lpcnt ; initialize LED pattern count ldx #led_tab ; Use X as the pointer to LED pattern table led_lp movb 1,x+,PORTB ; turn on one LED ldy #5 ; wait for half a second jsr delayby100ms ; " dec lpcnt ; reach the end of the table yet? bne led_lp bra forever ; start from beginning led_tab dc.b $80,$40,$20,$10,$08,$04,$02,$01 dc.b $01,$02,$04,$08,$10,$20,$40,$80 #include "C:\miniide\delay.asm" ; org $FFFE ; uncomment this line for CodeWarrior ; dc.w start ; uncomment this line for CodeWarrior end

67 Interfacing with Seven-Segment Displays
Seven-segment displays are mainly used to display decimal digits and a small set of letters. The HCS12 I/O port can drive a seven-segment display directly. Buffer chips are used mainly to save excessive current draw from the HCS12. A example circuit for interfacing with seven-segment display is shown in Figure 4.17. Some people may use PB0 to PB6 to drive segment a to g instead. The microcontroller must send an appropriate value to the output in order to display a certain value.

68 Displaying Multiple Seven-Segment Displays
A time-multiplexing technique is often used to display multiple digits. The circuit in Figure 4.18 can display up to six digits simultaneous using the time- multiplexing technique. The HCS12 lights one digit for a short period of time and then switches to the next digit. Within one second, each digit is lighted in turn many times. Due to the persistence of vision, all six digits appear to be lighted at the same time.

69 To display 7 on the display #5
movb #$FF,DDRB ; configure Port B for output movb #$3F,DDRP ; configure Port P for output movb #$1F,PTP ; enable display #5 to be lighted movb #$07,PTB ; send out the segment pattern of 7

70 Example 4.14 Write a program to display 123456 on the six seven-segment displays
shown in Figure 4.18. Solution: The values 1,2,3,4,5, and 6 are displayed on display #5, #4, …, #0, respectively. A table is set up to control the pattern and digit selection as shown in Table 4.8. #include "c:\miniide\hcs12.inc" org $1500 start lds #$1500 movb #$FF,DDRB movb #$3F,DDRP forever ldx #DispTab ; set X to point to the display table loopi movb 1,x+,PTB ; output segment pattern movb 1,x+,PTP ; output display select ldy #1 jsr delayby1ms ; wait for 1 ms

71 Generating a Digital Waveform Using an I/O Pin
cpx #DispTab+12 ; reach the end of the table? bne loopi bra forever #include "c:\miniide\delay.asm" DispTab dc.b $06,$1F dc.b $5B,$2F dc.b $4F,$37 dc.b $66,$3B dc.b $6D,$3D dc.b $7D,$3E ; org $FFFE ; uncomment this line for CodeWarrior ; dc.w start ; uncomment this line for CodeWarrior end Generating a Digital Waveform Using an I/O Pin A square wave can be generated by pulling a pin to high for certain amount of time and then pull it to low for the same amount of time and repeat. By connecting the pin to a speaker and making the frequency in the audible range, a sound can be made.

72 Example 4.15 Write a program to generate a 1-kHz periodic square wave from the PT5 pin.
Solution: #include "c:\miniide\hcs12.inc" org $1500 start lds #$1500 bset DDRT,BIT5 ; configure PT5 pin for output forever bset PTT,BIT5 ; pull PT5 pin to high ldy #10 ; wait for 0.5 ms jsr delayby50us ; “ bclr PTT,BIT5 ; pull PT5 pin to low bra forever #include "c:\miniide\delay.asm" ; org $FFFE ; dc.w start end Example 4.16 Write a program to generate a two-tone siren that alternates between 250 Hz and 500 Hz with each tone lasting for half of a second.

73 #include "c:\miniide\hcs12.inc"
org $1500 start lds #$1500 bset DDRT,BIT5 ; configure PT5 pin for output forever ldx #250 ; repeat 500 Hz waveform 250 times tone1 bset PTT,BIT5 ; pull PT5 pin to high ldy #1 jsr delayby1ms bclr PTT,BIT5 dbne x,tone1 ldx #125 ; repeat 250 Hz waveform for 125 times tone2 bset PTT,BIT5 ldy #2 dbne x,tone2 bra forever #include "c:\miniide\delay.asm" end

74 Interfacing with DIP Switches
A dual-in-line package can be connected any port with 8 pins. A set of pull-up resistors are needed to pull the voltage to high on one side of the DIP. To read data into accumulator A movb #0,DDRA ; configure port A for input ldaa PTA ; read into accumulator A

75 Tips for Program Debugging Involving Subroutine Calls
What to do when the program gets stuck? Step 1 Find out which subroutine gets stuck by setting a breakpoint immediately after the jsr or bsr instruction. Step 2 Find out why the subroutine gets stuck. - Forgetting to restore registers pushed onto the stack before return. - Forgetting to deallocate local variables before return. - There are some infinite loops in the subroutine. - Calling other subroutines that do not return.

76 General Debugging Strategy
1. Make sure all leaf subroutines work correctly by using the methods described in Section 2.9. 2. Debug intermediate subroutines. Make sure no intermediate subroutines get stuck. 3. Debug the top level program.


Download ppt "Chapter 4: Advanced Assembly Programming"

Similar presentations


Ads by Google