mirror of
https://github.com/Pecusx/libretro-atari800.git
synced 2026-05-21 06:39:36 +02:00
391 lines
10 KiB
C
391 lines
10 KiB
C
/*
|
|
* af80.c - Emulation of the Austin Franklin 80 column card.
|
|
*
|
|
* Copyright (C) 2009 Perry McFarlane
|
|
* Copyright (C) 2009 Atari800 development team (see DOC/CREDITS)
|
|
*
|
|
* This file is part of the Atari800 emulator project which emulates
|
|
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
|
*
|
|
* Atari800 is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Atari800 is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Atari800; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "af80.h"
|
|
#include "atari.h"
|
|
#include "util.h"
|
|
#include "log.h"
|
|
#include "memory.h"
|
|
#include "cpu.h"
|
|
#include <stdlib.h>
|
|
|
|
static UBYTE *af80_rom = NULL;
|
|
static char af80_rom_filename[FILENAME_MAX];
|
|
static UBYTE *af80_charset = NULL;
|
|
static char af80_charset_filename[FILENAME_MAX];
|
|
|
|
static UBYTE *af80_screen = NULL;
|
|
static UBYTE *af80_attrib = NULL;
|
|
|
|
int AF80_enabled = FALSE;
|
|
|
|
/* Austin Franklin information from forum posts by warerat at Atariage */
|
|
static int rom_bank_select; /* bits 0-3 of d5f7, $0-$f 16 banks */
|
|
static int not_rom_output_enable; /* bit 4 of d5f7 0 = Enable ROM 1 = Disable ROM */
|
|
static int not_right_cartridge_rd4_control; /* 0=$8000-$9fff cart ROM, 1= $8000-$9fff system RAM */
|
|
static int not_enable_2k_character_ram;
|
|
static int not_enable_2k_attribute_ram;
|
|
static int not_enable_crtc_registers;
|
|
static int not_enable_80_column_output;
|
|
static int video_bank_select; /* bits 0-3 of d5f6, $0-$f 16 banks */
|
|
static int crtreg[0x40];
|
|
static int const rgbi_palette[16] = {
|
|
0x000000, /* black */
|
|
0x0000AA, /* blue */
|
|
0x00AA00, /* green */
|
|
0x00AAAA, /* cyan */
|
|
0xAA0000, /* red */
|
|
0xAA00AA, /* magenta */
|
|
0xAA5500, /* brown */
|
|
0xAAAAAA, /* white */
|
|
0x555555, /* grey */
|
|
0x5555FF, /* light blue */
|
|
0x55FF55, /* light green */
|
|
0x55FFFF, /* light cyan */
|
|
0xFF5555, /* light red */
|
|
0xFF55FF, /* light magenta */
|
|
0xFFFF55, /* yellow */
|
|
0xFFFFFF /* white (high intensity) */
|
|
};
|
|
int AF80_palette[16];
|
|
|
|
#ifdef AF80_DEBUG
|
|
#define D(a) a
|
|
#else
|
|
#define D(a) do{}while(0)
|
|
#endif
|
|
|
|
static void update_d6(void)
|
|
{
|
|
if (!not_enable_2k_character_ram) {
|
|
memcpy(MEMORY_mem + 0xd600, af80_screen + (video_bank_select<<7), 0x80);
|
|
memcpy(MEMORY_mem + 0xd680, af80_screen + (video_bank_select<<7), 0x80);
|
|
}
|
|
else if (!not_enable_2k_attribute_ram) {
|
|
memcpy(MEMORY_mem + 0xd600, af80_attrib + (video_bank_select<<7), 0x80);
|
|
memcpy(MEMORY_mem + 0xd680, af80_attrib + (video_bank_select<<7), 0x80);
|
|
}
|
|
else if (not_enable_crtc_registers) {
|
|
memset(MEMORY_mem + 0xd600, 0xff, 0x100);
|
|
}
|
|
}
|
|
|
|
static void update_d5(void)
|
|
{
|
|
if (not_rom_output_enable) {
|
|
memset(MEMORY_mem + 0xd500, 0xff, 0x100);
|
|
}
|
|
else {
|
|
memcpy(MEMORY_mem + 0xd500, af80_rom + (rom_bank_select<<8), 0x100);
|
|
}
|
|
}
|
|
|
|
static void update_8000_9fff(void)
|
|
{
|
|
if (not_right_cartridge_rd4_control) return;
|
|
if (not_rom_output_enable) {
|
|
memset(MEMORY_mem + 0x8000, 0xff, 0x2000);
|
|
}
|
|
else {
|
|
int i;
|
|
for (i=0; i<32; i++) {
|
|
memcpy(MEMORY_mem + 0x8000 + (i<<8), af80_rom + (rom_bank_select<<8), 0x100);
|
|
}
|
|
}
|
|
}
|
|
|
|
int AF80_Initialise(int *argc, char *argv[])
|
|
{
|
|
int i, j;
|
|
int help_only = FALSE;
|
|
for (i = j = 1; i < *argc; i++) {
|
|
if (strcmp(argv[i], "-af80") == 0) {
|
|
AF80_enabled = TRUE;
|
|
}
|
|
else {
|
|
if (strcmp(argv[i], "-help") == 0) {
|
|
help_only = TRUE;
|
|
Log_print("\t-af80 Emulate the Austin Franklin 80 column board");
|
|
}
|
|
argv[j++] = argv[i];
|
|
}
|
|
}
|
|
*argc = j;
|
|
|
|
if (help_only)
|
|
return TRUE;
|
|
|
|
if (AF80_enabled) {
|
|
Log_print("Austin Franklin 80 enabled");
|
|
af80_rom = (UBYTE *)Util_malloc(0x1000);
|
|
if (!Atari800_LoadImage(af80_rom_filename, af80_rom, 0x1000)) {
|
|
free(af80_rom);
|
|
af80_rom = NULL;
|
|
AF80_enabled = FALSE;
|
|
Log_print("Couldn't load Austin Franklin ROM image");
|
|
return FALSE;
|
|
}
|
|
else {
|
|
Log_print("loaded Austin Franklin rom image");
|
|
}
|
|
af80_charset = (UBYTE *)Util_malloc(0x1000);
|
|
if (!Atari800_LoadImage(af80_charset_filename, af80_charset, 0x1000)) {
|
|
free(af80_charset);
|
|
free(af80_rom);
|
|
af80_charset = af80_rom = NULL;
|
|
AF80_enabled = FALSE;
|
|
Log_print("Couldn't load Austin Franklin charset image");
|
|
return FALSE;
|
|
}
|
|
else {
|
|
Log_print("loaded Austin Franklin charset image");
|
|
}
|
|
af80_screen = (UBYTE *)Util_malloc(0x800);
|
|
af80_attrib = (UBYTE *)Util_malloc(0x800);
|
|
AF80_Reset();
|
|
|
|
/* swap palette */
|
|
for (i=0; i<16; i++ ) {
|
|
j=i;
|
|
j = (j&0x0a) + ((j&0x01) << 2) + ((j&0x04) >> 2);
|
|
AF80_palette[i] = rgbi_palette[j];
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void AF80_Exit(void)
|
|
{
|
|
free(af80_screen);
|
|
free(af80_attrib);
|
|
free(af80_charset);
|
|
free(af80_rom);
|
|
af80_screen = af80_attrib = af80_charset = af80_rom = NULL;
|
|
}
|
|
|
|
void AF80_InsertRightCartridge(void)
|
|
{
|
|
MEMORY_Cart809fEnable();
|
|
update_d5();
|
|
update_8000_9fff();
|
|
}
|
|
|
|
int AF80_ReadConfig(char *string, char *ptr)
|
|
{
|
|
if (strcmp(string, "AF80_ROM") == 0)
|
|
Util_strlcpy(af80_rom_filename, ptr, sizeof(af80_rom_filename));
|
|
else if (strcmp(string, "AF80_CHARSET") == 0)
|
|
Util_strlcpy(af80_charset_filename, ptr, sizeof(af80_charset_filename));
|
|
else return FALSE; /* no match */
|
|
return TRUE; /* matched something */
|
|
}
|
|
|
|
void AF80_WriteConfig(FILE *fp)
|
|
{
|
|
fprintf(fp, "AF80_ROM=%s\n", af80_rom_filename);
|
|
fprintf(fp, "AF80_CHARSET=%s\n", af80_charset_filename);
|
|
}
|
|
|
|
int AF80_D6GetByte(UWORD addr, int no_side_effects)
|
|
{
|
|
int result = 0xff;
|
|
if (!not_enable_2k_character_ram) {
|
|
result = MEMORY_dGetByte(addr);
|
|
}
|
|
else if (!not_enable_2k_attribute_ram) {
|
|
result = MEMORY_dGetByte(addr);
|
|
}
|
|
else if (!not_enable_crtc_registers) {
|
|
if (video_bank_select == 0 ) {
|
|
if ((addr&0xff)<0x40) {
|
|
result = crtreg[addr&0xff];
|
|
if ((addr&0xff) == 0x3a) {
|
|
result = 0x01;
|
|
}
|
|
}
|
|
D(printf("AF80 Read addr:%4x cpu:%4x\n", addr, CPU_remember_PC[(CPU_remember_PC_curpos-1)%CPU_REMEMBER_PC_STEPS]));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void AF80_D6PutByte(UWORD addr, UBYTE byte)
|
|
{
|
|
if (!not_enable_2k_character_ram) {
|
|
MEMORY_dPutByte((addr&0xff7f),byte);
|
|
MEMORY_dPutByte((addr&0xff7f)+0x80,byte);
|
|
af80_screen[(addr&0x7f) + (video_bank_select<<7)] = byte;
|
|
}
|
|
else if (!not_enable_2k_attribute_ram) {
|
|
MEMORY_dPutByte((addr&0xff7f),byte);
|
|
MEMORY_dPutByte((addr&0xff7f)+0x80,byte);
|
|
af80_attrib[(addr&0x7f) + (video_bank_select<<7)] = byte;
|
|
D(printf("AF80 Write, attribute, addr:%4x byte:%2x, cpu:%4x\n", addr, byte,CPU_remember_PC[(CPU_remember_PC_curpos-1)%CPU_REMEMBER_PC_STEPS]));
|
|
}
|
|
else if (!not_enable_crtc_registers) {
|
|
if (video_bank_select == 0 ) {
|
|
if ((addr&0xff)<0x40) {
|
|
crtreg[addr&0xff] = byte;
|
|
}
|
|
D(if (1 || (addr!=0xd618 && addr!=0xd619)) printf("AF80 Write addr:%4x byte:%2x, cpu:%4x\n", addr, byte,CPU_remember_PC[(CPU_remember_PC_curpos-1)%CPU_REMEMBER_PC_STEPS]));
|
|
}
|
|
else {
|
|
D(printf("AF80 Write, video_bank_select!=0, addr:%4x byte:%2x, cpu:%4x\n", addr, byte,CPU_remember_PC[(CPU_remember_PC_curpos-1)%CPU_REMEMBER_PC_STEPS]));
|
|
}
|
|
}
|
|
}
|
|
|
|
int AF80_D5GetByte(UWORD addr, int no_side_effects)
|
|
{
|
|
int result = MEMORY_dGetByte(addr);
|
|
return result;
|
|
}
|
|
|
|
void AF80_D5PutByte(UWORD addr, UBYTE byte)
|
|
{
|
|
if (addr == 0xd5f6) {
|
|
int need_update_d6 = FALSE;
|
|
if ((byte&0x10) != not_enable_2k_character_ram) {
|
|
not_enable_2k_character_ram = (byte & 0x10);
|
|
need_update_d6 = TRUE;
|
|
}
|
|
if ((byte&0x20) != not_enable_2k_attribute_ram) {
|
|
not_enable_2k_attribute_ram = (byte & 0x20);
|
|
need_update_d6 = TRUE;
|
|
}
|
|
if ((byte&0x40) != not_enable_crtc_registers) {
|
|
not_enable_crtc_registers = (byte & 0x40);
|
|
need_update_d6 = TRUE;
|
|
}
|
|
if ((byte&0x80) != not_enable_80_column_output) {
|
|
not_enable_80_column_output = (byte & 0x80);
|
|
}
|
|
if ((byte&0x0f) != video_bank_select) {
|
|
video_bank_select = (byte & 0x0f);
|
|
need_update_d6 = TRUE;
|
|
}
|
|
if (need_update_d6) {
|
|
update_d6();
|
|
}
|
|
}
|
|
else if (addr == 0xd5f7) {
|
|
int need_update_d5 = FALSE;
|
|
int need_update_8000_9fff = FALSE;
|
|
if ((byte&0x10) != not_rom_output_enable) {
|
|
not_rom_output_enable = (byte & 0x10);
|
|
need_update_d5 = TRUE;
|
|
if (byte&0x20) {
|
|
need_update_8000_9fff = TRUE;
|
|
}
|
|
}
|
|
if ((byte&0x20) != not_right_cartridge_rd4_control) {
|
|
not_right_cartridge_rd4_control = (byte & 0x20);
|
|
if (not_right_cartridge_rd4_control) {
|
|
MEMORY_Cart809fDisable();
|
|
}
|
|
else {
|
|
MEMORY_Cart809fEnable();
|
|
need_update_8000_9fff = TRUE;
|
|
}
|
|
}
|
|
if ((byte&0x0f) != rom_bank_select) {
|
|
rom_bank_select = (byte & 0x0f);
|
|
if (!not_rom_output_enable) {
|
|
need_update_d5 = TRUE;
|
|
if (!not_right_cartridge_rd4_control) {
|
|
need_update_8000_9fff = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (need_update_d5) {
|
|
update_d5();
|
|
}
|
|
if (need_update_8000_9fff) {
|
|
update_8000_9fff();
|
|
}
|
|
}
|
|
D(if (addr!=0xd5f7 && addr!=0xd5f6) printf("AF80 Write addr:%4x byte:%2x, cpu:%4x\n", addr, byte,CPU_remember_PC[(CPU_remember_PC_curpos-1)%CPU_REMEMBER_PC_STEPS]));
|
|
}
|
|
|
|
UBYTE AF80_GetPixels(int scanline, int column, int *colour, int blink)
|
|
{
|
|
#define AF80_ROWS 25
|
|
#define AF80_CELL_HEIGHT 10
|
|
UBYTE character;
|
|
int attrib;
|
|
UBYTE font_data;
|
|
int table_start = crtreg[0x0c] + ((crtreg[0x0d]&0x3f)<<8);
|
|
int row = scanline / AF80_CELL_HEIGHT;
|
|
int line = scanline % AF80_CELL_HEIGHT;
|
|
int screen_pos;
|
|
if (row >= AF80_ROWS) {
|
|
return 0;
|
|
}
|
|
|
|
if (row >= crtreg[0x10]) {
|
|
screen_pos = (row-crtreg[0x10])*80 + column + crtreg[0x0e] + ((crtreg[0x0f]&0x3f)<<8);
|
|
}
|
|
else {
|
|
screen_pos = row*80+column + table_start;
|
|
}
|
|
screen_pos &= 0x7ff;
|
|
character = af80_screen[screen_pos];
|
|
attrib = af80_attrib[screen_pos];
|
|
font_data = af80_charset[character*16 + line];
|
|
if (attrib & 0x01) {
|
|
font_data ^= 0xff; /* invert */
|
|
}
|
|
if ((attrib & 0x02) && blink) {
|
|
font_data = 0x00; /* blink */
|
|
}
|
|
if (line+1 == AF80_CELL_HEIGHT && (attrib & 0x04)) {
|
|
font_data = 0xff; /* underline */
|
|
}
|
|
if (row == crtreg[0x18] && column == crtreg[0x19] && !blink) {
|
|
font_data = 0xff; /* cursor */
|
|
}
|
|
*colour = attrib>>4; /* set number of palette entry */
|
|
return font_data;
|
|
}
|
|
|
|
void AF80_Reset(void)
|
|
{
|
|
memset(af80_screen, 0, 0x800);
|
|
memset(af80_attrib, 0, 0x800);
|
|
rom_bank_select = 0;
|
|
not_rom_output_enable = 0;
|
|
not_right_cartridge_rd4_control = 0;
|
|
not_enable_2k_character_ram = 0;
|
|
not_enable_2k_attribute_ram = 0;
|
|
not_enable_crtc_registers = 0;
|
|
not_enable_80_column_output = 0;
|
|
video_bank_select = 0;
|
|
memset(crtreg, 0, 0x40);
|
|
}
|
|
|
|
/*
|
|
vim:ts=4:sw=4:
|
|
*/
|