Fixes #49 Implement low resolution graphics (#55)

This commit is contained in:
Stefan Arentz 2016-12-02 11:36:07 -05:00 committed by GitHub
parent 1a26f30350
commit 8c540daa1b
3 changed files with 226 additions and 87 deletions

115
a2p.c
View File

@ -29,23 +29,27 @@
#include "dsk.h"
#include "a2p.h"
#define EWM_A2P_SS_KBD 0xc000
#define EWM_A2P_SS_KBDSTRB 0xc010
#define EWM_A2P_SS_SPKR 0xc030
#define EWM_A2P_SS_TXTCLR 0xc050
#define EWM_A2P_SS_TXTSET 0xc051
#define EWM_A2P_SS_LOSCR 0xc054
#define EWM_A2P_SS_HISCR 0xc055
#define EWM_A2P_SS_LORES 0xc056
#define EWM_A2P_SS_HIRES 0xc057
#define EWM_A2P_SS_SETAN0 0xc058
#define EWM_A2P_SS_KBD 0xc000
#define EWM_A2P_SS_KBDSTRB 0xc010
#define EWM_A2P_SS_SPKR 0xc030
#define EWM_A2P_SS_SCREEN_MODE_GRAPHICS 0xc050
#define EWM_A2P_SS_SCREEN_MODE_TEXT 0xc051
#define EWM_A2P_SS_GRAPHICS_STYLE_FULL 0xc052
#define EWM_A2P_SS_GRAPHICS_STYLE_MIXED 0xc053
#define EWM_A2P_SS_SCREEN_PAGE1 0xc054
#define EWM_A2P_SS_SCREEN_PAGE2 0xc055
#define EWM_A2P_SS_GRAPHICS_MODE_LGR 0xc056
#define EWM_A2P_SS_GRAPHICS_MODE_HGR 0xc057
#define EWM_A2P_SS_SETAN0 0xc058
#define EWM_A2P_SS_CLRAN0 0xc059
#define EWM_A2P_SS_SETAN1 0xc05a
#define EWM_A2P_SS_SETAN1 0xc05a
#define EWM_A2P_SS_CLRAN1 0xc05b
#define EWM_A2P_SS_SETAN2 0xc05c
#define EWM_A2P_SS_CLRAN2 0xc05d
#define EWM_A2P_SS_CLRAN2 0xc05d
#define EWM_A2P_SS_SETAN3 0xc05e
#define EWM_A2P_SS_CLRAN3 0xc05f
#define EWM_A2P_SS_CLRAN3 0xc05f
uint8_t a2p_iom_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) {
struct a2p_t *a2p = (struct a2p_t*) mem->obj;
@ -56,16 +60,48 @@ uint8_t a2p_iom_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) {
a2p->key &= 0x7f;
return 0x00;
case EWM_A2P_SS_LOSCR:
a2p->current_screen = 0;
a2p->screen1_dirty = true;
a2p->screen2_dirty = false;
case EWM_A2P_SS_SCREEN_MODE_GRAPHICS:
a2p->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS;
a2p->screen_dirty = true;
break;
case EWM_A2P_SS_SCREEN_MODE_TEXT:
a2p->screen_mode = EWM_A2P_SCREEN_MODE_TEXT;
a2p->screen_dirty = true;
break;
case EWM_A2P_SS_HISCR:
a2p->current_screen = 1;
a2p->screen1_dirty = false;
a2p->screen2_dirty = true;
case EWM_A2P_SS_GRAPHICS_MODE_LGR:
a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR;
a2p->screen_dirty = true;
break;
case EWM_A2P_SS_GRAPHICS_MODE_HGR:
a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_HGR;
a2p->screen_dirty = true;
break;
case EWM_A2P_SS_GRAPHICS_STYLE_FULL:
a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL;
a2p->screen_dirty = true;
break;
case EWM_A2P_SS_GRAPHICS_STYLE_MIXED:
a2p->screen_graphics_style = EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED;
a2p->screen_dirty = true;
break;
case EWM_A2P_SS_SCREEN_PAGE1:
a2p->screen_page = EWM_A2P_SCREEN_PAGE1;
a2p->screen_dirty = true;
break;
case EWM_A2P_SS_SCREEN_PAGE2:
a2p->screen_page = EWM_A2P_SCREEN_PAGE2;
a2p->screen_dirty = true;
break;
case EWM_A2P_SS_SPKR:
// TODO Implement speaker support
break;
default:
printf("[A2P] Unexpected read at $%.4X\n", addr);
break;
}
return 0;
@ -77,33 +113,26 @@ void a2p_iom_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t
case EWM_A2P_SS_KBDSTRB:
a2p->key &= 0x7f;
break;
default:
printf("[A2P] Unexpected write at $%.4X\n", addr);
break;
}
}
uint8_t a2p_screen1_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) {
uint8_t a2p_screen_txt_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) {
struct a2p_t *a2p = (struct a2p_t*) mem->obj;
return a2p->screen1_data[addr - mem->start];
return a2p->screen_txt_data[addr - mem->start];
}
void a2p_screen1_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) {
void a2p_screen_txt_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) {
struct a2p_t *a2p = (struct a2p_t*) mem->obj;
a2p->screen1_data[addr - mem->start] = b;
a2p->screen1_dirty = true;
}
uint8_t a2p_screen2_read(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr) {
struct a2p_t *a2p = (struct a2p_t*) mem->obj;
return a2p->screen2_data[addr - mem->start];
}
void a2p_screen2_write(struct cpu_t *cpu, struct mem_t *mem, uint16_t addr, uint8_t b) {
struct a2p_t *a2p = (struct a2p_t*) mem->obj;
a2p->screen2_data[addr - mem->start] = b;
a2p->screen2_dirty = true;
a2p->screen_txt_data[addr - mem->start] = b;
a2p->screen_dirty = true;
//printf("[A2P] $%.4X = $%.2X\n", addr, b);
}
void a2p_init(struct a2p_t *a2p, struct cpu_t *cpu) {
memset(a2p, sizeof(struct a2p_t), 0x00);
memset(a2p, 0x00, sizeof(struct a2p_t));
a2p->ram = cpu_add_ram(cpu, 0x0000, 48 * 1024);
a2p->rom = cpu_add_rom_file(cpu, 0xd000, "roms/a2p.rom");
@ -111,11 +140,13 @@ void a2p_init(struct a2p_t *a2p, struct cpu_t *cpu) {
a2p->dsk = ewm_dsk_create(cpu);
a2p->screen1_data = malloc(1 * 1024);
a2p->screen1 = cpu_add_iom(cpu, 0x0400, 0x07ff, a2p, a2p_screen1_read, a2p_screen1_write);
// TODO Introduce ewm_scr_t that captures everything related to the apple 2 screen so that it can be re-used.
a2p->screen2_data = malloc(1 * 1024);
a2p->screen2 = cpu_add_iom(cpu, 0x0800, 0x0bff, a2p, a2p_screen2_read, a2p_screen2_write);
a2p->screen_txt_data = malloc(2 * 1024);
a2p->screen_txt_iom = cpu_add_iom(cpu, 0x0400, 0x0bff, a2p, a2p_screen_txt_read, a2p_screen_txt_write);
//a2p->screen_hgr_data = malloc(16 * 1024);
//a2p->screen_hgr_iom = cpu_add_iom(cpu, 0x2000, 0x7fff, a2p, a2p_screen_hgr_read, a2p_screen_hgr_write);
}
int a2p_load_disk(struct a2p_t *a2p, int drive, char *path) {

27
a2p.h
View File

@ -28,21 +28,32 @@
struct mem_t;
struct ewm_dsk_t;
#define EWM_A2P_SCREEN_MODE_TEXT 0
#define EWM_A2P_SCREEN_MODE_GRAPHICS 1
#define EWM_A2P_SCREEN_GRAPHICS_MODE_LGR 0
#define EWM_A2P_SCREEN_GRAPHICS_MODE_HGR 1
#define EWM_A2P_SCREEN_GRAPHICS_STYLE_FULL 0
#define EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED 1
#define EWM_A2P_SCREEN_PAGE1 0
#define EWM_A2P_SCREEN_PAGE2 1
struct a2p_t {
struct mem_t *ram;
struct mem_t *rom;
struct mem_t *iom;
struct ewm_dsk_t *dsk;
struct mem_t *screen1;
uint8_t *screen1_data;
bool screen1_dirty;
uint8_t *screen_txt_data;
struct mem_t *screen_txt_iom;
struct mem_t *screen2;
uint8_t *screen2_data;
bool screen2_dirty;
int current_screen;
int screen_mode;
int screen_graphics_mode;
int screen_graphics_style;
int screen_page;
int screen_dirty;
uint8_t key;
};

171
scr.c
View File

@ -44,7 +44,7 @@ void scr_init() {
//
window = SDL_CreateWindow("Test", 40, 60, 280*3, 192*3, SDL_WINDOW_SHOWN);
window = SDL_CreateWindow("Test", 400, 60, 280*3, 192*3, SDL_WINDOW_SHOWN);
if (window == NULL) {
fprintf(stderr, "Failed create window: %s\n", SDL_GetError());
exit(1);
@ -75,6 +75,113 @@ static int screen2_offsets[24] = {
0xa28, 0xaa8, 0xb28, 0xba8, 0x850, 0x8d0, 0x950, 0x9d0, 0xa50, 0xad0, 0xb50, 0xbd0
};
static inline void _render_character(struct a2p_t *a2p, int row, int column, int *offsets) {
uint8_t c = a2p->screen_txt_data[(offsets[row] + column) - 0x0400];
if (chr->characters[c] != NULL) {
SDL_Rect dst;
dst.x = column * 21;
dst.y = row * 24;
dst.w = 21;
dst.h = 24;
SDL_RenderCopy(renderer, chr->characters[c], NULL, &dst);
}
}
static SDL_Color lores_colors[16] = {
{ 0, 0, 0, 0 }, // 0 Black
{ 255, 0, 255, 0 }, // 1 Magenta
{ 0, 0, 204, 0 }, // 2 Dark Blue
{ 128, 0, 128, 0 }, // 3 Purple
{ 0, 100, 0, 0 }, // 4 Dark Green
{ 128, 128, 128, 0 }, // 5 Grey 1
{ 0, 0, 205, 0 }, // 6 Medium Blue
{ 173, 216, 230, 0 }, // 7 Light Blue
{ 165, 42, 42, 0 }, // 8 Brown
{ 255, 165, 0, 0 }, // 9 Orange
{ 211, 211, 211, 0 }, // 10 Grey 2
{ 255, 192, 203, 0 }, // 11 Pink
{ 144, 238, 144, 0 }, // 12 Light Green
{ 255, 255, 0, 0 }, // 13 Yellow
{ 127, 255, 212, 0 }, // 14 Aquamarine
{ 255, 255, 255, 0 }, // 15 White
};
static inline void _render_lores_block(struct a2p_t *a2p, int row, int column, int *offsets) {
uint8_t block = a2p->screen_txt_data[(offsets[row] + column) - 0x0400];
if (block != 0) {
SDL_Rect dst;
dst.x = column * 21;
dst.y = row * 24;
dst.w = 21;
dst.h = 12;
uint c = block & 0x0f;
if (c != 0) {
SDL_SetRenderDrawColor(renderer, lores_colors[c].r, lores_colors[c].g, lores_colors[c].b, lores_colors[c].a);
SDL_RenderFillRect(renderer, &dst);
}
c = (block & 0xf0) >> 4;
if (c != 0) {
dst.y += 12;
SDL_SetRenderDrawColor(renderer, lores_colors[c].r, lores_colors[c].g, lores_colors[c].b, lores_colors[c].a);
SDL_RenderFillRect(renderer, &dst);
}
}
}
static void _render_txt_screen1(struct a2p_t *a2p) {
for (int row = 0; row < 24; row++) {
for (int column = 0; column < 40; column++) {
_render_character(a2p, row, column, screen1_offsets);
}
}
}
static void _render_txt_screen2(struct a2p_t *a2p) {
for (int row = 0; row < 24; row++) {
for (int column = 0; column < 40; column++) {
_render_character(a2p, row, column, screen2_offsets);
}
}
}
static void _render_lgr_screen1(struct a2p_t *a2p, bool mixed) {
// Render graphics
int rows = mixed ? 20 : 24;
for (int row = 0; row < rows; row++) {
for (int column = 0; column < 40; column++) {
_render_lores_block(a2p, row, column, screen1_offsets);
}
}
// Render bottom 4 lines
if (mixed) {
for (int row = 20; row < 24; row++) {
for (int column = 0; column < 40; column++) {
_render_character(a2p, row, column, screen1_offsets);
}
}
}
}
static void _render_lgr_screen2(struct a2p_t *a2p, bool mixed) {
// Render graphics
int rows = mixed ? 20 : 24;
for (int row = 0; row < rows; row++) {
for (int column = 0; column < 40; column++) {
_render_lores_block(a2p, row, column, screen2_offsets);
}
}
// Render bottom 4 lines
if (mixed) {
for (int row = 20; row < 24; row++) {
for (int column = 0; column < 40; column++) {
_render_character(a2p, row, column, screen2_offsets);
}
}
}
}
void scr_main(struct cpu_t *cpu, struct a2p_t *a2p) {
bool quit = false;
//bool running = true;
@ -166,53 +273,43 @@ void scr_main(struct cpu_t *cpu, struct a2p_t *a2p) {
// Render
if (a2p->screen1_dirty || a2p->screen2_dirty) {
if (a2p->screen_dirty) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
switch (a2p->current_screen) {
case 0:
if (a2p->screen1_dirty) {
for (int row = 0; row < 24; row++) {
uint16_t row_offset = screen1_offsets[row] - 0x0400;
for (int column = 0; column < 40; column++) {
uint8_t c = a2p->screen1_data[row_offset + column];
if (chr->characters[c] != NULL) {
SDL_Rect dst;
dst.x = column * 21;
dst.y = row * 24;
dst.w = 21;
dst.h = 24;
SDL_RenderCopy(renderer, chr->characters[c], NULL, &dst);
}
}
}
switch (a2p->screen_mode) {
case EWM_A2P_SCREEN_MODE_TEXT:
switch (a2p->screen_page) {
case EWM_A2P_SCREEN_PAGE1:
_render_txt_screen1(a2p);
break;
case EWM_A2P_SCREEN_PAGE2:
_render_txt_screen2(a2p);
break;
}
break;
case 1:
if (a2p->screen2_dirty) {
for (int row = 0; row < 24; row++) {
uint16_t row_offset = screen2_offsets[row] - 0x0800;
for (int column = 0; column < 40; column++) {
uint8_t c = a2p->screen2_data[row_offset + column];
if (chr->characters[c] != NULL) {
SDL_Rect dst;
dst.x = column * 21;
dst.y = row * 24;
dst.w = 21;
dst.h = 24;
SDL_RenderCopy(renderer, chr->characters[c], NULL, &dst);
}
case EWM_A2P_SCREEN_MODE_GRAPHICS:
switch (a2p->screen_graphics_mode) {
case EWM_A2P_SCREEN_GRAPHICS_MODE_LGR:
switch (a2p->screen_page) {
case EWM_A2P_SCREEN_PAGE1:
_render_lgr_screen1(a2p, a2p->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED);
break;
case EWM_A2P_SCREEN_PAGE2:
_render_lgr_screen2(a2p, a2p->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED);
break;
}
}
break;
case EWM_A2P_SCREEN_GRAPHICS_MODE_HGR:
// TODO Implement
break;
}
break;
}
a2p->screen1_dirty = false;
a2p->screen2_dirty = false;
SDL_RenderPresent(renderer);
a2p->screen_dirty = false;
}
}