aboutsummaryrefslogtreecommitdiff
path: root/gwfetch.bas
blob: 34ad59351e55a19a2f379dc3d2e7da209583d7d7 (plain)
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

10 REM Customization!
19 REM Maybe you want to name the computer?
20 COMPNAME$ = ENVIRON$("HOSTNAME")
28 REM Uptime can be shown, but is meaningless if the system has a clock
29 REM battery or the time has been set
30 SHOWUPTIME = 1

150 PRINT "GWFetch 0.1 (c) 2024 J.Armstrong - See LICENSE.txt"
151 STARTTIME = TIMER:PRINT
152 PRINT "While we have some time, please consider donating if you"
153 PRINT "like this software.  I suggest:"
154 PRINT
155 IF INT((TIMER - INT(TIMER)) * 10) MOD 2 = 0 THEN GOTO 165
160 PRINT "The Greater Cleveland Food Bank"
161 PRINT "https://greaterclevelandfoodbank.org/"
162 PRINT:PRINT "Working to end hunger today, tomorrow, and for a lifetime"
163 PRINT "in northeast Ohio (U.S.A)."
164 GOTO 190
165 PRINT "Colors+ Youth Center"
166 PRINT "https://colorsplus.org/"
167 PRINT:PRINT "Working to strengthen LGBTQ+ youth and allies by promoting"
168 PRINT "individual and community wellness in northeast Ohio (U.S.A.)."
169 GOTO 190

190 PRINT:PRINT "(The author of GWFetch has no affiliation with the above org."
191 PRINT "Please consider donating if you like this software.)"
200 PRINT:PRINT "Working..."
210 TMPFILE$ = "TEST.TMP"
211 REM The array for our asm routines
212 DIM Q%(400)
219 REM Determine the OS and interpretter
220 CMD$ = "ver":GOSUB 5000:GOSUB 10000
230 REM PRINT "INTERPRETER: ";INTERP$
240 REM IF LEN(VERSION$) > 0 THEN PRINT "VERSION: ";VERSION$

270 REM CPU Detection
280 GOSUB 12000:GOSUB 12300

290 REM Get a machine name
295 DOSBOX = 0
300 GOSUB 14000
302 IF CPUID% <> 7 THEN GOTO 310
303 GOSUB 14500
304 IF DOSBOX = 0 THEN GOTO 310
305 MACHINE$ = DBNAME$ + " (Emulator)"

310 REM DOS version
320 GOSUB 12500
329 REM DR-DOS is a mess, probably to fool MS software
330 IF OEM% = &HEE OR OEM% = &HEF THEN GOSUB 12900
339 REM Get a name for this OS
340 GOSUB 12700

400 REM Memory free
410 IF DOSBOX = 1 THEN GOSUB 14700:GOTO 500
419 REM MS-DOS version 4 and up should have MEM available as a cmd
420 IF OEM$ = "MS-DOS" AND VAL(DVERMAJOR$) >= 4 THEN CMD$ = "mem":GOSUB 5000:GOSUB 15000:GOTO 500 
429 REM FreeDOS mem is the spitting image of MS-DOS mem
430 IF OEM% = &HFD THEN CMD$ = "mem":GOSUB 5000:GOSUB 15000:GOTO 500
440 CMD$ = "chkdsk":GOSUB 5000:GOSUB 11000

500 REM Just produce an MSDOS logo for now
510 DIM LOGO$(25)
520 IF DOSBOX = 1 THEN GOSUB 20050:GOTO 1000
530 IF OEM% = &HEE OR OEM% = &HEF THEN GOSUB 20100:GOTO 1000
540 IF OEM% = &HFD THEN GOSUB 20150:GOTO 1000
550 GOSUB 20000

1000 REM Output time
1001 WHILE TIMER - STARTTIME < 5:WEND
1002 CLS
1003 GOSUB 30100
1004 GOSUB 3000
1010 R = 2:C=41
1020 IF LEN(COMPNAME$) = 0 THEN GOTO 1100
1030 LOCATE R,C:PRINT COMPNAME$;
1035 R = R + 1
1040 LOCATE R,C:PRINT STRING$(LEN(COMPNAME$),"-");
1095 R = R + 1 
1100 REM Normal output
1170 LOCATE R,C:PRINT "OS: ";OEM$;" Version ";DVERMAJOR$;".";DVERMINOR$;
1175 R = R + 1
1180 IF LEN(MACHINE$) = 0 THEN GOTO 1200
1190 LOCATE R,C:PRINT "Host: ";MACHINE$;
1195 R = R + 1
1199 REM Uptime really only makes sense if the time was never set
1200 IF SHOWUPTIME <> 1 THEN GOTO 1220
1201 LOCATE R,C:PRINT "Uptime Today: ";:GOSUB 13000
1205 R = R + 1
1220 LOCATE R,C:PRINT "Shell: ";INTERP$;
1222 IF LEN(VERSION$) > 0 THEN PRINT " ";VERSION$;
1225 R = R + 1
1230 LOCATE R,C:PRINT "CPU: ";CPU$;
1235 R = R + 1
1240 LOCATE R,C:PRINT "Memory: ";MEMFREE$;" / ";MEMTOTAL$;

