initial commit
This commit is contained in:
commit
1ff32316f8
|
@ -0,0 +1,23 @@
|
||||||
|
# IFF ILBM reader on DOS PC
|
||||||
|
|
||||||
|
There's three pieces of code in here, two for the PC and one for the Amiga.
|
||||||
|
|
||||||
|
## PC code
|
||||||
|
|
||||||
|
* `vgapal.c` cycles through every greyscale color that VGA can handle.
|
||||||
|
* `iffilbm.c` can read up to 32 color IFF ILBM files, either compressed
|
||||||
|
or uncompressed, cropping them to the top left 320x200 pixels.
|
||||||
|
|
||||||
|
Compile using [Open Watcom 2.0](https://open-watcom.github.io/)
|
||||||
|
and use the the DOS/4G protected mode compiler `wlc386`.
|
||||||
|
|
||||||
|
## Amiga code
|
||||||
|
|
||||||
|
* `load_uncompressed.c` will render `chiicken art 32 uncompressed.iff` without
|
||||||
|
doing any translation of the BODY chunk -- it will load it raw into Chip
|
||||||
|
RAM and set up the correct modulos for the custom chips to display it.
|
||||||
|
|
||||||
|
Compile using SAS/C: `sc load_uncompressed.c link`.
|
||||||
|
|
||||||
|
All code and art is copyright 2024 John Bintz. Code is released under the
|
||||||
|
MIT License. Art is all rights reserved.
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,349 @@
|
||||||
|
/*
|
||||||
|
* Read an IFF ILBM image and display it in VGA 320x200.
|
||||||
|
*
|
||||||
|
* Copyright 2024 John Bintz. Licensed under the MIT
|
||||||
|
* License. Visit https://theindustriousrabbit.com
|
||||||
|
* for more!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dos.h>
|
||||||
|
#include <conio.h>
|
||||||
|
|
||||||
|
char buffer[8];
|
||||||
|
long current_chunk_size;
|
||||||
|
|
||||||
|
char far *VGA = (char*)0xA0000;
|
||||||
|
|
||||||
|
typedef struct BMHDHeader {
|
||||||
|
unsigned short int width;
|
||||||
|
unsigned short int height;
|
||||||
|
unsigned short int _a1;
|
||||||
|
unsigned short int _a2;
|
||||||
|
unsigned char planes;
|
||||||
|
unsigned char _a3;
|
||||||
|
unsigned char compression;
|
||||||
|
unsigned char _a4;
|
||||||
|
unsigned short int _a5;
|
||||||
|
unsigned char _a6;
|
||||||
|
unsigned char _a7;
|
||||||
|
unsigned short int _a8;
|
||||||
|
unsigned short int _a9;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ILBMColor {
|
||||||
|
unsigned char red;
|
||||||
|
unsigned char green;
|
||||||
|
unsigned char blue;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ImageReadData {
|
||||||
|
short int width;
|
||||||
|
short int height;
|
||||||
|
char compressed;
|
||||||
|
char planes;
|
||||||
|
FILE *fh;
|
||||||
|
char *row_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a big endian long (68k) into little endian (x86)
|
||||||
|
*/
|
||||||
|
long ntohl(long input) {
|
||||||
|
return input >> 24 & 0xFF |
|
||||||
|
input >> 8 & 0xFF00 |
|
||||||
|
input << 8 & 0xFF0000 |
|
||||||
|
input << 24 & 0xFF000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a big endian word (68k) into little endian (x86)
|
||||||
|
*/
|
||||||
|
unsigned short int ntohw(unsigned short int input) {
|
||||||
|
return input >> 8 & 0xFF |
|
||||||
|
input << 8 & 0xFF00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the next IFF chunk header.
|
||||||
|
*/
|
||||||
|
void read_chunk_header(FILE *fh) {
|
||||||
|
int read_count;
|
||||||
|
|
||||||
|
read_count = fread(buffer, sizeof(char), 8, fh);
|
||||||
|
current_chunk_size = ntohl(*((long*)(buffer + 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the filetype chunk, which is 4 bytes long.
|
||||||
|
*/
|
||||||
|
void read_filetype_chunk(FILE *fh) {
|
||||||
|
fread(buffer, sizeof(char), 4, fh);
|
||||||
|
|
||||||
|
// reset the chunk size to 0 so no future code tries to use it.
|
||||||
|
current_chunk_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current chunk type is of the requested type.
|
||||||
|
* This doesn't use strcmp because the buffer only contains the
|
||||||
|
* four characters in the IFF header.
|
||||||
|
*/
|
||||||
|
int check_chunk_type(char* type) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; ++i) {
|
||||||
|
if (type[i] != buffer[i]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the BMHD struct.
|
||||||
|
*
|
||||||
|
* This also converts the width and height to little endian.
|
||||||
|
*/
|
||||||
|
void read_bmhd_data(FILE *fh, struct BMHDHeader *bmhd) {
|
||||||
|
fread(bmhd, sizeof(struct BMHDHeader), 1, fh);
|
||||||
|
bmhd->width = ntohw(bmhd->width);
|
||||||
|
bmhd->height = ntohw(bmhd->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the CMAP data into the palette.
|
||||||
|
*/
|
||||||
|
int read_cmap_data(FILE *fh, struct ILBMColor palette[]) {
|
||||||
|
int number_of_colors = current_chunk_size / 3;
|
||||||
|
|
||||||
|
fread(palette, sizeof(struct ILBMColor), number_of_colors, fh);
|
||||||
|
|
||||||
|
return number_of_colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read Amiga-specific display info.
|
||||||
|
*/
|
||||||
|
void read_camg_data(FILE *fh) {
|
||||||
|
long whatever;
|
||||||
|
// we throw this out
|
||||||
|
fread(&whatever, sizeof(long), 1, fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a bitplane byte of data into indexed color data.
|
||||||
|
*/
|
||||||
|
void add_byte_to_row_data(
|
||||||
|
unsigned char data,
|
||||||
|
int plane,
|
||||||
|
int x,
|
||||||
|
unsigned char *row_data
|
||||||
|
) {
|
||||||
|
int bit;
|
||||||
|
|
||||||
|
for (bit = 7; bit >= 0; --bit) {
|
||||||
|
row_data[x + bit] |= ((data & 1) << plane);
|
||||||
|
data >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read BODY data and write to the VGA buffer.
|
||||||
|
*/
|
||||||
|
void read_body_data(struct ImageReadData *image_read_data) {
|
||||||
|
int y, x, literal, plane;
|
||||||
|
unsigned char data;
|
||||||
|
|
||||||
|
char far *current_vga_pos;
|
||||||
|
|
||||||
|
unsigned char *row_data = malloc(image_read_data->width);
|
||||||
|
int pos = 0;
|
||||||
|
int run_count;
|
||||||
|
|
||||||
|
for (y = 0; y < image_read_data->height; ++y) {
|
||||||
|
for (x = 0; x < image_read_data->width; ++x) {
|
||||||
|
row_data[x] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (plane = 0; plane < image_read_data->planes; ++plane) {
|
||||||
|
for (x = 0; x < image_read_data->width;) {
|
||||||
|
// ByteRun1 run encoding?
|
||||||
|
if (image_read_data->compressed) {
|
||||||
|
fread(&data, 1, 1, image_read_data->fh);
|
||||||
|
if (data < 128) {
|
||||||
|
// literal: copy the next bytes from the data stream
|
||||||
|
for (run_count = data + 1; run_count > 0; --run_count) {
|
||||||
|
fread(&data, 1, 1, image_read_data->fh);
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
// we can't handle more than 320 columns of data
|
||||||
|
if (x < 320) {
|
||||||
|
add_byte_to_row_data(data, plane, x, row_data);
|
||||||
|
}
|
||||||
|
x += 8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// repeat: copy the next byte this many times into the data stream
|
||||||
|
run_count = 129 - (data - 128);
|
||||||
|
fread(&data, 1, 1, image_read_data->fh);
|
||||||
|
|
||||||
|
for (; run_count > 0; --run_count) {
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
// we can't handle more than 320 columns of data
|
||||||
|
if (x < 320) {
|
||||||
|
add_byte_to_row_data(data, plane, x, row_data);
|
||||||
|
}
|
||||||
|
x += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// uncompressed, copy the next byte into the data stream
|
||||||
|
fread(&data, 1, 1, image_read_data->fh);
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
// we can't handle more than 320 columns of data
|
||||||
|
if (x < 320) {
|
||||||
|
add_byte_to_row_data(data, plane, x, row_data);
|
||||||
|
}
|
||||||
|
x += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// data is written to the stream word-padded
|
||||||
|
if (pos & 1) {
|
||||||
|
fread(&data, 1, 1, image_read_data->fh);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write row data to VGA buffer
|
||||||
|
current_vga_pos = (char far *)(VGA + y * 320);
|
||||||
|
for (x = 0; x < image_read_data->width; ++x) {
|
||||||
|
*(current_vga_pos++) = row_data[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can't handle more than 200 lines of data
|
||||||
|
if (y >= 200) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(row_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
char *filename = argv[1];
|
||||||
|
int keep_reading = 1;
|
||||||
|
|
||||||
|
FILE *image;
|
||||||
|
|
||||||
|
struct BMHDHeader bmhd_header;
|
||||||
|
struct ILBMColor palette[32];
|
||||||
|
int i, number_of_colors;
|
||||||
|
|
||||||
|
struct ImageReadData image_read_data;
|
||||||
|
|
||||||
|
union REGS regs;
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
printf("Usage: %s filename.iff\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open for binary reading
|
||||||
|
// char 26 was not being read properly when I didn't do this
|
||||||
|
image = fopen(filename, "rb");
|
||||||
|
if (!image) {
|
||||||
|
printf("Can't open %s for reading.\n", filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_chunk_header(image);
|
||||||
|
if (!check_chunk_type("FORM")) {
|
||||||
|
printf("Not an IFF file");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_filetype_chunk(image);
|
||||||
|
if (!check_chunk_type("ILBM")) {
|
||||||
|
printf("Not an ILBM file");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* next chunks can be in any order, but once we hit BODY, we stop */
|
||||||
|
while (keep_reading) {
|
||||||
|
keep_reading = 0;
|
||||||
|
read_chunk_header(image);
|
||||||
|
|
||||||
|
if (check_chunk_type("BMHD")) {
|
||||||
|
read_bmhd_data(image, &bmhd_header);
|
||||||
|
|
||||||
|
keep_reading = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_chunk_type("CMAP")) {
|
||||||
|
number_of_colors = read_cmap_data(image, palette);
|
||||||
|
|
||||||
|
keep_reading = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_chunk_type("CAMG")) {
|
||||||
|
read_camg_data(image);
|
||||||
|
|
||||||
|
keep_reading = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_chunk_type("BODY")) {
|
||||||
|
// we made it, let's activate VGA mode and set up the
|
||||||
|
// palette now
|
||||||
|
regs.h.ah = 0x00;
|
||||||
|
regs.h.al = 0x13;
|
||||||
|
int386(0x10, ®s, ®s);
|
||||||
|
|
||||||
|
// if we send 0 to 0x3c8, we can dump all the colors in
|
||||||
|
// one after the other
|
||||||
|
outp(0x3c8, 0);
|
||||||
|
for (i = 0; i < number_of_colors; ++i) {
|
||||||
|
outp(0x3c9, (palette[i].red >> 2));
|
||||||
|
outp(0x3c9, (palette[i].green >> 2));
|
||||||
|
outp(0x3c9, (palette[i].blue >> 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
image_read_data.width = bmhd_header.width;
|
||||||
|
image_read_data.height = bmhd_header.height;
|
||||||
|
image_read_data.compressed = bmhd_header.compression;
|
||||||
|
image_read_data.planes = bmhd_header.planes;
|
||||||
|
image_read_data.fh = image;
|
||||||
|
|
||||||
|
read_body_data(&image_read_data);
|
||||||
|
// stop reading here
|
||||||
|
} else {
|
||||||
|
if (!keep_reading) {
|
||||||
|
// if we're out of chunks, stop reading
|
||||||
|
if (!feof(image)) {
|
||||||
|
// otherwise, it's an unknown chunk, skip it and keep reading
|
||||||
|
fseek(image, current_chunk_size, SEEK_CUR);
|
||||||
|
keep_reading = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait 10 seconds
|
||||||
|
delay(10000);
|
||||||
|
|
||||||
|
// back to 80x25 text mode
|
||||||
|
regs.h.ah = 0x00;
|
||||||
|
regs.h.al = 0x03;
|
||||||
|
int386(0x10, ®s, ®s);
|
||||||
|
|
||||||
|
if (fclose(image)) {
|
||||||
|
printf("Can't close %s for reading.\n", filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,260 @@
|
||||||
|
/**
|
||||||
|
* Displaying an uncompressed IFF ILBM image
|
||||||
|
* straight from RAM.
|
||||||
|
*
|
||||||
|
* Since ILBM images are interleaved bitmaps, if
|
||||||
|
* your display mode is set to be the same size as the
|
||||||
|
* image, you can treat that raw image data as the
|
||||||
|
* bitmap source for Agnus.
|
||||||
|
*
|
||||||
|
* Copyright 2024 John Bintz. Licensed under the MIT
|
||||||
|
* License. Visit https://theindustriousrabbit.com
|
||||||
|
* for more!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <clib/dos_protos.h>
|
||||||
|
#include <clib/exec_protos.h>
|
||||||
|
#include <clib/intuition_protos.h>
|
||||||
|
#include <clib/graphics_protos.h>
|
||||||
|
#include <exec/memory.h>
|
||||||
|
#include <graphics/GfxBase.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <hardware/cia.h>
|
||||||
|
#include <hardware/dmabits.h>
|
||||||
|
|
||||||
|
#define BPLCON0_COLOR (0x200)
|
||||||
|
|
||||||
|
// We only need four fields from the image header
|
||||||
|
typedef struct BMHDHeader {
|
||||||
|
UWORD width;
|
||||||
|
UWORD height;
|
||||||
|
UWORD _a1;
|
||||||
|
UWORD _a2;
|
||||||
|
UBYTE planes;
|
||||||
|
UBYTE _a3;
|
||||||
|
UBYTE compression;
|
||||||
|
UBYTE _a4;
|
||||||
|
UWORD _a5;
|
||||||
|
UBYTE _a6;
|
||||||
|
UBYTE _a7;
|
||||||
|
UWORD _a8;
|
||||||
|
UWORD _a9;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make it very easy to load all color info at
|
||||||
|
// once.
|
||||||
|
typedef struct ILBMColor {
|
||||||
|
UBYTE red;
|
||||||
|
UBYTE green;
|
||||||
|
UBYTE blue;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct GfBase *GfxBase;
|
||||||
|
extern struct Custom far custom;
|
||||||
|
extern struct CIA far ciaa,ciab;
|
||||||
|
|
||||||
|
// The code below follows the same pattern as the
|
||||||
|
// PC DOS IFF ILBM reader.
|
||||||
|
UBYTE charBuffer[8];
|
||||||
|
ULONG chunkLength;
|
||||||
|
|
||||||
|
struct ILBMColor palette[32];
|
||||||
|
struct BMHDHeader bmhdHeader;
|
||||||
|
|
||||||
|
void readChunk(BPTR fh) {
|
||||||
|
FRead(fh, &charBuffer, 8, 1);
|
||||||
|
chunkLength = *(ULONG *)(charBuffer + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
BYTE checkChunkType(char type[]) {
|
||||||
|
BYTE i;
|
||||||
|
for (i = 0; i < 4; ++i) {
|
||||||
|
if (type[i] != charBuffer[i]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
int color, plane, imageModulo;
|
||||||
|
BYTE keepReading = 1;
|
||||||
|
void *imageData, *copperlist;
|
||||||
|
UWORD *copperlist_ptr;
|
||||||
|
|
||||||
|
struct View *OldView = ((struct GfxBase *)GfxBase)->ActiView;
|
||||||
|
ULONG OldCopper = (ULONG)((struct GfxBase *)GfxBase)->copinit;
|
||||||
|
ULONG OldDMACON, OldINTENA, OldINTREQ, OldADKCON;
|
||||||
|
|
||||||
|
// this is a 320x200x32 uncompressed IFF ILBM image.
|
||||||
|
BPTR fh = Open("chicken art 32 uncompressed.iff", MODE_OLDFILE);
|
||||||
|
|
||||||
|
readChunk(fh);
|
||||||
|
|
||||||
|
if (!checkChunkType("FORM")) {
|
||||||
|
printf("Not IFF\n");
|
||||||
|
Close(fh);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FRead(fh, &charBuffer, 4, 1);
|
||||||
|
if (!checkChunkType("ILBM")) {
|
||||||
|
printf("Not ILBM\n");
|
||||||
|
Close(fh);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (keepReading) {
|
||||||
|
keepReading = 0;
|
||||||
|
|
||||||
|
readChunk(fh);
|
||||||
|
printf("Chunk length: %d\n", chunkLength);
|
||||||
|
if (checkChunkType("BMHD")) {
|
||||||
|
FRead(fh, &bmhdHeader, sizeof(struct BMHDHeader), 1);
|
||||||
|
printf("Width: %d\n", bmhdHeader.width);
|
||||||
|
printf("Height: %d\n", bmhdHeader.height);
|
||||||
|
printf("Bitplanes: %d\n", bmhdHeader.planes);
|
||||||
|
printf("Compression: %d\n", bmhdHeader.compression);
|
||||||
|
|
||||||
|
if (bmhdHeader.compression == 1) {
|
||||||
|
printf("Can't read compressed files\n");
|
||||||
|
Close(fh);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
keepReading = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkChunkType("CMAP")) {
|
||||||
|
printf("Colors: %d\n", 1 << bmhdHeader.planes);
|
||||||
|
FRead(fh, &palette, sizeof(struct ILBMColor), 1 << bmhdHeader.planes);
|
||||||
|
|
||||||
|
for (color = 0; color < (1 << bmhdHeader.planes); ++color) {
|
||||||
|
printf("Color %d: %d %d %d\n", color, palette[color].red, palette[color].green, palette[color].blue);
|
||||||
|
}
|
||||||
|
keepReading = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkChunkType("BODY")) {
|
||||||
|
printf("Made it to BODY\n");
|
||||||
|
|
||||||
|
// read raw image data into Chip RAM for Agnus
|
||||||
|
imageData = AllocMem(chunkLength, MEMF_CHIP | MEMF_CLEAR);
|
||||||
|
FRead(fh, imageData, chunkLength, 1);
|
||||||
|
|
||||||
|
// The modulo is what makes this work. While Agnus is
|
||||||
|
// reading data from the bitplane pointers, those pointer
|
||||||
|
// values are increasing (See "Amiga Rasterbars are Cool"
|
||||||
|
// to observe this in action). At the end of the display
|
||||||
|
// line, Agnus will then add the modulo vaule to the
|
||||||
|
// bitplane pointer. This allows you to interleave image
|
||||||
|
// data, row by row, in memory, rather than having each
|
||||||
|
// bitplane be a separate chunk of memory.
|
||||||
|
imageModulo = (bmhdHeader.width + 8 - 1) / 8;
|
||||||
|
printf("Modulo: %d\n", imageModulo);
|
||||||
|
|
||||||
|
OldDMACON = custom.dmaconr | 0x8000;
|
||||||
|
OldINTENA = custom.intenar | 0x8000;
|
||||||
|
OldINTREQ = custom.intreqr | 0x8000;
|
||||||
|
OldADKCON = custom.adkconr | 0x8000;
|
||||||
|
|
||||||
|
// disable interrupts
|
||||||
|
custom.intena = 0xc000;
|
||||||
|
custom.intena = 0x3fff;
|
||||||
|
|
||||||
|
// set up dma
|
||||||
|
custom.dmacon = DMAF_SETCLR | DMAF_COPPER | DMAF_RASTER;
|
||||||
|
custom.dmacon = DMAF_AUDIO | DMAF_DISK | DMAF_SPRITE | DMAF_BLITTER;
|
||||||
|
|
||||||
|
// take control
|
||||||
|
LoadView(NULL);
|
||||||
|
WaitTOF();
|
||||||
|
WaitTOF();
|
||||||
|
OwnBlitter();
|
||||||
|
WaitBlit();
|
||||||
|
Forbid();
|
||||||
|
|
||||||
|
// Activate color rendering for as many bitplanes in the file
|
||||||
|
custom.bplcon0 = BPLCON0_COLOR | (bmhdHeader.planes << 12);
|
||||||
|
custom.bplcon1 = 0;
|
||||||
|
|
||||||
|
// The modulo is being added to the pointer once a display line
|
||||||
|
// has been read. We need to skip (number of planes - 1) rows down
|
||||||
|
// to get to the start of the next line for the bitplane in question.
|
||||||
|
custom.bpl1mod = imageModulo * (bmhdHeader.planes - 1);
|
||||||
|
custom.bpl2mod = imageModulo * (bmhdHeader.planes - 1);
|
||||||
|
|
||||||
|
// 320x256 display
|
||||||
|
custom.diwstrt = 0x2c21;
|
||||||
|
custom.diwstop = 0x2cc1;
|
||||||
|
custom.ddfstrt = 0x0038;
|
||||||
|
custom.ddfstop = 0x00d0;
|
||||||
|
|
||||||
|
// create our copperlist
|
||||||
|
copperlist = AllocMem(2000, MEMF_CHIP | MEMF_CLEAR);
|
||||||
|
copperlist_ptr = (UWORD *)copperlist;
|
||||||
|
|
||||||
|
// bitplane pointers
|
||||||
|
for (plane = 0; plane < bmhdHeader.planes; ++plane) {
|
||||||
|
// We use the modulos here as well, to start each bitplane pointer on
|
||||||
|
// the correct row of data.
|
||||||
|
*(copperlist_ptr++) = 0x0E0 + (plane * 4);
|
||||||
|
*(copperlist_ptr++) = (((ULONG)imageData + plane * imageModulo) >> 16) & 0xffff;
|
||||||
|
|
||||||
|
*(copperlist_ptr++) = 0x0E2 + (plane * 4);
|
||||||
|
*(copperlist_ptr++) = (((ULONG)imageData + plane * imageModulo)) & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// palette
|
||||||
|
for (color = 0; color < (1 << bmhdHeader.planes); ++color) {
|
||||||
|
*(copperlist_ptr++) = 0x180 + color * 2;
|
||||||
|
*(copperlist_ptr++) = (palette[color].red >> 4) << 8 |
|
||||||
|
(palette[color].green >> 4) << 4 |
|
||||||
|
(palette[color].blue >> 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
*(copperlist_ptr++) = 0xffff;
|
||||||
|
*(copperlist_ptr++) = 0xfffe;
|
||||||
|
|
||||||
|
custom.cop1lc = (ULONG)copperlist;
|
||||||
|
|
||||||
|
// wait for a mouse click
|
||||||
|
while ((ciaa.ciapra >> 6) == 3) {}
|
||||||
|
|
||||||
|
// put everything back
|
||||||
|
custom.dmacon = 0x7FFF;
|
||||||
|
custom.dmacon = OldDMACON;
|
||||||
|
custom.intena = 0x7FFF;
|
||||||
|
custom.intena = OldINTENA;
|
||||||
|
custom.intreq = 0x7FFF;
|
||||||
|
custom.intreq = OldINTREQ;
|
||||||
|
custom.adkcon = 0x7FFF;
|
||||||
|
custom.adkcon = OldADKCON;
|
||||||
|
|
||||||
|
custom.cop1lc = OldCopper;
|
||||||
|
LoadView(OldView);
|
||||||
|
WaitTOF();
|
||||||
|
WaitTOF();
|
||||||
|
WaitBlit();
|
||||||
|
DisownBlitter();
|
||||||
|
Permit();
|
||||||
|
|
||||||
|
// free the RAM we allocated
|
||||||
|
FreeMem(copperlist, 2000);
|
||||||
|
FreeMem(imageData, chunkLength);
|
||||||
|
} else {
|
||||||
|
if (!keepReading) {
|
||||||
|
// ignore every other chunk
|
||||||
|
if (chunkLength & 1 == 1) chunkLength++;
|
||||||
|
if (Seek(fh, chunkLength, OFFSET_CURRENT) != -1) {
|
||||||
|
keepReading = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Close(fh);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Cycle through every greyscale value that VGA can handle.
|
||||||
|
*
|
||||||
|
* Copyright 2024 John Bintz. Licensed under the MIT
|
||||||
|
* License. Visit https://theindustriousrabbit.com
|
||||||
|
* for more!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dos.h>
|
||||||
|
#include <conio.h>
|
||||||
|
|
||||||
|
char far *VGA = (char*)0xA0000;
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
union REGS regs;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
regs.h.ah = 0x00;
|
||||||
|
regs.h.al = 0x13;
|
||||||
|
int386(0x10, ®s, ®s);
|
||||||
|
|
||||||
|
for (i = 0; i < 63; ++i) {
|
||||||
|
outp(0x3c8, 0);
|
||||||
|
outp(0x3c9, i);
|
||||||
|
outp(0x3c9, i);
|
||||||
|
outp(0x3c9, i);
|
||||||
|
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
regs.h.ah = 0x00;
|
||||||
|
regs.h.al = 0x03;
|
||||||
|
int386(0x10, ®s, ®s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue