Booting from External SD Cards

This commit is contained in:
Peter Weingartner 2024-08-22 20:59:17 -04:00
parent fda10b8287
commit 575b96e7f0
6 changed files with 273 additions and 68 deletions

5
samples/README.md Normal file
View file

@ -0,0 +1,5 @@
# Samples
This directory contains some simple example programs for the Foenix Toolbox.
* pgx/sample_pgx -- This is a very simple 64TASS assembly project that builds a PGX file that can work with the toolbox. This PGX file does not actually use any toolbox functions, but it can serve as a test file for executing binaries or bootable FNXBOOT.PGX files. The included BAT file can build the binary file.

View file

@ -0,0 +1 @@
64tass -b --list sample.lst -o sample.pgx sample_pgx.s

46
samples/pgx/sample_pgx.s Normal file
View file

@ -0,0 +1,46 @@
.cpu "65816"
vky_text = $f04000
vky_mst_ctrl_0 = $f01000
* = $002000
header: .text "PGX"
.byte $01 ; Version 0, CPU 65816
.byte 0
.byte `start
.byte >start
.byte <start
start: sep #$20
.as
rep #$10
.xl
; Switch to text only mode
lda #$01
sta vky_mst_ctrl_0
; Fill the text screen with blanks
lda #' '
ldx #0
clrloop1: sta @l vky_text,x
inx
cpx #$2000
bne clrloop1
; Write the message to the screen
ldx #0
putloop: lda @l message,x
beq done
sta @l vky_text,x
inx
bra putloop
done: nop
bra done
message: .null 'Hello, world!'

View file

@ -12,6 +12,8 @@
#include "boot.h"
#include "memory.h"
#include "proc.h"
#include "dev/fsys.h"
#include "timers.h"
#include "dev/txt_screen.h"
#include "dev/sprites.h"
#include "dev/tiles.h"
@ -19,9 +21,12 @@
#include "rsrc/sprites/boot_sprites.h"
#include "rsrc/tiles/boot_tiles.h"
#include <stdio.h>
#include <string.h>
const uint32_t boot_record_alignment = 8192; // Number of bytes for boot record alignement
const uint32_t boot_rom_location = 0xf00000; // Location to check for a boot record in ROM
const uint32_t boot_cart_location = 0xf40000; // Location to check for a boot record in ROM
enum boot_src_e {
BOOT_SRC_NONE = 0, // Nothing more to check
@ -54,9 +59,8 @@ static enum boot_src_e boot_chain[] = {
BOOT_SRC_RAM,
BOOT_SRC_CARTRIDGE,
BOOT_SRC_SD0,
BOOT_SRC_SD1,
// BOOT_SRC_SD1,
BOOT_SRC_ROM,
BOOT_SRC_NONE
};
/**
@ -85,56 +89,183 @@ short boot_ram_launch(boot_record_p record) {
}
/**
* @brief Look for a boot record in RAM
* @return 1 if success, 0 if boot record not validated
* @brief Check to see if the boot record is valid
*
* @param record pointer to the potential boot record to validate
* @return true the record is valid
* @return false the record is not valid
*/
short boot_ram() {
unsigned long top_ram = mem_get_ramtop();
for (uint32_t address = 0; address < top_ram; address += boot_record_alignment) {
if (boot_ram_launch((boot_record_p)address)) {
return 1;
}
}
return 0;
bool is_valid_boot_record(boot_record_p record) {
return ((record->signature1 == 0xf8) && (record->signature2 == 0x16) && (record->version == 0));
}
/**
* @brief Look for a boot record in the flash cartridge
* @return 1 if success, 0 if boot record not validated
* @brief Check to see if a device has bootable code
*
* For RAM, ROM, and Flash boot devices, there will be a boot record on the device.
* The SD cards will not have a boot record.
*
* @param device the ID of the boot device to check
* @param boot_record pointer to the pointer that should be set for the boot record on the device
* @return true if the device is bootable
* @return false if the device cannot be booted
*/
short boot_cartridge() {
return 0;
}
bool is_bootable(enum boot_src_e device, boot_record_p * boot_record) {
uint32_t top_ram = 0;
boot_record_p record = 0;
t_file_info file_info;
/**
* @brief Find and launch the user's code
*
*/
void boot_launch() {
for (short i = 0; boot_chain[i] != BOOT_SRC_NONE; i++) {
switch(boot_chain[i]) {
// By default, do not have a boot record
*boot_record = 0;
switch(device) {
case BOOT_SRC_RAM:
if (boot_ram()) {
return;
top_ram = (uint32_t)mem_get_ramtop();
for (uint32_t address = 0; address < top_ram; address += boot_record_alignment) {
record = (boot_record_p)address;
if (is_valid_boot_record(record)) {
*boot_record = record;
return true;
}
}
break;
case BOOT_SRC_ROM:
record = (boot_record_p)boot_rom_location;
if (is_valid_boot_record(record)) {
*boot_record = record;
return true;
}
break;
case BOOT_SRC_CARTRIDGE:
record = (boot_record_p)boot_cart_location;
if (is_valid_boot_record(record)) {
*boot_record = record;
return true;
}
break;
case BOOT_SRC_SD0:
if (proc_run("/sd0/fnxboot.pgz", 0, boot_args) == 0) {
return;
} else if (proc_run("/sd0/fnxboot.pgx", 0, boot_args) == 0) {
return;
} else if (proc_run("/sd0/fnxboot.elf", 0, boot_args) == 0) {
return;
if ((fsys_stat("/sd0/fnxboot.pgx", &file_info) >= 0) || (fsys_stat("/sd0/fnxboot.pgz", &file_info) >= 0)) {
return true;
}
break;
case BOOT_SRC_SD1:
// if (fsys_stat("/sd1/fnxboot.pgx", &file_info) || fsys_stat("/sd1/fnxboot.pgz", &file_info)) {
// return true;
// }
break;
default:
break;
}
return 0;
}
/**
* @brief Do the initial display of the boot device icon
*
* @param position the position in the boot sequence (0 - 4)
* @param device the ID of the boot device
*/
void boot_icon(short position, enum boot_src_e device) {
short base_x = 14*8;
short base_y = 21*8;
uint8_t x = base_x + 32*position;
switch(device) {
case BOOT_SRC_RAM:
sprite_assign(position, ram_pixels, 0, 0);
break;
case BOOT_SRC_ROM:
sprite_assign(position, rom_pixels, 0, 0);
break;
case BOOT_SRC_CARTRIDGE:
sprite_assign(position, cartridge_pixels, 0, 0);
break;
case BOOT_SRC_SD0:
sprite_assign(position, sd_ext_pixels, 0, 0);
break;
case BOOT_SRC_SD1:
sprite_assign(position, sd_int_pixels, 0, 0);
break;
default:
break;
}
// By default, we'll set the icon to dim
// We'll make it bright when we've confirmed it is bootable
sprite_clut(position, 1);
// Set the position of the icon
sprite_position(position, x, base_y);
// Enable the icon to display it
sprite_enable(position, true);
}
/**
* @brief Indicate that the device is bootable by making it brighter
*
* @param position the position in the boot sequence (0 - 4)
*/
void boot_icon_highlight(short position) {
sprite_clut(position, 0);
}
void boot_from(enum boot_src_e device) {
short result = 0;
t_file_info file_info;
switch(device) {
case BOOT_SRC_SD0:
if (fsys_stat("/sd0/fnxboot.pgz", &file_info) >= 0) {
txt_print(0, "Booting: /sd0/fnxboot.pgz\n");
proc_run("/sd0/fnxboot.pgz", 0, boot_args);
} else if (fsys_stat("/sd0/fnxboot.pgx", &file_info) >= 0) {
txt_print(0, "Booting: /sd0/fnxboot.pgx\n");
result = proc_run("/sd0/fnxboot.pgx", 0, boot_args);
if (result != 0) {
printf("proc_run error: %d\n", result);
}
}
break;
default:
txt_print(0, "No bootable device is present.\n");
break;
}
}
const char * boot_source_name(enum boot_src_e device) {
switch(device) {
case BOOT_SRC_SD0:
return "External SDC";
case BOOT_SRC_SD1:
return "Internal SDC";
case BOOT_SRC_RAM:
return "RAM";
case BOOT_SRC_ROM:
return "ROM";
case BOOT_SRC_CARTRIDGE:
return "CARTRIDGE";
default:
return "None";
}
}
@ -143,6 +274,10 @@ void boot_launch() {
*
*/
void boot_screen() {
enum boot_src_e boot_source = BOOT_SRC_NONE;
short i = 0;
long jiffies_target = 0;
// txt_set_mode(0, TXT_MODE_TEXT | TXT_MODE_SPRITE);
*tvky_mstr_ctrl = (uint16_t)(VKY_MCR_TILE | VKY_MCR_SPRITE | VKY_MCR_GRAPHICS | VKY_MCR_TEXT_OVERLAY | VKY_MCR_TEXT);
@ -150,7 +285,7 @@ void boot_screen() {
tvky_bg_color->green = 0;
tvky_bg_color->red = 0;
for (int i = 0; i < 4 * 256; i++) {
for (i = 0; i < 4 * 256; i++) {
VKY_GR_CLUT_0[i] = boot_clut[i];
VKY_GR_CLUT_1[i] = boot_clut[i] >> 2;
}
@ -172,9 +307,6 @@ void boot_screen() {
*tvky_layers = 0x0444;
short base_x = 14*8;
short base_y = 21*8;
// Set up the text window for the boot messaging
t_rect boot_text_window;
boot_text_window.origin.x = 20;
@ -183,35 +315,47 @@ void boot_screen() {
boot_text_window.size.height = 12;
txt_set_region(0, &boot_text_window);
txt_print(0, "Scanning for bootable devices...\n\n");
txt_print(0, "Scanning for bootable devices...\n");
sprite_assign(0, cartridge_pixels, 0, 0);
sprite_assign(1, ram_pixels, 0, 0);
sprite_assign(2, sd_ext_pixels, 0, 0);
sprite_assign(3, sd_int_pixels, 0, 0);
sprite_assign(4, rom_pixels, 0, 0);
for (short position = 0; position < sizeof(boot_chain) / sizeof(enum boot_src_e); position++) {
boot_record_p boot_record;
sprite_clut(0, 1);
sprite_clut(1, 0);
sprite_clut(2, 1);
sprite_clut(3, 0);
sprite_clut(4, 0);
boot_icon(position, boot_chain[position]);
if (is_bootable(boot_chain[position], &boot_record)) {
boot_icon_highlight(position);
sprite_position(0, base_x, base_y);
sprite_position(1, base_x + 32, base_y);
sprite_position(2, base_x + 32*2, base_y);
sprite_position(3, base_x + 32*3, base_y);
sprite_position(4, base_x + 32*4, base_y);
// Assign the boot source to this, if it hasn't already been bound
if (boot_source == BOOT_SRC_NONE) {
txt_print(0, "Default boot source: ");
txt_print(0, boot_source_name(boot_chain[position]));
txt_put(0, '\n');
boot_source = boot_chain[position];
}
sprite_enable(0, true);
sprite_enable(1, true);
sprite_enable(2, true);
sprite_enable(3, true);
sprite_enable(4, true);
// If there is a boot icon specified in the boot record, change to that icon
if (boot_record != 0) {
if (boot_record->icon_address != 0) {
sprite_assign(position, (uint8_t *)(boot_record->icon_address), 0, 0);
// txt_print(0, "1: Start 'Frogger' from the cartridge.\n");
txt_print(0, "1: Start 'Foobar' in RAM.\n");
// txt_print(0, "3: Start from external SD card.\n");
txt_print(0, "2: Start from internal SD card.\n");
txt_print(0, "3: Start 'f/Manager'.\n");
// If there is a CLUT defined for the boot record, switch to use that clut
if (boot_record->clut_address != 0) {
for (i = 0; i < 4 * 256; i++) {
uint8_t * source_clut = (uint8_t *)boot_record->clut_address;
VKY_GR_CLUT_2[i] = source_clut[i];
}
sprite_clut(position, 2);
}
}
}
}
}
// Wait some time for user input
jiffies_target = timers_jiffies() + 60 * 10;
while (jiffies_target > timers_jiffies()) {
;
}
// And launch the system
boot_from(boot_source);
}

View file

@ -45,7 +45,7 @@ else ifeq ($(UNIT),F256K)
AR=nlib
SRCS_FOR_UNIT=txt_f256.c kbd_f256.c kbd_f256k.c indicators_c256.c interrupts_f256.c sdc_f256.c iec.c # timers_c256.c
CFLAGS_FOR_UNIT=-DMODEL=2 -DCPU=255 --code-model large --data-model large # --target Foenix
CFLAGS_FOR_UNIT=-DMODEL=2 -DCPU=255 --code-model large --data-model large --target f256
endif
INCLUDES=-I.. -I../include

View file

@ -23,6 +23,7 @@
#include "fatfs/ff.h"
#include "log.h"
#include "syscalls.h"
#include "sys_general.h"
#include "simpleio.h"
#include "utilities.h"
@ -332,7 +333,7 @@ SYSTEMCALL short fsys_stat(const char * path, p_file_info file) {
// FatFS's f_stat function does not handle root directories so bodge this in...
// For each drive...
for (i = 0; i < 3; i++) {
for (i = 0; i < FF_VOLUMES; i++) {
// Compute two legitimate paths to it
strcpy(match1, "/");
strcat(match1, (char *)VolumeStr[i]);
@ -1212,7 +1213,11 @@ short fsys_elf_loader(short chan, long destination, long * start) {
* 0 on success, negative number on error
*/
short fsys_pgx_loader(short chan, long destination, long * start) {
#if CPU == CPU_WDC65816
const char signature[] = "PGX\x01";
#else
const char signature[] = "PGX\x02";
#endif
unsigned char * chunk = 0;
unsigned char * dest = 0;
long file_idx = 0;
@ -1477,7 +1482,7 @@ short fsys_register_loader(const char * extension, p_file_loader loader) {
for (i = 0; i < MAX_LOADERS; i++) {
if (g_file_loader[i].status == 0) {
g_file_loader[i].status = 1; /* Claim this loader record */
g_file_loader[i].loader = loader; /* Set the loader routine */
for (j = 0; j <= MAX_EXT; j++) { /* Clear out the extension */
g_file_loader[i].extension[j] = 0;
}
@ -1492,6 +1497,10 @@ short fsys_register_loader(const char * extension, p_file_loader loader) {
}
}
// NOTE: this was moved here because somehow clearing the extension in the loader record
// was clobbering the loader pointer. Not sure why
g_file_loader[i].loader = loader; /* Set the loader routine */
return 0;
}
}