1997 LOCATE ROWS, 41:PRINT "Press any key...";
1998 WHILE LEN(INKEY$) = 0:WEND
1999 REM SYSTEM
2000 END

3000 REM Logo output
3010 FOR I = 1 TO ROWS
3020 LOCATE I, 1:PRINT LOGO$(I);
3030 NEXT I
3040 RETURN

5000 REM Run and pipe command
5010 REM TMPFILE$ contains the file for capture
5020 REM CMD$ contains the command to run
5030 SHELL CMD$ + " > " + TMPFILE$
5040 RETURN

10000 REM 'ver' output parser
10010 REM TMPFILE$ contains the file to open
10011 REM output: INTERP$ is the interpeter
10012 REM         VERSION$ is the version
10020 OPEN TMPFILE$ FOR INPUT AS #2
10030 IF EOF(2) THEN GOTO 10060
10040 INPUT#2, VERLINE$
10050 IF LEN(VERLINE$) = 0 THEN GOTO 10030 ELSE GOTO 10060
10060 CLOSE#2
10070 REM VERLINE$ now contains the first non-blank version
10080 REM output.  Technically it is just the command 
10090 REM interpreter version.
10100 IDX = INSTR(VERLINE$, "ersion")
10104 REM DR DOS says "Release" 
10105 IF IDX = 0 THEN IDX = INSTR(VERLINE$, "elease")
10110 IF IDX = 0 THEN GOTO 10210
10120 IDX = IDX - 1
10130 INTERP$ = LEFT$(VERLINE$, IDX - 2)
10140 IDX = IDX + 7
10150 VERSION$ = RIGHT$(VERLINE$, LEN(VERLINE$) - IDX)
10160 FOR IDX = 1 TO LEN(VERSION$)
10170   IF MID$(VERSION$, IDX, 1) = " " THEN VERSION$ = LEFT$(VERSION$,IDX-1)
10180 NEXT IDX
10190 IF INTERP$ = "MS-DOS" OR INTERP$="PC-DOS" OR INTERP$="DR DOS" THEN INTERP$ = INTERP$ + " Command"
10200 RETURN
10210 REM If we're here, the word "version" wasn't found, but no worries
10220 SPACED = 0:IDX=0:VERSION$ = ""
10230 FOR IDX = 2 TO LEN(VERLINE$)
10240 C$ = MID$(IDX, 1)
10250 GOSUB 30200
10240 IF SPACED > 0 AND ISNUM = 1 THEN VERSION$ = VERSION$ + C$ ELSE GOTO 10270
10250 IF SPACED = 0 AND C$ = " " THEN SPACED = IDX
10260 NEXT IDX
10270 IF LEN(VERSION$) > 0 AND SPACED > 0 THEN INTERP$ = LEFT$(VERLINE$, SPACED - 1) ELSE INTERP$ = VERLINE$
10280 RETURN


11000 REM 'chkdsk' output parser
11010 REM TMPFILE$ contains the file to open
11011 REM output: MEMTOTAL$ is the total memory in bytes
11012 REM         MEMFREE$ is the available memory in bytes
11020 MEMTOTAL$ = "":MEMFREE$ = ""
11030 OPEN TMPFILE$ FOR INPUT AS #2
11040 INPUT#2, MEMLINE$
11050 IDX = INSTR(MEMLINE$, "bytes total memory")
11060 IF IDX > 0 THEN MEMTOTAL$ = LEFT$(MEMLINE$, IDX-1):GOTO 11090
11070 IDX = INSTR(MEMLINE$, "bytes free")
11080 IF IDX > 0 THEN MEMFREE$ = LEFT$(MEMLINE$, IDX-1)
11085 IF EOF(2) THEN GOTO 11110
11090 IF LEN(MEMTOTAL$) = 0 OR LEN(MEMFREE$) = 0 THEN GOTO 11040 ELSE GOTO 11110
11100 RESUME 11110
11110 CLOSE#2
11119 REM Convert to kB since chkdsk is not going to return >1MB in most cases
11120 MEMNUM = VAL(MEMTOTAL$):MEMNUM = INT(MEMNUM/1024) 
11130 TOTRIM$ = STR$(MEMNUM):GOSUB 30000:MEMTOTAL$ = TOTRIM$+"kB"
11140 MEMNUM = VAL(MEMFREE$):MEMNUM = INT(MEMNUM/1024)
11150 TOTRIM$ = STR$(MEMNUM):GOSUB 30000:MEMFREE$ = TOTRIM$+"kB"
11160 RETURN

