|

Among the
microcontrollers I worked with, the Zilog Z8 series is my personal favorite.
It’s fast, attractively priced, and its register based architecture makes it
a delightful thing to program.
Yet, one single
feature most of us are not happy with is its OTP – One Time
Programmability. OTP means you don’t have a second chance. Once you make a
programming mistake; that IC goes straight to the trash.
It is
heartbreaking to throw away an otherwise good IC, just because of that
little bitchy bug in the program that could have been be easily fixed by
inserting or changing a couple of lines in your code.
The good news
is, following some simple rules coupled with smart programming techniques,
you can actually beat this limitation! You can “reprogram” the OTP device
several times. How many times? This will depend on your programming skill
and the amount of free OTP ROM available.
Theory
The
un-programmed z8 microcontroller memory will contain %FF (all bits are at
logic 1). When we program the z8, we actually ‘blow’ these bits to logic 0.
Once changed to 0, it stays there forever- no amount reprogramming can
return its state back to 1.
The trick to
make the z8 OTP reprogrammable is to reserve an area in the program memory
which we could later reprogram into a JP (JumP) instruction. Ideally, we
should leave this reserve space to its un-programmed value of %FF.
Fortunately,
the un-programmed memory content %FF corresponds to the machine code NOP (No
OPeration). Since NOP is a do nothing instruction, we can liberally place
any number of these instructions anywhere in our program without affecting
the logic of the program (inserting an %FF anywhere in the program will not
change the program logic).
Let me
illustrate this point with an example. This simple program
Test:
Dec %34
Jr ne,Test
Will not change
its operation or function(Note 1) if we add NOPs as shown below:
Test:
Nop
Nop
Nop
Nop
Nop
Nop
Dec %34
Nop
Nop
Nop
Jr ne,Test
Keeping in mind
that the three consecutive NOPs are actually translated by the assembler (ZDS)
into three consecutive %FFs, we can further rewrite this program as follows:
Test:
.byte %FF,%FF,%FF
.byte %FF,%FF,%FF
Dec %34
.byte %FF,%FF,%FF
jr ne,Test
If for any
reason, we decided to scrap this routine, we could easily find a way around
it by replacing the first three %FF into a jp (or jr) instruction:
Test:
.byte %FF,%FF,%FF
Jp Test_Fixed
; JP instruction occupies 3 bytes
; the three %FF previously reserved on these
; locations are used to reprogram the device
; with this instruction
Dec
%34 ; Following instructions are skipped
.byte %FF,%FF,%FF ; and
will no longer affect the program
jr ne,Test
; Program Fix
Test_Fixed:
; Program Fix starts here
.
.
.
Writing
codes for re-programmability
Armed with this
technique, we can now formulate a standard program format that will make our
z8 reprogrammable and reusable:
.org 0
; The interrupt vectors points to a intermediate %FF filled locations
.word Int0
.word Int1
.word Int2
.word Int3
.word Int4
.word Int5
; MCU Cold Start begins here
.org %0c
ei
di
.byte %ff,%ff,%ff ; replace last with jp instruction
.byte %ff,%ff,%ff ; replace 3rd
.byte %ff,%ff,%ff ; replace 2nd
.byte %ff,%ff,%ff ; replace 1st
jp Cold_Start ; Jump to actual start of program
Int0:
.byte %ff,%ff,%ff ; replace last with jp instruction
.byte %ff,%ff,%ff ; replace 3rd
.byte %ff,%ff,%ff ; replace 2nd
.byte %ff,%ff,%ff ; replace 1st
jp Interrupt_0 ; Jump to actual Int0 routine
Int1:
.byte %ff,%ff,%ff ; replace last with jp instruction
.byte %ff,%ff,%ff ; replace 3rd
.byte %ff,%ff,%ff ; replace 2nd
.byte %ff,%ff,%ff ; replace 1st
jp Interrupt_1 ; Jump to actual Int1 routine
Int2:
.byte %ff,%ff,%ff ; replace last with jp instruction
.byte %ff,%ff,%ff ; replace 3rd
.byte %ff,%ff,%ff ; replace 2nd
.byte %ff,%ff,%ff ; replace 1st
jp Interrupt_2 ; Jump to actual Int2 routine
Int3:
.byte %ff,%ff,%ff ; replace last with jp instruction
.byte %ff,%ff,%ff ; replace 3rd
.byte %ff,%ff,%ff ; replace 2nd
.byte %ff,%ff,%ff ; replace 1st
jp Interrupt_3 ; Jump to actual Int3 routine
Int4:
.byte %ff,%ff,%ff ; replace last with jp instruction
.byte %ff,%ff,%ff ; replace 3rd
.byte %ff,%ff,%ff ; replace 2nd
.byte %ff,%ff,%ff ; replace 1st
jp Interrupt_4 ; Jump to actual Int4 routine
Int5:
.byte %ff,%ff,%ff ; replace last with jp instruction
.byte %ff,%ff,%ff ; replace 3rd
.byte %ff,%ff,%ff ; replace 2nd
.byte %ff,%ff,%ff ; replace 1st
jp Interrupt_5 ; Jump to actual Int5 routine
Replace each
3-byte %FF set as may be necessary starting from the bottom of the affected
group, working your way up each reprogramming times.
Using the Int5
group as an example, if Interrupt_5 has an error and must be bypassed,
replace the first 3 %FF immediately before the jp Interrupt_5 instruction
with the new jp instruction to the modified routine, say Interrupt_5a:
Int5:
.byte %ff,%ff,%ff ; replace last with jp instruction
.byte %ff,%ff,%ff ; replace 3rd
.byte %ff,%ff,%ff ; replace 2nd
jp Interrupt_5a ; replace 1st
.byte %00,%00,%00 ; Jump to actual Int5 routine
The previous jp
Interrupt_5 instruction is replaced by three zero value bytes. (Why? See
The Need for Zeroes section) If error is found on Interrupt_5a and must
be, again, bypassed, replace the first 3 %FF immediately before the jp
Interrupt_5a instruction with the new jp instruction to the modified
routine, say Interrupt_5b:
Int5:
.byte %ff,%ff,%ff ; replace last with jp instruction
.byte %ff,%ff,%ff ; replace 3rd
jp Interrupt_5b ; replace 2nd
.byte %00,%00,%00 ; replace 1st
.byte %00,%00,%00 ; Jump to actual Int5 routine
Replace the previous jp Interrupt_5a instruction with three zeroes, and so
on.
The need for zeroes
The old jp instructions that are no longer needed must be replaced by zeroes
to prevent the device programmer (ccp emulator) from aborting the
programming operation caused by verification error. The ccp emulator and
device programmer programs the device byte by byte, and every time a byte is
written, it immediately reads it back to check if it matches with the
programming byte. If a match is confirmed, it continues operation by
proceeding and writing the next programming byte. Otherwise, programming
operation stops and aborted at the first instance of verification failure,
leaving the remaining bytes un programmed.
Putting zeroes on old jp positions prevents verification errors as stated
and ensures the completion of the programming sequence.
Reprogramming the main program space
OK, now that we
learned how to write the startup code essential to make our z8
reprogrammable. We also learned that we have to replace the old discarded
space with zeroes to keep the CCP emulator programmer happy and motivate it
to finish its job.
The same rule
applies to the main program space. All discarded space should be programmed
with zero. To do that, we should determine where the old program ends in the
program memory space. And where do we find this information? By examining
the listing file (.lst extension) automatically generated by the assembler.
Example:
The simple
source code
.org %0c
test:
nop
ld r10,#3
Loop:
dec r10
jr nz,Loop
jr test
will generate
the following listing file after assembly (BUILD)
Location Object Type Line Source
A 1 .org %0c
0000000C A 2
test:
0000000C FF A 3
nop
0000000D AC 03 A 4
ld r10,#3
0000000F A 5
Loop:
0000000F 00 EA A 6
dec r10
00000011 EB FC A 7
jr nz,Loop
00000013 8B F7 A 8
jr test
The first
column indicates the hexadecimal address location of the corresponding
machine code generated for each source instruction. In this example, the
program starts at .org %0c, hence the first column address start shows
000000C. The program ends at hex 0000014 (why?).
Once you
determine the last address of the old program, zeroing it out is a matter of
using the assembler directive:
.BLKB
%015-%0C,00
%015-%0C is
program END+1 minus program START address, which actually tells the
assembler how many consecutive address should it put the value indicated in
the second field, which, in this example is 00. Applying it to the above
example:
.org %0c
; The next directive fill the old program space
with zeroes
.BLKB %015-%0C,00
; The new, modified program starts here
Newtest:
ei
ld r10,#5
Loop:
dec r10
jr nz,Loop
jr Newtest
Enjoy your Z8
many times more!
Notes:
(1) NOPs will
not affect program logic but will add delays of few microsecond in your
program. In almost all but few rare instances, this delay will not matter.
|