Link bootloader with bios to get access to symbols

By doing this we don't need to call bios functions, we can just jsr
directly to the addresses. This does mean that everytime the bios
changes the bootloader has to change, but ideally all the bootloader
does is load the bios and then get remapped out of memory. Any important
drivers like file system can be loaded from the bootloader.

This also means that the runtime functions are located in the bios for
the bootloader, so the rom will have to stay mapped in until the kernel
is started, at which point it will have its own runtime and the rom and
bootloader are no longer needed.
This commit is contained in:
Byron Lathi
2023-08-08 22:16:42 -07:00
parent e73c4e1d08
commit 6b0caea84c
13 changed files with 46 additions and 501 deletions

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<efx:project name="super6502" description="" last_change_date="Tue August 8 2023 19:25:59" location="/home/byron/Projects/super6502/hw/efinix_fpga" sw_version="2023.1.150" last_run_state="pass" last_run_tool="efx_pgm" last_run_flow="bitstream" config_result_in_sync="true" design_ood="sync" place_ood="sync" route_ood="sync" xmlns:efx="http://www.efinixinc.com/enf_proj" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.efinixinc.com/enf_proj enf_proj.xsd">
<efx:project name="super6502" description="" last_change_date="Tue August 8 2023 21:51:46" location="/home/byron/Projects/super6502/hw/efinix_fpga" sw_version="2023.1.150" last_run_state="pass" last_run_tool="efx_pgm" last_run_flow="bitstream" config_result_in_sync="true" design_ood="sync" place_ood="sync" route_ood="sync" xmlns:efx="http://www.efinixinc.com/enf_proj" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.efinixinc.com/enf_proj enf_proj.xsd">
<efx:device_info>
<efx:family name="Trion"/>
<efx:device name="T20F256"/>

View File

@@ -11,7 +11,7 @@ LISTS=lists
TESTS=tests
SRCS=$(wildcard *.s) $(wildcard *.c)
SRCS+=$(filter-out $(wildcard tests/*), $(wildcard **/*.s)) $(filter-out $(wildcard tests/*) $(wildcard filesystem/*), $(wildcard **/*.c))
SRCS+=$(filter-out $(wildcard tests/*), $(wildcard **/*.s)) $(filter-out $(wildcard tests/*), $(wildcard **/*.c))
OBJS+=$(patsubst %.s,%.o,$(filter %s,$(SRCS)))
OBJS+=$(patsubst %.c,%.o,$(filter %c,$(SRCS)))

41
sw/bios/bootloader.s Normal file
View File

@@ -0,0 +1,41 @@
.importzp sp, ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3
.autoimport on
.feature string_escapes
.MACPACK generic
_console_clear = $0
_console_read_char = $2
_console_write_char = $4
_sd_readblock = $6
.segment "BOOTSECTOR"
_start:
jmp _main
.byte "SUPR6502"
_main:
lda #<str
ldx #>str
jsr _cputs
@end: bra @end
str: .asciiz "Hello from the bootloader!\r\n"
_end:
.res (446+_start-_end)
.res 16
.res 16
.res 16
.res 16
.byte $55
.byte $AA

View File

