The tty interfacing via IM2 mode is a bit hackish for now. Unfortunately because
the emulation of the tu-art in Z80pack is currently extremely buggy we are
basically forced to use IM2.
Eventually we want to do IM2 properly anyway. The entire system is built around
a design that has proper interrupt handling.
With these changes sorted interrupts now work, and you can fail to find a root
file system. Next task is getting the fdc and root fs working.
-CSRCS = devtty.c devfd.c
+CSRCS = devtty.c devfd.c interrupt.c
CSRCS += devices.c main.c
ASRCS = crt0.s cromemco.s usermem.s
-ASRCS += tricks.s commonmem.s
+ASRCS += tricks.s commonmem.s vector.s
AOBJS = $(ASRCS:.s=.rel)
COBJS = $(CSRCS:.c=.rel)
It's easier to arrange this way anyhow.
TODO
-- Debug a loader (seems to work)
-- An awful lot of early kernel boot debugging
-- Interrupts properly (and IM2)
+DONE - Debug a loader (seems to work)
+IP - An awful lot of early kernel boot debugging
+HACK - Interrupts properly (and IM2)
+ not yet doing tty interrupts nicely with buffer
+ and queues
+
UARTs: TMS 5501 x 2 per board. These can do Z80 IM2 where the vector is
-determined by A7-A5 of the IRQ (used as D7-D5 of the vector)
+determined by A7-A5 of the IRQ (used as D7-D5 of the vector) or generate the
+8080 RST vectors.
R 0 Status
W 40 bank select
+
+Interrupt vectors
+24 uart1a parallel port
+28 uart1a rda
+2A uart1a tbe
+34 uart1b parallel port
+38 uart1b RDA
+3A uart1b TBE
+
+(Uart0 strapped for 8080 mode for some reason)
+C7 uart 0a timer 1
+CF uart 0a timer 2
+D7 FDC EOJ
+DF uart 0a timer 3
+E7 uart 0a RDA
+EF uart 0a TBE
+F7 uart 0a timer 4
+FF uart 0a timer 5 also RTC timer
+
+Doc actually says
+
+D7-D5 = A7-A5 of port
+D4 = 0 for A 1 for B
+D3-1
+ 000 timer 1
+ 001 timer 2
+ 010 !sensa
+ 011 timer 3
+ 100 rda
+ 101 tbe
+ 110 timer4
+ 111 timer5 (PI7)
+
.globl _need_resched
.globl _ssig
+ .globl _set_irq
+ .globl _spurious
+ .globl _irqvec
+ .globl interrupt_high
+
.globl outcharhex
.globl outhl, outde, outbc
.globl outnewline
; -----------------------------------------------------------------------------
.area _COMMONMEM
+;
+; Must be page aligned
+;
+_irqvec:
+ ; 0
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 16
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 32
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 48
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 64
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 80
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 96
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 112
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 128
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 144
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 160
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 176
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 192
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 208
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 224
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 240
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ .word _spurious
+ ; 256
+ .byte 0
+
+
_platform_reboot:
_platform_monitor:
jr _platform_monitor
.area _CODE
init_early:
+ ret
+
+init_hardware:
+ ; set system RAM size
+ ld hl, #448 ; Assuming fully loaded for now
+ ld (_ramsize), hl
+ ld hl, #(448-64) ; 64K for kernel
+ ld (_procmem), hl
+
ld a,#0x81 ; Every memory writeable, read kernel
out (0x40),a ; MMU set
ld bc,#0x67
ldir
- ; And the common across all banks
- ld hl,#s__COMMONMEM
- ld d,h
- ld e,l
- ld bc,#l__COMMONMEM
- ldir
-
ld a,#0x01 ; bank to the kernel bank
out (0x40),a
- ret
-
-init_hardware:
- ; set system RAM size
- ld hl, #448 ; Assuming fully loaded for now
- ld (_ramsize), hl
- ld hl, #(448-64) ; 64K for kernel
- ld (_procmem), hl
+ ld a,#0x40 ; enable interrupt mode
+ out (2),a
+ ld a,#0x70
+ out (3),a ; serial 0 timer 4 tbe rda
ld a, #156 ; ticks for 10Hz (9984uS per tick)
out (8), a ; 10Hz timer on
- im 1 ; really should use a page and im2?
+ ld hl,#_irqvec
+ ld a,h ; deal with linker limits
+ ld i,a
+ im 2
ret
_program_vectors:
ret
+;
+; Called when we get an unexpected vector - just ack and return
+;
+_spurious: ; unexpected IRQ vector - handy breakpoint
+ ei
+ reti
+
+;
+; Set an interrupt table entry. Done in asm as we want to write it
+; through to all the banks.
+;
+_set_irq:
+ pop hl
+ pop de
+ pop bc
+ push bc
+ push de
+ push hl
+ ld hl,#_irqvec
+ di
+ add hl,de
+ ld a,#0x81
+ out (0x40),a ; write to all banks, read kernel
+ ld (hl),c
+ inc hl
+ ld (hl),b
+ ld a,#0x01
+ out (0x40),a ; kernel mapping back
+ ld a,(_int_disabled)
+ or a
+ ret nz
+ ei
+ ret
+
; outchar: Wait for UART TX idle, then print the char in A
; destroys: AF
outchar:
; and leap into user space
jp (hl)
nop
- nop
rst20: ret
nop
nop
#include <stdbool.h>
#include <tty.h>
#include <devtty.h>
+#include <irq.h>
char tbuf1[TTYSIZ];
char tbuf2[TTYSIZ];
{
}
-__sfr __at 0x08 timer4;
-
void tty_irq(uint8_t minor)
{
- uint8_t r = ttybase[minor];
- uint8_t op;
- while((op = in(r + 3)) != 0xFF) {
- switch(op) {
- case 0xE7:
- /* should check in(r) for error bits */
- tty_inproc(minor, in(r + 1 ));
- case 0xEF:
- ttypoll &= ~(1 << minor);
- wakeup(&ttydata[minor]);
- case 0xF7:
- if (minor == 1) {
- timer_interrupt();
- timer4 = 156;
- }
- }
+ if (minor != 1)
+ return; /* For the moment */
+ if (rx0a_int) {
+ uint8_t c = rx0a_char;
+ rx0a_int = 0;
+ tty_inproc(1, rx0a_char);
+ }
+ if (tx0a_int) {
+ tx0a_int = 0;
+ ttypoll &= ~(1 << 1);
+ wakeup(&ttydata[1]);
}
}
platform-cromemco/crt0.rel
platform-cromemco/commonmem.rel
platform-cromemco/cromemco.rel
+platform-cromemco/vector.rel
platform-cromemco/main.rel
+platform-cromemco/interrupt.rel
start.rel
version.rel
lowlevel-z80-thunked.rel
--- /dev/null
+#include <kernel.h>
+#include <irq.h>
+
+static uint8_t irqbusy(uint8_t vec)
+{
+ uint16_t *p = (uint16_t *)(irqvec + vec);
+ if (*p != (uint16_t)spurious)
+ return 1;
+ return 0;
+}
+
+static uint8_t irqcheck(uint8_t vec)
+{
+ if (irqbusy(vec & 0xFE))
+ return 1;
+ /* If it's odd and not 255 then check the vector above is also free */
+ if ((vec & 1) && ++vec && irqbusy(vec))
+ return 1;
+ return 0;
+}
+
+int request_irq(uint8_t vec, void (*func)(uint8_t))
+{
+ /* This is fun because the Cromemco has uart 0 set to use the 8080 mode
+ vectors which are odd numbers */
+ if (irqcheck(vec))
+ return -EBUSY;
+ set_irq(vec, func);
+ return 0;
+}
+
+void free_irq(uint8_t vec)
+{
+ if (!irqcheck(vec))
+ panic("free_irq");
+ set_irq(vec, spurious);
+}
+
--- /dev/null
+extern int request_irq(uint8_t vec, void (*func)(uint8_t));
+extern void free_irq(uint8_t vec);
+
+/* These have to live in common space */
+extern uint8_t irqvec[];
+extern void spurious(uint8_t vec);
+extern void set_irq(uint16_t vec, void (*func)(uint8_t));
+
+extern void uart0a_rx(uint8_t unused);
+extern void uart0a_txdone(uint8_t unused);
+extern void uart0a_timer4(uint8_t unused);
+
+extern uint8_t rx0a_int, tx0a_int, rx0a_char;
#include <kernel.h>
#include <devtty.h>
#include <printf.h>
+#include <irq.h>
uaddr_t ramtop = PROGTOP;
void platform_idle(void)
{
- /* halt ?? */
+ __asm
+ halt
+ __endasm;
}
+__sfr __at 0x08 timer4;
+
void platform_interrupt(void)
{
+ timer4 = 156;
tty_irq(1);
- tty_irq(2);
- tty_irq(3);
+ timer_interrupt();
+// tty_irq(2);
+// tty_irq(3);
}
-/* Nothing to do for the map of init */
+/* Get this into discard ... */
+
+/* Nothing to do for the map of init but we do set our vectors up here */
void map_init(void)
{
+ if (request_irq(0xE7, uart0a_rx) |
+ request_irq(0xEF, uart0a_txdone) |
+ request_irq(0xF7, uart0a_timer4))
+ panic("irqset");
+ /* We need to claim these in case we set one off as they are at odd vectors
+ as the base tu_uart is strapped for 8080 mode */
+ if (
+ request_irq(0xC7, spurious) |
+ request_irq(0xCF, spurious) |
+ request_irq(0xD7, spurious) |
+ request_irq(0xDF, spurious) |
+ request_irq(0xFF, spurious)
+ )
+ panic("irqset2");
}
uint8_t platform_param(char *p)
--- /dev/null
+export BANKED=-thunked
--- /dev/null
+;
+; We have a custom implementation of usermem. We really need
+; to optimize ldir_to/from_user.
+;
+ .module usermem
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+ ; exported symbols
+ .globl __uget
+ .globl __ugetc
+ .globl __ugetw
+
+ .globl __uput
+ .globl __uputc
+ .globl __uputw
+ .globl __uzero
+
+ .globl ldir_from_user
+ .globl ldir_to_user
+
+ .area _COMMONMEM
+
+
+uputget:
+ ; load DE with the byte count
+ ld c, 8(ix) ; byte count
+ ld b, 9(ix)
+ ld a, b
+ or c
+ ret z ; no work
+ ; load HL with the source address
+ ld l, 4(ix) ; src address
+ ld h, 5(ix)
+ ; load DE with destination address
+ ld e, 6(ix)
+ ld d, 7(ix)
+ ret
+
+__uget:
+ push ix
+ ld ix,#0
+ add ix, sp
+ call uputget
+ jr z, uget_out
+ push de
+ pop ix
+ call ldir_from_user
+uget_out:
+ pop ix
+ ld hl,#0
+ ret
+
+__uput:
+ push ix
+ ld ix,#0
+ add ix,sp
+ call uputget
+ jr z, uget_out
+ push de
+ pop ix
+ call ldir_to_user
+ jr uget_out
+
+;
+; The kernel IRQ code will restore the bank if it interrupts this
+; logic
+;
+__uzero:
+ pop de
+ pop hl
+ pop bc
+ push bc
+ push hl
+ push de
+ ld a,b
+ or c
+ ret z
+ ld a,(U_DATA__U_PAGE)
+ out (0x40),a ; user bank
+ ld (hl),#0
+ dec bc
+ ld a,b
+ or c
+ jr z, uout
+ ld e,l
+ ld d,h
+ inc de
+ ldir
+uout:
+ ld a,#1
+ out (0x40),a
+ ret
+
+__ugetc:
+ pop bc
+ pop hl
+ push hl
+ push bc
+ ld a,(U_DATA__U_PAGE)
+ out (0x40),a
+ ld l,(hl)
+ ld h,#0
+ jr uout
+
+__ugetw:
+ pop bc
+ pop hl
+ push hl
+ push bc
+ ld a,(U_DATA__U_PAGE)
+ out (0x40),a
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a
+ jr uout
+
+__uputc:
+ pop bc
+ pop de
+ pop hl
+ push hl
+ push de
+ push bc
+ ld a,(U_DATA__U_PAGE)
+ out (0x40),a
+ ld (hl),e
+ jr uout
+
+__uputw:
+ pop bc
+ pop de
+ pop hl
+ push hl
+ push de
+ push bc
+ ld a,(U_DATA__U_PAGE)
+ out (0x40),a
+ ld (hl),e
+ inc hl
+ ld (hl),d
+ jr uout
--- /dev/null
+;
+; Interrupt handlers. These will get replaced by some proper FIFO
+; logic
+;
+
+ .area _COMMONMEM
+ .module vector
+
+ .globl _rx0a_char
+ .globl _rx0a_int
+ .globl _tx0a_int
+ .globl interrupt_high
+
+ .globl _uart0a_rx
+ .globl _uart0a_txdone
+ .globl _uart0a_timer4
+
+_uart0a_rx: ; 0xE7
+ push af
+ push bc
+ push de
+ ld bc,#0x8140
+ in d,(c)
+ out (c),b
+ in a,(1)
+ ld (_rx0a_char),a
+ ld a,#1
+ ld (_rx0a_int),a
+ out (c),d
+ pop de
+ pop bc
+ pop af
+ ei
+ reti
+
+_uart0a_txdone: ; 0xEF
+ push af
+ push bc
+ push de
+ ld bc,#0x8140
+ in d,(c)
+ out (c),b
+ ld a,#1
+ ld (_tx0a_int),a
+ out (c),d
+ pop de
+ pop bc
+ pop af
+ ei
+ reti
+
+_uart0a_timer4:
+ jp interrupt_high
+
+_rx0a_char:
+ .byte 0
+_rx0a_int:
+ .byte 0
+_tx0a_int:
+ .byte 0