Initial commit

This commit is contained in:
Joppe Blondel
2026-09-28 13:17:35 +02:00
commit 67ec3c9a65
7 changed files with 403 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
build
+56
View File
@@ -0,0 +1,56 @@
TARGET := monitor
BUILDDIR := build
METAL_C_SRCFILES := $(wildcard *.c)
METAL_ASM_SRCFILES := $(wildcard *.asm)
METAL_RELFILES := $(METAL_ASM_SRCFILES:%.asm=$(BUILDDIR)/metal/%.rel) \
$(METAL_C_SRCFILES:%.c=$(BUILDDIR)/metal/%.rel)
MUSIC_FILE := updraft_flight
METAL_CC := sdcc
METAL_AS := sdasz80
METAL_MAKEBIN := makebin
METAL_FLAGS := -mz80 --no-std-crt0 --code-loc 0x0080 --data-loc 0x8000
METAL_CPPFLAGS := -I src -I $(BUILDDIR)/metal/generated --disable-warning 283
METAL_BIN_SIZE := 32768
METAL_RAM_SIZE := 32768
.DEFAULT_GOAL := metal
.PHONY: all metal clean
all: metal
metal: $(BUILDDIR)/metal/$(TARGET).ihx $(BUILDDIR)/metal/$(TARGET).bin $(BUILDDIR)/metal/$(TARGET).prog.bin
@awk -v rom_capacity=$(METAL_BIN_SIZE) -v ram_capacity=$(METAL_RAM_SIZE) '\
$$1 ~ /^_(CODE|HOME|GSINIT|GSFINAL|INITIALIZER|ITABLE[0-7]*)$$/ { rom += $$5 } \
$$1 ~ /^_(DATA|INITIALIZED|BSEG|BSS|HEAP|STACK)$$/ { ram += $$5 } \
END { \
printf "\nMemory usage:\n"; \
printf " ROM: %5u / %5u bytes (%5.1f%%), %5u bytes free\n", rom, rom_capacity, 100 * rom / rom_capacity, rom_capacity - rom; \
printf " RAM: %5u / %5u bytes (%5.1f%%), %5u bytes free\n", ram, ram_capacity, 100 * ram / ram_capacity, ram_capacity - ram; \
}' $(BUILDDIR)/metal/$(TARGET).map
$(BUILDDIR)/metal/$(TARGET).ihx: $(METAL_RELFILES)
@mkdir -p $(@D)
$(METAL_CC) $(METAL_FLAGS) -o $@ $(METAL_RELFILES)
$(BUILDDIR)/metal/$(TARGET).bin: $(BUILDDIR)/metal/$(TARGET).ihx
$(METAL_MAKEBIN) -s $(METAL_BIN_SIZE) $< $@
$(BUILDDIR)/metal/$(TARGET).prog.bin: $(BUILDDIR)/metal/$(TARGET).ihx
$(METAL_MAKEBIN) -p $< $@
$(BUILDDIR)/metal/%.rel: %.c
@mkdir -p $(@D)
$(METAL_CC) $(METAL_FLAGS) $(METAL_CPPFLAGS) -o $@ -c $<
$(BUILDDIR)/metal/%.rel: %.asm
@mkdir -p $(@D)
$(METAL_AS) -plosgff -o $@ $<
clean:
rm -rf $(BUILDDIR)
-include $(EMU_DEPFILES)
+175
View File
@@ -0,0 +1,175 @@
STACK_TOP = 0xFFFF
DELAY_LOOPS_PER_MS = 40
.globl _main
.globl _delay_ms
.globl _io_out
.globl _io_in
.globl _enable_interrupts
.globl _disable_interrupts
.globl s__DATA
.globl l__DATA
.globl s__INITIALIZER
.globl s__INITIALIZED
.globl l__INITIALIZER
.area _ITABLE (ABS)
.org 0x00
jp init
.org 0x08
reti
.org 0x10
reti
.org 0x18
reti
.org 0x20
reti
.org 0x28
reti
.org 0x30
reti
.org 0x38
reti
; ITABLE
.org 0x40
.dw ctc_isr ; CTC channel 0: vector 0x40
.dw ctc_isr ; CTC channel 1: vector 0x42
.dw ctc_isr ; CTC channel 2: vector 0x44
.dw uart_isr ; CTC channel 3: vector 0x46
.area _HOME
init:
di
ld sp, #STACK_TOP
call gsinit
; Setup interrupt mode 2
xor a
ld i, a
im 2
call _main
hang:
jr hang
.area _GSINIT
gsinit:
ld bc, #l__INITIALIZER
ld a, b
or c
jr z, zero_data
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
zero_data:
ld bc, #l__DATA
ld a, b
or c
jr z, gsinit_done
ld hl, #s__DATA
xor a
clear_loop:
xor a ; BC loop test below changes A
ld (hl), a
inc hl
dec bc
ld a, b
or c
jr nz, clear_loop
gsinit_done:
.area _GSFINAL
ret
.area _CODE
.macro MAKE_ISR entry, handler
.globl handler
entry:
push af
push bc
push de
push hl
push ix
push iy
ex af, af'
push af
exx
push bc
push de
push hl
call handler
pop hl
pop de
pop bc
exx
pop af
ex af, af'
pop iy
pop ix
pop hl
pop de
pop bc
pop af
ei
reti
.endm
MAKE_ISR ctc_isr, _ctc_interrupt
MAKE_ISR uart_isr, _uart_interrupt
_enable_interrupts::
ei
ret
_disable_interrupts::
di
ret
; Write a byte to an I/O port.
; Input: A = port address, L = data byte.
_io_out::
ld c, a
ld a, l
out (c), a
ret
; Read a byte from an I/O port.
; Input: A = port address.
; Return: A = data byte.
_io_in::
ld c, a
in a, (c)
ret
; Approximate millisecond delay.
; Input: HL = delay in milliseconds.
_delay_ms::
ld a, h
or l
ret z
delay_ms_outer$:
push hl
ld hl, #DELAY_LOOPS_PER_MS
delay_ms_inner$:
dec hl
ld a, h
or l
jr nz, delay_ms_inner$
pop hl
dec hl
ld a, h
or l
jr nz, delay_ms_outer$
ret
+10
View File
@@ -0,0 +1,10 @@
#ifndef __H_BOOT
#include <stdint.h>
void io_out(uint8_t a, uint8_t d);
uint8_t io_in(uint8_t d);
void delay_ms(uint16_t t);
void enable_interrupts();
void disable_interrupts();
#endif
+33
View File
@@ -0,0 +1,33 @@
#include "circ_buf.h"
void buffer_init(volatile circular_buffer_t* b){
b->count = 0;
b->head = 0;
b->tail = 0;
}
uint8_t buffer_is_full(volatile circular_buffer_t* b){
return b->count == BUFFER_SIZE;
}
uint8_t buffer_is_empty(volatile circular_buffer_t* b){
return b->count == 0;
}
uint8_t buffer_enqueue(volatile circular_buffer_t* b, char c){
if(buffer_is_full(b))
return 0;
b->data[b->head] = c;
b->head = (b->head+1)%BUFFER_SIZE;
b->count++;
return 1;
}
uint8_t buffer_dequeue(volatile circular_buffer_t* b, char* c){
if(buffer_is_empty(b))
return 0;
*c = b->data[b->tail];
b->tail = (b->tail+1)%BUFFER_SIZE;
b->count--;
return 1;
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef __H_CIRC_BUF
#include <stdint.h>
#define BUFFER_SIZE 5
typedef struct {
char data[BUFFER_SIZE];
uint8_t head;
uint8_t tail;
uint8_t count;
} circular_buffer_t;
void buffer_init(volatile circular_buffer_t* b);
uint8_t buffer_is_full(volatile circular_buffer_t* b);
uint8_t buffer_is_empty(volatile circular_buffer_t* b);
uint8_t buffer_enqueue(volatile circular_buffer_t* b, char c);
uint8_t buffer_dequeue(volatile circular_buffer_t* b, char* c);
#endif
+109
View File
@@ -0,0 +1,109 @@
#include <stdint.h>
#include "boot.h"
#include "circ_buf.h"
#define CPUREG 0x00
#define CPUREG_ROM_DISABLE 0x01
#define CPUREG_BANK_SELECT 0x02
#define CPUREG_BANK_ENABLE 0x04
#define CPUREG_GPO_0 0x08
#define CPUREG_GPO_1 0x10
#define CPUREG_GPO_2 0x20
#define CPUREG_GPO_3 0x40
#define CPUREG_GPO_4 0x80
#define CPUREG_GPO_MASK 0xf8
#define UART_CTC_CARD_BASE 0x20
#define CTC_BASE (UART_CTC_CARD_BASE+0x00)
#define CTC_CHANNEL_0 (CTC_BASE+0)
#define CTC_CHANNEL_1 (CTC_BASE+1)
#define CTC_CHANNEL_2 (CTC_BASE+2)
#define CTC_CHANNEL_3 (CTC_BASE+3)
#define UART_BASE (UART_CTC_CARD_BASE+0x04)
#define UART_CONTROL (UART_BASE+0)
#define UART_DATA (UART_BASE+1)
volatile uint8_t v;
volatile uint8_t uart_tx_busy;
volatile circular_buffer_t rx_buf;
volatile circular_buffer_t tx_buf;
void uart_puts(char * c){
while(*c){
// fill tx buffer till it is full
while(*c && buffer_enqueue(&tx_buf, *c)){
c++;
}
// if uart not busy kick off
if(!uart_tx_busy){
disable_interrupts();
char cc;
if(buffer_dequeue(&tx_buf, &cc)){
uart_tx_busy = 1;
io_out(UART_DATA, cc);
io_out(UART_CONTROL, 0b10110101);
}
enable_interrupts();
}
}
}
void uart_interrupt(){
uint8_t status = io_in(UART_CONTROL);
if((status&0x21)!=0){
// RDRF or OVRN
char c = io_in(UART_DATA);
if((status&0x01)!=0){
// RDRF
buffer_enqueue(&rx_buf, c);
}
}
if((status&0x02)!=0 && uart_tx_busy){
//TXRE
char c;
if(buffer_dequeue(&tx_buf, &c)){
io_out(UART_DATA, c);
}else{
io_out(UART_CONTROL, 0b10010101);
uart_tx_busy = 0;
}
}
}
void ctc_interrupt(){
}
void main(){
buffer_init(&rx_buf);
buffer_init(&tx_buf);
io_out(CTC_CHANNEL_0, 0x57); // no int, counter @ rising, value follows, soft reset, control word
io_out(CTC_CHANNEL_0, 13); // 38.462 kHz out;
io_out(CTC_CHANNEL_3, 0b11001111); // int, counter @ falling, ext trig, value follows, soft reset, control word
io_out(CTC_CHANNEL_3, 1);
io_out(CTC_CHANNEL_0, 0x00); // vector follows
io_out(CTC_CHANNEL_0, 0x40);
uart_tx_busy = 0;
io_out(UART_CONTROL, 0b00000011); // master reset
io_out(UART_CONTROL, 0b10010101); // recv int, 8N1, /16 -> 2403.85 baud
enable_interrupts();
uart_puts("Z80 monitor - v1\r\n");
uart_puts("----------------\r\n");
v = 0;
for(;;){
char c[2] = {0, 0};
while(buffer_dequeue(&rx_buf, c)){
uart_puts(c);
}
}
}