Compare commits
4 Commits
dfaa1dc101
...
d466c6d95d
Author | SHA1 | Date | |
---|---|---|---|
|
d466c6d95d | ||
|
6ae65940d2 | ||
|
0892193cbe | ||
|
98191d0b0d |
1
blk/001
1
blk/001
@ -2,6 +2,7 @@ MASTER INDEX
|
|||||||
|
|
||||||
3 Usage 30 Dictionary
|
3 Usage 30 Dictionary
|
||||||
70 Implementation notes 100 Block editor
|
70 Implementation notes 100 Block editor
|
||||||
|
200 Z80 assembler
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
13
blk/200
Normal file
13
blk/200
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Z80 Assembler
|
||||||
|
|
||||||
|
201 Guide 208 Instructions list
|
||||||
|
212 Loader 213 Variables & consts
|
||||||
|
215 Utils 216 OP1
|
||||||
|
218 OP1r 220 OP1qq
|
||||||
|
222 OP1rr 224 OP2
|
||||||
|
226 OP2n 228 OP2rn
|
||||||
|
230 OP2br 232 OProt
|
||||||
|
234 OP2r 236 OP2ss
|
||||||
|
238 OP3ddnn 240 OP3nn
|
||||||
|
242 Specials 246 Flow
|
||||||
|
|
15
blk/201
Normal file
15
blk/201
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
The following words allow you to assemble z80 binaries. Being
|
||||||
|
Forth words, opcode assembly is a bit different than with a
|
||||||
|
typical assembler. For example, what would traditionally be
|
||||||
|
"ld a, b" would become "A B LDrr,".
|
||||||
|
|
||||||
|
H@ offset at which we consider our PC 0. Used to compute PC. To
|
||||||
|
have a proper PC, call "H@ ORG !" at the beginning of your
|
||||||
|
assembly process.
|
||||||
|
|
||||||
|
Labels are a convenient way of managing relative jump
|
||||||
|
calculations. Backward labels are easy. It is only a matter or
|
||||||
|
recording "HERE" and do subtractions. Forward labels record the
|
||||||
|
place where we should write the offset, and then when we get to
|
||||||
|
that point later on, the label records the offset there.
|
||||||
|
|
13
blk/202
Normal file
13
blk/202
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
To avoid using dict memory in compilation targets, we
|
||||||
|
pre-declare label variables here, which means we have a limited
|
||||||
|
number of it. For now, 4 ought to be enough.
|
||||||
|
|
||||||
|
Flow
|
||||||
|
|
||||||
|
There are 2 label types: backward and forward. For each type,
|
||||||
|
there are two actions: set and write. Setting a label is
|
||||||
|
declaring where it is. It has to be performed at the label's
|
||||||
|
destination. Writing a label is writing its offset difference
|
||||||
|
to the binary result. It has to be done right after a relative
|
||||||
|
jump operation. Yes, labels are only for relative jumps.
|
||||||
|
|
14
blk/203
Normal file
14
blk/203
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
For backward labels, set happens before write. For forward
|
||||||
|
labels, write happen before set. The write operation writes a
|
||||||
|
dummy placeholder, and then the set operation writes the offset
|
||||||
|
at that placeholder's address.
|
||||||
|
|
||||||
|
Variable actions are expected to be called with labels in
|
||||||
|
front of them. Example, "L2 FSET"
|
||||||
|
|
||||||
|
About that "1 -": z80 relative jumps record "e-2", that is,
|
||||||
|
the offset that *counts the 2 bytes of the jump itself*.
|
||||||
|
Because we set the label *after* the jump OP1 itself, that's 1
|
||||||
|
byte that is taken care of. We still need to adjust by another
|
||||||
|
byte before writing the offset.
|
||||||
|
|
16
blk/208
Normal file
16
blk/208
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Instructions list
|
||||||
|
|
||||||
|
r => A B C D E H L (HL)
|
||||||
|
ss/qq => BC DE HL AF/SP
|
||||||
|
cc => CNZ CZ CNC CC CPO CPE CP CM
|
||||||
|
|
||||||
|
LD [rr, rn, ddnn, (nn)HL, HL(nn), dd(nn), (nn)dd, rIXY, IXYr,
|
||||||
|
(DE)A, A(DE)]
|
||||||
|
ADD [r, n, HLss, IXss, IXIX, IYss, IYIY]
|
||||||
|
ADC [r, HLss]
|
||||||
|
CP [r]
|
||||||
|
SBC [r, HLss]
|
||||||
|
SUB [r, n]
|
||||||
|
PUSH [qq] POP [qq]
|
||||||
|
INC [r, ss] DEC [r, ss]
|
||||||
|
AND [r, n] OR [r, n] XOR [r, n] (cont.)
|
11
blk/209
Normal file
11
blk/209
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
(cont.)
|
||||||
|
OUT [nA, (C)r] IN [An, r(C)]
|
||||||
|
SET [br] RES [br] BIT [br]
|
||||||
|
RL [r] RLC [r] SLA [r] RLA RLCA
|
||||||
|
RR [r] RRC [r] SRL [r] RRA RRCA
|
||||||
|
CALL [nn] DJNZ
|
||||||
|
JP [nn, (HL), (IX), (IY)]
|
||||||
|
JR [, Z, NZ, C, NC]
|
||||||
|
|
||||||
|
DI EI EXDEHL EXX HALT
|
||||||
|
NOP RET SCF
|
8
blk/212
Normal file
8
blk/212
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
( 59 == z80a's memory )
|
||||||
|
H@ 0x59 RAM+ !
|
||||||
|
10 ALLOT
|
||||||
|
|
||||||
|
213 LOAD 215 LOAD 216 LOAD 217 LOAD 218 LOAD 219 LOAD
|
||||||
|
220 LOAD 222 LOAD 223 LOAD 224 LOAD 226 LOAD 228 LOAD
|
||||||
|
230 LOAD 232 LOAD 234 LOAD 236 LOAD 238 LOAD 240 LOAD
|
||||||
|
242 LOAD 243 LOAD 244 LOAD 246 LOAD 247 LOAD
|
9
blk/213
Normal file
9
blk/213
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
: Z80AMEM+ 0x59 RAM+ @ + ;
|
||||||
|
: ORG 0 Z80AMEM+ ;
|
||||||
|
: L1 2 Z80AMEM+ ; : L2 4 Z80AMEM+ ;
|
||||||
|
: L3 6 Z80AMEM+ ; : L4 8 Z80AMEM+ ;
|
||||||
|
: A 7 ; : B 0 ; : C 1 ; : D 2 ;
|
||||||
|
: E 3 ; : H 4 ; : L 5 ; : (HL) 6 ;
|
||||||
|
: BC 0 ; : DE 1 ; : HL 2 ; : AF 3 ; : SP AF ;
|
||||||
|
: CNZ 0 ; : CZ 1 ; : CNC 2 ; : CC 3 ;
|
||||||
|
: CPO 4 ; : CPE 5 ; : CP 6 ; : CM 7 ;
|
16
blk/215
Normal file
16
blk/215
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
( Splits word into msb/lsb, lsb being on TOS )
|
||||||
|
: SPLITB
|
||||||
|
256 /MOD SWAP
|
||||||
|
;
|
||||||
|
: PC H@ ORG @ - ;
|
||||||
|
( A, spits an assembled byte, A,, spits an assembled word
|
||||||
|
Both increase PC. To debug, change C, to .X )
|
||||||
|
: A, C, ; : A,, SPLITB A, A, ;
|
||||||
|
: <<3 8 * ; : <<4 16 * ;
|
||||||
|
( As a general rule, IX and IY are equivalent to spitting an
|
||||||
|
extra 0xdd / 0xfd and then spit the equivalent of HL )
|
||||||
|
: IX 0xdd A, HL ; : IY 0xfd A, HL ;
|
||||||
|
: _ix+- 0xff AND 0xdd A, (HL) ;
|
||||||
|
: _iy+- 0xff AND 0xfd A, (HL) ;
|
||||||
|
: IX+ _ix+- ; : IX- 0 -^ _ix+- ;
|
||||||
|
: IY+ _iy+- ; : IY- 0 -^ _iy+- ;
|
10
blk/216
Normal file
10
blk/216
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
: OP1 CREATE C, DOES> C@ A, ;
|
||||||
|
0xf3 OP1 DI, 0xfb OP1 EI,
|
||||||
|
0xeb OP1 EXDEHL, 0xd9 OP1 EXX,
|
||||||
|
0x76 OP1 HALT, 0xe9 OP1 JP(HL),
|
||||||
|
0x12 OP1 LD(DE)A, 0x1a OP1 LDA(DE),
|
||||||
|
0x00 OP1 NOP, 0xc9 OP1 RET,
|
||||||
|
0x17 OP1 RLA, 0x07 OP1 RLCA,
|
||||||
|
0x1f OP1 RRA, 0x0f OP1 RRCA,
|
||||||
|
0x37 OP1 SCF,
|
||||||
|
|
11
blk/217
Normal file
11
blk/217
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
( Relative jumps are a bit special. They're supposed to take
|
||||||
|
an argument, but they don't take it so they can work with
|
||||||
|
the label system. Therefore, relative jumps are an OP1 but
|
||||||
|
when you use them, you're expected to write the offset
|
||||||
|
afterwards yourself. )
|
||||||
|
|
||||||
|
0x18 OP1 JR, 0x10 OP1 DJNZ,
|
||||||
|
0x38 OP1 JRC, 0x30 OP1 JRNC,
|
||||||
|
0x28 OP1 JRZ, 0x20 OP1 JRNZ,
|
||||||
|
|
||||||
|
|
10
blk/218
Normal file
10
blk/218
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
( r -- )
|
||||||
|
: OP1r
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
C@ ( r op )
|
||||||
|
SWAP ( op r )
|
||||||
|
<<3 ( op r<<3 )
|
||||||
|
OR A,
|
||||||
|
;
|
||||||
|
0x04 OP1r INCr, 0x05 OP1r DECr,
|
13
blk/219
Normal file
13
blk/219
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
( also works for cc )
|
||||||
|
0xc0 OP1r RETcc,
|
||||||
|
( r -- )
|
||||||
|
: OP1r0
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
C@ ( r op )
|
||||||
|
OR A,
|
||||||
|
;
|
||||||
|
0x80 OP1r0 ADDr, 0x88 OP1r0 ADCr,
|
||||||
|
0xa0 OP1r0 ANDr, 0xb8 OP1r0 CPr,
|
||||||
|
0xb0 OP1r0 ORr, 0x90 OP1r0 SUBr,
|
||||||
|
0x98 OP1r0 SBCr, 0xa8 OP1r0 XORr,
|
15
blk/220
Normal file
15
blk/220
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
( qq -- also works for ss )
|
||||||
|
: OP1qq
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
C@ ( qq op )
|
||||||
|
SWAP ( op qq )
|
||||||
|
<<4 ( op qq<<4 )
|
||||||
|
OR A,
|
||||||
|
;
|
||||||
|
0xc5 OP1qq PUSHqq, 0xc1 OP1qq POPqq,
|
||||||
|
0x03 OP1qq INCss, 0x0b OP1qq DECss,
|
||||||
|
0x09 OP1qq ADDHLss,
|
||||||
|
|
||||||
|
: ADDIXss, 0xdd A, ADDHLss, ; : ADDIXIX, HL ADDIXss, ;
|
||||||
|
: ADDIYss, 0xfd A, ADDHLss, ; : ADDIYIY, HL ADDIYss, ;
|
14
blk/222
Normal file
14
blk/222
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
: _1rr
|
||||||
|
C@ ( rd rr op )
|
||||||
|
ROT ( rr op rd )
|
||||||
|
<<3 ( rr op rd<<3 )
|
||||||
|
OR OR A,
|
||||||
|
;
|
||||||
|
|
||||||
|
( rd rr )
|
||||||
|
: OP1rr
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
_1rr
|
||||||
|
;
|
||||||
|
0x40 OP1rr LDrr,
|
13
blk/223
Normal file
13
blk/223
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
( ixy+- HL rd )
|
||||||
|
: LDIXYr,
|
||||||
|
( dd/fd has already been spit )
|
||||||
|
LDrr, ( ixy+- )
|
||||||
|
A,
|
||||||
|
;
|
||||||
|
|
||||||
|
( rd ixy+- HL )
|
||||||
|
: LDrIXY,
|
||||||
|
ROT ( ixy+- HL rd )
|
||||||
|
SWAP ( ixy+- rd HL )
|
||||||
|
LDIXYr,
|
||||||
|
;
|
7
blk/224
Normal file
7
blk/224
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
: OP2 CREATE , DOES> @ 256 /MOD A, A, ;
|
||||||
|
0xedb1 OP2 CPIR,
|
||||||
|
0xed46 OP2 IM0,
|
||||||
|
0xed56 OP2 IM1,
|
||||||
|
0xed5e OP2 IM2,
|
||||||
|
0xed44 OP2 NEG,
|
||||||
|
0xed4d OP2 RETI,
|
13
blk/226
Normal file
13
blk/226
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
( n -- )
|
||||||
|
: OP2n
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
C@ A, A,
|
||||||
|
;
|
||||||
|
0xd3 OP2n OUTnA,
|
||||||
|
0xdb OP2n INAn,
|
||||||
|
0xc6 OP2n ADDn,
|
||||||
|
0xe6 OP2n ANDn,
|
||||||
|
0xf6 OP2n ORn,
|
||||||
|
0xd6 OP2n SUBn,
|
||||||
|
|
11
blk/228
Normal file
11
blk/228
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
( r n -- )
|
||||||
|
: OP2rn
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
C@ ( r n op )
|
||||||
|
ROT ( n op r )
|
||||||
|
<<3 ( n op r<<3 )
|
||||||
|
OR A, A,
|
||||||
|
;
|
||||||
|
0x06 OP2rn LDrn,
|
||||||
|
|
14
blk/230
Normal file
14
blk/230
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
( b r -- )
|
||||||
|
: OP2br
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
0xcb A,
|
||||||
|
C@ ( b r op )
|
||||||
|
ROT ( r op b )
|
||||||
|
<<3 ( r op b<<3 )
|
||||||
|
OR OR A,
|
||||||
|
;
|
||||||
|
0xc0 OP2br SETbr,
|
||||||
|
0x80 OP2br RESbr,
|
||||||
|
0x40 OP2br BITbr,
|
||||||
|
|
16
blk/232
Normal file
16
blk/232
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
( bitwise rotation ops have a similar sig )
|
||||||
|
( r -- )
|
||||||
|
: OProt
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
0xcb A,
|
||||||
|
C@ ( r op )
|
||||||
|
OR A,
|
||||||
|
;
|
||||||
|
0x10 OProt RLr,
|
||||||
|
0x00 OProt RLCr,
|
||||||
|
0x18 OProt RRr,
|
||||||
|
0x08 OProt RRCr,
|
||||||
|
0x20 OProt SLAr,
|
||||||
|
0x38 OProt SRLr,
|
||||||
|
|
14
blk/234
Normal file
14
blk/234
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
( cell contains both bytes. MSB is spit as-is, LSB is ORed
|
||||||
|
with r )
|
||||||
|
( r -- )
|
||||||
|
: OP2r
|
||||||
|
CREATE ,
|
||||||
|
DOES>
|
||||||
|
@ SPLITB SWAP ( r lsb msb )
|
||||||
|
A, ( r lsb )
|
||||||
|
SWAP <<3 ( lsb r<<3 )
|
||||||
|
OR A,
|
||||||
|
;
|
||||||
|
0xed41 OP2r OUT(C)r,
|
||||||
|
0xed40 OP2r INr(C),
|
||||||
|
|
12
blk/236
Normal file
12
blk/236
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
( ss -- )
|
||||||
|
: OP2ss
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
0xed A,
|
||||||
|
C@ SWAP ( op ss )
|
||||||
|
<<4 ( op ss<< 4 )
|
||||||
|
OR A,
|
||||||
|
;
|
||||||
|
0x4a OP2ss ADCHLss,
|
||||||
|
0x42 OP2ss SBCHLss,
|
||||||
|
|
11
blk/238
Normal file
11
blk/238
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
( dd nn -- )
|
||||||
|
: OP3ddnn
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
C@ ( dd nn op )
|
||||||
|
ROT ( nn op dd )
|
||||||
|
<<4 ( nn op dd<<4 )
|
||||||
|
OR A,
|
||||||
|
A,,
|
||||||
|
;
|
||||||
|
0x01 OP3ddnn LDddnn,
|
11
blk/240
Normal file
11
blk/240
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
( nn -- )
|
||||||
|
: OP3nn
|
||||||
|
CREATE C,
|
||||||
|
DOES>
|
||||||
|
C@ A,
|
||||||
|
A,,
|
||||||
|
;
|
||||||
|
0xcd OP3nn CALLnn,
|
||||||
|
0xc3 OP3nn JPnn,
|
||||||
|
0x22 OP3nn LD(nn)HL,
|
||||||
|
0x2a OP3nn LDHL(nn),
|
16
blk/242
Normal file
16
blk/242
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
( dd nn -- )
|
||||||
|
: LDdd(nn),
|
||||||
|
0xed A,
|
||||||
|
SWAP <<4 0x4b OR A,
|
||||||
|
A,,
|
||||||
|
;
|
||||||
|
|
||||||
|
( nn dd -- )
|
||||||
|
: LD(nn)dd,
|
||||||
|
0xed A,
|
||||||
|
<<4 0x43 OR A,
|
||||||
|
A,,
|
||||||
|
;
|
||||||
|
|
||||||
|
: JP(IX), IX DROP JP(HL), ;
|
||||||
|
: JP(IY), IY DROP JP(HL), ;
|
14
blk/243
Normal file
14
blk/243
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
( 26 == next )
|
||||||
|
: JPNEXT, 26 JPnn, ;
|
||||||
|
( 29 == chkPS )
|
||||||
|
: chkPS, 29 CALLnn, ;
|
||||||
|
|
||||||
|
: CODE
|
||||||
|
( same as CREATE, but with native word )
|
||||||
|
(entry)
|
||||||
|
( 23 == nativeWord )
|
||||||
|
23 C,
|
||||||
|
;
|
||||||
|
|
||||||
|
: ;CODE JPNEXT, ;
|
||||||
|
|
3
blk/244
Normal file
3
blk/244
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
( Macros )
|
||||||
|
( clear carry + SBC )
|
||||||
|
: SUBHLss, A ORr, SBCHLss, ;
|
13
blk/246
Normal file
13
blk/246
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
( Place BEGIN, where you want to jump back and AGAIN after
|
||||||
|
a relative jump operator. Just like BSET and BWR. )
|
||||||
|
: BEGIN, PC ;
|
||||||
|
: AGAIN, PC - 1- A, ;
|
||||||
|
|
||||||
|
: BSET PC SWAP ! ;
|
||||||
|
: BWR @ AGAIN, ;
|
||||||
|
( same as BSET, but we need to write a placeholder )
|
||||||
|
: FJR, PC 0 A, ;
|
||||||
|
: IFZ, JRNZ, FJR, ;
|
||||||
|
: IFNZ, JRZ, FJR, ;
|
||||||
|
: IFC, JRNC, FJR, ;
|
||||||
|
: IFNC, JRC, FJR, ;
|
9
blk/247
Normal file
9
blk/247
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
: THEN,
|
||||||
|
DUP PC ( l l pc )
|
||||||
|
-^ 1- ( l off )
|
||||||
|
( warning: l is a PC offset, not a mem addr! )
|
||||||
|
SWAP ORG @ + ( off addr )
|
||||||
|
C!
|
||||||
|
;
|
||||||
|
: FWR BSET 0 A, ;
|
||||||
|
: FSET @ THEN, ;
|
@ -8,8 +8,7 @@ BOOTSRCS = ./forth/conf.fs \
|
|||||||
../forth/icore.fs \
|
../forth/icore.fs \
|
||||||
./forth/xstop.fs
|
./forth/xstop.fs
|
||||||
|
|
||||||
FORTHSRCS = core.fs cmp.fs print.fs str.fs parse.fs readln.fs fmt.fs z80a.fs \
|
FORTHSRCS = core.fs cmp.fs print.fs str.fs parse.fs readln.fs fmt.fs blk.fs
|
||||||
link.fs blk.fs
|
|
||||||
FORTHSRC_PATHS = ${FORTHSRCS:%=../forth/%} forth/run.fs
|
FORTHSRC_PATHS = ${FORTHSRCS:%=../forth/%} forth/run.fs
|
||||||
OBJS = emul.o libz80/libz80.o
|
OBJS = emul.o libz80/libz80.o
|
||||||
SLATEST = ../tools/slatest
|
SLATEST = ../tools/slatest
|
||||||
@ -58,12 +57,15 @@ forth/forth1.bin: forth/core.bin $(SLATEST)
|
|||||||
forth/forth1-bin.h: forth/forth1.bin $(BIN2C)
|
forth/forth1-bin.h: forth/forth1.bin $(BIN2C)
|
||||||
$(BIN2C) KERNEL < forth/forth1.bin > $@
|
$(BIN2C) KERNEL < forth/forth1.bin > $@
|
||||||
|
|
||||||
forth/stage2: forth/stage.c $(OBJS) forth/forth1-bin.h
|
forth/stage2: forth/stage.c $(OBJS) forth/forth1-bin.h forth/blkfs-bin.h
|
||||||
$(CC) -DSTAGE2 forth/stage.c $(OBJS) -o $@
|
$(CC) -DSTAGE2 forth/stage.c $(OBJS) -o $@
|
||||||
|
|
||||||
blkfs: $(BLKPACK)
|
blkfs: $(BLKPACK)
|
||||||
$(BLKPACK) ../blk > $@
|
$(BLKPACK) ../blk > $@
|
||||||
|
|
||||||
|
forth/blkfs-bin.h: blkfs $(BIN2C)
|
||||||
|
$(BIN2C) BLKFS < blkfs > $@
|
||||||
|
|
||||||
forth/forth: forth/forth.c $(OBJS) forth/forth1-bin.h blkfs
|
forth/forth: forth/forth.c $(OBJS) forth/forth1-bin.h blkfs
|
||||||
$(CC) forth/forth.c $(OBJS) -o $@
|
$(CC) forth/forth.c $(OBJS) -o $@
|
||||||
|
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
|
212 LOAD ( z80 assembler )
|
||||||
0xe800 CONSTANT RAMSTART
|
0xe800 CONSTANT RAMSTART
|
||||||
0xf000 CONSTANT RS_ADDR
|
0xf000 CONSTANT RS_ADDR
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
['] EFS@ BLK@* !
|
['] EFS@ BLK@* !
|
||||||
['] EFS! BLK!* !
|
['] EFS! BLK!* !
|
||||||
RDLN$
|
RDLN$
|
||||||
Z80A$
|
|
||||||
LIT< _sys [entry]
|
LIT< _sys [entry]
|
||||||
INTERPRET
|
INTERPRET
|
||||||
;
|
;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "../emul.h"
|
#include "../emul.h"
|
||||||
#ifdef STAGE2
|
#ifdef STAGE2
|
||||||
#include "forth1-bin.h"
|
#include "forth1-bin.h"
|
||||||
|
#include "blkfs-bin.h"
|
||||||
#else
|
#else
|
||||||
#include "forth0-bin.h"
|
#include "forth0-bin.h"
|
||||||
#endif
|
#endif
|
||||||
@ -34,6 +35,10 @@ trouble of compiling defs to binary.
|
|||||||
// To know which part of RAM to dump, we listen to port 2, which at the end of
|
// To know which part of RAM to dump, we listen to port 2, which at the end of
|
||||||
// its compilation process, spits its HERE addr to port 2 (MSB first)
|
// its compilation process, spits its HERE addr to port 2 (MSB first)
|
||||||
#define HERE_PORT 0x02
|
#define HERE_PORT 0x02
|
||||||
|
// Port for block reads. Write 2 bytes, MSB first, on that port and then
|
||||||
|
// read 1024 bytes from the DATA port.
|
||||||
|
#define BLK_PORT 0x03
|
||||||
|
#define BLKDATA_PORT 0x04
|
||||||
|
|
||||||
static int running;
|
static int running;
|
||||||
// We support double-pokes, that is, a first poke to tell where to start the
|
// We support double-pokes, that is, a first poke to tell where to start the
|
||||||
@ -41,6 +46,8 @@ static int running;
|
|||||||
// then ending HERE and we start at sizeof(KERNEL).
|
// then ending HERE and we start at sizeof(KERNEL).
|
||||||
static uint16_t start_here = 0;
|
static uint16_t start_here = 0;
|
||||||
static uint16_t end_here = 0;
|
static uint16_t end_here = 0;
|
||||||
|
static uint16_t blkid = 0;
|
||||||
|
static unsigned int blkpos = 0;
|
||||||
|
|
||||||
static uint8_t iord_stdio()
|
static uint8_t iord_stdio()
|
||||||
{
|
{
|
||||||
@ -68,6 +75,20 @@ static void iowr_here(uint8_t val)
|
|||||||
end_here |= val;
|
end_here |= val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef STAGE2
|
||||||
|
static void iowr_blk(uint8_t val)
|
||||||
|
{
|
||||||
|
blkid <<= 8;
|
||||||
|
blkid |= val;
|
||||||
|
blkpos = blkid * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t iord_blkdata()
|
||||||
|
{
|
||||||
|
return BLKFS[blkpos++];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
Machine *m = emul_init();
|
Machine *m = emul_init();
|
||||||
@ -75,6 +96,10 @@ int main(int argc, char *argv[])
|
|||||||
m->iord[STDIO_PORT] = iord_stdio;
|
m->iord[STDIO_PORT] = iord_stdio;
|
||||||
m->iowr[STDIO_PORT] = iowr_stdio;
|
m->iowr[STDIO_PORT] = iowr_stdio;
|
||||||
m->iowr[HERE_PORT] = iowr_here;
|
m->iowr[HERE_PORT] = iowr_here;
|
||||||
|
#ifdef STAGE2
|
||||||
|
m->iowr[BLK_PORT] = iowr_blk;
|
||||||
|
m->iord[BLKDATA_PORT] = iord_blkdata;
|
||||||
|
#endif
|
||||||
// initialize memory
|
// initialize memory
|
||||||
for (int i=0; i<sizeof(KERNEL); i++) {
|
for (int i=0; i<sizeof(KERNEL); i++) {
|
||||||
m->mem[i] = KERNEL[i];
|
m->mem[i] = KERNEL[i];
|
||||||
|
Binary file not shown.
392
forth/z80a.fs
392
forth/z80a.fs
@ -1,392 +0,0 @@
|
|||||||
( Z80 assembler )
|
|
||||||
|
|
||||||
: Z80AMEM+ 0x59 RAM+ @ + ;
|
|
||||||
|
|
||||||
( H@ offset at which we consider our PC 0. Used to compute
|
|
||||||
PC. To have a proper PC, call "H@ ORG !" at the beginning
|
|
||||||
of your assembly process. )
|
|
||||||
: ORG 0 Z80AMEM+ ;
|
|
||||||
|
|
||||||
( Labels are a convenient way of managing relative jump
|
|
||||||
calculations. Backward labels are easy. It is only a matter
|
|
||||||
or recording "HERE" and do subtractions. Forward labels
|
|
||||||
record the place where we should write the offset, and then
|
|
||||||
when we get to that point later on, the label records the
|
|
||||||
offset there.
|
|
||||||
|
|
||||||
To avoid using dict memory in compilation targets, we
|
|
||||||
pre-declare label variables here, which means we have a
|
|
||||||
limited number of it. For now, 4 ought to be enough. )
|
|
||||||
: L1 2 Z80AMEM+ ;
|
|
||||||
: L2 4 Z80AMEM+ ;
|
|
||||||
: L3 6 Z80AMEM+ ;
|
|
||||||
: L4 8 Z80AMEM+ ;
|
|
||||||
|
|
||||||
: Z80A$
|
|
||||||
( 59 == z80a's memory )
|
|
||||||
H@ 0x59 RAM+ !
|
|
||||||
10 ALLOT
|
|
||||||
;
|
|
||||||
|
|
||||||
( Splits word into msb/lsb, lsb being on TOS )
|
|
||||||
: SPLITB
|
|
||||||
256 /MOD SWAP
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
: PC H@ ORG @ - ;
|
|
||||||
|
|
||||||
( A, spits an assembled byte, A,, spits an assembled word
|
|
||||||
Both increase PC. To debug, change C, to .X )
|
|
||||||
: A, C, ;
|
|
||||||
: A,, SPLITB A, A, ;
|
|
||||||
|
|
||||||
( "r" register constants )
|
|
||||||
7 CONSTANT A
|
|
||||||
0 CONSTANT B
|
|
||||||
1 CONSTANT C
|
|
||||||
2 CONSTANT D
|
|
||||||
3 CONSTANT E
|
|
||||||
4 CONSTANT H
|
|
||||||
5 CONSTANT L
|
|
||||||
6 CONSTANT (HL)
|
|
||||||
|
|
||||||
( "ss" register constants )
|
|
||||||
0 CONSTANT BC
|
|
||||||
1 CONSTANT DE
|
|
||||||
2 CONSTANT HL
|
|
||||||
3 CONSTANT AF
|
|
||||||
3 CONSTANT SP
|
|
||||||
|
|
||||||
( "cc" condition constants )
|
|
||||||
0 CONSTANT CNZ
|
|
||||||
1 CONSTANT CZ
|
|
||||||
2 CONSTANT CNC
|
|
||||||
3 CONSTANT CC
|
|
||||||
4 CONSTANT CPO
|
|
||||||
5 CONSTANT CPE
|
|
||||||
6 CONSTANT CP
|
|
||||||
7 CONSTANT CM
|
|
||||||
|
|
||||||
( As a general rule, IX and IY are equivalent to spitting an
|
|
||||||
extra 0xdd / 0xfd and then spit the equivalent of HL )
|
|
||||||
: IX 0xdd A, HL ;
|
|
||||||
: IY 0xfd A, HL ;
|
|
||||||
: _ix+- 0xff AND 0xdd A, (HL) ;
|
|
||||||
: _iy+- 0xff AND 0xfd A, (HL) ;
|
|
||||||
: IX+ _ix+- ;
|
|
||||||
: IX- 0 -^ _ix+- ;
|
|
||||||
: IY+ _iy+- ;
|
|
||||||
: IY- 0 -^ _iy+- ;
|
|
||||||
|
|
||||||
: <<3 8 * ;
|
|
||||||
: <<4 16 * ;
|
|
||||||
|
|
||||||
( -- )
|
|
||||||
: OP1 CREATE C, DOES> C@ A, ;
|
|
||||||
0xf3 OP1 DI,
|
|
||||||
0xfb OP1 EI,
|
|
||||||
0xeb OP1 EXDEHL,
|
|
||||||
0xd9 OP1 EXX,
|
|
||||||
0x76 OP1 HALT,
|
|
||||||
0xe9 OP1 JP(HL),
|
|
||||||
0x12 OP1 LD(DE)A,
|
|
||||||
0x1a OP1 LDA(DE),
|
|
||||||
0x00 OP1 NOP,
|
|
||||||
0xc9 OP1 RET,
|
|
||||||
0x17 OP1 RLA,
|
|
||||||
0x07 OP1 RLCA,
|
|
||||||
0x1f OP1 RRA,
|
|
||||||
0x0f OP1 RRCA,
|
|
||||||
0x37 OP1 SCF,
|
|
||||||
|
|
||||||
( Relative jumps are a bit special. They're supposed to take
|
|
||||||
an argument, but they don't take it so they can work with
|
|
||||||
the label system. Therefore, relative jumps are an OP1 but
|
|
||||||
when you use them, you're expected to write the offset
|
|
||||||
afterwards yourself. )
|
|
||||||
|
|
||||||
0x18 OP1 JR,
|
|
||||||
0x38 OP1 JRC,
|
|
||||||
0x30 OP1 JRNC,
|
|
||||||
0x28 OP1 JRZ,
|
|
||||||
0x20 OP1 JRNZ,
|
|
||||||
0x10 OP1 DJNZ,
|
|
||||||
|
|
||||||
( r -- )
|
|
||||||
: OP1r
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
C@ ( r op )
|
|
||||||
SWAP ( op r )
|
|
||||||
<<3 ( op r<<3 )
|
|
||||||
OR A,
|
|
||||||
;
|
|
||||||
0x04 OP1r INCr,
|
|
||||||
0x05 OP1r DECr,
|
|
||||||
( also works for cc )
|
|
||||||
0xc0 OP1r RETcc,
|
|
||||||
|
|
||||||
( r -- )
|
|
||||||
: OP1r0
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
C@ ( r op )
|
|
||||||
OR A,
|
|
||||||
;
|
|
||||||
0x80 OP1r0 ADDr,
|
|
||||||
0x88 OP1r0 ADCr,
|
|
||||||
0xa0 OP1r0 ANDr,
|
|
||||||
0xb8 OP1r0 CPr,
|
|
||||||
0xb0 OP1r0 ORr,
|
|
||||||
0x90 OP1r0 SUBr,
|
|
||||||
0x98 OP1r0 SBCr,
|
|
||||||
0xa8 OP1r0 XORr,
|
|
||||||
|
|
||||||
( qq -- also works for ss )
|
|
||||||
: OP1qq
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
C@ ( qq op )
|
|
||||||
SWAP ( op qq )
|
|
||||||
<<4 ( op qq<<4 )
|
|
||||||
OR A,
|
|
||||||
;
|
|
||||||
0xc5 OP1qq PUSHqq,
|
|
||||||
0xc1 OP1qq POPqq,
|
|
||||||
0x03 OP1qq INCss,
|
|
||||||
0x0b OP1qq DECss,
|
|
||||||
0x09 OP1qq ADDHLss,
|
|
||||||
|
|
||||||
: ADDIXss, 0xdd A, ADDHLss, ;
|
|
||||||
: ADDIXIX, HL ADDIXss, ;
|
|
||||||
: ADDIYss, 0xfd A, ADDHLss, ;
|
|
||||||
: ADDIYIY, HL ADDIYss, ;
|
|
||||||
|
|
||||||
: _1rr
|
|
||||||
C@ ( rd rr op )
|
|
||||||
ROT ( rr op rd )
|
|
||||||
<<3 ( rr op rd<<3 )
|
|
||||||
OR OR A,
|
|
||||||
;
|
|
||||||
|
|
||||||
( rd rr )
|
|
||||||
: OP1rr
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
_1rr
|
|
||||||
;
|
|
||||||
0x40 OP1rr LDrr,
|
|
||||||
|
|
||||||
( ixy+- HL rd )
|
|
||||||
: LDIXYr,
|
|
||||||
( dd/fd has already been spit )
|
|
||||||
LDrr, ( ixy+- )
|
|
||||||
A,
|
|
||||||
;
|
|
||||||
|
|
||||||
( rd ixy+- HL )
|
|
||||||
: LDrIXY,
|
|
||||||
ROT ( ixy+- HL rd )
|
|
||||||
SWAP ( ixy+- rd HL )
|
|
||||||
LDIXYr,
|
|
||||||
;
|
|
||||||
|
|
||||||
: OP2 CREATE , DOES> @ 256 /MOD A, A, ;
|
|
||||||
0xedb1 OP2 CPIR,
|
|
||||||
0xed46 OP2 IM0,
|
|
||||||
0xed56 OP2 IM1,
|
|
||||||
0xed5e OP2 IM2,
|
|
||||||
0xed44 OP2 NEG,
|
|
||||||
0xed4d OP2 RETI,
|
|
||||||
|
|
||||||
( n -- )
|
|
||||||
: OP2n
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
C@ A, A,
|
|
||||||
;
|
|
||||||
0xd3 OP2n OUTnA,
|
|
||||||
0xdb OP2n INAn,
|
|
||||||
0xc6 OP2n ADDn,
|
|
||||||
0xe6 OP2n ANDn,
|
|
||||||
0xf6 OP2n Orn,
|
|
||||||
0xd6 OP2n SUBn,
|
|
||||||
|
|
||||||
( r n -- )
|
|
||||||
: OP2rn
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
C@ ( r n op )
|
|
||||||
ROT ( n op r )
|
|
||||||
<<3 ( n op r<<3 )
|
|
||||||
OR A, A,
|
|
||||||
;
|
|
||||||
0x06 OP2rn LDrn,
|
|
||||||
|
|
||||||
( b r -- )
|
|
||||||
: OP2br
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
0xcb A,
|
|
||||||
C@ ( b r op )
|
|
||||||
ROT ( r op b )
|
|
||||||
<<3 ( r op b<<3 )
|
|
||||||
OR OR A,
|
|
||||||
;
|
|
||||||
0xc0 OP2br SETbr,
|
|
||||||
0x80 OP2br RESbr,
|
|
||||||
0x40 OP2br BITbr,
|
|
||||||
|
|
||||||
( bitwise rotation ops have a similar sig )
|
|
||||||
( r -- )
|
|
||||||
: OProt
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
0xcb A,
|
|
||||||
C@ ( r op )
|
|
||||||
OR A,
|
|
||||||
;
|
|
||||||
0x10 OProt RLr,
|
|
||||||
0x00 OProt RLCr,
|
|
||||||
0x18 OProt RRr,
|
|
||||||
0x08 OProt RRCr,
|
|
||||||
0x20 OProt SLAr,
|
|
||||||
0x38 OProt SRLr,
|
|
||||||
|
|
||||||
( cell contains both bytes. MSB is spit as-is, LSB is ORed with r )
|
|
||||||
( r -- )
|
|
||||||
: OP2r
|
|
||||||
CREATE ,
|
|
||||||
DOES>
|
|
||||||
@ SPLITB SWAP ( r lsb msb )
|
|
||||||
A, ( r lsb )
|
|
||||||
SWAP <<3 ( lsb r<<3 )
|
|
||||||
OR A,
|
|
||||||
;
|
|
||||||
0xed41 OP2r OUT(C)r,
|
|
||||||
0xed40 OP2r INr(C),
|
|
||||||
|
|
||||||
( ss -- )
|
|
||||||
: OP2ss
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
0xed A,
|
|
||||||
C@ SWAP ( op ss )
|
|
||||||
<<4 ( op ss<< 4 )
|
|
||||||
OR A,
|
|
||||||
;
|
|
||||||
0x4a OP2ss ADCHLss,
|
|
||||||
0x42 OP2ss SBCHLss,
|
|
||||||
|
|
||||||
( dd nn -- )
|
|
||||||
: OP3ddnn
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
C@ ( dd nn op )
|
|
||||||
ROT ( nn op dd )
|
|
||||||
<<4 ( nn op dd<<4 )
|
|
||||||
OR A,
|
|
||||||
A,,
|
|
||||||
;
|
|
||||||
0x01 OP3ddnn LDddnn,
|
|
||||||
|
|
||||||
( nn -- )
|
|
||||||
: OP3nn
|
|
||||||
CREATE C,
|
|
||||||
DOES>
|
|
||||||
C@ A,
|
|
||||||
A,,
|
|
||||||
;
|
|
||||||
0xcd OP3nn CALLnn,
|
|
||||||
0xc3 OP3nn JPnn,
|
|
||||||
0x22 OP3nn LD(nn)HL,
|
|
||||||
0x2a OP3nn LDHL(nn),
|
|
||||||
|
|
||||||
( Specials )
|
|
||||||
|
|
||||||
( dd nn -- )
|
|
||||||
: LDdd(nn),
|
|
||||||
0xed A,
|
|
||||||
SWAP <<4 0x4b OR A,
|
|
||||||
A,,
|
|
||||||
;
|
|
||||||
|
|
||||||
( nn dd -- )
|
|
||||||
: LD(nn)dd,
|
|
||||||
0xed A,
|
|
||||||
<<4 0x43 OR A,
|
|
||||||
A,,
|
|
||||||
;
|
|
||||||
|
|
||||||
: JP(IX), IX DROP JP(HL), ;
|
|
||||||
: JP(IY), IY DROP JP(HL), ;
|
|
||||||
|
|
||||||
( 26 == next )
|
|
||||||
: JPNEXT, 26 JPnn, ;
|
|
||||||
|
|
||||||
: CODE
|
|
||||||
( same as CREATE, but with native word )
|
|
||||||
(entry)
|
|
||||||
( 23 == nativeWord )
|
|
||||||
23 C,
|
|
||||||
;
|
|
||||||
|
|
||||||
: ;CODE JPNEXT, ;
|
|
||||||
|
|
||||||
|
|
||||||
( Macros )
|
|
||||||
( clear carry + SBC )
|
|
||||||
: SUBHLss, A ORr, SBCHLss, ;
|
|
||||||
|
|
||||||
( Routines )
|
|
||||||
( 29 == chkPS )
|
|
||||||
: chkPS, 29 CALLnn, ;
|
|
||||||
|
|
||||||
( Flow
|
|
||||||
|
|
||||||
There are 2 label types: backward and forward. For each
|
|
||||||
type, there are two actions: set and write. Setting a label
|
|
||||||
is declaring where it is. It has to be performed at the
|
|
||||||
label's destination. Writing a label is writing its offset
|
|
||||||
difference to the binary result. It has to be done right
|
|
||||||
after a relative jump operation. Yes, labels are only for
|
|
||||||
relative jumps.
|
|
||||||
|
|
||||||
For backward labels, set happens before write. For forward
|
|
||||||
labels, write happen before set. The write operation writes
|
|
||||||
a dummy placeholder, and then the set operation writes the
|
|
||||||
offset at that placeholder's address.
|
|
||||||
|
|
||||||
Variable actions are expected to be called with labels in
|
|
||||||
front of them. Example, "L2 FSET"
|
|
||||||
|
|
||||||
About that "1 -": z80 relative jumps record "e-2", that is,
|
|
||||||
the offset that *counts the 2 bytes of the jump itself*.
|
|
||||||
Because we set the label *after* the jump OP1 itself, that's
|
|
||||||
1 byte that is taken care of. We still need to adjust by
|
|
||||||
another byte before writing the offset.
|
|
||||||
)
|
|
||||||
|
|
||||||
( Place BEGIN, where you want to jump back and AGAIN after
|
|
||||||
a relative jump operator. Just like BSET and BWR. )
|
|
||||||
: BEGIN, PC ;
|
|
||||||
: AGAIN, PC - 1- A, ;
|
|
||||||
|
|
||||||
: BSET PC SWAP ! ;
|
|
||||||
: BWR @ AGAIN, ;
|
|
||||||
( same as BSET, but we need to write a placeholder )
|
|
||||||
: FJR, PC 0 A, ;
|
|
||||||
: IFZ, JRNZ, FJR, ;
|
|
||||||
: IFNZ, JRZ, FJR, ;
|
|
||||||
: IFC, JRNC, FJR, ;
|
|
||||||
: IFNC, JRC, FJR, ;
|
|
||||||
: THEN,
|
|
||||||
DUP PC ( l l pc )
|
|
||||||
-^ 1- ( l off )
|
|
||||||
( warning: l is a PC offset, not a mem addr! )
|
|
||||||
SWAP ORG @ + ( off addr )
|
|
||||||
C!
|
|
||||||
;
|
|
||||||
: FWR BSET 0 A, ;
|
|
||||||
: FSET @ THEN, ;
|
|
@ -33,24 +33,17 @@ 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.
|
||||||
|
|
||||||
## Using the at28 driver
|
## Building your stage 4
|
||||||
|
|
||||||
The AT28 driver is at `drv/at28.fs` and is a pure forth source file so it's
|
Using the same technique as you used for building your stage 3, you can append
|
||||||
rather easy to set up from the base Stage 3 binary:
|
required words to your boot binary. Required units are `forth/adev.fs` and
|
||||||
|
`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!*`.
|
||||||
|
|
||||||
It's not in the Stage 3 binary, but because it's a small piece of Forth code,
|
First, upload your binary to some place in memory, for example `a000`. To do so,
|
||||||
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,19 +0,0 @@
|
|||||||
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.asm` to
|
hacked off SPI relay for the RC2014. It can then be used with `sdc.fs` to
|
||||||
drive a SD card.
|
drive a SD card.
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
@ -18,10 +18,8 @@ design.
|
|||||||
|
|
||||||
## Gathering parts
|
## Gathering parts
|
||||||
|
|
||||||
* A RC2014 with Collapse OS with these features:
|
* A RC2014 Classic
|
||||||
* shell
|
* `stage3.bin` from the base recipe
|
||||||
* 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
|
||||||
@ -34,7 +32,7 @@ design.
|
|||||||
|
|
||||||
## Building the SPI relay
|
## Building the SPI relay
|
||||||
|
|
||||||
The [schematic][schematic] supplied with this recipe works well with `sdc.asm`.
|
The [schematic][schematic] supplied with this recipe works well with `sdc.fs`.
|
||||||
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.
|
||||||
|
|
||||||
@ -71,95 +69,41 @@ 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 the kernel
|
## Building your stage 4
|
||||||
|
|
||||||
To be able to work with your SPI relay and communicate with the card, you
|
Using the same technique as you used for building your stage 3, you can append
|
||||||
should have [glue code that looks like this](glue.asm).
|
required words to your boot binary. Required units are `forth/blk.fs` and
|
||||||
|
`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.
|
||||||
|
|
||||||
Initially, when you don't know if things work well yet, you should comment out
|
## Testing in the emulator
|
||||||
the block creation part.
|
|
||||||
|
|
||||||
## Reading from the SD card
|
The RC2014 emulator includes SDC emulation. You can attach a SD card image to
|
||||||
|
it by invoking it with a second argument:
|
||||||
|
|
||||||
The first thing we'll do is fill the SD card's first 12 bytes with "Hello
|
../../../emul/hw/rc2014/classic stage4.bin ../../../emul/blkfs
|
||||||
World!":
|
|
||||||
|
|
||||||
echo "Hello World!" > /dev/sdX
|
You will then run with a SD card having the contents from `/blk`.
|
||||||
|
|
||||||
Then, insert your SD card in your SPI relay and boot the RC2014.
|
## Usage
|
||||||
|
|
||||||
Run the `sdci` command which will initialize the card. The blockdev 0 is
|
First, the SD card needs to be initialized
|
||||||
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.
|
|
||||||
|
|
||||||
Set your memory pointer to somewhere you can write to with `mptr 9000` and then
|
SDC$
|
||||||
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!
|
|
||||||
|
|
||||||
## Mounting a filesystem from the SD card
|
If there is no error message, we're fine. Then, we need to hook `BLK@*` and
|
||||||
|
`BLK!*` into the SDC driver:
|
||||||
|
|
||||||
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!
|
' SDC! BLK!* !
|
||||||
|
|
||||||
$ cat sdcard.cfs > /dev/sdX
|
And thats it! You have full access to disk block mechanism:
|
||||||
|
|
||||||
Then, you insert your SD card in your SPI relay and go:
|
102 LOAD
|
||||||
|
BROWSE
|
||||||
|
|
||||||
Collapse OS
|
(at this moment, the driver is a bit slow though...)
|
||||||
> 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
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
; 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
|
|
@ -1,12 +0,0 @@
|
|||||||
; 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