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 "dsk.h"
#include "a2p.h" #include "a2p.h"
#define EWM_A2P_SS_KBD 0xc000 #define EWM_A2P_SS_KBD 0xc000
#define EWM_A2P_SS_KBDSTRB 0xc010 #define EWM_A2P_SS_KBDSTRB 0xc010
#define EWM_A2P_SS_SPKR 0xc030 #define EWM_A2P_SS_SPKR 0xc030
#define EWM_A2P_SS_TXTCLR 0xc050
#define EWM_A2P_SS_TXTSET 0xc051 #define EWM_A2P_SS_SCREEN_MODE_GRAPHICS 0xc050
#define EWM_A2P_SS_LOSCR 0xc054 #define EWM_A2P_SS_SCREEN_MODE_TEXT 0xc051
#define EWM_A2P_SS_HISCR 0xc055 #define EWM_A2P_SS_GRAPHICS_STYLE_FULL 0xc052
#define EWM_A2P_SS_LORES 0xc056 #define EWM_A2P_SS_GRAPHICS_STYLE_MIXED 0xc053
#define EWM_A2P_SS_HIRES 0xc057 #define EWM_A2P_SS_SCREEN_PAGE1 0xc054
#define EWM_A2P_SS_SETAN0 0xc058 #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_CLRAN0 0xc059
#define EWM_A2P_SS_SETAN1 0xc05a #define EWM_A2P_SS_SETAN1 0xc05a
#define EWM_A2P_SS_CLRAN1 0xc05b #define EWM_A2P_SS_CLRAN1 0xc05b
#define EWM_A2P_SS_SETAN2 0xc05c #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_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) { 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; 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; a2p->key &= 0x7f;
return 0x00; return 0x00;
case EWM_A2P_SS_LOSCR: case EWM_A2P_SS_SCREEN_MODE_GRAPHICS:
a2p->current_screen = 0; a2p->screen_mode = EWM_A2P_SCREEN_MODE_GRAPHICS;
a2p->screen1_dirty = true; a2p->screen_dirty = true;
a2p->screen2_dirty = false; break;
case EWM_A2P_SS_SCREEN_MODE_TEXT:
a2p->screen_mode = EWM_A2P_SCREEN_MODE_TEXT;
a2p->screen_dirty = true;
break; break;
case EWM_A2P_SS_HISCR: case EWM_A2P_SS_GRAPHICS_MODE_LGR:
a2p->current_screen = 1; a2p->screen_graphics_mode = EWM_A2P_SCREEN_GRAPHICS_MODE_LGR;
a2p->screen1_dirty = false; a2p->screen_dirty = true;
a2p->screen2_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; break;
} }
return 0; 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: case EWM_A2P_SS_KBDSTRB:
a2p->key &= 0x7f; a2p->key &= 0x7f;
break; 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; 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; struct a2p_t *a2p = (struct a2p_t*) mem->obj;
a2p->screen1_data[addr - mem->start] = b; a2p->screen_txt_data[addr - mem->start] = b;
a2p->screen1_dirty = true; a2p->screen_dirty = true;
} //printf("[A2P] $%.4X = $%.2X\n", addr, b);
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;
} }
void a2p_init(struct a2p_t *a2p, struct cpu_t *cpu) { 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->ram = cpu_add_ram(cpu, 0x0000, 48 * 1024);
a2p->rom = cpu_add_rom_file(cpu, 0xd000, "roms/a2p.rom"); 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->dsk = ewm_dsk_create(cpu);
a2p->screen1_data = malloc(1 * 1024); // TODO Introduce ewm_scr_t that captures everything related to the apple 2 screen so that it can be re-used.
a2p->screen1 = cpu_add_iom(cpu, 0x0400, 0x07ff, a2p, a2p_screen1_read, a2p_screen1_write);
a2p->screen2_data = malloc(1 * 1024); a2p->screen_txt_data = malloc(2 * 1024);
a2p->screen2 = cpu_add_iom(cpu, 0x0800, 0x0bff, a2p, a2p_screen2_read, a2p_screen2_write); 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) { 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 mem_t;
struct ewm_dsk_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 a2p_t {
struct mem_t *ram; struct mem_t *ram;
struct mem_t *rom; struct mem_t *rom;
struct mem_t *iom; struct mem_t *iom;
struct ewm_dsk_t *dsk; struct ewm_dsk_t *dsk;
struct mem_t *screen1; uint8_t *screen_txt_data;
uint8_t *screen1_data; struct mem_t *screen_txt_iom;
bool screen1_dirty;
struct mem_t *screen2; int screen_mode;
uint8_t *screen2_data; int screen_graphics_mode;
bool screen2_dirty; int screen_graphics_style;
int screen_page;
int current_screen; int screen_dirty;
uint8_t key; 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) { if (window == NULL) {
fprintf(stderr, "Failed create window: %s\n", SDL_GetError()); fprintf(stderr, "Failed create window: %s\n", SDL_GetError());
exit(1); exit(1);
@ -75,6 +75,113 @@ static int screen2_offsets[24] = {
0xa28, 0xaa8, 0xb28, 0xba8, 0x850, 0x8d0, 0x950, 0x9d0, 0xa50, 0xad0, 0xb50, 0xbd0 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) { void scr_main(struct cpu_t *cpu, struct a2p_t *a2p) {
bool quit = false; bool quit = false;
//bool running = true; //bool running = true;
@ -166,53 +273,43 @@ void scr_main(struct cpu_t *cpu, struct a2p_t *a2p) {
// Render // Render
if (a2p->screen1_dirty || a2p->screen2_dirty) { if (a2p->screen_dirty) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
switch (a2p->current_screen) { switch (a2p->screen_mode) {
case 0: case EWM_A2P_SCREEN_MODE_TEXT:
if (a2p->screen1_dirty) { switch (a2p->screen_page) {
for (int row = 0; row < 24; row++) { case EWM_A2P_SCREEN_PAGE1:
uint16_t row_offset = screen1_offsets[row] - 0x0400; _render_txt_screen1(a2p);
for (int column = 0; column < 40; column++) { break;
uint8_t c = a2p->screen1_data[row_offset + column]; case EWM_A2P_SCREEN_PAGE2:
if (chr->characters[c] != NULL) { _render_txt_screen2(a2p);
SDL_Rect dst; break;
dst.x = column * 21;
dst.y = row * 24;
dst.w = 21;
dst.h = 24;
SDL_RenderCopy(renderer, chr->characters[c], NULL, &dst);
}
}
}
} }
break; break;
case 1: case EWM_A2P_SCREEN_MODE_GRAPHICS:
if (a2p->screen2_dirty) { switch (a2p->screen_graphics_mode) {
for (int row = 0; row < 24; row++) { case EWM_A2P_SCREEN_GRAPHICS_MODE_LGR:
uint16_t row_offset = screen2_offsets[row] - 0x0800; switch (a2p->screen_page) {
for (int column = 0; column < 40; column++) { case EWM_A2P_SCREEN_PAGE1:
uint8_t c = a2p->screen2_data[row_offset + column]; _render_lgr_screen1(a2p, a2p->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED);
if (chr->characters[c] != NULL) { break;
SDL_Rect dst; case EWM_A2P_SCREEN_PAGE2:
dst.x = column * 21; _render_lgr_screen2(a2p, a2p->screen_graphics_style == EWM_A2P_SCREEN_GRAPHICS_STYLE_MIXED);
dst.y = row * 24; break;
dst.w = 21;
dst.h = 24;
SDL_RenderCopy(renderer, chr->characters[c], NULL, &dst);
}
} }
} break;
case EWM_A2P_SCREEN_GRAPHICS_MODE_HGR:
// TODO Implement
break;
} }
break; break;
} }
a2p->screen1_dirty = false;
a2p->screen2_dirty = false;
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
a2p->screen_dirty = false;
} }
} }