Month: November 2009

Z80 vs. PIC

Yesterday I wrote some lines of PIC assembly code to manage interrupt service routine so that I can select from C the code to execute when an interrupt occurs. Just to give you an idea of the pain, I will show you a comparison between a Z80 (1971) and a PIC18 (2002) in an indirect call. Let’s say that you want to jump at a program address stored in two bytes at address TL and TH.

Z80 PIC18
    jr L1
L2: ld hl,(TL)
    jp (hl)
L1: call L2


   bra L1
L2 movff TH,PCLATH
   movlb bank(TL)
   movf TL,W,B
   movf PCLAT
L1 rcall L2


Z80 routine is 9 bytes long while PIC18 spreads over 14 bytes. When it comes to execution times things are not so bad for PIC18 – 36 machine cycles compared to 53 of the Z80. My guess is that a 2002 architecture involves a pipeline that allows the CPU to crank out an instruction for machine cycle. In fact modern incarnations of the Zilog CPU have a revised architecture that runs 4 times faster or more than the original Z80.

I’ve got the PIC

It was 1971 when the first single chip CPU hit the shelves. 4004 was the name. Rough by today standards, nonetheless it featured several impressive features – among which 16 registers and 5 instructions operating at 16 bits. One year later and it was the time for 8008, with 6 registers of 8 bit each. This chip was the base for the 8080, Z80 and 8086. I am quite familiar with 8080 (basically the cpu powering the GameBoy Color) and with Z80 (the heart of many of 80s home computers – ZX Spectrum and CPC Amstrad). Later it was time for extremely elegant and rational architectures – the MC68000 and the ARM.
So I supposed that the evolution of CPUs drove to better chip with rational architecture and with legacy kludges slowly moved into the oblivion. I was happy.
Then I met the Microchip PIC. To give you an idea I would say that the PIC is to CPUs what the Cobol is to programming languages.
PIC has basically one single register, plus a set of memory location with hardwired operations. For example if you want to indirect access a memory location, you write the memory location address at a specific address, then you read another specific address and you got the indirect addressing.
PIC features an harvard architecture that is program memory is separate from data memory. Program memory can address up to 24bits, while data memory holds no more than 64kbyte, but usually you get a few kilos.
The CPU has a 31 level hardware stack for call. That means that on this stack only return addresses can be stored. If you want to use the stack to pass parameters and/or to store local variables you have to implement your software stack. In the latest PIC you get some specialized memory addresses that helps you in this task.
But obviously this architecture is not thought for modern language (if you can call modern C with his 40 years of history). So at microchip they decided that some extended instruction set was needed. I think they had best intentions and that, being engineers, they took the best decisions. But the result leave me headscraping… Basically they added a static configuration bit to the CPU. This bit is stored in the program memory, so you can’t change it without rebooting. When this bit is set the meaning of nearly half of the instruction set is altered so that rather than accessing a fixed memory address, that address is used as a displacement from a pointer half in a given memory location.
Kiss backward compatibility goodbye.
I would add that the harvard architecture doesn’t mate well with C (at least with the compiler you can buy at microchip). In fact, C language pointers may have different size according to the pointed type. But a void pointer is large enough to accommodate any size pointer. With the PIC C compiler this is not true – the size of the pointer depends of a non-standard modifier “rom” or “ram” (ram is the default). So if you point in ram the pointer is 16bits wide, but if you point in rom the pointer is 24 bits. If you move a rom pointer into a void pointer you lose the 8 most significant bits. The drawback is that you cannot write code agnostic to the location of pointed data.
Considering all, the fact that the compiler requires “main” to be declared as “void main(void)” can be well ignored.