Merge pull request #40 from forth-ev/8086-3.9.x

8086 3.9.x
This commit is contained in:
Carsten Strotmann 2022-08-18 11:32:56 +00:00 committed by GitHub
commit e8bf0dc77a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 4331 additions and 435 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
*.log
/.DS_Store
*~
/tools/blkpack
/tools/blkunpack

File diff suppressed because one or more lines are too long

View File

@ -20,7 +20,7 @@ ende 123
\ *** Block No. 1, Hexblock 1
\ volksFORTH Loadscreen for py65 target cas 15juli2020
\ volksFORTH Loadscreen for py65 target cas 02aug2020
forth definitions
: (C [compile] ( ; IMMEDIATE \ : ) ; IMMEDIATE
@ -58,7 +58,7 @@ HERE DUP ORIGIN!
\ *** Block No. 3, Hexblock 3
\ Coldstartvalues and user variables cas 15juli2020
\ Coldstartvalues and user variables cas 02aug2020
\
0 JMP 0 JSR HERE 2- >LABEL >WAKE
@ -67,7 +67,7 @@ HERE DUP ORIGIN!
0D6 ALLOT
\ Bootlabel
," VOLKSFORTH-83 3.8 py65 15july2020 CS"
," VolksForth-83 3.8.1 py65 02aug2020 CS"
@ -172,7 +172,7 @@ USER UDP \ POINTS TO NEXT FREE ADDR IN USER
\ *** Block No. 9, Hexblock 9
\ MANIPULATE SYSTEM POINTERS 29JAN85BP)
\ MANIPULATE SYSTEM POINTERS 29JAN85BP) cas 02aug2020
CODE SP@ ( -- ADDR)
SP LDA N STA SP 1+ LDA N 1+ STA
@ -628,12 +628,12 @@ CODE U< ( U1 U2 -- FLAG)
\ *** Block No. 33, Hexblock 21
\ COMPARISION WORDS 24DEC83KS)
\ COMPARISION WORDS 24DEC83KS) cas 02aug2020
| : 0< 8000 AND 0<> ;
: > ( N1 N2 -- FLAG) SWAP < ;
: 0> ( N -- FLAG) NEGATE 0< ;
: 0> ( N -- FLAG) DUP 0< SWAP 0= OR NOT ;
: 0<> ( N -- FLAG) 0= NOT ;
: U> ( U1 U2 -- FLAG) SWAP U< ;
: = ( N1 N2 -- FLAG) - 0= ;
@ -2300,7 +2300,7 @@ HOST TARGET
\ *** Block No. 121, Hexblock 79
\ 'COLD 07JUN85BP) cas 15juli2020
\ 'COLD 07JUN85BP) cas 02aug2020
| : INIT-VOCABULARYS VOC-LINK @
BEGIN DUP 2- @ OVER 4 - ! @ ?DUP 0= UNTIL ;
@ -2309,7 +2309,7 @@ HOST TARGET
DEFER 'COLD ' NOOP IS 'COLD
| : (COLD INIT-VOCABULARYS INIT-BUFFERS PAGE 'COLD ONLYFORTH
." volksFORTH-83 3.8 py65 202007" CR RESTART ; -2 ALLOT
." volksFORTH-83 3.8.1 py65 202008" CR RESTART ; -2 ALLOT
DEFER 'RESTART ' NOOP IS 'RESTART
| : (RESTART ['] (QUIT IS 'QUIT

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
\\ Transinient Assembler 11Nov86 Dieses File enthaelt Befehle, die den Assembler vollstaendig in den Heap laden, so dass er schliesslich mit clear wieder vergessen werden kann. Dadurch ist es nicht notwendig in einer Anwendung den ganzen Assembler im Speicher lassen zu muessen, nur weil einige primitive Worte in Assembler geschrieben sind. \ Internal Assembler UH 22Oct86 Onlyforth here $C00 hallot heap dp ! include ass8080.scr dp !

File diff suppressed because one or more lines are too long

1
8080/AmstradCPC/COPY.SCR Normal file
View File

@ -0,0 +1 @@
\ Copy und Convey 19Nov87 Dieses File enthaelt Definitionen, die urspruenglich im Kern enthalten waren. Sie sind jetzt ausgelagert worden, um den Kern klein zu halten. copy kopiert einen Screen convey kopiert einen Bereich von Screens \ moving blocks 20Oct86 19Nov87| : full? ( -- flag ) prev BEGIN @ dup @ 0= UNTIL 6 + @ 0< ; | : fromblock ( blk -- adr ) fromfile @ (block ; | : (copy ( from to -- ) dup isfile@ core? IF prev @ emptybuf THEN full? IF save-buffers THEN offset @ + isfile@ rot fromblock 6 - 2! update ; | : blkmove ( from to quan --) save-buffers >r over r@ + over u> >r 2dup u< r> and IF r@ r@ d+ r> 0 ?DO -1 -2 d+ 2dup (copy LOOP ELSE r> 0 ?DO 2dup (copy 1 1 d+ LOOP THEN save-buffers 2drop ; : copy ( from to --) 1 blkmove ; : convey ( [blk1 blk2] [to.blk --) swap 1+ 2 pick - dup 0> not Abort" Nein !" blkmove ;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
\\ Double words 11Nov86 Dieses File enthaelt Worte fuer 32-Bit Objekte. Im Kern bereits enthalten sind: 2@ 2! 2dup 2drop 2swap dnegate d+ Hier werden definiert: 2Variable 2Constant 2over d* \ 2over 2@ 2! 2Variable 2Constant UH 30Oct86 : 2Variable Variable 2 allot ; : 2Constant Create , , does> 2@ ; Code 2over ( 32b1 32b2 -- 32b1 32b2 32b1 ) 7 H lxi SP dad M D mov H dcx M E mov D push H dcx M D mov H dcx M E mov D push Next end-code --> \\ Code 2@ ( addr -- 32b ) H pop H push H inx H inx M E mov H inx M D mov H pop D push M E mov H inx M D mov D push Next end-code Code 2! ( 32b addr -- ) H pop D pop E M mov H inx D M mov H inx D pop E M mov H inx D M mov Next end-code \ d* d- 29Jun86 : d* ( d1 d2 -- d1*d2 ) rot 2over rot um* 2swap um* d+ 2swap um* d+ ; : d- ( d1 d2 -- d1-d2 ) dnegate d+ ;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
8080/AmstradCPC/KERNEL.COM Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
\ Mathematics calculating sin & cos nach FD IV 1 6UH 03Dec86 Dieses File enthaelt Definitionen zur Berechnung von Integer-Sinus und -Cosinus. Sie werden z.B. von der Turtle-Grafik benutzt. \ Mathematics calculating sin & cos nach FD IV 1 6 05Sep86 Create sintab decimal 0000 , 0175 , 0349 , 0523 , 0698 , 0872 , 1045 , 1219 , 1392 , 1564 , 1736 , 1908 , 2079 , 2250 , 2419 , 2588 , 2756 , 2924 , 3090 , 3256 , 3420 , 3584 , 3746 , 3907 , 4067 , 4226 , 4384 , 4540 , 4695 , 4848 , 5000 , 5150 , 5299 , 5446 , 5592 , 5736 , 5878 , 6018 , 6157 , 6293 , 6428 , 6561 , 6691 , 6820 , 6947 , 7071 , 7193 , 7314 , 7431 , 7547 , 7660 , 7771 , 7880 , 7986 , 8090 , 8192 , 8290 , 8387 , 8480 , 8572 , 8660 , 8746 , 8829 , 8910 , 8988 , 9063 , 9135 , 9205 , 9272 , 9336 , 9397 , 9455 , 9511 , 9563 , 9613 , 9659 , 9703 , 9744 , 9781 , 9816 , 9848 , 9877 , 9903 , 9925 , 9945 , 9962 , 9976 , 9986 , 9994 , 9998 , 10000 , : sintable ( deg -- sine*10000 ) 2* sintab + @ ; --> \ sin 05Sep86 : s180 ( deg -- sine*10000 ) dup 90 > IF 180 swap - ( reflect ) THEN sintable ; : sin ( deg -- sine*10000 ) 360 mod dup 180 > IF 180 - s180 negate exit THEN s180 ; : cos ( deg -- cosine*10000 ) 90 + sin ; hex

View File

@ -0,0 +1 @@
\ 8080-Portzugriff UH 11Nov86 Dieses File enthaelt Definitionen um die 8080-Ports ueber 8-Bit Adressen anzusprechen. Der Code ist leider selbstmodifizierend, da beim 8080 die Portadresse im Code ausdruecklich angegeben werden muss. Sollte dies unerwuenscht sein und ein Z80-Komputer vorliegen, kann auch das File portz80.scr benutzt werden, indem die Z80-IO-Befehle (16Bit-Adressen) benutzt werden. \ 8080-Portzugriff pc@, pc! 15Jul86 ' 0 | Alias patch Code pc@ ( addr -- c ) H pop L A mov here 4 + sta patch in 0 H mvi A L mov Hpush jmp end-code Code pc! ( c addr -- ) H pop L A Mov here 6 + sta H pop L A mov patch out Next end-code

View File

@ -0,0 +1 @@
\ Z80-Portzugriff UH 05Nov86 Dieses File enthaelt Definitionen um die Z80-Ports ueber 16-Bit Adressen anzusprechen. Einige Komputer, so die der Schneider Serie dekodieren ihre Ports etwas unkonventionell, sodass sie unbedingt ueber 16-Bit Adressen angesprochen werden muessen. Im allgemeinen sollte es ausreichen 8-Bit Adressen zu benutzen. \ Z80-Portaccess Extending 8080-Assembler UH 05Nov86 Assembler definitions | : Z80-io ( base -- ) \ define special Z80-io instruction Create c, Does> ( reg -- ) $ED c, c@ swap 8 * + c, ; $40 Z80-io (c)in $41 Z80-io (c)out Forth definitions --> \ store and fetch values with 16-bit port-adresses UH 05Nov86 Code pc@ ( 16b -- 8b ) \ fetch 8-bit value from 16-bit port-addr H pop IP push H B mvx L (c)in 0 H mvi IP pop hpush jmp end-code Code pc! ( 8b 16b -- ) \ store 8-bit value to 16-bit port-addr H pop D pop IP push H B mvx E (c)out IP pop Next end-code

View File

@ -0,0 +1 @@
\\ Primitivst Editor zur Installation UH 17Nov86 Da zur Installationszeit der Full-Screen Editor noch nicht funtionsfaehig ist, muessen die zu aendernden Screens auf eine andere Weise ge{nder werden: mit dem primitivst Editor PRIMED, der nur ein Benutzer wort enthaelt: Benutzung: Mit "nn LIST" Screen nn zum editieren Anwaehlen, dann mit "ll NEW" den Screen aendern. Es koennen immer nur ganze Zeilen neu geschrieben werden. ll gibt an, ab welcher Zeilennummer neue Zeilen eingeben werden sollen. Die Eingabe einer leeren Zeile (nur RETURN) bewirkt den Abruch von NEW. Nach jeder Eingabe von RETURN wird die eingegebene Zeile in den Screen uebernommen, und der ganze Screen zur Kontrolle nocheinmal ausgegeben. \ primitivst Editor PRIMED UH 17Nov86 | : !line ( adr count line# -- ) scr @ block swap c/l * + dup c/l bl fill swap cmove update ; : new ( n -- ) l/s 1+ swap ?DO cr I . pad c/l expect span @ 0= IF leave THEN pad span @ I !line cr scr @ list LOOP ; \ PRIMED Demo-Screen Dieser Text entstand durch: "2 LIST 4 NEW" mit anschliessender Eingabe dieses Textes Die Kopfzeile (Zeile 0) wurde spaeter durch Verlassen von new durch Eingabe einer leeren Zeile (nur RETURN) und Neustart mit "0 NEW" erzeugt. Ulrich Hoffmann

File diff suppressed because one or more lines are too long

123
8080/AmstradCPC/READ.ME Normal file
View File

@ -0,0 +1,123 @@
[nderungen im CP/M-volksFORTH von Version 3.80 zu Version 3.80a UH 04M{r88
=============================================================================
Die Unvertr{glichkeit des urspr}nglichen CP/M-volksFORTHs mit CP/M+ und die
damit verbundene Vielzahl von unterschiedlichen Versionen hat eine allgmeine
]berarbeitung des CP/M-volksFORTHs notwendig gemacht.
Bei dieser Gelegenheit wurden gleich einige Fehler beseitigt und einige
neue Funktionen eingef}hrt.
1. [nderungen im Kern (SOURCE.SCR)
- Die Terminal-Ein- und Ausgabe wurde auf ein Mindestma~ begrenzt,
soda~ auch unmittelbar mit dem Kern gearbeitet werden kann.
Es gibt keinen Zeileneditor f}r die Eingabezeile mehr, dieser wurde
zusammen mit der "Terminal:" Funktion in das File XINOUT.SCR ausgelagert.
- Der Kern enth{lt kein Fileinterface mehr, sondern arbeitet nur in dem
File, da~ bei Aufruf in der Kommandozeile mit angegeben wird (default-
file). Typischerweise wird mit diesem Mechanismus zuerst das
File-Interface geladen.
- Direkter Diskettezugriff wird im Kern nicht mehr unterst}tzt, da er
unter CP/M+ nicht problemlos zu implementieren ist. Au~erdem kann
in Ermangelung eines CP/M+ Systems der Code hier nicht getestet werden.
Diskettenzugriff findet nur noch }ber das BDOS statt.
- Zahlreiche Funktionen des Kerns wurden neu }berarbeitet und in Code
geschrieben, als wichtige neue Funktion des Kerns ist "search hinzu-
gekommen, das eine schnelle Suche mit Ber}cksichtigung der Gro~/Klein-
schreibung erm|glicht.
- Die Funktion CAPITALIZE ist durch die {hnliche Funktion UPPER ersetzt
worden. Das EXIT in NAME verschiebt sich dadurch.
- Der Kern gibt beim Verlassen eine Gr|~enangabe in (256 Byte)-Seiten aus.
Diese Angabe kann direkt benutzt werden, um mit dem CP/M SAVE Kommando
das System auf Diskette zu schreiben. (Forth: SAVE nicht vergessen! )
- SAVE-BUFFERS ist um ein defered Wort SAVE-DOS-BUFFERS erweitert worden.
Damit sollte der l{stige CP/M+ Fehler ausgeschaltet sein.
- Das defered Wort POSTLUDE regelt die letzte Handlung des Systems vor dem
CP/M Warmstart (Cursor anschalten, Bildschirm l|schen oder Systemgr|~e
ausgeben...)
- Die Kommandozeile des Aufrufs wird in den TIB kopiert und kann dort
interpretiert werden. Das \ffnen des default-Files l|scht allerdings den
TIB wieder, soda~ diese Funktion erst ausgenutzt werden kann, wenn das
Fileinterface geladen ist. (DRVINIT |ffnet nicht mehr das default-File.)
- Die Interpret-Loop wurde }berarbeitet und um das Wort PROMPT erweitert.
Das Sonderwort >INTERPRET ist weggefallen. Seine Funktion uebernimmt
jetzt das (normale) defered Wort PARSER.
- Die Kontrollstruktur-Anweisungen (IF, WHILE ... ) sind jetzt auch inter-
aktiv verwendbar.
- Diverse kleinere [nderungen haben stattgefunden.
2. [nderungen im Editor (EDITOR.SCR, STRING.SCR)
- Das Markieren der Screens wurde korrigiert und geschieht jetzt auch
beim Suchen/Ersetzen und bei showload richtig.
- VIEW wurde ge{ndert und sucht nun nach dem in Blanks eingerahmten Wort.
- Es wird nun zus{tzlich das Associative File angezeigt.
- Beim Suchen/Ersetzen wird die Screennummer hochgez{hlt, um eine Kontrolle
}ber das Suchen zu geben.
- Die Textsuche ist nun schon im Kern definiert, die elementaren String-
funktionen sind mit in das EDITOR.SCR genommen worden. STRING.SCR ist
daher entfallen.
3. [nderungen im Multi-Tasker (TASKER.SCR)
- Das Wort TASK wurde ge{ndert: Die Konstante ist nun vor der Task
definiert. Man kann also nun mit FORGET <taskname> tats{chlich die Task
vergessen.
- Der PAUSE/WAKE/STOP-Mechanismus wurde ge{ndert. In der benutzung ergibt
sich daraus keine [nderung.
4. [nderungen im Fileinterface (FILEINT.SCR)
- Das Fileinterface wurde }berarbeitet und einige Fehler beseitigt.
Die Namen zahlreicher Worte haben sich ge{ndert, sind dadurch aber
systematischer geworden. Die Funktionen sind im Wesentlichen gleich
geblieben.
5. Terminal-Installation (Zusatz zu Anpassung von volksFORTH an den Computer)
- Da der Kern kein Fileinterface mehr enth{lt, mu~ dies noch vor
dem Primitivst-Editor geladen werden. Es ergibt sich also die Kommando-
sequenz:
A> kernel fileint.scr
1 load
use primed.scr 1 load
use terminal.scr
6. Erstellen eines Standard-Systems
- Mit folgender Kommandosequenz wird aus KERNEL.COM das File
VOLKS4TH.COM gemacht:
A> kernel fileint.scr
1 load
include startup.scr
7. Neue Files auf der Diskette
- READ.ME dieses File
- XINOUT.SCR Terminalfunktionen und Zeileneditor f}r Eingabe
- COPY.SCR Die Funktionen COPY und CONVEY (fr}her im Kern).
- STRING.SCR Entf{llt, da in EDITOR.SCR und SOURCE.SCR integriert.


View File

@ -0,0 +1 @@
\\ Relocate System 11Nov86 Dieses File enthaelt das Utility-Wort BUFFERS. Mit ihm ist es moeglich die Zahl der Disk-Buffers festzulegen, die volksFORTH benutzt. Voreingestellt sind 4 Buffer. Benutzung: nn BUFFERS \ Relocate a system 16Jul86 | : relocate-tasks ( mainup -- ) up@ dup BEGIN 2+ under @ 2dup - WHILE rot drop REPEAT 2drop ! ; | : relocate ( stacklen rstacklen -- ) 2dup + b/buf + 2+ limit origin - u> abort" kills all buffers" over pad $100 + origin - u< abort" cuts the dictionary" dup udp @ $40 + u< abort" a ticket to the moon with no return ..." flush empty over + origin + origin $0A + ! \ r0 origin + dup relocate-tasks \ multitasking link 6 - origin 8 + ! \ s0 cold ; --> \ bytes.more buffers 29Jun86 | : bytes.more ( n+- -- ) up@ origin - + r0 @ up@ - relocate ; : buffers ( +n -- ) b/buf * 4+ limit r0 @ - swap - bytes.more ;

View File

@ -0,0 +1 @@
\\ savesystem 11Nov86 Dieses File enthaelt das Utility-Wort SAVESYSTEM. Mit ihm kann man das gesamte System als File auf Disk schreiben. Achtung: Es wird SAVE ausgefuehrt, daher ist nach SAVESYSTEM der Heap geloescht! Benutzung: SAVESYSTEM <filename> \ savsystem 05Nov86 : savesystem \ filename save $100 here over - savefile ; \\ Einfaches savesystem 18Aug86 | : message ( -- ) base push decimal cr ." ready for SAVE " here 1- $100 / u. ." VOLKS4TH.COM" cr ; : savesystem ( -- ) save message bye ;

1
8080/AmstradCPC/SEE.SCR Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
\\ Simple Files 11Nov86 Wenn volksFORTH im Direktzugriff Disketten bearbeitet, ist es trotzdem wuenschenswert eine Art File-Struktur zu besitzen. Dieses File enthaelt eine einfache Implementation eines Filesystems. Der/die Programmierer/in muss selbst die Direktory auf dem laufenden halten: in ihr sind die Start-Bloecke des entsprechenden Diskettenteils gespeichert. Sogar eine Hierarchie von Direktories laesst sich so relisieren. Vorgestellt wurde dieses FileSystem von Georg Rehfeld und auch von ihm fuer volksFORTH implementiert (ultraFORTH auf dem C64). \ simple files 12feb86 \needs search .( search missing) \\ | Variable (dir : dir (dir @ ; : root 0 (dir ! ; root | : read" ( -- n) Ascii " word count dup >r dir block b/blk search 0= abort" not found" r> + >in push >in ! bl dir block b/blk (word number drop ; : load" read" dir + load ; : dir" read" (dir +! ; : list" read" dir + list ; \ 1 +load \ Only if file" is needed \ simple files 01feb86 | : snap ( n0 -- n1) $20 / 3 max $20 * ; : file" ( n --) Ascii " word count 2dup dir block b/blk search IF + nip ELSE drop dir block b/blk -trailing nip snap $20 + dup b/blk 1- > abort" directory full" 2dup + >r dir block + swap cmove r> THEN snap $18 + >r dir - extend under dabs <# # # # # base @ $0A = IF Ascii & ELSE Ascii $ THEN hold rot 0< IF Ascii - ELSE bl THEN hold #> r> dir block + swap cmove update ; \ dir load" 11feb86 \needs search .( search missing) \\ 0 Constant dir : load" ( -- ) Ascii " word count dup >r dir block b/blk search 0= abort" not found" r> + >in @ blk @ rot >in ! dir blk ! bl word number drop -rot blk ! >in ! load ;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
\\ Startup: Load Standard System UH 11Nov86 Dieses File enthaelt Befehle, die aus dem File KERNEL.COM ein vollstaendiges volksFORTH machen, das mit SAVESYSTEM als File (z.B. VOLKS4th.COM) auf Disk geschrieben werden kann. \ System LOAD-Screen fuer CP/M VolksForth ( 10.02.89/KK ) include ass8080.scr include xinout.scr \ Erweiterte Ein- u. Ausgabe include terminal.scr save \ Terminal include copy.scr cr .( copy und convey geladen.) cr include savesys.scr cr .( Savesystem geladen.) cr include editor.scr cr .( Editor geladen.) cr include tools.scr cr .( Tools geladen.) cr include see.scr cr .( Decompiler geladen.) cr include tasker.scr cr .( Multitasker geladen.) cr include printer.scr cr .( Printer Interface geladen.) cr include relocate.scr cr .( Relocating geladen. ) cr .( May the volksFORTH be with you ...) cr decimal caps on editor.scr scr off r# off ( savesystem volks4th.com ) \ UH 22Oct86

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
\\ Terminal-Anpassung 11Nov86 In diesem File wird volksFORTH an das benutzte Terminal angepasst. Ueber folgende Faehigkeiten muss das Terminal verfuegen, damit alle Moeglichkeiten von volksFORTH ausgenutzt werden koennen: curon, curoff \ Ein- bzw. Ausschalten des Cursors curleft, currite \ Cursor nach links bzw. rechts bewegen rvson, rvsoff \ Ein- bzw. Ausschalten der Inversedarstellungdark \ Loeschen des Bildschirms locate \ Positionieren des Cursors auf eine \ bestimmte Position auf dem Bildschirm \ Schneider CPC464-Terminal Anpassung UH 18Mar87 | : CPCcuron ( -- ) 3 con! ; | : CPCcuroff ( -- ) 2 con! ; | Variable reverse reverse off | : CPCrvson ( -- ) reverse @ ?exit reverse on $18 con! ; | : CPCrvsoff ( -- ) reverse @ 0= ?exit reverse off $18 con! ; | : CPCdark ( -- ) $0C con! ; | : CPClocate ( row col -- ) $1F con! 1+ con! &24 min 1+ con! ; Terminal: schneider CPCcuron CPCcuroff CPCrvson CPCrvsoff CPCdark CPClocate ; schneider page .( CPC-464 Terminal installiert. ) cr cr

View File

@ -0,0 +1 @@
\\ Times Often: interactive loops 11Nov86 Dieses File enthaelt die Definitionen der beiden Utility-Worte TIMES, OFTEN, die interaktiv benutzt werden koennen, was normalerweise mit BEGIN WHILE ... nicht moeglich ist. Benutzung: nur interaktiv! a b ... nn times \ Wiederhole die Befehlsfolge "a b ..." nn mal, \ oder bis eine Taste gedrueckt wird, oder \ bis ein Fehler auftritt, a b ... often \ Wiederhole die Befehlsfolge "a b ..." \ so oft, bis eine Taste gedrueckt wird, oder \ bis ein Fehler auftritt. \ Times, Often 02feb86 also Forth definitions : often stop? ?exit >in off ; | Variable #times #times off : times ( n --) ?dup IF #times @ 2+ u< stop? or IF #times off exit THEN 1 #times +! ELSE stop? ?exit THEN >in off ; toss definitions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
\\ Startup: Load Standard System UH 11Nov86 Dieses File enthaelt Befehle, die aus dem File KERNEL.COM ein vollstaendiges volksFORTH machen, das mit SAVESYSTEM als File (z.B. VOLKS4th.COM) auf Disk geschrieben werden kann. \ System LOAD-Screen fuer CP/M VolksForth UH 27Nov87include ass8080.fb include xinout.fb \ extended I/O include terminal.fb save \ Terminal include copy.fb cr .( copy and convey loaded) cr include savesys.fb cr .( Savesystem loaded) cr include editor.fb cr .( Editor loaded) cr include tools.fb cr .( Tools loaded) cr include see.fb cr .( Decompiler loaded) cr include tasker.fb cr .( Multitasker loaded) cr include printer.fb cr .( Printer Interface loaded) cr include relocate.fb cr .( Relocating loaded) cr .( May the volksFORTH be with you ...) cr decimal caps on scr off r# off savesystem volks4th.com UH 22Oct86
\\ Startup: Load Standard System UH 11Nov86 Dieses File enthaelt Befehle, die aus dem File KERNEL.COM ein vollstaendiges volksFORTH machen, das mit SAVESYSTEM als File (z.B. VOLKS4th.COM) auf Disk geschrieben werden kann. \ System LOAD-Screen fuer CP/M VolksForth UH 27Nov87include ass8080.fb include xinout.fb \ extended I/O \ include terminal.fb save \ Terminal include copy.fb cr .( copy and convey loaded) cr include savesys.fb cr .( Savesystem loaded) cr include editor.fb cr .( Editor loaded) cr include tools.fb cr .( Tools loaded) cr \ include see.fb cr .( Decompiler loaded) cr \ include tasker.fb cr .( Multitasker loaded) cr \ include printer.fb cr .( Printer Interface loaded) cr include relocate.fb cr .( Relocating loaded) cr .( May the volksFORTH be with you ...) cr decimal caps on scr off r# off savesystem volks4th.com UH 22Oct86

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,25 @@
TARGET = forth.com
BASE = ../..
BLKPACK = $(BASE)/tools/blkpack
BOOTPRG = ./bootdisk
.PHONY: all
all: $(TARGET)
%.fb: %.fth $(BLKPACK)
$(BLKPACK) < $< > $@
$(TARGET): kernel.fb meta.fb
emu2 $(BASE)/8086/pc-baremetal/volks4th.com "include kernel.fb bye"
.PHONY: floppy
floppy:
$(BOOTPRG)/mkimg144 -bs $(BOOTPRG)/flp144.bin -o floppy.img -us $(TARGET)
.PHONY: qemu
qemu:
qemu-system-i386 -curses -drive file=floppy.img,if=floppy,format=raw -monitor telnet:127.0.0.1:1234,server,nowait
.PHONY: clean
clean:
rm -f $(TARGET) meta.com *.fb floppy.img

View File

@ -0,0 +1,14 @@
TARGET = mkimg144 flp144.bin
.PHONY: all
all: $(TARGET)
flp144.bin: flp144.asm
nasm $< -f bin -o $@
mkimg144: mkimg144.c
$(CC) -o $@ $<
.PHONY: clean
clean:
rm -f $(TARGET)

View File

@ -0,0 +1,526 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; "BootProg" Loader v 1.5 by Alexey Frunze (c) 2000-2015 ;;
;; 2-clause BSD license. ;;
;; ;;
;; ;;
;; This is a version of boot12.asm fully ready for a 1.44MB 3"5 floppy. ;;
;; ;;
;; ;;
;; How to Compile: ;;
;; ~~~~~~~~~~~~~~~ ;;
;; nasm flp144.asm -f bin -o flp144.bin ;;
;; ;;
;; ;;
;; Features: ;;
;; ~~~~~~~~~ ;;
;; - FAT12 supported ;;
;; ;;
;; - Loads a 16-bit executable file in the MS-DOS .COM or .EXE format ;;
;; from the root directory of a disk and transfers control to it ;;
;; (the "ProgramName" variable holds the name of the file to be loaded) ;;
;; ;;
;; - Prints an error if the file isn't found or couldn't be read ;;
;; (the "RE" message stands for "Read Error", ;;
;; the "NF" message stands for "file Not Found") ;;
;; and waits for a key to be pressed, then executes the Int 19h ;;
;; instruction and lets the BIOS continue bootstrap. ;;
;; ;;
;; ;;
;; Known Limitations: ;;
;; ~~~~~~~~~~~~~~~~~~ ;;
;; - Works only on the 1st MBR partition which must be a PRI DOS partition ;;
;; with FAT12 (File System ID: 1) ;;
;; ;;
;; ;;
;; Known Bugs: ;;
;; ~~~~~~~~~~~ ;;
;; - All bugs are fixed as far as I know. The boot sector has been tested ;;
;; on the following types of diskettes: ;;
;; - 360KB 5"25 ;;
;; - 1.2MB 5"25 ;;
;; - 1.44MB 3"5 ;;
;; ;;
;; ;;
;; Memory Layout: ;;
;; ~~~~~~~~~~~~~~ ;;
;; The diagram below shows the typical memory layout. The actual location ;;
;; of the boot sector and its stack may be lower than A0000H if the BIOS ;;
;; reserves memory for its Extended BIOS Data Area just below A0000H and ;;
;; reports less than 640 KB of RAM via its Int 12H function. ;;
;; ;;
;; physical address ;;
;; +------------------------+ 00000H ;;
;; | Interrupt Vector Table | ;;
;; +------------------------+ 00400H ;;
;; | BIOS Data Area | ;;
;; +------------------------+ 00500H ;;
;; | PrtScr Status / Unused | ;;
;; +------------------------+ 00600H ;;
;; | Loaded Image | ;;
;; +------------------------+ nnnnnH ;;
;; | Available Memory | ;;
;; +------------------------+ A0000H - 512 - 2KB ;;
;; | 2KB Boot Stack | ;;
;; +------------------------+ A0000H - 512 ;;
;; | Boot Sector | ;;
;; +------------------------+ A0000H ;;
;; | Video RAM | ;;
;; ;;
;; ;;
;; Boot Image Startup (register values): ;;
;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;;
;; dl = BIOS boot drive number (e.g. 0, 80H) ;;
;; cs:ip = program entry point ;;
;; ss:sp = program stack (don't confuse with boot sector's stack) ;;
;; COM program defaults: cs = ds = es = ss = 50h, sp = 0, ip = 100h ;;
;; EXE program defaults: ds = es = 50h, other stuff depends on EXE header ;;
;; Magic numbers: ;;
;; si = 16381 (prime number 2**14-3) ;;
;; di = 32749 (prime number 2**15-19) ;;
;; bp = 65521 (prime number 2**16-15) ;;
;; The magic numbers let the program know whether it has been loaded by ;;
;; this boot sector or by MS-DOS, which may be handy for universal, bare- ;;
;; metal and MS-DOS programs. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 16]
;;? equ 0
ImageLoadSeg equ 60h ; <=07Fh because of "push byte ImageLoadSeg" instructions
[SECTION .text]
[ORG 0]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Boot sector starts here ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
jmp short start ; MS-DOS/Windows checks for this jump
nop
bsOemName DB "BootProg" ; 0x03
;;;;;;;;;;;;;;;;;;;;;
;; BPB starts here ;;
;;;;;;;;;;;;;;;;;;;;;
bpbBytesPerSector DW 512 ; 0x0B
bpbSectorsPerCluster DB 1 ; 0x0D
bpbReservedSectors DW 1 ; 0x0E
bpbNumberOfFATs DB 2 ; 0x10
bpbRootEntries DW 224 ; 0x11
bpbTotalSectors DW 2880 ; 0x13
bpbMedia DB 0F0h ; 0x15
bpbSectorsPerFAT DW 9 ; 0x16
bpbSectorsPerTrack DW 18 ; 0x18
bpbHeadsPerCylinder DW 2 ; 0x1A
bpbHiddenSectors DD 0 ; 0x1C
bpbTotalSectorsBig DD 0 ; 0x20
;;;;;;;;;;;;;;;;;;;
;; BPB ends here ;;
;;;;;;;;;;;;;;;;;;;
bsDriveNumber DB 0 ; 0x24
bsUnused DB 0 ; 0x25
bsExtBootSignature DB 29H ; 0x26
bsSerialNumber DD 11223344h ; 0x27
bsVolumeLabel DB "NO NAME " ; 0x2B
bsFileSystem DB "FAT12 " ; 0x36
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Boot sector code starts here ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
start:
cld
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; How much RAM is there? ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
int 12h ; get conventional memory size (in KBs)
shl ax, 6 ; and convert it to 16-byte paragraphs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Reserve memory for the boot sector and its stack ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
sub ax, 512 / 16 ; reserve 512 bytes for the boot sector code
mov es, ax ; es:0 -> top - 512
sub ax, 2048 / 16 ; reserve 2048 bytes for the stack
mov ss, ax ; ss:0 -> top - 512 - 2048
mov sp, 2048 ; 2048 bytes for the stack
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Copy ourselves to top of memory ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov cx, 256
mov si, 7C00h
xor di, di
mov ds, di
rep movsw
;;;;;;;;;;;;;;;;;;;;;;
;; Jump to the copy ;;
;;;;;;;;;;;;;;;;;;;;;;
push es
push byte main
retf
main:
push cs
pop ds
mov [bsDriveNumber], dl ; store BIOS boot drive number
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Reserve memory for the FAT12 image (6KB max) ;;
;; and load it in its entirety ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ax, [bpbBytesPerSector]
shr ax, 4 ; ax = sector size in paragraphs
mov cx, [bpbSectorsPerFAT] ; cx = FAT size in sectors
mul cx ; ax = FAT size in paragraphs
mov di, ss
sub di, ax
mov es, di
xor bx, bx ; es:bx -> buffer for the FAT
mov ax, [bpbHiddenSectors]
mov dx, [bpbHiddenSectors+2]
add ax, [bpbReservedSectors]
adc dx, bx ; dx:ax = LBA
call ReadSector
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Reserve memory for the root directory ;;
;; and load it in its entirety ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov bx, ax
mov di, dx ; save LBA to di:bx
mov ax, 32
mov si, [bpbRootEntries]
mul si
div word [bpbBytesPerSector]
mov cx, ax ; cx = root directory size in sectors
mov al, [bpbNumberOfFATs]
cbw
mul word [bpbSectorsPerFAT]
add ax, bx
adc dx, di ; dx:ax = LBA
push es ; push FAT segment (2nd parameter)
push byte ImageLoadSeg
pop es
xor bx, bx ; es:bx -> buffer for root directory
call ReadSector
add ax, cx
adc dx, bx ; adjust LBA for cluster data
push dx
push ax ; push LBA for data (1st parameter)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Look for the COM/EXE file to load and run ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov di, bx ; es:di -> root entries array
mov dx, si ; dx = number of root entries
mov si, ProgramName ; ds:si -> program name
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Looks for a file/dir by its name ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Input: DS:SI -> file name (11 chars) ;;
;; ES:DI -> root directory array ;;
;; DX = number of root entries ;;
;; Output: SI = cluster number ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FindName:
mov cx, 11
FindNameCycle:
cmp byte [es:di], ch
je FindNameFailed ; end of root directory
pusha
repe cmpsb
popa
je FindNameFound
add di, 32
dec dx
jnz FindNameCycle ; next root entry
FindNameFailed:
jmp ErrFind
FindNameFound:
mov si, [es:di+1Ah] ; si = cluster no.
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Load the entire file ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;
ReadNextCluster:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Reads a FAT12 cluster ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Inout: ES:BX -> buffer ;;
;; SI = cluster no ;;
;; Output: SI = next cluster ;;
;; ES:BX -> next addr ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ReadCluster:
mov bp, sp
lea ax, [si-2]
xor ch, ch
mov cl, [bpbSectorsPerCluster]
; cx = sector count
mul cx
add ax, [bp]
adc dx, [bp+1*2]
; dx:ax = LBA
call ReadSector
mov ax, [bpbBytesPerSector]
shr ax, 4 ; ax = paragraphs per sector
mul cx ; ax = paragraphs read
mov cx, es
add cx, ax
mov es, cx ; es:bx updated
mov ax, 3
mul si
shr ax, 1
xchg ax, si ; si = cluster * 3 / 2
push ds
mov ds, [bp+2*2] ; ds = FAT segment
mov si, [si] ; si = next cluster
pop ds
jnc ReadClusterEven
shr si, 4
ReadClusterEven:
and si, 0FFFh ; mask cluster value
ReadClusterDone:
cmp si, 0FF8h
jc ReadNextCluster ; if not End Of File
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Type detection, .COM or .EXE? ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
push byte ImageLoadSeg
pop ds
mov ax, ds ; ax=ds=seg the file is loaded to
cmp word [0], 5A4Dh ; "MZ" signature?
je RelocateEXE ; yes, it's an EXE program
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Setup and run a .COM program ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
sub ax, 10h ; "org 100h" stuff :)
mov es, ax
mov ds, ax
mov ss, ax
xor sp, sp
push es
push word 100h
jmp short Run
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Relocate, setup and run a .EXE program ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RelocateEXE:
add ax, [08h] ; ax = image base
mov cx, [06h] ; cx = reloc items
mov bx, [18h] ; bx = reloc table pointer
jcxz RelocationDone
ReloCycle:
mov di, [bx] ; di = item ofs
mov dx, [bx+2] ; dx = item seg (rel)
add dx, ax ; dx = item seg (abs)
push ds
mov ds, dx ; ds = dx
add [di], ax ; fixup
pop ds
add bx, 4 ; point to next entry
loop ReloCycle
RelocationDone:
mov bx, ax
add bx, [0Eh]
mov ss, bx ; ss for EXE
mov sp, [10h] ; sp for EXE
add ax, [16h] ; cs
push ax
push word [14h] ; ip
Run:
mov dl, [cs:bsDriveNumber] ; pass the BIOS boot drive
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set the magic numbers so the program knows that it ;;
;; has been loaded by this bootsector and not by MS-DOS ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov si, 16381 ; prime number 2**14-3
mov di, 32749 ; prime number 2**15-19
mov bp, 65521 ; prime number 2**16-15
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; All done, transfer control to the program now ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
retf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Reads a sector using BIOS Int 13h fn 2 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Input: DX:AX = LBA ;;
;; CX = sector count ;;
;; ES:BX -> buffer address ;;
;; Output: CF = 1 if error ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ReadSector:
pusha
ReadSectorNext:
mov di, 5 ; attempts to read
ReadSectorRetry:
pusha
div word [bpbSectorsPerTrack]
; ax = LBA / SPT
; dx = LBA % SPT = sector - 1
mov cx, dx
inc cx
; cx = sector no.
xor dx, dx
div word [bpbHeadsPerCylinder]
; ax = (LBA / SPT) / HPC = cylinder
; dx = (LBA / SPT) % HPC = head
mov ch, al
; ch = LSB 0...7 of cylinder no.
shl ah, 6
or cl, ah
; cl = MSB 8...9 of cylinder no. + sector no.
mov dh, dl
; dh = head no.
mov dl, [bsDriveNumber]
; dl = drive no.
mov ax, 201h
; al = sector count = 1
; ah = 2 = read function no.
int 13h ; read sectors
jnc ReadSectorDone ; CF = 0 if no error
xor ah, ah ; ah = 0 = reset function
int 13h ; reset drive
popa
dec di
jnz ReadSectorRetry ; extra attempt
jmp short ErrRead
ReadSectorDone:
popa
dec cx
jz ReadSectorDone2 ; last sector
add bx, [bpbBytesPerSector] ; adjust offset for next sector
add ax, 1
adc dx, 0 ; adjust LBA for next sector
jmp short ReadSectorNext
ReadSectorDone2:
popa
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Error Messaging Code ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;
ErrRead:
mov si, MsgErrRead
jmp short Error
ErrFind:
mov si, MsgErrFind
Error:
mov ah, 0Eh
mov bx, 7
lodsb
int 10h ; 1st char
lodsb
int 10h ; 2nd char
xor ah, ah
int 16h ; wait for a key...
mov dl, [bsDriveNumber] ; restore BIOS boot drive number
int 19h ; bootstrap
;;;;;;;;;;;;;;;;;;;;;;
;; String constants ;;
;;;;;;;;;;;;;;;;;;;;;;
MsgErrRead db "RE"
MsgErrFind db "NF"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Fill free space with zeroes ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times (512-13-($-$$)) db 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Name of the file to load and run ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ProgramName db "FORTH COM" ; name and extension each must be
; padded with spaces (11 bytes total)
;;;;;;;;;;;;;;;;;;;;;;;;;;
;; End of the sector ID ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;
dw 0AA55h ; BIOS checks for this ID

Binary file not shown.

View File

@ -0,0 +1,26 @@
Copyright (c) 2000-2015, Alexey Frunze
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.

Binary file not shown.

View File

@ -0,0 +1,612 @@
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
typedef unsigned char uchar, uint8;
typedef unsigned short uint16;
#ifndef __SMALLER_C__
#if UINT_MAX >= 0xFFFFFFFF
typedef unsigned uint32;
#else
typedef unsigned long uint32;
#endif
#else
typedef unsigned long uint32;
#endif
typedef unsigned uint;
typedef unsigned long ulong;
#ifndef __SMALLER_C__
#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]
C_ASSERT(CHAR_BIT == 8);
C_ASSERT(sizeof(uint16) == 2);
C_ASSERT(sizeof(uint32) == 4);
#endif
#pragma pack (push, 1)
typedef struct tFATBPB1
{
uint16 BytesPerSector;
uint8 SectorsPerCluster;
uint16 ReservedSectorsCount;
uint8 NumberOfFATs;
uint16 RootEntriesCount;
uint16 TotalSectorsCount16;
uint8 MediaType;
uint16 SectorsPerFAT1x;
uint16 SectorsPerTrack;
uint16 HeadsPerCylinder;
uint32 HiddenSectorsCount;
uint32 TotalSectorsCount32;
} tFATBPB1;
typedef union tFATBPB2
{
struct
{
uint8 DriveNumber;
uint8 reserved1;
uint8 ExtendedBootSignature;
uint32 VolumeSerialNumber;
char VolumeLabel[11];
char FileSystemName[8];
uchar aBootCode1x[0x1C];
} FAT1x;
struct
{
uint32 SectorsPerFAT32;
uint16 ExtendedFlags;
uint16 FSVersion;
uint32 RootDirectoryClusterNo;
uint16 FSInfoSectorNo;
uint16 BackupBootSectorNo;
uint8 reserved[12];
uint8 DriveNumber;
uint8 reserved1;
uint8 ExtendedBootSignature;
uint32 VolumeSerialNumber;
char VolumeLabel[11];
char FileSystemName[8];
} FAT32;
} tFATBPB2;
typedef struct tFATBPB
{
tFATBPB1 BPB1;
tFATBPB2 BPB2;
} tFATBPB;
typedef struct tFATBootSector
{
uchar aJump[3];
char OEMName[8];
tFATBPB BPB;
uchar aBootCode32[0x1A4];
uint16 Signature0xAA55;
} tFATBootSector;
typedef enum tFATDirEntryAttribute
{
dea_READ_ONLY = 0x01,
dea_HIDDEN = 0x02,
dea_SYSTEM = 0x04,
dea_VOLUME_ID = 0x08,
dea_DIRECTORY = 0x10,
dea_ARCHIVE = 0x20,
dea_LONG_NAME = dea_READ_ONLY|dea_HIDDEN|dea_SYSTEM|dea_VOLUME_ID
} tFATDirEntryAttribute;
typedef struct tFATDirectoryEntry
{
char Name[8];
char Extension[3];
uint8 Attribute;
uint8 WinNTreserved;
uint8 CreationTimeSecTenths;
uint16 CreationTime2Secs;
uint16 CreationDate;
uint16 LastAccessDate;
uint16 FirstClusterHiWord;
uint16 LastWriteTime;
uint16 LastWriteDate;
uint16 FirstClusterLoWord;
uint32 Size;
} tFATDirectoryEntry;
#define DELETED_DIR_ENTRY_MARKER 0xE5
#pragma pack (pop)
#ifndef __SMALLER_C_32__
C_ASSERT(sizeof(tFATBootSector) == 512);
C_ASSERT(sizeof(tFATDirectoryEntry) == 32);
#endif
#define FBUF_SIZE 1024
char* BootSectName;
char* OutName = "floppy.img";
int UniqueSerial;
FILE* fout;
tFATBootSector BootSector;
uint32 Fat1Lba;
uint32 SectorsPerFat;
uint32 Fats;
uint32 RootDirLba;
uint32 DirEntriesPerSector;
uint32 RootDirEntries;
uint32 RootDirSectors;
uint32 Cluster2Lba;
uint32 SectorsPerCluster;
uint32 ClusterSize;
uint32 DataSectors;
uint32 Clusters;
uint8 FatSector[512];
uint32 Cluster;
tFATDirectoryEntry RootDirSector[512 / sizeof(tFATDirectoryEntry)];
uint32 RootDirEntryIdx;
void error(char* format, ...)
{
#ifndef __SMALLER_C__
va_list vl;
va_start(vl, format);
#else
void* vl = &format + 1;
#endif
if (fout)
fclose(fout);
remove(OutName);
puts("");
vprintf(format, vl);
#ifndef __SMALLER_C__
va_end(vl);
#endif
exit(EXIT_FAILURE);
}
FILE* Fopen(const char* filename, const char* mode)
{
FILE* stream = fopen(filename, mode);
if (!stream)
error("Can't open/create file \"%s\"\n", filename);
return stream;
}
void Fclose(FILE* stream)
{
if (fclose(stream))
error("Can't close a file\n");
}
void Fseek(FILE* stream, long offset, int whence)
{
int r = fseek(stream, offset, whence);
if (r)
error("Can't seek a file\n");
}
void Fread(void* ptr, size_t size, FILE* stream)
{
size_t r = fread(ptr, 1, size, stream);
if (r != size)
error("Can't read a file\n");
}
void Fwrite(const void* ptr, size_t size, FILE* stream)
{
size_t r = fwrite(ptr, 1, size, stream);
if (r != size)
error("Can't write a file\n");
}
void FillWithByte(unsigned char byte, unsigned long size, FILE* stream)
{
static unsigned char buf[FBUF_SIZE];
memset(buf, byte, FBUF_SIZE);
while (size)
{
unsigned long csz = size;
if (csz > FBUF_SIZE)
csz = FBUF_SIZE;
Fwrite(buf, csz, stream);
size -= csz;
}
}
// Determines binary file size portably (when stat()/fstat() aren't available)
long fsize(FILE* binaryStream)
{
long ofs, ofs2;
int result;
if (fseek(binaryStream, 0, SEEK_SET) != 0 ||
fgetc(binaryStream) == EOF)
return 0;
ofs = 1;
while ((result = fseek(binaryStream, ofs, SEEK_SET)) == 0 &&
(result = (fgetc(binaryStream) == EOF)) == 0 &&
ofs <= LONG_MAX / 4 + 1)
ofs *= 2;
// If the last seek failed, back up to the last successfully seekable offset
if (result != 0)
ofs /= 2;
for (ofs2 = ofs / 2; ofs2 != 0; ofs2 /= 2)
if (fseek(binaryStream, ofs + ofs2, SEEK_SET) == 0 &&
fgetc(binaryStream) != EOF)
ofs += ofs2;
// Return -1 for files longer than LONG_MAX
if (ofs == LONG_MAX)
return -1;
return ofs + 1;
}
void FlushFatSector(void)
{
uint32 ofs = (Cluster * 3 / 2) & 511;
uint32 i;
if (ofs == 0 && (Cluster & 1) == 0)
return;
for (i = 0; i < Fats; i++)
{
uint32 ofs = Fat1Lba + i * SectorsPerFat;
ofs += (Cluster * 3 / 2) / 512;
Fseek(fout, ofs * 512, SEEK_SET);
Fwrite(FatSector, sizeof FatSector, fout);
}
memset(FatSector, 0, sizeof FatSector);
}
void ChainCluster(uint32 nextCluster)
{
uint32 ofs = (Cluster * 3 / 2) & 511;
if (Cluster & 1)
FatSector[ofs] |= nextCluster << 4;
else
FatSector[ofs] = nextCluster;
if (ofs == 511)
FlushFatSector();
ofs = (ofs + 1) & 511;
if (Cluster & 1)
FatSector[ofs] = nextCluster >> 4;
else
FatSector[ofs] = (nextCluster >> 8) & 0xF;
if (ofs == 511 && (Cluster & 1))
FlushFatSector();
Cluster++;
}
void FlushRootDirSector(void)
{
uint32 ofs;
if (RootDirEntryIdx % DirEntriesPerSector == 0)
return;
ofs = RootDirLba + RootDirEntryIdx / DirEntriesPerSector;
Fseek(fout, ofs * 512, SEEK_SET);
Fwrite(RootDirSector, sizeof RootDirSector, fout);
}
void AddRootDirEntry(tFATDirectoryEntry* de)
{
RootDirSector[RootDirEntryIdx % DirEntriesPerSector] = *de;
if ((RootDirEntryIdx + 1) % DirEntriesPerSector == 0)
FlushRootDirSector();
RootDirEntryIdx++;
}
void Init(void)
{
if (BootSectName)
{
FILE* fsect = Fopen(BootSectName, "rb");
Fread(&BootSector, sizeof BootSector, fsect);
Fclose(fsect);
}
else
{
memcpy(BootSector.OEMName, "BootProg", 8);
memcpy(BootSector.BPB.BPB2.FAT1x.VolumeLabel, "NO NAME ", 11);
memcpy(BootSector.BPB.BPB2.FAT1x.FileSystemName, "FAT12 ", 8);
BootSector.aJump[0] = 0xEB; // jmp short $+0x3E
BootSector.aJump[1] = 0x3C;
BootSector.aJump[2] = 0x90; // nop
// TBD??? replace the below with code to print an error message like "Not a system/bootable disk"?
BootSector.BPB.BPB2.FAT1x.aBootCode1x[0] = 0xF4; // hlt
BootSector.BPB.BPB2.FAT1x.aBootCode1x[1] = 0xEB; // jmp short $-1
BootSector.BPB.BPB2.FAT1x.aBootCode1x[2] = 0xFD;
}
fout = Fopen(OutName, "wb");
BootSector.BPB.BPB1.BytesPerSector = 512; // note, we're normally assuming 512 bytes per sector everywhere
BootSector.BPB.BPB1.SectorsPerCluster = 1;
BootSector.BPB.BPB1.ReservedSectorsCount = 1; // includes the boot sector
BootSector.BPB.BPB1.NumberOfFATs = 2;
BootSector.BPB.BPB1.RootEntriesCount = 224; // must be a multiple of 16 (16 32-byte entries in 512-byte sector)
BootSector.BPB.BPB1.TotalSectorsCount16 = 2880;
BootSector.BPB.BPB1.MediaType = 0xF0;
BootSector.BPB.BPB1.SectorsPerFAT1x = 9;
BootSector.BPB.BPB1.SectorsPerTrack = 18;
BootSector.BPB.BPB1.HeadsPerCylinder = 2;
BootSector.BPB.BPB1.HiddenSectorsCount = 0;
BootSector.BPB.BPB1.TotalSectorsCount32 = 0;
BootSector.BPB.BPB2.FAT1x.DriveNumber = 0;
BootSector.BPB.BPB2.FAT1x.reserved1 = 0;
BootSector.BPB.BPB2.FAT1x.ExtendedBootSignature = 0x29;
BootSector.BPB.BPB2.FAT1x.VolumeSerialNumber = 0x11223344;
if (UniqueSerial)
BootSector.BPB.BPB2.FAT1x.VolumeSerialNumber = time(NULL);
BootSector.Signature0xAA55 = 0xAA55;
// Write the boot sector
Fwrite(&BootSector, sizeof BootSector, fout);
// Zero out the rest of the image
FillWithByte(0, (BootSector.BPB.BPB1.TotalSectorsCount16 - 1) * 512UL, fout);
// FAT12's first two entries need special initialization
ChainCluster(0xF00 | BootSector.BPB.BPB1.MediaType);
ChainCluster(0xFFF);
// Helper variables
Fat1Lba = BootSector.BPB.BPB1.ReservedSectorsCount;
SectorsPerFat = BootSector.BPB.BPB1.SectorsPerFAT1x;
Fats = BootSector.BPB.BPB1.NumberOfFATs;
RootDirLba = Fat1Lba + SectorsPerFat * Fats;
DirEntriesPerSector = 512 / sizeof(tFATDirectoryEntry);
RootDirEntries = BootSector.BPB.BPB1.RootEntriesCount;
RootDirSectors = (RootDirEntries * sizeof(tFATDirectoryEntry) + 511) / 512;
Cluster2Lba = RootDirLba + RootDirSectors;
SectorsPerCluster = BootSector.BPB.BPB1.SectorsPerCluster;
ClusterSize = SectorsPerCluster * 512;
DataSectors = BootSector.BPB.BPB1.TotalSectorsCount16 -
BootSector.BPB.BPB1.ReservedSectorsCount - SectorsPerFat * Fats - RootDirSectors;
Clusters = DataSectors / SectorsPerCluster;
}
void Done(void)
{
FlushFatSector();
FlushRootDirSector();
Fclose(fout);
}
void NameTo8Dot3Name(const char* in, char out[8 + 3])
{
static const char aInvalid8Dot3NameChars[] = "\"*+,./:;<=>?[\\]|";
int i, j;
int namelen = 0, dots = 0, extlen = 0;
memset(out, ' ', 8 + 3);
if (*in == '\0' || *in == '.')
goto lerr;
for (j = i = 0; in[i]; i++)
{
int c = (unsigned char)in[i];
if (i >= 12) // at most 12 input chars can fit into an 8.3 name
goto lerr;
if (i == 0 && c == 0xE5)
{
// 0xE5 in the first character of the name is a marker for deleted files,
// it needs to be translated to 0x05
c = 0x05;
}
else if (c == '.')
{
if (dots++) // at most one dot allowed
goto lerr;
j = 8; // now writing extension
continue;
}
if (c <= 0x20 || strchr(aInvalid8Dot3NameChars, c) != NULL)
goto lerr;
if (dots)
{
if (++extlen > 3) // at most 3 chars in extension
goto lerr;
}
else
{
if (++namelen > 8) // at most 8 chars in name
goto lerr;
}
if (c >= 'a' && c <= 'z')
c -= 'a' - 'A';
out[j++] = c;
}
// TBD??? error out on the following reserved names: "COM1"-"COM9", "CON", "LPT1"-"LPT9", "NUL", "PRN"?
return;
lerr:
error("Can't convert \"%s\" to an 8.3 DOS name\n", in);
}
void AddFile(char* fname)
{
char* pslash = strrchr(fname, '/');
char* pbackslash = strrchr(fname, '\\');
char* pname;
char name8_3[8 + 3];
FILE* f;
long size;
tFATDirectoryEntry de;
uint32 ofs;
// First, find where the path ends in the file name, if any
// In DOS/Windows paths can contain either '\\' or '/' as a separator between directories,
// choose the right-most