mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-02 12:20:56 +11:00
Compare commits
No commits in common. "98191d0b0deed5498dab89103d9e5ad91b3a4286" and "29a6ee128d07fe437944c328cbcec87301b7fc53" have entirely different histories.
98191d0b0d
...
29a6ee128d
@ -110,11 +110,11 @@
|
|||||||
( TODO implement RLCELL )
|
( TODO implement RLCELL )
|
||||||
|
|
||||||
( Copy dict from target wordref, including header, up to HERE.
|
( Copy dict from target wordref, including header, up to HERE.
|
||||||
We're going relocate those words by specified offset. To do
|
We're going to compact the space between that word and its
|
||||||
this, we're copying this whole memory area in HERE and then
|
prev word. To do this, we're copying this whole memory area
|
||||||
iterate through that copied area and call RLWORD on each
|
in HERE and then iterate through that copied area and call
|
||||||
word. That results in a dict that can be concatenated to
|
RLWORD on each word. That results in a dict that can be
|
||||||
target's prev entry in a more compact way.
|
concatenated to target's prev entry in a more compact way.
|
||||||
|
|
||||||
This copy of data doesn't allocate anything, so H@ doesn't
|
This copy of data doesn't allocate anything, so H@ doesn't
|
||||||
move. Moreover, we reserve 4 bytes at H@ to write our target
|
move. Moreover, we reserve 4 bytes at H@ to write our target
|
||||||
@ -129,16 +129,19 @@
|
|||||||
possible to reliably detect its end. If you need that last
|
possible to reliably detect its end. If you need that last
|
||||||
word, define a dummy word before calling RLDICT.
|
word, define a dummy word before calling RLDICT.
|
||||||
)
|
)
|
||||||
( target offset -- )
|
( target -- )
|
||||||
: RLDICT
|
: RLDICT
|
||||||
( First of all, let's get our offset. It's easy, it's
|
( First of all, let's get our offset. It's easy, it's
|
||||||
target's prev field, which is already an offset, minus
|
target's prev field, which is already an offset, minus
|
||||||
its name length. We expect, in RLDICT that a target's
|
its name length. We expect, in RLDICT that a target's
|
||||||
prev word is a "hook word", that is, an empty word. )
|
prev word is a "hook word", that is, an empty word. )
|
||||||
( H@+2 == offset )
|
|
||||||
H@ 2+ ! ( target )
|
|
||||||
( H@ == target )
|
( H@ == target )
|
||||||
H@ ! ( )
|
DUP H@ !
|
||||||
|
DUP 1- C@ 0x7f AND ( t namelen )
|
||||||
|
SWAP 3 - @ ( namelen po )
|
||||||
|
-^ ( o )
|
||||||
|
( H@+2 == offset )
|
||||||
|
H@ 2+ ! ( )
|
||||||
( We have our offset, now let's copy our memory chunk )
|
( We have our offset, now let's copy our memory chunk )
|
||||||
H@ @ WORD( ( src )
|
H@ @ WORD( ( src )
|
||||||
DUP H@ -^ ( src u )
|
DUP H@ -^ ( src u )
|
||||||
@ -177,9 +180,5 @@
|
|||||||
|
|
||||||
( Relink a regular Forth full interpreter. )
|
( Relink a regular Forth full interpreter. )
|
||||||
: RLCORE
|
: RLCORE
|
||||||
LIT< H@ (find) DROP ( target )
|
LIT< H@ (find) DROP RLDICT
|
||||||
DUP 3 - @ ( t prevoff )
|
|
||||||
( subtract H@ name length )
|
|
||||||
2- ( t o )
|
|
||||||
RLDICT
|
|
||||||
;
|
;
|
||||||
|
@ -210,49 +210,22 @@ That's it! our binary is ready to run!
|
|||||||
|
|
||||||
../../emul/hw/rc2014/classic stage2r.bin
|
../../emul/hw/rc2014/classic stage2r.bin
|
||||||
|
|
||||||
And there you have it, a stage2 binary that you've assembled yourself.
|
And there you have it, a stage2 binary that you've assembled yourself. Now,
|
||||||
|
here's for your homework: use the same technique to add the contents of
|
||||||
|
`readln.fs` and `adev.fs` to stage2 so that you have a full-featured
|
||||||
|
interpreter.
|
||||||
|
|
||||||
### Assembling stage 3
|
Name it `stage3.bin` (the version without any source code appended and no
|
||||||
|
`INIT` word defined), you'll need this binary for sub-recipes written for the
|
||||||
|
RC2014.
|
||||||
|
|
||||||
Stage 2 gives you a useable prompt, but bare. Because 8K isn't a lot of space
|
Here's a little cheatsheet, but seriously, you should figure most of it
|
||||||
to cram source code, we're limited in what we can include for this stage.
|
yourself. Tough love they call it.
|
||||||
|
|
||||||
However, now that we have a usable prompt, we can do a lot (be cautious though:
|
* `cat stage2.bin ../../forth/readln.fs ../../forth/adev.fs run.fs > stage2r.bin`
|
||||||
there is no `readln` yet, so you have no backspace), for example, build a
|
* Don't forget `RDLN$` and `ADEV$`.
|
||||||
stage 3 with `readln`.
|
* `RLDICT` is like `RLCORE` but with a chosen target.
|
||||||
|
* `stripfc` can help you deal with size constraints.
|
||||||
Copy the unit's source
|
|
||||||
|
|
||||||
cat ../../forth/readln.fs | ../../tools/stripfc | xclip
|
|
||||||
|
|
||||||
and just paste it in your terminal. If you're doing the real thing and not
|
|
||||||
using the emulator, pasting so much code at once might freeze up the RC2014, so
|
|
||||||
it is recommended that you use `/tools/exec` that let the other side enough
|
|
||||||
time to breathe.
|
|
||||||
|
|
||||||
After your pasting, you'll have a compiled dict of that code in memory. You'll
|
|
||||||
need to relocate it in the same way you did for stage 2, but instead of using
|
|
||||||
`RLCORE`, which is a convenience word hardcoded for stage 1, we'll parametrize
|
|
||||||
`RLDICT`, the word doing the real work.
|
|
||||||
|
|
||||||
`RLDICT` takes 2 arguments, `target` and `offset`. `target` is the first word
|
|
||||||
of your relocated dict. In our case, it's going to be `' INBUFSZ`. `offset` is
|
|
||||||
the offset we'll apply to every eligible word references in our dict. In our
|
|
||||||
case, that offset is the offset of the *beginning* of the `INBUFSZ` entry (that
|
|
||||||
is, `' INBUFSZ WORD(` minus the offset of the last word (which should be a hook
|
|
||||||
word) in the ROM binary.
|
|
||||||
|
|
||||||
That offset can be conveniently fetched from code because it is the value of
|
|
||||||
the `LATEST` constant in stable ABI, which is at offset `0x08`. Therefore, our
|
|
||||||
offset value is:
|
|
||||||
|
|
||||||
' INBUFSZ WORD( 0x08 @ -
|
|
||||||
|
|
||||||
You can now run `RLDICT` and proceed with concatenation (and manual adjustments
|
|
||||||
of course) as you did with stage 2. Don't forget to adjust `run.fs` so that it
|
|
||||||
initializes `RDLN$` instead of creating a minimal `(c<)`.
|
|
||||||
|
|
||||||
Keep that `stage3.bin` around, you will need it for further recipes.
|
|
||||||
|
|
||||||
[rc2014]: https://rc2014.co.uk
|
[rc2014]: https://rc2014.co.uk
|
||||||
[romwrite]: https://github.com/hsoft/romwrite
|
[romwrite]: https://github.com/hsoft/romwrite
|
||||||
|
@ -33,17 +33,24 @@ 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.
|
||||||
|
|
||||||
## Building your stage 4
|
## Using the at28 driver
|
||||||
|
|
||||||
Using the same technique as you used for building your stage 3, you can append
|
The AT28 driver is at `drv/at28.fs` and is a pure forth source file so it's
|
||||||
required words to your boot binary. Required units are `forth/adev.fs` and
|
rather easy to set up from the base Stage 3 binary:
|
||||||
`drv/at28.fs`.
|
|
||||||
|
cat ../stage3.bin ../pre.fs ../../../drv/at28.fs ../run.fs > os.bin
|
||||||
|
../../../emul/hw/rc2014/classic os.bin
|
||||||
|
|
||||||
## Writing contents to the AT28
|
## Writing contents to the AT28
|
||||||
|
|
||||||
The driver provides `AT28!` which can be plugged in adev's `A!*`.
|
The driver provides `AT28!` which can be plugged in adev's `A!*`.
|
||||||
|
|
||||||
First, upload your binary to some place in memory, for example `a000`. To do so,
|
It's not in the Stage 3 binary, but because it's a small piece of Forth code,
|
||||||
|
let's just run its definition code:
|
||||||
|
|
||||||
|
cat ../../../drv/at28.fs | ./stripfc | ./exec <tty device>
|
||||||
|
|
||||||
|
Then, upload your binary to some place in memory, for example `a000`. To do so,
|
||||||
run this from your modern computer:
|
run this from your modern computer:
|
||||||
|
|
||||||
./upload <tty device> a000 <filename>
|
./upload <tty device> a000 <filename>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
: (c<) KEY DUP EMIT ;
|
: (c<) KEY DUP EMIT ;
|
||||||
: INIT
|
: INIT
|
||||||
ACIA$
|
ACIA$
|
||||||
." Collapse OS" CRLF
|
." Collapse OS" CR LF
|
||||||
( 0c == CINPTR )
|
( 0c == CINPTR )
|
||||||
['] (c<) 0x0c RAM+ !
|
['] (c<) 0x0c RAM+ !
|
||||||
;
|
;
|
||||||
|
19
recipes/rc2014/sdcard/Makefile
Normal file
19
recipes/rc2014/sdcard/Makefile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
TARGETS = os.bin cfsin/helo
|
||||||
|
BASEDIR = ../../..
|
||||||
|
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||||
|
KERNEL = $(BASEDIR)/kernel
|
||||||
|
APPS = $(BASEDIR)/apps
|
||||||
|
CFSPACK = $(BASEDIR)/tools/cfspack/cfspack
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(TARGETS) sdcard.cfs
|
||||||
|
os.bin: glue.asm
|
||||||
|
cfsin/helo: helo.asm
|
||||||
|
$(TARGETS):
|
||||||
|
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
|
||||||
|
|
||||||
|
$(CFSPACK):
|
||||||
|
make -C $(BASEDIR)/tools/cfspack
|
||||||
|
|
||||||
|
sdcard.cfs: cfsin $(CFSPACK)
|
||||||
|
$(CFSPACK) cfsin > $@
|
@ -8,7 +8,7 @@ You can't really keep pins high and low on an IO line. You need some kind of
|
|||||||
intermediary between z80 IOs and SPI.
|
intermediary between z80 IOs and SPI.
|
||||||
|
|
||||||
There are many ways to achieve this. This recipe explains how to build your own
|
There are many ways to achieve this. This recipe explains how to build your own
|
||||||
hacked off SPI relay for the RC2014. It can then be used with `sdc.fs` to
|
hacked off SPI relay for the RC2014. It can then be used with `sdc.asm` to
|
||||||
drive a SD card.
|
drive a SD card.
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
@ -18,8 +18,10 @@ design.
|
|||||||
|
|
||||||
## Gathering parts
|
## Gathering parts
|
||||||
|
|
||||||
* A RC2014 Classic
|
* A RC2014 with Collapse OS with these features:
|
||||||
* `stage3.bin` from the base recipe
|
* shell
|
||||||
|
* blockdev
|
||||||
|
* sdc
|
||||||
* A MicroSD breakout board. I use Adafruit's.
|
* A MicroSD breakout board. I use Adafruit's.
|
||||||
* A proto board + header pins with 39 positions so we can make a RC2014 card.
|
* A proto board + header pins with 39 positions so we can make a RC2014 card.
|
||||||
* Diodes, resistors and stuff
|
* Diodes, resistors and stuff
|
||||||
@ -32,7 +34,7 @@ design.
|
|||||||
|
|
||||||
## Building the SPI relay
|
## Building the SPI relay
|
||||||
|
|
||||||
The [schematic][schematic] supplied with this recipe works well with `sdc.fs`.
|
The [schematic][schematic] supplied with this recipe works well with `sdc.asm`.
|
||||||
Of course, it's not the only possible design that works, but I think it's one
|
Of course, it's not the only possible design that works, but I think it's one
|
||||||
of the most straighforwards.
|
of the most straighforwards.
|
||||||
|
|
||||||
@ -69,41 +71,95 @@ matter. However, it *does* matter for the `SELECT` line, so I don't follow my
|
|||||||
own schematic with regards to the `M1` and `A2` lines and use two inverters
|
own schematic with regards to the `M1` and `A2` lines and use two inverters
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
## Building your stage 4
|
## Building the kernel
|
||||||
|
|
||||||
Using the same technique as you used for building your stage 3, you can append
|
To be able to work with your SPI relay and communicate with the card, you
|
||||||
required words to your boot binary. Required units are `forth/blk.fs` and
|
should have [glue code that looks like this](glue.asm).
|
||||||
`drv/sdc.fs`. You also need `drv/sdc.z80` but to save you the troubles of
|
|
||||||
rebuilding from stage 1 for this recipe, we took the liberty of already having
|
|
||||||
included it in the base recipe.
|
|
||||||
|
|
||||||
## Testing in the emulator
|
Initially, when you don't know if things work well yet, you should comment out
|
||||||
|
the block creation part.
|
||||||
|
|
||||||
The RC2014 emulator includes SDC emulation. You can attach a SD card image to
|
## Reading from the SD card
|
||||||
it by invoking it with a second argument:
|
|
||||||
|
|
||||||
../../../emul/hw/rc2014/classic stage4.bin ../../../emul/blkfs
|
The first thing we'll do is fill the SD card's first 12 bytes with "Hello
|
||||||
|
World!":
|
||||||
|
|
||||||
You will then run with a SD card having the contents from `/blk`.
|
echo "Hello World!" > /dev/sdX
|
||||||
|
|
||||||
## Usage
|
Then, insert your SD card in your SPI relay and boot the RC2014.
|
||||||
|
|
||||||
First, the SD card needs to be initialized
|
Run the `sdci` command which will initialize the card. The blockdev 0 is
|
||||||
|
already selected at initialization, but you could, to be sure, run `bsel 0` to
|
||||||
|
select the first blockdev, which is configured to be the sd card.
|
||||||
|
|
||||||
SDC$
|
Set your memory pointer to somewhere you can write to with `mptr 9000` and then
|
||||||
|
you're ready to load your contents with `load d` (load the 13 bytes that you
|
||||||
|
wrote to your sd card earlier. You can then `peek d` and see that your
|
||||||
|
"Hello World!\n" got loaded in memory!
|
||||||
|
|
||||||
If there is no error message, we're fine. Then, we need to hook `BLK@*` and
|
## Mounting a filesystem from the SD card
|
||||||
`BLK!*` into the SDC driver:
|
|
||||||
|
|
||||||
' SDC@ BLK@* !
|
The Makefile compiles `helo.asm` in `cfsin` and then packs `cfsin` into a CFS
|
||||||
' SDC! BLK!* !
|
filesystem into the `sdcard.cfs` file. That can be mounted by Collapse OS!
|
||||||
|
|
||||||
And thats it! You have full access to disk block mechanism:
|
$ cat sdcard.cfs > /dev/sdX
|
||||||
|
|
||||||
102 LOAD
|
Then, you insert your SD card in your SPI relay and go:
|
||||||
BROWSE
|
|
||||||
|
|
||||||
(at this moment, the driver is a bit slow though...)
|
Collapse OS
|
||||||
|
> sdci
|
||||||
|
> fson
|
||||||
|
> fls
|
||||||
|
helo
|
||||||
|
hello.txt
|
||||||
|
> helo
|
||||||
|
Hello!
|
||||||
|
>
|
||||||
|
|
||||||
|
The `helo` command is a bit magical and is due to the hook implemented in
|
||||||
|
`pgm.asm`: when an unknown command is typed, it looks in the currently mounted
|
||||||
|
filesystem for a file with the same name. If it finds it, it loads it in memory
|
||||||
|
at a predefined place (in our case, `0x9000`) and executes it.
|
||||||
|
|
||||||
|
Now let that sink in for a minute. You've just mounted a filesystem on a SD
|
||||||
|
card, loaded a file from it in memory and executed that file, all that on a
|
||||||
|
kernel that weights less than 3 kilobytes!
|
||||||
|
|
||||||
|
## Writing to a file in the SD card
|
||||||
|
|
||||||
|
Now what we're going to do is to write back to a file on the SD card. From a
|
||||||
|
system with the SD card initialized and the FS mounted, do:
|
||||||
|
|
||||||
|
> fopn 0 hello.txt
|
||||||
|
> bsel 1
|
||||||
|
> mptr 9000
|
||||||
|
9000
|
||||||
|
> load d
|
||||||
|
> peek d
|
||||||
|
48656C6C6F20576F726C64210A
|
||||||
|
|
||||||
|
Now that we have our "Hello World!\n" loaded in memory, let's modify it and make
|
||||||
|
it start with "XXX" and save it to the file. `sdcf` flushes the current SD card
|
||||||
|
buffer to the card. It's automatically ran whenever we change sector during a
|
||||||
|
read/write/seek, but was can also explicitly call it with `sdcf`.
|
||||||
|
|
||||||
|
> poke 3
|
||||||
|
[type "XXX"]
|
||||||
|
> peek d
|
||||||
|
5858586C6F20576F726C64210A
|
||||||
|
> seek 00 0000
|
||||||
|
0000
|
||||||
|
> save d
|
||||||
|
> sdcf
|
||||||
|
|
||||||
|
The new "XXXlo World!\n" is now written to the card, at its proper place in CFS!
|
||||||
|
You can verify this by pulling out the card (no need to unmount it from Collapse
|
||||||
|
OS, but if you insert it again, you'll need to run `sdci` again), insert it in
|
||||||
|
your modern system and run:
|
||||||
|
|
||||||
|
$ head -c 512 /dev/sdX | xxd
|
||||||
|
|
||||||
|
You'll see your "XXXlo World!\n" somewhere, normally at offset `0x120`!
|
||||||
|
|
||||||
[schematic]: spirelay/spirelay.pdf
|
[schematic]: spirelay/spirelay.pdf
|
||||||
[inspiration]: https://www.ecstaticlyrics.com/electronics/SPI/fast_z80_interface.html
|
[inspiration]: https://www.ecstaticlyrics.com/electronics/SPI/fast_z80_interface.html
|
||||||
|
114
recipes/rc2014/sdcard/glue.asm
Normal file
114
recipes/rc2014/sdcard/glue.asm
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
; 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.
|
||||||
|
.equ USER_CODE 0xa000
|
||||||
|
|
||||||
|
jp init ; 3 bytes
|
||||||
|
|
||||||
|
; *** Jump Table ***
|
||||||
|
jp printstr
|
||||||
|
jp sdcWaitResp
|
||||||
|
jp sdcCmd
|
||||||
|
jp sdcCmdR1
|
||||||
|
jp sdcCmdR7
|
||||||
|
jp sdcSendRecv
|
||||||
|
|
||||||
|
; interrupt hook
|
||||||
|
.fill 0x38-$
|
||||||
|
jp aciaInt
|
||||||
|
|
||||||
|
.inc "err.h"
|
||||||
|
.inc "ascii.h"
|
||||||
|
.inc "blkdev.h"
|
||||||
|
.inc "fs.h"
|
||||||
|
.inc "core.asm"
|
||||||
|
.inc "str.asm"
|
||||||
|
.equ ACIA_RAMSTART RAMSTART
|
||||||
|
.inc "acia.asm"
|
||||||
|
.equ BLOCKDEV_RAMSTART ACIA_RAMEND
|
||||||
|
.equ BLOCKDEV_COUNT 2
|
||||||
|
.inc "blockdev.asm"
|
||||||
|
; List of devices
|
||||||
|
.dw sdcGetB, sdcPutB
|
||||||
|
.dw blk2GetB, blk2PutB
|
||||||
|
|
||||||
|
|
||||||
|
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
|
||||||
|
.equ STDIO_GETC aciaGetC
|
||||||
|
.equ STDIO_PUTC aciaPutC
|
||||||
|
.inc "stdio.asm"
|
||||||
|
|
||||||
|
.equ FS_RAMSTART STDIO_RAMEND
|
||||||
|
.equ FS_HANDLE_COUNT 1
|
||||||
|
.inc "fs.asm"
|
||||||
|
|
||||||
|
; *** BASIC ***
|
||||||
|
|
||||||
|
; RAM space used in different routines for short term processing.
|
||||||
|
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
|
||||||
|
.equ SCRATCHPAD FS_RAMEND
|
||||||
|
.inc "lib/util.asm"
|
||||||
|
.inc "lib/ari.asm"
|
||||||
|
.inc "lib/parse.asm"
|
||||||
|
.inc "lib/fmt.asm"
|
||||||
|
.equ EXPR_PARSE parseLiteralOrVar
|
||||||
|
.inc "lib/expr.asm"
|
||||||
|
.inc "basic/util.asm"
|
||||||
|
.inc "basic/parse.asm"
|
||||||
|
.inc "basic/tok.asm"
|
||||||
|
.equ VAR_RAMSTART SCRATCHPAD+SCRATCHPAD_SIZE
|
||||||
|
.inc "basic/var.asm"
|
||||||
|
.equ BUF_RAMSTART VAR_RAMEND
|
||||||
|
.inc "basic/buf.asm"
|
||||||
|
.inc "basic/blk.asm"
|
||||||
|
.inc "basic/sdc.asm"
|
||||||
|
.equ BFS_RAMSTART BUF_RAMEND
|
||||||
|
.inc "basic/fs.asm"
|
||||||
|
.equ BAS_RAMSTART BFS_RAMEND
|
||||||
|
.inc "basic/main.asm"
|
||||||
|
|
||||||
|
.equ SDC_RAMSTART BAS_RAMEND
|
||||||
|
.equ SDC_PORT_CSHIGH 6
|
||||||
|
.equ SDC_PORT_CSLOW 5
|
||||||
|
.equ SDC_PORT_SPI 4
|
||||||
|
.inc "sdc.asm"
|
||||||
|
|
||||||
|
init:
|
||||||
|
di
|
||||||
|
ld sp, RAMEND
|
||||||
|
im 1
|
||||||
|
call aciaInit
|
||||||
|
call fsInit
|
||||||
|
call basInit
|
||||||
|
ld hl, basFindCmdExtra
|
||||||
|
ld (BAS_FINDHOOK), hl
|
||||||
|
|
||||||
|
xor a
|
||||||
|
ld de, BLOCKDEV_SEL
|
||||||
|
call blkSel
|
||||||
|
|
||||||
|
ei
|
||||||
|
jp basStart
|
||||||
|
|
||||||
|
basFindCmdExtra:
|
||||||
|
ld hl, basFSCmds
|
||||||
|
call basFindCmd
|
||||||
|
ret z
|
||||||
|
ld hl, basBLKCmds
|
||||||
|
call basFindCmd
|
||||||
|
ret z
|
||||||
|
ld hl, basSDCCmds
|
||||||
|
jp basFindCmd
|
||||||
|
|
||||||
|
; *** blkdev 2: file handle 0 ***
|
||||||
|
|
||||||
|
blk2GetB:
|
||||||
|
ld ix, FS_HANDLES
|
||||||
|
jp fsGetB
|
||||||
|
|
||||||
|
blk2PutB:
|
||||||
|
ld ix, FS_HANDLES
|
||||||
|
jp fsPutB
|
12
recipes/rc2014/sdcard/helo.asm
Normal file
12
recipes/rc2014/sdcard/helo.asm
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
; prints "Hello!" on screen
|
||||||
|
.equ printstr 0x03
|
||||||
|
|
||||||
|
.org 0x9000
|
||||||
|
|
||||||
|
ld hl, sHello
|
||||||
|
call printstr
|
||||||
|
xor a ; success
|
||||||
|
ret
|
||||||
|
|
||||||
|
sHello:
|
||||||
|
.db "Hello!", 0x0d, 0x0a, 0
|
Loading…
Reference in New Issue
Block a user