@@ -1,208 +0,0 @@
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#include "fat.h"
#include "devices/sd_card.h"
uint8_t fat_buf[512];
static uint32_t fat_end_of_chain;
static full_bpb_t bpb;
static uint32_t data_region_start;
void fat_init(){
int i;
sd_readblock(0, fat_buf);
memcpy(&bpb, &fat_buf[11], sizeof(full_bpb_t));
sd_readblock(1, fat_buf);
sd_readblock(32, fat_buf);
data_region_start = bpb.reserved_sectors + bpb.fat_count*bpb.sectors_per_fat_32;
sd_readblock(bpb.reserved_sectors, fat_buf);
//uncomment to view start of FAT
/*
for (i = 0; i < FAT_CLUSTERS_PER_SECTOR; i++) {
cprintf("%lx ", ((uint32_t*)fat_buf)[i]);
}
cprintf("\n\n");
*/
fat_end_of_chain = ((uint32_t*)fat_buf)[1] & FAT_EOC_CLUSTERMASK;
cprintf("End of chain indicator: %lx\n", fat_end_of_chain);
}
// make sure you have enough space.
void fat_read_cluster(uint16_t cluster, uint8_t* buf) {
uint8_t i;
for (i = 0; i < bpb.sectors_per_cluster; i++) {
sd_readblock(data_region_start + i + (cluster - 2) * 8, buf+i*bpb.bytes_per_sector);
}
}
uint32_t fat_get_chain_value(uint16_t cluster) {
sd_readblock(bpb.reserved_sectors, fat_buf);
return ((uint32_t*)fat_buf)[cluster];
}
//the dentry is a double pointer because we need to increment it.
void fat_parse_vfat_filenamename(vfat_dentry_t** vfat_dentry, char* name, uint32_t cluster) {
uint8_t i;
uint8_t overflows;
uint8_t done;
char* shift_name;
uint8_t sequence_number = (*vfat_dentry)->sequence_number;
overflows = 0;
for (;;){
shift_name = name + 13*((sequence_number & FAT_LFN_ENTRY_MASK) - 1);
done = 0;
for(i = 0; i < 5; i++) {
shift_name[i] = (*vfat_dentry)->filename0[i];
if (!shift_name[i]) {
done = 1;
break;
}
}
if (!done) {
for(i = 0; i < 6; i++) {
shift_name[i+5] = (*vfat_dentry)->filename1[i];
if (!shift_name[i+5]) {
done = 1;
break;
}
}
}
if (!done) {
for(i = 0; i < 2; i++) {
shift_name[i+11] = (*vfat_dentry)->filename2[i];
if (!shift_name[i+11]) {
done = 1;
break;
}
}
}
if ((sequence_number & FAT_LFN_ENTRY_MASK) == 1) {
break;
} else {
do {
(*vfat_dentry)++;
if ((uint8_t*)*vfat_dentry >= fat_buf + sizeof(fat_buf)) {
overflows++;
if (overflows == bpb.sectors_per_cluster) {
cprintf("Too many overflows, go back to fat!\n"); //TODO this
return;
}
sd_readblock(data_region_start + (cluster - 2) * 8 + overflows, fat_buf);
*vfat_dentry = (vfat_dentry_t*)fat_buf;
}
} while((*vfat_dentry)->sequence_number == 0xe5);
sequence_number = (*vfat_dentry)->sequence_number;
}
}
}
uint32_t fat_find_cluster_num(char* name, uint32_t cluster) {
vfat_dentry_t* vfat_dentry;
dos_dentry_t* dos_dentry;
char* vfat_name;
cprintf("Looking for file %s\n", name);
sd_readblock(data_region_start + (cluster - 2) * 8, fat_buf);
vfat_dentry = (vfat_dentry_t*)fat_buf;
vfat_name = (char*)malloc(FAT_MAX_FILE_NAME);
while(vfat_dentry->sequence_number == 0xe5)
vfat_dentry++;
vfat_name[0] = '\0';
while(vfat_dentry->sequence_number) {
fat_parse_vfat_filenamename(&vfat_dentry, vfat_name, cluster);
cprintf("Parsed filename: %s\n", vfat_name);
if (!strcmp(vfat_name, name)) { //TODO this is probably unsafe, use strncmp
cprintf("Found file %s\n", vfat_name);
break;
} else {
vfat_dentry += 2;
while(vfat_dentry->sequence_number == 0xe5)
vfat_dentry++;
}
}
free(vfat_name);
if (!vfat_dentry->sequence_number) {
cprintf("File not found.\n");
return -1;
}
dos_dentry = (dos_dentry_t*) vfat_dentry + 1; //dos entry follows last vfat entry
cluster = ((uint32_t)dos_dentry->first_cluster_h << 16) + dos_dentry->first_cluster_l;
cprintf("Cluster: %ld\n", cluster);
return cluster;
}
uint16_t fat_parse_path_to_cluster(char* filename) {
//basically start at the root folder and search through it
int i;
int len;
uint8_t dirs = 0;
char* spaced_filename;
char* fragment;
uint32_t cluster = 2; //root chain is chain 2
if (filename[0] != '/') {
cprintf("Filename does not begin with '/'\n");
return 0;
}
filename++;
len = strlen(filename);
spaced_filename = (char*)malloc(len+1); //need to account for null byte
for (i = 0; i <= len; i++) {
if (filename[i] == '/') {
spaced_filename[i] = '\0';
dirs++;
} else {
spaced_filename[i] = filename[i];
}
}
fragment = spaced_filename;
cprintf("Dirs: %d\n", dirs);
for (i = 0; i <= dirs; i++) {
cprintf("Fragment: %s\n", fragment);
cluster = fat_find_cluster_num(fragment, cluster);
fragment = spaced_filename + strlen(fragment) + 1;
}
free(spaced_filename);
return cluster;
}