12000 REM CPU Identity
12001 CPUQ = 0:CPUID% = 0
12005 REM DEF SEG=VARSEG(Q%(1))
12007 BWOFFSET = -1 : LASTBYTE = 0 : JC = 0
12010 FOR I = 1 TO 102
12020 READ JC:POKE I+VARPTR(Q%(1)), JC
12022 REM Detect where we need to insert an offset for bus width detection
12025 IF LASTBYTE=99 AND JC=0 THEN BWOFFSET = I - 1
12029 LASTBYTE = JC
12030 NEXT I

12039 REM Need to set the offset for bus width
12040 TGTOFFSET = 0
12045 TGTOFFSET = BWOFFSET + VARPTR(Q%(1)) + 16
12050 POKE BWOFFSET + VARPTR(Q%(1)), (TGTOFFSET MOD 256)
12060 POKE BWOFFSET + VARPTR(Q%(1)) + 1, INT(TGTOFFSET / 256)

12080 CPUQ = VARPTR(Q%(1)) + 1
12090 CALL CPUQ(CPUID%)
12100 REM         ; Results:
12101 REM         ; 0 - 8088
12102 REM         ; 1 - 8086
12103 REM         ; 2 - V20
12104 REM         ; 3 - V30
12105 REM         ; 4 - 80188
12106 REM         ; 5 - 80186
12107 REM         ; 6 - 80286
12108 REM         ; 7 - 80386+
12109 DATA &H55 : REM         push bp
12110 DATA &H8B, &HEC : REM         mov bp, sp ; needs to be &H8B, &HEC
12111 DATA &H9C : REM         pushf
12112 REM         ; push zero onto the stack and pop it into flags.
12113 REM         ; some bits won't change
12114 DATA &H31, &HC0 : REM         xor ax,ax
12115 DATA &H50 : REM         push ax
12116 DATA &H9D : REM         popf
12117 DATA &H9C : REM         pushf
12118 DATA &H58 : REM         pop ax
12119 DATA &H25, &H00, &HF0 : REM         and ax, 0xf000 ;0f000h
12120 DATA &H3D, &H00, &HF0 : REM         cmp ax, 0xf000 ;0f000h
12121 DATA &H74, &H13 : REM         je lessthantwoeightsix
12122 REM         ; marked as 286 (6)
12123 DATA &HB2, &H06 : REM         mov dl, 6
12124 DATA &HB8, &H00, &H70 : REM         mov ax, 0x7000 ;7000h
12125 DATA &H50 : REM         push ax
12126 DATA &H9D : REM         popf
12127 DATA &H9C : REM         pushf
12128 DATA &H58 : REM         pop ax
12129 DATA &H25, &H00, &H70 : REM         and ax, 0x7000 ;7000h
12130 DATA &H74, &H3A : REM         jz alldone
12131 REM         ; mark as 386 (7)
12132 DATA &HFE, &HC2 : REM         inc dl
12133 DATA &HE9, &H35, &H00 : REM         jmp alldone
12134 REM lessthantwoeightsix:
12135 REM         ; Mark as 80188 (4)
12136 DATA &HB2, &H04 : REM         mov dl, 4
12137 DATA &HB0, &HFF : REM         mov al, 0xff ;0ffh
12138 DATA &HB1, &H21 : REM         mov cl, 0x21 ;21h
12139 DATA &HD2, &HE8 : REM         shr al, cl
12140 REM         ; if the shift leaves zero, it's a 8088 class cpu, else 8018
12141 DATA &H75, &H14 : REM         jnz buswidth
12142 REM         ; Lets see if we have a V2
12143 DATA &HB2, &H02 : REM         mov dl, 2
12144 DATA &HFB : REM         sti
12145 DATA &H56 : REM         push si
12146 DATA &HBE, &H00, &H00 : REM         mov si, 0
12147 DATA &HB9, &HFF, &HFF : REM         mov cx, 0xffff ;0ffffh
12148 REM         ;rep lods [BYTE PTR es:si]
12149 REM         ; INLINE assembler doesn't understand above - must be replaced after...
12150 REM         ; because we need a lods with ES as a parameter
12151 REM         ; so drop a 3-byte nonsense call to be replaced
12152 DATA &HF3, &H26, &HAC : REM         mov cx, 0x0f99 ; replace with &HF3, &H26, &HAC
12153 DATA &H5E : REM         pop si
12154 DATA &H09, &HC9 : REM         or cx, cx
12155 DATA &H74, &H02 : REM         jz buswidth
12156 REM         ; mark as a 808
12157 DATA &HB2, &H00 : REM         mov dl, 0
12158 REM buswidth:
12159 REM         ; we are destroying es now, must be restored later
12160 DATA &H0E : REM         push cs
12161 DATA &H07 : REM         pop es
12162 DATA &HFD : REM         std
12163 REM         ; The 99 _must_ be replaced with the true offset of qqend
12164 DATA &HBF, &H63, &H00 : REM         mov di, 99 ; OFFSET qqend ;99
12165 REM         ;set up al and cx: 0xfb is the 'sti' instruction
12166 DATA &HB0, &HFB : REM         mov al, 0xfb
12167 DATA &HB9, &H03, &H00 : REM         mov cx, 3
12168 REM         ; disable interrupts
12169 DATA &HFA : REM         cli
12170 DATA &HF2, &HAA : REM         rep stosb
12171 DATA &HFC : REM         cld
12172 DATA &H90 : REM         nop
12173 DATA &H90 : REM         nop
12174 DATA &H90 : REM         nop
12175 DATA &H42 : REM         inc dx
12176 DATA &H90 : REM         nop
12177 REM qqend:
12178 DATA &HFB : REM         sti
12179 REM         ; ES must be restored
12180 DATA &H1E : REM         push ds
12181 DATA &H07 : REM         pop es
12182 REM alldone:
12183 REM         ; store in ax
12184 DATA &H30, &HF6 : REM         xor dh, dh
12185 DATA &H8B, &H7E, &H06 : REM         mov di, [bp]+6
12186 DATA &H89, &H15 : REM         mov [di], dx
12187 DATA &H9D : REM         popf
12188 DATA &H5D : REM         pop bp
12189 DATA &HCA, &H02, &H00
12190 REM Total Bytes in Data: 102

