diff --git a/image/.gitignore b/image/.gitignore new file mode 100644 index 0000000..bb875a0 --- /dev/null +++ b/image/.gitignore @@ -0,0 +1,3 @@ +out/ +bin/ +*~ \ No newline at end of file diff --git a/image/Makefile b/image/Makefile new file mode 100644 index 0000000..d266bb1 --- /dev/null +++ b/image/Makefile @@ -0,0 +1,38 @@ +MAKEADF=../tools/makeadf +FLOPPY=bin/image.adf +IMAGE_DATA=out/image-copper.s out/image-data.bin + +all: bin out $(MAKEADF) $(FLOPPY) + +gdrive: all + cp $(FLOPPY) ~/Google\ Drive + +bin: + mkdir bin + +out: + mkdir out + +$(MAKEADF): ../tools/makeadf.c + gcc ../tools/makeadf.c -o $(MAKEADF) + +$(FLOPPY): out/bootblock.bin + $(MAKEADF) out/bootblock.bin > $(FLOPPY) + +out/bootblock.bin: out/bootblock.o + vlink -brawbin1 $< -o $@ + +out/bootblock.o: bootblock.s out/main.bin + vc -c $< -o $@ + +out/main.o: image.s $(IMAGE_DATA) + vc -c $< -o $@ + +out/main.bin: out/main.o + vlink -brawbin1 $< -o $@ + +$(IMAGE_DATA): images/hello.png ../tools/bitplanify.py + ../tools/bitplanify.py images/hello.png --copper $(IMAGE_DATA) + +clean: + rm -rf out bin diff --git a/image/README.md b/image/README.md new file mode 100644 index 0000000..f7c370a --- /dev/null +++ b/image/README.md @@ -0,0 +1,6 @@ +Display a simple image +====================== + +Building on trackdisk.device, we now display a simple color image + +Image display code and conversion tools from https://github.com/vilcans/amiga-startup \ No newline at end of file diff --git a/image/bootblock.s b/image/bootblock.s new file mode 100644 index 0000000..16b7349 --- /dev/null +++ b/image/bootblock.s @@ -0,0 +1,30 @@ + include exec/io.i + include lvo/exec_lib.i + +bootblock: + dc.b "DOS",0 + dc.l 0 + dc.l 880 + +bootEntry: + ;; a6 = Exec base + ;; a1 = trackdisk.device I/O request pointer + + lea $70000,a5 ; main.s entry point + + ;; Load the progam from the floppy using trackdisk.device + move.l #mainEnd-mainStart,IO_LENGTH(a1) + move.l a5,IO_DATA(a1) + move.l #mainStart-bootblock,IO_OFFSET(a1) + jsr _LVODoIO(a6) + + jmp (a5) ; -> main.s entry point + + ;; Pad the remainder of the bootblock + cnop 0,1024 + +mainStart: + incbin "out/main.bin" + cnop 0,512 +mainEnd: + end \ No newline at end of file diff --git a/image/image.s b/image/image.s new file mode 100644 index 0000000..7770427 --- /dev/null +++ b/image/image.s @@ -0,0 +1,69 @@ + include ../include/registers.i + include hardware/dmabits.i + include hardware/intbits.i + +LVL3_INT_VECTOR equ $6c +SCREEN_WIDTH_BYTES equ (320/8) +SCREEN_BIT_DEPTH equ 4 + + +entry: + lea level3InterruptHandler(pc),a3 + move.l a3,LVL3_INT_VECTOR + + lea CUSTOM,a1 + + ;; install copper list and enable DMA + lea copper(pc),a0 + move.l a0,cop1lc(a1) + move.w COPJMP1(a1),d0 + move.w #(DMAF_SETCLR!DMAF_COPPER!DMAF_RASTER!DMAF_MASTER),dmacon(a1) + +.mainLoop: + bra.b .mainLoop + +level3InterruptHandler: + movem.l d0-a6,-(sp) + +.checkVerticalBlank: + lea CUSTOM,a5 + move.w INTREQR(a5),d0 + and.w #INTF_VERTB,d0 + beq.s .checkCopper + +.verticalBlank: + move.w #INTF_VERTB,INTREQ(a5) ; Clear interrupt bit + +.resetBitplanePointers: + lea bitplanes,a1 + lea $dff0e0,a2 + moveq #SCREEN_BIT_DEPTH,d0 +.bitplaneloop: + move.l a1,(a2) + lea SCREEN_WIDTH_BYTES(a1),a1 ; Bit plane data is interleaved + addq #4,a2 + dbra d0,.bitplaneloop + +.checkCopper: + lea CUSTOM,a5 + move.w INTREQR(a5),d0 + and.w #INTF_COPER,d0 + beq.s .interruptComplete +.copperInterrupt: + move.w #INTF_COPER,INTREQ(a5) ; Clear interrupt bit + +.interruptComplete: + movem.l (sp)+,d0-a6 + rte + +copper: + dc.w BPLCON0,(SCREEN_BIT_DEPTH<<12)|$200 ; Set color depth and enable COLOR + dc.w BPL1MOD,SCREEN_WIDTH_BYTES*SCREEN_BIT_DEPTH-SCREEN_WIDTH_BYTES + dc.w BPL2MOD,SCREEN_WIDTH_BYTES*SCREEN_BIT_DEPTH-SCREEN_WIDTH_BYTES + + include "out/image-copper.s" + + dc.l $fffffffe +bitplanes: + incbin "out/image-data.bin" + \ No newline at end of file diff --git a/image/images/hello.png b/image/images/hello.png new file mode 100644 index 0000000..1048f49 Binary files /dev/null and b/image/images/hello.png differ diff --git a/trackdisk/registers.i b/include/registers.i similarity index 100% rename from trackdisk/registers.i rename to include/registers.i diff --git a/tools/bitmap.py b/tools/bitmap.py new file mode 100644 index 0000000..610d370 --- /dev/null +++ b/tools/bitmap.py @@ -0,0 +1,59 @@ +from array import array + + +class BitplaneImage(object): + def __init__(self, byte_width, height, bitplanes): + self.byte_width = byte_width + self.height = height + self.bitplanes = bitplanes + + +def image_to_bitplanes(image, bit_depth): + """ + Convert a PIL image into bitplanes. + + Returns a bitmap image. + """ + bitplanes = [array('c') for _ in xrange(bit_depth)] + width, height = image.size + byte_width = (width + 7) // 8 + + for row in xrange(height): + for byte in xrange(byte_width): + planes = [0] * bit_depth + for bit in xrange(8): + palette_index = image.getpixel((byte * 8 + 7 - bit, row)) + for plane_index in range(bit_depth): + planes[plane_index] |= ((palette_index >> plane_index) & 1) << bit + for plane_index in range(bit_depth): + bitplanes[plane_index].append(chr(planes[plane_index])) + + return BitplaneImage(byte_width, height, bitplanes) + + +def write_interleaved(file, image): + """Write all bitplanes in interleaved mode to a file.""" + for row in xrange(image.height): + offset = row * image.byte_width + for plane in image.bitplanes: + plane[offset:offset + image.byte_width].write(file) + + +def write_bitplane(file, image, bitplane_index): + """Write a single bitplane to a file.""" + image.bitplanes[bitplane_index].write(file) + + +def to_amiga_colors(palette): + """ + Convert 24 bit colors to Amiga 12 bit colors. + palette is a sequence of integers: [r, g, b, ...] + """ + number_of_colors = len(palette) // 3 + amiga_colors = [] + for color_index in xrange(number_of_colors): + r, g, b = [c for c in palette[color_index * 3:(color_index + 1) * 3]] + rgb = ((r >> 4) << 8) | ((g >> 4) << 4) | (b >> 4) + amiga_colors.append(rgb) + + return amiga_colors diff --git a/tools/bitmap.pyc b/tools/bitmap.pyc new file mode 100644 index 0000000..748acdb Binary files /dev/null and b/tools/bitmap.pyc differ diff --git a/tools/bitplanify.py b/tools/bitplanify.py new file mode 100755 index 0000000..243d6a8 --- /dev/null +++ b/tools/bitplanify.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +import sys +import math +from PIL import Image +from bitmap import ( + image_to_bitplanes, + write_bitplane, + write_interleaved, + to_amiga_colors, +) + + +def main(args): + source_file = args.source + bit_depth = args.depth + verbose = args.verbose + + img = Image.open(source_file) + if img.mode != 'P': + print 'Image is not palette-based' + sys.exit(1) + + palette = img.getpalette() # always 256*3 values + if not bit_depth: + max_index = img.getextrema()[1] + bit_depth = int(math.log(max_index) / math.log(2)) + 1 + if verbose: + print 'Maximum palette index used: %d: assuming bit depth %d' % (max_index, bit_depth) + + amiga_palette = to_amiga_colors( + palette[:(1 << bit_depth) * 3] + ) + + width, height = img.size + if (width & 7) != 0: + print 'Width is not divisable by 8:', width + sys.exit(1) + + bitmap_image = image_to_bitplanes(img, bit_depth) + if verbose: + print 'Image width: %d pixels = %d bytes. Height: %d pixels' % ( + width, bitmap_image.byte_width, height) + + if args.separate: + for plane_index in range(bit_depth): + with open(args.out % (plane_index + 1), 'wb') as out: + write_bitplane(out, bitmap_image, plane_index) + else: + with open(args.out, 'wb') as out: + write_interleaved(out, bitmap_image) + + if args.copper: + with open(args.copper, 'w') as out: + for index, rgb in enumerate(amiga_palette): + out.write('\tdc.w\t$%x,$%03x\n' % (0x180 + index * 2, rgb)) + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser(description='Convert an image to Amiga bitplanes') + parser.add_argument( + '--depth', metavar='N', type=int, default=None, + help='Set number of bitplanes in output') + parser.add_argument( + '--copper', + help='Write palette as copper source code (default is to auto-detect)') + parser.add_argument( + '--separate', action='store_true', + help='Write one bitplane per file. Use %%s in filename as replacement for bitplane number.') + parser.add_argument( + '--verbose', '-v', action='store_true', + help='Write more information to stdout') + parser.add_argument('source', metavar='IMAGE_FILE', + help='Image file to convert to bitplanes') + parser.add_argument('out', metavar='BITPLANES_FILE', + help='File to write bitmaps to') + + args = parser.parse_args() + main(args) diff --git a/tools/makeadf b/tools/makeadf new file mode 100755 index 0000000..f7c7e58 Binary files /dev/null and b/tools/makeadf differ diff --git a/trackdisk/Makefile b/trackdisk/Makefile index b858ced..e17ef4d 100644 --- a/trackdisk/Makefile +++ b/trackdisk/Makefile @@ -1,4 +1,4 @@ -MAKEADF=bin/makeadf +MAKEADF=../tools/makeadf FLOPPY=bin/test.adf all: bin out $(MAKEADF) $(FLOPPY) @@ -12,8 +12,8 @@ bin: out: mkdir out -$(MAKEADF): makeadf.c - gcc makeadf.c -o $(MAKEADF) +$(MAKEADF): ../tools/makeadf.c + gcc ../tools/makeadf.c -o $(MAKEADF) $(FLOPPY): out/bootblock.bin $(MAKEADF) out/bootblock.bin > $(FLOPPY) diff --git a/trackdisk/main.s b/trackdisk/main.s index 27a9b9c..e2d74a7 100644 --- a/trackdisk/main.s +++ b/trackdisk/main.s @@ -1,4 +1,4 @@ - include registers.i + include ../include/registers.i include hardware/dmabits.i include hardware/intbits.i