class @vmsvga_info
{
    U16 io_base;
    U32* fifo;
    U16 width;
    U16 height;
    U16 bpp;
    U64 fb;
    U32 capabilities;
};

#define VMWGFX_FIFO_STATIC_SIZE (1024 * 1024)

#define VMSVGA_MAGIC 0x900000
#define VMSVGA_ID_2 (VMSVGA_MAGIC << 8 | 2)
#define VMSVGA_MOUSE_ID 1

#define VMSVGA_CAP_GMR 0x00100000

#define VMSVGA_CMD_INVALID_CMD 0
#define VMSVGA_CMD_UPDATE 1
#define VMSVGA_CMD_RECT_COPY 3
#define VMSVGA_CMD_DEFINE_CURSOR 19
#define VMSVGA_CMD_DEFINE_ALPHA_CURSOR 22
#define VMSVGA_CMD_UPDATE_VERBOSE 25
#define VMSVGA_CMD_FRONT_ROP_FILL 29
#define VMSVGA_CMD_FENCE 30
#define VMSVGA_CMD_ESCAPE 33
#define VMSVGA_CMD_DEFINE_SCREEN 34
#define VMSVGA_CMD_DESTROY_SCREEN 35
#define VMSVGA_CMD_DEFINE_GMRFB 36
#define VMSVGA_CMD_BLIT_GMRFB_TO_SCREEN 37
#define VMSVGA_CMD_BLIT_SCREEN_TO_GMRFB 38
#define VMSVGA_CMD_ANNOTATION_FILL 39
#define VMSVGA_CMD_ANNOTATION_COPY 40
#define VMSVGA_CMD_DEFINE_GMR2 41
#define VMSVGA_CMD_REMAP_GMR2 42

#define VMSVGA_REG_ID 0
#define VMSVGA_REG_ENABLE 1
#define VMSVGA_REG_WIDTH 2
#define VMSVGA_REG_HEIGHT 3
#define VMSVGA_REG_MAX_WIDTH 4
#define VMSVGA_REG_MAX_HEIGHT 5
#define VMSVGA_REG_DEPTH 6
#define VMSVGA_REG_BITS_PER_PIXEL 7 /* Current bpp in the guest */
#define VMSVGA_REG_PSEUDOCOLOR 8
#define VMSVGA_REG_RED_MASK 9
#define VMSVGA_REG_GREEN_MASK 10
#define VMSVGA_REG_BLUE_MASK 11
#define VMSVGA_REG_BYTES_PER_LINE 12
#define VMSVGA_REG_FB_START 13 /* (Deprecated) */
#define VMSVGA_REG_FB_OFFSET 14
#define VMSVGA_REG_VRAM_SIZE 15
#define VMSVGA_REG_FB_SIZE 16

/* ID 0 implementation only had the above registers  then the palette */
#define VMSVGA_REG_CAPABILITIES 17
#define VMSVGA_REG_MEM_START 18 /* (Deprecated) */
#define VMSVGA_REG_MEM_SIZE 19
#define VMSVGA_REG_CONFIG_DONE 20         /* Set when memory area configured */
#define VMSVGA_REG_SYNC 21                /* See "FIFO Synchronization Registers" */
#define VMSVGA_REG_BUSY 22                /* See "FIFO Synchronization Registers" */
#define VMSVGA_REG_GUEST_ID 23            /* Set guest OS identifier */
#define VMSVGA_REG_CURSOR_ID 24           /* (Deprecated) */
#define VMSVGA_REG_CURSOR_X 25            /* (Deprecated) */
#define VMSVGA_REG_CURSOR_Y 26            /* (Deprecated) */
#define VMSVGA_REG_CURSOR_ON 27           /* (Deprecated) */
#define VMSVGA_REG_HOST_BITS_PER_PIXEL 28 /* (Deprecated) */
#define VMSVGA_REG_SCRATCH_SIZE 29        /* Number of scratch registers */
#define VMSVGA_REG_MEM_REGS 30            /* Number of FIFO registers */
#define VMSVGA_REG_NUM_DISPLAYS 31        /* (Deprecated) */
#define VMSVGA_REG_PITCHLOCK 32           /* Fixed pitch for all modes */
#define VMSVGA_REG_IRQMASK 33             /* Interrupt mask */

/* Legacy multi-monitor support */
#define VMSVGA_REG_NUM_GUEST_DISPLAYS \
    34 /* Number of guest displays in X/Y direction */
#define VMSVGA_REG_DISPLAY_ID \
    35 /* Display ID for the following display attributes */
