Initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
build
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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
@@ -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
@@ -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
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user