FreeBE/mach64/driver.c

1809 lines
50 KiB
C
Raw Permalink Normal View History

2024-05-29 19:55:33 +03:00
/*
2024-05-29 20:11:25 +03:00
* ______ ____ ______ _____ ______
2024-05-29 19:55:33 +03:00
* | ____| | _ \| ____| / / _ \| ____|
2024-05-29 20:11:25 +03:00
* | |__ _ __ ___ ___| |_) | |__ / / |_| | |__
2024-05-29 19:55:33 +03:00
* | __| '__/ _ \/ _ \ _ <| __| / /| _ | __|
* | | | | | __/ __/ |_) | |____ / / | | | | |
* |_| |_| \___|\___|____/|______/_/ |_| |_|_|
*
*
* ATI mach64 accelerated driver.
*
* Written by Ove Kaaven <ovek@arcticnet.no>
*
* See freebe.txt for copyright information.
*/
#include <pc.h>
#include "vbeaf.h"
/* [OK] if you don't like VESA and like accessing the mach64 BIOS directly
2024-05-29 20:11:25 +03:00
better, define this, kill the bugs, and report to me... */
2024-05-29 19:55:33 +03:00
#undef REFUSE_VESA
/* driver function prototypes */
2024-05-29 20:11:25 +03:00
void SetBank32( );
void SetBank32End( );
int ExtStub( );
2024-05-29 19:55:33 +03:00
long GetVideoModeInfo(AF_DRIVER *af, short mode, AF_MODE_INFO *modeInfo);
2024-05-29 20:11:25 +03:00
long SetVideoMode(AF_DRIVER *af, short mode, long virtualX, long virtualY,
long *bytesPerLine, int numBuffers, AF_CRTCInfo *crtc);
2024-05-29 19:55:33 +03:00
void RestoreTextMode(AF_DRIVER *af);
long GetClosestPixelClock(AF_DRIVER *af, short mode, unsigned long pixelClock);
void SaveRestoreState(AF_DRIVER *af, int subfunc, void *saveBuf);
void SetDisplayStart(AF_DRIVER *af, long x, long y, long waitVRT);
void SetActiveBuffer(AF_DRIVER *af, long index);
void SetVisibleBuffer(AF_DRIVER *af, long index, long waitVRT);
2024-05-29 20:11:25 +03:00
int GetDisplayStartStatus(AF_DRIVER *af);
void SetPaletteData(AF_DRIVER *af, AF_PALETTE *pal, long num, long index,
long waitVRT);
2024-05-29 19:55:33 +03:00
void SetBank(AF_DRIVER *af, long bank);
void WaitForFifo(AF_DRIVER *af, int entries);
void WaitTillIdle(AF_DRIVER *af);
void ResetEngine(AF_DRIVER *af);
void InitEngine(AF_DRIVER *af, int width, int height, int bpp);
void SetMix(AF_DRIVER *af, long foreMix, long backMix);
void Set8x8MonoPattern(AF_DRIVER *af, unsigned char *pattern);
void Set8x8ColorPattern(AF_DRIVER *af, int index, unsigned long *pattern);
void Use8x8ColorPattern(AF_DRIVER *af, int index);
void DrawScan(AF_DRIVER *af, long color, long y, long x1, long x2);
2024-05-29 20:11:25 +03:00
void DrawPattScan(AF_DRIVER *af, long foreColor, long backColor, long y,
long x1, long x2);
2024-05-29 19:55:33 +03:00
void DrawColorPattScan(AF_DRIVER *af, long y, long x1, long x2);
2024-05-29 20:11:25 +03:00
void DrawRect(AF_DRIVER *af, unsigned long color, long left, long top,
long width, long height);
void DrawPattRect(AF_DRIVER *af, unsigned long foreColor,
unsigned long backColor, long left, long top, long width,
long height);
void DrawColorPattRect(AF_DRIVER *af, long left, long top, long width,
long height);
void BitBlt(AF_DRIVER *af, long left, long top, long width, long height,
long dstLeft, long dstTop, long op);
void SrcTransBlt(AF_DRIVER *af, long left, long top, long width, long height,
long dstLeft, long dstTop, long op, unsigned long transparent);
void DrawLine(AF_DRIVER *af, unsigned long color, fixed x1, fixed y1, fixed x2,
fixed y2);
2024-05-29 19:55:33 +03:00
/* if you need some video memory for internal use by the accelerator
2024-05-29 20:11:25 +03:00
* code (for example storing pattern data), you can define this value to
2024-05-29 19:55:33 +03:00
* reserve room for yourself at the very end of the memory space, that
* the application will not be allowed to use.
*/
2024-05-29 20:11:25 +03:00
#define RESERVED_VRAM 0
2024-05-29 19:55:33 +03:00
/* list which ports we are going to access (only needed under Linux) */
unsigned short ports_table[] = { 0xFFFF };
/* at startup we use the get_vesa_info() helper function to find out
* what resolutions the VESA driver provides for this card, storing them
* in a table for future use. In a real driver you would replace this
2024-05-29 20:11:25 +03:00
* VESA code with a static list of modes and their attributes, because
2024-05-29 19:55:33 +03:00
* you would know right from the start what modes are possible on your
* card.
*/
2024-05-29 20:11:25 +03:00
typedef struct VIDEO_MODE {
int vesa_num;
int bios_num;
int w;
int h;
int bpp;
int redsize;
int redpos;
int greensize;
int greenpos;
int bluesize;
int bluepos;
int rsvdsize;
int rsvdpos;
2024-05-29 19:55:33 +03:00
} VIDEO_MODE;
2024-05-29 20:11:25 +03:00
#define MAX_MODES 128
2024-05-29 19:55:33 +03:00
/* [OK] we'll ignore the 4-bit (16-color) modes */
/* [OK] the idea of a mode list *and* VESA mode inquiry is that
certain modes seems to be available only through VESA (320x200, 640x400),
and some only through the mach64 BIOS (1600x1200, 32bpp modes) */
2024-05-29 20:11:25 +03:00
VIDEO_MODE mode_list[MAX_MODES] = {
{ 0x100, 0, 640, 400, 8 },
{ 0x101, 0x12C2, 640, 480, 8 },
{ 0x103, 0x6AC2, 800, 600, 8 },
{ 0x105, 0x55C2, 1024, 768, 8 },
{ 0x107, 0x83C2, 1280, 1024, 8 },
{ 0x110, 0x12C3, 640, 480, 15, 5, 10, 5, 5, 5, 0, 0, 0 },
{ 0x111, 0x12C4, 640, 480, 16, 5, 11, 6, 5, 5, 0, 0, 0 },
{ 0x112, 0x12C5, 640, 480, 24, 8, 16, 8, 8, 8, 0, 0, 0 },
{ 0x113, 0x6AC3, 800, 600, 15, 5, 10, 5, 5, 5, 0, 0, 0 },
{ 0x114, 0x6AC4, 800, 600, 16, 5, 11, 6, 5, 5, 0, 0, 0 },
/* [OK] Tim Riemann reported these >1MB modes (and the 0x107 above) */
{ 0x115, 0x6AC5, 800, 600, 24, 8, 16, 8, 8, 8, 0, 0, 0 },
{ 0x116, 0x55C3, 1024, 768, 15, 5, 10, 5, 5, 5, 0, 0, 0 },
{ 0x117, 0x55C4, 1024, 768, 16, 5, 11, 6, 5, 5, 0, 0, 0 },
{ 0x118, 0x55C5, 1024, 768, 24, 8, 16, 8, 8, 8, 0, 0, 0 },
{ 0x119, 0x83C3, 1280, 1024, 15, 5, 10, 5, 5, 5, 0, 0, 0 },
{ 0x11A, 0x83C4, 1280, 1024, 16, 5, 11, 6, 5, 5, 0, 0, 0 },
{ 0x11B, 0x83C5, 1280, 1024, 24, 8, 16, 8, 8, 8, 0, 0, 0 },
/* [OK] nobody has reported VESA mode numbers for the following yet */
/* [OK] ...are there any at all? anyone want to report on these? */
/* [OK] 1600x1200 modes */
{ 0, 0x84C2, 1600, 1200, 8 },
{ 0, 0x84C3, 1600, 1200, 15, 5, 10, 5, 5, 5, 0, 0, 0 },
{ 0, 0x84C4, 1600, 1200, 16, 5, 11, 6, 5, 5, 0, 0, 0 },
{ 0, 0x84C5, 1600, 1200, 24, 8, 16, 8, 8, 8, 0, 0, 0 },
/* [OK] 32 bpp modes (mostly guesswork) */
{ 0, 0x12C6, 640, 480, 32, 8, 16, 8, 8, 8, 0, 0, 0 },
{ 0, 0x6AC6, 800, 600, 32, 8, 16, 8, 8, 8, 0, 0, 0 },
{ 0, 0x55C6, 1024, 768, 32, 8, 16, 8, 8, 8, 0, 0, 0 },
{ 0, 0x83C6, 1280, 1024, 32, 8, 16, 8, 8, 8, 0, 0, 0 },
{ 0, 0x84C6, 1600, 1200, 32, 8, 16, 8, 8, 8, 0, 0, 0 }
/* [OK] if VESA mode numbers do not exist, I may have to use bios_num */
2024-05-29 19:55:33 +03:00
};
2024-05-29 20:11:25 +03:00
short available_modes[MAX_MODES + 1] = { 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, */
-1 };
2024-05-29 19:55:33 +03:00
int num_modes = 17;
/* internal driver state variables */
int af_bpp;
int af_width;
int af_height;
int af_linear;
int af_visible_page;
int af_active_page;
int af_scroll_x;
int af_scroll_y;
int af_bank;
int af_fore_mix;
int af_back_mix;
unsigned mach64_dpmix, mach64_dpsrc;
unsigned mach64_dcntl, mach64_scntl;
/* register addresses */
int mach64_floating, mach64_iobase;
int mach64_wpsel, mach64_rpsel, mach64_offpitch;
int mach64_dacrg, mach64_intr;
#define ioOFFPITCH 0x05
2024-05-29 20:11:25 +03:00
#define ioINTCNTL 0x06
#define ioGENCNTL 0x07
2024-05-29 19:55:33 +03:00
#define ioSCRATCH0 0x10
#define ioSCRATCH1 0x11
2024-05-29 20:11:25 +03:00
#define ioBUSCNTL 0x13
#define ioWPSEL 0x15
#define ioRPSEL 0x16
#define ioDACREGS 0x17
#define ioDACCNTL 0x18
#define ioGTCNTL 0x19
2024-05-29 19:55:33 +03:00
#define mmOFFPITCH 0x05
2024-05-29 20:11:25 +03:00
#define mmINTCNTL 0x06
#define mmGENCNTL 0x07
2024-05-29 19:55:33 +03:00
#define mmSCRATCH0 0x20
#define mmSCRATCH1 0x21
2024-05-29 20:11:25 +03:00
#define mmBUSCNTL 0x28
#define mmWPSEL 0x2D
#define mmRPSEL 0x2E
#define mmDACREGS 0x30
#define mmDACCNTL 0x31
#define mmGTCNTL 0x34
2024-05-29 19:55:33 +03:00
#define HWCURSOR_ENABLE 0x80
2024-05-29 20:11:25 +03:00
#define ENGINE_ENABLE 0x100
2024-05-29 19:55:33 +03:00
#define mmDOFFPTCH 0x40
2024-05-29 20:11:25 +03:00
#define mmDX 0x41
#define mmDY 0x42
#define mmDYX 0x43
#define mmDWDTH 0x44
#define mmDHT 0x45
#define mmDHTWDTH 0x46
#define mmDXWDTH 0x47
#define mmDBRSLEN 0x48
#define mmDBRSERR 0x49
#define mmDBRSINC 0x4A
#define mmDBRSDEC 0x4B
#define mmDCNTL 0x4C
#define DC_X_R2L 0x00
#define DC_X_L2R 0x01
#define DC_Y_B2T 0x00
#define DC_Y_T2B 0x02
#define DC_X_MAJ 0x00
#define DC_Y_MAJ 0x04
2024-05-29 19:55:33 +03:00
#define DC_X_TILE 0x08
#define DC_Y_TILE 0x10
#define DC_LAST_P 0x20
2024-05-29 20:11:25 +03:00
#define DC_POLY 0x40
#define DC_R_24 0x80
2024-05-29 19:55:33 +03:00
#define mmSOFFPTCH 0x60
2024-05-29 20:11:25 +03:00
#define mmSX 0x61
#define mmSY 0x62
#define mmSYX 0x63
#define mmSWDTH1 0x64
#define mmSHT1 0x65
2024-05-29 19:55:33 +03:00
#define mmSHTWDTH1 0x66
2024-05-29 20:11:25 +03:00
#define mmSXSTRT 0x67
#define mmSYSTRT 0x68
#define mmSYXSTRT 0x69
#define mmSWDTH2 0x6A
#define mmSHT2 0x6B
2024-05-29 19:55:33 +03:00
#define mmSHTWDTH2 0x6C
2024-05-29 20:11:25 +03:00
#define mmSCNTL 0x6D
#define SC_PATT 0x01
#define SC_ROT 0x02
#define SC_LIN 0x04
2024-05-29 19:55:33 +03:00
#define SC_BYTE_A 0x08
#define SC_LX_R2L 0x00
#define SC_LX_L2R 0x10
#define mmHOSTCNTL 0x90
2024-05-29 20:11:25 +03:00
#define mmPATREG0 0xA0
#define mmPATREG1 0xA1
#define mmPATCNTL 0xA2
2024-05-29 19:55:33 +03:00
#define PC_MONO 0x01
#define PC_C4x2 0x02
#define PC_C8x1 0x04
2024-05-29 20:11:25 +03:00
#define mmSCLEFT 0xA8
#define mmSCRIGHT 0xA9
#define mmSCLTRT 0xAA
#define mmSCTOP 0xAB
2024-05-29 19:55:33 +03:00
#define mmSCBOTTOM 0xAC
2024-05-29 20:11:25 +03:00
#define mmSCTPBT 0xAD
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
#define mmDPBGCOL 0xB0
#define mmDPFGCOL 0xB1
#define mmDPWRMSK 0xB2
2024-05-29 19:55:33 +03:00
#define mmDPCHNMSK 0xB3
2024-05-29 20:11:25 +03:00
#define mmPIXWDTH 0xB4
#define PW_1 0
#define PW_4 1
#define PW_8 2
2024-05-29 19:55:33 +03:00
#define PW_15 3
#define PW_16 4
#define PW_32 6
#define PW_MSB 0x00000000
#define PW_LSB 0x01000000
2024-05-29 20:11:25 +03:00
#define mmDPMIX 0xB5
#define mmDPSRC 0xB6
2024-05-29 19:55:33 +03:00
#define DS_BGCOL 0x00
#define DS_FGCOL 0x01
2024-05-29 20:11:25 +03:00
#define DS_HOST 0x02
#define DS_BLIT 0x03
#define DS_PATT 0x04
2024-05-29 19:55:33 +03:00
#define DSM_TRUE 0x00000
#define DSM_PATT 0x10000
#define DSM_HOST 0x20000
#define DSM_BLIT 0x30000
2024-05-29 20:11:25 +03:00
#define mmCCMPCLR 0xC0
#define mmCCMPMSK 0xC1
2024-05-29 19:55:33 +03:00
#define mmCCMPCNTL 0xC2
#define CC_FALSE 0x00
2024-05-29 20:11:25 +03:00
#define CC_TRUE 0x01
2024-05-29 19:55:33 +03:00
#define CC_NOTEQ 0x04
#define CC_EQUAL 0x05
2024-05-29 20:11:25 +03:00
#define CC_DST 0x00000000
#define CC_SRC 0x01000000
2024-05-29 19:55:33 +03:00
#define mmFIFOSTAT 0xC4
#define FIFO_ERR 0x80000000
2024-05-29 20:11:25 +03:00
#define mmGUISTAT 0xCE
2024-05-29 19:55:33 +03:00
#define ENGINE_BUSY 1
#define mmCNTXMASK 0xC8
#define mmCNTXLDCT 0xCB
#define mmTRAJCNTL 0xCC
2024-05-29 20:11:25 +03:00
#define mmGUISTAT 0xCE
2024-05-29 19:55:33 +03:00
/* [OK] don't forget "volatile" if you need to do any loops,
like in WaitForFifo and WaitTillIdle (had some trouble with that) */
2024-05-29 20:11:25 +03:00
#define mm_port(x) (((volatile unsigned long *)(af->IOMemMaps[0]))[x])
2024-05-29 19:55:33 +03:00
/* [OK] this routine is adapted from Allegro's ati.c: */
/* get_mach64_port:
* Calculates the port address for accessing a specific mach64 register.
*/
2024-05-29 20:11:25 +03:00
static int get_mach64_port(int io_sel, int mm_sel) {
if (mach64_floating) {
return (mm_sel << 2) + mach64_iobase;
} else {
return (io_sel << 10) + mach64_iobase;
}
2024-05-29 19:55:33 +03:00
}
/*
taken from mach64 example code, I don't understand what this macro
really does, but I interpret it in this way
#define GET24BPPROTATION(x) (unsigned long)(((x * 3) / 4) % 6)
r g b r g b r g b r g b r g b r g b r g b r g b
0 1 2 3 4 5 6 7
---0--- ---1--- ---2--- ---3--- ---4--- ---5---
though it doesn't seem very logical to have a "rotation" value do this
*/
2024-05-29 20:11:25 +03:00
#define m64_rot24(x) (DC_R_24 | ((((x) / 4) % 6) << 8))
2024-05-29 19:55:33 +03:00
/* note about VBE/AF multiple page modes: the API supports any number of
* video memory pages, one of which is "active" (being drawn onto), while
* the other is visible on your monitor. Allegro doesn't actually use
* this functionality, so you may safely leave it out and just reject
* any mode set requests with a numBuffers value greater than one, but
2024-05-29 20:11:25 +03:00
* that might upset other VBE/AF applications if they depend on this
2024-05-29 19:55:33 +03:00
* functionality. To support multiple pages, you must offset all the
* hardware drawing operations so their coordinate system is relative
* to the active page. You must also maintain an offset from the start
* of vram to the start of the active page in the OriginOffset of the
2024-05-29 20:11:25 +03:00
* driver structure, and adjust the OffscreenStartY and OffscreenEndY
* values so they will refer to the same offscreen memory region regardless
* of the current accelerator coordinate system. This is all handled by the
* SetActiveBuffer() function below, so in practice you can simply add
2024-05-29 19:55:33 +03:00
* af_active_page*af_height onto the input Y coordinate of any accelerator
* drawing funcs, and multiple page modes should work correctly.
*/
#ifndef REFUSE_VESA
/* mode_callback:
* Callback for the get_vesa_info() function to add a new resolution to
* the table of available modes.
*/
2024-05-29 20:11:25 +03:00
void mode_callback(int vesa_num, int linear, int w, int h, int bpp,
int bytes_per_scanline, int redsize, int redpos,
int greensize, int greenpos, int bluesize, int bluepos,
int rsvdsize, int rsvdpos) {
int chk;
if (num_modes >= MAX_MODES) return;
if ((bpp != 8) && (bpp != 15) && (bpp != 16) && (bpp != 24) && (bpp != 32))
return;
/* [OK] make sure mode is not already in list */
for (chk = 0; chk < num_modes; chk++)
if (mode_list[chk].vesa_num == vesa_num) return;
for (chk = 0; chk < num_modes; chk++)
if ((mode_list[chk].w == w) && (mode_list[chk].h == h) &&
(mode_list[chk].bpp == bpp)) {
mode_list[chk].vesa_num = vesa_num;
return;
}
mode_list[num_modes].vesa_num = vesa_num;
mode_list[num_modes].w = w;
mode_list[num_modes].h = h;
mode_list[num_modes].bpp = bpp;
mode_list[num_modes].redsize = redsize;
mode_list[num_modes].redpos = redpos;
mode_list[num_modes].greensize = greensize;
mode_list[num_modes].greenpos = greenpos;
mode_list[num_modes].bluesize = bluesize;
mode_list[num_modes].bluepos = bluepos;
mode_list[num_modes].rsvdsize = rsvdsize;
mode_list[num_modes].rsvdpos = rsvdpos;
available_modes[num_modes] = num_modes + 1;
available_modes[num_modes + 1] = -1;
num_modes++;
2024-05-29 19:55:33 +03:00
}
#endif
/* [OK] the mach64 BIOS reports these VRAM amounts */
2024-05-29 20:11:25 +03:00
int vram_sizes[] = { 512, 1024, 2048, 4096, 6144, 8192, 12288, 8192 };
2024-05-29 19:55:33 +03:00
/* SetupDriver:
* The first thing ever to be called after our code has been relocated.
* This is in charge of filling in the driver header with all the required
* information and function pointers. We do not yet have access to the
* video memory, so we can't talk directly to the card.
*/
2024-05-29 20:11:25 +03:00
int SetupDriver(AF_DRIVER *af) {
RM_REGS r;
int scratch_reg;
unsigned long old;
unsigned long aperture_addr, aperture_size;
int vram_size;
int i;
2024-05-29 19:55:33 +03:00
#ifndef REFUSE_VESA
2024-05-29 20:11:25 +03:00
/* find out what VESA has to say for itself */
if (get_vesa_info(&vram_size, NULL, mode_callback) != 0) return -1;
2024-05-29 19:55:33 +03:00
#endif
2024-05-29 20:11:25 +03:00
/* [OK] call upon the mach64 BIOS to get its I/O base address */
r.x.ax = 0xA012;
r.x.cx = 0;
rm_int(0x10, &r);
if (r.h.ah) return -1; /* [OK] there's no mach64 here */
mach64_floating = r.x.cx;
mach64_iobase = r.x.dx;
if (!mach64_iobase) mach64_iobase = 0x2EC;
/* [OK] this test is mostly copied from Allegro's ati.c: */
/* test scratch register to confirm we have a mach64 */
scratch_reg = get_mach64_port(ioSCRATCH1, mmSCRATCH1);
old = inportl(scratch_reg);
outportl(scratch_reg, 0x55555555);
if (inportl(scratch_reg) != 0x55555555) {
outportl(scratch_reg, old);
return -1;
}
outportl(scratch_reg, 0xAAAAAAAA);
if (inportl(scratch_reg) != 0xAAAAAAAA) {
outportl(scratch_reg, old);
return -1;
}
outportl(scratch_reg, old);
/* [OK] we have confirmed the presence of a mach64 */
mach64_wpsel = get_mach64_port(ioWPSEL, mmWPSEL);
mach64_rpsel = get_mach64_port(ioRPSEL, mmRPSEL);
mach64_offpitch = get_mach64_port(ioOFFPITCH, mmOFFPITCH);
mach64_dacrg = get_mach64_port(ioDACREGS, mmDACREGS);
mach64_intr = get_mach64_port(ioINTCNTL, mmINTCNTL);
/* [OK] now we need to query the BIOS about our VRAM configuration */
r.x.ax = 0xA006;
rm_int(0x10, &r);
vram_size = vram_sizes[r.h.cl];
aperture_addr = r.x.bx * 1024 * 1024;
if (r.h.al & 2)
aperture_size = 8 * 1024 * 1024;
else if (r.h.al & 1)
aperture_size = 4 * 1024 * 1024;
else {
aperture_size = 0;
aperture_addr = 0;
}
/* [OK] there we go */
/* pointer to a list of the available mode numbers, ended by -1.
* Our mode numbers just count up from 1, so the mode numbers can
* be used as indexes into the mode_list[] table (zero is an
* invalid mode number, so this indexing must be offset by 1).
*/
af->AvailableModes = available_modes;
/* amount of video memory in K */
af->TotalMemory = vram_size;
/* driver attributes (see definitions in vbeaf.h) */
af->Attributes = (afHaveMultiBuffer | afHaveVirtualScroll |
afHaveBankedBuffer | afHaveAccel2D | afHaveROP2);
if (aperture_addr) af->Attributes |= afHaveLinearBuffer;
/* banked memory size and location: zero if not supported */
af->BankSize = 32;
af->BankedBasePtr = 0xA0000;
/* linear framebuffer size and location: zero if not supported */
if (aperture_addr) {
af->LinearSize = MIN(vram_size, (int)aperture_size - 0x400);
af->LinearBasePtr = aperture_addr;
} else {
af->LinearSize = 0;
af->LinearBasePtr = 0;
}
/* list which ports we are going to access (only needed under Linux) */
af->IOPortsTable = ports_table;
/* list physical memory regions that we need to access (zero for none) */
for (i = 0; i < 4; i++) {
af->IOMemoryBase[i] = 0;
af->IOMemoryLen[i] = 0;
}
/* [OK] the mach64 MMIO registers are at end of aperture */
if (aperture_addr)
af->IOMemoryBase[0] = aperture_addr + aperture_size - 0x400;
else
af->IOMemoryBase[0] = 0xC0000 - 0x400;
af->IOMemoryLen[0] = 0x400;
/* driver state variables (initialised later during the mode set) */
af->BufferEndX = 0;
af->BufferEndY = 0;
af->OriginOffset = 0;
af->OffscreenOffset = 0;
af->OffscreenStartY = 0;
af->OffscreenEndY = 0;
/* relocatable bank switcher (not required by Allegro) */
af->SetBank32 = SetBank32;
af->SetBank32Len = (long)SetBank32End - (long)SetBank32;
/* extension functions */
af->SupplementalExt = ExtStub;
/* device driver functions */
af->GetVideoModeInfo = GetVideoModeInfo;
af->SetVideoMode = SetVideoMode;
af->RestoreTextMode = RestoreTextMode;
af->GetClosestPixelClock = GetClosestPixelClock;
af->SaveRestoreState = SaveRestoreState;
af->SetDisplayStart = SetDisplayStart;
af->SetActiveBuffer = SetActiveBuffer;
af->SetVisibleBuffer = SetVisibleBuffer;
af->GetDisplayStartStatus = GetDisplayStartStatus;
af->EnableStereoMode = NULL;
af->SetPaletteData = SetPaletteData;
af->SetGammaCorrectData = NULL;
af->SetBank = SetBank;
/* hardware cursor functions (not supported: not used by Allegro) */
af->SetCursor = NULL;
af->SetCursorPos = NULL;
af->SetCursorColor = NULL;
af->ShowCursor = NULL;
/* wait until the accelerator hardware has finished drawing */
af->WaitTillIdle = WaitTillIdle;
/* on some cards the CPU cannot access the framebuffer while it is in
* hardware drawing mode. If this is the case, you should fill in these
* functions with routines to switch in and out of the accelerator mode.
* The application will call EnableDirectAccess() whenever it is about
* to write to the framebuffer directly, and DisableDirectAccess()
* before it calls any hardware drawing routines. If this arbitration is
* not required, leave these routines as NULL.
*/
af->EnableDirectAccess = NULL;
af->DisableDirectAccess = NULL;
/* sets the hardware drawing mode (solid, XOR, etc). Required. */
af->SetMix = SetMix;
/* pattern download functions. May be NULL if patterns not supported */
af->Set8x8MonoPattern = Set8x8MonoPattern;
2024-05-29 19:55:33 +03:00
#if 0
af->Set8x8ColorPattern = Set8x8ColorPattern;
af->Use8x8ColorPattern = Use8x8ColorPattern;
#endif
2024-05-29 20:11:25 +03:00
/* not supported: not used by Allegro */
af->SetLineStipple = NULL;
af->SetLineStippleCount = NULL;
/* not supported. There really isn't much point in this function because
* a lot of hardware can't do clipping at all, and even when it can there
* are usually problems with very large or negative coordinates. This
* means that a software clip is still required, so you may as well
* ignore this routine.
*/
af->SetClipRect = NULL;
/* DrawScan() is required: patterned versions may be NULL */
af->DrawScan = DrawScan;
af->DrawPattScan = DrawPattScan;
2024-05-29 19:55:33 +03:00
#if 0
af->DrawColorPattScan = DrawColorPattScan;
#endif
2024-05-29 20:11:25 +03:00
/* not supported: not used by Allegro */
af->DrawScanList = NULL;
af->DrawPattScanList = NULL;
af->DrawColorPattScanList = NULL;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* rectangle filling: may be NULL */
af->DrawRect = DrawRect;
af->DrawPattRect = DrawPattRect;
2024-05-29 19:55:33 +03:00
#if 0
af->DrawColorPattRect = DrawColorPattRect;
#endif
2024-05-29 20:11:25 +03:00
/* not supported: not used by Allegro */
af->DrawLine = NULL;
af->DrawStippleLine = NULL;
af->DrawTrap = NULL;
af->DrawTri = NULL;
af->DrawQuad = NULL;
af->PutMonoImage = NULL;
af->PutMonoImageLin = NULL;
af->PutMonoImageBM = NULL;
/* blitting within the video memory (may be NULL) */
af->BitBlt = BitBlt;
/* not supported: not used by Allegro */
af->BitBltSys = NULL;
af->BitBltLin = NULL;
af->BitBltBM = NULL;
/* masked blitting within the video memory (may be NULL) */
af->SrcTransBlt = NULL;
/* not supported: not used by Allegro */
af->SrcTransBltSys = NULL;
af->SrcTransBltLin = NULL;
af->SrcTransBltBM = NULL;
af->DstTransBlt = NULL;
af->DstTransBltSys = NULL;
af->DstTransBltLin = NULL;
af->DstTransBltBM = NULL;
af->StretchBlt = NULL;
af->StretchBltSys = NULL;
af->StretchBltLin = NULL;
af->StretchBltBM = NULL;
af->SrcTransStretchBlt = NULL;
af->SrcTransStretchBltSys = NULL;
af->SrcTransStretchBltLin = NULL;
af->SrcTransStretchBltBM = NULL;
af->DstTransStretchBlt = NULL;
af->DstTransStretchBltSys = NULL;
af->DstTransStretchBltLin = NULL;
af->DstTransStretchBltBM = NULL;
af->SetVideoInput = NULL;
af->SetVideoOutput = NULL;
af->StartVideoFrame = NULL;
af->EndVideoFrame = NULL;
return 0;
2024-05-29 19:55:33 +03:00
}
/* InitDriver:
2024-05-29 20:11:25 +03:00
* The second thing to be called during the init process, after the
2024-05-29 19:55:33 +03:00
* application has mapped all the memory and I/O resources we need.
* This is in charge of finding the card, returning 0 on success or
* -1 to abort.
*/
2024-05-29 20:11:25 +03:00
int InitDriver(AF_DRIVER *af) {
return 0;
2024-05-29 19:55:33 +03:00
}
/* FreeBEX:
* Returns an interface structure for the requested FreeBE/AF extension.
*/
2024-05-29 20:11:25 +03:00
void *FreeBEX(AF_DRIVER *af, unsigned long id) {
switch (id) {
default: return NULL;
}
2024-05-29 19:55:33 +03:00
}
/* ExtStub:
* Vendor-specific extension hook: we don't provide any.
*/
2024-05-29 20:11:25 +03:00
int ExtStub( ) {
return 0;
2024-05-29 19:55:33 +03:00
}
/* GetVideoModeInfo:
* Retrieves information about this video mode, returning zero on success
* or -1 if the mode is invalid.
*/
2024-05-29 20:11:25 +03:00
long GetVideoModeInfo(AF_DRIVER *af, short mode, AF_MODE_INFO *modeInfo) {
VIDEO_MODE *info;
int i, bytes_per_scanline;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
if ((mode <= 0) || (mode > num_modes)) return -1;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
info = &mode_list[mode - 1];
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* clear the structure to zero */
for (i = 0; i < (int)sizeof(AF_MODE_INFO); i++) ((char *)modeInfo)[i] = 0;
2024-05-29 19:55:33 +03:00
#ifdef REFUSE_VESA
2024-05-29 20:11:25 +03:00
if (!info->bios_num) return -1;
2024-05-29 19:55:33 +03:00
#endif
2024-05-29 20:11:25 +03:00
/* copy data across from our stored list of mode attributes */
modeInfo->Attributes = af->Attributes;
modeInfo->XResolution = info->w;
modeInfo->YResolution = info->h;
modeInfo->BitsPerPixel = info->bpp;
bytes_per_scanline = info->w * BYTES_PER_PIXEL(info->bpp);
/* available pages of video memory */
modeInfo->MaxBuffers = (af->TotalMemory * 1024 - RESERVED_VRAM) /
(bytes_per_scanline * info->h);
/* maximum virtual scanline length in both bytes and pixels. How wide
* this can go will very much depend on the card: 1024 is pretty safe
* on anything, but you will want to allow larger limits if the card
* is capable of them.
*/
modeInfo->MaxBytesPerScanLine = 1024 * BYTES_PER_PIXEL(info->bpp);
modeInfo->MaxScanLineWidth = 1024;
/* for banked video modes, fill in these variables: */
modeInfo->BytesPerScanLine = bytes_per_scanline;
modeInfo->BnkMaxBuffers = modeInfo->MaxBuffers;
modeInfo->RedMaskSize = info->redsize;
modeInfo->RedFieldPosition = info->redpos;
modeInfo->GreenMaskSize = info->greensize;
modeInfo->GreenFieldPosition = info->greenpos;
modeInfo->BlueMaskSize = info->bluesize;
modeInfo->BlueFieldPosition = info->bluepos;
modeInfo->RsvdMaskSize = info->rsvdsize;
modeInfo->RsvdFieldPosition = info->rsvdpos;
/* for linear video modes, fill in these variables: */
modeInfo->LinBytesPerScanLine = bytes_per_scanline;
modeInfo->LinMaxBuffers = modeInfo->MaxBuffers;
modeInfo->LinRedMaskSize = info->redsize;
modeInfo->LinRedFieldPosition = info->redpos;
modeInfo->LinGreenMaskSize = info->greensize;
modeInfo->LinGreenFieldPosition = info->greenpos;
modeInfo->LinBlueMaskSize = info->bluesize;
modeInfo->LinBlueFieldPosition = info->bluepos;
modeInfo->LinRsvdMaskSize = info->rsvdsize;
modeInfo->LinRsvdFieldPosition = info->rsvdpos;
/* I'm not sure exactly what these should be: Allegro doesn't use them */
modeInfo->MaxPixelClock = 135000000;
modeInfo->VideoCapabilities = 0;
modeInfo->VideoMinXScale = 0;
modeInfo->VideoMinYScale = 0;
modeInfo->VideoMaxXScale = 0;
modeInfo->VideoMaxYScale = 0;
return 0;
2024-05-29 19:55:33 +03:00
}
/* SetVideoMode:
* Sets the specified video mode, returning zero on success.
*
* Possible flag bits that may be or'ed with the mode number:
*
* 0x8000 = don't clear video memory
* 0x4000 = enable linear framebuffer
* 0x2000 = enable multi buffering
* 0x1000 = enable virtual scrolling
* 0x0800 = use refresh rate control
* 0x0400 = use hardware stereo
*/
2024-05-29 20:11:25 +03:00
long SetVideoMode(AF_DRIVER *af, short mode, long virtualX, long virtualY,
long *bytesPerLine, int numBuffers, AF_CRTCInfo *crtc) {
int linear = ((mode & 0x4000) != 0);
int noclear = ((mode & 0x8000) != 0);
long available_vram;
long used_vram;
VIDEO_MODE *info;
RM_REGS r;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* reject anything with hardware stereo */
if (mode & 0x400) return -1;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* mask off the other flag bits */
mode &= 0x3FF;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
if ((mode <= 0) || (mode > num_modes)) return -1;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
info = &mode_list[mode - 1];
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* reject the linear flag if the mode doesn't support it */
if ((linear) && (!af->Attributes & afHaveLinearBuffer)) return -1;
2024-05-29 19:55:33 +03:00
#ifndef REFUSE_VESA
2024-05-29 20:11:25 +03:00
if (info->vesa_num) {
/* call VESA to set the mode */
r.x.ax = 0x4F02;
r.x.bx = info->vesa_num;
2024-05-29 19:55:33 +03:00
#if 0
/* [OK] the mach64 BIOS is VESA 1.2 which can't handle LFB itself */
if (linear)
r.x.bx |= 0x4000;
#endif
2024-05-29 20:11:25 +03:00
if (noclear) r.x.bx |= 0x8000;
rm_int(0x10, &r);
if (r.h.ah) return -1;
} else {
2024-05-29 19:55:33 +03:00
#endif
2024-05-29 20:11:25 +03:00
/* [OK] call mach64 BIOS to set the mode */
r.x.ax = 0xA002;
r.x.cx = info->bios_num;
rm_int(0x10, &r);
if (r.h.ah) return -1;
2024-05-29 19:55:33 +03:00
#ifndef REFUSE_VESA
2024-05-29 20:11:25 +03:00
}
2024-05-29 19:55:33 +03:00
#endif
2024-05-29 20:11:25 +03:00
/* adjust the virtual width for widescreen modes */
if (virtualX < info->w) virtualX = info->w;
/* [OK] enforce necessary alignment */
virtualX = virtualX & ~7;
outportl(mach64_offpitch,
(inportl(mach64_offpitch) & 0xFFFFF) | (virtualX << 19));
*bytesPerLine = virtualX * BYTES_PER_PIXEL(info->bpp);
/* [OK] enable the mach64 linear aperture */
if (linear) {
r.x.ax = 0xA005;
r.x.cx = 1;
rm_int(0x10, &r);
}
/* store info about the current mode */
af_bpp = info->bpp;
af_width = *bytesPerLine;
af_height = MAX(info->h, virtualY);
af_linear = linear;
af_visible_page = 0;
af_active_page = 0;
af_bank = -1;
af_fore_mix = AF_REPLACE_MIX;
af_back_mix = AF_FORE_MIX;
/* [OK] provide acceleration where possible */
switch (af_bpp) {
case 8:
case 15:
case 16:
case 32:
af->SrcTransBlt = SrcTransBlt;
af->DrawLine = DrawLine;
break;
default: /* 24bpp */
af->SrcTransBlt =
NULL; /* haven't figured how to do this in 24bpp */
af->DrawLine = NULL; /* would have to be done pixel-by-pixel */
break;
}
/* return framebuffer dimensions to the application */
af->BufferEndX = af_width / BYTES_PER_PIXEL(af_bpp) - 1;
af->BufferEndY = af_height - 1;
af->OriginOffset = 0;
used_vram = af_width * af_height * numBuffers;
available_vram = af->TotalMemory * 1024 - RESERVED_VRAM;
if (used_vram > available_vram) return -1;
if (available_vram - used_vram >= af_width) {
af->OffscreenOffset = used_vram;
af->OffscreenStartY = af_height * numBuffers;
af->OffscreenEndY = available_vram / af_width - 1;
} else {
af->OffscreenOffset = 0;
af->OffscreenStartY = 0;
af->OffscreenEndY = 0;
}
/* [OK] load mode parameters into the graphics coprocessor */
InitEngine(af, virtualX, (numBuffers < 2) ? af->OffscreenEndY : af_height,
info->bpp);
2024-05-29 19:55:33 +03:00
#ifndef REFUSE_VESA
2024-05-29 20:11:25 +03:00
if (!info->vesa_num)
2024-05-29 19:55:33 +03:00
#endif
2024-05-29 20:11:25 +03:00
if (!noclear) {
DrawRect(af, 0, 0, 0, info->w, af_height);
WaitTillIdle(af);
}
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
return 0;
2024-05-29 19:55:33 +03:00
}
/* RestoreTextMode:
* Returns to text mode, shutting down the accelerator hardware.
*/
2024-05-29 20:11:25 +03:00
void RestoreTextMode(AF_DRIVER *af) {
RM_REGS r;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* [OK] disable aperture, just in case */
r.x.ax = 0xA005;
r.x.cx = 0;
rm_int(0x10, &r);
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* [OK] back to where it all began */
r.x.ax = 3;
rm_int(0x10, &r);
2024-05-29 19:55:33 +03:00
}
/* GetClosestPixelClock:
* I don't have a clue what this should return: it is used for the
* refresh rate control.
*/
2024-05-29 20:11:25 +03:00
long GetClosestPixelClock(AF_DRIVER *af, short mode, unsigned long pixelClock) {
/* ??? */
return 135000000;
2024-05-29 19:55:33 +03:00
}
/* SaveRestoreState:
* Stores the current driver status: not presently implemented.
*/
2024-05-29 20:11:25 +03:00
void SaveRestoreState(AF_DRIVER *af, int subfunc, void *saveBuf) {
/* not implemented (not used by Allegro) */
2024-05-29 19:55:33 +03:00
}
/* SetDisplayStart:
* Hardware scrolling function.
*/
2024-05-29 20:11:25 +03:00
void SetDisplayStart(AF_DRIVER *af, long x, long y, long waitVRT) {
if (waitVRT != -1) {
long a = x + ((y + af_visible_page * af_height) * af_width);
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
a *= BYTES_PER_PIXEL(af_bpp);
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
if (waitVRT) {
do {
} while (inportb(mach64_intr) & 1);
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
do { } while (!(inportb(mach64_intr) & 1)); }
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
outportl(mach64_offpitch,
(inportl(mach64_offpitch) & 0xFFF00000) | (a >> 3));
}
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
af_scroll_x = x;
af_scroll_y = y;
2024-05-29 19:55:33 +03:00
}
/* SetActiveBuffer:
* Sets which buffer is being drawn onto, for use in multi buffering
* systems (not used by Allegro).
*/
2024-05-29 20:11:25 +03:00
void SetActiveBuffer(AF_DRIVER *af, long index) {
if (af->OffscreenOffset) {
af->OffscreenStartY += af_active_page * af_height;
af->OffscreenEndY += af_active_page * af_height;
}
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
af_active_page = index;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
af->OriginOffset = af_width * af_height * index;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
if (af->OffscreenOffset) {
af->OffscreenStartY -= af_active_page * af_height;
af->OffscreenEndY -= af_active_page * af_height;
}
2024-05-29 19:55:33 +03:00
}
/* SetVisibleBuffer:
* Sets which buffer is displayed on the screen, for use in multi buffering
* systems (not used by Allegro).
*/
2024-05-29 20:11:25 +03:00
void SetVisibleBuffer(AF_DRIVER *af, long index, long waitVRT) {
af_visible_page = index;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
SetDisplayStart(af, af_scroll_x, af_scroll_y, waitVRT);
2024-05-29 19:55:33 +03:00
}
/* GetDisplayStartStatus:
* Status poll for triple buffering. Not possible on the majority of
* present cards: this function is just a placeholder.
*/
2024-05-29 20:11:25 +03:00
int GetDisplayStartStatus(AF_DRIVER *af) {
return 1;
2024-05-29 19:55:33 +03:00
}
/* SetPaletteData:
* Palette setting routine.
*/
2024-05-29 20:11:25 +03:00
void SetPaletteData(AF_DRIVER *af, AF_PALETTE *pal, long num, long index,
long waitVRT) {
int i;
if (waitVRT) {
do {
} while (inportb(mach64_intr) & 1);
do { } while (!(inportb(mach64_intr) & 1)); }
for (i = 0; i < num; i++) {
outportb(mach64_dacrg, index + i);
outportb(mach64_dacrg + 1, pal[i].red / 4);
outportb(mach64_dacrg + 1, pal[i].green / 4);
outportb(mach64_dacrg + 1, pal[i].blue / 4);
}
2024-05-29 19:55:33 +03:00
}
/* SetBank32:
* Relocatable bank switch function. This is called with a bank number in
2024-05-29 20:11:25 +03:00
* %edx. I'm not sure what registers it is allowed to clobber, so it is
2024-05-29 19:55:33 +03:00
* probably a good idea to save them all.
*
* This function may be copied anywhere within the address space of the
2024-05-29 20:11:25 +03:00
* calling program, so it must be 100% relocatable. That means that you
* must not refer to any internal variables of the /AF driver, because
2024-05-29 19:55:33 +03:00
* you know nothing about where in memory it will be located. Your only
* input is the bank number in %edx, and your only output should be
* changing the relevant hardware registers.
*
* If you are unable to provide a relocatable bank switcher of this type,
2024-05-29 20:11:25 +03:00
* remove this function (clear the SetBank32 pointer to NULL during the
* header init), and fill in the SetBank() routine below instead. Allegro
* only ever uses SetBank(), so there will be no problem with leaving this
* function out, but you may have problems running other VBE/AF
2024-05-29 19:55:33 +03:00
* applications if you don't provide it.
*/
asm ("
.globl _SetBank32, _SetBank32End
.align 4
_SetBank32:
pushal
2024-05-29 20:11:25 +03:00
#[OK] mostly copied from Allegro's bank.s
2024-05-29 19:55:33 +03:00
movl %edx,%eax
2024-05-29 20:11:25 +03:00
#two 32K apertures, bank and bank + 1
2024-05-29 19:55:33 +03:00
movb %al,%ah
incb %ah
shll $8,%eax
shrw $8,%ax
2024-05-29 20:11:25 +03:00
#[OK] hey, I 'm violating Shawn' s rules for SetBank32...
#[OK] oh well, I might make it 100 % relocatable later
#[OK](how ? I can think of at least 3 ways, so don't worry)
#[OK] or just use LFB meanwhile(or preferably always)
2024-05-29 19:55:33 +03:00
movl _mach64_wpsel,%edx
outl %eax,%dx
movl _mach64_rpsel,%edx
outl %eax,%dx
popal
ret
_SetBank32End:
");
/* SetBank:
* C-callable bank switch function. This version simply chains to the
* relocatable SetBank32() above. If you can't provide a relocatable
* function (because you need access to global variables or some part
* of the /AF driver structure), you should put the bank switch code
* here instead.
*/
void SetBank(AF_DRIVER *af, long bank)
{
2024-05-29 20:11:25 +03:00
asm(" call _SetBank32 " : : "d"(bank));
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
af_bank = bank;
2024-05-29 19:55:33 +03:00
}
/* WaitForFifo:
* [OK] Delay until there's room for more data.
*/
void WaitForFifo(AF_DRIVER *af, int entries)
{
2024-05-29 20:11:25 +03:00
unsigned ent = 0x8000 >> entries;
2024-05-29 19:55:33 +03:00
#ifdef TEST
2024-05-29 20:11:25 +03:00
alarm(5);
2024-05-29 19:55:33 +03:00
#endif
2024-05-29 20:11:25 +03:00
while ((mm_port(mmFIFOSTAT) & 0xFFFF) > ent)
;
2024-05-29 19:55:33 +03:00
#ifdef TEST
2024-05-29 20:11:25 +03:00
alarm(0);
2024-05-29 19:55:33 +03:00
#endif
}
/* WaitTillIdle:
* Delay until the hardware controller has finished drawing.
*/
void WaitTillIdle(AF_DRIVER *af)
{
2024-05-29 20:11:25 +03:00
WaitForFifo(af, 16);
2024-05-29 19:55:33 +03:00
#ifdef TEST
2024-05-29 20:11:25 +03:00
alarm(5);
2024-05-29 19:55:33 +03:00
#endif
2024-05-29 20:11:25 +03:00
while (mm_port(mmGUISTAT) & ENGINE_BUSY)
;
2024-05-29 19:55:33 +03:00
#ifdef TEST
2024-05-29 20:11:25 +03:00
alarm(0);
2024-05-29 19:55:33 +03:00
#endif
}
/* ResetEngine:
* [OK] Reset the GUI engine and clear any errors.
*/
void ResetEngine(AF_DRIVER *af)
{
2024-05-29 20:11:25 +03:00
int cntl_reg;
/* reset engine */
cntl_reg = get_mach64_port(ioGTCNTL, mmGTCNTL);
outportl(cntl_reg, inportl(cntl_reg) & 0xFFFFFEFF);
/* enable engine */
outportl(cntl_reg, inportl(cntl_reg) | ENGINE_ENABLE);
/* clear any errors */
cntl_reg = get_mach64_port(ioBUSCNTL, mmBUSCNTL);
outportl(cntl_reg, (inportl(cntl_reg) & 0xFF00FFFF) | 0x00AE0000);
2024-05-29 19:55:33 +03:00
}
unsigned mach64_mix[]={
7, /* REPLACE_MIX */
12, /* AND_MIX */
11, /* OR_MIX */
5, /* XOR_MIX */
3, /* NOP_MIX */
7,7,7,7,7,7,7,7,7,7,7, /* padding */
/* ROP2 conversion */
/* S:1100 */
/* D:1010 */
/* ---- */
1, /* 0000 BLACK aka ZERO */
15, /* 0001 NOTMERGESRC aka NOT_D_AND_NOT_S */
14, /* 0010 MASKNOTSRC aka D_AND_NOT_S */
4, /* 0011 NOTCOPYSRC aka NOT_S */
13, /* 0100 MASKSRCNOT aka NOT_D_AND_S */
0, /* 0101 NOT aka NOT_D */
5, /* 0110 XORSRC aka D_XOR_S */
8, /* 0111 NOTMASKSRC aka NOT_D_OR_NOT_S */
12, /* 1000 MASKSRC aka D_AND_S */
6, /* 1001 NOTXORSRC aka NOT_D_XOR_S */
3, /* 1010 NOP aka D */
9, /* 1011 MERGENOTSRC aka D_OR_NOT_S */
7, /* 1100 COPYSRC aka S */
10, /* 1101 MERGESRCNOT aka NOT_D_OR_S */
11, /* 1110 MERGESRC aka D_OR_S */
2 /* 1111 WHITE aka ONE */
/* the mach64 also supports a (S+D)/2 mix mode (hardware-accelerated
blending, cool) 0x17, but I don't think VBE/AF supports that... */
};
/* SetMix:
* Specifies the pixel mix mode to be used for hardware drawing functions
* (not the blit routines: they take an explicit mix parameter). Both
* parameters should both be one of the AF_mixModes enum defined in vbeaf.h.
*
* VBE/AF requires all drivers to support the REPLACE, AND, OR, XOR,
* and NOP mix types. This file implements all the required types, but
* Allegro only actually uses the REPLACE and XOR modes for scanline and
* rectangle fills, REPLACE mode for blitting and color pattern drawing,
* and either REPLACE or foreground REPLACE and background NOP for mono
* pattern drawing.
*/
void SetMix(AF_DRIVER *af, long foreMix, long backMix)
{
2024-05-29 20:11:25 +03:00
af_fore_mix = foreMix;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
if (backMix == AF_FORE_MIX)
af_back_mix = foreMix;
else
af_back_mix = backMix;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
mach64_dpmix = (mach64_mix[af_fore_mix] << 16) | mach64_mix[af_back_mix];
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
WaitForFifo(af, 1);
mm_port(mmDPMIX) = mach64_dpmix;
2024-05-29 19:55:33 +03:00
}
/* InitEngine:
* [OK] Loads the current mode parameters into the graphics coprocessor.
*/
void InitEngine(AF_DRIVER *af, int width, int height, int bpp)
{
2024-05-29 20:11:25 +03:00
/* reset engine */
ResetEngine(af);
if (bpp == 24) {
/* [OK] the mach64 graphics coprocessor doesn't support 24bpp,
so it must be set to 8bpp, which means the width must be
adjusted accordingly */
width *= 3;
}
/* make sure the FIFO has room for our next 14 register loads */
WaitForFifo(af, 14);
/* full context mask */
mm_port(mmCNTXMASK) = 0xFFFFFFFF;
/* destination parameters */
mm_port(mmDOFFPTCH) = width << 19;
mm_port(mmDYX) = 0;
mm_port(mmDHT) = 0;
mm_port(mmDBRSERR) = 0;
mm_port(mmDBRSINC) = 0;
mm_port(mmDBRSDEC) = 0;
mach64_dcntl = DC_LAST_P | DC_Y_T2B | DC_X_L2R;
mm_port(mmDCNTL) = mach64_dcntl;
/* source parameters */
mm_port(mmSOFFPTCH) = width << 19;
mm_port(mmSYX) = 0;
mm_port(mmSHTWDTH1) = 0;
mm_port(mmSYXSTRT) = 0;
mm_port(mmSHTWDTH2) = 0;
mach64_scntl = SC_LX_L2R;
mm_port(mmSCNTL) = mach64_scntl;
/* 13 more register loads on their way */
WaitForFifo(af, 13);
/* host control */
mm_port(mmHOSTCNTL) = 0;
/* pattern */
mm_port(mmPATREG0) = 0;
mm_port(mmPATREG1) = 0;
mm_port(mmPATCNTL) = 0;
/* clipping */
mm_port(mmSCLEFT) = 0;
mm_port(mmSCTOP) = 0;
mm_port(mmSCBOTTOM) = height - 1;
mm_port(mmSCRIGHT) = width - 1;
/* colors */
mm_port(mmDPBGCOL) = 0;
mm_port(mmDPFGCOL) = 0xFFFFFFFF;
/* write mask */
mm_port(mmDPWRMSK) = 0xFFFFFFFF;
/* mix mode = replace */
mach64_dpmix = 7 << 16 | 7;
mm_port(mmDPMIX) = mach64_dpmix;
/* foreground source */
mach64_dpsrc = DS_FGCOL << 8 | DS_BGCOL;
mm_port(mmDPSRC) = mach64_dpsrc;
/* 5 register loads left */
WaitForFifo(af, 5);
/* pixel masking */
mm_port(mmCCMPCLR) = 0;
mm_port(mmCCMPMSK) = 0xFFFFFFFF;
mm_port(mmCCMPCNTL) = 0;
/* bpp mode */
switch (bpp) {
case 4: /* just for completeness */
mm_port(mmPIXWDTH) = PW_4 << 16 | PW_4 << 8 | PW_4 | PW_LSB;
mm_port(mmDPCHNMSK) = 0x8888;
break;
case 15:
mm_port(mmPIXWDTH) = PW_15 << 16 | PW_15 << 8 | PW_15 | PW_LSB;
mm_port(mmDPCHNMSK) = 0x4210;
break;
case 16:
mm_port(mmPIXWDTH) = PW_16 << 16 | PW_16 << 8 | PW_16 | PW_LSB;
mm_port(mmDPCHNMSK) = 0x8410;
break;
case 32:
mm_port(mmPIXWDTH) = PW_32 << 16 | PW_32 << 8 | PW_32 | PW_LSB;
mm_port(mmDPCHNMSK) = 0x8080;
break;
default: /* 8bpp and 24bpp */
mm_port(mmPIXWDTH) = PW_8 << 16 | PW_8 << 8 | PW_8 | PW_LSB;
mm_port(mmDPCHNMSK) = 0x8080;
break;
}
/* wait for graphics coprocessor to swallow it all */
WaitTillIdle(af);
2024-05-29 19:55:33 +03:00
}
/* af_putpixel:
* Writes a pixel to the screen with the specified mix mode. This function
* is _not_ intended to be useful: it is grossly inefficient! It is just
* a helper for the example drawing routines below: these should of course
* be replaced by hardware specific code.
*/
void af_putpixel(AF_DRIVER *af, int x, int y, int c, int mix)
{
2024-05-29 20:11:25 +03:00
long offset;
int bank;
int c2 = 0;
void *p;
y += af_active_page * af_height;
offset = y * af_width + x * BYTES_PER_PIXEL(af_bpp);
/* quit if this is a noop */
if (mix == AF_NOP_MIX) return;
/* get pointer to vram */
if (af_linear) {
p = af->LinearMem + offset;
} else {
p = af->BankedMem + (offset & 0x7FFF);
bank = offset >> 15; /* 32K granularity */
if (bank != af_bank) {
af->SetBank(af, bank);
af_bank = bank;
}
}
if (mix != AF_REPLACE_MIX) {
/* read destination pixel for mixing */
switch (af_bpp) {
case 8: c2 = *((unsigned char *)p); break;
case 15:
case 16: c2 = *((unsigned short *)p); break;
case 24: c2 = *((unsigned long *)p) & 0xFFFFFF; break;
case 32: c2 = *((unsigned long *)p); break;
}
/* apply logical mix modes */
switch (mix) {
case AF_AND_MIX: c &= c2; break;
case AF_OR_MIX: c |= c2; break;
case AF_XOR_MIX: c ^= c2; break;
}
}
/* write the pixel */
switch (af_bpp) {
case 8: *((unsigned char *)p) = c; break;
case 15:
case 16: *((unsigned short *)p) = c; break;
case 24:
*((unsigned short *)p) = c & 0xFFFF;
*((unsigned char *)(p + 2)) = c >> 16;
break;
case 32: *((unsigned long *)p) = c; break;
}
2024-05-29 19:55:33 +03:00
}
/* af_getpixel:
* Reads a pixel from the screen. This function is _not_ intended to
* be useful: it is grossly inefficient! It is just a helper for the
* example drawing routines below: these should of course be replaced
* by hardware specific code.
*/
int af_getpixel(AF_DRIVER *af, int x, int y)
{
2024-05-29 20:11:25 +03:00
long offset;
int bank;
void *p;
y += af_active_page * af_height;
offset = y * af_width + x * BYTES_PER_PIXEL(af_bpp);
/* get pointer to vram */
if (af_linear) {
p = af->LinearMem + offset;
} else {
p = af->BankedMem + (offset & 0x7FFF);
bank = offset >> 15; /* 32K granularity */
if (bank != af_bank) {
af->SetBank(af, bank);
af_bank = bank;
}
}
/* read the pixel */
switch (af_bpp) {
case 8: return *((unsigned char *)p);
case 15:
case 16: return *((unsigned short *)p);
case 24: return *((unsigned long *)p) & 0xFFFFFF;
case 32: return *((unsigned long *)p);
}
return 0;
2024-05-29 19:55:33 +03:00
}
/* stored mono pattern data */
unsigned char mono_pattern[8];
/* Set8x8MonoPattern:
* Downloads a monochrome (packed bit) pattern, for use by the
* DrawPattScan() and DrawPattRect() functions. This is always sized
* 8x8, and aligned with the top left corner of video memory: if other
* alignments are desired, the pattern will be prerotated before it
* is passed to this routine.
*/
void Set8x8MonoPattern(AF_DRIVER *af, unsigned char *pattern)
{
2024-05-29 20:11:25 +03:00
int i;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
for (i = 0; i < 8; i++) mono_pattern[i] = pattern[i];
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
WaitForFifo(af, 3);
mm_port(mmPATREG0) = ((unsigned long *)(&mono_pattern))[0];
mm_port(mmPATREG1) = ((unsigned long *)(&mono_pattern))[1];
mm_port(mmPATCNTL) = PC_MONO;
2024-05-29 19:55:33 +03:00
}
/* stored color pattern data */
unsigned long color_pattern[8][64];
unsigned long *current_color_pattern = color_pattern[0];
/* [OK] I've disabled color patterns for now */
/* Set8x8ColorPattern:
* Downloads a color pattern, for use by the DrawColorPattScan() and
* DrawColorPattRect() functions. This is always sized 8x8, and aligned
* with the top left corner of video memory: if other alignments are
* desired, the pattern will be prerotated before it is passed to this
* routine. The color values are presented in the native format for
* the current video mode, but padded to 32 bits (so the pattern is
* always an 8x8 array of longs).
*
* VBE/AF supports 8 different color patterns, which may be downloaded
* individually and then selected for use with the Use8x8ColorPattern()
* function. If the card is not able to cache these patterns in hardware,
* the driver is responsible for storing them internally and downloading
* the appropriate data when Use8x8ColorPattern() is called.
*
* Allegro only actually ever uses the first of these patterns, so
* you only need to bother about this if you want your code to work
* with other programs as well.
*/
void Set8x8ColorPattern(AF_DRIVER *af, int index, unsigned long *pattern)
{
2024-05-29 20:11:25 +03:00
int i;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
for (i = 0; i < 64; i++) color_pattern[index][i] = pattern[i];
2024-05-29 19:55:33 +03:00
}
/* Use8x8ColorPattern:
* Selects one of the patterns previously downloaded by Set8x8ColorPattern().
*/
void Use8x8ColorPattern(AF_DRIVER *af, int index)
{
2024-05-29 20:11:25 +03:00
current_color_pattern = color_pattern[index];
2024-05-29 19:55:33 +03:00
}
/* DrawScan:
* Fills a scanline in the current foreground mix mode. Draws up to but
* not including the second x coordinate. If the second coord is less
* than the first, they are swapped. If they are equal, nothing is drawn.
*/
void DrawScan(AF_DRIVER *af, long color, long y, long x1, long x2)
{
2024-05-29 20:11:25 +03:00
if (x2 < x1)
DrawRect(af, color, x2, y, x1 - x2, 1);
else
DrawRect(af, color, x1, y, x2 - x1, 1);
2024-05-29 19:55:33 +03:00
}
/* DrawPattScan:
* Fills a scanline using the current mono pattern. Set pattern bits are
* drawn using the specified foreground color and the foreground mix
* mode, and clear bits use the background color and background mix mode.
*/
void DrawPattScan(AF_DRIVER *af, long foreColor, long backColor, long y, long x1, long x2)
{
2024-05-29 20:11:25 +03:00
if (x2 < x1)
DrawPattRect(af, foreColor, backColor, x2, y, x1 - x2, 1);
else
DrawPattRect(af, foreColor, backColor, x1, y, x2 - x1, 1);
2024-05-29 19:55:33 +03:00
}
/* DrawColorPattScan:
* Fills a scanline using the current color pattern and mix mode.
*/
void DrawColorPattScan(AF_DRIVER *af, long y, long x1, long x2)
{
2024-05-29 20:11:25 +03:00
int orig_bank = af_bank;
int patx, paty;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
if (x2 < x1) {
int tmp = x1;
x1 = x2;
x2 = tmp;
}
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
while (x1 < x2) {
patx = x1 & 7;
paty = y & 7;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
af_putpixel(af, x1, y, current_color_pattern[paty * 8 + patx],
af_fore_mix);
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
x1++;
}
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
if (af_bank != orig_bank) af->SetBank(af, orig_bank);
2024-05-29 19:55:33 +03:00
}
/* DrawRect:
* Fills a rectangle in the current foreground mix mode.
*/
void DrawRect(AF_DRIVER *af, unsigned long color, long left, long top, long width, long height)
{
2024-05-29 20:11:25 +03:00
if (af_bpp == 24) {
left *= 3;
width *= 3;
WaitForFifo(af, 1);
mm_port(mmDCNTL) = mach64_dcntl | m64_rot24(left);
}
WaitForFifo(af, 5);
/* [OK] set color */
mm_port(mmDPFGCOL) = color;
/* [OK] draw rectangle */
mm_port(mmDX) = left;
mm_port(mmDY) = top + af_active_page * af_height;
mm_port(mmDHT) = height;
mm_port(mmDWDTH) = width;
if (af_bpp == 24) {
WaitForFifo(af, 1);
mm_port(mmDCNTL) = mach64_dcntl;
}
2024-05-29 19:55:33 +03:00
}
/* DrawPattRect:
* Fills a rectangle using the current mono pattern. Set pattern bits are
* drawn using the specified foreground color and the foreground mix
* mode, and clear bits use the background color and background mix mode.
*/
void DrawPattRect(AF_DRIVER *af, unsigned long foreColor, unsigned long backColor, long left, long top, long width, long height)
{
2024-05-29 20:11:25 +03:00
WaitForFifo(af, 2);
/* [OK] set background color and source */
mm_port(mmDPBGCOL) = backColor;
/* [OK] I'm not quite sure about this...
I don't think it works, but I don't have a test program
anyone to test it for me? */
mm_port(mmDPSRC) = DSM_PATT | (DS_FGCOL << 8) | DS_BGCOL;
DrawRect(af, foreColor, left, top, width, height);
/* [OK] restore defaults */
WaitForFifo(af, 1);
mm_port(mmDPSRC) = mach64_dpsrc;
2024-05-29 19:55:33 +03:00
}
/* DrawColorPattRect:
* Fills a rectangle using the current color pattern and mix mode.
*/
void DrawColorPattRect(AF_DRIVER *af, long left, long top, long width, long height)
{
2024-05-29 20:11:25 +03:00
int orig_bank = af_bank;
int patx, paty;
int x, y;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
patx = (left + x) & 7;
paty = (top + y) & 7;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
af_putpixel(af, left + x, top + y,
current_color_pattern[paty * 8 + patx], af_fore_mix);
}
}
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
if (af_bank != orig_bank) af->SetBank(af, orig_bank);
2024-05-29 19:55:33 +03:00
}
/* BitBlt:
* Blits from one part of video memory to another, using the specified
* mix operation. This must correctly handle the case where the two
* regions overlap.
*/
void BitBlt(AF_DRIVER *af, long left, long top, long width, long height, long dstLeft, long dstTop, long op)
{
2024-05-29 20:11:25 +03:00
if (af_bpp == 24) {
left *= 3;
width *= 3;
dstLeft *= 3;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
WaitForFifo(af, 1);
mm_port(mmDCNTL) = mach64_dcntl | m64_rot24(dstLeft);
}
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
WaitForFifo(af, 2);
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* [OK] set blt source and mix mode */
mm_port(mmDPSRC) = (DS_BLIT << 8) | DS_BGCOL;
mm_port(mmDPMIX) = (mach64_mix[op] << 16) | mach64_mix[op];
2024-05-29 19:55:33 +03:00
#if 0
if ((left+width > dstLeft) && (top+height > dstTop) &&
(dstLeft+width > left) && (dstTop+height > top) &&
((dstTop > top) || ((dstTop == top) && (dstLeft > left)))) {
#else
2024-05-29 20:11:25 +03:00
/* [OK] looks like the mach64 only needs to reverse the Y direction */
if ((top + height > dstTop) && (dstTop > top)) {
2024-05-29 19:55:33 +03:00
#endif
2024-05-29 20:11:25 +03:00
/* have to do the copy backward */
WaitForFifo(af, 9);
mm_port(mmDCNTL) = (mach64_dcntl & ~DC_Y_T2B) | DC_Y_B2T;
/* [OK] do the blit */
mm_port(mmSX) = left;
mm_port(mmSY) = top + height + af_active_page * af_height;
mm_port(mmSHT1) = height;
mm_port(mmSWDTH1) = width;
mm_port(mmDX) = dstLeft;
mm_port(mmDY) = dstTop + height + af_active_page * af_height;
mm_port(mmDHT) = height;
mm_port(mmDWDTH) = width;
/* [OK] restore defaults */
WaitForFifo(af, 1);
mm_port(mmDCNTL) = mach64_dcntl;
2024-05-29 19:55:33 +03:00
} else {
2024-05-29 20:11:25 +03:00
WaitForFifo(af, 8);
/* [OK] do the blit */
mm_port(mmSX) = left;
mm_port(mmSY) = top + af_active_page * af_height;
mm_port(mmSHT1) = height;
mm_port(mmSWDTH1) = width;
mm_port(mmDX) = dstLeft;
mm_port(mmDY) = dstTop + af_active_page * af_height;
mm_port(mmDHT) = height;
mm_port(mmDWDTH) = width;
if (af_bpp == 24) {
WaitForFifo(af, 1);
mm_port(mmDCNTL) = mach64_dcntl;
}
2024-05-29 19:55:33 +03:00
}
/* [OK] restore defaults */
WaitForFifo(af, 2);
mm_port(mmDPMIX) = mach64_dpmix;
mm_port(mmDPSRC) = mach64_dpsrc;
}
/* SrcTransBlt:
* Blits from one part of video memory to another, using the specified
* mix operation and skipping any source pixels which match the specified
* transparent color. Results are undefined if the two regions overlap.
*/
2024-05-29 20:11:25 +03:00
void SrcTransBlt(AF_DRIVER *af, long left, long top, long width, long height,
long dstLeft, long dstTop, long op,
unsigned long transparent) {
/* [OK] note: this routine doesn't work in 24bpp for some reason,
so it is disabled at mode set */
2024-05-29 19:55:33 +03:00
#if 0
if (af_bpp == 24) {
left *= 3;
width *= 3;
dstLeft *= 3;
WaitForFifo(af, 1);
mm_port(mmDCNTL) = mach64_dcntl | m64_rot24(dstLeft);
}
#endif
2024-05-29 20:11:25 +03:00
WaitForFifo(af, 4);
/* [OK] set transparency color */
mm_port(mmCCMPCLR) = transparent;
mm_port(mmCCMPCNTL) = CC_EQUAL | CC_SRC;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* [OK] set blt source and mix mode */
mm_port(mmDPSRC) = (DS_BLIT << 8) | DS_BLIT;
mm_port(mmDPMIX) = (mach64_mix[op] << 16) | mach64_mix[op];
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
WaitForFifo(af, 8);
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* [OK] do the blit */
mm_port(mmSX) = left;
mm_port(mmSY) = top + af_active_page * af_height;
mm_port(mmSHT1) = height;
mm_port(mmSWDTH1) = width;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
mm_port(mmDX) = dstLeft;
mm_port(mmDY) = dstTop + af_active_page * af_height;
mm_port(mmDHT) = height;
mm_port(mmDWDTH) = width;
2024-05-29 19:55:33 +03:00
2024-05-29 20:11:25 +03:00
/* [OK] restore defaults */
WaitForFifo(af, 3);
mm_port(mmDPMIX) = mach64_dpmix;
mm_port(mmDPSRC) = mach64_dpsrc;
mm_port(mmCCMPCNTL) = 0;
2024-05-29 19:55:33 +03:00
#if 0
if (af_bpp == 24) {
WaitForFifo(af, 1);
mm_port(mmDCNTL) = mach64_dcntl;
}
#endif
}
2024-05-29 20:11:25 +03:00
void DrawLine(AF_DRIVER *af, unsigned long color, fixed x1, fixed y1, fixed x2,
fixed y2) {
/* [OK] note: this routine won't work in 24bpp, so it is disabled at mode
* set */
int dx, dy, dir = 0, v1, v2, err, inc, dec;
/* [OK] I'm not quite sure yet how to do fractional coordinates */
/* [OK] just round them to integers for now */
x1 = (x1 + 0x8000) >> 16;
y1 = (y1 + 0x8000) >> 16;
x2 = (x2 + 0x8000) >> 16;
y2 = (y2 + 0x8000) >> 16;
/* [OK] calculate Bresenham parameters */
if (x1 < x2) {
dx = x2 - x1;
dir |= 1;
} else
dx = x1 - x2;
if (y1 < y2) {
dy = y2 - y1;
dir |= 2;
} else
dy = y1 - y2;
if (dx < dy) {
v1 = dx;
v2 = dy;
dir |= 4;
} else {
v1 = dy;
v2 = dx;
}
inc = 2 * v1;
err = inc - v2;
/* [OK] I don't know what this 0x3FFFF is for, it was just part
of the mach64 source code I had access to... */
dec = 0x3FFFF - 2 * (v2 - v1);
/* [OK] do the line */
WaitForFifo(af, 8);
mm_port(mmDPFGCOL) = color;
mm_port(mmDX) = x1;
mm_port(mmDY) = y1 + af_active_page * af_height;
mm_port(mmDCNTL) = (mach64_dcntl & (DC_LAST_P | DC_POLY)) | dir;
mm_port(mmDBRSERR) = err;
mm_port(mmDBRSINC) = inc;
mm_port(mmDBRSDEC) = dec;
mm_port(mmDBRSLEN) = (v2 + 1);
WaitForFifo(af, 1);
mm_port(mmDCNTL) = mach64_dcntl;
2024-05-29 19:55:33 +03:00
}