#define VMSVGA_REG_DISPLAY_IS_PRIMARY                                         \
    36                                   /* Whether this is a primary display \
                                          */
#define VMSVGA_REG_DISPLAY_POSITION_X 37 /* The display position x */
#define VMSVGA_REG_DISPLAY_POSITION_Y 38 /* The display position y */
#define VMSVGA_REG_DISPLAY_WIDTH 39      /* The display's width */
#define VMSVGA_REG_DISPLAY_HEIGHT 40     /* The display's height */

/* See "Guest memory regions" below. */
#define VMSVGA_REG_GMR_ID 41
#define VMSVGA_REG_GMR_DESCRIPTOR 42
#define VMSVGA_REG_GMR_MAX_IDS 43
#define VMSVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH 44
#define VMSVGA_REG_TRACES \
    45 /* Enable trace-based updates even when FIFO is on */
#define VMSVGA_REG_GMRS_MAX_PAGES \
    46 /* Maximum number of 4KB pages for all GMRs */
#define VMSVGA_REG_MEMORY_SIZE \
    47                    /* Total dedicated device memory excluding FIFO */
#define VMSVGA_REG_TOP 48 /* Must be 1 more than the last register */

#define VMSVGA_FIFO_MIN 0
#define VMSVGA_FIFO_MAX 1
#define VMSVGA_FIFO_NEXT_CMD 2
#define VMSVGA_FIFO_STOP 3
#define VMSVGA_FIFO_CAPABILITIES 4
#define VMSVGA_FIFO_FLAGS 5
#define VMSVGA_FIFO_FENCE 6
#define VMSVGA_FIFO_3D_HWVERSION 7
#define VMSVGA_FIFO_PITCHLOCK 8
#define VMSVGA_FIFO_CURSOR_ON 9
#define VMSVGA_FIFO_CURSOR_X 10
#define VMSVGA_FIFO_CURSOR_Y 11
#define VMSVGA_FIFO_CURSOR_COUNT 12
#define VMSVGA_FIFO_CURSOR_LAST_UPDATED 13
#define VMSVGA_FIFO_RESERVED 14
#define VMSVGA_FIFO_CURSOR_SCREEN_ID 15
#define VMSVGA_FIFO_DEAD 16
#define VMSVGA_FIFO_3D_HWVERSION_REVISED 17
#define VMSVGA_FIFO_3D_CAPS 18
#define VMSVGA_FIFO_3D_CAPS_LAST = 19
#define VMSVGA_FIFO_GUEST_3D_HWVERSION 20
#define VMSVGA_FIFO_FENCE_GOAL 21
#define VMSVGA_FIFO_BUSY 22
#define VMSVGA_FIFO_NUM_REGS 23

#define VMSVGA_FIFO_CAP_NONE 0
#define VMSVGA_FIFO_CAP_FENCE (1 << 0)
#define VMSVGA_FIFO_CAP_ACCELFRONT (1 << 1)
#define VMSVGA_FIFO_CAP_PITCHLOCK (1 << 2)
#define VMSVGA_FIFO_CAP_VIDEO (1 << 3)
#define VMSVGA_FIFO_CAP_CURSOR_BYPASS_3 (1 << 4)
#define VMSVGA_FIFO_CAP_ESCAPE (1 << 5)
#define VMSVGA_FIFO_CAP_RESERVE (1 << 6)
#define VMSVGA_FIFO_CAP_SCREEN_OBJECT (1 << 7)
#define VMSVGA_FIFO_CAP_GMR2 (1 << 8)
// #define VMSVGA_FIFO_CAP_3D_HWVERSION_REVISED  VMSVGA_FIFO_CAP_GMR2
#define VMSVGA_FIFO_CAP_SCREEN_OBJECT_2 (1 << 9)
#define VMSVGA_FIFO_CAP_DEAD (1 << 10)

@vmsvga_info vmsvga;
MemSet(&vmsvga, 0, sizeof(@vmsvga_info));

U32 @vmsvga_reg_read(I64 index)
{
    OutU32(vmsvga.io_base, index);
    return InU32(vmsvga.io_base + 1);
}

U0 @vmsvga_reg_write(I64 index, U32 val)
{
    OutU32(vmsvga.io_base, index);
    OutU32(vmsvga.io_base + 1, val);
}

