From 41640fe33b90ec675cb86bf8179a44618ee1cfa5 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Sat, 30 Mar 2019 03:41:23 -0400 Subject: initial commit --- README.txt | 79 ++++++++++++++++++++++ img2atari | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ loader.s | 114 +++++++++++++++++++++++++++++++ viewer.s | 35 ++++++++++ 4 files changed, 451 insertions(+) create mode 100644 README.txt create mode 100755 img2atari create mode 100644 loader.s create mode 100644 viewer.s diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..1344c27 --- /dev/null +++ b/README.txt @@ -0,0 +1,79 @@ +img2atari - Quick & dirty image converter for Atari 8-bit computers, + using ImageMagick. + +Written by B. Watson (yalhcru@gmail.com), released under the +WTFPL. See http://www.wtfpl.net/txt/copying/ for details. + +Purpose: + +Convert images in common formats (JPEG, PNG, etc) to black & white Atari +8-bit image files or executables, in graphics modes 8, 9, or 15. + +Requirements: + +- Linux or some other UNIX/POSIX like environment (possibly including + Mac OSX). You might be able to use Cygwin or MSYS on Windows, or + the Ubuntu emulation included in modern Windows (?). + +- bash. Easily installed on most Linux and similar OSes these days, if not + already part of the OS. + +- ImageMagick. At least the convert and composite commands must be found + in $PATH. + +- Some way to run Atari 8-bit executables, either an emulator or a real + Atari with e.g. an SIO2PC cable. + +Download: + + wget http://urchlay.naptime.net/repos/img2atari/plain/img2atari + chmod +x img2atari + +...or: + + git clone http://urchlay.naptime.net/img2atari.git + +Installation: + +Just copy the img2atari script to some place that's in your $PATH, +or else run it from the directory you saved it to as "./img2atari". + +Usage: + +Run "img2atari --help" for full usage information. + +Notes: + +You don't actually need the loader.s or viewer.s files. These are the +assembly sources for the object code included in img2atari, and are pretty +much only useful for informational purposes. You can modify them, but +there's no easy way to import your modified code into the shell script. + +The input file can be in any format ImageMagick can read. This includes at +least JPEG, PNG, GIF, TIFF, BMP, XPM, EPS... for testing, you can even use +the ImageMagick built-in images (e.g. "logo:" for the ImageMagick logo). +For vector formats (e.g. SVG), you'll want to render it to a PNG first. +For multi-image formats (e.g. animated GIF, PDF), you'll want to extract +single images and convert those. + +Not all images will convert to something that looks OK on the +Atari. Photographs look OK in GR.9 (though blurry due to the low +horizontal resolution). Line art looks better in GR.8 or GR.15. + +You may have to tweak the image some, before converting it. Images that +are too dark or too bright will come out with very little detail, so +try loading the image in an image editor and altering the saturation. +Also, the conversion to black & white is simplistic. You may get better +results if you pre-convert to mono or greyscale. + +GR.8 images with dithering may show a lot of artifacting on the Atari. +About the only thing you can do to avoid this is use the chroma/luma +(s-video) output instead of composite video or RF. + +If you get errors from convert and/or composite (e.g. because the input +file doesn't exist, or isn't recognized by ImageMagick), the output file +will be useless and should be deleted. + +The 'loader' included with the -x option uses the OS to set up the +graphics mode, so custom resolutions (wide/narrow playfield, nonstandard +number of scanlines) are not supported. diff --git a/img2atari b/img2atari new file mode 100755 index 0000000..d52c8e2 --- /dev/null +++ b/img2atari @@ -0,0 +1,223 @@ +#!/usr/bin/env bash + +# This really is a bash script. It also seems to work in zsh, but not +# ksh or dash. This is not a bug. + +SELF="$( basename $0 )" + +help() { + cat < Graphics mode for outputfile (8, 9, or 15, default 9) +-f Color register value for GR.8 foreground (default 0x0c) +-b Color register value for GR.8 background (default 0x00) +-x Create standalone executable with loader and viewer +-X Create executable with loader only as an init segment +-a Create binary load data file, load image at +-l Load address for -x/-X loader (default 0x0400). Give + address in either decimal or hex with 0x prefix. +-d Dither. May improve results, or ruin them (YMMV). Doesn't + actually seem to have any visible effect. +-r For use with -x/-X: set RAMTOP. Default 192 (>=48K Atari + with no cartridge or BASIC). Use 160 if BASIC will be enabled, + or 64 for unexpanded 400 or 600XL, etc. + +To write to standard output, use - as the outputfile. Reading from +standard input is not supported. + +The image is scaled to fit in the desired graphics mode (with aspect +ratio preserved) and converted to greyscale: 4 bits for -g 9, 2 bits +for -g 15, and 1 bit (true monochrome) for -g 8. + +With -g 15, the loader will set the color registers to \$00 \$04 \$08 +\$0C. For raw images, you'll want to set them in whatever code you write +that uses the raw file. + +Output file format depends on options given: + +- With no -a/-x/-X options, the output is a raw screen memory image. + With -g 8 or 9, this is the same as an Atari .GR8 or .GR9 file. With + -g 15, it's similar to a Koala .pic file, except the color registers + aren't saved. + +- With -a, the output is an Atari binary load file that loads the + image data at the given address, but includes no executable code. By + itself, this isn't very useful, but it could be used as part of a + multi-segment executable, or with the BLOAD command in Turbo BASIC. + +- With -x, the output is a standalone executable that loads and displays + the image in the given graphics mode, then waits for the user to press + a key, then exits to DOS. + +- With -X, the output is a binary load file that displays the image, but + doesn't include the "wait for keypress" code. This would be useful as + part of a multi-segment executable. +EOF +} + +#-n BROKEN! Narrow GTIA mode (256x192 for GR.8, 128x192 for GR.15) + +die() { + echo "$SELF: $@" 1>&2 + exit 1 +} + +printbyte() { + printf -v tmpbyte "%02x" "$1" + LANG=C echo -n -e "\x$tmpbyte" +} + +printword() { + printbyte $(( $1 % 256 )) + printbyte $(( $1 / 256 )) +} + +printbytes() { + for i in "$@"; do printbyte 0x$i; done +} + +GRMODE=9 +DITHER="+dither" +RAMTOP=192 +LOADERADDR=1024 +SDMCTL=0x22 + +if [ "$#" = "0" ]; then + help + exit 0 +fi + +while [ "$1" != "" ]; do + case "$1" in + "--help"|"-help"|"-h"|"-?") help ; exit 0 ;; + "-g") GRMODE="$2" ; shift ;; + "-f") FGCOLOR="$2" ; shift ;; + "-b") BGCOLOR="$2" ; shift ;; + "-x") LOADER=1; VIEWER=1 ;; + "-X") LOADER=1; VIEWER=0 ;; + "-a") IMGSTART="$2" ; shift ;; + "-l") LOADERADDR="$2" ; shift ;; + #"-n") NARROW=1; SDMCTL=0x21 ;; + "-n") die "narrow mode not yet supported" ;; + "-d") DITHER="" ;; + "-r") RAMTOP="$2" ; shift ;; + -?*) die "invalid option '$1'" ;; + *) + if [ "$INFILE" = "" ]; then + INFILE="$1" + elif [ "$OUTFILE" = "" ]; then + OUTFILE="$1" + else + die "extra junk on the command line: '$1'" + fi + ;; + esac + shift +done + +type -p convert > /dev/null && type -p composite > /dev/null || \ + die "Can't find 'convert' and 'composite' in \$PATH. Install ImageMagick?" + +[ "$INFILE" = "" ] && die "No input filename given" +[ "$INFILE" = "-" ] && die "Reading stdin not supported" +[ "$OUTFILE" = "" ] && die "No output filename given (use - for stdout)" + +# ASPECT pretends GR.8 pixels are square, GR.15 are 2x1 rectangles, +# and GR.9 are 4x1. This isn't entirely true... +case "$GRMODE" in + 8) W=320; H=192; COLORS=2; DEPTH=1; ASPECT=100; FGCOLOR=${FGCOLOR:-0x0c}; BGCOLOR=${BGCOLOR:-0x00} ;; + 15) W=160; H=192; COLORS=4; DEPTH=2; ASPECT=50 ;; + 9) W=80; H=192; COLORS=16; DEPTH=4; ASPECT=25 ;; + *) die "invalid graphics mode '$GRMODE' (only 8 and 15 supported)" ;; +esac + +#[ "$NARROW" = "1" ] && W=$(( $W / 40 * 32 )) + +[ "$OUTFILE" = "" ] && OUTFILE="-" +[ "$OUTFILE" != "-" ] && exec > $OUTFILE + +if [ "$LOADER" = "1" ]; then + if [ "$IMGSTART" != "" ]; then + die "-a option can't be combined with -x/-X" + fi + + LOADEREND=$(( $LOADERADDR + 113 )) + IMGSTART=$(( $RAMTOP * 256 - 7856 )) + + # xex header + printword 0xffff + printword $LOADERADDR + printword $LOADEREND + + # data table (see loader.s) + printbyte $RAMTOP + printbyte $GRMODE + printbyte 0x04 + printbyte ${FGCOLOR:-0x08} + printbyte ${BGCOLOR:-0x0c} + printbyte 0x00 + printbyte 0x00 + printbyte $SDMCTL + + # code (see loader.s) + printbytes 78 a9 00 8d 0e d4 a9 60 85 d4 20 d4 00 ba ca bd 00 01 + printbytes 38 e9 14 85 d4 bd 01 01 e9 00 85 d5 58 a9 40 8d + printbytes 0e d4 a0 00 b1 d4 85 6a a2 60 a9 0c 9d 42 03 20 + printbytes 56 e4 a2 60 a9 0c 9d 4a 03 a0 01 b1 d4 9d 4b 03 + printbytes a9 03 9d 42 03 a9 53 85 d6 a9 d6 9d 44 03 a9 00 + printbytes 9d 45 03 20 56 e4 a0 02 b1 d4 99 c2 02 c8 c0 07 + printbytes d0 f6 b1 d4 8d 2f 02 60 + + # INITAD segment + printbytes e2 02 e3 02 + printword $(( LOADERADDR + 8 )) + + # Narrow display needs 2nd DL LMS adjusted +## if [ "$NARROW" = 1 ]; then +## printword 0xffff +## printword $(( IMGSTART - 182 )) +## printword $(( IMGSTART - 181 )) +## printword $(( IMGSTART + 32 * 94 )) +## fi +fi + +if [ "$IMGSTART" != "" ]; then + IMGBYTES=$(( $W * $H / 8 * $DEPTH )) + IMGEND=$(( $IMGSTART + $IMGBYTES - 1 )) + printword 0xffff + printword $IMGSTART + printword $IMGEND +fi + +convert "$INFILE" $DITHER -scale ${ASPECT}%x100%\! -colorspace gray -scale ${W}x${H} -depth $DEPTH -colors $COLORS gif:- | \ +composite gif:- -size ${W}x${H} -depth $DEPTH -colors $COLORS -gravity center xc:black gray:- + +if [ "$VIEWER" = 1 ]; then + LOADEREND=$(( $LOADERADDR + 12 )) + + # xex header + printword 0xffff + printword $LOADERADDR + printword $LOADEREND + + # code (see viewer.s) + printbytes a9 ff 8d fc 02 ad fc 02 c9 ff f0 f9 60 + + # RUNAD segment + printbytes e0 02 e1 02 + printword $LOADERADDR +fi + +exit 0 + +# dump pixels' RGB values as text: +# convert in.png +dither -scale 320x192 -depth 2 -colors 4 -remap a8_ntsc_palette.png txt:- |sed 's,.*#\([0-9A-F]\{6\}\).*,\1,'|sort -u +# could someday be used to support color images in GR.15, or something like APAC. diff --git a/loader.s b/loader.s new file mode 100644 index 0000000..3b78fbd --- /dev/null +++ b/loader.s @@ -0,0 +1,114 @@ +; Use CIO to set up S: as GR.8 or GR.15 +; Relocatable code. +; Assemble with: +; cl65 -t none -o loader.xex loader.s + + .include "atari.inc" + +dataptr = FR0 +tmp = FR0+2 + +start = $0400 + +; segment header + .org start-6 + .word $FFFF + .word start + .word endmain-1 + + .org start + .byte $c0 ; default RAMTOP + .byte $0f ; GR. mode (8 or 15) + .byte $04 ; COLOR0 (01 pixels in GR.15) + .byte $08 ; COLOR1 (10 pixels in GR.15, 1 pixels in GR.8) + .byte $0c ; COLOR2 (11 pixels in GR.15, 0 pixels in GR.8) + .byte $00 ; COLOR3 (unused) + .byte $00 ; COLOR4 (00 pixels in GR.15, border in GR.8) + .byte $22 ; SDMCTL ($21 for narrow, $22 for normal) + +main: +; find our datablock's start address. this song and dance routine +; allows us to be fully relocatable, and have all the data in a +; single block (easily modified by the shell script that uses this). + + sei ; disable interrupts so they don't clobber the stack + lda #$00 + sta NMIEN + lda #$60 ; RTS opcode + sta dataptr + jsr dataptr +ret: ; here is where the RTS returns to + +; address is now in the stack area, fix it up and save it + tsx + dex + lda $100,x ; lo byte + sec + sbc #ret-start-1 + sta dataptr + lda $101,x ; hi byte + sbc #0 + sta dataptr+1 ; $d4/$d5 is now a pointer to 'start' + + cli ; re-enable interrupts + lda #$40 + sta NMIEN + +; set RAMTOP + ldy #0 + lda (dataptr),y + sta RAMTOP + +; close device #6 + ldx #$60 + lda #CLOSE + sta ICCOM,x + jsr CIOV + +; open device #6 (GRAPHICS command basically) + ldx #$60 + lda #$0c ; read/write, no +16 or +32 + sta ICAX1,x + ldy #$01 + lda (dataptr),y + sta ICAX2,x ; just the mode (8 or 16) + lda #OPEN + sta ICCOM,x + lda #'S' + sta tmp + lda #tmp + sta ICBAH,x + jsr CIOV + +; set up color regs + ldy #$02 +cloop: + lda (dataptr),y + sta COLOR0-2,y + iny + cpy #$07 + bne cloop + +; set up display width + lda (dataptr),y + sta SDMCTL + + rts +;;; put some stuff in screen RAM so we can see it +;; lda #$ff +;; sta $a150 +;; lda #$aa +;; sta $a151 +;; lda #$55 +;; sta $a152 +;;hang: jmp hang + +endmain: + +; segment header + .word INITAD + .word INITAD+1 + .word main + diff --git a/viewer.s b/viewer.s new file mode 100644 index 0000000..8479cc4 --- /dev/null +++ b/viewer.s @@ -0,0 +1,35 @@ +; Wait for keypress, then exit +; Relocatable code. +; Assemble with: +; cl65 -t none -o viewer.xex viewer.s + + .include "atari.inc" + +dataptr = FR0 +tmp = FR0+2 + +start = $0400 + +; segment header + .org start-6 + .word $FFFF + .word start + .word endmain-1 + + .org start +main: + lda #$ff + sta 764 +loop: + lda 764 + cmp #$ff + beq loop + + rts +endmain: + +; segment header + .word RUNAD + .word RUNAD+1 + .word main + -- cgit v1.2.3