2023-10-25 07:50:14 +00:00
|
|
|
/*
|
|
|
|
* mii_video.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include "mii.h"
|
|
|
|
#include "mii_bank.h"
|
|
|
|
#include "mii_rom_iiee_video.h"
|
|
|
|
#include "mii_sw.h"
|
|
|
|
#include "minipt.h"
|
|
|
|
|
|
|
|
#define VIDEO_RESOLUTION_X 280
|
|
|
|
#define VIDEO_RESOLUTION_Y 192
|
|
|
|
|
|
|
|
#define VIDEO_BYTES_PER_LINE 40
|
|
|
|
#define SCREEN_LINE_OFFSET 0x80
|
|
|
|
#define VIDEO_SEGMENT_OFFSET 0x28
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
// https://rich12345.tripod.com/aiivideo/vbl.html
|
|
|
|
MII_VBL_DOWN_CYCLES = 12480,
|
|
|
|
MII_VBL_UP_CYCLES = 4550,
|
|
|
|
MII_VIDEO_H_CYCLES = 40,
|
|
|
|
MII_VIDEO_HB_CYCLES = 25,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Colors were lifted from
|
|
|
|
* https://comp.sys.apple2.narkive.com/lTSrj2ZI/apple-ii-colour-rgb
|
2024-01-20 12:05:26 +00:00
|
|
|
* and
|
|
|
|
* https://www.mrob.com/pub/xapple2/colors.html
|
2023-10-25 07:50:14 +00:00
|
|
|
*/
|
2024-01-23 10:12:42 +00:00
|
|
|
typedef struct mii_color_t {
|
|
|
|
uint32_t rgb;
|
|
|
|
uint8_t l;
|
|
|
|
} mii_color_t;
|
2023-10-25 07:50:14 +00:00
|
|
|
|
2024-01-23 10:12:42 +00:00
|
|
|
#define HI_LUMA(r,g,b) \
|
|
|
|
((uint8_t)(0.2126 * (r) + 0.7152 * (g) + 0.0722 * (b)))
|
|
|
|
|
|
|
|
#define HI_RGB(r,g,b) { \
|
|
|
|
.rgb = (0xff000000 | ((b) << 16) | ((g) << 8) | (r)), \
|
|
|
|
.l = HI_LUMA(r,g,b) \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this 'dims' the colors for every second line of pixels
|
|
|
|
* This is a very very cheap filter but it works really well!
|
|
|
|
*/
|
|
|
|
#define C_SCANLINE_MASK 0xffc0c0c0
|
|
|
|
|
|
|
|
/* These are the 'official' RGB colors for apple II,
|
|
|
|
* Well not really, it is just ONE interpreation of many, we could possibly
|
|
|
|
* make some sort of color lookup table to allow switching them on the fly?
|
|
|
|
*/
|
|
|
|
#define C_BLACK HI_RGB(0x00, 0x00, 0x00) // black
|
|
|
|
#define C_PURPLE HI_RGB(0xff, 0x44, 0xfd) // purple
|
|
|
|
#define C_GREEN HI_RGB(0x14, 0xf5, 0x3c) // green
|
|
|
|
#define C_BLUE HI_RGB(0x14, 0xcf, 0xfd) // blue
|
|
|
|
#define C_ORANGE HI_RGB(0xff, 0x6a, 0x3c) // orange
|
|
|
|
#define C_WHITE HI_RGB(0xff, 0xff, 0xff) // white
|
|
|
|
#define C_MAGENTA HI_RGB(0xe3, 0x1e, 0x60) // magenta
|
|
|
|
#define C_DARKBLUE HI_RGB(0x60, 0x4e, 0xbd) // dark blue
|
|
|
|
#define C_DARKGREEN HI_RGB(0x00, 0xa3, 0x60) // dark green
|
|
|
|
#define C_GRAY1 HI_RGB(0x9c, 0x9c, 0x9c) // gray 1
|
|
|
|
#define C_GRAY2 HI_RGB(0x9c, 0x9c, 0x9c) // gray 2
|
|
|
|
#define C_LIGHTBLUE HI_RGB(0xd0, 0xc3, 0xff) // light blue
|
|
|
|
#define C_BROWN HI_RGB(0x60, 0x72, 0x03) // brown
|
|
|
|
#define C_PINK HI_RGB(0xff, 0xa0, 0xd0) // pink
|
|
|
|
#define C_YELLOW HI_RGB(0xd0, 0xdd, 0x8d) // yellow
|
|
|
|
#define C_AQUA HI_RGB(0x72, 0xff, 0xd0) // aqua
|
|
|
|
|
|
|
|
// this is not an official color, just 'my' interpretation of an amber screen
|
|
|
|
#define C_AMBER HI_RGB(0xfd, 0xcf, 0x14) // amber
|
|
|
|
|
|
|
|
static const mii_color_t lores_colors[2][16] = { {
|
|
|
|
[0x0] = C_BLACK, [0x1] = C_MAGENTA, [0x2] = C_DARKBLUE, [0x3] = C_PURPLE,
|
|
|
|
[0x4] = C_DARKGREEN,[0x5] = C_GRAY1, [0x6] = C_BLUE, [0x7] = C_LIGHTBLUE,
|
|
|
|
[0x8] = C_BROWN, [0x9] = C_ORANGE, [0xa] = C_GRAY2, [0xb] = C_PINK,
|
|
|
|
[0xc] = C_GREEN, [0xd] = C_YELLOW, [0xe] = C_AQUA, [0xf] = C_WHITE,
|
|
|
|
},{
|
|
|
|
[0x0] = C_BLACK, [0x1] = C_DARKBLUE, [0x2] = C_DARKGREEN,[0x3] = C_BLUE,
|
|
|
|
[0x4] = C_BROWN, [0x5] = C_GRAY2, [0x6] = C_GREEN, [0x7] = C_AQUA,
|
|
|
|
[0x8] = C_MAGENTA, [0x9] = C_PURPLE, [0xa] = C_GRAY1, [0xb] = C_LIGHTBLUE,
|
|
|
|
[0xc] = C_ORANGE, [0xd] = C_PINK, [0xe] = C_YELLOW, [0xf] = C_WHITE,
|
|
|
|
} };
|
|
|
|
static const mii_color_t dhires_colors[] = {
|
|
|
|
[0x0] = C_BLACK, [0x1] = C_MAGENTA, [0x2] = C_BROWN, [0x3] = C_ORANGE,
|
|
|
|
[0x4] = C_DARKGREEN,[0x5] = C_GRAY1, [0x6] = C_GREEN, [0x7] = C_YELLOW,
|
|
|
|
[0x8] = C_DARKBLUE, [0x9] = C_PURPLE, [0xa] = C_GRAY2, [0xb] = C_PINK,
|
|
|
|
[0xc] = C_BLUE, [0xd] = C_LIGHTBLUE,[0xe] = C_AQUA, [0xf] = C_WHITE,
|
2023-10-25 07:50:14 +00:00
|
|
|
};
|
|
|
|
|
2024-01-23 10:12:42 +00:00
|
|
|
static const mii_color_t hires_colors[] = {
|
2023-10-25 07:50:14 +00:00
|
|
|
C_BLACK,
|
|
|
|
C_PURPLE,
|
|
|
|
C_GREEN,
|
|
|
|
C_GREEN,
|
|
|
|
C_PURPLE,
|
2023-10-25 17:57:42 +00:00
|
|
|
C_BLUE,
|
|
|
|
C_ORANGE,
|
2024-01-23 10:12:42 +00:00
|
|
|
C_ORANGE,
|
|
|
|
C_BLUE,
|
2023-10-25 07:50:14 +00:00
|
|
|
C_WHITE,
|
|
|
|
};
|
|
|
|
|
2024-01-23 10:12:42 +00:00
|
|
|
static const mii_color_t mono[3][2] = {
|
|
|
|
{ C_BLACK, C_WHITE },
|
|
|
|
{ C_BLACK, C_GREEN },
|
|
|
|
{ C_BLACK, C_AMBER },
|
2023-10-25 07:50:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2024-01-23 10:12:42 +00:00
|
|
|
// TODO redo the hires decoder by reversing bits line by line...
|
|
|
|
static inline uint8_t reverse8(uint8_t b) {
|
|
|
|
b = (b & 0b11110000) >> 4 | (b & 0b00001111) << 4;
|
|
|
|
b = (b & 0b11001100) >> 2 | (b & 0b00110011) << 2;
|
|
|
|
b = (b & 0b10101010) >> 1 | (b & 0b01010101) << 1;
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
static inline uint8_t reverse4(uint8_t b) {
|
|
|
|
b = (b & 0b0001) << 3 | (b & 0b0010) << 1 |
|
|
|
|
(b & 0b0100) >> 1 | (b & 0b1000) >> 3;
|
|
|
|
return b;
|
|
|
|
}
|
2023-10-25 07:50:14 +00:00
|
|
|
|
|
|
|
static inline uint16_t
|
|
|
|
_mii_line_to_video_addr(
|
|
|
|
uint16_t addr,
|
|
|
|
uint8_t line)
|
|
|
|
{
|
|
|
|
addr += ((line & 0x07) << 10) |
|
|
|
|
(((line >> 3) & 7) << 7) |
|
|
|
|
((line >> 6) << 5) | ((line >> 6) << 3);
|
|
|
|
return addr;
|
|
|
|
}
|
2024-01-20 12:05:26 +00:00
|
|
|
|
2023-10-25 07:50:14 +00:00
|
|
|
/*
|
|
|
|
* This is the state of the video output
|
|
|
|
* All timings lifted from https://rich12345.tripod.com/aiivideo/vbl.html
|
|
|
|
*
|
|
|
|
* This is a 'protothread' basically cooperative scheduling using an
|
|
|
|
* old compiler trick. It's not a real thread, but it's a way to
|
|
|
|
* write code that looks like a thread, and is easy to read.
|
|
|
|
* The 'pt_start' macro starts the thread, and pt_yield() yields
|
|
|
|
* the thread to the main loop.
|
|
|
|
* The pt_end() macro ends the thread.
|
|
|
|
* Remeber you cannot have locals in the thread, they must be
|
|
|
|
* static or global.
|
|
|
|
* *everything* before the pt_start call is ran every time, so you can use
|
|
|
|
* that to reload some sort of state, as here, were we reload all the
|
2024-01-20 12:05:26 +00:00
|
|
|
* video mode softswitches.
|
|
|
|
*
|
|
|
|
* This function is also a 'cycle timer' it returns the number of 6502
|
|
|
|
* cycles to wait until being called again, so it mostly returns the
|
|
|
|
* number of cycles until the next horizontal blanking between each lines,
|
|
|
|
* but also the number of cycles until the next vertical blanking once
|
|
|
|
* the last line is drawn.
|
2023-10-25 07:50:14 +00:00
|
|
|
*/
|
2024-01-20 12:05:26 +00:00
|
|
|
static uint64_t
|
|
|
|
mii_video_timer_cb(
|
|
|
|
mii_t *mii,
|
|
|
|
void *param)
|
2023-10-25 07:50:14 +00:00
|
|
|
{
|
2024-01-20 12:05:26 +00:00
|
|
|
uint64_t res = MII_VIDEO_H_CYCLES * mii->speed;
|
2023-10-25 07:50:14 +00:00
|
|
|
mii_bank_t * main = &mii->bank[MII_BANK_MAIN];
|
2023-10-28 05:23:02 +00:00
|
|
|
bool text = SW_GETSTATE(mii, SWTEXT);
|
|
|
|
bool page2 = SW_GETSTATE(mii, SWPAGE2);
|
|
|
|
bool col80 = SW_GETSTATE(mii, SW80COL);
|
|
|
|
bool store80 = SW_GETSTATE(mii, SW80STORE);
|
|
|
|
bool mixed = SW_GETSTATE(mii, SWMIXED);
|
|
|
|
bool hires = SW_GETSTATE(mii, SWHIRES);
|
|
|
|
bool dhires = SW_GETSTATE(mii, SWDHIRES);
|
2023-10-25 07:50:14 +00:00
|
|
|
|
|
|
|
pt_start(mii->video.state);
|
|
|
|
/*
|
|
|
|
We cheat and draw a whole line at a time, then 'wait' until
|
|
|
|
horizontal blanking, then wait until vertical blanking.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
// 'clear' VBL flag. Flag is 0 during retrace
|
|
|
|
mii_bank_poke(main, SWVBL, 0x80);
|
|
|
|
if (mixed && !text) {
|
|
|
|
text = mii->video.line >= 192 - (4 * 8);
|
|
|
|
}
|
2024-01-23 10:12:42 +00:00
|
|
|
const int mode = mii->video.color_mode;
|
2023-10-25 07:50:14 +00:00
|
|
|
// http://www.1000bit.it/support/manuali/apple/technotes/aiie/tn.aiie.03.html
|
|
|
|
if (hires && !text && col80 && dhires) {
|
|
|
|
if (store80)
|
|
|
|
page2 = 0;
|
|
|
|
uint8_t reg = mii_bank_peek(main, SWAN3_REGISTER);
|
|
|
|
uint16_t a = (0x2000 + (0x2000 * page2));
|
|
|
|
a = _mii_line_to_video_addr(a, mii->video.line);
|
|
|
|
uint32_t * screen = mii->video.pixels +
|
|
|
|
(mii->video.line * MII_VRAM_WIDTH * 2);
|
|
|
|
uint32_t * l2 = screen + MII_VRAM_WIDTH;
|
|
|
|
|
|
|
|
mii_bank_t * aux = &mii->bank[MII_BANK_AUX];
|
|
|
|
|
2024-01-23 10:12:42 +00:00
|
|
|
if (reg == 0 || mode != MII_VIDEO_COLOR) {
|
2024-01-20 12:05:26 +00:00
|
|
|
const uint32_t clut[2] = {
|
2024-01-23 10:12:42 +00:00
|
|
|
mono[mode][0].rgb,
|
|
|
|
mono[mode][1].rgb };
|
2024-01-20 12:05:26 +00:00
|
|
|
for (int x = 0; x < 40; x++) {
|
2023-10-25 07:50:14 +00:00
|
|
|
uint32_t ext = (mii_bank_peek(aux, a + x) & 0x7f) |
|
|
|
|
((mii_bank_peek(main, a + x) & 0x7f) << 7);
|
|
|
|
for (int bi = 0; bi < 14; bi++) {
|
|
|
|
uint8_t pixel = (ext >> bi) & 1;
|
2024-01-20 12:05:26 +00:00
|
|
|
uint32_t col = clut[pixel];
|
2023-10-25 07:50:14 +00:00
|
|
|
*screen++ = col;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // color mode
|
|
|
|
int x = 0, dx = 0;
|
|
|
|
do {
|
|
|
|
uint64_t run = 0;
|
|
|
|
// get 8 bytes, so we get 8*7=56 bits for 14 pixels
|
|
|
|
for (int bx = 0; bx < 8 && x < 80; bx++, x++) {
|
|
|
|
uint8_t b = mii_bank_peek(
|
|
|
|
x & 1 ? main : aux, a + (x / 2));
|
|
|
|
run |= ((uint64_t)(b & 0x7f) << (bx * 7));
|
|
|
|
}
|
|
|
|
for (int px = 0; px < 14 && dx < 80*2; px++, dx++) {
|
2024-01-23 10:12:42 +00:00
|
|
|
uint8_t pixel = reverse4(run & 0xf);
|
2023-10-25 07:50:14 +00:00
|
|
|
run >>= 4;
|
2024-01-23 10:12:42 +00:00
|
|
|
uint32_t col = dhires_colors[pixel].rgb;
|
2023-10-25 07:50:14 +00:00
|
|
|
*screen++ = col;
|
|
|
|
*screen++ = col;
|
|
|
|
*screen++ = col;
|
|
|
|
*screen++ = col;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
}
|
|
|
|
} while (x < 80);
|
|
|
|
}
|
|
|
|
} else if (hires && !text) {
|
|
|
|
if (store80)
|
|
|
|
page2 = 0;
|
|
|
|
uint16_t a = (0x2000 + (0x2000 * page2));
|
|
|
|
a = _mii_line_to_video_addr(a, mii->video.line);
|
|
|
|
uint32_t * screen = mii->video.pixels +
|
|
|
|
(mii->video.line * MII_VRAM_WIDTH * 2);
|
|
|
|
uint32_t * l2 = screen + MII_VRAM_WIDTH;
|
|
|
|
|
|
|
|
uint8_t b0 = 0;
|
|
|
|
uint8_t b1 = mii_bank_peek(main, a + 0);
|
|
|
|
for (int x = 0; x < 40; x++) {
|
|
|
|
uint8_t b2 = mii_bank_peek(main, a + x + 1);
|
|
|
|
// last 2 pixels, current 7 pixels, next 2 pixels
|
|
|
|
uint16_t run = ((b0 & 0x60) >> ( 5 )) |
|
|
|
|
((b1 & 0x7f) << ( 2 )) |
|
|
|
|
((b2 & 0x03) << ( 9 ));
|
2024-01-20 12:05:26 +00:00
|
|
|
int odd = (x & 1) << 1;
|
|
|
|
int offset = (b1 & 0x80) >> 5;
|
2024-01-23 10:12:42 +00:00
|
|
|
if (mode == MII_VIDEO_COLOR) {
|
2023-10-25 07:50:14 +00:00
|
|
|
for (int i = 0; i < 7; i++) {
|
|
|
|
uint8_t left = (run >> (1 + i)) & 1;
|
|
|
|
uint8_t pixel = (run >> (2 + i)) & 1;
|
|
|
|
uint8_t right = (run >> (3 + i)) & 1;
|
|
|
|
|
|
|
|
int idx = 0; // black
|
|
|
|
if (pixel) {
|
|
|
|
if (left || right) {
|
|
|
|
idx = 9; // white
|
|
|
|
} else {
|
|
|
|
idx = offset + odd + (i & 1) + 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (left && right) {
|
|
|
|
idx = offset + odd + 1 - (i & 1) + 1;
|
|
|
|
}
|
|
|
|
}
|
2024-01-23 10:12:42 +00:00
|
|
|
uint32_t col = hires_colors[idx].rgb;
|
2023-10-25 07:50:14 +00:00
|
|
|
*screen++ = col;
|
|
|
|
*screen++ = col;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
|
|
uint8_t pixel = (run >> (2 + i)) & 1;
|
2024-01-23 10:12:42 +00:00
|
|
|
uint32_t col = mono[mode][pixel].rgb;
|
2023-10-25 07:50:14 +00:00
|
|
|
*screen++ = col;
|
|
|
|
*screen++ = col;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b0 = b1;
|
|
|
|
b1 = b2;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (store80)
|
|
|
|
page2 = 0;
|
|
|
|
uint16_t a = (0x400 + (0x400 * page2));
|
|
|
|
int i = mii->video.line >> 3;
|
|
|
|
a += ((i & 0x07) << 7) | ((i >> 3) << 5) | ((i >> 3) << 3);
|
|
|
|
|
|
|
|
mii_bank_t * aux = &mii->bank[MII_BANK_AUX];
|
|
|
|
uint32_t * screen = mii->video.pixels +
|
|
|
|
(mii->video.line * MII_VRAM_WIDTH * 2);
|
|
|
|
uint32_t * l2 = screen + MII_VRAM_WIDTH;
|
|
|
|
for (int x = 0; x < 40 + (40 * col80); x++) {
|
|
|
|
uint8_t c = 0;
|
|
|
|
if (col80)
|
|
|
|
c = mii_bank_peek(
|
|
|
|
x & 1 ? main : aux, a + (x >> 1));
|
|
|
|
else
|
|
|
|
c = mii_bank_peek(main, a + x);
|
|
|
|
if (text) {
|
|
|
|
const uint8_t * rom = iie_enhanced_video + (c << 3);
|
|
|
|
uint8_t bits = rom[mii->video.line & 0x07];
|
|
|
|
for (int pi = 0; pi < 7; pi++) {
|
|
|
|
uint8_t pixel = (bits >> pi) & 1;
|
2024-01-23 10:12:42 +00:00
|
|
|
uint32_t col = mono[mode][!pixel].rgb;
|
2023-10-25 07:50:14 +00:00
|
|
|
*screen++ = col;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
if (!col80) {
|
|
|
|
*screen++ = col;
|
|
|
|
*l2++ = col & C_SCANLINE_MASK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // lores graphics
|
|
|
|
int lo_line = mii->video.line / 4;
|
|
|
|
c = c >> ((lo_line & 1) * 4);
|
2024-01-23 10:12:42 +00:00
|
|
|
|
|
|
|
if (mode == MII_VIDEO_COLOR) {
|
|
|
|
uint32_t pixel = lores_colors[(x & col80) ^ col80][c & 0x0f].rgb;
|
|
|
|
for (int pi = 0; pi < 7; pi++) {
|
|
|
|
*screen++ = pixel;
|
|
|
|
*l2++ = pixel & C_SCANLINE_MASK;
|
|
|
|
if (!col80) {
|
|
|
|
*screen++ = pixel;
|
|
|
|
*l2++ = pixel & C_SCANLINE_MASK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Not sure at all this should be rendered like this,
|
|
|
|
* but I can't find a reference on how to render low
|
|
|
|
* res graphics in mono */
|
|
|
|
c |= (c << 4);
|
|
|
|
for (int pi = 0; pi < 7; pi++) {
|
|
|
|
uint32_t pixel = mono[mode][c & 1].rgb;
|
|
|
|
c >>= 1;
|
2023-10-25 07:50:14 +00:00
|
|
|
*screen++ = pixel;
|
|
|
|
*l2++ = pixel & C_SCANLINE_MASK;
|
2024-01-23 10:12:42 +00:00
|
|
|
if (!col80) {
|
|
|
|
*screen++ = pixel;
|
|
|
|
*l2++ = pixel & C_SCANLINE_MASK;
|
|
|
|
}
|
2023-10-25 07:50:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mii->video.line++;
|
|
|
|
if (mii->video.line == 192) {
|
|
|
|
mii->video.line = 0;
|
2024-01-20 12:05:26 +00:00
|
|
|
res = MII_VIDEO_H_CYCLES * mii->speed;
|
2023-10-25 07:50:14 +00:00
|
|
|
pt_yield(mii->video.state);
|
|
|
|
mii_bank_poke(main, SWVBL, 0x00);
|
2024-01-20 12:05:26 +00:00
|
|
|
res = MII_VBL_UP_CYCLES * mii->speed;
|
2023-10-25 07:50:14 +00:00
|
|
|
mii->video.frame_count++;
|
|
|
|
pt_yield(mii->video.state);
|
|
|
|
} else {
|
2024-01-20 12:05:26 +00:00
|
|
|
res = (MII_VIDEO_H_CYCLES + MII_VIDEO_HB_CYCLES) *
|
2023-10-25 07:50:14 +00:00
|
|
|
mii->speed;
|
|
|
|
pt_yield(mii->video.state);
|
|
|
|
}
|
|
|
|
} while (1);
|
|
|
|
pt_end(mii->video.state);
|
2024-01-20 12:05:26 +00:00
|
|
|
return res;
|
2023-10-25 07:50:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
mii_access_video(
|
|
|
|
mii_t *mii,
|
|
|
|
uint16_t addr,
|
|
|
|
uint8_t * byte,
|
|
|
|
bool write)
|
|
|
|
{
|
|
|
|
bool res = false;
|
|
|
|
mii_bank_t * main = &mii->bank[MII_BANK_MAIN];
|
|
|
|
switch (addr) {
|
|
|
|
case SWALTCHARSETOFF:
|
|
|
|
case SWALTCHARSETON:
|
|
|
|
if (!write) break;
|
|
|
|
res = true;
|
2023-10-28 05:23:02 +00:00
|
|
|
SW_SETSTATE(mii, SWALTCHARSET, addr & 1);
|
2023-10-25 07:50:14 +00:00
|
|
|
mii_bank_poke(main, SWALTCHARSET, (addr & 1) << 7);
|
|
|
|
break;
|
|
|
|
case SWVBL:
|
|
|
|
case SW80COL:
|
|
|
|
case SWTEXT:
|
|
|
|
case SWMIXED:
|
|
|
|
case SWPAGE2:
|
|
|
|
case SWHIRES:
|
|
|
|
case SWALTCHARSET:
|
|
|
|
case SWRDDHIRES:
|
|
|
|
res = true;
|
|
|
|
if (!write)
|
|
|
|
*byte = mii_bank_peek(main, addr);
|
|
|
|
break;
|
|
|
|
case SW80COLOFF:
|
|
|
|
case SW80COLON:
|
|
|
|
if (!write) break;
|
|
|
|
res = true;
|
2023-10-28 05:23:02 +00:00
|
|
|
SW_SETSTATE(mii, SW80COL, addr & 1);
|
2023-10-25 07:50:14 +00:00
|
|
|
mii_bank_poke(main, SW80COL, (addr & 1) << 7);
|
|
|
|
break;
|
|
|
|
case SWDHIRESOFF: // 0xc05f,
|
|
|
|
case SWDHIRESON: { // = 0xc05e,
|
|
|
|
res = true;
|
|
|
|
uint8_t an3 = !!mii_bank_peek(main, SWAN3);
|
|
|
|
bool an3_on = !!(addr & 1); // 5f is ON, 5e is OFF
|
|
|
|
uint8_t reg = mii_bank_peek(main, SWAN3_REGISTER);
|
|
|
|
if (an3_on && !an3) {
|
|
|
|
uint8_t bit = !!mii_bank_peek(main, SW80COL);
|
|
|
|
reg = ((reg << 1) | bit) & 3;
|
2023-10-28 05:23:02 +00:00
|
|
|
// printf("VIDEO 80:%d REG now %x\n", bit, reg);
|
2023-10-25 07:50:14 +00:00
|
|
|
mii_bank_poke(main, SWAN3_REGISTER, reg);
|
|
|
|
}
|
|
|
|
mii_bank_poke(main, SWAN3, an3_on);
|
2023-10-28 05:23:02 +00:00
|
|
|
// printf("DHRES IS %s mode:%d\n",
|
|
|
|
// (addr & 1) ? "OFF" : "ON", reg);
|
|
|
|
mii->sw_state = (mii->sw_state & ~M_SWDHIRES) |
|
|
|
|
(!(addr & 1) << B_SWDHIRES);
|
|
|
|
SW_SETSTATE(mii, SWDHIRES, !(addr & 1));
|
2023-10-25 07:50:14 +00:00
|
|
|
mii_bank_poke(main, SWRDDHIRES, (!(addr & 1)) << 7);
|
|
|
|
} break;
|
|
|
|
case SWTEXTOFF:
|
|
|
|
case SWTEXTON:
|
|
|
|
res = true;
|
2023-10-28 05:23:02 +00:00
|
|
|
SW_SETSTATE(mii, SWTEXT, addr & 1);
|
2023-10-25 07:50:14 +00:00
|
|
|
mii_bank_poke(main, SWTEXT, (addr & 1) << 7);
|
|
|
|
break;
|
|
|
|
case SWMIXEDOFF:
|
|
|
|
case SWMIXEDON:
|
|
|
|
res = true;
|
2023-10-28 05:23:02 +00:00
|
|
|
SW_SETSTATE(mii, SWMIXED, addr & 1);
|
2023-10-25 07:50:14 +00:00
|
|
|
mii_bank_poke(main, SWMIXED, (addr & 1) << 7);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2024-01-20 12:05:26 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
mii_video_init(
|
|
|
|
mii_t *mii)
|
|
|
|
{
|
|
|
|
mii->video.timer_id = mii_timer_register(mii,
|
|
|
|
mii_video_timer_cb, NULL, MII_VIDEO_H_CYCLES, __func__);
|
2024-01-23 10:12:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
_mii_mish_video(
|
|
|
|
void * param,
|
|
|
|
int argc,
|
|
|
|
const char * argv[])
|
|
|
|
{
|
|
|
|
mii_t * mii = param;
|
|
|
|
|
|
|
|
if (!argv[1] || !strcmp(argv[1], "list")) {
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
|
|
printf("%01x: %08x %08x %08x\n", i,
|
|
|
|
lores_colors[0][i].rgb,
|
|
|
|
lores_colors[1][i].rgb,
|
|
|
|
dhires_colors[i].rgb);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "mish.h"
|
|
|
|
|
|
|
|
MISH_CMD_NAMES(video, "video");
|
|
|
|
MISH_CMD_HELP(video,
|
|
|
|
"video: test patterns generator",
|
|
|
|
" <default>: dump color tables"
|
|
|
|
);
|
|
|
|
MII_MISH(video, _mii_mish_video);
|