diff --git a/016.copper_fun/Makefile b/016.copper_fun/Makefile
new file mode 100644
index 0000000..90f554e
--- /dev/null
+++ b/016.copper_fun/Makefile
@@ -0,0 +1,14 @@
+FLOPPY=bin/copper_fun.adf
+EXTRA=out/copper-new.s
+LIBS=`GraphicsMagick-config --ldflags --libs`
+MODULE=copper_fun.s
+BASE_ADDRESS=10000
+include ../shared/base.mk
+
+out/copper-new.s: assets/test.gif out/copper_fun_generate
+ /opt/ImageMagick/bin/convert assets/test.gif out/%05d.png
+ ./convert.sh > out/copper-new.s
+
+out/copper_fun_generate: copper_fun_generate.c Makefile
+ gcc $(HOST_CFLAGS) `GraphicsMagick-config --cppflags` copper_fun_generate.c -o out/copper_fun_generate $(LIBS)
+
diff --git a/016.copper_fun/README.md b/016.copper_fun/README.md
new file mode 100644
index 0000000..b95bb06
--- /dev/null
+++ b/016.copper_fun/README.md
@@ -0,0 +1,11 @@
+copper fun
+==========
+
+screenshot
+----------
+
+
+try it
+------
+ * [Download disk image](bin/copper_vert.adf?raw=true)
+ * Run in SAE
diff --git a/016.copper_fun/assets/test.gif b/016.copper_fun/assets/test.gif
new file mode 100644
index 0000000..82085dd
Binary files /dev/null and b/016.copper_fun/assets/test.gif differ
diff --git a/016.copper_fun/bin/copper_fun.adf b/016.copper_fun/bin/copper_fun.adf
new file mode 100644
index 0000000..93294c7
Binary files /dev/null and b/016.copper_fun/bin/copper_fun.adf differ
diff --git a/016.copper_fun/convert.sh b/016.copper_fun/convert.sh
new file mode 100755
index 0000000..041502b
--- /dev/null
+++ b/016.copper_fun/convert.sh
@@ -0,0 +1,13 @@
+ODD=1
+for I in out/*.png; do
+ if [ $ODD = 0 ] ; then
+ echo `echo $I | sed -e 's/\///' -e 's/\.png//'`":"
+ ./out/copper_fun_generate $I
+ echo " dc.l \$fffffffe"
+ ODD=1
+
+ else
+ ODD=0
+ fi
+
+done
\ No newline at end of file
diff --git a/016.copper_fun/copper_fun.s b/016.copper_fun/copper_fun.s
new file mode 100644
index 0000000..fc71642
--- /dev/null
+++ b/016.copper_fun/copper_fun.s
@@ -0,0 +1,77 @@
+ include "../include/registers.i"
+ include "hardware/dmabits.i"
+ include "hardware/intbits.i"
+
+LVL3_INT_VECTOR equ $6c
+SCREEN_WIDTH equ 320
+SCREEN_HEIGHT equ 256
+SCREEN_WIDTH_BYTES equ (SCREEN_WIDTH/8)
+SCREEN_BIT_DEPTH equ 1
+SCREEN_RES equ 8 ; 8=lo resolution, 4=hi resolution
+RASTER_X_START equ $81 ; hard coded coordinates from hardware manual
+RASTER_Y_START equ $2c
+RASTER_X_STOP equ RASTER_X_START+SCREEN_WIDTH
+RASTER_Y_STOP equ RASTER_Y_START+SCREEN_HEIGHT
+
+entry:
+ ;; custom chip base globally in a6
+ lea CUSTOM,a6
+
+ move #$7ff,DMACON(a6) ; disable all dma
+ move #$7fff,INTENA(a6) ; disable all interrupts
+
+ ;; set up playfield
+ move.w #(RASTER_Y_START<<8)|RASTER_X_START,DIWSTRT(a6)
+ move.w #((RASTER_Y_STOP-256)<<8)|(RASTER_X_STOP-256),DIWSTOP(a6)
+
+ move.w #(RASTER_X_START/2-SCREEN_RES),DDFSTRT(a6)
+ move.w #(RASTER_X_START/2-SCREEN_RES)+(8*((SCREEN_WIDTH/16)-1)),DDFSTOP(a6)
+
+ move.w #(SCREEN_BIT_DEPTH<<12)|$200,BPLCON0(a6)
+ move.w #SCREEN_WIDTH_BYTES*SCREEN_BIT_DEPTH-SCREEN_WIDTH_BYTES,BPL1MOD(a6)
+
+ ;; install copper list, then enable dma and selected interrupts
+ if 0
+ lea copper(pc),a0
+ move.l a0,COP1LC(a6)
+ endif
+ move.w COPJMP1(a6),d0
+ move.w #(DMAF_SETCLR!DMAF_COPPER!DMAF_RASTER!DMAF_MASTER),DMACON(a6)
+ move.w #(INTF_SETCLR|INTF_INTEN|INTF_EXTER),INTENA(a6)
+
+.mainLoop:
+ bsr waitVerticalBlank
+
+ lea bitplane(pc),a1
+ lea.l BPL1PT(a6),a0
+ move.l a1,(a0)
+
+ move.l counter,d0
+ cmpi.l #5,counter
+ bne .ok
+ move.l #0,counter
+ move.l copperptr,a0
+ move.l a0,COP1LC(a6)
+ add.l #size,copperptr
+ move.l copperptr,d0
+ cmpi.l #end,d0
+ bne .ok
+ move.l #copper,copperptr
+.ok
+ addi.l #1,counter
+ bra.b .mainLoop
+
+ include "utils.s"
+
+size equ out00003-out00001
+
+counter:
+ dc.l 0
+
+copperptr:
+ dc.l out00001
+bitplane:
+ dcb.b (SCREEN_WIDTH*SCREEN_HEIGHT)/8,$00
+copper:
+ include "out/copper-new.s"
+end:
\ No newline at end of file
diff --git a/016.copper_fun/copper_fun_generate.c b/016.copper_fun/copper_fun_generate.c
new file mode 100644
index 0000000..9e0bc29
--- /dev/null
+++ b/016.copper_fun/copper_fun_generate.c
@@ -0,0 +1,129 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define HEIGHT 256+16
+#define COPPER_WIDTH 52
+
+typedef struct {
+ char** argv;
+ float blur;
+ int debug;
+} config_t;
+
+typedef struct {
+ Image *image;
+ Image *resizeImage;
+ ImageInfo *imageInfo;
+} image_t;
+
+image_t image = {0};
+config_t config = {.blur = 0.75};
+
+static void
+cleanup()
+{
+ if (image.image != (Image *) NULL) {
+ DestroyImage(image.image);
+ }
+
+ if (image.resizeImage != (Image *)NULL) {
+ DestroyImage(image.resizeImage);
+ }
+
+
+ if (image.imageInfo != (ImageInfo *) NULL) {
+ DestroyImageInfo(image.imageInfo);
+ }
+ DestroyMagick();
+}
+
+
+static void
+abort_(const char * s, ...)
+{
+ fprintf(stderr, "%s: ", config.argv[0]);
+ va_list args;
+ va_start(args, s);
+ vfprintf(stderr, s, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ cleanup();
+ exit(1);
+}
+
+
+static void
+getframedata(char* inputFile)
+{
+ ExceptionInfo exception;
+ InitializeMagick(NULL);
+ image.imageInfo=CloneImageInfo(0);
+ GetExceptionInfo(&exception);
+ char* outputFile = "resized.png";
+
+ (void) strcpy(image.imageInfo->filename, inputFile);
+ image.image = ReadImage(image.imageInfo, &exception);
+ if (image.image == (Image *) NULL) {
+ CatchException(&exception);
+ abort_("Failed to read image %s\n", inputFile);
+ }
+
+ int width = COPPER_WIDTH;
+ int height = HEIGHT;
+
+ image.resizeImage=ResizeImage(image.image, width, height, BesselFilter, config.blur, &exception);
+
+ strcpy(image.resizeImage->filename, outputFile);
+
+ if (config.debug) {
+ if (!WriteImage(image.imageInfo, image.resizeImage)) {
+ CatchException(&image.resizeImage->exception);
+ abort_("Failed to write image %d\n", outputFile);
+ }
+ }
+}
+
+
+int
+main(int argc, char** argv)
+{
+ config.argv = argv;
+ getframedata(argv[1]);
+
+ int startLine = 0x2c-16;
+ int screenHeight = HEIGHT;
+ int endLine = startLine+screenHeight;
+ int startHpos = 6+8;
+ int i, line;
+
+ for (line = startLine; line < endLine; ++line) {
+ if (line <= 255) {
+ printf("\tdc.w $%x,$fffe\n",line<<8|startHpos|1);
+ } else {
+ printf("\tdc.w $%x,$fffe\n", (line-256)<<8|startHpos|1);
+ }
+
+ int endw = COPPER_WIDTH;
+ if (line == 255) {
+ endw = 50;
+ }
+
+ for (i = 0; i < endw; i++) {
+ PixelPacket pixel = GetOnePixel(image.resizeImage, i, line-startLine);
+ printf("\tdc.w COLOR00,$%x%x%x\n", pixel.red>>4, pixel.green>>4, pixel.blue>>4);
+ }
+
+ if (line <= 255) {
+ printf("\tdc.w $%x,$fffe\n",line<<8|0xe1|1);
+ }
+ }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/016.copper_fun/screenshot.png b/016.copper_fun/screenshot.png
new file mode 100644
index 0000000..510ea92
Binary files /dev/null and b/016.copper_fun/screenshot.png differ
diff --git a/016.copper_fun/utils.s b/016.copper_fun/utils.s
new file mode 100644
index 0000000..bdde568
--- /dev/null
+++ b/016.copper_fun/utils.s
@@ -0,0 +1,23 @@
+waitVerticalBlank:
+ movem.l d0-a6,-(sp)
+.loop move.l $dff004,d0
+ and.l #$1ff00,d0
+ cmp.l #303<<8,d0
+ bne.b .loop
+ movem.l (sp)+,d0-a6
+ rts
+
+
+
+waitRaster: ;wait for rasterline d0.w. Modifies d0-d2/a0.
+ movem.l d0-a6,-(sp)
+ move.l #$1ff00,d2
+ lsl.l #8,d0
+ and.l d2,d0
+ lea $dff004,a0
+.wr: move.l (a0),d1
+ and.l d2,d1
+ cmp.l d1,d0
+ bne.s .wr
+ movem.l (sp)+,d0-a6
+ rts
\ No newline at end of file