12200 RETURN

12300 REM Interprets CPUID% and returns a CPU name
12310 DIM CPUNAME$(7)
12320 CPUNAME$(0) = "8088" : CPUNAME$(1) = "8086"
12330 CPUNAME$(2) = "V20" : CPUNAME$(3) = "V30"
12340 CPUNAME$(4) = "80188" : CPUNAME$(5) = "80186"
12350 CPUNAME$(6) = "80286"
12360 CPUNAME$(7) = "80386"
12370 IF CPUID% = 2 OR CPUID% = 3 THEN MFG$ = "NEC" ELSE MFG$ = "Intel"
12380 CPU$ = MFG$+" "+CPUNAME$(CPUID%)
12390 RETURN

12500 REM DOS Version
12502 DVER%=0:OEM%=&HFF:DOSVER=0
12510 FOR I = 1 TO 77
12520 READ JC:POKE I+VARPTR(Q%(1)), JC
12530 NEXT I
12540 DOSVER = 1 + VARPTR(Q%(1))
12550 CALL DOSVER(DVER%, OEM%)
12570 TOTRIM$ = STR$(DVER% MOD 256):GOSUB 30000:DVERMAJOR$ = TOTRIM$
12580 TOTRIM$ = STR$(INT(DVER% / 256)):GOSUB 30000:DVERMINOR$ = TOTRIM$
12600 DATA &H55 : REM         push bp
12601 DATA &H8B, &HEC : REM         mov bp, sp  ; needs to be &H8B, &HEC
12602 DATA &H9C : REM         pushf
12603 DATA &HB0, &H00 : REM         mov al, 0
12604 DATA &HB4, &H30 : REM         mov ah, 0x30
12605 DATA &HCD, &H21 : REM         int 0x21
12606 REM         ; Push actual version info onto stack
12607 DATA &H50 : REM         push ax
12608 DATA &H80, &HFF, &H00 : REM         cmp bh, 0x00
12609 DATA &H75, &H0D : REM         jne notdrdos
12610 REM         ; Check if it is DR_DOS lying about being PC_DOS
12611 DATA &HB8, &H52, &H44 : REM         mov ax, 0x4452
12612 DATA &HCD, &H21 : REM         int 0x21
12613 DATA &H72, &H06 : REM         jc notdrdos
12614 REM         ; Mark drdos as 0xee
12615 DATA &HB7, &HEE : REM         mov bh, 0xee
12616 REM         ; clear the stack in a meaningless way
12617 DATA &H59 : REM         pop cx
12618 REM         ; DrDos version, or something like it, is already in the ax reggy
12619 DATA &HE9, &H1B, &H00 : REM         jmp alldone
12620 REM notdrdos:
12621 REM         ; pop the actual version into ax now that we're done
12622 REM         ; with drdos checks
12623 DATA &H58 : REM         pop ax
12624 DATA &H3C, &H05 : REM         cmp al, 5
12625 DATA &H74, &H0D : REM         je five
12626 REM         ; if al is still zero, it's dos 1
12627 DATA &H3C, &H00 : REM         cmp al, 0
12628 DATA &H75, &H12 : REM         jne alldone
12629 REM         ; Mark it as MS-DOS 1.0
12630 DATA &HB0, &H01 : REM         mov al, 1
12631 DATA &HB4, &H00 : REM         mov ah, 0
12632 DATA &HB7, &HFF : REM         mov bh, 0xFF
12633 DATA &HE9, &H09, &H00 : REM         jmp alldone
12634 REM five:
12635 REM         ; Store the OEM version
12636 DATA &H53 : REM         push bx
12637 DATA &HB8, &H06, &H33 : REM         mov ax, 0x3306
12638 DATA &HCD, &H21 : REM         int 0x21
12639 REM         ; True version should have been in bx
12640 DATA &H53 : REM         push bx
12641 DATA &H58 : REM         pop ax
12642 DATA &H5B : REM         pop bx
12643 REM alldone:
12644 DATA &HB1, &H08 : REM         mov cl, 8
12645 DATA &HD3, &HEB : REM         shr bx, cl
12646 DATA &H30, &HFF : REM         xor bh, bh
12647 REM         ; second argument - OEM code in high byte
12648 DATA &H8B, &H7E, &H06 : REM         mov di, [bp]+6
12649 DATA &H89, &H1D : REM         mov [di], bx
12650 REM         ; first argument - major (al) minor(ah) versions
12651 DATA &H8B, &H7E, &H08 : REM         mov di, [bp]+8
12652 DATA &H89, &H05 : REM         mov [di], ax
12653 DATA &H9D : REM         popf
12654 DATA &H5D : REM         pop bp
12655 DATA &HCA, &H04, &H00
12656 REM Total Bytes in Data: 77

