3D1-Microprocessor Systems 1 Lecture 17: Top-Down Design Using Subroutines A common mechanism for the re-use of a block of instructions is to incorporate it into a subroutine. The subroutine is given a name and is supplied with the input parameters of the problem and returns the results as output parameters. Thus if the solution of a problem has been neatly encapsulated into a subroutine which has been comprehensively tested and debugged then it can be incorporated into subsequent programs. This obviously saves much work and removes some sources of error. In this lecture, we will apply a top-down approach to develop a program that reads and evaluates simple expressions involving unsigned word integers and the usual arithmetic operators (+, -, * and /). Learning Outcomes: On completion of this lecture, you will be able to: Discuss the advantages of the top-down approach; Decompose complex problems into small modular subroutines. 17.1 The top-down approach When solving large problems it is usually necessary to split the problem down into a series of sub-problems, which in turn may be split into further sub-problems etc. This is usually called a top-down approach. This process continues until problems become of such a size that they can be solved by a single programmer. This top-down approach is essential if the work has to be shared out between a team of programmers, each programmer ending up with a specification for a part of the system which is to be written as a subroutine (or subroutines). While writing a single subroutine the programmer is able to concentrate on the solution of this one problem only and is thus more likely to be able to solve the problem and make fewer errors. This subroutine can then be tested on its own for correctness. 17.2 Example Write a subroutine to read and evaluate simple expressions involving unsigned word integers and the usual arithmetic operators (+, -, * and /). The expressions are stored as NUL-terminated ASCII character sequences of the form: [integer][space] [operator][space] [integer] [end] Where [space] is a single ASCII space character (ASCII code: 0100000). For example: 651 * 70 A solution could be presented as follows: Get first number Skip space character Get operator Skip space character Get second number Compute operation Save result Then each task can be broken down into smaller tasks: 17-1 3D1-Microprocessor Systems 1 Get Number: Push the address of the ASCII code string of digits into the stack Push the address where to save the binary number on stack Repeat Test if character is a digit If Yes then do convertion Else goto exit Exit save binary number in the address loaded on the stack Save address of character pointer on the stack Skip space: increment the character pointer Get Operator: Save the contents of memory pointed at by the character pointer in a Dn Increment the character pointer Compute operation: If operator = ‘+’ Then add Else if operator = ‘-’ Then subtract Else if operator = ‘*’ Then multiply Else if operator = ‘/’ then divide End if End if End if End if 17.3 Program implementation in 68000 Assembly Language: op1 op2 RSLT ORG DS.W DS.W DS.L $2000 1 1 1 origin of reserve 1 reserve 1 reserve 1 num ORG DC.B $2100 '671 * 70',$0 origin of input data define the expression to evaluate ORG PEA PEA BSR LEA MOVE.L MOVE.B ADDA.L MOVE.L PEA BSR MOVE.W MOVE.W BSR $4000 num op1 origin of main program push the reference to num into the stack push the reference to op1 into the stack GetNum get the 1st number 4(A7),A7 (A7),A0 (A0),D5 #2,A0 A0,(A7) op2 clean up the stack by removing op1 load top of stack into A0 save operator in D5 skip space character load character pointer to the stack push the reference to op1 into the stack GetNum get the 2nd number op1,D2 op2,D3 action copy 1st operand in D2 copy 2nd operand in D3 compute operation and save result in RSLT TRAP #0 return control of the CPU to Monitor main return data word for 1st operand word for 2nd operand longword for operation result 17-2 3D1-Microprocessor Systems 1 ORG GetNum MOVEM.L MOVEQ LEA MOVE.L MOVEQ CMP.B BNE MOVEQ ADDA.L loop exit $3000 D0-D2/A0-A2,-(A7) #0,D0 (A7),A1 32(A1),A0 #1,D2 #'-',(A0) loop #-1,D2 #1,A0 (A0)+,D1 #$30,D1 exit #$3A,D1 exit #$30,D1 #10,D0 D1,D0 loop D2,D0 28(A1),A2 (A2),A2 D0,(A2) A0,32(A1) (A7)+,D0-D2/A0-A2 copy ascii char in D1 * * *if char not a digit then exit * else subtract $30 from ascii code [D0] -> [D0]*10 add [D1] and [D0] goto loop [D0] -> [D0]*[±1] Get pointer to address of operand Get actual address of operand save operand in memory save pointer in stack Restore working registers return to caller ORG $3200 D2/D3/D5,-(A7) #'+',D5 compute the operation CMP.B BEQ CMP.B BEQ CMP.B BEQ CMP.B BEQ BRA Plus #'-',D5 Minus #'*',D5 Mult #'/',D5 Div return ADD.L BRA D2,D3 Minus SUB.L D2,D3 Mult Div copy addr top of stack in A1 (local stack pointer) * * *check for minus sign before number * * * MOVE.B CMP.B BMI CMP.B BPL SUB.B MULU ADD.L BRA MULS LEA MOVEA.L MOVE.W MOVE.L MOVEM.L RTS action MOVEM.L Plus convert sting of ascii digits into binary save working registers on the stack clear D0 return BRA return MULU BRA D2,D3 DIVU AND.L D2,D3 #$0000FFFF,D3 return MOVE.L MOVEM.L RTS return mask remainder D3,RSLT (A7)+,D2/D3/D5 END 17-3 3D1-Microprocessor Systems 1 17.4 Conclusion The top-down approach to program design splits the initial problem into several subproblems which in turn can be further sub-divided. Once a sub-problem is simple enough to be solved then it can be implemented as a subroutine. A subroutine should be completely self-contained. That is it should communicate with the calling program only via supplied input parameters and output parameters. REFERENCES Clements; A Four-function Calculator, In: 68000 Family Assembly Language; pp.255264; PWS Publishing Company; 1994. Dr. Mike Brady, Microprocessor Systems 1, dept of Computer Science, Trinity College Dublin: http://www.tcd.ie/Engineering/Courses/BAI/JS_Subjects/3D1/. Look on the Web at http://www.mee.tcd.ie/~assambc/3D1. 17-4