Z8 Many Times Programmable

e-gizmo.com website is javascript driven. Web page may not show correctly if scripting is blocked.

 

 

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.

 

 

Written

by

 Henry L. Chua

Rev. A

11/21/2006

Last Update

 

comments?

hlc@e-gizmo.com

 

Copyright 2004, by e-Gizmo mechatronix and the author. No parts of this publication may be reproduced elsewhere without the author's permission. 

 

All information contained herein are believed to be correct and accurate. By using this site, you agree that the author or e-Gizmo Mechatronix Central cannot be held liable for any damage arising from the use or misuse of any information from this site.

 

 

| PRODUCTS |  |   KITS   |  | ARTICLES |  | GALLERY |  |ABOUT US |  | FORUM |