12699 RETURN

12700 REM Returns the DOS type from the OEM% value
12710 OEM$ = "MS-DOS"
12720 IF OEM% = &HEE OR OEM% = &HEF THEN OEM$ = "DR DOS":RETURN
12730 IF OEM% = &HFD THEN OEM$ = "FreeDos":RETURN
12740 IF OEM% = 0 THEN OEM$ = "PC-DOS":RETURN
12750 IF OEM% = &H66 THEN OEM$ = "PTS-DOS":RETURN
12760 IF OEM% = &H5E THEN OEM$ = "RxDOS":RETURN
12769 REM If we're here, stick with MS-DOS...
12770 RETURN

12900 REM Decipher DR-DOS versioning
12910 LOWVER% = DVER% MOD 256
12920 IF LOWVER% = &H41 THEN DVERMAJOR$ = "1":DVERMINOR$ = "2":RETURN
12930 IF LOWVER% = &H60 THEN DVERMAJOR$ = "2":DVERMINOR$ = "0":RETURN
12940 IF LOWVER% = &H63 THEN DVERMAJOR$ = "3":DVERMINOR$ = "41":RETURN
12950 IF LOWVER% = &H64 THEN DVERMAJOR$ = "3":DVERMINOR$ = "42":RETURN
12960 IF LOWVER% = &H65 THEN DVERMAJOR$ = "5":DVERMINOR$ = "0":RETURN
12970 IF LOWVER% = &H67 OR LOWVER% = &H71 THEN DVERMAJOR$ = "6":DVERMINOR$ = "0":RETURN
12980 IF LOWVER% = &H72 OR LOWVER% = &H73 THEN DVERMAJOR$ = "7":DVERMINOR$ = "0":RETURN
12990 DVERMAJOR$ = "?":DVERMINOR$ = "?"
12999 RETURN

13000 REM Create an Uptime string
13010 HOURS = INT(TIMER / 3600)
13020 MINUTES = INT(TIMER / 60) MOD 60
13040 IF HOURS > 0 THEN PRINT HOURS;" hours ";
13050 PRINT MINUTES;" minutes";
13060 RETURN

14000 REM Guess machine
14010 REM output: MACHINE$
14015 MACHID = 0
14020 REM Check for Rainbow video memory first
14030 DEF SEG=&HEE00
14040 IF PEEK(0) = &HFF AND PEEK(1) = 3 AND PEEK(3) = &HFF AND PEEK(4) = 18 THEN MACHID=100
14050 DEF SEG
14099 IF MACHID > 0 THEN GOTO 14200
14100 REM Detect using the machine id from the IBM bios
14110 DEF SEG=&HF000
14120 MACHID = PEEK(&HFFFE)
14130 DEF SEG
14200 REM Parse Machine ID
14210 IF MACHID = 100 THEN MACHINE$="Digital Rainbow 100":GOTO 14299
14220 IBMID = MACHID - &HF8
14230 DIM IBMMODELS$(7)
14240 IBMMODELS$(0) = "PS/2 Model 80":IBMMODELS$(1) = "Convertible PC":IBMMODELS$(2) = "PS/2 Model 30"
14241 IBMMODELS$(3) = "PC/XT":IBMMODELS$(4) = "PC/AT or Compatible":IBMMODELS$(5) = "PCjr"::
14242 IBMMODELS$(6) = "PC/XT":IBMMODELS$(7) = "PC"
14250 IF IBMID >= 0 THEN MACHINE$ = "IBM "+IBMMODELS$(IBMID):GOTO 14299
14260 IF MACHID = 154 OR MACHID = 46 THEN MACHINE$ = "Compaq PC":GOTO 14299
14270 IF MACHID = 49 THEN MACHINE$ = "Sperry PC":GOTO 14299
14299 RETURN

