diff --git a/a2p.c b/a2p.c index 0af5374..77b4528 100644 --- a/a2p.c +++ b/a2p.c @@ -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) { diff --git a/a2p.h b/a2p.h index 50e663b..434b837 100644 --- a/a2p.h +++ b/a2p.h @@ -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; }; diff --git a/scr.c b/scr.c index 171b594..865a6f6 100644 --- a/scr.c +++ b/scr.c @@ -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; } }