mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-11 08:28:06 +11:00
Add at28w app and recipe
This allows us to write to an AT28 EEPROM from within collapse os.
This commit is contained in:
parent
145b48efb7
commit
817636242a
24
apps/at28w/glue.asm
Normal file
24
apps/at28w/glue.asm
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
; at28w - Write to AT28 EEPROM
|
||||||
|
;
|
||||||
|
; Write data from the active block device into an eeprom device geared as
|
||||||
|
; regular memory. Implements write polling to know when the next byte can be
|
||||||
|
; written and verifies that data is written properly.
|
||||||
|
;
|
||||||
|
; Optionally receives a word argument that specifies the number or bytes to
|
||||||
|
; write. If unspecified, will write until max bytes (0x2000) is reached or EOF
|
||||||
|
; is reached on the block device.
|
||||||
|
|
||||||
|
; *** Requirements ***
|
||||||
|
; blkGetC
|
||||||
|
; parseArgs
|
||||||
|
;
|
||||||
|
; *** Includes ***
|
||||||
|
|
||||||
|
#include "user.h"
|
||||||
|
#include "err.h"
|
||||||
|
.org USER_CODE
|
||||||
|
.equ AT28W_RAMSTART USER_RAMSTART
|
||||||
|
|
||||||
|
jp at28wMain
|
||||||
|
|
||||||
|
#include "at28w/main.asm"
|
76
apps/at28w/main.asm
Normal file
76
apps/at28w/main.asm
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
; *** Consts ***
|
||||||
|
; Memory address where the AT28 is configured to start
|
||||||
|
.equ AT28W_MEMSTART 0x2000
|
||||||
|
|
||||||
|
; Value mismatch during validation
|
||||||
|
.equ AT28W_ERR_MISMATCH 0x10
|
||||||
|
|
||||||
|
; *** Variables ***
|
||||||
|
.equ AT28W_MAXBYTES AT28W_RAMSTART
|
||||||
|
.equ AT28W_RAMEND AT28W_MAXBYTES+2
|
||||||
|
; *** Code ***
|
||||||
|
|
||||||
|
at28wMain:
|
||||||
|
ld de, .argspecs
|
||||||
|
ld ix, AT28W_MAXBYTES
|
||||||
|
call parseArgs
|
||||||
|
jr z, at28wInner
|
||||||
|
; bad args
|
||||||
|
ld a, SHELL_ERR_BAD_ARGS
|
||||||
|
ret
|
||||||
|
.argspecs:
|
||||||
|
.db 0b111, 0b101, 0
|
||||||
|
|
||||||
|
at28wInner:
|
||||||
|
ld hl, (AT28W_MAXBYTES)
|
||||||
|
ld b, h
|
||||||
|
ld c, l
|
||||||
|
ld hl, AT28W_MEMSTART
|
||||||
|
call at28BCZero
|
||||||
|
jr nz, .loop
|
||||||
|
; BC is zero, default to 0x2000 (8x, the size of the AT28)
|
||||||
|
ld bc, 0x2000
|
||||||
|
.loop:
|
||||||
|
call blkGetC
|
||||||
|
jr nz, .loopend
|
||||||
|
ld (hl), a
|
||||||
|
ld e, a ; save expected data for verification
|
||||||
|
; initiate polling
|
||||||
|
ld a, (hl)
|
||||||
|
ld d, a
|
||||||
|
.wait:
|
||||||
|
; as long as writing operation is running, IO/6 will toggle at each
|
||||||
|
; read attempt. We know that write is finished when we read the same
|
||||||
|
; value twice.
|
||||||
|
ld a, (hl)
|
||||||
|
cp d
|
||||||
|
jr z, .waitend
|
||||||
|
ld d, a
|
||||||
|
jr .wait
|
||||||
|
.waitend:
|
||||||
|
|
||||||
|
; same value was read twice. A contains our final value for this memory
|
||||||
|
; address. Let's compare with what we're written.
|
||||||
|
cp e
|
||||||
|
jr nz, .mismatch
|
||||||
|
inc hl
|
||||||
|
dec bc
|
||||||
|
call at28BCZero
|
||||||
|
jr nz, .loop
|
||||||
|
|
||||||
|
.loopend:
|
||||||
|
; We're finished. Success!
|
||||||
|
xor a
|
||||||
|
ret
|
||||||
|
|
||||||
|
.mismatch:
|
||||||
|
ld a, AT28W_ERR_MISMATCH
|
||||||
|
ret
|
||||||
|
|
||||||
|
at28BCZero:
|
||||||
|
xor a
|
||||||
|
cp b
|
||||||
|
ret nz
|
||||||
|
cp c
|
||||||
|
ret
|
||||||
|
|
@ -23,6 +23,7 @@ disabled.
|
|||||||
This recipe is for installing a minimal Collapse OS system on the RC2014. There
|
This recipe is for installing a minimal Collapse OS system on the RC2014. There
|
||||||
are other recipes related to the RC2014:
|
are other recipes related to the RC2014:
|
||||||
|
|
||||||
|
* [Writing to a AT28 from Collapse OS](eeprom/README.md)
|
||||||
* [Accessing a MicroSD card](sdcard/README.md)
|
* [Accessing a MicroSD card](sdcard/README.md)
|
||||||
* [Assembling binaries](zasm/README.md)
|
* [Assembling binaries](zasm/README.md)
|
||||||
|
|
||||||
|
10
recipes/rc2014/eeprom/Makefile
Normal file
10
recipes/rc2014/eeprom/Makefile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
TARGET = os.bin
|
||||||
|
ZASM = ../../../tools/zasm.sh
|
||||||
|
KERNEL = ../../../kernel
|
||||||
|
APPS = ../../../apps
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(TARGET)
|
||||||
|
$(TARGET): glue.asm
|
||||||
|
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||||
|
|
83
recipes/rc2014/eeprom/README.md
Normal file
83
recipes/rc2014/eeprom/README.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# Writing to a AT28 from Collapse OS
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Write in an AT28 EEPROM from within Collapse OS so that you can have it update
|
||||||
|
itself.
|
||||||
|
|
||||||
|
## Gathering parts
|
||||||
|
|
||||||
|
* A RC2014 Classic that could install the base recipe
|
||||||
|
* An extra AT28C64B
|
||||||
|
* 1x 40106 inverter gates
|
||||||
|
* Proto board, RC2014 header pins, wires, IC sockets, etc.
|
||||||
|
|
||||||
|
## Building the EEPROM holder
|
||||||
|
|
||||||
|
The AT28 is SRAM compatible so you could use a RAM module for it. However,
|
||||||
|
there is only one RAM module with the Classic version of the RC2014 and we
|
||||||
|
need it to run Collapse OS.
|
||||||
|
|
||||||
|
You could probably use the 64K RAM module for this purpose, but I don't have one
|
||||||
|
and I haven't tried it. For this recipe, I built my own module which is the same
|
||||||
|
as the regular ROM module but with `WR` wired and geared for address range
|
||||||
|
`0x2000-0x3fff`.
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
## Building the kernel
|
||||||
|
|
||||||
|
For this recipe to work, we need a block device for the `at28w` program to read
|
||||||
|
from. The easiest way to go around would be to use a SD card, but maybe you
|
||||||
|
haven't built a SPI relay yet and it's quite a challenge to do so.
|
||||||
|
|
||||||
|
Therefore, for this recipe, we'll have `at28w` read from a memory map and we'll
|
||||||
|
upload contents to write to memory through our serial link.
|
||||||
|
|
||||||
|
`at28w` is designed to be ran as a "user application", but in this case, because
|
||||||
|
we run from a kernel without a filesystem and that `pgm` can't run without it,
|
||||||
|
we'll integrate `at28w` directly in our kernel and expose it as an extra shell
|
||||||
|
command (renaming it to `a28w` to fit the 4 chars limit).
|
||||||
|
|
||||||
|
For all this to work, you'll need [glue code that looks like this](glue.asm).
|
||||||
|
Running `make` in this directory will produce a `os.bin` with that glue code
|
||||||
|
that you can install in the same way you did with the basic RC2014 recipe.
|
||||||
|
|
||||||
|
If your range is different than `0x2000-0x3fff`, you'll have to modify
|
||||||
|
`AT28W_MEMSTART` before you build.
|
||||||
|
|
||||||
|
## Writing contents to the AT28
|
||||||
|
|
||||||
|
The memory map is configured to start at `0xd000`. The first step is to upload
|
||||||
|
contents at that address as documented in ["Load code in RAM and run it"][load].
|
||||||
|
|
||||||
|
You have to know the size of the contents you've loaded because you'll pass it
|
||||||
|
as at argument to `a28w`. You can run:
|
||||||
|
|
||||||
|
Collapse OS
|
||||||
|
> bsel 0
|
||||||
|
> seek 00 0000
|
||||||
|
> a28w <size-of-contents>
|
||||||
|
|
||||||
|
It takes a while to write. About 1 second per byte (soon, I'll implement page
|
||||||
|
writing which should make it much faster).
|
||||||
|
|
||||||
|
If the program doesn't report an error, you're all good! The program takes care
|
||||||
|
of verifying each byte, so everything should be in place. You can verify
|
||||||
|
yourself by `peek`-ing around the `0x2000-0x3fff` range.
|
||||||
|
|
||||||
|
Note that to write a single byte to the AT28 eeprom, you don't need a special
|
||||||
|
program. You can, while you're in the `0x2000-0x3fff` range, run `poke 1` and
|
||||||
|
send an arbitrary char. It will work. The problem is with writing multiple
|
||||||
|
bytes: you have to wait until the eeprom is finished writing before writing to
|
||||||
|
a new address, something a regular `poke` doesn't do but `at28w` does.
|
||||||
|
|
||||||
|
[load]: ../../../doc/load-run-code.md
|
||||||
|
|
73
recipes/rc2014/eeprom/glue.asm
Normal file
73
recipes/rc2014/eeprom/glue.asm
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
; classic RC2014 setup (8K ROM + 32K RAM) and a stock Serial I/O module
|
||||||
|
; 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.
|
||||||
|
|
||||||
|
jp init
|
||||||
|
|
||||||
|
; interrupt hook
|
||||||
|
.fill 0x38-$
|
||||||
|
jp aciaInt
|
||||||
|
|
||||||
|
#include "err.h"
|
||||||
|
#include "core.asm"
|
||||||
|
#include "parse.asm"
|
||||||
|
.equ ACIA_RAMSTART RAMSTART
|
||||||
|
#include "acia.asm"
|
||||||
|
|
||||||
|
.equ MMAP_START 0xd000
|
||||||
|
#include "mmap.asm"
|
||||||
|
|
||||||
|
.equ BLOCKDEV_RAMSTART ACIA_RAMEND
|
||||||
|
.equ BLOCKDEV_COUNT 1
|
||||||
|
#include "blockdev.asm"
|
||||||
|
; List of devices
|
||||||
|
.dw mmapGetC, mmapPutC
|
||||||
|
|
||||||
|
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
|
||||||
|
#include "stdio.asm"
|
||||||
|
|
||||||
|
.equ AT28W_RAMSTART STDIO_RAMEND
|
||||||
|
#include "at28w/main.asm"
|
||||||
|
|
||||||
|
.equ SHELL_RAMSTART AT28W_RAMEND
|
||||||
|
.equ SHELL_EXTRA_CMD_COUNT 5
|
||||||
|
#include "shell.asm"
|
||||||
|
; Extra cmds
|
||||||
|
.dw a28wCmd
|
||||||
|
.dw blkBselCmd, blkSeekCmd, blkLoadCmd, blkSaveCmd
|
||||||
|
|
||||||
|
#include "blockdev_cmds.asm"
|
||||||
|
|
||||||
|
init:
|
||||||
|
di
|
||||||
|
; setup stack
|
||||||
|
ld hl, RAMEND
|
||||||
|
ld sp, hl
|
||||||
|
im 1
|
||||||
|
|
||||||
|
call aciaInit
|
||||||
|
ld hl, aciaGetC
|
||||||
|
ld de, aciaPutC
|
||||||
|
call stdioInit
|
||||||
|
call shellInit
|
||||||
|
|
||||||
|
xor a
|
||||||
|
ld de, BLOCKDEV_SEL
|
||||||
|
call blkSel
|
||||||
|
|
||||||
|
ei
|
||||||
|
jp shellLoop
|
||||||
|
|
||||||
|
a28wCmd:
|
||||||
|
.db "a28w", 0b011, 0b001
|
||||||
|
ld a, (hl)
|
||||||
|
ld (AT28W_MAXBYTES+1), a
|
||||||
|
inc hl
|
||||||
|
ld a, (hl)
|
||||||
|
ld (AT28W_MAXBYTES), a
|
||||||
|
jp at28wInner
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user