14500 REM Detect DOSBox for real
14501 REM output: DOSBOX = 1 or 0
14502 REM         DBNAME$ for emulator name if DOSBOX = 1
14504 REM Reads the BIOS version string to find "DOSBox" anywhere
14505 REM Should correctly store DOSBox-X as the name if appropriate too
14510 DEF SEG=&HF000
14530 BIOSVER$ = SPACE$(128)
14540 FOR I = 0 TO 127
14550 JC = PEEK(&HE000 + I)
14560 IF JC >= 33 AND JC <= 126 THEN MID$(BIOSVER$, I+1, 1) = CHR$(JC)
14570 NEXT I
14580 DEF SEG
14590 I = INSTR(BIOSVER$, "DOSBox")
14600 IF I = 0 THEN RETURN
14610 FOR J = I+6 TO 128
14620 IF MID$(BIOSVER$, J, 1) = " " THEN GOTO 14640
14630 NEXT J
14640 DBNAME$ = MID$(BIOSVER$, I, J-I)
14645 DOSBOX = 1
14650 RETURN

14700 REM DOSBox - memory stats
14710 CMD$ = "config -get "+CHR$(34)+"dosbox memsize"+CHR$(34):GOSUB 5000
14720 OPEN TMPFILE$ FOR INPUT AS #2
14730 INPUT#2, MEMTOTAL$
14740 CLOSE#2
14750 MEMTOTAL$ = MEMTOTAL$+"MB"
14760 CMD$ = "mem":GOSUB 5000
14761 MEMORY = 0
14762 EX = 0
14770 OPEN TMPFILE$ FOR INPUT AS #2
14780 INPUT#2, TOTRIM$:GOSUB 30000
14800 FOR I=1 TO LEN(TOTRIM$)
14803 IF MID$(TOTRIM$,I,1) = " " THEN GOTO 14810
14808 NEXT I
14810 IF (INSTR(TOTRIM$, "extended") + INSTR(TOTRIM$, "expanded")) > 0 THEN EX = EX + 1
14815 IF EX > 1 THEN GOTO 14830
14820 MEMFREE$ = MID$(TOTRIM$, 1, I):MEMORY = MEMORY + VAL(MEMFREE$)
14830 IF EOF(2) THEN GOTO 14840 ELSE GOTO 14780
14840 CLOSE#2
14850 MEMORY = INT(MEMORY / 1024)
14860 TOTRIM$ = STR$(MEMORY):GOSUB 30000
14870 MEMFREE$ = TOTRIM$+"MB"
14880 RETURN

15000 REM MS-DOS mem parser
15010 REM TMPFILE$ contains the file to open
15011 REM output: MEMTOTAL$ is the total memory in kbytes
15012 REM         MEMFREE$ is the available memory in bytes
15020 MEMTOTAL$ = "":MEMFREE$ = "":MEMLINE$ = ""
15030 OPEN TMPFILE$ FOR INPUT AS #2
15040 LINE INPUT#2, MEMLINE$
15050 IF INSTR(MEMLINE$, "Total memory") > 0 THEN GOTO 15070
15060 IF EOF(2) THEN GOTO 15070 ELSE GOTO 15040
15070 CLOSE #2
15080 TOTRIM$ = MEMLINE$:GOSUB 30000
15089 REM Get rid of the "Total memory" text
15090 TOTRIM$ = RIGHT$(TOTRIM$, LEN(TOTRIM$) - 13):GOSUB 30000
15099 REM pull out the tokens, should be total, used, free
15100 GOSUB 30300:MEMTOTAL$ = TOKEN$
15110 GOSUB 30300
15120 GOSUB 30300:MEMFREE$ = TOKEN$
15130 IDX = INSTR(MEMTOTAL$, "K")
15140 IF IDX > 0 THEN MEMTOTAL$ = LEFT$(MEMTOTAL$, IDX-1) + "kB" ELSE MEMTOTAL$ = MEMTOTAL$ + "B"
15150 IDX = INSTR(MEMFREE$, "K")
15160 IF IDX > 0 THEN MEMFREE$ = LEFT$(MEMFREE$, IDX-1) + "kB" ELSE MEMFREE$ = MEMFREE$ + "B"
15170 RETURN 

