-
Notifications
You must be signed in to change notification settings - Fork 1
/
forth2.fs
1468 lines (1071 loc) · 34.1 KB
/
forth2.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\
\ This is the core of Legs Forth
\
: true -1 ;
: false 0 ;
: noop ; \ a no operation
: cr d emit ; \ prints a cr
: space 20 emit ; \ prints a space
: ba here 0 , ; \ puts a back reference on stack
: imm 1 latest >flag ! ; \ mark latest word immediate
: then here swap ! ; imm \ then
: ' \ find a name
word find 0= 0bra [ ba ]
wnf quit then ;
: pp ' , ; imm \ compile word (ignoring imm flag)
: ^ lit lit , pp pp lit , , ; imm \ compile word ( later )
: if ^ 0bra ba ; imm \ if
: else ^ bra ba swap pp then ; imm \ else
: begin here ; imm \ begin
: again ^ bra , ; imm \ again
: until ^ 0bra , ; imm \ until
: while pp if ; imm \ while
: repeat swap pp again pp then ; imm \ repeat
: for ^ push here ^ dofor ba ; imm \ for
: next pp repeat ; imm \ next
: r@ rp@ cell+ @ ; \ copy top of return stack
: cell- cell - ; \ decriment TOS by a cell
\ ( a x -- a ) push a software stack
: -! swap cell- tuck ! ;
\ ( a b c -- b a c ) swap stack items 1 and 2
: 12swap rot swap ;
\ ( a b c -- c b a ) swap stack items 0 and 2
: 02swap -rot swap ;
: char? \ returns the ascii code for first letter in next word
^ lit word @+ drop c@ , ; imm
: ptib \ returns address of general parse buffer
tib 40 + ; \ locate past the word buffer
: parse \ ( c -- ca ) gets chars from input until char c is found
ptib 0 !+
begin over ekey tuck - while
c!+ 1 ptib +! repeat 3drop ptib ;
: ( \ embedded comment
char? ) parse drop ; imm
: slit pull dup @+ + push ; \ puts a string literal on stack
: " char? " parse ; \ makes a immediate mode string
: p" here " s, ; \ compiles a string
: s" ^ slit " s, ; imm \ comiles a string literal
: n" " @+ for c@+ c, next drop ; \ compiles just string bytes
\ append c to ca
: astr ( c ca -- )
dup push @+ + c! 1 pull +! ;
: defer ^ noop ; imm \ defers a word's definition
: [is] ( xt1 xt2 -- ) \ xt1 action is replace with xt2
!+ lit exit swap ! ;
: is ( xt "name" -- )
' swap [is] ;
: do&forget ( xt -- ) \ do xt and reset cp to before xt
dup exec cp ! ;
: xlit
pull @+ over + push ;
: [[ here ] ; \ starts a quote
: ]] pp ; ; imm \ ends a quote
: {{ \ strart compiled quote
^ xlit ba ; imm
: }} \ resolve compiled quote
^ exit here cell - over - swap ! ; imm
: type
@+ for c@+ emit next drop ;
: ." pp s" ^ type ; imm \ prints a string literal
: .( char? ) parse type ; imm \ prints a string literal
: utoc ( u -- c ) \ converts digit to ascii
dup a - 0< if 30 else 57 then + ;
: u. ( x -- ) \ print unsigned number
dup f and utoc swap shr shr shr shr ?dup if u. then emit ;
: . u. space cr ;
: depth sp@ 7f80 swap - shr ;
: bemit dup shr shr shr shr utoc emit f and utoc emit ;
: wemit sp@ dup c@ bemit char+ c@ bemit drop ;
: dump ( a -- ) \ dump memory
cr dup wemit ." :" cr
8 for
8 for
dup c@ bemit space char+
next
8 -
8 for
dup c@ dup bl? if drop 2e emit else emit then char+
next cr
next
cr drop
;
\
\ Replace the reset vector with
\ something nicer
\
[[ ." Legs Forth - 16 bit" cr ." ok" cr quit ]] 0 !
\
\ Replace WNF with a nicer version
\
[[ cr ." *** Word Not Found: " type cr bye ]] is wnf
: interact ( -- )
lit pp wnf {{ cr ." *** Word Not Found: " type cr quit }} [is] ;
: = xor 0= ; \ compares value to equality
: <> = com ; \ compares value for inequality
: u< 2dup xor 0< if nip 0< exit then - 0< ;
: u> swap u< ;
\ Signed Cell Compares
: < 2dup xor 0< if drop 0< exit then - 0< ;
: > swap < ;
: >= < com ;
: <= 2dup = -rot < or ;
\
\ for a *slightly* better interactive experience
\ make backspace do something
\ This replaces "word"
\
: bl? 21 u< ; \ this is used to make key that return -1 work!
\ ( -- ca ) \ get next word from input stream
[[
tib 0 !+
begin ekey dup bl? while drop repeat
\ exit with empty buffer if source closed
dup 0< if 2drop tib exit then
begin
dup 8 = here @ and if
drop 1- -1 tib +!
else
c!+ 1 tib +!
then
ekey dup dup dup bl? swap 8 <> and swap 0< or
until 2drop tib
]] is word
: docreate \ runs xt ( addr -- ) mem: docreate xt data....
r@ cell+ pull @ exec ;
: create ( "name" -- @xt ) \ creates a word that does something when called :)
header ^ docreate ^ exit ;
: does> ( -- ) \ resolves the xt address
pull latest >xt @ cell+ ! ;
: variable ( x "name" -- ) \ create a variable "name" init'd to x
create , ;
: constant ( x "name" -- ) \ create a constant "name" init'd to x
create , does> @ ;
\ this is not proper! *** it is implimentation dependent ***
: cells shl ;
: allot ( x -- ) \ allots x bytes to
here + cp ! ;
\ : salloc ( u -- a ) \ returns address of alloted bytes
\ here swap allot ;
\ : free ( a -- ) \ frees alloted bytes
\ drop
\ ;
interact bye
\ ******************************************
\ Structures
\ ******************************************
: field ( u u -- u ) \ defines a field in a structure
over + swap create , does> @ + ;
: struct ( u -- ) \ defines a structure
constant ;
\ *************************************************
\ Vocabularies
\ *************************************************
create vstack 8 cells allot \ search order stack
latest \ stack last vocab word for breaking list apart
vstack variable vp \ vocabulary pointer
: vocabulary ( "name" -- ) \ creates a vocabulary
create 0 , does> vp @ ! ;
vocabulary legs \ these are the primitive definitions
vocabulary forth \ these are the base system
vocabulary vocab \ just the vocabulary words
0 variable compile-list \ where to compile new definitions
: also ( -- ) \ duplicates top of search stack
vp @ dup @ swap cell+ dup vp ! ! ;
: only ( -- ) \ reset vocabulary stack and vocabulary words
vstack vp ! vocab ;
: previous ( -- ) \ drops top of search stack
vp @ cell- vp ! ;
: definitions ( -- ) \ sets new definitions to go to top of search stack
vp @ @ compile-list ! ;
: words
vp @ begin
dup @ @ begin dup while dup >name type space @ repeat drop cr
cell- dup vstack u< until drop
;
: newfind ( ca -- ca 0 | xt 1 | xt -1 )
vp @ begin
2dup @ dfind ?dup if
\ return found ( ca vp da )
nip nip dup >xt @ swap >flag @ if 1 else -1 then exit then
cell- dup vstack u< until drop 0
;
: newdh ( -- lh ) \ new dh
compile-list @ ;
[[
\ redefine find
lit find lit newfind [is]
\ setup search list
only legs also forth definitions also vocab
\
\ split existing dictionary into three vocabularies
\
\ find first "forth" word
latest @ begin dup @ >xt @ ff u> while @ repeat
\ make it the head to the "legs" vocab
dup @ vstack @ ! 0 swap !
over dup @ vstack cell+ @ ! 0 swap !
\ make latest head to "vocab"
latest vstack cell+ cell+ @ !
\ redefine dh
lit dh lit newdh [is]
]] do&forget
only vocab also legs also forth definitions
\ ********************************************
\ Now We're gonna split the dictionary
\ and code area up.
\ ********************************************
cp0 constant host
host variable cpp \ pointer to code pointer
host variable dpp \ pointer to dictionary pointer
\ This is a new CP method
[[ cpp @ ]]
: dp dpp @ ;
: dhere dp @ ;
: d, dhere swap !+ dp ! ;
: dc, dhere swap c!+ dp ! ;
: ds, ( ca -- ) \ compile string to dictionary area
@+ dup d, for c@+ dc, next drop ;
\ This is a slighly modified "header"
\ to work with separate dictionary / compile areas
[[
dhere latest d, dh ! \ link
dhere 0 d, \ xt
0 d, \ compile blank flag
word ds, \ compile word new
here swap ! \ resolve xt
]]
\ install new methods
here host ! \ setup "host" cp
is header \ set new :
is cp \ set new cp
\ *************************************************
\ Local Names
\ This forth's "locals" doesn't refer to stack data
\ "locals" refers to local word definitions
\ for creating temporily named "helper" words
\ *************************************************
memz 500 - constant localarea \ The start of the local headers area
localarea variable lp \ the local header pointer (like CP )
vocabulary localdefs \ and the list head
\ This starts a "local block" - all definitions headers will be
\ compiled to a separate area, and linked in to the localdefs
\ vocabulary
: { ( -- lp lh )
also localdefs definitions \ add search list and compile vocab
lp dpp ! \ compile headers to local area
lp @ \ copy of lp for later reversion
vp @ @ @ \ copy of list head for later revert
;
\ Pulic changes the compile area back to regular - but leaves the
\ newly defined locals findable
: public ( -- )
previous definitions also localdefs \ change compile list to reg
host dpp ! \ compile headers back to host area
;
\ and this ends the local block, it earses the headers of the
\ locals ( not the code ) and resets the list head
: } ( lp lh -- )
vp @ @ ! \ restore local list head
lp ! \ restore localarea pointer
previous definitions \ change search list back
host dpp ! \ compile header back to host area
;
\ ***************************************
\ memory move, compare, fill
\ ***************************************
: mv ( d s u -- ) \ move u bytes at a1 to a2
for c@+ 12swap c!+ swap next 2drop ;
: cmp ( a1 a2 u -- f ) \ compare u bytes
for c@+ push swap c@+ pull <> if 2drop false unloop exit then next
2drop true ;
\ ***************************************
\ Simple run-time debug
\ ***************************************
: de. ( -- "out" ) \ prints debug and stack depth
." debug: " depth . cr sp@ dump ;
\ ******************************
\ Dynamic Memory Allocation
\ ******************************
{
0
cell field >next
cell field >flags
struct mlist
: size ( a -- u ) \ returns effective size of chunk
dup @ swap - mlist - ;
: >data ( a -- a ) \ return chunk data field address
mlist + ;
: >ca ( a -- a ) \ converts a data field address to chunk address
mlist - ;
: mark ( a -- ) \ marks chunk as used
>flags dup @ mint or swap ! ;
: unmark ( a -- ) \ marks chunk as free
>flags dup @ mint com and swap ! ;
: used? ( a -- f ) \ returns true if chunk is used
>flags @ 0< ;
: free? ( a -- f ) \ returns true if chunk is free
used? 0= ;
: last? ( a -- f ) \ returns true if chunk is last in list
@ @ 0= ;
: nconsol ( a -- ) \ consolidates chunk with next chunk
dup @ @ swap ! ;
: split ( u a -- ) \ split chunk to u bytes
tuck + mlist + \ calc new chunk address ( a n )
dup unmark \ mark new chunk as free ( a n )
over @ over ! \ init new chunks next field ( a n )
swap ! \ mod parent chunk's next field
;
: find ( u a1 -- a2 ) \ find node big enough for u bytes
begin 2dup size u> 0= over free? and if nip exit then
dup last? 0= while @ repeat 2drop false ;
: compact ( a -- ) \ compact heap list
begin dup last? 0= while
dup free? over @ free? and if dup nconsol else @ then
repeat drop ;
public
\ allocate u bytes of data on a heap starting with
\ node address a1
: alloc ( u a1 -- a2 )
2dup find ( u a a )
\ if no chunk big enough then compact
\ and try again
dup 0= if drop dup compact
2dup find ( u a a )
dup 0= if nip nip exit then
then
\ mark chunk as used
nip dup mark
\ split chunk if big enough
2dup size swap - mlist cell+ u> if 2dup split then
\ return data field
nip >data
;
\ dallocate dynamic memory a
: free ( a -- )
dup >ca free? if ." dup free!" cr drop exit then
?dup 0= if exit then >ca unmark ;
: .h ( a -- ) \ print heap
begin
dup wemit space dup size wemit space dup @ wemit space dup used? wemit cr
dup last? 0= while @
repeat drop ;
\ address size
}
\ allots and inits named chunk
: dchunk ( u "name" -- ) \ initialize chunks
create ba false , swap allot here swap ! false , ;
600 dchunk heap
: salloc ( u -- a ) \ allocate system heap
heap alloc ?dup 0= if ." out of memory" cr quit then
;
: .heap
heap .h ;
: debug ( x -- ) \ print debug message
." debug " . ;
\ ***********************************
\ objects
\ ***********************************
0
cell field >csize \ size of this class structure
cell field >msize \ size of object's member data
cell field >parent \ ptr to parent's class struct
cell field >vocab \ class's vocabulary (linked with parent's)
0 field >vmt \ Method table ( array of xt's )
struct class
create vmt 32 cells allot \ class assembly area
0 variable obj \ run-time object pointer
0 variable ovp \ saved vp pointer
: calloc ( u -- a ) \ compile alloc
here swap allot ;
: redef ( xt "name" -- ) \ redefined a dictionary xt
word
vp @ begin
2dup @ dfind ?dup if
\ if found ( xt ca vp da )
nip nip >xt ! exit then
cell- dup vstack u< until
;
: svvp ( -- ) \ saves vp in ovp var
vp @ ovp ! ;
[[ svvp : ]] redef :
[[ ovp @ vp ! pp ; ]] redef ;
: self ( -- object ) \ puts self-object on stack
obj @ ;
: method ( "name" -- ) ( x*i obj -- x*j )
svvp
\ play with the class assembly area
create vmt @ , \ create name to offset
here vmt dup @ + ! \ set xt in vmt
cell vmt +! \ increment vmt size
] \ start compiling method
\ make a run-time action for new method
does> self push @ swap dup obj ! @ + @ exec pull obj ! ;
: member ( size "name" -- ) ( -- a )
create vmt >msize @ , \ create name with offset
vmt >msize +! \ incrmenet vmt member size
\ make a run-time action for new member
does> @ self + ;
: use ( "class" -- ) \ use a class vocabulary
pp ' exec also >vocab vp @ ! ; imm
: flz ( -- ) ( -- class ) \ finalizes a class
previous definitions
vmt @ calloc dup vmt vmt @ mv ;
: class ( "name" -- ) ( -- class ) \ finalizes class and names
flz constant
;
: extend ( class -- ) \ copies class to class building area
dup
vmt swap dup @ mv \ copy class struct
vmt >parent ! \ fill in new class's parant ptr
also vmt >vocab vp @ ! \ use new vocabulary
definitions
;
: as ( "name" -- ) \ overrides parent method
svvp here vmt pp ' cell+ cell+ @ + ! ] ;
: parent ( "name" -- ) \ compiles parent's method
pull dup cell+ push @
self dup @ dup push >parent @ swap !
self swap exec pull self ! ;
\
\ a hand-build base object class
\
vmt
4 cells !+ \ class size
cell !+ \ member size
0 !+ \ parent class ( NULL in obj's case )
0 !+ \ vocab head
also vmt >vocab vp @ ! definitions \ use new vocab
method init true ; ( -- f ) \ initializes object
method deinit true ; ( -- f ) \ deinitializes object
class obj
: new ( i*x class -- object ) \ makes an object
use obj
dup >msize @ salloc tuck !
dup push init if pull exit then
pull free false
;
: destroy ( object -- ) \ destroys an object
use obj
dup deinit if free else drop then
;
: single ( "name" -- ) \ finalizes class and creates a signalton object
flz new constant
;
\ **********************************
\ Abstract Containers
\ **********************************
obj extend
method add ; ( item -- ) \ add an object to the container
method giter ; ( -- iter ) \ get an iterater object for the container
method no ; ( -- u ) \ number of items in container
class container
obj extend
method next? ; ( -- f ) \ is there another value?
method inext ; ( -- ) \ position to new value
method gobj ; ( -- obj ) \ get item
class iter
\ xt ( item -- f )
: iforeach ( iter xt -- f ) \ iterate through container
use iter
swap
begin dup next? while
2dup push push gobj swap exec
if pull pull 2drop true exit then
pull pull dup inext
repeat 2drop false ;
\ xt ( obj -- f )
: foreach ( container xt -- item | 0 ) \ iterate through container
use container use iter
swap giter dup push swap iforeach
if r@ gobj
else false
then pull destroy ;
\ ****************************
\ Association List
\ ****************************
obj extend
as init ( ca -- f )
;
method gkey ( -- ca ) \ gets key value
;
method gval ( -- x ) \ gets value
;
class assoc
\ find item by key
\ returns false if key is not found
\ val may be a double!!
: findbykey ( ca container -- val -1 | 0 )
use assoc
{{ gkey over s= }} foreach nip
dup if gval true then ;
\ list assoc list's key value's
: listkeys ( container -- )
use assoc
{{ dup gval wemit space gkey type cr false }} foreach drop ;
\ *********************************
\ Simple Linked List Container
\ *********************************
obj extend
cell member nexti \ ptr to next list item
cell member ref \ ptr to contained object
as init ( obj head -- f )
nexti ! ref ! true ;
method gnext ( -- a ) \ get ptr to next list item
nexti @ ;
method gref ( -- obj ) \ get ptr to contained object
ref @ ;
class litem
iter extend
cell member addr \ the current addr
as init ( head -- f ) \ init an iterator
addr ! true ;
as next? ( -- f ) \ is there another value?
addr @ ;
as inext ( -- ) \ position to new value
use litem
addr @ gnext addr ! ;
as gobj ( -- obj ) \ get item
use litem
addr @ gref ;
class liter
container extend
cell member head \ head of list
cell member count \ number of items
as init ( -- f )
0 count ! 0 head ! true ;
as add ( obj -- )
head @ litem new head ! 1 count +! ;
as giter ( head -- iter )
head @ liter new ;
as no ( -- u )
count @ ;
class list
\ **************************
\ Memory Allocated Assocition List
\ **************************
assoc extend
cell member key \ key value ptr storage
as init ( list ca -- f )
use list
key ! self swap add true
;
as gkey ( -- ca ) \ gets key value
key @
;
as gval ( -- obj ) \ gets value
self ;
class massoc
\ ***********************************
\ Block Device Objects
\ ***********************************
0
cell field >low
cell field >high
cell field >drive
cell field >device
struct baddr
list new constant cdevs
massoc extend
as init ( ca -- f ) \ init charactor device
cdevs swap parent init ;
method get ( -- c ) ; \ get charactor
method put ( c -- ) ; \ put charactor
class cdev
\ ****************************************
\ Becker Character Device
\ ****************************************
cdev extend
as init
parent init ;
as put ( c -- ) \ send a byte via becker port
ff42 p! ;
as get ( -- c ) \ receive a byte via becker port
begin ff41 p@ until ff42 p@ ;
class becker
\ **********************************
\ Block Devices
\ **********************************
list new constant bdevs
massoc extend
as init ( ca -- f ) \ init block device
bdevs swap parent init ;
method get ( baddr a -- f ) ; \ get sector
method put ( baddr a -- f ) ; \ put sector
class bdev
\ *********************************
\ DriveWire 4 block device
\ *********************************
bdev extend
cell member device \ which charactor device to use
as init ( ca_name ca_cdev -- f )
cdevs findbykey
if device ! parent init
else drop false then
;
: bkr! ( c -- )
use cdev device @ put ;
: bkr@ ( -- c )
use cdev device @ get ;
: cksum ( addr -- x ) \ compute a 256 byte checksum
0 swap 100 for c@+ rot + swap next drop ;
: senda ( baddr op -- ) \ send opcode and address
bkr! \ send opcode
dup >drive @ bkr! \ send drive no
dup >high @ bkr! \ send hi sector
>low c@+ bkr! \ send low sector msb
c@ bkr! \ send low sector lsb
;
: sendck ( a -- f ) \ send cksum
cksum sp@ c@+ bkr! c@ bkr! drop bkr@ ;
as get ( baddr a -- f ) \ get a drive sector
swap d2 senda \ send readop
dup 100 for bkr@ c!+ next drop
sendck
;
as put ( baddr a -- f ) \ put a drive sector
swap 57 senda \ send drive write op
dup 100 for c@+ bkr! next drop
sendck
;
class dw4
\ ************************************
\ Caching
\ ************************************
100 constant sector \ size of a disk sector
list new constant cache \ list of cobs
obj extend {
sector member data \ databuffer
baddr member addr \ address of cob
cell member count \ lock count
cell member time \ time of release
cell member dflag \ dirty flag
0 variable rtimer \ release timer
: fill ( -- ) \ fill cob's data buffer
use bdev addr data over >device @ get drop ;
: write ( -- ) \ writes cob's data back
use bdev addr data over >device @ put drop ;
public
method reassign ( baddr -- ) \ reassign
addr swap baddr mv \ copy addr
fill \ fill data from device driver
1 count ! \ set count to 1 lock
0 time ! \ reset time
;
as init ( baddr -- f )
self reassign
use container
self cache add \ add self to cache
true
;
method lock ( -- ) \ obtain lock on cob
1 count +! ;
method rel ( -- ) \ release lock on cob
count @ 1- dup count !
0= if rtimer @ 1+ dup rtimer ! time ! then
;
method daddr ( -- a ) \ return ptr to data
data ;
method rtime ( -- u ) \ return time of release
time @ ;
method free? ( -- f ) \ returns true if cob is free
count @ 0= ;
method dirty? ( -- f ) \ returns true if cob is dirty
dflag @ ;
method ccmp ( baddr -- f ) \ return true if baddr=addr
addr baddr cmp ;
method dirty ( -- ) \ mark cob as dirty
true dflag ! ;
} class cob
{
: csearch ( baddr -- cob | 0 ) \ search for matching cob
use cob
cache {{ over swap ccmp }} foreach nip ;
: free ( -- cob | 0 ) \ search for free cob
use cob
\ find a free cob
cache {{ free? }} foreach dup 0= if exit then
\ search list for a free and older cob x
cache {{ over rtime over rtime u> over free? and
if swap then drop false }} foreach drop
;
: 4nip ( dev dri high low x -- x ) \ nip 4 times
nip nip nip nip ;
public
4 variable cmax \ maximum cobs in a cache
: getc ( dev dri high low -- cob | 0 ) \ get a cob
sp@
use cob use list
\ if cache hit then return
dup csearch ?dup if 4nip nip dup lock exit then
\ if cache is less than max size then return with new cob
cache no cmax @ u< if cob new 4nip exit then
\ find an old unuse cob and reuse
free ?dup if tuck reassign 4nip exit then
\ and if cache is full on used cobs:
." Error: Cache full!" 4nip drop cr quit
;
: relc ( cob -- ) \ releases a cob
use cob ?dup if rel then ;
}
\ ****************************
\ Filesystem Class
\ ****************************
list new constant fses
massoc extend
cell member fs \ ptr to filesystem class
as init ( fs ca -- f )
fses swap parent init drop
fs !
true ;
as gval ( -- fs ) \ get the filesystem
fs @ ;
class fsitem
list new constant mounts
massoc extend
cell member drive \ drive no that the fs sits on
cell member device \ device that the fs sits on
cell member count \ how many open files
\ init this filesystem under the name ca on device and drive no
as init ( device drive ca_mount -- f )
mounts swap parent init drop
drive ! device !
0 count !
true ;
as deinit ( -- f )
count @ if ." FS is busy!" cr false else true ;
method fsgetc ( d d -- cob ) \ get a cob