1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-26 09:28:05 +11:00

Compare commits

..

No commits in common. "69daf49920c77821c78b747bb59ea5e3ad0409ac" and "8a696a1e23cc94f4db71489cfb39d0cf9e2747a8" have entirely different histories.

23 changed files with 276 additions and 201 deletions

View File

@ -1,5 +1,5 @@
This file describe tricks and conventions that are used throughout the code and This file describe tricks that are used throughout the code and might need
might need explanation. explanation.
or a: Equivalent to "cp 0", but results in a shorter opcode. 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 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, "unsetZ" routine exists for that, although that in certain circumstances,
"inc a \ dec a" or "or a" can work. "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.

View File

@ -7,43 +7,47 @@ look like:
; The RAM module is selected on A15, so it has the range 0x8000-0xffff ; The RAM module is selected on A15, so it has the range 0x8000-0xffff
.equ RAMSTART 0x8000 .equ RAMSTART 0x8000
.equ RAMEND 0xffff .equ RAMEND 0xffff
.equ ACIA_CTL 0x80 ; Control and status. RS off. .equ ACIA_CTL 0x80 ; Control and status. RS off.
.equ ACIA_IO 0x81 ; Transmit. RS on. .equ ACIA_IO 0x81 ; Transmit. RS on.
jp init jr init
; interrupt hook ; interrupt hook
.fill 0x38-$ .fill 0x38-$
jp aciaInt 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"
init: init:
di di
; setup stack ; setup stack
ld hl, RAMEND ld hl, RAMEND
ld sp, hl ld sp, hl
im 1 im 1
call aciaInit
call aciaInit xor a
call shellInit ld de, BLOCKDEV_SEL
call blkSel
call stdioInit
call shellInit
ei 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: Once this is written, building it is easy:

View File

@ -98,17 +98,17 @@ aciaInt:
reti reti
; *** STDIO *** ; *** BLOCKDEV ***
; These function below follow the stdio API. ; These function below follow the blockdev API.
aciaGetC: aciaGetC:
push de push de
.loop:
ld a, (ACIA_BUFWRIDX) ld a, (ACIA_BUFWRIDX)
ld e, a ld e, a
ld a, (ACIA_BUFRDIDX) ld a, (ACIA_BUFRDIDX)
cp e cp e
jr z, .loop ; equal? nothing to read. loop jr z, .nothingToRead ; equal? nothing to read.
; Alrighty, buffer not empty. let's read. ; Alrighty, buffer not empty. let's read.
ld de, ACIA_BUF ld de, ACIA_BUF
@ -120,6 +120,12 @@ aciaGetC:
; And finally, fetch the value. ; And finally, fetch the value.
ld a, (de) ld a, (de)
cp a ; ensure Z
jr .end
.nothingToRead:
call unsetZ
.end:
pop de pop de
ret ret

View File

@ -35,10 +35,6 @@
; number of entries in shellCmdTbl ; number of entries in shellCmdTbl
.equ SHELL_CMD_COUNT 6+SHELL_EXTRA_CMD_COUNT .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 *** ; *** VARIABLES ***
; Memory address that the shell is currently "pointing at" for peek, load, call ; Memory address that the shell is currently "pointing at" for peek, load, call
; operations. Set with mptr. ; operations. Set with mptr.
@ -46,12 +42,14 @@
; Places where we store arguments specifiers and where resulting values are ; Places where we store arguments specifiers and where resulting values are
; written to after parsing. ; 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 ; 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 *** ; *** CODE ***
shellInit: shellInit:
@ -60,6 +58,7 @@ shellInit:
ld (SHELL_MEM_PTR+1), a ld (SHELL_MEM_PTR+1), a
ld hl, noop ld hl, noop
ld (SHELL_CMDHOOK), hl ld (SHELL_CMDHOOK), hl
ld (SHELL_LOOPHOOK), hl
; print welcome ; print welcome
ld hl, .welcome ld hl, .welcome
@ -71,8 +70,15 @@ shellInit:
; Inifite loop that processes input. Because it's infinite, you should jump ; 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. ; to it rather than call it. Saves two precious bytes in the stack.
shellLoop: 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 printcrlf
call stdioGetLine
call shellParse call shellParse
ld hl, .prompt ld hl, .prompt
call printstr call printstr
@ -86,7 +92,7 @@ shellParse:
; first thing: is command empty? ; first thing: is command empty?
ld a, (hl) ld a, (hl)
or a or a
ret z ; empty, nothing to do ret z ; empty, nthing to do
push af push af
push bc push bc
@ -104,13 +110,6 @@ shellParse:
; no arg, (HL) is zero to facilitate processing later, add a second ; 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. ; null next to that one to indicate unambiguously that we have no args.
inc hl 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: .hasArgs:
xor a xor a
ld (hl), a ld (hl), a

