aboutsummaryrefslogtreecommitdiff
path: root/img2atari
blob: d52c8e254c11877ab6b5bc03d9cc8e18f199badf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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 <<EOF
$SELF - 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.

Usage: $SELF [options] [inputfile] [outputfile]

Options:
-g <mode>   Graphics mode for outputfile (8, 9, or 15, default 9)
-f <color>  Color register value for GR.8 foreground (default 0x0c)
-b <color>  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 <addr>   Create binary load data file, load image at <addr>
-l <addr>   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 <byte>   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.