20000 REM MS-DOS Logo
20010 LOGO$(1)  = " "
20011 LOGO$(2)  = " &&&&&&     &&&&&&   &&&&&&&&"
20012 LOGO$(3)  = " &&&&&&.   &&&&&&& &&&&&&&&&&&&"
20013 LOGO$(4)  = " &&&&&&&   &&&&&&&.&&&&     &&&&"
20014 LOGO$(5)  = " &&&&&&&   &&&&&&&&&&&+"
20015 LOGO$(6)  = " &&&&&&&; &&&&&&&& &&&&&&&&"
20016 LOGO$(7)  = " &&&&&&&& &&& &&&&   &&&&&&&&&&"
20017 LOGO$(8)  = " &&&& &&&&&&& &&&&        &&&&&&&
20018 LOGO$(9)  = " &&&& &&&&&&& &&&&&&&&       &&&&
20019 LOGO$(10) = " &&&& ;&&&&&  &&&& &&&&+    &&&&&
20020 LOGO$(11) = " &&&&  &&&&&  &&&&  &&&&&&&&&&&$"
20021 LOGO$(12) = " &&&&. &&&&   &&&&     &&&&&&"
20022 LOGO$(13) = " $$$$$$$$$$  $$$$$$$ :::::::."
20023 LOGO$(14) = " $$$$$$$$.$$$$$$$$.::::::::::::"
20024 LOGO$(15) = " $$$$    $$$$$X   :::: &$$ .::::"
20025 LOGO$(16) = " $$$$   $$$$$ $$ .::: $$$$& ...."
20026 LOGO$(17) = " $$$$   $$$$;$$$$ ::::&$$$$"
20027 LOGO$(18) = " $$$$   $$$$ $$$$   ::;$$$$ ::."
20028 LOGO$(19) = " $$$$ . $$$$ $$$$     &$$$$ ::::"
20029 LOGO$(20) = " $$$$   $$$&$$$$$:::: $$$$& .::::"
20030 LOGO$(21) = " $$$$..    $$$$$$::::: $$$ ..::::"
20031 LOGO$(22) = " $$$$$$$$$$$$$$$$&::::::::::::::"
20032 LOGO$(23) = " $$$$$$$$$$$$$$$$$$..::::::::.."
20033 LOGO$(24) = " "
20034 LOGO$(25) = " "
20035 RETURN

20050 REM DOSBox Logo
20051 LOGO$(1)  = "            8888888b."
20052 LOGO$(2)  = "            888   '88b"
20053 LOGO$(3)  = "            888   .88P"
20054 LOGO$(4)  = "            88888888K."
20055 LOGO$(5)  = "            888   'Y88b"
20056 LOGO$(6)  = "            888     888"
20057 LOGO$(7)  = "            888    d88P"
20058 LOGO$(8)  = "            88888888P'"             
20059 LOGO$(9)  = " 8888888b.   .d88888b.   .d8888b."
20060 LOGO$(10) = " 888  'Y88b d88P' 'Y88b d88P  Y88b"
20061 LOGO$(11) = " 888    888 888     888 Y88b."
20062 LOGO$(12) = " 888    888 888     888  'Y888b."
20063 LOGO$(13) = " 888    888 888     888     'Y88b."
20064 LOGO$(14) = " 888    888 888     888       '888"
20065 LOGO$(15) = " 888  .d88P Y88b. .d88P Y88b  d88P"
20066 LOGO$(16) = " 8888888P'   'Y88888P'   'Y8888P'"
20067 LOGO$(17) = "            Y88b   d88P"
20068 LOGO$(18) = "             Y88b d88P"
20069 LOGO$(19) = "              Y88o88P"
20070 LOGO$(20) = "               Y888P"
20071 LOGO$(21) = "               d888b"
20072 LOGO$(22) = "              d88888b"
20073 LOGO$(23) = "             d88P Y88b"
20074 LOGO$(24) = "            d88P   Y88b"
20075 LOGO$(25) = " "
20076 RETURN

