initial commit
This commit is contained in:
commit
1ff32316f8
23
README.md
Normal file
23
README.md
Normal file
@ -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.
|
BIN
chicken art 32 compressed.iff
Normal file
BIN
chicken art 32 compressed.iff
Normal file
Binary file not shown.
BIN
chicken art 32 uncompressed.iff
Normal file
BIN
chicken art 32 uncompressed.iff
Normal file
Binary file not shown.
349
iffilbm.c
Normal file
349
iffilbm.c
Normal file
@ -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;
|
||||
}
|
260
load_uncompressed.c
Normal file
260
load_uncompressed.c
Normal file
@ -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);
|
||||
}
|
||||
|
36
vgapal.c
Normal file
36
vgapal.c
Normal file
@ -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
Block a user