Firmware Release 23-01-16-119

New autodetect routines for Apple II+, IIe (Platinum), IIgs (ROM03).
Timing tweaks to improve compatibility on IIgs and IIe.
Corrected text rendering on all supported machines.
Initial Super HiRes support (not recommended to use at this time)
Monochrome DHGR and HGR support activated with IIgs MONOCOLOR or NEWVID registers.
80 Column mode on IIe and IIgs.
This commit is contained in:
David Kuder 2023-01-16 20:36:00 -05:00
parent c62efee7ee
commit d9be2ed9cd
30 changed files with 1632 additions and 2946 deletions

View File

@ -29,6 +29,8 @@ target_sources(v2-analog-${PICO_BOARD} PUBLIC
common/main.c
diag/businterface.c
diag/diag.c
fs/businterface.c
fs/fs.c
vga/vgamain.c
vga/businterface.c
vga/vgabuf.c
@ -36,9 +38,10 @@ target_sources(v2-analog-${PICO_BOARD} PUBLIC
vga/render_hires.c
vga/render_lores.c
vga/render_text.c
vga/render_terminal.c
vga/render_videx.c
vga/render_dhgr.c
vga/render_dgr.c
vga/render_shr.c
vga/render_test.c
vga/terminal_rom.c
vga/character_rom.c
@ -61,11 +64,16 @@ target_link_libraries(v2-analog-${PICO_BOARD} PUBLIC
pico_multicore
pico_stdlib
littlefs-lib
pico_cyw43_arch_lwip_poll
hardware_dma
hardware_pio
)
if(${PICO_BOARD} MATCHES "pico_w")
target_link_libraries(v2-analog-${PICO_BOARD} PUBLIC
pico_cyw43_arch_lwip_poll
)
endif(${PICO_BOARD} MATCHES "pico_w")
pico_enable_stdio_usb(v2-analog-${PICO_BOARD} 0)
pico_enable_stdio_uart(v2-analog-${PICO_BOARD} 0)

View File

@ -33,9 +33,9 @@ next_bus_cycle:
write_cycle:
; the current time is P0+88ns (P0 + 16ns + 2 clocks (input synchronizers) + 7 instructions)
set PINS, 0b110 [15] ; enable Data tranceiver & wait until both ~DEVSEL and the written data are valid (P0+200ns)
set PINS, 0b110 [14] ; enable Data tranceiver & wait until both ~DEVSEL and the written data are valid (P0+200ns)
in PINS, 10 ; read R/W, ~DEVSEL, and Data[7:0], then autopush
wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall
wait 0 GPIO, PHI0_GPIO [6] ; wait for PHI0 to fall
jmp next_bus_cycle
read_cycle:
@ -45,7 +45,7 @@ read_cycle:
in PINS, 10 ; read R/W, ~DEVSEL, and dontcare[7:0], then autopush
irq set READ_DATA_TRIGGER_IRQ ; trigger the data read state machine to put data on the data bus
wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall
wait 0 GPIO, PHI0_GPIO [6] ; wait for PHI0 to fall
wait 0 irq DATA_BUSY_IRQ ; wait for the data handling state machine to complete to avoid contention w/transceiver control
.wrap

View File

@ -1,3 +1,5 @@
#pragma once
#include <stdint.h>
extern volatile uint8_t config_memory[32];
@ -48,3 +50,47 @@ extern volatile uint8_t *slot7rom;
/* Videx VideoTerm */
extern volatile uint8_t *videx_page;
extern volatile uint32_t soft_switches;
#define SOFTSW_TEXT_MODE 0x00000001
#define SOFTSW_MIX_MODE 0x00000002
#define SOFTSW_HIRES_MODE 0x00000004
#define SOFTSW_MODE_MASK 0x00000007
#define SOFTSW_PAGE_2 0x00000008
// Apple IIe/c/gs softswitches
#define SOFTSW_80STORE 0x00000100
#define SOFTSW_AUX_READ 0x00000200
#define SOFTSW_AUX_WRITE 0x00000400
#define SOFTSW_AUXZP 0x00000800
#define SOFTSW_SLOT3ROM 0x00001000
#define SOFTSW_80COL 0x00002000
#define SOFTSW_ALTCHAR 0x00004000
#define SOFTSW_DGR 0x00008000
#define SOFTSW_NEWVID_MASK 0xE0
#define SOFTSW_NEWVID_SHIFT 11
#define SOFTSW_MONOCHROME 0x00010000
#define SOFTSW_LINEARIZE 0x00020000
#define SOFTSW_SHR 0x00040000
#define SOFTSW_IOUDIS 0x00080000
#define SOFTSW_SHADOW_MASK 0x7F
#define SOFTSW_SHADOW_SHIFT 20
#define SOFTSW_SHADOW_TEXT 0x00100000
#define SOFTSW_SHADOW_HGR1 0x00200000
#define SOFTSW_SHADOW_HGR2 0x00400000
#define SOFTSW_SHADOW_SHR 0x00800000
#define SOFTSW_SHADOW_AUXHGR 0x01000000
#define SOFTSW_SHADOW_ALTDISP 0x02000000
#define SOFTSW_SHADOW_IO 0x04000000
// V2 Analog specific softswitches
#define SOFTSW_VIDEX 0x10000000
#define SOFTSW_TEST 0x20000000
#define SOFTSW_IIE_REGS 0x40000000
#define SOFTSW_IIGS_REGS 0x80000000

View File

@ -8,13 +8,18 @@ v2mode_t v2mode;
usbmux_t usbmux;
serialmux_t serialmux;
wifimode_t wifimode;
compat_t machine;
uint8_t wifi_ssid[32];
uint8_t wifi_psk[32];
extern bool userfont;
void parse_config(uint8_t *buffer) {
if(!memcmp("MODE=", buffer, 5)) {
if(!strcmp("DIAG", buffer+5)) {
v2mode = MODE_DIAG;
} else if(!strcmp("FS", buffer+5)) {
v2mode = MODE_FS;
} else if(!strcmp("VGA", buffer+5)) {
v2mode = MODE_VGACARD;
} else if(!strcmp("Z80", buffer+5)) {
@ -26,6 +31,18 @@ void parse_config(uint8_t *buffer) {
} else if(!strcmp("SNESMAX", buffer+5)) {
v2mode = MODE_SNESMAX;
}
} else if(!memcmp("MACHINE=", buffer, 8)) {
if(!strcmp("II", buffer+8)) {
machine = APPLE_II;
soft_switches &= ~(SOFTSW_IIE_REGS | SOFTSW_IIGS_REGS);
} else if(!strcmp("IIE", buffer+8)) {
machine = APPLE_IIE;
soft_switches &= ~SOFTSW_IIGS_REGS;
soft_switches |= SOFTSW_IIE_REGS;
} else if(!strcmp("IIGS", buffer+8)) {
machine = APPLE_IIGS;
soft_switches |= SOFTSW_IIE_REGS | SOFTSW_IIGS_REGS;
}
} else if(!memcmp("MUX=", buffer, 4)) {
if(!strcmp("USB", buffer+4)) {
serialmux = SERIAL_USB;
@ -62,6 +79,8 @@ void default_config() {
wifimode = WIFI_AP;
strcpy(wifi_ssid, "V2RetroNet");
strcpy(wifi_psk, "Analog");
machine = COMPAT_AUTO;
soft_switches = 0;
}
void write_config() {
@ -75,6 +94,11 @@ void write_config() {
return;
switch(v2mode) {
case MODE_FS:
memset(config_temp, 0, sizeof(config_temp));
strcpy(config_temp, "MODE=FS");
pico_write(file, config_temp, 32);
break;
case MODE_VGACARD:
memset(config_temp, 0, sizeof(config_temp));
strcpy(config_temp, "MODE=VGA");
@ -106,6 +130,23 @@ void write_config() {
pico_write(file, config_temp, 32);
break;
}
switch(machine) {
case APPLE_II:
memset(config_temp, 0, sizeof(config_temp));
strcpy(config_temp, "MACHINE=II");
pico_write(file, config_temp, 32);
break;
case APPLE_IIE:
memset(config_temp, 0, sizeof(config_temp));
strcpy(config_temp, "MACHINE=IIE");
pico_write(file, config_temp, 32);
break;
case APPLE_IIGS:
memset(config_temp, 0, sizeof(config_temp));
strcpy(config_temp, "MACHINE=IIGS");
pico_write(file, config_temp, 32);
break;
}
switch(serialmux) {
case SERIAL_USB:
memset(config_temp, 0, sizeof(config_temp));
@ -185,10 +226,19 @@ void read_config() {
pico_close(file);
}
volatile uint8_t *videx_page_tmp = (private_memory+0xF000);
extern uint8_t character_rom[4096];
void config_handler() {
if(config_memory[31] != 0) return;
if(!strcmp("WRITE_CONFIG", (uint8_t*)config_memory)) {
if(!strcmp("BANK=SAVE", (uint8_t*)config_memory)) {
videx_page_tmp = videx_page;
} else if(!strcmp("BANK=RESTORE", (uint8_t*)config_memory)) {
videx_page = videx_page_tmp;
} else if(!memcmp("BANK=FONT", (uint8_t*)config_memory, 9)) {
videx_page = character_rom + (((config_memory[9] - '0') & 3) * 1024);
} else if(!strcmp("WRITE_CONFIG", (uint8_t*)config_memory)) {
write_config();
} else if(!strcmp("REBOOT", (uint8_t*)config_memory)) {
v2mode = MODE_REBOOT;

View File

@ -20,6 +20,7 @@
typedef enum {
MODE_REBOOT = 0,
MODE_DIAG,
MODE_FS,
MODE_VGACARD,
MODE_APPLICARD,
MODE_SERIAL,
@ -54,6 +55,15 @@ typedef enum {
extern wifimode_t wifimode;
typedef enum {
APPLE_II = 0,
APPLE_IIE = 1,
APPLE_IIGS = 2,
COMPAT_AUTO = 0xff
} compat_t;
extern compat_t machine;
enum {
ABUS_MAIN_SM = 0,
ABUS_DEVICE_READ_SM = 1,

View File

@ -1,11 +1,14 @@
#include <pico/stdlib.h>
#include <pico/cyw43_arch.h>
#include <pico/multicore.h>
#include <hardware/flash.h>
#include <hardware/watchdog.h>
#include <hardware/resets.h>
#include "config.h"
#ifdef RASPBERRYPI_PICO_W
#include <pico/cyw43_arch.h>
#endif
void flash_reboot() __attribute__ ((noreturn));
// Reboot the Pico

View File

@ -20,6 +20,11 @@ static void core1_loop() {
diag_businterface();
core1_running = 0;
break;
case MODE_FS:
core1_running = 1;
fs_businterface();
core1_running = 0;
break;
case MODE_VGACARD:
core1_running = 1;
vga_businterface();
@ -53,6 +58,9 @@ static void core0_loop() {
case MODE_DIAG:
diagmain();
break;
case MODE_FS:
fsmain();
break;
case MODE_VGACARD:
vgamain();
break;
@ -84,6 +92,8 @@ int main() {
// Sensible defaults if there is no config / fs
default_config();
multicore_launch_core1(core1_loop);
// Try mounting the LittleFS, or format if it isn't there.
if(pico_mount(0) == LFS_ERR_OK) {
read_config();
@ -91,8 +101,6 @@ int main() {
read_config();
}
multicore_launch_core1(core1_loop);
core0_loop();
return 0;

View File

@ -14,4 +14,7 @@ void parallel_businterface();
void diag_businterface();
void diagmain();
void fs_businterface();
void fsmain();
void flash_reboot() __attribute__ ((noreturn));

View File

@ -0,0 +1,95 @@
#include <string.h>
#include <hardware/pio.h>
#include "common/config.h"
#include "common/buffers.h"
#include "abus.pio.h"
#include "fs/businterface.h"
#include "fs/fs.h"
static inline void __time_critical_func(shadow_memory)(uint32_t address, uint32_t value) {
// Shadow parts of the Apple's memory by observing the bus write cycles
if((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0) {
if(address < 0x200) {
if((soft_switches & (SOFTSW_80STORE | SOFTSW_AUXZP)) == (SOFTSW_80STORE | SOFTSW_AUXZP)) {
private_memory[address] = value & 0xff;
} else {
apple_memory[address] = value & 0xff;
}
} else if((address < 0xC000) || (address >= 0xD000)) {
if((soft_switches & (SOFTSW_80STORE | SOFTSW_AUX_WRITE)) == (SOFTSW_80STORE | SOFTSW_AUX_WRITE)) {
private_memory[address] = value & 0xff;
} else {
apple_memory[address] = value & 0xff;
}
}
}
if(CARD_SELECT) {
if(CARD_DEVSEL) {
if((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0) {
apple_memory[address] = value;
if((address & 0xCF8F) == 0xC08F)
fs_handler((address & 0x70) >> 4);
}
}
if(CARD_IOSTROBE) {
apple_memory[address] = value;
}
// Config memory in card slot-rom address space
if(CARD_IOSEL) {
if((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0) {
config_memory[address & 0x1F] = value;
if((address & 0xFF) == 0xFF)
config_handler();
}
}
}
// Shadow the soft-switches by observing all read & write bus cycles
if((soft_switches & SOFTSW_IIE_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
if((address & 0xff80) == 0xc000) {
switch(address & 0x7f) {
case 0x00:
soft_switches &= ~SOFTSW_80STORE;
break;
case 0x01:
soft_switches |= SOFTSW_80STORE;
break;
case 0x04:
soft_switches &= ~SOFTSW_AUX_WRITE;
break;
case 0x05:
soft_switches |= SOFTSW_AUX_WRITE;
break;
case 0x08:
soft_switches &= ~SOFTSW_AUXZP;
break;
case 0x09:
soft_switches |= SOFTSW_AUXZP;
break;
}
}
}
}
void __time_critical_func(fs_businterface)() {
while(v2mode == MODE_DIAG) {
uint32_t value = pio_sm_get_blocking(CONFIG_ABUS_PIO, ABUS_MAIN_SM);
uint32_t dout;
uint32_t address = (value >> 10) & 0xffff;
if((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) != 0) {
if(CARD_SELECT) {
dout = apple_memory[address];
// device read access
pio_sm_put_blocking(CONFIG_ABUS_PIO, ABUS_DEVICE_READ_SM, dout);
}
}
shadow_memory(address, value);
}
}

View File

@ -0,0 +1,3 @@
#pragma once
void diag_businterface();

231
v2-analog-rev1/fs/fs.c Normal file
View File

@ -0,0 +1,231 @@
#include <string.h>
#include <pico/stdlib.h>
#include <pico/multicore.h>
#include "common/config.h"
#include "common/buffers.h"
#include "fs/businterface.h"
#include "fs/fs.h"
#include "pico_hal.h"
typedef struct {
int handle;
bool valid;
} fs_file_t;
#define FS_MAXFILE 8
fs_file_t fs_file[FS_MAXFILE];
void fs_open(volatile uint8_t *fsregs) {
int file;
for(int i = 0; i < FS_MAXFILE; i++) {
if(!fs_file[i].valid) {
int flags = 0;
if(fsregs[FS_FLAGS] & FS_O_RD)
flags |= LFS_O_RDONLY;
if(fsregs[FS_FLAGS] & FS_O_WR)
flags |= LFS_O_WRONLY;
if(fsregs[FS_FLAGS] & FS_O_CREATE)
flags |= LFS_O_CREAT;
if(fsregs[FS_FLAGS] & FS_O_APPEND)
flags |= LFS_O_APPEND;
if(fsregs[FS_FLAGS] & FS_O_EXISTING)
flags |= LFS_O_EXCL;
if(fsregs[FS_FLAGS] & FS_O_TRUNC)
flags |= LFS_O_TRUNC;
if((flags & LFS_O_RDWR) != 0) {
// Ensure pathname is 128 bytes or less.
apple_memory[0xC880] = 0x00;
file = pico_open((char*)(apple_memory + 0xC800), flags);
} else {
fsregs[FS_STATUS] = FS_ERR_INVAL;
fsregs[FS_FILE] = FS_ERR_INVAL;
return;
}
if(file < 0) {
fsregs[FS_STATUS] = file;
fsregs[FS_FILE] = file;
return;
} else {
fs_file[i].handle = file;
fs_file[i].valid = true;
fsregs[FS_STATUS] = FS_ERR_OK;
fsregs[FS_FILE] = i;
return;
}
}
}
fsregs[FS_STATUS] = FS_ERR_NOMEM;
}
void fs_close(volatile uint8_t *fsregs) {
int i = fsregs[FS_FILE];
if((i >= FS_MAXFILE) || (!fs_file[i].valid) || (fs_file[i].handle < 0)) {
fsregs[FS_STATUS] = FS_ERR_INVAL;
return;
}
pico_close(fs_file[i].handle);
fs_file[i].handle = -1;
fs_file[i].valid = false;
}
void fs_read(volatile uint8_t *fsregs) {
int i = fsregs[FS_FILE];
uint16_t size = (((uint16_t)fsregs[FS_OFFMSB]) << 8) | ((uint16_t)fsregs[FS_SIZELSB]);
if((i >= FS_MAXFILE) || (!fs_file[i].valid) || (fs_file[i].handle < 0) || (size > 512)) {
fsregs[FS_STATUS] = FS_ERR_INVAL;
return;
}
if(size == 0) {
fsregs[FS_STATUS] = FS_ERR_OK;
}
int rv = pico_read(fs_file[i].handle, (uint8_t*)(apple_memory+0xC800), size);
if(rv < 0) {
fsregs[FS_STATUS] = rv;
} else {
fsregs[FS_SIZELSB] = rv & 0xFF;
fsregs[FS_SIZEMSB] = (rv >> 8);
fsregs[FS_STATUS] = FS_ERR_OK;
}
}
void fs_write(volatile uint8_t *fsregs) {
int i = fsregs[FS_FILE];
uint16_t size = (((uint16_t)fsregs[FS_OFFMSB]) << 8) | ((uint16_t)fsregs[FS_SIZELSB]);
if((i >= FS_MAXFILE) || (!fs_file[i].valid) || (fs_file[i].handle < 0) || (size > 512)) {
fsregs[FS_STATUS] = FS_ERR_INVAL;
return;
}
if(size == 0) {
fsregs[FS_STATUS] = FS_ERR_OK;
}
int rv = pico_write(fs_file[i].handle, (uint8_t*)(apple_memory+0xC800), size);
if(rv < 0) {
fsregs[FS_STATUS] = rv;
} else {
fsregs[FS_SIZELSB] = rv & 0xFF;
fsregs[FS_SIZEMSB] = (rv >> 8);
fsregs[FS_STATUS] = FS_ERR_OK;
}
}
void fs_seek(volatile uint8_t *fsregs) {
int i = fsregs[FS_FILE];
int16_t off = (int16_t)((((uint16_t)fsregs[FS_OFFMSB]) << 8) | ((uint16_t)fsregs[FS_OFFLSB]));
int whence = fsregs[FS_WHENCE];
if((i >= FS_MAXFILE) || (!fs_file[i].valid) || (fs_file[i].handle < 0)) {
fsregs[FS_STATUS] = FS_ERR_INVAL;
return;
}
int rv = pico_lseek(fs_file[i].handle, off, whence);
if(rv < 0) {
fsregs[FS_STATUS] = rv;
} else {
fsregs[FS_OFFLSB] = rv & 0xFF;
fsregs[FS_OFFMSB] = (rv >> 8);
fsregs[FS_STATUS] = FS_ERR_OK;
}
}
void fs_tell(volatile uint8_t *fsregs) {
int i = fsregs[FS_FILE];
if((i >= FS_MAXFILE) || (!fs_file[i].valid) || (fs_file[i].handle < 0)) {
fsregs[FS_STATUS] = FS_ERR_INVAL;
return;
}
int rv = pico_tell(fs_file[i].handle);
if(rv < 0) {
fsregs[FS_STATUS] = rv;
} else {
fsregs[FS_OFFLSB] = rv & 0xFF;
fsregs[FS_OFFMSB] = (rv >> 8);
fsregs[FS_STATUS] = FS_ERR_OK;
}
}
void prodos_command(volatile uint8_t *fsregs) {
}
void fs_handler(uint8_t slot) {
volatile uint8_t *fsregs = apple_memory + 0xC080 + (slot << 4);
if(fsregs[FS_EXECUTE] != 0x00) {
fsregs[FS_STATUS] = FS_ERR_INVAL;
return;
}
fsregs[FS_BUSY] = 0xff;
switch(fsregs[FS_COMMAND]) {
case FS_OPEN:
fs_open(fsregs);
break;
case FS_CLOSE:
fs_close(fsregs);
break;
case FS_READ:
fs_read(fsregs);
break;
case FS_WRITE:
fs_write(fsregs);
break;
case FS_SEEK:
fs_seek(fsregs);
break;
case FS_TELL:
fs_tell(fsregs);
break;
case PRODOS_COMMAND:
prodos_command(fsregs);
break;
}
fsregs[FS_BUSY] = 0x00;
}
void fsmain() {
int i;
memset((uint8_t*)(apple_memory+0xC000), 0x00, 0x1000);
for(i = 0; i < FS_MAXFILE; i++) {
fs_file[i].handle = -1;
fs_file[i].valid = false;
}
// Attempt to open boot disk image
fs_file[0].handle = pico_open("boot.dsk", LFS_O_RDONLY);
fs_file[0].valid = (fs_file[0].handle >= 0);
strcpy((uint8_t*)(apple_memory+0xC1E0), "FSREADY.");
strcpy((uint8_t*)(apple_memory+0xC2E0), "FSREADY.");
strcpy((uint8_t*)(apple_memory+0xC3E0), "FSREADY.");
strcpy((uint8_t*)(apple_memory+0xC4E0), "FSREADY.");
strcpy((uint8_t*)(apple_memory+0xC5E0), "FSREADY.");
strcpy((uint8_t*)(apple_memory+0xC6E0), "FSREADY.");
strcpy((uint8_t*)(apple_memory+0xC7E0), "FSREADY.");
while(v2mode == MODE_FS) {
sleep_ms(50);
}
// Close any open handles
for(i = 0; i < FS_MAXFILE; i++) {
if(fs_file[i].valid) {
pico_close(fs_file[i].handle);
fs_file[i].handle = -1;
fs_file[i].valid = false;
}
}
}

54
v2-analog-rev1/fs/fs.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#define FS_COMMAND 0x0
#define FS_FILE 0x1
#define FS_FLAGS 0x1
#define FS_SIZELSB 0x2
#define FS_SIZEMSB 0x3
#define FS_OFFLSB 0x2
#define FS_OFFMSB 0x3
#define FS_WHENCE 0x4
#define FS_BUSY 0xD
#define FS_STATUS 0xE
#define FS_EXECUTE 0xF
#define FS_O_RD 1
#define FS_O_WR 2
#define FS_O_APPEND 4
#define FS_O_EXISTING 8
#define FS_O_CREATE 16
#define FS_O_TRUNC 32
#define FS_SEEK_SET 0
#define FS_SEEK_CUR 1
#define FS_SEEK_END 2
typedef enum {
FS_OPEN = 0x10,
FS_CLOSE = 0x11,
FS_READ = 0x12,
FS_WRITE = 0x13,
FS_SEEK = 0x14,
FS_TELL = 0x15,
PRODOS_COMMAND = 0x20,
} fscommand_t;
typedef enum {
FS_ERR_OK = 0, // No error
FS_ERR_IO = -1, // Error during device operation
FS_ERR_CORRUPT = -2, // Corrupted
FS_ERR_NOENT = -3, // No directory entry
FS_ERR_EXIST = -4, // Entry already exists
FS_ERR_NOTDIR = -5, // Entry is not a dir
FS_ERR_ISDIR = -5, // Entry is a dir
FS_ERR_NOTEMPTY = -7, // Dir is not empty
FS_ERR_BADF = -8, // Bad file number
FS_ERR_FBIG = -9, // File too large
FS_ERR_INVAL = -10, // Invalid parameter
FS_ERR_NOSPC = -11, // No space left on device
FS_ERR_NOMEM = -12, // No more memory available
FS_ERR_NOATTR = -13, // No data/attr available
FS_ERR_NAMETOOLONG = -14 // File name too long
} fserror_t;
void fs_handler(uint8_t slot);

View File

@ -7,7 +7,7 @@
volatile uint8_t *videx_page = videx_memory;
uint32_t a2_first_write = 0;
static inline void __time_critical_func(videx_crtc_addr)(uint32_t value) {
}
@ -18,23 +18,185 @@ static inline void __time_critical_func(videx_crtc_write)(uint32_t value) {
static inline void __time_critical_func(shadow_memory)(uint32_t address, uint32_t value) {
// Shadow the soft-switches by observing all read & write bus cycles
if((address & 0xff80) == 0xc000) {
switch(address & 0x7f) {
case 0x00:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches &= ~SOFTSW_80STORE;
}
return;
case 0x01:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches |= SOFTSW_80STORE;
}
return;
case 0x04:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches &= ~SOFTSW_AUX_WRITE;
}
return;
case 0x05:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches |= SOFTSW_AUX_WRITE;
}
return;
case 0x08:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches &= ~SOFTSW_AUXZP;
}
return;
case 0x09:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches |= SOFTSW_AUXZP;
}
return;
case 0x0c:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches &= ~SOFTSW_80COL;
}
return;
case 0x0d:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches |= SOFTSW_80COL;
}
return;
case 0x0e:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches &= ~SOFTSW_ALTCHAR;
}
return;
case 0x0f:
if((soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches |= SOFTSW_ALTCHAR;
}
return;
case 0x21:
if((soft_switches & SOFTSW_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
if(value & 0x80) {
soft_switches |= SOFTSW_MONOCHROME;
} else {
soft_switches &= ~SOFTSW_MONOCHROME;
}
}
return;
case 0x22:
if((soft_switches & SOFTSW_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
terminal_tbcolor = value & 0xff;
}
return;
case 0x29:
if((soft_switches & SOFTSW_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches = (soft_switches & ~(SOFTSW_NEWVID_MASK << SOFTSW_NEWVID_SHIFT)) | ((value & SOFTSW_NEWVID_MASK) << SOFTSW_NEWVID_SHIFT);
}
return;
case 0x34:
if((soft_switches & SOFTSW_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
terminal_border = value & 0x0f;
}
return;
case 0x35:
if((soft_switches & SOFTSW_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches = (soft_switches & ~(SOFTSW_SHADOW_MASK << SOFTSW_SHADOW_SHIFT)) | ((value & SOFTSW_SHADOW_MASK) << SOFTSW_SHADOW_SHIFT);
}
return;
case 0x50:
soft_switches &= ~SOFTSW_TEXT_MODE;
return;
case 0x51:
soft_switches |= SOFTSW_TEXT_MODE;
return;
case 0x52:
soft_switches &= ~SOFTSW_MIX_MODE;
return;
case 0x53:
soft_switches |= SOFTSW_MIX_MODE;
return;
case 0x54:
soft_switches &= ~SOFTSW_PAGE_2;
return;
case 0x55:
soft_switches |= SOFTSW_PAGE_2;
return;
case 0x56:
soft_switches &= ~SOFTSW_HIRES_MODE;
return;
case 0x57:
soft_switches |= SOFTSW_HIRES_MODE;
return;
case 0x5e:
if(soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) {
soft_switches |= SOFTSW_DGR;
}
return;
case 0x5f:
if(soft_switches & (SOFTSW_IIGS_REGS | SOFTSW_IIE_REGS)) {
soft_switches &= ~SOFTSW_DGR;
}
return;
case 0x7e:
if((soft_switches & SOFTSW_IIE_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches |= SOFTSW_IOUDIS;
}
return;
case 0x7f:
if((soft_switches & SOFTSW_IIE_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) {
soft_switches &= ~SOFTSW_IOUDIS;
}
return;
}
}
// Shadow parts of the Apple's memory by observing the bus write cycles
if((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0) {
// Mirror Video Memory from MAIN & AUX banks
if(terminal_switches & TERMINAL_AUX_WRITE) {
if((address >= 0x400) && (address < 0xC00)) {
private_memory[address] = value & 0xff;
} else if((address >= 0x2000) && (address < 0x6000)) {
private_memory[address] = value & 0xff;
}
if((machine == COMPAT_AUTO) && (a2_first_write == 0)) {
a2_first_write = (address << 16) | (value & 0x3FF);
if(a2_first_write == 0xC0330100) { // Apple IIgs ROM03 Clock Access
machine = APPLE_IIGS;
soft_switches &= ~SOFTSW_IIE_REGS;
soft_switches |= SOFTSW_IIGS_REGS;
} else if(a2_first_write == 0x01F901FB) { // Apple II Plus
machine = APPLE_II;
soft_switches &= ~(SOFTSW_IIE_REGS | SOFTSW_IIGS_REGS);
} else if(a2_first_write == 0x01F5018B) { // Apple IIe Platinum
machine = APPLE_IIE;
soft_switches |= SOFTSW_IIE_REGS;
soft_switches &= ~SOFTSW_IIGS_REGS;
} else {
if((address >= 0x400) && (address < 0xC00)) {
apple_memory[address] = value & 0xff;
} else if((address >= 0x2000) && (address < 0x6000)) {
apple_memory[address] = value & 0xff;
a2_first_write = 0;
}
}
// Mirror Video Memory from MAIN & AUX banks
if(soft_switches & SOFTSW_LINEARIZE) {
if((address >= 0x2000) && (address < 0xC000)) {
private_memory[address] = value & 0xff;
return;
}
}
if(soft_switches & SOFTSW_80STORE) {
if(soft_switches & SOFTSW_PAGE_2) {
if((address >= 0x400) && (address < 0x800)) {
private_memory[address] = value & 0xff;
return;
} else if((soft_switches & SOFTSW_HIRES_MODE) && (address >= 0x2000) && (address < 0x4000)) {
private_memory[address] = value & 0xff;
return;
}
}
} else if(soft_switches & SOFTSW_AUX_WRITE) {
if((address >= 0x200) && (address < 0xC000)) {
private_memory[address] = value & 0xff;
return;
}
}
if((address >= 0x200) && (address < 0xC000)) {
apple_memory[address] = value & 0xff;
return;
}
// Videx 80 Column Card
if(CARD_SELECT) {
if((address >= 0xCC00) && (address < 0xD000)) {
@ -62,63 +224,9 @@ static inline void __time_critical_func(shadow_memory)(uint32_t address, uint32_
}
// Any access to Videx I/O sets the VRAM page visible in 0xCC00-0xD000
if(CARD_DEVSEL) {
if(CARD_SELECT && CARD_DEVSEL) {
videx_page = videx_memory + ((address & 0x0C) << 9);
}
// Shadow the soft-switches by observing all read & write bus cycles
if((address & 0xff80) == 0xc000) {
switch(address & 0x7f) {
case 0x0c:
if(terminal_switches & TERMINAL_IIE_REGS)
terminal_switches &= ~((uint32_t)TERMINAL_80COL);
break;
case 0x0d:
if(terminal_switches & TERMINAL_IIE_REGS)
terminal_switches |= TERMINAL_80COL;
break;
case 0x22:
if((terminal_switches & TERMINAL_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0))
terminal_tbcolor = value & 0xff;
break;
case 0x34:
if((terminal_switches & TERMINAL_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0))
terminal_border = value & 0x0f;
break;
case 0x50:
soft_switches &= ~((uint32_t)SOFTSW_TEXT_MODE);
break;
case 0x51:
soft_switches |= SOFTSW_TEXT_MODE;
break;
case 0x52:
soft_switches &= ~((uint32_t)SOFTSW_MIX_MODE);
break;
case 0x53:
soft_switches |= SOFTSW_MIX_MODE;
break;
case 0x54:
soft_switches &= ~((uint32_t)SOFTSW_PAGE_2);
break;
case 0x55:
soft_switches |= SOFTSW_PAGE_2;
break;
case 0x56:
soft_switches &= ~((uint32_t)SOFTSW_HIRES_MODE);
break;
case 0x57:
soft_switches |= SOFTSW_HIRES_MODE;
break;
case 0x5e:
if(terminal_switches & TERMINAL_IIE_REGS)
terminal_switches |= TERMINAL_DGR;
break;
case 0x5f:
if(terminal_switches & TERMINAL_IIE_REGS)
terminal_switches &= ~((uint32_t)TERMINAL_DGR);
break;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,5 +2,6 @@
#include <stdint.h>
extern const uint8_t default_character_rom[256*8];
extern const uint8_t default_character_rom[2048];
extern const uint8_t appleiie_character_rom[2048];
extern const uint8_t appleiigs_character_rom[2048];

View File

@ -6,18 +6,61 @@
#include "vga/render.h"
#include "vga/character_rom.h"
#include "vga/vgaout.h"
#include "pico_hal.h"
uint16_t text_fore;
uint16_t text_back;
uint16_t text_border;
compat_t machinefont = APPLE_II;
bool userfont = false;
// Initialize the character generator ROM
void switch_font() {
switch(machine) {
default:
case APPLE_II:
memcpy(character_rom, default_character_rom, 2048);
break;
case APPLE_IIE:
memcpy(character_rom, appleiie_character_rom, 2048);
break;
case APPLE_IIGS:
memcpy(character_rom, appleiigs_character_rom, 2048);
break;
}
machinefont = machine;
}
void load_font() {
int file = pico_open("font", LFS_O_RDONLY);
int br = 0;
userfont = false;
if(file < 0) {
return;
}
br = pico_read(file, character_rom, 2048);
if(br == 2048) {
userfont = true;
}
pico_close(file);
return;
}
void render_init() {
int i;
// Initialize the character generator ROM
memcpy(character_rom, default_character_rom, sizeof(character_rom));
soft_switches = 0;
terminal_switches = TERMINAL_TEST | TERMINAL_IIE_REGS | TERMINAL_IIGS_REGS;
terminal_tbcolor = 0x0f;
load_font();
if(!userfont)
switch_font();
if((soft_switches & SOFTSW_MODE_MASK) == 0)
soft_switches |= SOFTSW_TEST;
terminal_tbcolor = 0xf0;
terminal_border = 0x00;
render_test_init();
@ -26,58 +69,62 @@ void render_init() {
uint32_t testdone=0;
void __noinline __time_critical_func(render_loop)() {
while(v2mode == MODE_VGACARD) {
if(!userfont && (machinefont != machine)) {
switch_font();
}
update_text_flasher();
text_fore = lores_palette[TERMINAL_FORE];
text_back = lores_palette[TERMINAL_BACK];
text_border = lores_palette[TERMINAL_BORDER];
if(terminal_switches & TERMINAL_TEST) {
if(soft_switches & SOFTSW_TEST) {
render_testpattern();
// Automatically dismiss the test pattern when the Apple II initializes
// soft switches during startup.
if(((soft_switches & SOFTSW_MODE_MASK) != 0) && (testdone == 0)) {
terminal_switches &= (~(uint32_t)TERMINAL_TEST);
soft_switches &= ~SOFTSW_TEST;
testdone = 1;
render_about_init();
}
} else if(soft_switches & SOFTSW_VIDEX) {
render_videx();
} else if(soft_switches & SOFTSW_SHR) {
render_shr();
} else {
switch(soft_switches & SOFTSW_MODE_MASK) {
case 0:
if(terminal_switches & TERMINAL_DGR) {
if(soft_switches & SOFTSW_DGR) {
render_dgr();
} else {
render_lores();
}
break;
case SOFTSW_MIX_MODE:
if(terminal_switches & (TERMINAL_80COL | TERMINAL_DGR) == (TERMINAL_80COL | TERMINAL_DGR)) {
if((soft_switches & (SOFTSW_80COL | SOFTSW_DGR)) == (SOFTSW_80COL | SOFTSW_DGR)) {
render_mixed_dgr();
} else {
render_mixed_lores();
}
break;
case SOFTSW_HIRES_MODE:
if(terminal_switches & TERMINAL_DGR) {
if(soft_switches & SOFTSW_DGR) {
render_dhgr();
} else {
render_hires();
}
break;
case SOFTSW_HIRES_MODE|SOFTSW_MIX_MODE:
if(terminal_switches & TERMINAL_DGR) {
if((soft_switches & (SOFTSW_80COL | SOFTSW_DGR)) == (SOFTSW_80COL | SOFTSW_DGR)) {
render_mixed_dhgr();
} else {
render_mixed_hires();
}
break;
default:
if(terminal_switches & TERMINAL_80COL) {
render_terminal();
} else {
render_text();
}
break;
}
}

View File

@ -17,10 +17,11 @@ extern void render_about_init();
extern void update_text_flasher();
extern void render_text();
extern void render_text_line(unsigned int line);
extern void render_text40_line(bool p2, unsigned int line);
extern void render_text80_line(bool p2, unsigned int line);
extern void render_terminal();
extern void render_terminal_line(unsigned int line);
extern void render_videx();
extern void render_videx_line(unsigned int line);
extern void render_border();
@ -36,6 +37,8 @@ extern void render_mixed_dhgr();
extern void render_dgr();
extern void render_mixed_dgr();
extern void render_shr();
extern uint_fast32_t text_flasher_mask;
extern void flash_dowork();

View File

@ -6,22 +6,20 @@
extern uint16_t lores_palette[16];
static void render_dgr_line(uint line);
static void render_dgr_line(bool p2, uint line);
void __time_critical_func(render_dgr)() {
vga_prepare_frame();
// Skip 48 lines to center vertically
struct vga_scanline *skip_sl = vga_prepare_scanline();
for(int i=0; i < 48; i++) {
skip_sl->data[i] = (uint32_t)THEN_WAIT_HSYNC << 16;
}
skip_sl->length = 48;
vga_submit_scanline(skip_sl);
render_border();
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
for(uint line=0; line < 24; line++) {
render_dgr_line(line);
render_dgr_line(p2, line);
}
render_border();
}
@ -30,17 +28,19 @@ void __time_critical_func(render_mixed_dgr)() {
render_border();
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
for(uint line=0; line < 20; line++) {
render_dgr_line(line);
render_dgr_line(p2, line);
}
if(terminal_switches & TERMINAL_80COL) {
if(soft_switches & SOFTSW_80COL) {
for(uint line=20; line < 24; line++) {
render_terminal_line(line);
render_text80_line(p2, line);
}
} else {
for(uint line=20; line < 24; line++) {
render_text_line(line);
render_text40_line(p2, line);
}
}
@ -48,28 +48,28 @@ void __time_critical_func(render_mixed_dgr)() {
}
static void __time_critical_func(render_dgr_line)(uint line) {
static void __time_critical_func(render_dgr_line)(bool p2, uint line) {
// Construct two scanlines for the two different colored cells at the same time
struct vga_scanline *sl1 = vga_prepare_scanline();
struct vga_scanline *sl2 = vga_prepare_scanline();
uint sl_pos = 0;
uint i;
const uint8_t *page = (const uint8_t *)text_memory;
const uint8_t *line_buf = page + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40);
const uint8_t *line_bufa = (const uint8_t *)((p2 ? text_p2 : text_p1) + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40));
const uint8_t *line_bufb = (const uint8_t *)((p2 ? text_p4 : text_p3) + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40));
// Pad 40 pixels on the left to center horizontally
while(sl_pos < 40/8) {
for(i = 0; i < 40/8; i++) {
sl1->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl2->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
}
for(i=0; i < 40; i++) {
uint32_t color1 = lores_palette[line_buf[i | (1 << 10)] & 0xf];
uint32_t color2 = lores_palette[(line_buf[i | (1 << 10)] >> 4) & 0xf];
uint32_t color3 = lores_palette[line_buf[i] & 0xf];
uint32_t color4 = lores_palette[(line_buf[i] >> 4) & 0xf];
uint32_t color1 = lores_palette[line_bufb[i] & 0xf];
uint32_t color2 = lores_palette[(line_bufb[i] >> 4) & 0xf];
uint32_t color3 = lores_palette[line_bufa[i] & 0xf];
uint32_t color4 = lores_palette[(line_bufa[i] >> 4) & 0xf];
// Each double lores pixel is 7 double hires pixels, or 7 VGA pixels wide
sl1->data[sl_pos] = (color1|THEN_EXTEND_6) | ((color3|THEN_EXTEND_6) << 16);
@ -77,7 +77,8 @@ static void __time_critical_func(render_dgr_line)(uint line) {
sl_pos++;
}
for(i=0; i < 40/8; i++) {
// Pad 40 pixels on the right to center horizontally
for(i = 0; i < 40/8; i++) {
sl1->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl2->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;

View File

@ -5,7 +5,7 @@
#include "render.h"
#include "vgaout.h"
static void render_dhgr_line(uint line);
static void render_dhgr_line(bool p2, uint line);
#define _RGB333(r, g, b) ( \
(((((uint)(r) * 256 / 36) + 128) / 256) << 6) | \
@ -16,18 +16,18 @@ static void render_dhgr_line(uint line);
uint16_t dhgr_palette[16] = {
_RGB333(0x00,0x00,0x00), // black 0000
_RGB333(0x00,0x00,0xb4), // d.blue 1000
_RGB333(0x00,0x48,0x00), // d.green 0100
_RGB333(0x00,0x90,0xfc), // h.blue 1100
_RGB333(0x24,0x24,0x00), // brown 0010
_RGB333(0x00,0x90,0x00), // d.green 0100
_RGB333(0x00,0x6c,0xd8), // h.blue 1100
_RGB333(0x6c,0x6c,0x00), // brown 0010
_RGB333(0x90,0x90,0x90), // l.gray 1010
_RGB333(0x00,0xd8,0x24), // h.green 0110
_RGB333(0x90,0xfc,0xb4), // aqua 1110
_RGB333(0x6c,0x00,0x6c), // magenta 0001
_RGB333(0x90,0xfc,0x90), // aqua 1110
_RGB333(0xd8,0x00,0x6c), // magenta 0001
_RGB333(0xb4,0x24,0xfc), // h.violet 1001
_RGB333(0x48,0x48,0x48), // d.gray 0101
_RGB333(0x6c,0x6c,0xfc), // l.blue 1101
_RGB333(0xfc,0x48,0x00), // h.orange 0011
_RGB333(0xfc,0x6c,0xfc), // pink 1011
_RGB333(0x6c,0x6c,0xff), // l.blue 1101
_RGB333(0xfc,0x6c,0x24), // h.orange 0011
_RGB333(0xff,0xd8,0xff), // pink 1011
_RGB333(0xd8,0xd8,0x00), // yellow 0111
_RGB333(0xff,0xff,0xff), // white 1111
};
@ -43,8 +43,10 @@ void __time_critical_func(render_dhgr)() {
render_border();
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
for(uint line=0; line < 192; line++) {
render_dhgr_line(line);
render_dhgr_line(p2, line);
}
render_border();
@ -56,17 +58,19 @@ void __time_critical_func(render_mixed_dhgr)() {
render_border();
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
for(uint line=0; line < 160; line++) {
render_dhgr_line(line);
render_dhgr_line(p2, line);
}
if(terminal_switches & TERMINAL_80COL) {
if(soft_switches & SOFTSW_80COL) {
for(uint line=20; line < 24; line++) {
render_terminal_line(line);
render_text80_line(p2, line);
}
} else {
for(uint line=20; line < 24; line++) {
render_text_line(line);
render_text40_line(p2, line);
}
}
@ -74,19 +78,18 @@ void __time_critical_func(render_mixed_dhgr)() {
}
static void __time_critical_func(render_dhgr_line)(uint line) {
static void __time_critical_func(render_dhgr_line)(bool p2, uint line) {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
uint i;
const uint8_t *page = (const uint8_t *)hires_memory;
const uint8_t *line_mem = page + dhgr_line_to_mem_offset(line);
const uint8_t *line_mema = (const uint8_t *)((p2 ? hgr_p2 : hgr_p1) + dhgr_line_to_mem_offset(line));
const uint8_t *line_memb = (const uint8_t *)((p2 ? hgr_p4 : hgr_p3) + dhgr_line_to_mem_offset(line));
// Pad 40 pixels on the left to center horizontally
while(sl_pos < 40/8) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
}
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
// DHGR is weird. Nuff said.
uint32_t dots = 0;
@ -96,23 +99,24 @@ static void __time_critical_func(render_dhgr_line)(uint line) {
i = 0;
while(i < 40) {
// Load in the next 28 subpixels
dots = (line_mem[i | 8192] & 0x7f) << 0;
dots |= (line_mem[i] & 0x7f) << 7;
dots = (line_memb[i] & 0x7f) << 0;
dots |= (line_mema[i] & 0x7f) << 7;
i++;
dots |= (line_mem[i | 8192] & 0x7f) << 14;
dots |= (line_mem[i] & 0x7f) << 21;
dots |= (line_memb[i] & 0x7f) << 14;
dots |= (line_mema[i] & 0x7f) << 21;
i++;
if(soft_switches & SOFTSW_MONOCHROME) {
// Consume 6 pixels (24 subpixel bits)
#if 0
for(j = 0; j < 12; j++) {
pixeldata = ((dots & 1) ? (text_fore) : (text_back));
pixeldata = ((dots & 1) ? (0x1ff) : (0x000));
dots >>= 1;
pixeldata |= (((dots & 1) ? (text_fore) : (text_back))) << 16;
pixeldata |= (((dots & 1) ? (0x1ff) : (0x000))) << 16;
dots >>= 1;
sl->data[sl_pos++] = pixeldata;
}
#else
} else {
// Consume 6 pixels (24 subpixel bits)
for(j = 0; j < 3; j++) {
pixeldata = (dhgr_palette[dots & 0xf] | THEN_EXTEND_3);
dots >>= 4;
@ -120,27 +124,28 @@ static void __time_critical_func(render_dhgr_line)(uint line) {
dots >>= 4;
sl->data[sl_pos++] = pixeldata;
}
#endif
}
// 4 subpixels roll over to the next block
// Load in the next 28 subpixels
dots |= (line_mem[i | 8192] & 0x7f) << 4;
dots |= (line_mem[i] & 0x7f) << 11;
dots |= (line_memb[i] & 0x7f) << 4;
dots |= (line_mema[i] & 0x7f) << 11;
i++;
dots |= (line_mem[i | 8192] & 0x7f) << 18;
dots |= (line_mem[i] & 0x7f) << 25;
dots |= (line_memb[i] & 0x7f) << 18;
dots |= (line_mema[i] & 0x7f) << 25;
i++;
if(soft_switches & SOFTSW_MONOCHROME) {
// Consume 8 pixels (32 subpixel bits)
#if 0
for(j = 0; j < 16; j++) {
pixeldata = ((dots & 1) ? (text_fore) : (text_back));
pixeldata = ((dots & 1) ? (0x1ff) : (0x000));
dots >>= 1;
pixeldata |= (((dots & 1) ? (text_fore) : (text_back))) << 16;
pixeldata |= (((dots & 1) ? (0x1ff) : (0x000))) << 16;
dots >>= 1;
sl->data[sl_pos++] = pixeldata;
}
#else
} else {
// Consume 8 pixels (32 subpixel bits)
for(j = 0; j < 4; j++) {
pixeldata = (dhgr_palette[dots & 0xf] | THEN_EXTEND_3);
dots >>= 4;
@ -148,16 +153,16 @@ static void __time_critical_func(render_dhgr_line)(uint line) {
dots >>= 4;
sl->data[sl_pos++] = pixeldata;
}
#endif
}
}
for(i = 0; i < 40/8; i++) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
}
// Pad 40 pixels on the left to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl->length = sl_pos;
sl->repeat_count = 1;
vga_submit_scanline(sl);
}

View File

@ -6,8 +6,7 @@
#include "vgaout.h"
static void render_hires_line(uint line);
static void render_hires_line(bool p2, uint line);
static uint hires_line_to_mem_offset(uint line) {
return ((line & 0x07) << 10) | ((line & 0x38) << 4) | (((line & 0xc0) >> 6) * 40);
@ -19,8 +18,10 @@ void __time_critical_func(render_hires)() {
render_border();
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
for(uint line=0; line < 192; line++) {
render_hires_line(line);
render_hires_line(p2, line);
}
render_border();
@ -32,17 +33,19 @@ void __time_critical_func(render_mixed_hires)() {
render_border();
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
for(uint line=0; line < 160; line++) {
render_hires_line(line);
render_hires_line(p2, line);
}
if(terminal_switches & TERMINAL_80COL) {
if(soft_switches & SOFTSW_80COL) {
for(uint line=20; line < 24; line++) {
render_terminal_line(line);
render_text80_line(p2, line);
}
} else {
for(uint line=20; line < 24; line++) {
render_text_line(line);
render_text40_line(p2, line);
}
}
@ -50,18 +53,16 @@ void __time_critical_func(render_mixed_hires)() {
}
static void __time_critical_func(render_hires_line)(uint line) {
static void __time_critical_func(render_hires_line)(bool p2, uint line) {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
const uint8_t *page = (const uint8_t *)((soft_switches & SOFTSW_PAGE_2) ? hgr_p2 : hgr_p1);
const uint8_t *line_mem = page + hires_line_to_mem_offset(line);
const uint8_t *line_mem = (const uint8_t *)((p2 ? hgr_p2 : hgr_p1) + hires_line_to_mem_offset(line));
// Pad 40 pixels on the left to center horizontally
while(sl_pos < 40/8) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
}
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
// Each hires byte contains 7 pixels which may be shifted right 1/2 a pixel. That is
// represented here by 14 'dots' to precisely describe the half-pixel positioning.
@ -83,7 +84,7 @@ static void __time_critical_func(render_hires_line)(uint line) {
// pixel
uint32_t dots = 0;
uint oddness = 0;
uint i;
uint i, j;
// Load in the first 14 dots
dots |= (uint32_t)hires_dot_patterns[line_mem[0]] << 15;
@ -97,6 +98,18 @@ static void __time_critical_func(render_hires_line)(uint line) {
}
dots |= (uint32_t)hires_dot_patterns[b] << 1;
if(soft_switches & SOFTSW_MONOCHROME) {
// Consume 14 dots
for(j = 0; j < 14; j++) {
uint32_t pixeldata = (dots & 0x2000) ? (0x1ff|THEN_EXTEND_1) : (0x000|THEN_EXTEND_1);
pixeldata |= (dots & 0x1000) ?
((uint32_t)0x1ff|THEN_EXTEND_1) << 16 :
((uint32_t)0x000|THEN_EXTEND_1) << 16;
dots <<= 2;
sl->data[sl_pos] = pixeldata;
sl_pos++;
}
} else {
// Consume 14 dots
for(uint j=0; j < 7; j++) {
uint dot_pattern = oddness | ((dots >> 24) & 0xff);
@ -106,12 +119,13 @@ static void __time_critical_func(render_hires_line)(uint line) {
oddness ^= 0x100;
}
}
for(i=0; i < 40/8; i++) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
}
// Pad 40 pixels on the right to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl->length = sl_pos;
sl->repeat_count = 1;
vga_submit_scanline(sl);

View File

@ -31,7 +31,7 @@ uint16_t lores_palette[16] = {
#undef _RGB333
static void render_lores_line(uint line);
static void render_lores_line(bool p2, uint line);
void __time_critical_func(render_lores)() {
@ -39,8 +39,10 @@ void __time_critical_func(render_lores)() {
render_border();
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
for(uint line=0; line < 24; line++) {
render_lores_line(line);
render_lores_line(p2, line);
}
render_border();
@ -52,17 +54,19 @@ void __time_critical_func(render_mixed_lores)() {
render_border();
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
for(uint line=0; line < 20; line++) {
render_lores_line(line);
render_lores_line(p2, line);
}
if(terminal_switches & TERMINAL_80COL) {
if(soft_switches & SOFTSW_80COL) {
for(uint line=20; line < 24; line++) {
render_terminal_line(line);
render_text80_line(p2, line);
}
} else {
for(uint line=20; line < 24; line++) {
render_text_line(line);
render_text40_line(p2, line);
}
}
@ -70,18 +74,17 @@ void __time_critical_func(render_mixed_lores)() {
}
static void __time_critical_func(render_lores_line)(uint line) {
static void __time_critical_func(render_lores_line)(bool p2, uint line) {
// Construct two scanlines for the two different colored cells at the same time
struct vga_scanline *sl1 = vga_prepare_scanline();
struct vga_scanline *sl2 = vga_prepare_scanline();
uint sl_pos = 0;
uint i;
const uint8_t *page = (const uint8_t *)((soft_switches & SOFTSW_PAGE_2) ? text_p2 : text_p1);
const uint8_t *line_buf = page + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40);
const uint8_t *line_buf = (const uint8_t *)((p2 ? text_p2 : text_p1) + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40));
// Pad 40 pixels on the left to center horizontally
while(sl_pos < 40/8) {
for(i = 0; i < 40/8; i++) {
sl1->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl2->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
@ -94,8 +97,12 @@ static void __time_critical_func(render_lores_line)(uint line) {
uint32_t color4 = lores_palette[(line_buf[i+1] >> 4) & 0xf];
// Each lores pixel is 7 hires pixels, or 14 VGA pixels wide
sl1->data[sl_pos] = (color1|THEN_EXTEND_6) | ((color3|THEN_EXTEND_6) << 16);
sl2->data[sl_pos] = (color2|THEN_EXTEND_6) | ((color4|THEN_EXTEND_6) << 16);
sl1->data[sl_pos] = (color1|THEN_EXTEND_6) | ((color1|THEN_EXTEND_6) << 16);
sl2->data[sl_pos] = (color2|THEN_EXTEND_6) | ((color2|THEN_EXTEND_6) << 16);
sl_pos++;
sl1->data[sl_pos] = (color3|THEN_EXTEND_6) | ((color3|THEN_EXTEND_6) << 16);
sl2->data[sl_pos] = (color4|THEN_EXTEND_6) | ((color4|THEN_EXTEND_6) << 16);
sl_pos++;
}

View File

@ -0,0 +1,129 @@
#include <pico/stdlib.h>
#include "hires_color_patterns.h"
#include "hires_dot_patterns.h"
#include "vgabuf.h"
#include "render.h"
#include "vgaout.h"
extern uint16_t dhgr_palette[16];
static void render_shr_line(uint16_t line);
static uint16_t __time_critical_func(rgb444_to_rgb333)(uint16_t a) {
return ((a & 0xe00) >> 3) | ((a & 0xe0) >> 2) | ((a & 0xe) >> 1);
}
// Skip 40 lines to center vertically
void __time_critical_func(render_shr_border)() {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
while(sl_pos < VGA_WIDTH/16) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 8 pixels per word
sl_pos++;
}
sl->length = sl_pos;
sl->repeat_count = 39;
vga_submit_scanline(sl);
}
void __time_critical_func(render_shr)() {
vga_prepare_frame();
render_shr_border();
for(uint line=0; line < 200; line++) {
render_shr_line(line);
}
render_shr_border();
}
static void __time_critical_func(render_shr_line)(uint16_t line) {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
uint i;
uint8_t control = private_memory[0x9D00 + line];
uint32_t line_palette_offset = (control & 0xF) << 5;
volatile uint16_t *shr_palette = (volatile uint16_t*)(private_memory + 0x9E00 + line_palette_offset);
volatile uint8_t *line_mem = (volatile uint8_t *)(private_memory + 0x2000 + (line * 160));
// SHR is weird. Nuff said.
uint32_t dots = 0;
uint32_t pixeldata;
uint16_t color_a = 0, color_b = 0;
int j;
i = 0;
#if 1
if(control & 0x80) { // 640 Pixels
while(i < 160) {
// Load in the next 4 subpixels
dots = line_mem[i++];
pixeldata = (dhgr_palette[(dots >> 6) & 0x3]);
pixeldata |= (dhgr_palette[(dots >> 4) & 0x3]) << 16;
sl->data[sl_pos++] = pixeldata;
pixeldata = (dhgr_palette[(dots >> 2) & 0x3]);
pixeldata |= (dhgr_palette[(dots >> 0) & 0x3]) << 16;
sl->data[sl_pos++] = pixeldata;
}
#else
if(control & 0x80) { // 640 Pixels
while(i < 160) {
// Load in the next 2 pixels
dots = (line_mem[i++] & 0xff);
color_a = ((dots >> 4) & 0xf);
color_b = ((dots >> 0) & 0xf);
// Consume 2 pixels
pixeldata = rgb444_to_rgb333(shr_palette[color_a]) | THEN_EXTEND_1;
pixeldata |= (rgb444_to_rgb333(shr_palette[color_b]) | THEN_EXTEND_1) << 16;
sl->data[sl_pos++] = pixeldata;
}
#endif
} else if(control & 0x40) { // 320 Pixels w/ color fill
while(i < 160) {
// Load in the next 4 subpixels
dots = (line_mem[i++] & 0xff);
// Consume 2 pixels
if(dots & 0xf0) {
color_a = ((dots >> 4) & 0xf);
} else {
color_a = color_b;
}
if(dots & 0x0f) {
color_b = ((dots >> 0) & 0xf);
} else {
color_b = color_a;
}
pixeldata = rgb444_to_rgb333(shr_palette[color_a]) | THEN_EXTEND_1;
pixeldata |= (rgb444_to_rgb333(shr_palette[color_b]) | THEN_EXTEND_1) << 16;
sl->data[sl_pos++] = pixeldata;
}
} else { // 320 Pixels
while(i < 160) {
// Load in the next 2 pixels
dots = (line_mem[i++] & 0xff);
color_a = ((dots >> 4) & 0xf);
color_b = ((dots >> 0) & 0xf);
// Consume 2 pixels
pixeldata = rgb444_to_rgb333(shr_palette[color_a]) | THEN_EXTEND_1;
pixeldata |= (rgb444_to_rgb333(shr_palette[color_b]) | THEN_EXTEND_1) << 16;
sl->data[sl_pos++] = pixeldata;
}
}
sl->length = sl_pos;
sl->repeat_count = 1;
vga_submit_scanline(sl);
}

View File

@ -1,12 +1,15 @@
#include <string.h>
#include <pico/stdlib.h>
#include <pico/unique_id.h>
#include <hardware/timer.h>
#include "vga/vgabuf.h"
#include "vga/render.h"
#include "vga/vgaout.h"
#include "vga/logo.h"
#ifdef RASPBERRYPI_PICO_W
#include <pico/unique_id.h>
#endif
#define _PIXPAIR(p1, p2) ((uint32_t)(p1) | (((uint32_t)p2) << 16))
char error_message[16*24+1];
@ -14,7 +17,7 @@ char error_message[16*24+1];
void render_test_init() {
memset(error_message, ' ', 16*24);
memcpy(error_message + 0, "HW: ANALOG-REV-1", 16);
memcpy(error_message + 16, "FW: 23-01-06-000", 16);
memcpy(error_message + 16, "FW: 23-01-16-119", 16);
memcpy(error_message + 64, " COPYRIGHT (C) ", 16);
memcpy(error_message + 80, " DAVID KUDER ", 16);
@ -31,10 +34,14 @@ void render_test_init() {
memcpy(error_message + 288, "CHECK FOR PROPER", 16);
memcpy(error_message + 304, " CARD INSERTION ", 16);
#ifdef RASPBERRYPI_PICO_W
memcpy(error_message + 352, " SERIAL NUMBER: ", 16);
// Get Pico's Flash Serial Number (Board ID) and terminating null.
pico_get_unique_board_id_string(error_message + 368, 17);
#else
memcpy(error_message + 368, " V2-ANALOG-LC ", 16);
#endif
}
// Clear the error message, in case the user sets 0x20 in terminal switches
@ -100,8 +107,8 @@ void __noinline __time_critical_func(render_testpattern)() {
uint32_t bits_b = char_text_bits(error_message[(w & 0x1f0) | (i+1)], (w/2) & 0x7);
uint32_t bits = (bits_a << 7) | bits_b;
for(uint j=0; j < 7; j++) {
uint32_t pixeldata = (bits & 0x2000) ? (0x1ff|THEN_EXTEND_1) : (0 | THEN_EXTEND_1);
pixeldata |= (bits & 0x1000) ? ((0x1ff|THEN_EXTEND_1) << 16) : ((0 | THEN_EXTEND_1) << 16);
uint32_t pixeldata = (bits & 0x2000) ? (0|THEN_EXTEND_1) : (0x1ff | THEN_EXTEND_1);
pixeldata |= (bits & 0x1000) ? ((0|THEN_EXTEND_1) << 16) : ((0x1ff | THEN_EXTEND_1) << 16);
bits <<= 2;
sl->data[sl_pos++] = pixeldata;
}
@ -162,8 +169,8 @@ void __noinline __time_critical_func(render_testpattern)() {
uint32_t bits_b = char_text_bits(error_message[(w & 0x1f0) | (i+1)], (w/2) & 0x7);
uint32_t bits = (bits_a << 7) | bits_b;
for(uint j=0; j < 7; j++) {
uint32_t pixeldata = (bits & 0x2000) ? (0x1ff|THEN_EXTEND_1) : (0 | THEN_EXTEND_1);
pixeldata |= (bits & 0x1000) ? ((0x1ff|THEN_EXTEND_1) << 16) : ((0 | THEN_EXTEND_1) << 16);
uint32_t pixeldata = (bits & 0x2000) ? (0|THEN_EXTEND_1) : (0x1ff | THEN_EXTEND_1);
pixeldata |= (bits & 0x1000) ? ((0|THEN_EXTEND_1) << 16) : ((0x1ff | THEN_EXTEND_1) << 16);
bits <<= 2;
sl->data[sl_pos++] = pixeldata;
}
@ -203,8 +210,8 @@ void __noinline __time_critical_func(render_testpattern)() {
uint32_t bits_b = char_text_bits(error_message[(w & 0x1f0) | (i+1)], (w/2) & 0x7);
uint32_t bits = (bits_a << 7) | bits_b;
for(uint j=0; j < 7; j++) {
uint32_t pixeldata = (bits & 0x2000) ? (0x1ff|THEN_EXTEND_1) : (0 | THEN_EXTEND_1);
pixeldata |= (bits & 0x1000) ? ((0x1ff|THEN_EXTEND_1) << 16) : ((0 | THEN_EXTEND_1) << 16);
uint32_t pixeldata = (bits & 0x2000) ? (0|THEN_EXTEND_1) : (0x1ff | THEN_EXTEND_1);
pixeldata |= (bits & 0x1000) ? ((0|THEN_EXTEND_1) << 16) : ((0x1ff | THEN_EXTEND_1) << 16);
bits <<= 2;
sl->data[sl_pos++] = pixeldata;
}

View File

@ -11,26 +11,27 @@ static uint64_t next_flash_tick = 0;
void update_text_flasher() {
uint64_t now = time_us_64();
if(now > next_flash_tick) {
text_flasher_mask ^= 0xff;
text_flasher_mask ^= 0x7f;
next_flash_tick = now + 250000u;
}
}
static inline uint_fast8_t __time_critical_func(char_text_bits)(uint_fast8_t ch, uint_fast8_t glyph_line) {
uint_fast8_t bits = character_rom[((uint_fast16_t)ch << 3) | glyph_line];
if(ch & 0x80) {
uint_fast8_t bits = character_rom[((uint_fast16_t)ch << 3) | glyph_line] & 0x7f;
if((soft_switches & SOFTSW_ALTCHAR) || (ch & 0x80)) {
// normal character
return bits & 0x7f;
return bits;
}
if((bits & 0x80) == 0) {
// inverse character
return bits ^ 0x7f;
} else {
if(ch & 0x40) {
// flashing character
return (bits ^ text_flasher_mask) & 0x7f;
return (bits ^ text_flasher_mask);
}
// inverse character
return bits;
}
@ -39,8 +40,8 @@ void __time_critical_func(render_border)() {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
while(sl_pos < VGA_WIDTH/8) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
while(sl_pos < VGA_WIDTH/16) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 8 pixels per word
sl_pos++;
}
@ -54,27 +55,33 @@ void __time_critical_func(render_text)() {
render_border();
for(int line=0; line < 24; line++) {
render_text_line(line);
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
if(soft_switches & SOFTSW_80COL) {
for(uint line=0; line < 24; line++) {
render_text80_line(p2, line);
}
} else {
for(uint line=0; line < 24; line++) {
render_text40_line(p2, line);
}
}
render_border();
}
void __time_critical_func(render_text_line)(unsigned int line) {
const uint8_t *page = (const uint8_t *)((soft_switches & SOFTSW_PAGE_2) ? text_p2 : text_p1);
const uint8_t *line_buf = page + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40);
void __time_critical_func(render_text40_line)(bool p2, unsigned int line) {
const uint8_t *page = (const uint8_t *)(p2 ? text_p2 : text_p1);
const uint8_t *line_buf = (const uint8_t *)(page + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40));
for(uint glyph_line=0; glyph_line < 8; glyph_line++) {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
// Pad 40 pixels on the left to center horizontally
while(sl_pos < 40/8) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
}
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
for(uint col=0; col < 40; ) {
// Grab 14 pixels from the next two characters
@ -87,10 +94,10 @@ void __time_critical_func(render_text_line)(unsigned int line) {
// Translate each pair of bits into a pair of pixels
for(int i=0; i < 7; i++) {
uint32_t pixeldata = (bits & 0x2000) ? (text_fore|THEN_EXTEND_1) : (text_back|THEN_EXTEND_1);
uint32_t pixeldata = (bits & 0x2000) ? (text_back|THEN_EXTEND_1) : (text_fore|THEN_EXTEND_1);
pixeldata |= (bits & 0x1000) ?
((uint32_t)text_fore|THEN_EXTEND_1) << 16 :
((uint32_t)text_back|THEN_EXTEND_1) << 16;
((uint32_t)text_back|THEN_EXTEND_1) << 16 :
((uint32_t)text_fore|THEN_EXTEND_1) << 16;
bits <<= 2;
sl->data[sl_pos] = pixeldata;
@ -98,10 +105,58 @@ void __time_critical_func(render_text_line)(unsigned int line) {
}
}
for(uint i=0; i < 40/8; i++) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
}
// Pad 40 pixels on the right to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl->length = sl_pos;
sl->repeat_count = 1;
vga_submit_scanline(sl);
}
}
void __time_critical_func(render_text80_line)(bool p2, unsigned int line) {
const uint8_t *page_a = (const uint8_t *)(p2 ? text_p2 : text_p1);
const uint8_t *page_b = (const uint8_t *)(p2 ? text_p4 : text_p3);
const uint8_t *line_buf_a = page_a + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40);
const uint8_t *line_buf_b = page_b + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40);
for(uint glyph_line=0; glyph_line < 8; glyph_line++) {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
// Pad 40 pixels on the left to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
for(uint col=0; col < 40; ) {
// Grab 14 pixels from the next four characters
uint32_t bits_a = char_text_bits(line_buf_a[col], glyph_line);
uint32_t bits_b = char_text_bits(line_buf_b[col], glyph_line);
col++;
uint32_t bits = (bits_b << 7) | bits_a;
// Translate each pair of bits into a pair of pixels
for(int i=0; i < 7; i++) {
uint32_t pixeldata = (bits & 0x2000) ? (text_back) : (text_fore);
pixeldata |= (bits & 0x1000) ?
((uint32_t)text_back) << 16 :
((uint32_t)text_fore) << 16;
bits <<= 2;
sl->data[sl_pos] = pixeldata;
sl_pos++;
}
}
// Pad 40 pixels on the right to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl->length = sl_pos;
sl->repeat_count = 1;

View File

@ -21,20 +21,20 @@ static inline uint_fast8_t __time_critical_func(char_terminal_bits)(uint_fast8_t
}
void __time_critical_func(render_terminal)() {
void __time_critical_func(render_videx)() {
vga_prepare_frame();
render_border();
for(int line=0; line < 24; line++) {
render_terminal_line(line);
render_videx_line(line);
}
render_border();
}
void __time_critical_func(render_terminal_line)(unsigned int line) {
void __time_critical_func(render_videx_line)(unsigned int line) {
const uint8_t *page = (const uint8_t *)videx_memory;
const uint8_t *line_buf = page + (line * 80);
@ -43,10 +43,9 @@ void __time_critical_func(render_terminal_line)(unsigned int line) {
uint sl_pos = 0;
// Pad 40 pixels on the left to center horizontally
while(sl_pos < 40/8) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
}
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
for(uint col=0; col < 80; ) {
// Grab 14 pixels from the next two characters
@ -70,10 +69,10 @@ void __time_critical_func(render_terminal_line)(unsigned int line) {
}
}
for(uint i=0; i < 40/8; i++) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl_pos++;
}
// Pad 40 pixels on the left to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 8 pixels per word
sl->length = sl_pos;
sl->repeat_count = 1;

View File

@ -36,7 +36,7 @@ pixel_out:
public wait_vsync:
wait 0 irq VSYNC_IRQ_NUM
public wait_hsync:
wait 0 irq HSYNC_IRQ_NUM [9]
wait 0 irq HSYNC_IRQ_NUM [1]
public extend_7:
nop [1]
public extend_6:

View File

@ -5,7 +5,7 @@
volatile uint32_t soft_switches = 0;
// The currently programmed character generator ROM for text mode
uint8_t character_rom[2048];
uint8_t character_rom[4096];
volatile uint8_t videx_crtc_reg;

View File

@ -3,40 +3,20 @@
#include <stdint.h>
#include "common/buffers.h"
enum {
SOFTSW_TEXT_MODE = 0x01,
SOFTSW_MIX_MODE = 0x02,
SOFTSW_HIRES_MODE = 0x04,
SOFTSW_MODE_MASK = 0x07,
SOFTSW_PAGE_2 = 0x08,
};
#define text_memory text_p1
#define hires_memory hgr_p1
#define videx_memory (private_memory+0xF000)
extern volatile uint32_t soft_switches;
extern uint8_t character_rom[2048];
extern uint8_t character_rom[4096];
extern uint8_t terminal_rom[2048];
extern volatile uint8_t videx_crtc_reg;
extern volatile uint8_t videx_addr;
extern volatile uint8_t terminal_switches;
#define terminal_tbcolor apple_memory[0xC022]
#define terminal_border apple_memory[0xC034]
enum {
TERMINAL_80COL = 0x01,
TERMINAL_DGR = 0x02,
TERMINAL_AUX_WRITE = 0x10,
TERMINAL_TEST = 0x20,
TERMINAL_IIE_REGS = 0x40,
TERMINAL_IIGS_REGS = 0x80,
};
#define TERMINAL_FORE (terminal_tbcolor & 0xf)
#define TERMINAL_BACK ((terminal_tbcolor>>4) & 0xf)
#define TERMINAL_FORE ((terminal_tbcolor>>4) & 0xf)
#define TERMINAL_BACK (terminal_tbcolor & 0xf)
#define TERMINAL_BORDER (terminal_border & 0xf)

View File

@ -20,7 +20,7 @@
#define HSYNC_TIMING_VALUE (((PIXELS_PER_LINE) / 8) - 23)
#define VSYNC_TIMING_VALUE ((LINES_PER_FRAME) - 4)
#define NUM_SCANLINE_BUFFERS 8
#define NUM_SCANLINE_BUFFERS 32
enum {
VGA_HSYNC_SM = 0,
@ -151,7 +151,7 @@ static void vga_dma_irq_handler() {
active_scanline->_flags &= ~(uint_fast8_t)FLAG_BUSY;
const uint32_t irq_status = spin_lock_blocking(lock);
scanline_queue_tail = (scanline_queue_tail + 1) % NUM_SCANLINE_BUFFERS;
scanline_queue_tail = (scanline_queue_tail + 1) & (NUM_SCANLINE_BUFFERS-1);
trigger_ready_scanline_dma();
spin_unlock(lock, irq_status);
}
@ -244,7 +244,7 @@ struct vga_scanline *vga_prepare_scanline() {
scanline->_flags = FLAG_BUSY;
scanline->_sync = (uint32_t)THEN_WAIT_HSYNC << 16;
scanline_queue_head = (scanline_queue_head + 1) % NUM_SCANLINE_BUFFERS;
scanline_queue_head = (scanline_queue_head + 1) & (NUM_SCANLINE_BUFFERS-1);
return scanline;
}

View File

@ -28,7 +28,7 @@ struct vga_scanline {
volatile uint_fast8_t _flags;
uint32_t _sync;
uint32_t data[(VGA_WIDTH)+1];
uint32_t data[(VGA_WIDTH/2)+8];
};