View File

@@ -1,122 +0,0 @@
#ifndef _FAT_H
#define _FAT_H
#include <stdint.h>
extern uint8_t fat_buf[];
#define FAT_MAX_FILE_NAME 255
#define FAT_CLUSTERS_PER_SECTOR 128
#define FAT_CLUSTERMASK 0x0fffffff
#define FAT_EOC_CLUSTERMASK 0x0ffffff8
#define FAT_LAST_LFN_MASK (1 << 6)
#define FAT_LFN_ENTRY_MASK 0x1f
typedef struct {
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t fat_count;
uint16_t max_dir_entries;
uint16_t total_sector_count;
uint8_t media_descriptor;
uint16_t sectors_per_fat;
} dos_2_bpb_t;
typedef struct {
dos_2_bpb_t bpb2;
uint16_t sectors_per_track;
uint16_t head_count;
uint32_t hidden_sector_count;
uint32_t logical_sector_count;
uint32_t sectors_per_fat;
uint16_t extended_flags;
uint16_t version;
uint32_t root_cluster;
uint16_t system_information;
uint16_t backup_boot_sector;
uint8_t reserved[12];
} dos_3_bpb_t;
typedef struct {
dos_3_bpb_t bpb3;
uint8_t drive_num;
uint8_t reserved;
uint8_t extended_signature;
uint32_t volume_id;
uint8_t partition_label[11];
uint8_t filesystem_type[8];
} ebpb_t;
typedef struct {
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t fat_count;
uint16_t max_dir_entries;
uint16_t total_sector_count;
uint8_t media_descriptor;
uint16_t sectors_per_fat_16;
uint16_t sectors_per_track;
uint16_t head_count;
uint32_t hidden_sector_count;
uint32_t logical_sector_count;
uint32_t sectors_per_fat_32;
uint16_t extended_flags;
uint16_t version;
uint32_t root_cluster;
uint16_t system_information;
uint16_t backup_boot_sector;
uint8_t reserved[12];
uint8_t drive_num;
uint8_t reserved2;
uint8_t extended_signature;
uint32_t volume_id;
uint8_t partition_label[11];
uint8_t filesystem_type[8];
} full_bpb_t;
typedef struct {
uint32_t sig;
uint8_t reserved[480];
uint32_t sig2;
uint32_t free_data_clusters;
uint32_t last_allocated_data_cluster;
uint32_t reserved2;
uint32_t sig3;
} fs_info_sector_t;
typedef struct {
uint8_t sequence_number;
uint16_t filename0[5];
uint8_t attributes;
uint8_t type;
uint8_t checksum;
uint16_t filename1[6];
uint16_t reserved;
uint16_t filename2[2];
} vfat_dentry_t;
typedef struct {
uint8_t filename[8];
uint8_t extension[3];
uint8_t attributes;
uint8_t reserved;
uint8_t create_time_10ms;
uint32_t create_date;
uint16_t access_date;
uint16_t first_cluster_h;
uint32_t modify_cluster;
uint16_t first_cluster_l;
uint32_t file_size;
} dos_dentry_t;
void fat_init();
uint16_t fat_parse_path_to_cluster(char* filename);
void fat_read_cluster(uint16_t cluster, uint8_t* buf);
uint32_t fat_get_chain_value(uint16_t cluster);
#endif

View File

@@ -1,28 +0,0 @@
#include <conio.h>
#include "o65.h"
void o65_print_option(o65_opt_t* opt) {
int i;
cprintf("Option Length: %d\n", opt->olen);
cprintf("Option Type: %x ", opt->type);
switch (opt->type) {
case O65_OPT_FILENAME: cprintf("Filename\n"); break;
case O65_OPT_OS: cprintf("OS\n"); break;
case O65_OPT_ASSEMBLER: cprintf("Assembler\n"); break;
case O65_OPT_AUTHOR: cprintf("Author\n"); break;
case O65_OPT_DATE: cprintf("Creation Date\n"); break;
default: cprintf("Invalid\n"); break;
}
if (opt->type != O65_OPT_OS) {
for (i = 0; i < opt->olen - 2; i++) {
cprintf("%c", opt->data[i]);
}
} else {
cprintf("%x", opt->data[0]);
}
cprintf("\n\n");
}

View File

