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