When discovering the ins and outs of #Amiga hardware during late 80s I learnt about the custom co-processors inside the Amiga:
• The Blitter – A coprocessor having ability to copy rectangular blocks of memory around while applying bit-logic operations on them and to draw lines.
• The Copper – A general purpose coprocessor synchronized to display beam position, able to control most of the other custom hardware, such as sprites, color palette, display properties and control the Blitter.
Even though the Blitter can perform only a limited set of logical operations on bit-level, the operations can be combined to perform more complicated logic. Tomas Rokicki demonstrated Conway’s Game Of Life by employing the Blitter already back in 1986.
Since Game of Life is Turing complete, if I could make it run without any CPU control it’d also mean that the amiga chipset itself would be (granted this has been known to be the case for a long time already). This required controlling the
... show moreWhen discovering the ins and outs of #Amiga hardware during late 80s I learnt about the custom co-processors inside the Amiga:
• The Blitter – A coprocessor having ability to copy rectangular blocks of memory around while applying bit-logic operations on them and to draw lines.
• The Copper – A general purpose coprocessor synchronized to display beam position, able to control most of the other custom hardware, such as sprites, color palette, display properties and control the Blitter.
Even though the Blitter can perform only a limited set of logical operations on bit-level, the operations can be combined to perform more complicated logic. Tomas Rokicki demonstrated Conway’s Game Of Life by employing the Blitter already back in 1986.
Since Game of Life is Turing complete, if I could make it run without any CPU control it’d also mean that the amiga chipset itself would be (granted this has been known to be the case for a long time already). This required controlling the necessary blit operations entirely from the Copper alone, something I already knew was feasible: I would only need to build a Copper program that would trigger the necessary blit operations in sequence. If the setup was done correctly, I could just let the copper list run while the CPU would be idling. Eventually I managed to get this working after dealing with all kinds of Blitter programming woes, such as coming up with the correct barrel shift register values for -1 and +1 cell fetch operations. This is a limited case of Turing Completeness as the total memory size is limited, also limiting the complexity of the system that could be emulated.
Having a Game Of Life running entirely with the Copper and the Blitter alone was all nice as such, but I could make it even more fancy by entirely wiping all CPU instructions from the memory after the setup had been done and stopping the CPU execution. I implemented this by copying section of the code inside one of the temporary buffers used by the Game Of Life blits and jumping to this code. The code would wipe all other system memory to zero, enable the DMA to start up the Copper, and finally execute “stop #$2000” instruction to indefinitely stop the CPU. The first iteration the Game Of Life would then wipe the remaining CPU instructions, entirely erasing last remnants of the 68000 code from RAM. I believe this method was used by some of the copy protections in some #Amiga games (Dragon's Lair is often mentioned).
Having successfully implemented the Game of Life I was already quite pleased with myself. Yet, I felt that there was more that could be done to spice things up. The copper list was static and only repeating the same program indefinitely after all. But what if I would use the Blitter to rewrite part of the copper program itself? That is: Maybe I could build a feedback loop where the Blitter would modify the Copper programming, building more complicated self-contained dynamic programs?
After tinkering around for few hours I came up with basic control blocks that are necessary to implement self-modifying Copper programs in practice. The minimal primitive consists of:
• Setting up the Blitter copy operation of 2+n*4 bytes
• Set the blit target address to the position where the lower 16-bits of the source address for this blit are set, and then n*4 bytes of arbitrary Copper instruction payload
• Execute the blit operation and wait for it to complete
This will update the Copper code in a way that on next display update the n payload Copper instructions piggybacked by the blit operation will get executed. The source address is used to chain together sequence of payloads with the last one of them pointing back to the beginning of the chain. In short, rather than actually diverging the path of execution of the Copper it dynamically modifies fixed section(s) of the Copper instruction flow for each screen refresh effectively resulting in different Copper instructions getting executed on each iteration. While it would also be possible to modify the “program counter” address of the Copper itself but this requires more code, and is less flexible.
I used this construct to create two separate repeating loops:
• Background color cycle of 24 different colors in an RYB color wheel.
• Displaying animated sprite with 8 different frames.
This is rather naïve use case for the capability however. Since the Blitter can do logic operations you could easily store higher level logic and conditional execution controlled by the Blitter source. In effect this demonstrates second means of achieving Turing Completeness.
I had always wondered if something like this could be pulled off. Now I know for sure.
#retrocomputing #programming #hack