mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-26 04:58:06 +11:00
Compare commits
No commits in common. "69daf49920c77821c78b747bb59ea5e3ad0409ac" and "8a696a1e23cc94f4db71489cfb39d0cf9e2747a8" have entirely different histories.
69daf49920
...
8a696a1e23
16
TRICKS.txt
16
TRICKS.txt
@ -1,5 +1,5 @@
|
||||
This file describe tricks and conventions that are used throughout the code and
|
||||
might need explanation.
|
||||
This file describe tricks that are used throughout the code and might need
|
||||
explanation.
|
||||
|
||||
or a: Equivalent to "cp 0", but results in a shorter opcode.
|
||||
|
||||
@ -13,15 +13,3 @@ Reset for failure. "xor a" (destroys A) and "cp a" (preserves A) are used to
|
||||
ensure Z is set. To ensure that it is reset, it's a bit more complicated and
|
||||
"unsetZ" routine exists for that, although that in certain circumstances,
|
||||
"inc a \ dec a" or "or a" can work.
|
||||
|
||||
z80 is little endian in its 16-bit loading operations. For example, "ld hl, (0)"
|
||||
will load the contents of memory address 0 in L and memory address 1 in H. This
|
||||
little-endianess is followed by Collapse OS in most situations. When it's not,
|
||||
it's specified in comments.
|
||||
|
||||
This get a bit awkward with regards to 32-bit. There are no "native" z80 32-bit
|
||||
operations, so z80 doesn't mandate an alignment. In Collapse OS, 32-bit numbers
|
||||
are stored as "big endian pair of little endian 16-bit numbers". For example,
|
||||
if "ld dehl, (0)" existed and if the first 4 bytes of memory were 0x01, 0x02,
|
||||
0x03 and 0x04, then DE (being the "high" word) would be 0x0201 and HL would be
|
||||
0x0403.
|
||||
|
@ -7,43 +7,47 @@ look like:
|
||||
|
||||
|
||||
; The RAM module is selected on A15, so it has the range 0x8000-0xffff
|
||||
.equ RAMSTART 0x8000
|
||||
.equ RAMEND 0xffff
|
||||
.equ ACIA_CTL 0x80 ; Control and status. RS off.
|
||||
.equ ACIA_IO 0x81 ; Transmit. RS on.
|
||||
.equ RAMSTART 0x8000
|
||||
.equ RAMEND 0xffff
|
||||
.equ ACIA_CTL 0x80 ; Control and status. RS off.
|
||||
.equ ACIA_IO 0x81 ; Transmit. RS on.
|
||||
|
||||
jp init
|
||||
jr init
|
||||
|
||||
; interrupt hook
|
||||
.fill 0x38-$
|
||||
jp aciaInt
|
||||
|
||||
.inc "err.h"
|
||||
.inc "core.asm"
|
||||
.inc "parse.asm"
|
||||
.equ ACIA_RAMSTART RAMSTART
|
||||
.inc "acia.asm"
|
||||
|
||||
.equ STDIO_RAMSTART ACIA_RAMEND
|
||||
.equ STDIO_GETC aciaGetC
|
||||
.equ STDIO_PUTC aciaPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ SHELL_RAMSTART STDIO_RAMEND
|
||||
.equ SHELL_EXTRA_CMD_COUNT 0
|
||||
.inc "shell.asm"
|
||||
jp aciaInt
|
||||
|
||||
init:
|
||||
di
|
||||
; setup stack
|
||||
ld hl, RAMEND
|
||||
ld sp, hl
|
||||
ld hl, RAMEND
|
||||
ld sp, hl
|
||||
im 1
|
||||
|
||||
call aciaInit
|
||||
call shellInit
|
||||
call aciaInit
|
||||
xor a
|
||||
ld de, BLOCKDEV_SEL
|
||||
call blkSel
|
||||
call stdioInit
|
||||
call shellInit
|
||||
ei
|
||||
jp shellLoop
|
||||
jp shellLoop
|
||||
|
||||
#include "core.asm"
|
||||
.equ ACIA_RAMSTART RAMSTART
|
||||
#include "acia.asm"
|
||||
.equ BLOCKDEV_RAMSTART ACIA_RAMEND
|
||||
.equ BLOCKDEV_COUNT 1
|
||||
#include "blockdev.asm"
|
||||
; List of devices
|
||||
.dw aciaGetC, aciaPutC, 0, 0
|
||||
|
||||
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
|
||||
#include "stdio.asm"
|
||||
|
||||
.equ SHELL_RAMSTART STDIO_RAMEND
|
||||
.equ SHELL_EXTRA_CMD_COUNT 0
|
||||
#include "shell.asm"
|
||||
|
||||
Once this is written, building it is easy:
|
||||
|
||||
|
@ -98,17 +98,17 @@ aciaInt:
|
||||
reti
|
||||
|
||||
|
||||
; *** STDIO ***
|
||||
; These function below follow the stdio API.
|
||||
; *** BLOCKDEV ***
|
||||
; These function below follow the blockdev API.
|
||||
|
||||
aciaGetC:
|
||||
push de
|
||||
.loop:
|
||||
|
||||
ld a, (ACIA_BUFWRIDX)
|
||||
ld e, a
|
||||
ld a, (ACIA_BUFRDIDX)
|
||||
cp e
|
||||
jr z, .loop ; equal? nothing to read. loop
|
||||
jr z, .nothingToRead ; equal? nothing to read.
|
||||
|
||||
; Alrighty, buffer not empty. let's read.
|
||||
ld de, ACIA_BUF
|
||||
@ -120,6 +120,12 @@ aciaGetC:
|
||||
|
||||
; And finally, fetch the value.
|
||||
ld a, (de)
|
||||
cp a ; ensure Z
|
||||
jr .end
|
||||
|
||||
.nothingToRead:
|
||||
call unsetZ
|
||||
.end:
|
||||
pop de
|
||||
ret
|
||||
|
||||
|
@ -35,10 +35,6 @@
|
||||
; number of entries in shellCmdTbl
|
||||
.equ SHELL_CMD_COUNT 6+SHELL_EXTRA_CMD_COUNT
|
||||
|
||||
; maximum length for shell commands. Should be confortably below stdio's
|
||||
; readline buffer length.
|
||||
.equ SHELL_MAX_CMD_LEN 0x10
|
||||
|
||||
; *** VARIABLES ***
|
||||
; Memory address that the shell is currently "pointing at" for peek, load, call
|
||||
; operations. Set with mptr.
|
||||
@ -46,12 +42,14 @@
|
||||
|
||||
; Places where we store arguments specifiers and where resulting values are
|
||||
; written to after parsing.
|
||||
.equ SHELL_CMD_ARGS @+2
|
||||
.equ SHELL_CMD_ARGS SHELL_MEM_PTR+2
|
||||
|
||||
; Pointer to a hook to call when a cmd name isn't found
|
||||
.equ SHELL_CMDHOOK @+PARSE_ARG_MAXCOUNT
|
||||
.equ SHELL_CMDHOOK SHELL_CMD_ARGS+PARSE_ARG_MAXCOUNT
|
||||
|
||||
.equ SHELL_RAMEND @+2
|
||||
; Pointer to a routine to call at each shell loop iteration
|
||||
.equ SHELL_LOOPHOOK SHELL_CMDHOOK+2
|
||||
.equ SHELL_RAMEND SHELL_LOOPHOOK+2
|
||||
|
||||
; *** CODE ***
|
||||
shellInit:
|
||||
@ -60,6 +58,7 @@ shellInit:
|
||||
ld (SHELL_MEM_PTR+1), a
|
||||
ld hl, noop
|
||||
ld (SHELL_CMDHOOK), hl
|
||||
ld (SHELL_LOOPHOOK), hl
|
||||
|
||||
; print welcome
|
||||
ld hl, .welcome
|
||||
@ -71,8 +70,15 @@ shellInit:
|
||||
; Inifite loop that processes input. Because it's infinite, you should jump
|
||||
; to it rather than call it. Saves two precious bytes in the stack.
|
||||
shellLoop:
|
||||
call stdioReadLine
|
||||
; First, call the loop hook
|
||||
ld ix, (SHELL_LOOPHOOK)
|
||||
call callIX
|
||||
; Then, let's wait until something is typed.
|
||||
call stdioReadC
|
||||
jr nz, shellLoop ; not done? loop
|
||||
; We're done. Process line.
|
||||
call printcrlf
|
||||
call stdioGetLine
|
||||
call shellParse
|
||||
ld hl, .prompt
|
||||
call printstr
|
||||
@ -86,7 +92,7 @@ shellParse:
|
||||
; first thing: is command empty?
|
||||
ld a, (hl)
|
||||
or a
|
||||
ret z ; empty, nothing to do
|
||||
ret z ; empty, nthing to do
|
||||
|
||||
push af
|
||||
push bc
|
||||
@ -104,13 +110,6 @@ shellParse:
|
||||
; no arg, (HL) is zero to facilitate processing later, add a second
|
||||
; null next to that one to indicate unambiguously that we have no args.
|
||||
inc hl
|
||||
; Oh wait, before we proceed, is our cmd length within limits? cmd len
|
||||
; is currently in A from findchar
|
||||
cp SHELL_MAX_CMD_LEN
|
||||
jr c, .hasArgs ; within limits
|
||||
; outside limits
|
||||
ld a, SHELL_ERR_UNKNOWN_CMD
|
||||
jr .error
|
||||
.hasArgs:
|
||||
xor a
|
||||
ld (hl), a
|
||||
|
@ -11,9 +11,6 @@
|
||||
; The space character is the first among special chars.
|
||||
; * C confirms letter selection
|
||||
;
|
||||
; This module is currently hard-wired to sms/vdp, that is, it calls vdp's
|
||||
; routines during padGetC to update character selection.
|
||||
;
|
||||
; *** Consts ***
|
||||
;
|
||||
.equ PAD_CTLPORT 0x3f
|
||||
@ -32,18 +29,21 @@
|
||||
;
|
||||
; Button status of last padUpdateSel call. Used for debouncing.
|
||||
.equ PAD_SELSTAT PAD_RAMSTART
|
||||
; Button status of last padGetC call.
|
||||
.equ PAD_GETCSTAT PAD_SELSTAT+1
|
||||
; Current selected character
|
||||
.equ PAD_SELCHR @+1
|
||||
.equ PAD_SELCHR PAD_GETCSTAT+1
|
||||
; When non-zero, will be the next char returned in GetC. So far, only used for
|
||||
; LF that is feeded when Start is pressed.
|
||||
.equ PAD_NEXTCHR @+1
|
||||
.equ PAD_RAMEND @+1
|
||||
.equ PAD_NEXTCHR PAD_SELCHR+1
|
||||
.equ PAD_RAMEND PAD_NEXTCHR+1
|
||||
|
||||
; *** Code ***
|
||||
|
||||
padInit:
|
||||
ld a, 0xff
|
||||
ld (PAD_SELSTAT), a
|
||||
ld (PAD_GETCSTAT), a
|
||||
xor a
|
||||
ld (PAD_NEXTCHR), a
|
||||
ld a, 'a'
|
||||
@ -78,14 +78,14 @@ padStatus:
|
||||
ret
|
||||
|
||||
; From a pad status in A, update current char selection and return it.
|
||||
; Sets Z if current selection was unchanged, unset if changed.
|
||||
; Returns the same Z as padStatus: set if unchanged, unset if changed
|
||||
padUpdateSel:
|
||||
call padStatus
|
||||
push hl ; --> lvl 1
|
||||
push hl
|
||||
ld hl, PAD_SELSTAT
|
||||
cp (hl)
|
||||
ld (hl), a
|
||||
pop hl ; <-- lvl 1
|
||||
pop hl
|
||||
jr z, .nothing ; nothing changed
|
||||
bit PAD_UP, a
|
||||
jr z, .up
|
||||
@ -156,51 +156,45 @@ padUpdateSel:
|
||||
ld (PAD_SELCHR), a
|
||||
jp unsetZ
|
||||
.nothing:
|
||||
; Z already set
|
||||
cp a ; ensure Z
|
||||
ld a, (PAD_SELCHR)
|
||||
ret
|
||||
|
||||
; Repeatedly poll the pad for input and returns the resulting "input char".
|
||||
; This routine takes a long time to return because it waits until C, B or Start
|
||||
; was pressed. Until this is done, this routine takes care of updating the
|
||||
; "current selection" directly in the VDP.
|
||||
padGetC:
|
||||
ld a, (PAD_NEXTCHR)
|
||||
or a
|
||||
jr nz, .nextchr
|
||||
call padUpdateSel
|
||||
jp z, padGetC ; nothing changed, loop
|
||||
; pad status was changed, let's see if an action button was pressed
|
||||
ld a, (PAD_SELSTAT)
|
||||
call padStatus
|
||||
push hl
|
||||
ld hl, PAD_GETCSTAT
|
||||
cp (hl)
|
||||
ld (hl), a
|
||||
pop hl
|
||||
jp z, unsetZ ; nothing changed
|
||||
bit PAD_BUTC, a
|
||||
jr z, .advance
|
||||
bit PAD_BUTA, a
|
||||
jr z, .backspace
|
||||
bit PAD_START, a
|
||||
jr z, .return
|
||||
; no action button pressed, but because our pad status changed, update
|
||||
; VDP before looping.
|
||||
ld a, (PAD_SELCHR)
|
||||
call vdpConv
|
||||
call vdpSpitC
|
||||
jp padGetC
|
||||
jp unsetZ
|
||||
.return:
|
||||
ld a, ASCII_LF
|
||||
ld (PAD_NEXTCHR), a
|
||||
; continue to .advance
|
||||
.advance:
|
||||
ld a, (PAD_SELCHR)
|
||||
; Z was already set from previous BIT instruction
|
||||
cp a
|
||||
ret
|
||||
.backspace:
|
||||
ld a, ASCII_BS
|
||||
; Z was already set from previous BIT instruction
|
||||
cp a
|
||||
ret
|
||||
.nextchr:
|
||||
; We have a "next char", return it and clear it.
|
||||
cp a ; ensure Z
|
||||
ex af, af'
|
||||
push af
|
||||
xor a
|
||||
ld (PAD_NEXTCHR), a
|
||||
ex af, af'
|
||||
pop af
|
||||
ret
|
||||
|
@ -21,8 +21,11 @@
|
||||
; Row of cursor
|
||||
.equ VDP_ROW VDP_RAMSTART
|
||||
; Line of cursor
|
||||
.equ VDP_LINE @+1
|
||||
.equ VDP_RAMEND @+1
|
||||
.equ VDP_LINE VDP_ROW+1
|
||||
; Returns, in A, the currently selected char in a "pad char selection" scheme.
|
||||
.equ VDP_CHRSELHOOK VDP_LINE+1
|
||||
.equ VDP_LASTSEL VDP_CHRSELHOOK+2
|
||||
.equ VDP_RAMEND VDP_LASTSEL+1
|
||||
|
||||
; *** Code ***
|
||||
|
||||
@ -30,6 +33,9 @@ vdpInit:
|
||||
xor a
|
||||
ld (VDP_ROW), a
|
||||
ld (VDP_LINE), a
|
||||
ld (VDP_LASTSEL), a
|
||||
ld hl, noop
|
||||
ld (VDP_CHRSELHOOK), hl
|
||||
|
||||
ld hl, vdpInitData
|
||||
ld b, vdpInitDataEnd-vdpInitData
|
||||
@ -115,6 +121,12 @@ vdpSpitC:
|
||||
ret
|
||||
|
||||
vdpPutC:
|
||||
; First, let's invalidate last sel
|
||||
ex af, af'
|
||||
xor a
|
||||
ld (VDP_LASTSEL), a
|
||||
ex af, af'
|
||||
|
||||
; Then, let's place our cursor. We need to first send our LSB, whose
|
||||
; 6 low bits contain our row*2 (each tile is 2 bytes wide) and high
|
||||
; 2 bits are the two low bits of our line
|
||||
@ -255,6 +267,26 @@ vdpConv:
|
||||
ld a, 0x5e
|
||||
ret
|
||||
|
||||
; During the shell loop, updates the currently selected char, if appropriate
|
||||
vdpShellLoopHook:
|
||||
push af
|
||||
push ix
|
||||
push hl
|
||||
xor a
|
||||
ld ix, (VDP_CHRSELHOOK)
|
||||
call callIX
|
||||
ld hl, VDP_LASTSEL
|
||||
cp (hl)
|
||||
jr z, .noChange
|
||||
; selection changed
|
||||
call vdpConv
|
||||
call vdpSpitC
|
||||
.noChange:
|
||||
pop hl
|
||||
pop ix
|
||||
pop af
|
||||
ret
|
||||
|
||||
vdpPaletteData:
|
||||
.db 0x00,0x3f
|
||||
vdpPaletteDataEnd:
|
||||
|
164
kernel/stdio.asm
164
kernel/stdio.asm
@ -4,26 +4,6 @@
|
||||
; in", that is, the console through which the user is connected in a decoupled
|
||||
; manner.
|
||||
;
|
||||
; Those GetC/PutC routines are hooked through defines and have this API:
|
||||
;
|
||||
; GetC: Blocks until a character is read from the device and return that
|
||||
; character in A.
|
||||
;
|
||||
; PutC: Write character specified in A onto the device.
|
||||
;
|
||||
; *** Accepted characters ***
|
||||
;
|
||||
; For now, we're in muddy waters in this regard. We try to stay close to ASCII.
|
||||
; Anything over 0x7f is undefined. Both CR and LF are interpreted as "line end".
|
||||
; Both BS and DEL mean "delete previous character".
|
||||
;
|
||||
; When outputting, newlines are marked by CR and LF. Outputting a character
|
||||
; deletion is made through BS then space then BS.
|
||||
;
|
||||
; *** Defines ***
|
||||
; STDIO_GETC: address of a GetC routine
|
||||
; STDIO_PUTC: address of a PutC routine
|
||||
;
|
||||
; *** Consts ***
|
||||
; Size of the readline buffer. If a typed line reaches this size, the line is
|
||||
; flushed immediately (same as pressing return).
|
||||
@ -32,19 +12,34 @@
|
||||
; *** Variables ***
|
||||
; Used to store formatted hex values just before printing it.
|
||||
.equ STDIO_HEX_FMT STDIO_RAMSTART
|
||||
.equ STDIO_GETC STDIO_HEX_FMT+2
|
||||
.equ STDIO_PUTC STDIO_GETC+2
|
||||
|
||||
; Line buffer. We read types chars into this buffer until return is pressed
|
||||
; This buffer is null-terminated.
|
||||
.equ STDIO_BUF @+2
|
||||
; This buffer is null-terminated and we don't keep an index around: we look
|
||||
; for the null-termination every time we write to it. Simpler that way.
|
||||
.equ STDIO_BUF STDIO_PUTC+2
|
||||
|
||||
; Index where the next char will go in stdioGetC.
|
||||
.equ STDIO_RAMEND @+STDIO_BUFSIZE
|
||||
.equ STDIO_BUFIDX STDIO_BUF+STDIO_BUFSIZE
|
||||
.equ STDIO_RAMEND STDIO_BUFIDX+1
|
||||
|
||||
; Sets GetC to the routine where HL points to and PutC to DE.
|
||||
stdioInit:
|
||||
ld (STDIO_GETC), hl
|
||||
ld (STDIO_PUTC), de
|
||||
xor a
|
||||
ld (STDIO_BUF), a
|
||||
ld (STDIO_BUFIDX), a
|
||||
ret
|
||||
|
||||
stdioGetC:
|
||||
jp STDIO_GETC
|
||||
ld ix, (STDIO_GETC)
|
||||
jp (ix)
|
||||
|
||||
stdioPutC:
|
||||
jp STDIO_PUTC
|
||||
ld ix, (STDIO_PUTC)
|
||||
jp (ix)
|
||||
|
||||
; print null-terminated string pointed to by HL
|
||||
printstr:
|
||||
@ -55,7 +50,7 @@ printstr:
|
||||
ld a, (hl) ; load character to send
|
||||
or a ; is it zero?
|
||||
jr z, .end ; if yes, we're finished
|
||||
call STDIO_PUTC
|
||||
call stdioPutC
|
||||
inc hl
|
||||
jr .loop
|
||||
|
||||
@ -70,7 +65,7 @@ printnstr:
|
||||
push hl
|
||||
.loop:
|
||||
ld a, (hl) ; load character to send
|
||||
call STDIO_PUTC
|
||||
call stdioPutC
|
||||
inc hl
|
||||
djnz .loop
|
||||
|
||||
@ -82,9 +77,9 @@ printnstr:
|
||||
printcrlf:
|
||||
push af
|
||||
ld a, ASCII_CR
|
||||
call STDIO_PUTC
|
||||
call stdioPutC
|
||||
ld a, ASCII_LF
|
||||
call STDIO_PUTC
|
||||
call stdioPutC
|
||||
pop af
|
||||
ret
|
||||
|
||||
@ -110,19 +105,21 @@ printHexPair:
|
||||
pop af
|
||||
ret
|
||||
|
||||
; Repeatedly calls stdioGetC until a whole line was read, that is, when CR or
|
||||
; LF is read or if the buffer is full. Sets HL to the beginning of the read
|
||||
; line, which is null-terminated.
|
||||
; Call stdioGetC and put the result in the buffer. Sets Z according to whether
|
||||
; the buffer is "complete", that is, whether CR or LF have been pressed or if
|
||||
; the the buffer is full. Z is set if the line is "complete", unset if not.
|
||||
; The next call to stdioReadC after a completed line will start a new line.
|
||||
;
|
||||
; This routine also takes care of echoing received characters back to the TTY.
|
||||
; It also manages backspaces properly.
|
||||
stdioReadLine:
|
||||
push bc
|
||||
ld hl, STDIO_BUF
|
||||
ld b, STDIO_BUFSIZE-1
|
||||
.loop:
|
||||
;
|
||||
; This routine doesn't wait after a typed char. If nothing is typed, we return
|
||||
; immediately with Z flag unset.
|
||||
;
|
||||
; Note that this routine doesn't bother returning the typed character.
|
||||
stdioReadC:
|
||||
; Let's wait until something is typed.
|
||||
call STDIO_GETC
|
||||
call stdioGetC
|
||||
ret nz ; nothing typed? nothing to do
|
||||
; got it. Now, is it a CR or LF?
|
||||
cp ASCII_CR
|
||||
jr z, .complete ; char is CR? buffer complete!
|
||||
@ -134,37 +131,86 @@ stdioReadLine:
|
||||
jr z, .delchr
|
||||
|
||||
; Echo the received character right away so that we see what we type
|
||||
call STDIO_PUTC
|
||||
call stdioPutC
|
||||
|
||||
; Ok, gotta add it do the buffer
|
||||
; save char for later
|
||||
ex af, af'
|
||||
ld a, (STDIO_BUFIDX)
|
||||
push hl ; --> lvl 1
|
||||
ld hl, STDIO_BUF
|
||||
; make HL point to dest spot
|
||||
call addHL
|
||||
; Write our char down
|
||||
ex af, af'
|
||||
ld (hl), a
|
||||
; follow up with a null char
|
||||
inc hl
|
||||
djnz .loop
|
||||
; buffer overflow, complete line
|
||||
.complete:
|
||||
; The line in our buffer is complete.
|
||||
; Let's null-terminate it and return.
|
||||
xor a
|
||||
ld (hl), a
|
||||
ld hl, STDIO_BUF
|
||||
pop bc
|
||||
pop hl ; <-- lvl 1
|
||||
; inc idx, which still is in AF'
|
||||
ex af, af'
|
||||
inc a
|
||||
cp STDIO_BUFSIZE-1 ; -1 is because we always want to keep our
|
||||
; last char at zero.
|
||||
jr z, .complete ; end of buffer reached? buffer is full.
|
||||
|
||||
; not complete. save idx back
|
||||
ld (STDIO_BUFIDX), a
|
||||
; Z already unset
|
||||
ret
|
||||
|
||||
.complete:
|
||||
; The line in our buffer is complete.
|
||||
; But before we do that, let's take care of a special case: the empty
|
||||
; line. If we didn't add any character since the last "complete", then
|
||||
; our buffer's content is the content from the last time. Let's set this
|
||||
; to an empty string.
|
||||
ld a, (STDIO_BUFIDX)
|
||||
or a
|
||||
jr nz, .completeSkip
|
||||
ld (STDIO_BUF), a
|
||||
.completeSkip:
|
||||
xor a ; sets Z
|
||||
ld (STDIO_BUFIDX), a
|
||||
ret
|
||||
|
||||
.delchr:
|
||||
; Deleting is a tricky business. We have to decrease HL and increase B
|
||||
; so that everything stays consistent. We also have to make sure that
|
||||
; We don't do buffer underflows.
|
||||
ld a, b
|
||||
cp STDIO_BUFSIZE-1
|
||||
jr z, .loop ; beginning of line, nothing to delete
|
||||
dec hl
|
||||
inc b
|
||||
ld a, (STDIO_BUFIDX)
|
||||
or a
|
||||
jp z, unsetZ ; buf empty? nothing to do
|
||||
; buffer not empty, let's go back one char and set a null char there.
|
||||
dec a
|
||||
ld (STDIO_BUFIDX), a
|
||||
push hl ;<|
|
||||
ld hl, STDIO_BUF ; |
|
||||
; make HL point to dest spot |
|
||||
call addHL ; |
|
||||
xor a ; |
|
||||
ld (hl), a ; |
|
||||
pop hl ;<|
|
||||
; Char deleted in buffer, now send BS + space + BS for the terminal
|
||||
; to clear its previous char
|
||||
ld a, ASCII_BS
|
||||
call STDIO_PUTC
|
||||
call stdioPutC
|
||||
ld a, ' '
|
||||
call STDIO_PUTC
|
||||
call stdioPutC
|
||||
ld a, ASCII_BS
|
||||
call STDIO_PUTC
|
||||
jr .loop
|
||||
call stdioPutC
|
||||
jp unsetZ
|
||||
|
||||
|
||||
; Make HL point to the line buffer. It is always null terminated.
|
||||
stdioGetLine:
|
||||
ld hl, STDIO_BUF
|
||||
ret
|
||||
|
||||
; Repeatedly call stdioReadC until Z is set, then make HL point to the read
|
||||
; buffer.
|
||||
stdioReadLine:
|
||||
call stdioReadC
|
||||
jr nz, stdioReadLine
|
||||
ld hl, STDIO_BUF
|
||||
ret
|
||||
|
||||
|
@ -22,30 +22,16 @@ for.
|
||||
|
||||
## Structure
|
||||
|
||||
Each top folder represents an architecture. In that top folder, there's a
|
||||
Each top folder represent an architecture. In that top folder, there's a
|
||||
`README.md` file presenting the architecture as well as instructions to
|
||||
minimally get Collapse OS running on it. Then, in the same folder, there are
|
||||
auxiliary recipes for nice stuff built around that architecture.
|
||||
|
||||
Installation procedures are centered around using a modern system to install
|
||||
Collapse OS. These are the most useful instructions to have under both
|
||||
pre-collapse and post-collapse conditions because even after the collapse,
|
||||
we'll interact mostly with modern technology for many years.
|
||||
The structure of those recipes follow a regular pattern: pre-collapse recipe
|
||||
and post-collapse recipe. That is, instructions to achieve the desired outcome
|
||||
from a "modern" system, and then, instructions to achieve the same thing from a
|
||||
system running Collapse OS.
|
||||
|
||||
There are, however, recipes to write to different storage media, thus making
|
||||
Collapse OS fully reproducible. For example, you can use `rc2014/eeprom` to
|
||||
write arbitrary data to a `AT28` EEPROM.
|
||||
|
||||
The `rc2014` architecture is considered the "canonical" one. That means that
|
||||
if a recipe is considered architecture independent, it's the `rc2014` recipe
|
||||
folder that's going to contain it.
|
||||
|
||||
For example, `rc2014/eeprom` can be considered architecture independent because
|
||||
it's much more about the `AT28` than about a specific z80 architecture. You can
|
||||
adapt it to any supported architecture with minimal hassle. Therefore, it's
|
||||
not going to be copied in every architecture recipe folder.
|
||||
|
||||
`rc2014` installation recipe also contains more "newbie-friendly" instructions
|
||||
than other installation recipes, which take this knowledge for granted. It is
|
||||
therefore recommended to have a look at it even if you're not planning on using
|
||||
a RC2014.
|
||||
Initially, those recipes will only be possible in a "modern" system, but as
|
||||
tooling improve, we should be able to have recipes that we can consider
|
||||
complete.
|
||||
|
@ -28,9 +28,11 @@ are other recipes related to the RC2014:
|
||||
* [Assembling binaries](zasm/README.md)
|
||||
* [Interfacing a PS/2 keyboard](ps2/README.md)
|
||||
|
||||
## Recipe
|
||||
## Goal
|
||||
|
||||
The goal is to have the shell running and accessible through the Serial I/O.
|
||||
Have the shell running and accessible through the Serial I/O.
|
||||
|
||||
## Pre-collapse
|
||||
|
||||
You'll need specialized tools to write data to the AT28 EEPROM. There seems to
|
||||
be many devices around made to write in flash and EEPROM modules, but being in
|
||||
@ -75,9 +77,7 @@ is decoupled from the ACIA and can get its IO from anything. See
|
||||
|
||||
We only have the shell to build, so it's rather straightforward:
|
||||
|
||||
../../tools/zasm.sh ../../kernel < glue.asm > os.bin
|
||||
|
||||
Running `make` will also work.
|
||||
zasm < glue.asm > rom.bin
|
||||
|
||||
### Write to the ROM
|
||||
|
||||
@ -100,6 +100,10 @@ identify the tty bound to it (in my case, `/dev/ttyUSB0`). Then:
|
||||
|
||||
Press the reset button on the RC2014 and you should see the Collapse OS prompt!
|
||||
|
||||
## Post-collapse
|
||||
|
||||
TODO
|
||||
|
||||
[rc2014]: https://rc2014.co.uk
|
||||
[romwrite]: https://github.com/hsoft/romwrite
|
||||
[zasm]: ../../tools/emul
|
||||
|
@ -27,7 +27,7 @@ If you're tempted by the idea of hacking your existing RC2014 ROM module by
|
||||
wiring `WR` and write directly to the range `0x0000-0x1fff` while running it,
|
||||
be aware that it's not that easy. I was also tempted by this idea, tried it,
|
||||
but on bootup, it seems that some random `WR` triggers happen and it corrupts
|
||||
the EEPROM contents. Theoretically, we could go around that by putting the AT28
|
||||
the EEPROM contents. Theoretically, we could go around that my putting the AT28
|
||||
in write protection mode, but I preferred building my own module.
|
||||
|
||||
I don't think you need a schematic. It's really simple.
|
||||
|
@ -27,8 +27,6 @@ jp aciaInt
|
||||
.dw mmapGetB, mmapPutB
|
||||
|
||||
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
|
||||
.equ STDIO_GETC aciaGetC
|
||||
.equ STDIO_PUTC aciaPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ AT28W_RAMSTART STDIO_RAMEND
|
||||
@ -51,6 +49,9 @@ init:
|
||||
im 1
|
||||
|
||||
call aciaInit
|
||||
ld hl, aciaGetC
|
||||
ld de, aciaPutC
|
||||
call stdioInit
|
||||
call shellInit
|
||||
|
||||
xor a
|
||||
|
@ -18,8 +18,6 @@ jp aciaInt
|
||||
.inc "acia.asm"
|
||||
|
||||
.equ STDIO_RAMSTART ACIA_RAMEND
|
||||
.equ STDIO_GETC aciaGetC
|
||||
.equ STDIO_PUTC aciaPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ SHELL_RAMSTART STDIO_RAMEND
|
||||
@ -34,6 +32,9 @@ init:
|
||||
im 1
|
||||
|
||||
call aciaInit
|
||||
ld hl, aciaGetC
|
||||
ld de, aciaPutC
|
||||
call stdioInit
|
||||
call shellInit
|
||||
ei
|
||||
jp shellLoop
|
||||
|
@ -16,8 +16,6 @@ jp init
|
||||
.inc "kbd.asm"
|
||||
|
||||
.equ STDIO_RAMSTART KBD_RAMEND
|
||||
.equ STDIO_GETC kbdGetC
|
||||
.equ STDIO_PUTC aciaPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ SHELL_RAMSTART STDIO_RAMEND
|
||||
@ -32,6 +30,9 @@ init:
|
||||
|
||||
call aciaInit
|
||||
call kbdInit
|
||||
ld hl, kbdGetC
|
||||
ld de, aciaPutC
|
||||
call stdioInit
|
||||
call shellInit
|
||||
jp shellLoop
|
||||
|
||||
|
@ -34,8 +34,6 @@ jp aciaInt
|
||||
|
||||
|
||||
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
|
||||
.equ STDIO_GETC aciaGetC
|
||||
.equ STDIO_PUTC aciaPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ FS_RAMSTART STDIO_RAMEND
|
||||
@ -68,6 +66,9 @@ init:
|
||||
ld sp, hl
|
||||
im 1
|
||||
call aciaInit
|
||||
ld hl, aciaGetC
|
||||
ld de, aciaPutC
|
||||
call stdioInit
|
||||
call fsInit
|
||||
call shellInit
|
||||
ld hl, pgmShellHook
|
||||
|
@ -65,8 +65,6 @@ jp aciaInt
|
||||
.inc "mmap.asm"
|
||||
|
||||
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
|
||||
.equ STDIO_GETC aciaGetC
|
||||
.equ STDIO_PUTC aciaPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ FS_RAMSTART STDIO_RAMEND
|
||||
@ -101,6 +99,9 @@ init:
|
||||
ld sp, hl
|
||||
im 1
|
||||
call aciaInit
|
||||
ld hl, aciaGetC
|
||||
ld de, aciaPutC
|
||||
call stdioInit
|
||||
call fsInit
|
||||
call shellInit
|
||||
ld hl, pgmShellHook
|
||||
|
@ -19,8 +19,6 @@
|
||||
.inc "sms/vdp.asm"
|
||||
|
||||
.equ STDIO_RAMSTART VDP_RAMEND
|
||||
.equ STDIO_GETC padGetC
|
||||
.equ STDIO_PUTC vdpPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ SHELL_RAMSTART STDIO_RAMEND
|
||||
@ -35,7 +33,15 @@ init:
|
||||
|
||||
call padInit
|
||||
call vdpInit
|
||||
ld hl, padUpdateSel
|
||||
ld (VDP_CHRSELHOOK), hl
|
||||
|
||||
ld hl, padGetC
|
||||
ld de, vdpPutC
|
||||
call stdioInit
|
||||
call shellInit
|
||||
ld hl, vdpShellLoopHook
|
||||
ld (SHELL_LOOPHOOK), hl
|
||||
jp shellLoop
|
||||
|
||||
.fill 0x7ff0-$
|
||||
|
@ -21,8 +21,6 @@
|
||||
.inc "sms/vdp.asm"
|
||||
|
||||
.equ STDIO_RAMSTART VDP_RAMEND
|
||||
.equ STDIO_GETC kbdGetC
|
||||
.equ STDIO_PUTC vdpPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ SHELL_RAMSTART STDIO_RAMEND
|
||||
@ -47,6 +45,10 @@ init:
|
||||
|
||||
call kbdInit
|
||||
call vdpInit
|
||||
|
||||
ld hl, kbdGetC
|
||||
ld de, vdpPutC
|
||||
call stdioInit
|
||||
call shellInit
|
||||
jp shellLoop
|
||||
|
||||
|
@ -52,8 +52,6 @@
|
||||
.inc "sms/vdp.asm"
|
||||
|
||||
.equ STDIO_RAMSTART VDP_RAMEND
|
||||
.equ STDIO_GETC kbdGetC
|
||||
.equ STDIO_PUTC vdpPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ MMAP_START 0xd700
|
||||
@ -106,6 +104,9 @@ init:
|
||||
ld a, 'S'
|
||||
ld (hl), a
|
||||
|
||||
ld hl, kbdGetC
|
||||
ld de, vdpPutC
|
||||
call stdioInit
|
||||
call fsInit
|
||||
xor a
|
||||
ld de, BLOCKDEV_SEL
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/python
|
||||
|
||||
# Read specified number of bytes in specified blkdev ID and spit it to stdout.
|
||||
# The proper blkdev has to be selected and placed already.
|
||||
|
@ -58,8 +58,6 @@
|
||||
.inc "mmap.asm"
|
||||
|
||||
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
|
||||
.equ STDIO_GETC emulGetC
|
||||
.equ STDIO_PUTC emulPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ FS_RAMSTART STDIO_RAMEND
|
||||
@ -86,6 +84,9 @@ init:
|
||||
; setup stack
|
||||
ld hl, KERNEL_RAMEND
|
||||
ld sp, hl
|
||||
ld hl, emulGetC
|
||||
ld de, emulPutC
|
||||
call stdioInit
|
||||
call fsInit
|
||||
ld a, 0 ; select fsdev
|
||||
ld de, BLOCKDEV_SEL
|
||||
|
@ -45,8 +45,6 @@ jp printstr
|
||||
.dw fsdevGetB, fsdevPutB
|
||||
|
||||
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
|
||||
.equ STDIO_GETC noop
|
||||
.equ STDIO_PUTC stderrPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ FS_RAMSTART STDIO_RAMEND
|
||||
@ -57,6 +55,9 @@ init:
|
||||
di
|
||||
ld hl, 0xffff
|
||||
ld sp, hl
|
||||
ld hl, unsetZ
|
||||
ld de, stderrPutC
|
||||
call stdioInit
|
||||
ld a, 2 ; select fsdev
|
||||
ld de, BLOCKDEV_SEL
|
||||
call blkSel
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/python
|
||||
|
||||
# Read specified number of bytes at specified memory address and dump it to
|
||||
# stdout.
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/python
|
||||
|
||||
# Push specified file to specified device and verify that the contents is
|
||||
# correct by sending a "peek" command afterwards and check the output. Errors
|
||||
|
Loading…
Reference in New Issue
Block a user