U0 @vmsvga_fifo_write(U32 value)
{
    /* Need to sync? */
    if ((vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] + sizeof(U32) == vmsvga.fifo[VMSVGA_FIFO_STOP]) || (vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] == vmsvga.fifo[VMSVGA_FIFO_MAX] - sizeof(U32) && vmsvga.fifo[VMSVGA_FIFO_STOP] == vmsvga.fifo[VMSVGA_FIFO_MIN])) {
        //"Syncing because of full fifo\n";
        // vmwareWaitForFB(pVMWARE);
    }
    vmsvga.fifo[vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] / sizeof(U32)] = value;
    if (vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] == vmsvga.fifo[VMSVGA_FIFO_MAX] - sizeof(U32)) {
        vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] = vmsvga.fifo[VMSVGA_FIFO_MIN];
    } else {
        vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] += sizeof(U32);
    }
}

U0 @vmsvga_fifo_get_cap(U8* s, I64 cap)
{
    "%32s:", s;
    if ((vmsvga.fifo[VMSVGA_FIFO_CAPABILITIES] & cap) == cap)
        "%s\n", "True";
    else
        "%s\n", "False";
}

I64 @vmsvga_init(I64 w, I64 h, I64 bpp)
{
    I64 j;
    j = PCIClassFind(0x030000, 0);
    if (j < 0) {
        //"VMSVGA device not found.\n";
        return -1;
    }
    vmsvga.io_base = PCIReadU16(j.u8[2], j.u8[1], j.u8[0], 0x10) & ~(0x0F);
    @vmsvga_reg_write(VMSVGA_REG_ID, VMSVGA_ID_2);
    if (@vmsvga_reg_read(VMSVGA_REG_ID) == VMSVGA_ID_2) {
        //"VMSVGA driver version 2 supported.\n";
    } else {
        //"VMSVGA device not supported.\n";
        return -1;
    }
    vmsvga.width = w;
    vmsvga.height = h;
    vmsvga.bpp = bpp;
    vmsvga.fb = @vmsvga_reg_read(VMSVGA_REG_FB_START);
    vmsvga.fifo = @vmsvga_reg_read(VMSVGA_REG_MEM_START);
    //"FIFO @ 0x%08X (%d bytes)\n", vmsvga.fifo,
    //    @vmsvga_reg_read(VMSVGA_REG_MEM_SIZE);
    @vmsvga_reg_write(VMSVGA_REG_WIDTH, 1920);
    @vmsvga_reg_write(VMSVGA_REG_HEIGHT, 1080);
    @vmsvga_reg_write(VMSVGA_REG_BITS_PER_PIXEL, 32);
    @vmsvga_reg_write(VMSVGA_REG_ENABLE, 1);
    vmsvga.fifo[VMSVGA_FIFO_MIN] = 16;
    vmsvga.fifo[VMSVGA_FIFO_MAX] = 16 + (10 * 1024);
    vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] = 16;
    vmsvga.fifo[VMSVGA_FIFO_STOP] = 16;
    @vmsvga_reg_write(VMSVGA_REG_CONFIG_DONE, 0);
    @vmsvga_fifo_write(VMSVGA_CMD_UPDATE);
    @vmsvga_fifo_write(0);
    @vmsvga_fifo_write(0);
    @vmsvga_fifo_write(0);
    @vmsvga_fifo_write(0);
    @vmsvga_reg_write(VMSVGA_REG_CONFIG_DONE, 1);
    return 0;
}

U0 @vmsvga_mouse_pointer_set(U32* pointer, I64 width, I64 height)
{
    @vmsvga_reg_write(VMSVGA_REG_CONFIG_DONE, 0);
    @vmsvga_fifo_write(VMSVGA_CMD_DEFINE_ALPHA_CURSOR);
    @vmsvga_fifo_write(VMSVGA_MOUSE_ID);
    @vmsvga_fifo_write(0);
    @vmsvga_fifo_write(0);
    @vmsvga_fifo_write(width);
    @vmsvga_fifo_write(height);
    I64 x, y;
    for (y = 0; y < height; y++) {
        for (x = 0; x < width; x++) {
            @vmsvga_fifo_write(pointer[(y * width) + x]);
        }
    }
    @vmsvga_reg_write(VMSVGA_REG_CONFIG_DONE, 1);
}

U64 @vmsvga_get_framebuffer() { return vmsvga.fb; }

U0 @vmsvga_display_update() { @vmsvga_reg_write(VMSVGA_REG_ENABLE, 1); }

class @vmsvga
{
    U0 (*Init)(I64 w, I64 h, I64 bpp);
    U64 (*FrameBuffer)();
};

@vmsvga VMSVGA;
VMSVGA.FrameBuffer = &@vmsvga_get_framebuffer;
VMSVGA.Init = &@vmsvga_init;

"vmsvga ";