View File

@ -11,9 +11,6 @@
; The space character is the first among special chars. ; The space character is the first among special chars.
; * C confirms letter selection ; * 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 *** ; *** Consts ***
; ;
.equ PAD_CTLPORT 0x3f .equ PAD_CTLPORT 0x3f
@ -32,18 +29,21 @@
; ;
; Button status of last padUpdateSel call. Used for debouncing. ; Button status of last padUpdateSel call. Used for debouncing.
.equ PAD_SELSTAT PAD_RAMSTART .equ PAD_SELSTAT PAD_RAMSTART
; Button status of last padGetC call.
.equ PAD_GETCSTAT PAD_SELSTAT+1
; Current selected character ; 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 ; When non-zero, will be the next char returned in GetC. So far, only used for
; LF that is feeded when Start is pressed. ; LF that is feeded when Start is pressed.
.equ PAD_NEXTCHR @+1 .equ PAD_NEXTCHR PAD_SELCHR+1
.equ PAD_RAMEND @+1 .equ PAD_RAMEND PAD_NEXTCHR+1
; *** Code *** ; *** Code ***
padInit: padInit:
ld a, 0xff ld a, 0xff
ld (PAD_SELSTAT), a ld (PAD_SELSTAT), a
ld (PAD_GETCSTAT), a
xor a xor a
ld (PAD_NEXTCHR), a ld (PAD_NEXTCHR), a
ld a, 'a' ld a, 'a'
@ -78,14 +78,14 @@ padStatus:
ret ret
; From a pad status in A, update current char selection and return it. ; 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: padUpdateSel:
call padStatus call padStatus
push hl ; --> lvl 1 push hl
ld hl, PAD_SELSTAT ld hl, PAD_SELSTAT
cp (hl) cp (hl)
ld (hl), a ld (hl), a
pop hl ; <-- lvl 1 pop hl
jr z, .nothing ; nothing changed jr z, .nothing ; nothing changed
bit PAD_UP, a bit PAD_UP, a
jr z, .up jr z, .up
@ -156,51 +156,45 @@ padUpdateSel:
ld (PAD_SELCHR), a ld (PAD_SELCHR), a
jp unsetZ jp unsetZ
.nothing: .nothing:
; Z already set cp a ; ensure Z
ld a, (PAD_SELCHR) ld a, (PAD_SELCHR)
ret 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: padGetC:
ld a, (PAD_NEXTCHR) ld a, (PAD_NEXTCHR)
or a or a
jr nz, .nextchr jr nz, .nextchr
call padUpdateSel call padStatus
jp z, padGetC ; nothing changed, loop push hl
; pad status was changed, let's see if an action button was pressed ld hl, PAD_GETCSTAT
ld a, (PAD_SELSTAT) cp (hl)
ld (hl), a
pop hl
jp z, unsetZ ; nothing changed
bit PAD_BUTC, a bit PAD_BUTC, a
jr z, .advance jr z, .advance
bit PAD_BUTA, a bit PAD_BUTA, a
jr z, .backspace jr z, .backspace
bit PAD_START, a bit PAD_START, a
jr z, .return jr z, .return
; no action button pressed, but because our pad status changed, update jp unsetZ
; VDP before looping.
ld a, (PAD_SELCHR)
call vdpConv
call vdpSpitC
jp padGetC
.return: .return:
ld a, ASCII_LF ld a, ASCII_LF
ld (PAD_NEXTCHR), a ld (PAD_NEXTCHR), a
; continue to .advance ; continue to .advance
.advance: .advance:
ld a, (PAD_SELCHR) ld a, (PAD_SELCHR)
; Z was already set from previous BIT instruction cp a
ret ret
.backspace: .backspace:
ld a, ASCII_BS ld a, ASCII_BS
; Z was already set from previous BIT instruction cp a
ret ret
.nextchr: .nextchr:
; We have a "next char", return it and clear it. ; We have a "next char", return it and clear it.
cp a ; ensure Z cp a ; ensure Z
ex af, af' push af
xor a xor a
ld (PAD_NEXTCHR), a ld (PAD_NEXTCHR), a
ex af, af' pop af
ret ret