20100 REM DR DOS Logo
20101 LOGO$(1)  = " "
20102 LOGO$(2)  = "                 XXXXX"
20103 LOGO$(3)  = "                 XXXXXXXXX"
20104 LOGO$(4)  = "            XXXXXXXXXXXXXXXX"
20105 LOGO$(5)  = "         XXXX         XXXXXXX"
20106 LOGO$(6)  = "        XX               XXXXX"
20107 LOGO$(7)  = "       XX                 XXXXX"
20108 LOGO$(8)  = "      XX                   XXXXX"
20109 LOGO$(9)  = "     XX                     XXXX"
20110 LOGO$(10) = "    XXX                     XXXXX"
20111 LOGO$(11) = "     XX                     XXXX"
20112 LOGO$(12) = "      XX                   XXXXX"
20113 LOGO$(13) = "       XX                 XXXXX"
20114 LOGO$(14) = "        XX               XXXXX"
20115 LOGO$(15) = "         XXXX         XXXXXXX"
20116 LOGO$(16) = "            XXXXXXXXXXXXXXXX"
20117 LOGO$(17) = "                 XXXXXXXXX"
20118 LOGO$(18) = "                 XXXXX"
20119 LOGO$(19) = " "
20120 LOGO$(20) = " .-,--. .-,--.   .-,--.  ,,--. .---."
20121 LOGO$(21) = " ' |   \ `|__/   ' |   \ |`, | \___ "
20122 LOGO$(22) = " , |   / )| \    , |   / |   |     \"
20123 LOGO$(23) = " `-^--'  `'  `   `-^--'  `---' `---'"
20124 LOGO$(24) = " "
20125 LOGO$(25) = " "
20126 RETURN

20150 REM FreeDOS Logo
20151 LOGO$(1)  = " "
20152 LOGO$(2)  = " "
20153 LOGO$(3)  = " "
20154 LOGO$(4)  = "          x&;:::::::&+"
20155 LOGO$(5)  = "        &::::::::::::::&"
20156 LOGO$(6)  = "      $::::::::::::::::::&"
20157 LOGO$(7)  = "     &::::::::::::::::::::&"
20158 LOGO$(8)  = "    &::::::::::::::::::::::&"
20159 LOGO$(9)  = "    $:::::::::::::::::::::::$"
20160 LOGO$(10) = "   X:::    :::::::::::::::::&_____"
20161 LOGO$(11) = "   &::: 00 :   :::::::::::::+::&&/"
20162 LOGO$(12) = "   &::: 00 : 0 :::::::::::::::x&/"
20163 LOGO$(13) = "   x&::    ::::::&:::\:::|&&:+&/"
20164 LOGO$(14) = "     &:&&&&&&&&&&:::::\::|  \&&\"
20165 LOGO$(15) = "     |:/::::::::::::::&\:|   \--+"
20166 LOGO$(16) = "     |/   &&&XXX&&&X    \|"
20167 LOGO$(17) = " "
20168 LOGO$(18) = " "
20169 LOGO$(19) = " ,---.               ,--. ,---.,---."
20170 LOGO$(20) = " |__. ,---.,---.,---.|   ||   |`---."
20171 LOGO$(21) = " |    |    |---'|---'|   ||   |    |"
20172 LOGO$(22) = " `    `    `---'`---'`--' `---'`---'"
20173 LOGO$(23) = " "
20174 LOGO$(24) = " "
20175 LOGO$(25) = " "
20176 RETURN

30000 REM String Trim Function
30010 REM input/output: TOTRIM$
30015 IF LEN(TOTRIM$) = 0 THEN RETURN
30016 IF INSTR(TOTRIM$, " ") = 0 THEN RETURN
30020 FOR IDX = 1 TO LEN(TOTRIM$)
30022 IF MID$(TOTRIM$, IDX, 1) <> " " THEN GOTO 30030
30024 NEXT IDX
30030 FOR RDX = LEN(TOTRIM$) TO IDX STEP -1
30032 IF MID$(TOTRIM$, RDX, 1) <> " " THEN GOTO 30040
30034 NEXT RDX
30040 TOTRIM$ = MID$(TOTRIM$, IDX, RDX - IDX + 1)
30050 RETURN

30100 REM Row check
30110 REM output: ROWS - number of screen rows
30120 ON ERROR GOTO 30150
30130 LOCATE 25,1
30140 ROWS=25:GOTO 30170
30150 RESUME 30160
30160 ROWS=24
30170 ON ERROR GOTO 0
30180 RETURN

30200 REM Character is number or dot
30210 REM input: C$
30220 REM output: ISNUM
30230 IF C$ = "." THEN ISNUM = 1:RETURN
30240 IF ASC(C$) >= ASC("0") AND ASC(C$) <= ASC("9") THEN ISNUM = 1:RETURN
30250 ISNUM = 0:RETURN

30300 REM Split first token by spaces
30301 REM input: TOTRIM$
30302 REM output: TOKEN$ the first token split on spaces
30303 REM         TOTRIM$ the remaining string
30310 GOSUB 30000
30320 FOR I = 1 TO LEN(TOTRIM$)
30330 IF MID$(TOTRIM$, I, 1) = " " THEN GOTO 30350
30340 NEXT I
30350 TOKEN$ = LEFT$(TOTRIM$, I)
30360 REMAINING = LEN(TOTRIM$) - I
30370 IF REMAINING < 1 THEN TOTRIM$ = "":RETURN
30380 TOTRIM$ = RIGHT$(TOTRIM$, REMAINING)
30390 GOSUB 30000
30395 RETURN