@@ -1,65 +0,0 @@
#ifndef _O65_H
#define _O65_H
#include <stdint.h>
#define O65_NON_C64 0x0001
#define O65_MAGIC_0 0x6f
#define O65_MAGIC_1 0x36
#define O65_MAGIC_2 0x35
#define O65_OPT_FILENAME 0
#define O65_OPT_OS 1
#define O65_OPT_ASSEMBLER 2
#define O65_OPT_AUTHOR 3
#define O65_OPT_DATE 4
#define O65_OS_OSA65 1
#define O65_OS_LUNIX 2
#define O65_OS_CC65 3
#define O65_OS_OPENCBM 4
#define O65_OS_SUPER6502 5
typedef union {
struct {
int cpu : 1;
int reloc : 1;
int size : 1;
int obj : 1;
int simple : 1;
int chain : 1;
int bsszero : 1;
int cpu2 : 4;
int align : 2;
};
uint16_t _mode;
} o65_mode_t;
typedef struct {
uint16_t c64_marker;
uint8_t magic[3];
uint8_t version;
o65_mode_t mode;
uint16_t tbase;
uint16_t tlen;
uint16_t dbase;
uint16_t dlen;
uint16_t bbase;
uint16_t blen;
uint16_t zbase;
uint16_t zlen;
uint16_t stack;
} o65_header_t;
typedef struct {
uint8_t olen;
uint8_t type;
uint8_t data[1]; //This is actually variable length
} o65_opt_t;
void o65_print_option(o65_opt_t* opt);
#endif

View File

@@ -2,7 +2,7 @@ MEMORY
{
ZP: start = $0, size = $100, type = rw, define = yes;
SDRAM: start = $200, size = $7e00, type = rw, define = yes;
BOOTLOADER: start = $8000, size = $1000, type = rw, define = yes;
BOOTLOADER: start = $8000, size = $1000, type = rw, define = yes, file = "bootloader.bin";
ROM: start = $F000, size = $1000, file = %O;
}
@@ -16,6 +16,7 @@ SEGMENTS {
CODE: load = ROM, type = ro;
RODATA: load = ROM, type = ro;
VECTORS: load = ROM, type = ro, start = $FFFA;
BOOTSECTOR: load = BOOTLOADER, type = rw, start = $8000;
}
FEATURES {

View File

@@ -7,7 +7,6 @@
#include "devices/uart.h"
#include "devices/sd_card.h"
#include "devices/sd_print.h"
#include "filesystem/fat.h"
#define KERNEL_LOAD_ADDR 0xD000

View File

@@ -1,38 +0,0 @@
CC=../cc65/bin/cl65
CFLAGS=-T -t none -I. --cpu "65C02"
LDFLAGS=-C link.ld -m $(NAME).map
NAME=bootloader
BIN=$(NAME).bin
HEX=$(NAME).hex
LISTS=lists
SRCS=$(wildcard *.s) $(wildcard *.c)
OBJS+=$(patsubst %.s,%.o,$(filter %s,$(SRCS)))
OBJS+=$(patsubst %.c,%.o,$(filter %c,$(SRCS)))
all: $(HEX)
$(HEX): $(BIN)
objcopy --input-target=binary --output-target=ihex $(BIN) $(HEX)
$(BIN): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $@
%.o: %.c $(LISTS)
$(CC) $(CFLAGS) -l $(LISTS)/$<.list -c $< -o $@
%.o: %.s $(LISTS)
$(CC) $(CFLAGS) -l $(LISTS)/$<.list -c $< -o $@
$(LISTS):
mkdir -p $(addprefix $(LISTS)/,$(sort $(dir $(SRCS))))
.PHONY: clean
clean:
rm -rf $(OBJS) $(BIN) $(HEX) $(LISTS) $(NAME).map

View File

@@ -1,27 +0,0 @@
.segment "BOOTSECTOR"
_start:
jmp _main
.byte "SUPR6502"
_main:
ldx #$04
lda #'A'
brk
nop ; This byte available for something
@1: jmp @1
_end:
.res (446+_start-_end)
.res 16
.res 16
.res 16
.res 16
.byte $55
.byte $AA

View File

@@ -1,8 +0,0 @@
MEMORY
{
BOOT: start = $8000, size = $200, file = %O;
}
SEGMENTS {
BOOTSECTOR: load = BOOT, start = $8000;
}

View File

@@ -1,6 +1,6 @@
#!/bin/bash
BOOTLOADER=../bootloader/bootloader.bin
BOOTLOADER=../bios/bootloader.bin
DEVICE=/dev/mmcblk0
TMPBOOTSECT=/tmp/bootsect
TMPMOUNT=/tmp/sd