Dynamic subroutinesThere's nothing mysterious about dynamic subroutines. Basically they are quite the same as the subroutines we saw on the previous page. The only difference is that we can change their behaviour by sending some parameters to them. Wouldn't it be nice if we had a delay routine that could be used for several purposes, with varying delay times? That is easy enough, we simply pass a parameter to the subroutine instructing it how long it should delay. Here's a program that does that: |
;------------------------------------------------------------------------ ; ; LESSON3C.ASM ; ; A Kitt scanner, with a dynamic subroutine ; ;------------------------------------------------------------------------ .IN INIT01 Initialize the assembler ;------------------------------------------------------------------------ LXI SP,00000H Initialize stack pointer MVI D,00011111B Initialize pattern MVI E,11111111B MOV H,E MOV L,E WALKR CALL OUTPUT Output both bit patterns and delay MVI B,25 Delay 25 * 5 ms CALL DELAY MOV A,L See if we have to change direction ANI 01000000B JZ GOLEFT We must if b6 is a 0! GORIGHT STC Set the carry MOV A,H Rotate entire pattern, including RAR the extra carry byte MOV H,A MOV A,D RAR MOV D,A MOV A,E RAR MOV E,A MOV A,L RAR MOV L,A JMP WALKR Always jump back to WALKR ;------------------------------------------------------------------------ WALKL CALL OUTPUT Output both bit patterns and delay MVI B,25 Delay 25 * 5 ms CALL DELAY MOV A,H See if we have to change direction ANI 000000010B JZ GORIGHT We must if b1 is a 0! GOLEFT STC Set the carry MOV A,L Rotate entire pattern, including RAL the extra carry byte MOV L,A MOV A,E RAL MOV E,A MOV A,D RAL MOV D,A MOV A,H RAL MOV H,A JMP WALKL Always jump back to WALKL ;------------------------------------------------------------------------ ; Delay B * 5 ms ; No registers are affected ;------------------------------------------------------------------------ DELAY PUSH PSW Save affected registers to the stack PUSH B (AF and BC) DELAY1 MVI C,250 Load inner loop counter DELAY2 NOP Begin of inner loop NOP NOP NOP NOP JMP $+3 NOP NOP NOP NOP DCR C Decrement inner loop counter JNZ DELAY2 Repeat until loop counter = 0 DCR B Decrement outer loop counter JNZ DELAY1 Repeat until loop counter = 0 POP B Restore affected registers from the POP PSW stack (in reverse order!) RET Return to calling program ;------------------------------------------------------------------------ OUTPUT MOV A,E Output the bit pattern to port A OUT PORTA MOV A,D Output the bit pattern to port B OUT PORTB RET Return to calling program |
What I've done in this program is to load the B register with the desired delay time (measured in 5 ms intervals) prior to calling the DELAY routine. This way you can use the same DELAY routine for a variety of delay times, from 5 ms until more than on second.
This dynamic subroutine only requires one parameter, others may need two, 4, or even more.
It is fine for subroutines with a limited number of parameters pass them using the internal registers of the 8085.
I have taken the liberty to make some minor changes to the DELAY routine itself, mainly to demonstrate the strength of the stack.
If you look a bit closer you see that I've changed the inner loop of the delay routine.
This has nothing to do with making the subroutine dynamic, I only wanted to double the inner loop to approximately 5 ms.
Doubling the time required me to add 30 clock pulses to the inner loop.
This could not be done by adding just NOP instructions, because a NOP takes 4 clock pulses to execute.
That is why I used 5 extra NOPs, totaling to 20 clock pulses, followed by a JMP $+3 instruction which takes another 10 clock pulses.
|
A new INIT program
Our include program INIT01.ASM has lasted a couple of experiments.
Do you still remember that it is still there, and absolutely essential!
If it weren't there we would have to initialize the assembler, the I/O constants and the 8255 in each and every experiment over and over again.
INIT01.ASM does that all for us, without us worrying about it.
|
;------------------------------------------------------------------------ ; ; INIT02.ASM ; ; Initialise experiments with the basic I/O board and define DELAY ; ;------------------------------------------------------------------------ .CR 8085 The processor we're using .CP 1,9600,N,8,1 SITCOM is connected to COM1 .TF COM1:,INT Send generated code to COM1 ;------------------------------------------------------------------------ ; ; Constants declarations ; ;------------------------------------------------------------------------ PORTA .EQ 000H Port A on the 8255 PORTB .EQ 001H Port B on the 8255 PORTC .EQ 002H Port C on the 8255 CONTROL .EQ 003H Control register of the 8255 ;------------------------------------------------------------------------ ; ; Initialize the 8255 ; ;------------------------------------------------------------------------ RESET JMP START Skip the subroutine definitions ;------------------------------------------------------------------------ ; Delay B * 5 ms ; No registers are affected ;------------------------------------------------------------------------ DELAY PUSH PSW Save affected registers to the stack PUSH B (AF and BC) DELAY1 MVI C,250 Load inner loop counter DELAY2 NOP Begin of inner loop NOP NOP NOP NOP JMP $+3 NOP NOP NOP NOP DCR C Decrement inner loop counter JNZ DELAY2 Repeat until loop counter = 0 DCR B Decrement outer loop counter JNZ DELAY1 Repeat until loop counter = 0 POP B Restore affected registers from the POP PSW stack (in reverse order!) RET Return to calling program ;------------------------------------------------------------------------ ; Here's the new start of the program ;------------------------------------------------------------------------ START MVI A,10001001B Select outputs for Ports A and B OUT CONTROL of the 8255, Port C are inputs LXI SP,00000H Initialize the stack |
Basically INIT02.ASM is quite similar to INIT01.ASM.
The assembler is initialized, the I/O addresses are defined and the 8255 is initialized.
I have also added a line to initialize the stack, as we will be needing that in almost every program that follows.
Our DELAY subroutine is now constantly assembled in all subsequent lessons, ready for us to use it with any delay time we desire from 5 ms, up until 1.275 seconds. |
Binary counting demo
As a bonus, to test our new INIT02.ASM prgoram, I add a small program that counts from 0 up to 65535 in binary.
|
;------------------------------------------------------------------------ ; ; LESSON3D.ASM ; ; Binary counting demo ; ;------------------------------------------------------------------------ .IN INIT02 Initialize the assembler ;------------------------------------------------------------------------ LXI D,00000H Initialize counting value BINCOUNT MOV A,D Get Most significant byte CMA Invert all bits OUT PORTB Before outputting it to port B MOV A,E Do the same for the Least significant CMA byte OUT PORTA MVI B,50 Delay 50 * 5 ms CALL DELAY INX D Increment the 16-bit register BC JMP BINCOUNT Always jump back to BINCOUNT |
First of all, be careful to use INIT02 this time as init file, otherwise you won't have the necessary DELAY routine.
Short program, isn't it?
It starts by initializing a 16-bit register DE.
Feel free to change the delay time if you don't like the counting speed. Or you can make the program count backwards, by replacing the INX D instruction by a DCX D instruction.
Experiment tips: |
Continue To Lesson 4 - Selective output |
[Home] [Latest News] [Essentials] [Hardware] [The Build] [Programs] [Projects] [Downloads] | |||