View File

@ -21,8 +21,11 @@
; Row of cursor ; Row of cursor
.equ VDP_ROW VDP_RAMSTART .equ VDP_ROW VDP_RAMSTART
; Line of cursor ; Line of cursor
.equ VDP_LINE @+1 .equ VDP_LINE VDP_ROW+1
.equ VDP_RAMEND @+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 *** ; *** Code ***
@ -30,6 +33,9 @@ vdpInit:
xor a xor a
ld (VDP_ROW), a ld (VDP_ROW), a
ld (VDP_LINE), a ld (VDP_LINE), a
ld (VDP_LASTSEL), a
ld hl, noop
ld (VDP_CHRSELHOOK), hl
ld hl, vdpInitData ld hl, vdpInitData
ld b, vdpInitDataEnd-vdpInitData ld b, vdpInitDataEnd-vdpInitData
@ -115,6 +121,12 @@ vdpSpitC:
ret ret
vdpPutC: 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 ; 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 ; 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 ; 2 bits are the two low bits of our line
@ -255,6 +267,26 @@ vdpConv:
ld a, 0x5e ld a, 0x5e
ret 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: vdpPaletteData:
.db 0x00,0x3f .db 0x00,0x3f
vdpPaletteDataEnd: vdpPaletteDataEnd:

View File

@ -4,26 +4,6 @@
; in", that is, the console through which the user is connected in a decoupled ; in", that is, the console through which the user is connected in a decoupled
; manner. ; 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 *** ; *** Consts ***
; Size of the readline buffer. If a typed line reaches this size, the line is ; Size of the readline buffer. If a typed line reaches this size, the line is
; flushed immediately (same as pressing return). ; flushed immediately (same as pressing return).
@ -32,19 +12,34 @@
; *** Variables *** ; *** Variables ***
; Used to store formatted hex values just before printing it. ; Used to store formatted hex values just before printing it.
.equ STDIO_HEX_FMT STDIO_RAMSTART .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 ; Line buffer. We read types chars into this buffer until return is pressed
; This buffer is null-terminated. ; This buffer is null-terminated and we don't keep an index around: we look
.equ STDIO_BUF @+2 ; 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. ; 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: stdioGetC:
jp STDIO_GETC ld ix, (STDIO_GETC)
jp (ix)
stdioPutC: stdioPutC:
jp STDIO_PUTC ld ix, (STDIO_PUTC)
jp (ix)
; print null-terminated string pointed to by HL ; print null-terminated string pointed to by HL
printstr: printstr:
@ -55,7 +50,7 @@ printstr:
ld a, (hl) ; load character to send ld a, (hl) ; load character to send
or a ; is it zero? or a ; is it zero?
jr z, .end ; if yes, we're finished jr z, .end ; if yes, we're finished
call STDIO_PUTC call stdioPutC
inc hl inc hl
jr .loop jr .loop
@ -70,7 +65,7 @@ printnstr:
push hl push hl
.loop: .loop:
ld a, (hl) ; load character to send ld a, (hl) ; load character to send
call STDIO_PUTC call stdioPutC
inc hl inc hl
djnz .loop djnz .loop
@ -82,9 +77,9 @@ printnstr:
printcrlf: printcrlf:
push af push af
ld a, ASCII_CR ld a, ASCII_CR
call STDIO_PUTC call stdioPutC
ld a, ASCII_LF ld a, ASCII_LF
call STDIO_PUTC call stdioPutC
pop af pop af
ret ret
@ -110,19 +105,21 @@ printHexPair:
pop af pop af
ret ret
; Repeatedly calls stdioGetC until a whole line was read, that is, when CR or ; Call stdioGetC and put the result in the buffer. Sets Z according to whether
; LF is read or if the buffer is full. Sets HL to the beginning of the read ; the buffer is "complete", that is, whether CR or LF have been pressed or if
; line, which is null-terminated. ; 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. ; This routine also takes care of echoing received characters back to the TTY.
; It also manages backspaces properly. ;
stdioReadLine: ; This routine doesn't wait after a typed char. If nothing is typed, we return
push bc ; immediately with Z flag unset.
ld hl, STDIO_BUF ;
ld b, STDIO_BUFSIZE-1 ; Note that this routine doesn't bother returning the typed character.
.loop: stdioReadC:
; Let's wait until something is typed. ; 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? ; got it. Now, is it a CR or LF?
cp ASCII_CR cp ASCII_CR
jr z, .complete ; char is CR? buffer complete! jr z, .complete ; char is CR? buffer complete!
@ -134,37 +131,86 @@ stdioReadLine:
jr z, .delchr jr z, .delchr
; Echo the received character right away so that we see what we type ; 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 ; 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 ld (hl), a
; follow up with a null char
inc hl 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 xor a
ld (hl), a ld (hl), a
ld hl, STDIO_BUF pop hl ; <-- lvl 1
pop bc ; 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 ret
.delchr: .delchr:
; Deleting is a tricky business. We have to decrease HL and increase B ld a, (STDIO_BUFIDX)
; so that everything stays consistent. We also have to make sure that or a
; We don't do buffer underflows. jp z, unsetZ ; buf empty? nothing to do
ld a, b ; buffer not empty, let's go back one char and set a null char there.
cp STDIO_BUFSIZE-1 dec a
jr z, .loop ; beginning of line, nothing to delete ld (STDIO_BUFIDX), a
dec hl push hl ;<|
inc b 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 ; Char deleted in buffer, now send BS + space + BS for the terminal
; to clear its previous char ; to clear its previous char
ld a, ASCII_BS ld a, ASCII_BS
call STDIO_PUTC call stdioPutC
ld a, ' ' ld a, ' '
call STDIO_PUTC call stdioPutC
ld a, ASCII_BS ld a, ASCII_BS
call STDIO_PUTC call stdioPutC
jr .loop 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

