mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-26 15:28:05 +11:00
Compare commits
5 Commits
5067d40e3b
...
9edab10a3a
Author | SHA1 | Date | |
---|---|---|---|
|
9edab10a3a | ||
|
79ce88c12c | ||
|
5cb4a7de9a | ||
|
eefa8e6de5 | ||
|
57e20f0532 |
2
blk/000
2
blk/000
@ -4,7 +4,7 @@ This is a Forth-style filesystems which is very simple. It is a
|
|||||||
list of 1024 bytes block, organised in 16 lines of 64 columns
|
list of 1024 bytes block, organised in 16 lines of 64 columns
|
||||||
each. You refer to blocks by numbers. You show them with LIST.
|
each. You refer to blocks by numbers. You show them with LIST.
|
||||||
You interpret them with LOAD. For a convenient way to browse
|
You interpret them with LOAD. For a convenient way to browse
|
||||||
blocks, see Block Explorer at B100.
|
blocks, see Block editor at B100.
|
||||||
|
|
||||||
Conventions: When you see "(cont.)" at the bottom right of a
|
Conventions: When you see "(cont.)" at the bottom right of a
|
||||||
block, it means that the next block continues the same kind of
|
block, it means that the next block continues the same kind of
|
||||||
|
14
blk/001
14
blk/001
@ -1,4 +1,16 @@
|
|||||||
MASTER INDEX
|
MASTER INDEX
|
||||||
|
|
||||||
3 Usage 30 Dictionary
|
3 Usage 30 Dictionary
|
||||||
70 Implementation notes 100 Block explorer
|
70 Implementation notes 100 Block editor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
4
blk/003
4
blk/003
@ -10,3 +10,7 @@ Contents
|
|||||||
4 DOES> 6 Compilation vs meta-comp.
|
4 DOES> 6 Compilation vs meta-comp.
|
||||||
8 I/O 11 Chained comparisons
|
8 I/O 11 Chained comparisons
|
||||||
14 Addressed devices 18 Signed-ness
|
14 Addressed devices 18 Signed-ness
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1
blk/004
1
blk/004
@ -13,3 +13,4 @@ At compile time, colon definition stops processing words when
|
|||||||
reaching the DOES>.
|
reaching the DOES>.
|
||||||
|
|
||||||
Example: ": CONSTANT CREATE HERE @ ! DOES> @ ;"
|
Example: ": CONSTANT CREATE HERE @ ! DOES> @ ;"
|
||||||
|
|
||||||
|
2
blk/009
2
blk/009
@ -12,3 +12,5 @@ new "(parse)" word.
|
|||||||
|
|
||||||
This way, we have a full-featured (and extensible) parsing with
|
This way, we have a full-featured (and extensible) parsing with
|
||||||
a tiny native core.
|
a tiny native core.
|
||||||
|
|
||||||
|
|
||||||
|
12
blk/016
12
blk/016
@ -2,3 +2,15 @@
|
|||||||
kind of ad-hoc way to have some kind of mapping function, but
|
kind of ad-hoc way to have some kind of mapping function, but
|
||||||
what you'll mostly want to to is to plug device drivers into
|
what you'll mostly want to to is to plug device drivers into
|
||||||
those words.
|
those words.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
7
blk/018
7
blk/018
@ -7,3 +7,10 @@ support the "-" prefix, but under the hood, it's all unsigned.
|
|||||||
This leads to some oddities. For example, "-1 0 <" is false.
|
This leads to some oddities. For example, "-1 0 <" is false.
|
||||||
To compare whether something is negative, use the "<0" word
|
To compare whether something is negative, use the "<0" word
|
||||||
which is the equivalent to "0x7fff >".
|
which is the equivalent to "0x7fff >".
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
5
blk/030
5
blk/030
@ -9,3 +9,8 @@ Be sure to read usage guide (B3) first.
|
|||||||
52 Addressed devices 54 Arithmetic / Bits
|
52 Addressed devices 54 Arithmetic / Bits
|
||||||
56 Logic 58 Strings
|
56 Logic 58 Strings
|
||||||
60 I/O 64 Disk
|
60 I/O 64 Disk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
6
blk/032
6
blk/032
@ -8,3 +8,9 @@ Words between "()" are "support words" that aren't really meant
|
|||||||
to be used directly, but as part of another word.
|
to be used directly, but as part of another word.
|
||||||
|
|
||||||
"*I*" in description indicates an IMMEDIATE word.
|
"*I*" in description indicates an IMMEDIATE word.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
13
blk/035
13
blk/035
@ -1,3 +1,16 @@
|
|||||||
(cont.)
|
(cont.)
|
||||||
~ - Container for native code. Usually not an executable word.
|
~ - Container for native code. Usually not an executable word.
|
||||||
? - Is it ...? (example: IMMED?)
|
? - Is it ...? (example: IMMED?)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/038
10
blk/038
@ -4,3 +4,13 @@ FORGET x -- Rewind the dictionary (both CURRENT and HERE)
|
|||||||
PREV a -- a Return a wordref's previous entry.
|
PREV a -- a Return a wordref's previous entry.
|
||||||
WHLEN a -- n Get word header length from wordref. That is,
|
WHLEN a -- n Get word header length from wordref. That is,
|
||||||
name length + 3. a is a wordref
|
name length + 3. a is a wordref
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1
blk/043
1
blk/043
@ -13,3 +13,4 @@ INTERPRET -- Get a line from stdin, compile it in tmp memory,
|
|||||||
then execute the compiled contents.
|
then execute the compiled contents.
|
||||||
QUIT -- Return to interpreter prompt immediately
|
QUIT -- Return to interpreter prompt immediately
|
||||||
EXIT! -- Exit current INTERPRET loop.
|
EXIT! -- Exit current INTERPRET loop.
|
||||||
|
|
||||||
|
5
blk/046
5
blk/046
@ -9,3 +9,8 @@ SWAP a b -- b a
|
|||||||
2DUP a b -- a b a b
|
2DUP a b -- a b a b
|
||||||
2OVER a b c d -- a b c d a b
|
2OVER a b c d -- a b c d a b
|
||||||
2SWAP a b c d -- c d a b
|
2SWAP a b c d -- c d a b
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
9
blk/048
9
blk/048
@ -5,3 +5,12 @@ R> R:n -- n Pops RS and push to PS
|
|||||||
I -- n Copy RS TOS to PS
|
I -- n Copy RS TOS to PS
|
||||||
I' -- n Copy RS second item to PS
|
I' -- n Copy RS second item to PS
|
||||||
J -- n Copy RS third item to PS
|
J -- n Copy RS third item to PS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1
blk/050
1
blk/050
@ -5,6 +5,7 @@ Memory
|
|||||||
? a -- Print value of addr a
|
? a -- Print value of addr a
|
||||||
+! n a -- Increase value of addr a by n
|
+! n a -- Increase value of addr a by n
|
||||||
C@ a -- c Set c to byte at address a
|
C@ a -- c Set c to byte at address a
|
||||||
|
C@+ a -- a+1 c Fetch c from a and inc a.
|
||||||
C! c a -- Store byte c in address a
|
C! c a -- Store byte c in address a
|
||||||
CURRENT -- a Set a to wordref of last added entry.
|
CURRENT -- a Set a to wordref of last added entry.
|
||||||
CURRENT* -- a A pointer to active CURRENT*. Useful
|
CURRENT* -- a A pointer to active CURRENT*. Useful
|
||||||
|
6
blk/052
6
blk/052
@ -8,3 +8,9 @@ A! c a -- Indirect C!
|
|||||||
A@* -- a Address for A@ word
|
A@* -- a Address for A@ word
|
||||||
A!* -- a Address for A! word
|
A!* -- a Address for A! word
|
||||||
AMOVE src dst u -- Same as MOVE, but with A@ and A!
|
AMOVE src dst u -- Same as MOVE, but with A@ and A!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
2
blk/054
2
blk/054
@ -12,3 +12,5 @@ OR a b -- c a | b -> c
|
|||||||
XOR a b -- c a ^ b -> c
|
XOR a b -- c a ^ b -> c
|
||||||
|
|
||||||
Shortcuts: 1+ 2+ 1- 2-
|
Shortcuts: 1+ 2+ 1- 2-
|
||||||
|
|
||||||
|
|
||||||
|
8
blk/056
8
blk/056
@ -6,3 +6,11 @@ Logic
|
|||||||
CMP n1 n2 -- n Compare n1 and n2 and set n to -1, 0, or 1.
|
CMP n1 n2 -- n Compare n1 and n2 and set n to -1, 0, or 1.
|
||||||
n=0: a1=a2. n=1: a1>a2. n=-1: a1<a2.
|
n=0: a1=a2. n=1: a1>a2. n=-1: a1<a2.
|
||||||
NOT f -- f Push the logical opposite of f
|
NOT f -- f Push the logical opposite of f
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
6
blk/058
6
blk/058
@ -8,3 +8,9 @@ LITS a -- Write word at addr a as a atring literal.
|
|||||||
S= a1 a2 -- f Returns whether string a1 == a2.
|
S= a1 a2 -- f Returns whether string a1 == a2.
|
||||||
SCPY a -- Copy string at addr a into HERE.
|
SCPY a -- Copy string at addr a into HERE.
|
||||||
SLEN a -- n Push length of str at a.
|
SLEN a -- n Push length of str at a.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/064
10
blk/064
@ -4,3 +4,13 @@ BLK> -- a Address of the current block variable.
|
|||||||
LIST n -- Prints the contents of the block n on screen in the
|
LIST n -- Prints the contents of the block n on screen in the
|
||||||
form of 16 lines of 64 columns.
|
form of 16 lines of 64 columns.
|
||||||
LOAD n -- Interprets Forth code from block n
|
LOAD n -- Interprets Forth code from block n
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/070
10
blk/070
@ -4,3 +4,13 @@ Implementation notes
|
|||||||
75 Stack management 77 Dictionary
|
75 Stack management 77 Dictionary
|
||||||
80 System variables 85 Word routines
|
80 System variables 85 Word routines
|
||||||
89 Initialization sequence
|
89 Initialization sequence
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
5
blk/071
5
blk/071
@ -9,3 +9,8 @@ it. As a general rule, we go like this:
|
|||||||
4. Is it a number?
|
4. Is it a number?
|
||||||
5. If yes, push that number to PS, goto 1
|
5. If yes, push that number to PS, goto 1
|
||||||
6. Error: undefined word.
|
6. Error: undefined word.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
2
blk/075
2
blk/075
@ -12,3 +12,5 @@ IX always points to RS' Top Of Stack (TOS)
|
|||||||
This return stack contain "Interpreter pointers", that is a
|
This return stack contain "Interpreter pointers", that is a
|
||||||
pointer to the address of a word, as seen in a compiled list of
|
pointer to the address of a word, as seen in a compiled list of
|
||||||
words.
|
words.
|
||||||
|
|
||||||
|
|
||||||
|
4
blk/078
4
blk/078
@ -10,3 +10,7 @@ chain). There are also "special words", for example NUMBER,
|
|||||||
LIT, FBR, that have a slightly different structure. They're
|
LIT, FBR, that have a slightly different structure. They're
|
||||||
also a pointer to an executable, but as for the other fields,
|
also a pointer to an executable, but as for the other fields,
|
||||||
the only one they have is the "flags" field.
|
the only one they have is the "flags" field.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/084
10
blk/084
@ -4,3 +4,13 @@ DRIVERS section is reserved for recipe-specific
|
|||||||
drivers. Here is a list of known usages:
|
drivers. Here is a list of known usages:
|
||||||
|
|
||||||
* 0x70-0x78: ACIA buffer pointers in RC2014 recipes.
|
* 0x70-0x78: ACIA buffer pointers in RC2014 recipes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/087
10
blk/087
@ -4,3 +4,13 @@ being followed by a 2 byte number, it is followed by a
|
|||||||
null-terminated string. Upon execution, the address of that
|
null-terminated string. Upon execution, the address of that
|
||||||
null-terminated string is pushed on the PSP and IP is advanced
|
null-terminated string is pushed on the PSP and IP is advanced
|
||||||
to the address following the null.
|
to the address following the null.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
9
blk/091
9
blk/091
@ -5,3 +5,12 @@ for space with HERE: New entries to the dict will overwrite
|
|||||||
that code! Also, because we're barebone, we can't have
|
that code! Also, because we're barebone, we can't have
|
||||||
comments. This can lead to peculiar code in this area where we
|
comments. This can lead to peculiar code in this area where we
|
||||||
try to "waste" space in initialization code.
|
try to "waste" space in initialization code.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
18
blk/100
18
blk/100
@ -1,10 +1,16 @@
|
|||||||
Block explorer
|
Block editor
|
||||||
|
|
||||||
This is an application to conveniently browse the contents of
|
This is an application to conveniently browse the contents of
|
||||||
the disk blocks. You can launch it with "102 LOAD".
|
the disk blocks and edit them. You can load it with "102 LOAD".
|
||||||
|
|
||||||
USAGE: When loaded, the Forth interpreter is replaced by the
|
Browse mode: If you execute BROWSE, the Forth interpreter is
|
||||||
explorer interpreter. Typing "Q" quits the program.
|
replaced by browser's loop. Typing "Q" quits the browser.
|
||||||
|
|
||||||
Typing a decimal number followed by space or return lists the
|
In this mode, typing a decimal number followed by space or
|
||||||
contents of that block. B for previous block, N for next.
|
return lists the contents of that block. B for previous block,
|
||||||
|
N for next.
|
||||||
|
|
||||||
|
When not in browse mode, your prompt is a regular Forth prompt
|
||||||
|
with editor words loaded.
|
||||||
|
|
||||||
|
(cont.)
|
||||||
|
16
blk/101
Normal file
16
blk/101
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
T ( n -- ): select line n for editing.
|
||||||
|
P xxx(return): put typed line on selected line.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
22
blk/102
22
blk/102
@ -1,14 +1,16 @@
|
|||||||
103 LOAD
|
103 LOAD 104 LOAD
|
||||||
VARIABLE _K
|
|
||||||
|
|
||||||
: PGM
|
: BROWSE
|
||||||
100 _LIST
|
100 _LIST
|
||||||
BEGIN
|
BEGIN
|
||||||
KEY
|
KEY CASE
|
||||||
DUP 'Q' = IF DROP EXIT THEN
|
'Q' OF DROP EXIT ENDOF
|
||||||
DUP 58 ( '9'+1 ) < IF _NUM
|
'B' OF B ENDOF
|
||||||
ELSE
|
'N' OF N ENDOF
|
||||||
_K ! _K (find) IF EXECUTE THEN
|
_NUM
|
||||||
THEN
|
ENDCASE
|
||||||
AGAIN
|
AGAIN
|
||||||
; PGM
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
11
blk/103
11
blk/103
@ -5,5 +5,12 @@ VARIABLE ACC
|
|||||||
IF _LIST 0 THEN
|
IF _LIST 0 THEN
|
||||||
ACC !
|
ACC !
|
||||||
;
|
;
|
||||||
: B BLK> @ 1- DUP BLK> ! _LIST ;
|
: L BLK> @ _LIST ;
|
||||||
: N BLK> @ 1+ DUP BLK> ! _LIST ;
|
: B BLK> @ 1- BLK> ! L ;
|
||||||
|
: N BLK> @ 1+ BLK> ! L ;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
16
blk/104
Normal file
16
blk/104
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
( Line numbers for the user are 1-based, but in code, they're
|
||||||
|
0-based. )
|
||||||
|
VARIABLE EDPOS
|
||||||
|
: _bpos 64 * BLK( + ;
|
||||||
|
: T 1- DUP EDPOS ! _bpos (print) CRLF ;
|
||||||
|
: P
|
||||||
|
EDPOS @ _bpos C<
|
||||||
|
64 0 DO ( bpos c )
|
||||||
|
DUP 0xd = IF DROP 0 THEN
|
||||||
|
2DUP SWAP I + C!
|
||||||
|
DUP IF DROP C< THEN
|
||||||
|
LOOP
|
||||||
|
2DROP
|
||||||
|
BLK!!
|
||||||
|
;
|
||||||
|
|
@ -16,6 +16,7 @@ SLATEST = ../tools/slatest
|
|||||||
STRIPFC = ../tools/stripfc
|
STRIPFC = ../tools/stripfc
|
||||||
BIN2C = ../tools/bin2c
|
BIN2C = ../tools/bin2c
|
||||||
BLKPACK = ../tools/blkpack
|
BLKPACK = ../tools/blkpack
|
||||||
|
BLKUNPACK = ../tools/blkunpack
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
@ -27,6 +28,7 @@ $(BLKPACK):
|
|||||||
$(STRIPFC): $(BLKPACK)
|
$(STRIPFC): $(BLKPACK)
|
||||||
$(SLATEST): $(BLKPACK)
|
$(SLATEST): $(BLKPACK)
|
||||||
$(BIN2C): $(BLKPACK)
|
$(BIN2C): $(BLKPACK)
|
||||||
|
$(BLKUNPACK): $(BLKPACK)
|
||||||
|
|
||||||
# z80c.bin is not in the prerequisites because it's a bootstrap
|
# z80c.bin is not in the prerequisites because it's a bootstrap
|
||||||
# binary that should be updated manually through make updatebootstrap.
|
# binary that should be updated manually through make updatebootstrap.
|
||||||
@ -77,6 +79,14 @@ emul.o: emul.c
|
|||||||
updatebootstrap: forth/stage2
|
updatebootstrap: forth/stage2
|
||||||
cat $(BOOTSRCS) | ./forth/stage2 > ./forth/z80c.bin
|
cat $(BOOTSRCS) | ./forth/stage2 > ./forth/z80c.bin
|
||||||
|
|
||||||
|
.PHONY: pack
|
||||||
|
pack:
|
||||||
|
rm blkfs && $(MAKE) blkfs
|
||||||
|
|
||||||
|
.PHONY: unpack
|
||||||
|
unpack:
|
||||||
|
$(BLKUNPACK) ../blk < blkfs
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGETS) emul.o forth/*-bin.h forth/forth?.bin blkfs
|
rm -f $(TARGETS) emul.o forth/*-bin.h forth/forth?.bin blkfs
|
||||||
|
@ -12,8 +12,9 @@
|
|||||||
// failing, send a non-zero value to RET_PORT to indicate failure
|
// failing, send a non-zero value to RET_PORT to indicate failure
|
||||||
#define RET_PORT 0x01
|
#define RET_PORT 0x01
|
||||||
// Port for block reads. Write 2 bytes, MSB first, on that port and then
|
// Port for block reads. Write 2 bytes, MSB first, on that port and then
|
||||||
// read 1024 bytes from the same port.
|
// read 1024 bytes from the DATA port.
|
||||||
#define BLK_PORT 0x03
|
#define BLK_PORT 0x03
|
||||||
|
#define BLKDATA_PORT 0x04
|
||||||
|
|
||||||
static int running;
|
static int running;
|
||||||
static FILE *fp;
|
static FILE *fp;
|
||||||
@ -44,7 +45,16 @@ static void iowr_ret(uint8_t val)
|
|||||||
retcode = val;
|
retcode = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t iord_blk()
|
static void iowr_blk(uint8_t val)
|
||||||
|
{
|
||||||
|
blkid <<= 8;
|
||||||
|
blkid |= val;
|
||||||
|
if (blkfp != NULL) {
|
||||||
|
fseek(blkfp, blkid*1024, SEEK_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t iord_blkdata()
|
||||||
{
|
{
|
||||||
uint8_t res = 0;
|
uint8_t res = 0;
|
||||||
if (blkfp != NULL) {
|
if (blkfp != NULL) {
|
||||||
@ -56,12 +66,10 @@ static uint8_t iord_blk()
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iowr_blk(uint8_t val)
|
static void iowr_blkdata(uint8_t val)
|
||||||
{
|
{
|
||||||
blkid <<= 8;
|
|
||||||
blkid |= val;
|
|
||||||
if (blkfp != NULL) {
|
if (blkfp != NULL) {
|
||||||
fseek(blkfp, blkid*1024, SEEK_SET);
|
putc(val, blkfp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,8 +107,9 @@ 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[RET_PORT] = iowr_ret;
|
m->iowr[RET_PORT] = iowr_ret;
|
||||||
m->iord[BLK_PORT] = iord_blk;
|
|
||||||
m->iowr[BLK_PORT] = iowr_blk;
|
m->iowr[BLK_PORT] = iowr_blk;
|
||||||
|
m->iord[BLKDATA_PORT] = iord_blkdata;
|
||||||
|
m->iowr[BLKDATA_PORT] = iowr_blkdata;
|
||||||
// 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];
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
: EFS@
|
: EFS@
|
||||||
256 /MOD 3 PC! 3 PC!
|
256 /MOD 3 PC! 3 PC!
|
||||||
1024 0 DO
|
1024 0 DO
|
||||||
3 PC@
|
4 PC@
|
||||||
BLK( I + C!
|
BLK( I + C!
|
||||||
LOOP
|
LOOP
|
||||||
;
|
;
|
||||||
|
: EFS!
|
||||||
|
256 /MOD 3 PC! 3 PC!
|
||||||
|
1024 0 DO
|
||||||
|
BLK( I + C@ 4 PC!
|
||||||
|
LOOP
|
||||||
|
;
|
||||||
|
|
||||||
: INIT
|
: INIT
|
||||||
CURRENT @ HERE !
|
CURRENT @ HERE !
|
||||||
BLK$
|
BLK$
|
||||||
['] EFS@ BLK@* !
|
['] EFS@ BLK@* !
|
||||||
|
['] EFS! BLK!* !
|
||||||
RDLN$
|
RDLN$
|
||||||
Z80A$
|
Z80A$
|
||||||
LIT< _sys [entry]
|
LIT< _sys [entry]
|
||||||
|
26
forth/blk.fs
26
forth/blk.fs
@ -7,23 +7,36 @@
|
|||||||
: BLK!* 2 BLKMEM+ ;
|
: BLK!* 2 BLKMEM+ ;
|
||||||
( Current blk pointer in ( )
|
( Current blk pointer in ( )
|
||||||
: BLK> 4 BLKMEM+ ;
|
: BLK> 4 BLKMEM+ ;
|
||||||
: BLK( 6 BLKMEM+ ;
|
( Whether buffer is dirty )
|
||||||
|
: BLKDTY 6 BLKMEM+ ;
|
||||||
|
: BLK( 8 BLKMEM+ ;
|
||||||
|
|
||||||
: BLK$
|
: BLK$
|
||||||
H@ 0x57 RAM+ !
|
H@ 0x57 RAM+ !
|
||||||
( 1024 for the block, 6 for variables )
|
( 1024 for the block, 8 for variables )
|
||||||
1030 ALLOT
|
1032 ALLOT
|
||||||
( LOAD detects end of block with ASCII EOT. This is why
|
( LOAD detects end of block with ASCII EOT. This is why
|
||||||
we write it there. EOT == 0x04 )
|
we write it there. EOT == 0x04 )
|
||||||
4 C,
|
4 C,
|
||||||
|
0 BLKDTY !
|
||||||
-1 BLK> !
|
-1 BLK> !
|
||||||
;
|
;
|
||||||
|
|
||||||
|
( -- )
|
||||||
|
: BLK!
|
||||||
|
BLK> @ BLK!* @ EXECUTE
|
||||||
|
0 BLKDTY !
|
||||||
|
;
|
||||||
|
|
||||||
|
( n -- )
|
||||||
: BLK@
|
: BLK@
|
||||||
DUP BLK> = IF DROP EXIT THEN
|
DUP BLK> @ = IF DROP EXIT THEN
|
||||||
|
BLKDTY @ IF BLK! THEN
|
||||||
DUP BLK> ! BLK@* @ EXECUTE
|
DUP BLK> ! BLK@* @ EXECUTE
|
||||||
;
|
;
|
||||||
|
|
||||||
|
: BLK!! 1 BLKDTY ! ;
|
||||||
|
|
||||||
: .2 DUP 10 < IF SPC THEN . ;
|
: .2 DUP 10 < IF SPC THEN . ;
|
||||||
|
|
||||||
: LIST
|
: LIST
|
||||||
@ -38,7 +51,10 @@
|
|||||||
: _
|
: _
|
||||||
(boot<)
|
(boot<)
|
||||||
DUP 4 = IF
|
DUP 4 = IF
|
||||||
DROP
|
( We drop our char, but also "a" from WORD: it won't
|
||||||
|
have the opportunity to balance PSP because we're
|
||||||
|
EXIT!ing. )
|
||||||
|
2DROP
|
||||||
( We're finished interpreting )
|
( We're finished interpreting )
|
||||||
EXIT!
|
EXIT!
|
||||||
THEN
|
THEN
|
||||||
|
@ -178,4 +178,8 @@
|
|||||||
DUP ( I I )
|
DUP ( I I )
|
||||||
R> DROP I 2- @ ( I I a )
|
R> DROP I 2- @ ( I I a )
|
||||||
= UNTIL
|
= UNTIL
|
||||||
|
DROP
|
||||||
;
|
;
|
||||||
|
|
||||||
|
( a -- a+1 c )
|
||||||
|
: C@+ DUP C@ SWAP 1+ SWAP ;
|
||||||
|
@ -54,11 +54,10 @@
|
|||||||
LOOP
|
LOOP
|
||||||
DROP
|
DROP
|
||||||
8 0 DO
|
8 0 DO
|
||||||
DUP C@
|
C@+
|
||||||
DUP <>{ 0x20 &< 0x7e |> <>}
|
DUP <>{ 0x20 &< 0x7e |> <>}
|
||||||
IF DROP '.' THEN
|
IF DROP '.' THEN
|
||||||
EMIT
|
EMIT
|
||||||
1+
|
|
||||||
LOOP
|
LOOP
|
||||||
CRLF
|
CRLF
|
||||||
;
|
;
|
||||||
|
@ -5,22 +5,21 @@
|
|||||||
|
|
||||||
: (print)
|
: (print)
|
||||||
BEGIN
|
BEGIN
|
||||||
DUP C@ ( a c )
|
C@+ ( a+1 c )
|
||||||
( exit if null )
|
( exit if null )
|
||||||
DUP NOT IF 2DROP EXIT THEN
|
DUP NOT IF 2DROP EXIT THEN
|
||||||
EMIT ( a )
|
EMIT ( a )
|
||||||
1 + ( a+1 )
|
|
||||||
AGAIN
|
AGAIN
|
||||||
;
|
;
|
||||||
|
|
||||||
: ."
|
: ."
|
||||||
34 , ( 34 == litWord )
|
34 , ( 34 == litWord )
|
||||||
BEGIN
|
BEGIN
|
||||||
C< DUP ( c c )
|
C<
|
||||||
( 34 is ASCII for " )
|
( 34 is ASCII for " )
|
||||||
DUP 34 = IF DROP DROP 0 0 THEN
|
DUP 34 = IF DROP 0 THEN
|
||||||
C,
|
DUP C,
|
||||||
0 = UNTIL
|
NOT UNTIL
|
||||||
COMPILE (print)
|
COMPILE (print)
|
||||||
; IMMEDIATE
|
; IMMEDIATE
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
: SLEN ( a -- n )
|
: SLEN ( a -- n )
|
||||||
DUP ( astart aend )
|
DUP ( astart aend )
|
||||||
BEGIN
|
BEGIN C@+ NOT UNTIL
|
||||||
DUP C@ 0 = IF -^ EXIT THEN
|
1- -^
|
||||||
1+
|
|
||||||
AGAIN
|
|
||||||
;
|
;
|
||||||
|
1
tools/.gitignore
vendored
1
tools/.gitignore
vendored
@ -9,3 +9,4 @@
|
|||||||
/bin2c
|
/bin2c
|
||||||
/exec
|
/exec
|
||||||
/blkpack
|
/blkpack
|
||||||
|
/blkunpack
|
||||||
|
@ -9,9 +9,10 @@ STRIPFC_TGT = stripfc
|
|||||||
BIN2C_TGT = bin2c
|
BIN2C_TGT = bin2c
|
||||||
EXEC_TGT = exec
|
EXEC_TGT = exec
|
||||||
BLKPACK_TGT = blkpack
|
BLKPACK_TGT = blkpack
|
||||||
|
BLKUNPACK_TGT = blkunpack
|
||||||
TARGETS = $(MEMDUMP_TGT) $(BLKDUMP_TGT) $(UPLOAD_TGT) $(FONTCOMPILE_TGT) \
|
TARGETS = $(MEMDUMP_TGT) $(BLKDUMP_TGT) $(UPLOAD_TGT) $(FONTCOMPILE_TGT) \
|
||||||
$(TTYSAFE_TGT) $(PINGPONG_TGT) $(SLATEST_TGT) $(STRIPFC_TGT) \
|
$(TTYSAFE_TGT) $(PINGPONG_TGT) $(SLATEST_TGT) $(STRIPFC_TGT) \
|
||||||
$(BIN2C_TGT) $(EXEC_TGT) $(BLKPACK_TGT)
|
$(BIN2C_TGT) $(EXEC_TGT) $(BLKPACK_TGT) $(BLKUNPACK_TGT)
|
||||||
OBJS = common.o
|
OBJS = common.o
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
@ -31,6 +32,7 @@ $(STRIPFC_TGT): $(STRIPFC_TGT).c
|
|||||||
$(BIN2C_TGT): $(BIN2C_TGT).c
|
$(BIN2C_TGT): $(BIN2C_TGT).c
|
||||||
$(EXEC_TGT): $(EXEC_TGT).c
|
$(EXEC_TGT): $(EXEC_TGT).c
|
||||||
$(BLKPACK_TGT): $(BLKPACK_TGT).c
|
$(BLKPACK_TGT): $(BLKPACK_TGT).c
|
||||||
|
$(BLKUNPACK_TGT): $(BLKUNPACK_TGT).c
|
||||||
$(TARGETS): $(OBJS)
|
$(TARGETS): $(OBJS)
|
||||||
$(CC) $(CFLAGS) $@.c $(OBJS) -o $@
|
$(CC) $(CFLAGS) $@.c $(OBJS) -o $@
|
||||||
|
|
||||||
|
43
tools/blkunpack.c
Normal file
43
tools/blkunpack.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
void usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: blkunpack dirname\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int blkid = 0;
|
||||||
|
if (argc != 2) {
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
while (fread(buf, 1024, 1, stdin) == 1) {
|
||||||
|
char fullpath[0x200];
|
||||||
|
sprintf(fullpath, "%s/%03d", argv[1], blkid);
|
||||||
|
char c = 0;
|
||||||
|
for (int i=0; i<1024; i++) {
|
||||||
|
c |= buf[i];
|
||||||
|
}
|
||||||
|
if (c) {
|
||||||
|
// not an empty block
|
||||||
|
FILE *fp = fopen(fullpath, "w");
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
int len = strlen(&buf[i*64]);
|
||||||
|
fwrite(&buf[i*64], len, 1, fp);
|
||||||
|
fputc('\n', fp);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
} else {
|
||||||
|
// empty block, delete
|
||||||
|
unlink(fullpath);
|
||||||
|
}
|
||||||
|
blkid++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
2
tools/cfspack/.gitignore
vendored
2
tools/cfspack/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
/cfspack
|
|
||||||
/cfsunpack
|
|
@ -1,16 +0,0 @@
|
|||||||
TARGETS = cfspack cfsunpack
|
|
||||||
|
|
||||||
CFSPACK_OBJS = cfspack.o libcfs.o
|
|
||||||
|
|
||||||
.PHONY: all
|
|
||||||
all: $(TARGETS)
|
|
||||||
|
|
||||||
cfspack: $(CFSPACK_OBJS)
|
|
||||||
$(CC) $(LDFLAGS) -o $@ $(CFSPACK_OBJS)
|
|
||||||
|
|
||||||
cfsunpack: cfsunpack.c
|
|
||||||
$(CC) $(CFLAGS) -o $@ cfsunpack.c
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
rm -f $(TARGETS)
|
|
@ -1,33 +0,0 @@
|
|||||||
# cfspack
|
|
||||||
|
|
||||||
A tool/library to pack files into a CFS blob and unpack a CFS blob into
|
|
||||||
a directory.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
To pack a directory into a CFS blob, run:
|
|
||||||
|
|
||||||
cfspack /path/to/directory
|
|
||||||
|
|
||||||
The blob is spit to stdout. If there are subdirectories, they will be prefixes
|
|
||||||
to the filenames under it.
|
|
||||||
|
|
||||||
`cfspack` takes optional -p pattern arguments. If specified, only files
|
|
||||||
matching at least one of the patterns ("fnmatch" style") will be included.
|
|
||||||
|
|
||||||
If path is a file, a CFS with a single file will be spit and its name will
|
|
||||||
exclude the directory part of that filename.
|
|
||||||
|
|
||||||
The chain being spitted is always ended with a "stop block" (a zero-allocation
|
|
||||||
block that stops the CFS chain). You can call `cfspack` with no argument to get
|
|
||||||
only a stop block.
|
|
||||||
|
|
||||||
The program errors out if a file name is too long (> 26 bytes) or too big
|
|
||||||
(> 0x10000 - 0x20 bytes).
|
|
||||||
|
|
||||||
To unpack a blob to a directory:
|
|
||||||
|
|
||||||
cfsunpack /path/to/dest < blob
|
|
||||||
|
|
||||||
If destination exists, files are created alongside existing ones. If a file to
|
|
||||||
unpack already exists, it is overwritten.
|
|
@ -1,10 +0,0 @@
|
|||||||
#define BLKSIZE 0x100
|
|
||||||
#define HEADERSIZE 0x20
|
|
||||||
#define MAX_FN_LEN 25 // 26 - null char
|
|
||||||
#define MAX_FILE_SIZE (BLKSIZE * 0x100) - HEADERSIZE
|
|
||||||
|
|
||||||
void set_spit_stream(FILE *stream);
|
|
||||||
int is_regular_file(char *path);
|
|
||||||
void spitempty();
|
|
||||||
int spitblock(char *fullpath, char *fn);
|
|
||||||
int spitdir(char *path, char *prefix, char **patterns);
|
|
@ -1,54 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <fnmatch.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "cfs.h"
|
|
||||||
|
|
||||||
void usage()
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Usage: cfspack [-p pattern] [/path/to/dir...]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int patterncount = 0;
|
|
||||||
char **patterns = malloc(sizeof(char**));
|
|
||||||
patterns[0] = NULL;
|
|
||||||
while (1) {
|
|
||||||
int c = getopt(argc, argv, "p:");
|
|
||||||
if (c < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (c) {
|
|
||||||
case 'p':
|
|
||||||
patterns[patterncount] = optarg;
|
|
||||||
patterncount++;
|
|
||||||
patterns = realloc(patterns, sizeof(char**)*(patterncount+1));
|
|
||||||
patterns[patterncount] = NULL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int res = 0;
|
|
||||||
for (int i=optind; i<argc; i++) {
|
|
||||||
if (is_regular_file(argv[i])) {
|
|
||||||
// special case: just one file
|
|
||||||
res = spitblock(argv[i], basename(argv[i]));
|
|
||||||
} else {
|
|
||||||
res = spitdir(argv[i], "", patterns);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (res == 0) {
|
|
||||||
spitempty();
|
|
||||||
}
|
|
||||||
free(patterns);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#define BLKSIZE 0x100
|
|
||||||
#define HEADERSIZE 0x20
|
|
||||||
#define MAX_FN_LEN 25 // 26 - null char
|
|
||||||
|
|
||||||
bool ensuredir(char *path)
|
|
||||||
{
|
|
||||||
char *s = path;
|
|
||||||
while (*s != '\0') {
|
|
||||||
if (*s == '/') {
|
|
||||||
*s = '\0';
|
|
||||||
struct stat path_stat;
|
|
||||||
if (stat(path, &path_stat) != 0) {
|
|
||||||
if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*s = '/';
|
|
||||||
}
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unpackblk(char *dstpath)
|
|
||||||
{
|
|
||||||
char buf[MAX_FN_LEN+1];
|
|
||||||
if (fgets(buf, 3+1, stdin) == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (strcmp(buf, "CFS") != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int c = getchar();
|
|
||||||
uint8_t blkcnt = c;
|
|
||||||
if (blkcnt == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
c = getchar();
|
|
||||||
uint16_t fsize = c & 0xff;
|
|
||||||
c = getchar();
|
|
||||||
fsize |= (c & 0xff) << 8;
|
|
||||||
|
|
||||||
if (fgets(buf, MAX_FN_LEN+1+1, stdin) == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char fullpath[0x1000];
|
|
||||||
strcpy(fullpath, dstpath);
|
|
||||||
strcat(fullpath, "/");
|
|
||||||
strcat(fullpath, buf);
|
|
||||||
if (!ensuredir(fullpath)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int blksize = (BLKSIZE-HEADERSIZE)+(BLKSIZE*(blkcnt-1));
|
|
||||||
int skipcount = blksize - fsize;
|
|
||||||
FILE *fp = fopen(fullpath, "w");
|
|
||||||
while (fsize) {
|
|
||||||
c = getchar();
|
|
||||||
if (c == EOF) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fputc(c, fp);
|
|
||||||
fsize--;
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
while (skipcount) {
|
|
||||||
getchar();
|
|
||||||
skipcount--;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
if (argc != 2) {
|
|
||||||
fprintf(stderr, "Usage: cfspack /path/to/dest\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
char *dstpath = argv[1];
|
|
||||||
// we fail if there isn't at least one block
|
|
||||||
if (!unpackblk(dstpath)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
while (unpackblk(dstpath));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <fnmatch.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "cfs.h"
|
|
||||||
|
|
||||||
#define PUTC(c) putc(c, spitstream)
|
|
||||||
|
|
||||||
static FILE *spitstream = NULL;
|
|
||||||
|
|
||||||
void set_spit_stream(FILE *stream)
|
|
||||||
{
|
|
||||||
spitstream = stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_regular_file(char *path)
|
|
||||||
{
|
|
||||||
struct stat path_stat;
|
|
||||||
stat(path, &path_stat);
|
|
||||||
return S_ISREG(path_stat.st_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void spitempty()
|
|
||||||
{
|
|
||||||
if (spitstream == NULL) spitstream = stdout;
|
|
||||||
PUTC('C');
|
|
||||||
PUTC('F');
|
|
||||||
PUTC('S');
|
|
||||||
for (int i=0; i<0x20-3; i++) {
|
|
||||||
PUTC(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int spitblock(char *fullpath, char *fn)
|
|
||||||
{
|
|
||||||
if (spitstream == NULL) spitstream = stdout;
|
|
||||||
FILE *fp = fopen(fullpath, "r");
|
|
||||||
fseek(fp, 0, SEEK_END);
|
|
||||||
long fsize = ftell(fp);
|
|
||||||
if (fsize > MAX_FILE_SIZE) {
|
|
||||||
fclose(fp);
|
|
||||||
fprintf(stderr, "File too big: %s %ld\n", fullpath, fsize);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/* Compute block count.
|
|
||||||
* We always have at least one, which contains 0x100 bytes - 0x20, which is
|
|
||||||
* metadata. The rest of the blocks have a steady 0x100.
|
|
||||||
*/
|
|
||||||
unsigned char blockcount = 1;
|
|
||||||
int fsize2 = fsize - (BLKSIZE - HEADERSIZE);
|
|
||||||
if (fsize2 > 0) {
|
|
||||||
blockcount += (fsize2 / BLKSIZE);
|
|
||||||
}
|
|
||||||
if (blockcount * BLKSIZE < fsize + HEADERSIZE) {
|
|
||||||
blockcount++;
|
|
||||||
}
|
|
||||||
PUTC('C');
|
|
||||||
PUTC('F');
|
|
||||||
PUTC('S');
|
|
||||||
PUTC(blockcount);
|
|
||||||
// file size is little endian
|
|
||||||
PUTC(fsize & 0xff);
|
|
||||||
PUTC((fsize >> 8) & 0xff);
|
|
||||||
int fnlen = strlen(fn);
|
|
||||||
for (int i=0; i<MAX_FN_LEN; i++) {
|
|
||||||
if (i < fnlen) {
|
|
||||||
PUTC(fn[i]);
|
|
||||||
} else {
|
|
||||||
PUTC(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// And the last FN char which is always null
|
|
||||||
PUTC(0);
|
|
||||||
char buf[MAX_FILE_SIZE] = {0};
|
|
||||||
rewind(fp);
|
|
||||||
fread(buf, fsize, 1, fp);
|
|
||||||
fclose(fp);
|
|
||||||
fwrite(buf, (blockcount * BLKSIZE) - HEADERSIZE, 1, spitstream);
|
|
||||||
fflush(spitstream);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int spitdir(char *path, char *prefix, char **patterns)
|
|
||||||
{
|
|
||||||
DIR *dp;
|
|
||||||
struct dirent *ep;
|
|
||||||
|
|
||||||
int prefixlen = strlen(prefix);
|
|
||||||
dp = opendir(path);
|
|
||||||
if (dp == NULL) {
|
|
||||||
fprintf(stderr, "Couldn't open directory.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
while ((ep = readdir(dp))) {
|
|
||||||
if ((strcmp(ep->d_name, ".") == 0) || strcmp(ep->d_name, "..") == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ep->d_type != DT_DIR && ep->d_type != DT_REG) {
|
|
||||||
fprintf(stderr, "Only regular file or directories are supported\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int slen = strlen(ep->d_name);
|
|
||||||
if (prefixlen + slen> MAX_FN_LEN) {
|
|
||||||
fprintf(stderr, "Filename too long: %s/%s\n", prefix, ep->d_name);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
char fullpath[0x1000];
|
|
||||||
strcpy(fullpath, path);
|
|
||||||
strcat(fullpath, "/");
|
|
||||||
strcat(fullpath, ep->d_name);
|
|
||||||
char newprefix[MAX_FN_LEN];
|
|
||||||
strcpy(newprefix, prefix);
|
|
||||||
if (prefixlen > 0) {
|
|
||||||
strcat(newprefix, "/");
|
|
||||||
}
|
|
||||||
strcat(newprefix, ep->d_name);
|
|
||||||
if (ep->d_type == DT_DIR) {
|
|
||||||
int r = spitdir(fullpath, newprefix, patterns);
|
|
||||||
if (r != 0) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
char **p = patterns;
|
|
||||||
// if we have no pattern, we match all
|
|
||||||
if (p && *p) {
|
|
||||||
int matches = 0;
|
|
||||||
while (*p) {
|
|
||||||
if (fnmatch(*p, ep->d_name, 0) == 0) {
|
|
||||||
matches = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
if (!matches) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int r = spitblock(fullpath, newprefix);
|
|
||||||
if (r != 0) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(dp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user