1 |
2 |
alfik |
/*
|
2 |
|
|
* Copyright 2010, Aleksander Osman, alfik@poczta.fm. All rights reserved.
|
3 |
|
|
*
|
4 |
|
|
* Redistribution and use in source and binary forms, with or without modification, are
|
5 |
|
|
* permitted provided that the following conditions are met:
|
6 |
|
|
*
|
7 |
|
|
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
8 |
|
|
* conditions and the following disclaimer.
|
9 |
|
|
*
|
10 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
11 |
|
|
* of conditions and the following disclaimer in the documentation and/or other materials
|
12 |
|
|
* provided with the distribution.
|
13 |
|
|
*
|
14 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
15 |
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
16 |
|
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
|
17 |
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
18 |
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
19 |
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
20 |
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
21 |
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
22 |
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23 |
|
|
*/
|
24 |
|
|
|
25 |
|
|
package aoOCS_tool;
|
26 |
|
|
|
27 |
|
|
import java.io.File;
|
28 |
|
|
import java.io.OutputStream;
|
29 |
|
|
import java.io.FileInputStream;
|
30 |
|
|
import java.io.FileOutputStream;
|
31 |
|
|
import java.nio.ByteBuffer;
|
32 |
|
|
import java.nio.channels.FileChannel;
|
33 |
|
|
import java.util.LinkedList;
|
34 |
|
|
import javax.imageio.ImageIO;
|
35 |
|
|
import java.awt.image.BufferedImage;
|
36 |
|
|
import java.util.Vector;
|
37 |
|
|
|
38 |
|
|
public class Main {
|
39 |
|
|
/**
|
40 |
|
|
* Print program call arguments description and exit.
|
41 |
|
|
*/
|
42 |
|
|
static void print_call_arguments() {
|
43 |
|
|
System.out.println("Can not parse program arguments.");
|
44 |
|
|
System.out.println("");
|
45 |
|
|
System.out.println("aoOCS_tool accepts the following arguments:");
|
46 |
|
|
System.out.println("<operation> [operation arguments]");
|
47 |
|
|
System.out.println("");
|
48 |
|
|
System.out.println("The following <operations> are available:");
|
49 |
|
|
System.out.println("\t sd_disk <intro image> <directory with ROM> <directory with AFS> <output disk image>");
|
50 |
|
|
System.out.println("\t control_osd <output control_osd.mif>");
|
51 |
|
|
System.out.println("\t spec_extract <input> <output>");
|
52 |
|
|
System.out.println("\t vga_to_png <input file> <output directory>");
|
53 |
|
|
System.out.println("");
|
54 |
|
|
System.out.println("For more information please read the aoOCS IP core documentation.");
|
55 |
|
|
|
56 |
|
|
System.exit(1);
|
57 |
|
|
}
|
58 |
|
|
|
59 |
|
|
static class LoadedFile {
|
60 |
|
|
String name;
|
61 |
|
|
long position_in_sectors;
|
62 |
|
|
}
|
63 |
|
|
|
64 |
|
|
static void p(String s) throws Exception {
|
65 |
|
|
if(p_out == null) {
|
66 |
|
|
System.out.println(s);
|
67 |
|
|
}
|
68 |
|
|
else {
|
69 |
|
|
s += "\n";
|
70 |
|
|
p_out.write(s.getBytes());
|
71 |
|
|
}
|
72 |
|
|
}
|
73 |
|
|
static int print_32bits(int offset, int a, int b, int c, int d) throws Exception {
|
74 |
|
|
String ha = Integer.toHexString(a);
|
75 |
|
|
String hb = Integer.toHexString(b);
|
76 |
|
|
String hc = Integer.toHexString(c);
|
77 |
|
|
String hd = Integer.toHexString(d);
|
78 |
|
|
|
79 |
|
|
if(ha.length() < 2) ha = "0" + ha;
|
80 |
|
|
if(hb.length() < 2) hb = "0" + hb;
|
81 |
|
|
if(hc.length() < 2) hc = "0" + hc;
|
82 |
|
|
if(hd.length() < 2) hd = "0" + hd;
|
83 |
|
|
|
84 |
|
|
p("" + (offset/4) + ": " + ha + hb + hc + hd + ";");
|
85 |
|
|
return offset+4;
|
86 |
|
|
}
|
87 |
|
|
static int print_string(String str, int val_a, int val_b, int offset) throws Exception {
|
88 |
|
|
for(int j=0; j<24; j+=4) {
|
89 |
|
|
int a = str.charAt(j +0);
|
90 |
|
|
int b = str.charAt(j +1);
|
91 |
|
|
int c = str.charAt(j +2);
|
92 |
|
|
int d = str.charAt(j +3);
|
93 |
|
|
|
94 |
|
|
offset = print_32bits(offset, a,b,c,d);
|
95 |
|
|
}
|
96 |
|
|
offset = print_32bits(offset, (val_a>>24)&0xFF, (val_a>>16)&0xFF, (val_a>>8)&0xFF, (val_a)&0xFF);
|
97 |
|
|
offset = print_32bits(offset, (val_b>>24)&0xFF, (val_b>>16)&0xFF, (val_b>>8)&0xFF, (val_b>>0)&0xFF);
|
98 |
|
|
return offset;
|
99 |
|
|
}
|
100 |
|
|
static void load_files(String directory, LinkedList<LoadedFile> list, FileChannel channel) throws Exception {
|
101 |
|
|
File rom_dir = new File(directory);
|
102 |
|
|
if(rom_dir.exists() == false || rom_dir.isDirectory() == false) throw new Exception("Can not find directory: " + rom_dir.getCanonicalPath());
|
103 |
|
|
|
104 |
|
|
for(File f : rom_dir.listFiles()) {
|
105 |
|
|
if(f.isFile()) {
|
106 |
|
|
byte bytes[] = new byte[(int)f.length()];
|
107 |
|
|
FileInputStream in = new FileInputStream(f);
|
108 |
|
|
if(in.read(bytes) != bytes.length) throw new Exception("Can not read file: " + f.getCanonicalPath());
|
109 |
|
|
in.close();
|
110 |
|
|
|
111 |
|
|
LoadedFile loaded = new LoadedFile();
|
112 |
|
|
loaded.name = " " + f.getName() + " ";
|
113 |
|
|
if(loaded.name.length() > 24) loaded.name = loaded.name.substring(0, 24);
|
114 |
|
|
loaded.position_in_sectors = channel.position()/512;
|
115 |
|
|
list.addLast(loaded);
|
116 |
|
|
System.out.println("" + f.getName() + ": " + channel.position());
|
117 |
|
|
channel.write(ByteBuffer.wrap(bytes));
|
118 |
|
|
|
119 |
|
|
if((channel.position() % 512) != 0) channel.position(channel.position() + 512 - (channel.position() % 512));
|
120 |
|
|
|
121 |
|
|
}
|
122 |
|
|
}
|
123 |
|
|
}
|
124 |
|
|
static void write_header(LinkedList<LoadedFile> list, FileChannel channel, int first, int middle, int last) throws Exception {
|
125 |
|
|
for(int i=0; i<list.size(); i++) {
|
126 |
|
|
LoadedFile f = list.get(i);
|
127 |
|
|
|
128 |
|
|
byte line[] = new byte[32];
|
129 |
|
|
System.arraycopy(f.name.getBytes(), 0, line, 0, 24);
|
130 |
|
|
int a = 0;
|
131 |
|
|
if(i == 0) a |= first;
|
132 |
|
|
if(i == list.size()-1) a |= last;
|
133 |
|
|
if(i != 0 && i != list.size()-1) a |= middle;
|
134 |
|
|
int b = (int)f.position_in_sectors;
|
135 |
|
|
|
136 |
|
|
line[24] = (byte)(a >> 24);
|
137 |
|
|
line[25] = (byte)(a >> 16);
|
138 |
|
|
line[26] = (byte)(a >> 8);
|
139 |
|
|
line[27] = (byte)(a);
|
140 |
|
|
line[28] = (byte)(b >> 24);
|
141 |
|
|
line[29] = (byte)(b >> 16);
|
142 |
|
|
line[30] = (byte)(b >> 8);
|
143 |
|
|
line[31] = (byte)(b);
|
144 |
|
|
|
145 |
|
|
channel.write(ByteBuffer.wrap(line));
|
146 |
|
|
}
|
147 |
|
|
}
|
148 |
|
|
public static void main(String[] args) throws Exception {
|
149 |
|
|
// check program call arguments
|
150 |
|
|
if(args.length == 0) print_call_arguments();
|
151 |
|
|
else if(args[0].equals("sd_disk") == true && args.length != 5) print_call_arguments();
|
152 |
|
|
else if(args[0].equals("control_osd") == true && args.length != 2) print_call_arguments();
|
153 |
|
|
else if(args[0].equals("spec_extract") == true && args.length != 3) print_call_arguments();
|
154 |
|
|
else if(args[0].equals("vga_to_png") == true && args.length != 3) print_call_arguments();
|
155 |
|
|
|
156 |
|
|
int SELECTABLE = 1;
|
157 |
|
|
int TOP = 2;
|
158 |
|
|
int BOTTOM = 4;
|
159 |
|
|
int RESET = 8;
|
160 |
|
|
int JOYSTICK = 16;
|
161 |
|
|
int FLOPPY = 32;
|
162 |
|
|
int WRITE_ENABLED = 64;
|
163 |
|
|
|
164 |
|
|
if(args[0].equals("control_osd")) {
|
165 |
|
|
File f = new File(args[1]);
|
166 |
|
|
p_out = new FileOutputStream(f);
|
167 |
|
|
|
168 |
|
|
p("DEPTH = 1024;");
|
169 |
|
|
p("WIDTH = 32;");
|
170 |
|
|
p("ADDRESS_RADIX = DEC;");
|
171 |
|
|
p("DATA_RADIX = HEX;");
|
172 |
|
|
p("CONTENT");
|
173 |
|
|
p("BEGIN");
|
174 |
|
|
int offset = 0;
|
175 |
|
|
|
176 |
|
|
offset = print_string("aoOCS version 1.0 ", 0,0, offset);
|
177 |
|
|
offset = print_string("Initializing SD card....", 0,0, offset);
|
178 |
|
|
offset = print_string("SD fatal error. ", 0,0, offset);
|
179 |
|
|
offset = print_string("SD ready. Select ROM: ", 0,0, offset);
|
180 |
|
|
offset += 32; // blank line
|
181 |
|
|
offset = print_string("Inserted floppy: ", 0, 0, offset);
|
182 |
|
|
offset = print_string("None ", 0, 0, offset);
|
183 |
|
|
offset = print_string("Joystick(kbd arrows) OFF", TOP | JOYSTICK | SELECTABLE, 0, offset);
|
184 |
|
|
offset = print_string("Joystick(kbd arrows) ON ", TOP | JOYSTICK | SELECTABLE, 0, offset);
|
185 |
|
|
offset = print_string("Floppy write enabled OFF", WRITE_ENABLED | SELECTABLE, 0, offset);
|
186 |
|
|
offset = print_string("Floppy write enabled ON ", WRITE_ENABLED | SELECTABLE, 0, offset);
|
187 |
|
|
offset = print_string("Reset ", RESET | SELECTABLE, 0, offset);
|
188 |
|
|
offset = print_string("Eject floppy ", BOTTOM | FLOPPY | SELECTABLE, 0, offset);
|
189 |
|
|
|
190 |
|
|
p("END;");
|
191 |
|
|
p_out.close();
|
192 |
|
|
}
|
193 |
|
|
else if(args[0].equals("sd_disk")) {
|
194 |
|
|
// read image file
|
195 |
|
|
File intro_image_file = new File(args[1]);
|
196 |
|
|
if(intro_image_file.exists() == false) throw new Exception("Can not read intro image file: " + intro_image_file.getCanonicalPath());
|
197 |
|
|
|
198 |
|
|
BufferedImage img = ImageIO.read(intro_image_file);
|
199 |
|
|
|
200 |
|
|
// prepare output file
|
201 |
|
|
FileOutputStream out = new FileOutputStream(args[4]);
|
202 |
|
|
FileChannel channel = out.getChannel();
|
203 |
|
|
|
204 |
|
|
channel.position(0);
|
205 |
|
|
for(int y=0; y<256; y++) {
|
206 |
|
|
for(int x=0; x<640; ) {
|
207 |
|
|
long three_pixels = 0;
|
208 |
|
|
|
209 |
|
|
for(int z=0; z<3; z++) {
|
210 |
|
|
int r,g,b;
|
211 |
|
|
|
212 |
|
|
if(x >= img.getWidth() || y >= img.getHeight()) {
|
213 |
|
|
r = g = b = 0;
|
214 |
|
|
}
|
215 |
|
|
else {
|
216 |
|
|
r = (img.getRGB(x, y)>>16) & 0xFF;
|
217 |
|
|
g = (img.getRGB(x, y)>>8) & 0xFF;
|
218 |
|
|
b = (img.getRGB(x, y)) & 0xFF;
|
219 |
|
|
}
|
220 |
|
|
|
221 |
|
|
three_pixels <<= 3;
|
222 |
|
|
three_pixels |= (r>>5) & 0x7;
|
223 |
|
|
three_pixels <<= 3;
|
224 |
|
|
three_pixels |= (g>>5) & 0x7;
|
225 |
|
|
three_pixels <<= 3;
|
226 |
|
|
three_pixels |= (b>>5) & 0x7;
|
227 |
|
|
|
228 |
|
|
x++;
|
229 |
|
|
}
|
230 |
|
|
three_pixels <<= 5;
|
231 |
|
|
|
232 |
|
|
byte word[] = new byte[4];
|
233 |
|
|
word[0] = (byte)((three_pixels >> 24) & 0xFF);
|
234 |
|
|
word[1] = (byte)((three_pixels >> 16) & 0xFF);
|
235 |
|
|
word[2] = (byte)((three_pixels >> 8) & 0xFF);
|
236 |
|
|
word[3] = (byte)((three_pixels) & 0xFF);
|
237 |
|
|
|
238 |
|
|
channel.write(ByteBuffer.wrap(word));
|
239 |
|
|
}
|
240 |
|
|
// padding
|
241 |
|
|
byte padding[] = new byte[4*2];
|
242 |
|
|
channel.write(ByteBuffer.wrap(padding));
|
243 |
|
|
}
|
244 |
|
|
System.out.println("Start channel pos: " + channel.position());
|
245 |
|
|
|
246 |
|
|
channel.position(216*4*256 + 4096);
|
247 |
|
|
|
248 |
|
|
LinkedList<LoadedFile> roms = new LinkedList<LoadedFile>();
|
249 |
|
|
load_files(args[2], roms, channel);
|
250 |
|
|
|
251 |
|
|
LinkedList<LoadedFile> floppies = new LinkedList<LoadedFile>();
|
252 |
|
|
load_files(args[3], floppies, channel);
|
253 |
|
|
|
254 |
|
|
channel.position(216*4*256 + 0);
|
255 |
|
|
write_header(roms, channel, TOP | SELECTABLE, SELECTABLE, BOTTOM | SELECTABLE);
|
256 |
|
|
|
257 |
|
|
channel.position(216*4*256 + 512);
|
258 |
|
|
write_header(floppies, channel, FLOPPY | SELECTABLE, FLOPPY | SELECTABLE, BOTTOM | FLOPPY | SELECTABLE);
|
259 |
|
|
|
260 |
|
|
channel.close();
|
261 |
|
|
out.close();
|
262 |
|
|
|
263 |
|
|
/* Sample zero and first SD sector.
|
264 |
|
|
* The 0 sector is loaded at byte offset 512 in control_osd|display_ram_inst.
|
265 |
|
|
* The 1-7 sectors are loaded starting at byte offset 1024 in control_osd|display_ram_inst.
|
266 |
|
|
*/
|
267 |
|
|
/*
|
268 |
|
|
offset = 512;
|
269 |
|
|
offset = print_string(" Some ROM ", TOP | SELECTABLE, 0, offset);
|
270 |
|
|
offset = print_string(" R1 ", SELECTABLE, 0, offset);
|
271 |
|
|
offset = print_string(" R2 ", SELECTABLE, 0, offset);
|
272 |
|
|
offset = print_string(" R3 ", SELECTABLE, 0, offset);
|
273 |
|
|
offset = print_string(" R4 ", SELECTABLE, 0, offset);
|
274 |
|
|
offset = print_string(" R5 ", SELECTABLE, 0, offset);
|
275 |
|
|
offset = print_string(" Some ROM2 ", BOTTOM | SELECTABLE, 0, offset);
|
276 |
|
|
|
277 |
|
|
offset = 1024;
|
278 |
|
|
offset = print_string(" Floppy1 ", FLOPPY | SELECTABLE, 1, offset);
|
279 |
|
|
offset = print_string(" F2 ", FLOPPY | SELECTABLE, 1, offset);
|
280 |
|
|
offset = print_string(" F3 ", FLOPPY | SELECTABLE, 1, offset);
|
281 |
|
|
offset = print_string(" F4 ", FLOPPY | SELECTABLE, 1, offset);
|
282 |
|
|
offset = print_string(" F5 ", FLOPPY | SELECTABLE, 1, offset);
|
283 |
|
|
offset = print_string(" F6 ", FLOPPY | SELECTABLE, 1, offset);
|
284 |
|
|
offset = print_string(" F7 ", FLOPPY | SELECTABLE, 1, offset);
|
285 |
|
|
offset = print_string(" F8 ", FLOPPY | SELECTABLE, 1, offset);
|
286 |
|
|
offset = print_string(" F9 ", FLOPPY | SELECTABLE, 1, offset);
|
287 |
|
|
offset = print_string(" F10 ", FLOPPY | SELECTABLE, 1, offset);
|
288 |
|
|
offset = print_string(" F11 ", FLOPPY | SELECTABLE, 1, offset);
|
289 |
|
|
offset = print_string(" F12 ", FLOPPY | SELECTABLE, 1, offset);
|
290 |
|
|
offset = print_string(" F13 ", FLOPPY | SELECTABLE, 1, offset);
|
291 |
|
|
offset = print_string(" F14 ", FLOPPY | SELECTABLE, 1, offset);
|
292 |
|
|
offset = print_string(" F15 ", FLOPPY | SELECTABLE, 1, offset);
|
293 |
|
|
offset = print_string(" F16 ", FLOPPY | SELECTABLE, 1, offset);
|
294 |
|
|
offset = print_string(" F17 ", FLOPPY | SELECTABLE, 1, offset);
|
295 |
|
|
offset = print_string(" F18 ", FLOPPY | SELECTABLE, 1, offset);
|
296 |
|
|
offset = print_string(" F19 ", FLOPPY | SELECTABLE, 1, offset);
|
297 |
|
|
offset = print_string(" F20 ", BOTTOM | FLOPPY | SELECTABLE, 1, offset);
|
298 |
|
|
*/
|
299 |
|
|
}
|
300 |
|
|
else if(args[0].equals("spec_extract")) {
|
301 |
|
|
DocumentationTool.extract(args[1], args[2]);
|
302 |
|
|
}
|
303 |
|
|
else if(args[0].equals("vga_to_png")) {
|
304 |
|
|
int screen[][] = new int[480][640];
|
305 |
|
|
int line_inserted[] = new int[480];
|
306 |
|
|
|
307 |
|
|
File in_file = new File(args[1]);
|
308 |
|
|
if(in_file.exists() == false || in_file.isDirectory() == true) throw new Exception("Can not open input file: " + in_file.getCanonicalPath());
|
309 |
|
|
|
310 |
|
|
FileInputStream in = new FileInputStream(in_file);
|
311 |
|
|
int b;
|
312 |
|
|
int frame_number = 0;
|
313 |
|
|
while(true) {
|
314 |
|
|
// read line number
|
315 |
|
|
int line_number;
|
316 |
|
|
|
317 |
|
|
b = in.read(); if(b == -1) break;
|
318 |
|
|
line_number = b;
|
319 |
|
|
b = in.read(); if(b == -1) break;
|
320 |
|
|
line_number |= (b<<8);
|
321 |
|
|
|
322 |
|
|
// read full line
|
323 |
|
|
byte line[] = new byte[960];
|
324 |
|
|
if(in.read(line) != line.length) break;
|
325 |
|
|
|
326 |
|
|
// convert bytes to bits
|
327 |
|
|
int bits[] = new int[960*8];
|
328 |
|
|
for(int i=0; i<line.length; i++) {
|
329 |
|
|
for(int j=0; j<8; j++) {
|
330 |
|
|
bits[i*8+j] = (line[i] >> j) & 0x01;
|
331 |
|
|
}
|
332 |
|
|
}
|
333 |
|
|
|
334 |
|
|
// fill in screen array
|
335 |
|
|
if(line_number < 0 || line_number >= 480) throw new Exception("Illegal line number: " + line_number);
|
336 |
|
|
for(int i=0; i<640; i++) {
|
337 |
|
|
int red = 0, green = 0, blue = 0;
|
338 |
|
|
for(int j=0; j<4; j++) blue |= ((bits[i*12 +j +0]) << j);
|
339 |
|
|
for(int j=0; j<4; j++) green |= ((bits[i*12 +j +4]) << j);
|
340 |
|
|
for(int j=0; j<4; j++) red |= ((bits[i*12 +j +8]) << j);
|
341 |
|
|
screen[line_number][i] = (0xFF << 24) | (red << 20) | (green << 12) | (blue << 4);
|
342 |
|
|
}
|
343 |
|
|
|
344 |
|
|
// check is screen frame ready
|
345 |
|
|
line_inserted[line_number] = 1;
|
346 |
|
|
|
347 |
|
|
boolean ready = true;
|
348 |
|
|
for(int v : line_inserted) if(v != 1) ready = false;
|
349 |
|
|
|
350 |
|
|
// create PNG image file when frame ready
|
351 |
|
|
if(ready) {
|
352 |
|
|
BufferedImage img = new BufferedImage(640,480, BufferedImage.TYPE_INT_ARGB);
|
353 |
|
|
for(int i=0; i<480; i++) {
|
354 |
|
|
for(int j=0; j<640; j++) {
|
355 |
|
|
img.setRGB(j, i, screen[i][j]);
|
356 |
|
|
}
|
357 |
|
|
}
|
358 |
|
|
File output_dir = new File(args[2]);
|
359 |
|
|
if(output_dir.exists() == false || output_dir.isDirectory() == false) throw new Exception("Output directory illegal: " + output_dir.getCanonicalPath());
|
360 |
|
|
|
361 |
|
|
File output_file = new File(output_dir.getCanonicalFile() + File.separator + "frame_" + frame_number);
|
362 |
|
|
boolean written = ImageIO.write(img, "PNG", output_file);
|
363 |
|
|
if(written == false) throw new Exception("Could not write image file: " + output_file.getCanonicalPath());
|
364 |
|
|
|
365 |
|
|
System.out.println("Written frame number: " + frame_number);
|
366 |
|
|
frame_number++;
|
367 |
|
|
for(int i=0; i<line_inserted.length; i++) line_inserted[i] = 0;
|
368 |
|
|
}
|
369 |
|
|
}
|
370 |
|
|
}
|
371 |
|
|
}
|
372 |
|
|
static OutputStream p_out;
|
373 |
|
|
}
|