View File

@ -22,30 +22,16 @@ for.
## Structure ## 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 `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 minimally get Collapse OS running on it. Then, in the same folder, there are
auxiliary recipes for nice stuff built around that architecture. auxiliary recipes for nice stuff built around that architecture.
Installation procedures are centered around using a modern system to install The structure of those recipes follow a regular pattern: pre-collapse recipe
Collapse OS. These are the most useful instructions to have under both and post-collapse recipe. That is, instructions to achieve the desired outcome
pre-collapse and post-collapse conditions because even after the collapse, from a "modern" system, and then, instructions to achieve the same thing from a
we'll interact mostly with modern technology for many years. system running Collapse OS.
There are, however, recipes to write to different storage media, thus making Initially, those recipes will only be possible in a "modern" system, but as
Collapse OS fully reproducible. For example, you can use `rc2014/eeprom` to tooling improve, we should be able to have recipes that we can consider
write arbitrary data to a `AT28` EEPROM. complete.
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.

View File

@ -28,9 +28,11 @@ are other recipes related to the RC2014:
* [Assembling binaries](zasm/README.md) * [Assembling binaries](zasm/README.md)
* [Interfacing a PS/2 keyboard](ps2/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 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 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: We only have the shell to build, so it's rather straightforward:
../../tools/zasm.sh ../../kernel < glue.asm > os.bin zasm < glue.asm > rom.bin
Running `make` will also work.
### Write to the ROM ### 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! Press the reset button on the RC2014 and you should see the Collapse OS prompt!
## Post-collapse
TODO
[rc2014]: https://rc2014.co.uk [rc2014]: https://rc2014.co.uk
[romwrite]: https://github.com/hsoft/romwrite [romwrite]: https://github.com/hsoft/romwrite
[zasm]: ../../tools/emul [zasm]: ../../tools/emul

View File

@ -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, 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, 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 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. in write protection mode, but I preferred building my own module.
I don't think you need a schematic. It's really simple. I don't think you need a schematic. It's really simple.

View File

@ -27,8 +27,6 @@ jp aciaInt
.dw mmapGetB, mmapPutB .dw mmapGetB, mmapPutB
.equ STDIO_RAMSTART BLOCKDEV_RAMEND .equ STDIO_RAMSTART BLOCKDEV_RAMEND
.equ STDIO_GETC aciaGetC
.equ STDIO_PUTC aciaPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ AT28W_RAMSTART STDIO_RAMEND .equ AT28W_RAMSTART STDIO_RAMEND
@ -51,6 +49,9 @@ init:
im 1 im 1
call aciaInit call aciaInit
ld hl, aciaGetC
ld de, aciaPutC
call stdioInit
call shellInit call shellInit
xor a xor a

View File

@ -18,8 +18,6 @@ jp aciaInt
.inc "acia.asm" .inc "acia.asm"
.equ STDIO_RAMSTART ACIA_RAMEND .equ STDIO_RAMSTART ACIA_RAMEND
.equ STDIO_GETC aciaGetC
.equ STDIO_PUTC aciaPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ SHELL_RAMSTART STDIO_RAMEND .equ SHELL_RAMSTART STDIO_RAMEND
@ -34,6 +32,9 @@ init:
im 1 im 1
call aciaInit call aciaInit
ld hl, aciaGetC
ld de, aciaPutC
call stdioInit
call shellInit call shellInit
ei ei
jp shellLoop jp shellLoop

View File

@ -16,8 +16,6 @@ jp init
.inc "kbd.asm" .inc "kbd.asm"
.equ STDIO_RAMSTART KBD_RAMEND .equ STDIO_RAMSTART KBD_RAMEND
.equ STDIO_GETC kbdGetC
.equ STDIO_PUTC aciaPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ SHELL_RAMSTART STDIO_RAMEND .equ SHELL_RAMSTART STDIO_RAMEND
@ -32,6 +30,9 @@ init:
call aciaInit call aciaInit
call kbdInit call kbdInit
ld hl, kbdGetC
ld de, aciaPutC
call stdioInit
call shellInit call shellInit
jp shellLoop jp shellLoop

View File

@ -34,8 +34,6 @@ jp aciaInt
.equ STDIO_RAMSTART BLOCKDEV_RAMEND .equ STDIO_RAMSTART BLOCKDEV_RAMEND
.equ STDIO_GETC aciaGetC
.equ STDIO_PUTC aciaPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ FS_RAMSTART STDIO_RAMEND .equ FS_RAMSTART STDIO_RAMEND
@ -68,6 +66,9 @@ init:
ld sp, hl ld sp, hl
im 1 im 1
call aciaInit call aciaInit
ld hl, aciaGetC
ld de, aciaPutC
call stdioInit
call fsInit call fsInit
call shellInit call shellInit
ld hl, pgmShellHook ld hl, pgmShellHook

View File

@ -65,8 +65,6 @@ jp aciaInt
.inc "mmap.asm" .inc "mmap.asm"
.equ STDIO_RAMSTART BLOCKDEV_RAMEND .equ STDIO_RAMSTART BLOCKDEV_RAMEND
.equ STDIO_GETC aciaGetC
.equ STDIO_PUTC aciaPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ FS_RAMSTART STDIO_RAMEND .equ FS_RAMSTART STDIO_RAMEND
@ -101,6 +99,9 @@ init:
ld sp, hl ld sp, hl
im 1 im 1
call aciaInit call aciaInit
ld hl, aciaGetC
ld de, aciaPutC
call stdioInit
call fsInit call fsInit
call shellInit call shellInit
ld hl, pgmShellHook ld hl, pgmShellHook

View File

@ -19,8 +19,6 @@
.inc "sms/vdp.asm" .inc "sms/vdp.asm"
.equ STDIO_RAMSTART VDP_RAMEND .equ STDIO_RAMSTART VDP_RAMEND
.equ STDIO_GETC padGetC
.equ STDIO_PUTC vdpPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ SHELL_RAMSTART STDIO_RAMEND .equ SHELL_RAMSTART STDIO_RAMEND
@ -35,7 +33,15 @@ init:
call padInit call padInit
call vdpInit call vdpInit
ld hl, padUpdateSel
ld (VDP_CHRSELHOOK), hl
ld hl, padGetC
ld de, vdpPutC
call stdioInit
call shellInit call shellInit
ld hl, vdpShellLoopHook
ld (SHELL_LOOPHOOK), hl
jp shellLoop jp shellLoop
.fill 0x7ff0-$ .fill 0x7ff0-$

View File

@ -21,8 +21,6 @@
.inc "sms/vdp.asm" .inc "sms/vdp.asm"
.equ STDIO_RAMSTART VDP_RAMEND .equ STDIO_RAMSTART VDP_RAMEND
.equ STDIO_GETC kbdGetC
.equ STDIO_PUTC vdpPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ SHELL_RAMSTART STDIO_RAMEND .equ SHELL_RAMSTART STDIO_RAMEND
@ -47,6 +45,10 @@ init:
call kbdInit call kbdInit
call vdpInit call vdpInit
ld hl, kbdGetC
ld de, vdpPutC
call stdioInit
call shellInit call shellInit
jp shellLoop jp shellLoop

View File

@ -52,8 +52,6 @@
.inc "sms/vdp.asm" .inc "sms/vdp.asm"
.equ STDIO_RAMSTART VDP_RAMEND .equ STDIO_RAMSTART VDP_RAMEND
.equ STDIO_GETC kbdGetC
.equ STDIO_PUTC vdpPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ MMAP_START 0xd700 .equ MMAP_START 0xd700
@ -106,6 +104,9 @@ init:
ld a, 'S' ld a, 'S'
ld (hl), a ld (hl), a
ld hl, kbdGetC
ld de, vdpPutC
call stdioInit
call fsInit call fsInit
xor a xor a
ld de, BLOCKDEV_SEL ld de, BLOCKDEV_SEL

View File

@ -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. # Read specified number of bytes in specified blkdev ID and spit it to stdout.
# The proper blkdev has to be selected and placed already. # The proper blkdev has to be selected and placed already.

View File

@ -58,8 +58,6 @@
.inc "mmap.asm" .inc "mmap.asm"
.equ STDIO_RAMSTART BLOCKDEV_RAMEND .equ STDIO_RAMSTART BLOCKDEV_RAMEND
.equ STDIO_GETC emulGetC
.equ STDIO_PUTC emulPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ FS_RAMSTART STDIO_RAMEND .equ FS_RAMSTART STDIO_RAMEND
@ -86,6 +84,9 @@ init:
; setup stack ; setup stack
ld hl, KERNEL_RAMEND ld hl, KERNEL_RAMEND
ld sp, hl ld sp, hl
ld hl, emulGetC
ld de, emulPutC
call stdioInit
call fsInit call fsInit
ld a, 0 ; select fsdev ld a, 0 ; select fsdev
ld de, BLOCKDEV_SEL ld de, BLOCKDEV_SEL

View File

@ -45,8 +45,6 @@ jp printstr
.dw fsdevGetB, fsdevPutB .dw fsdevGetB, fsdevPutB
.equ STDIO_RAMSTART BLOCKDEV_RAMEND .equ STDIO_RAMSTART BLOCKDEV_RAMEND
.equ STDIO_GETC noop
.equ STDIO_PUTC stderrPutC
.inc "stdio.asm" .inc "stdio.asm"
.equ FS_RAMSTART STDIO_RAMEND .equ FS_RAMSTART STDIO_RAMEND
@ -57,6 +55,9 @@ init:
di di
ld hl, 0xffff ld hl, 0xffff
ld sp, hl ld sp, hl
ld hl, unsetZ
ld de, stderrPutC
call stdioInit
ld a, 2 ; select fsdev ld a, 2 ; select fsdev
ld de, BLOCKDEV_SEL ld de, BLOCKDEV_SEL
call blkSel call blkSel

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/python
# Read specified number of bytes at specified memory address and dump it to # Read specified number of bytes at specified memory address and dump it to
# stdout. # stdout.

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/python
# Push specified file to specified device and verify that the contents is # Push specified file to specified device and verify that the contents is
# correct by sending a "peek" command afterwards and check the output. Errors # correct by sending a "peek" command afterwards and check the output. Errors