1. Timing Delays
The following loop uses two Bank Registers to provide a precisely timed
delay:
mov Ra, DelayLo
mov Rb, DelayHi
Loop:
djnz Ra, Loop
djnz Rb, Loop
To calculate the delay, use the formula:
TimeDelay = ( 12 * ( DelayLo + ( 257 * DelayHi ) - 254 )) / Freq
To determine the correct values for the two eight bit "Delay" values, I
first find the highest "DelayHi" value that does not exceed the desired
delay (with "DelayLo" set to zero).
2. Table Operations
Using a standard table read (with the "movc A,@A+PC" instruction), up to
255 table entries can be returned from a byte index value. To return all
the corresponding table value for a byte (all 256 different values), the
following subroutine could be used:
Read_Table: ; Read the Table Entry from "A"
inc A ; Point to Offset in Table/Get
; Test for 0FFh
jnzRead_Table_Get
mov A,#Entry_0FFh ; Return the 0FFh Entry
ret
InitGetTable: ; Table Offset to Display
movc A,@A+PC
ret
InitTable: ; The Control Store Table for
db ... ; Initializing "Array" - 255 Entries
3. Hex to Ascii Conversion
This is a port of my PICMicro code to convert a four bit nybble to it's
corresponding ASCII Character ("0"-"9", "A"-"F"):
ByteToASCII: ; Convert the Byte in "A" to two ASCII
; characters in "B" and "A"
mov B,A ; Save the Byte for Low Nybble Conversion
swap A ; Do High Nybble Conversion
anl A,#00Fh
acall NybbleToASCII
xch A,B ; Save the ASCII of the High Byte
anl A,#00Fh ; Do Conversion on the Low Nybble
acall NybbleToASCII
ret
To convert the individual nybbles to ASCII, the following subroutine is
used:
NybbleToASCII: ; Convert the Contents of "ACC" to an
; ASCII Character
add A,#036h ; If ( A & 0x00Fh ) + 6 > 0x010h
jnb AC,Skip
add A,#7 ; Add 7 to Jump to "A" to "F"
Skip:
subb A,#6 ; Take Away Flag Set
ret
4. Loading the "Encryption Array"
If there is any unused EPROM or the Encryption Array is left unprogrammed,
somebody (ie a Pirate) could figure out what the EPROM or encryption array
contents were by reading out the EPROM contents and checking to see if
what's read back can be executed directly or, if the encryption array is
programmed, looking for a repeating pattern.
To prevent this, a non-repeating pattern should be put into both the
Encryption Array as well as the unused EPROM.
The following conditional assembler code could be used:
ProgEnd EQU $ ; Identify the End Address of the Code
while ( $ < DEVEND ) ; Repeat to the End Address of the Device
db ( ProgEnd + (( $ - ProgEnd ) * 3 ) & 0FFh
wend
If the code above was put at the end of an 8051's program, the remainder
of control store memory, no matter how large would be filled with a pattern
increasing by three for each byte and starting with the least significant
byte of the address after the end of the program. By incrementing by three,
the pattern won't repeat at the same address for 768 bytes. This can be
increased by using a larger prime number to multiply the current address
by.
5. Circular Buffers
In some applications which require incoming data to be processed serially
and data may come in before the microcontroller can devote the cycles to
processing it, the incoming data has to be "buffered" (saved) for some
period of time. Ideally, the algorithms used for saving and retrieving
this data, as well as maintaining the buffer should be as simple and fast
executing as possible. Like most problems, there are many ways of solving
it, with one of the best being the "circular buffer".
A circular buffer is a memory area that has been set aside for an index
to store data and another index to retrieve the data. Once an index has
reached the end of the buffer, it is reset to the start of the buffer.
In this way, the buffer is "circular" in that it never ends.
To "Push" data into such a buffer, the following code could be used:
Push: ; Push the Contents of the Accumulator into the
; Circular Buffer
mov @R0,A ; Save the Contents of A into Buffer Pointed to
; by R0
inc R0 ; Point to the next Location in the buffer
cjne R0,#BufferEnd+1,PushEnd
mov R0,#BufferStart ; If at the End of the Buffer, Point to the
; Start
PushEnd:
ret
After "Push" has gone through each address, the data pointer ("R0") is
reset to the start of the buffer. I used "R0" as the index because it can
be compared to and updated without affecting the contents of the accumulator
or PSW register.
This can be improved by sizing and locating the buffer in such a way
that it's starting address has a "0" at the least significant bit before
the bits used to address within the buffer. For a sixteen byte circular
buffer, having bit four of the address always reset will allow the "Push"
routine to be simplified to:
Push: ; Push the Contents of the Accumulator into the
; Circular Buffer
mov @R0,A ; Save the Contents of A into Buffer Pointed to
; by R0
inc R0 ; Point to the next Location in the buffer
anl R0,#(BufferStart+(BufferSize-1))
ret
copyright © Myke Predko 1998
|