322 lines
10 KiB
C
322 lines
10 KiB
C
/*
|
|
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <GLUT/glut.h>
|
|
#include "shoebill.h"
|
|
|
|
#define ROM_SIZE 4096
|
|
|
|
uint8_t macii_video_rom[ROM_SIZE] = {
|
|
/*
|
|
* Redacted!
|
|
* But if you own a toby frame buffer card, you
|
|
* can dump its 4kb ROM and stick it here.
|
|
* A/UX 1, 2 and 3 seem happy with this hacky
|
|
* implementation.
|
|
*/
|
|
};
|
|
|
|
typedef struct {
|
|
uint8_t *buf_base;
|
|
uint32_t buf_size;
|
|
uint32_t clut_idx;
|
|
uint32_t h_offset; // offset in bytes for each horizontal line
|
|
uint8_t clut[256 * 3];
|
|
uint8_t depth;
|
|
uint8_t vsync;
|
|
|
|
uint8_t *gldat;
|
|
} tfb_ctx_t;
|
|
|
|
|
|
void nubus_tfb_display_func (void)
|
|
{
|
|
uint32_t myw = glutGet(GLUT_WINDOW_WIDTH);
|
|
uint32_t myh = glutGet(GLUT_WINDOW_HEIGHT);
|
|
uint32_t slotnum, i;
|
|
int32_t my_window_id = glutGetWindow();
|
|
|
|
for (slotnum = 0; slotnum < 16; slotnum++)
|
|
if (shoe.slots[slotnum].glut_window_id == my_window_id)
|
|
break;
|
|
|
|
tfb_ctx_t *ctx = (tfb_ctx_t*)shoe.slots[slotnum].ctx;
|
|
|
|
const uint8_t depth = ctx->depth;
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0, myw, 0, myh, 0, 1);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glColor3f(0.1, 0.1, 0.8);
|
|
|
|
uint32_t gli = 0;
|
|
for (i=0; i < (1024 * 480);) {
|
|
uint8_t code;
|
|
|
|
if (i % 1024 == 640) {
|
|
i += (1024 - 640);
|
|
continue;
|
|
}
|
|
|
|
assert(gli < (640 * 480));
|
|
|
|
if (depth <= 1) {
|
|
code = (ctx->buf_base[ctx->h_offset + i/8] & (0x80 >> (i % 8))) != 0;
|
|
code = (code << 7) | 0x7f;
|
|
}
|
|
else if (depth == 2) {
|
|
const uint8_t byte = ctx->buf_base[ctx->h_offset + i/4];
|
|
const uint8_t mod = i % 4;
|
|
code = (byte << (mod * 2)) & 0xc0;
|
|
code |= 0x3f;
|
|
}
|
|
else if (depth == 4) {
|
|
const uint8_t byte = ctx->buf_base[ctx->h_offset + i/2];
|
|
const uint8_t mod = i % 2;
|
|
code = (byte << (mod * 4)) & 0xf0;
|
|
code |= 0x0f;
|
|
}
|
|
else if (depth == 8) {
|
|
code = ctx->buf_base[ctx->h_offset + i];
|
|
}
|
|
|
|
ctx->gldat[gli * 3 + 0] = ctx->clut[code * 3 + 2];
|
|
ctx->gldat[gli * 3 + 1] = ctx->clut[code * 3 + 1];
|
|
ctx->gldat[gli * 3 + 2] = ctx->clut[code * 3 + 0];
|
|
|
|
gli++;
|
|
i++;
|
|
}
|
|
|
|
glViewport(0, 0, myw, myh);
|
|
glRasterPos2i(0, myh);
|
|
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
glPixelZoom(1.0, -1.0);
|
|
|
|
glDrawPixels(myw, myh, GL_RGB, GL_UNSIGNED_BYTE, ctx->gldat);
|
|
|
|
glFlush();
|
|
}
|
|
|
|
void global_mouse_func (int button, int state, int x, int y);
|
|
void global_motion_func (int x, int y);
|
|
void global_passive_motion_func (int x, int y);
|
|
void global_keyboard_up_func (unsigned char c, int x, int y);
|
|
void global_keyboard_down_func (unsigned char c, int x, int y);
|
|
void global_special_up_func (int special, int x, int y);
|
|
void global_special_down_func (int special, int x, int y);
|
|
|
|
void nubus_tfb_init(uint8_t slotnum)
|
|
{
|
|
tfb_ctx_t *ctx = (tfb_ctx_t*)calloc(sizeof(tfb_ctx_t), 1);
|
|
|
|
ctx->buf_size = 512 * 1024;
|
|
ctx->buf_base = (uint8_t*)calloc(ctx->buf_size, 1);
|
|
ctx->depth = 1;
|
|
ctx->clut_idx = 786;
|
|
ctx->gldat = valloc(480 * 640 * 3);
|
|
|
|
memset(ctx->clut, 0x0, 384);
|
|
memset(ctx->clut+384, 0xff, 384);
|
|
memset(ctx->buf_base, 0xff, ctx->buf_size);
|
|
|
|
shoe.slots[slotnum].ctx = ctx;
|
|
|
|
glutInitWindowSize(640, 480);
|
|
shoe.slots[slotnum].glut_window_id = glutCreateWindow("");
|
|
glutDisplayFunc(nubus_tfb_display_func);
|
|
|
|
glutIgnoreKeyRepeat(1);
|
|
glutKeyboardFunc(global_keyboard_down_func);
|
|
glutKeyboardUpFunc(global_keyboard_up_func);
|
|
|
|
glutSpecialFunc(global_special_down_func);
|
|
glutSpecialUpFunc(global_special_up_func);
|
|
|
|
glutMouseFunc(global_mouse_func);
|
|
glutMotionFunc(global_motion_func);
|
|
glutPassiveMotionFunc(global_passive_motion_func);
|
|
|
|
glShadeModel(GL_FLAT);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glClearColor(0.1, 1.0, 0.1, 1.0);
|
|
}
|
|
|
|
uint32_t nubus_tfb_read_func(const uint32_t rawaddr, const uint32_t size, const uint8_t slotnum)
|
|
{
|
|
// 1111 nnnn xxxx rrrr SSSSSSSS SSSSSSxx
|
|
// n -> slot number
|
|
// S -> significant bits
|
|
// x -> ignored
|
|
// region ->
|
|
// 0000-0111(?) - framebuffer
|
|
// 1000
|
|
// 1001
|
|
// 1010
|
|
// 1011
|
|
// 1100
|
|
// 1101
|
|
// 1110
|
|
// 1111 = ROM (repeating 4kb images in S bits)
|
|
tfb_ctx_t *ctx = (tfb_ctx_t*)shoe.slots[slotnum].ctx;
|
|
|
|
const uint32_t addr = rawaddr & 0x000fffff;
|
|
uint32_t result = 0;
|
|
|
|
switch (addr >> 16) {
|
|
case 0x0 ... 0x7: { // frame buffer
|
|
uint32_t i;
|
|
for (i=0; i<size; i++)
|
|
result = (result << 8) | ctx->buf_base[addr + i];
|
|
break;
|
|
}
|
|
|
|
case 0xf: { // rom
|
|
if ((addr & 3) == 0) {
|
|
const uint32_t rom_addr = (addr >> 2) % 4096;
|
|
result = macii_video_rom[rom_addr];
|
|
printf("nubus_tfb_read_func: returning %x for addr %x (rom_addr=%x)\n", (uint32_t)result, shoe.physical_addr, rom_addr);
|
|
}
|
|
else
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
case 0xd: { // vsync registers
|
|
// 0xd0000 = ReadVSync
|
|
// 0xd0004, d0008 = ??
|
|
if (addr == 0xd0000) {
|
|
ctx->vsync++;
|
|
result = (ctx->vsync >> 7)&1;
|
|
printf("nubus_tfb_read_func: reading vsync bit shoe.pc=0x%08x\n", shoe.orig_pc);
|
|
// We just need to oscillate the vsync bit, to pretend we're going in and out of the blanking interval.
|
|
// The card driver waits until the bit goes low, then waits until it goes high
|
|
// FIXME: clean this up
|
|
break;
|
|
}
|
|
else {
|
|
printf("nubus_tfb_read_func: unknown read of *0x%08x\n", shoe.physical_addr);
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
default: {
|
|
printf("nubus_tfb_read_func: unknown read of *0x%08x\n", shoe.physical_addr);
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void nubus_tfb_write_func(const uint32_t rawaddr, const uint32_t size,
|
|
const uint32_t data, const uint8_t slotnum)
|
|
{
|
|
tfb_ctx_t *ctx = (tfb_ctx_t*)shoe.slots[slotnum].ctx;
|
|
const uint32_t addr = rawaddr & 0x000fffff;
|
|
|
|
switch (addr >> 16) {
|
|
|
|
case 0x0 ... 0x7: { // frame buffer
|
|
uint32_t i;
|
|
for (i=0; i<size; i++) {
|
|
ctx->buf_base[addr + size - (i+1)] = (data >> (8*i)) & 0xFF;
|
|
}
|
|
return ;
|
|
}
|
|
|
|
case 0x8: { // other registers
|
|
|
|
if (addr == 0x80000) { // some register that seems to indicate the color depth
|
|
const uint8_t val = data & 0xff;
|
|
if (val == 0xdf)
|
|
ctx->depth = 1;
|
|
else if (val == 0xbf)
|
|
ctx->depth = 2;
|
|
else if (val == 0x7f)
|
|
ctx->depth = 4;
|
|
else if (val == 0xff)
|
|
ctx->depth = 8;
|
|
else
|
|
assert(!"Can't figure out the color depth!");
|
|
return ;
|
|
}
|
|
|
|
if (addr == 0x8000c) { // horizontal offset
|
|
ctx->h_offset = 4 * ((~data) & 0xff);
|
|
return ;
|
|
}
|
|
else {
|
|
printf("video_nubus_set: unknown write to *0x%08x (0x%x)\n", shoe.physical_addr, data);
|
|
return ;
|
|
}
|
|
}
|
|
|
|
case 0x9: { // CLUT registers?
|
|
if (addr == 0x90018) { // CLUT
|
|
printf("clut[0x%03x (0x%02x+%u)] = 0x%02x\n", ctx->clut_idx, ctx->clut_idx/3, ctx->clut_idx%3, (uint8_t)(data & 0xff));
|
|
ctx->clut[ctx->clut_idx] = 255 - (data & 0xff);
|
|
|
|
ctx->clut_idx = (ctx->clut_idx == 0) ? 767 : ctx->clut_idx-1;
|
|
|
|
return ;
|
|
}
|
|
|
|
if (addr == 0x9001c) { // CLUT register?
|
|
const uint32_t clut_dat = data & 0xff;
|
|
|
|
ctx->clut_idx = clut_dat * 3 + 2;
|
|
|
|
}
|
|
printf("video_nubus_set: unknown write to *0x%08x (0x%x)\n", shoe.physical_addr, data);
|
|
return ;
|
|
}
|
|
|
|
case 0xa: { // clear interrupt for slot 0xa(?) in via2.rega (by setting the appropriate bit)
|
|
assert((data & 0xff) == 0);
|
|
// shoe.via[1].rega |= 0b00000010;
|
|
shoe.via[1].rega |= (1 << (slotnum - 9));
|
|
return ;
|
|
}
|
|
|
|
default: {
|
|
printf("video_nubus_set: unknown write to *0x%08x (0x%x)\n", shoe.physical_addr, data);
|
|
return ;
|
|
}
|
|
}
|
|
}
|