Fixed assembler bug that caused crash on IR array resize. Added line editor app.
This commit is contained in:
+18
-1
@@ -179,6 +179,23 @@ else:
|
||||
problems.append("%s is bit %d of the console status port and The Console does"
|
||||
" not mention it" % (name, bit))
|
||||
|
||||
# ---- Every service the system offers has a row ----
|
||||
#
|
||||
# services.asm is the one place the numbers are written, and both the system and every
|
||||
# program include it. A service added there and not here is one nothing can find out about
|
||||
# except by reading the source of the operating system.
|
||||
services = read("Programs/CosmOS/Source/services.asm")
|
||||
offered = re.findall(r'^\s{2}(os[A-Za-z]+)\s+0d\d+', services, re.M)
|
||||
if not offered:
|
||||
problems.append("no services could be found in services.asm")
|
||||
elif "## What A Program May Ask The System For:" not in pm:
|
||||
problems.append("the Programming Manual has lost its services section")
|
||||
else:
|
||||
section = pm.split("## What A Program May Ask The System For:")[1].split("\n## ")[0]
|
||||
for name in offered:
|
||||
if ("| %s |" % name) not in section:
|
||||
problems.append("%s is a service and has no row in the services table" % name)
|
||||
|
||||
# ---- Every directive the assembler knows is written down ----
|
||||
for directive in sorted(set(re.findall(r'"(#[A-Za-z]+)"', util))):
|
||||
if directive not in am:
|
||||
@@ -189,7 +206,7 @@ for directive in sorted(set(re.findall(r'"(#[A-Za-z]+)"', util))):
|
||||
# The first column of the table in each of these sections names something the library has
|
||||
# to define. A routine renamed in the source and not in the manual is caught here, which
|
||||
# is what keeps the tables a description rather than a memory.
|
||||
for heading, library in [("## Reading The Filesystem:", "Programs/CosmOS/Source/sbfs.asm"),
|
||||
for heading, library in [("## Reading And Writing The Filesystem:", "Programs/CosmOS/Source/sbfs.asm"),
|
||||
("## The Console Library:", "Programs/CosmOS/Source/console.asm")]:
|
||||
if heading not in pm:
|
||||
problems.append("the Programming Manual has lost its \"%s\" section"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
0000 0001 0001 0002 0003 0005 0008 000D 0015 0022 0037 0059 0090 00E9 0179 0262 03DB 063D 0A18 1055 1A6D 2AC2 452F 6FF1
|
||||
Execution halted after 2534 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -222,5 +222,5 @@
|
||||
|
||||
|
||||
|
||||
Execution stopped after 3000000 cycles. (cycle limit reached)
|
||||
Execution stopped. (cycle limit reached)
|
||||
[exit 0]
|
||||
|
||||
@@ -222,5 +222,5 @@
|
||||
|
||||
|
||||
|
||||
Execution stopped after 3000000 cycles. (cycle limit reached)
|
||||
Execution stopped. (cycle limit reached)
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
00000000 00000001 00000001 00000002 00000003 00000005 00000008 0000000D 00000015 00000022 00000037 00000059 00000090 000000E9 00000179 00000262 000003DB 0000063D 00000A18 00001055 00001A6D 00002AC2 0000452F 00006FF1 0000B520 00012511 0001DA31 0002FF42 0004D973 0007D8B5 000CB228 00148ADD 00213D05 0035C7E2 005704E7 008CCCC9 00E3D1B0 01709E79 02547029 03C50EA2 06197ECB 09DE8D6D 0FF80C38 19D699A5 29CEA5DD 43A53F82 6D73E55F
|
||||
Execution halted after 10610 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
0 1 1 2 3 5 8 13 21 34 55 89 144 233
|
||||
Execution halted after 1506 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251
|
||||
Execution halted after 54061 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Fault: The device on port 233 refused the access at Program Address 0x001D, and nothing is installed to deal with it.
|
||||
O
|
||||
Execution halted after 16 cycles.
|
||||
Execution halted.
|
||||
[exit 1]
|
||||
|
||||
@@ -3,5 +3,5 @@ blitted
|
||||
DE AD
|
||||
untouched
|
||||
ABABCDEFGH
|
||||
Execution halted after 464 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
QAB C qab c
|
||||
9876543210
|
||||
Execution halted after 176 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -3,5 +3,5 @@ asked for: 0D ready keys interrupts
|
||||
what arrived:
|
||||
keys
|
||||
at the end: 02 ended
|
||||
Execution halted after 1597 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -6,5 +6,5 @@ hi
|
||||
|
||||
at the end: 06 ended keys
|
||||
line mode: 02 ended
|
||||
Execution halted after 891 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -10,5 +10,5 @@ lines read:
|
||||
[SplitBit] 8
|
||||
[a line that is f] 16
|
||||
end of input
|
||||
Execution halted after 6064 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
DE AD BE EF
|
||||
01 FF
|
||||
00 00
|
||||
Execution halted after 315 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -2,5 +2,5 @@ AA BB
|
||||
readonly
|
||||
nobank
|
||||
done
|
||||
Execution halted after 245 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
CosmOS
|
||||
> dir list what is on the disk
|
||||
load <file> read a program off the disk
|
||||
run start what was loaded
|
||||
run [words] start what was loaded, and tell it those words
|
||||
delete <file> take it off the disk
|
||||
rename <file> <to> call it something else
|
||||
dump sixty four bytes of memory, and again for more
|
||||
dump <program|data|bank> <address>
|
||||
help this
|
||||
@@ -21,5 +23,5 @@ aName22CharactersLong! 22
|
||||
12 files
|
||||
> > I do not know: frobnicate
|
||||
> halted
|
||||
Execution halted after 12376 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -19,5 +19,5 @@ CosmOS
|
||||
0020 00 FF 00 00 00 00 00 00 00 FF 00 00 00 00 00 00 ................
|
||||
0030 00 FF 00 00 00 00 00 00 00 FF 00 00 00 00 00 00 ................
|
||||
> halted
|
||||
Execution halted after 23512 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
CosmOS
|
||||
> loaded, starting at 2000
|
||||
> poem.txt, 0 lines
|
||||
> : : : : > : : > 1: alpha
|
||||
2: INSERTED
|
||||
3: beta
|
||||
4: gamma
|
||||
> : > > 1: CHANGED
|
||||
2: INSERTED
|
||||
3: beta
|
||||
> written, 22 bytes
|
||||
> finished
|
||||
> poem.txt, 3 lines
|
||||
> 1: CHANGED
|
||||
2: INSERTED
|
||||
3: beta
|
||||
> there is no such line
|
||||
> finished
|
||||
> halted
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
@@ -0,0 +1,17 @@
|
||||
CosmOS
|
||||
> one.txt 13
|
||||
two.txt 14
|
||||
2 files
|
||||
> renamed
|
||||
> first.txt 13
|
||||
two.txt 14
|
||||
2 files
|
||||
> gone
|
||||
> two.txt 14
|
||||
1 file
|
||||
> no such file
|
||||
> rename what to what?
|
||||
> there is no such file, or that name is taken
|
||||
> halted
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
@@ -5,5 +5,5 @@ finished
|
||||
> Hello, World!
|
||||
finished
|
||||
> halted
|
||||
Execution halted after 2702 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -13,5 +13,5 @@ FE10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
FE20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
FE30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
> halted
|
||||
Execution halted after 10055 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -869,5 +869,5 @@ the board has settled
|
||||
finished
|
||||
>
|
||||
halted
|
||||
Execution halted after 11671230 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -21,5 +21,5 @@ stopped
|
||||
finished
|
||||
> >
|
||||
halted
|
||||
Execution halted after 26243 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -3,5 +3,5 @@ no filesystem on the disk
|
||||
> no filesystem on the disk
|
||||
>
|
||||
halted
|
||||
Execution halted after 636 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -5,8 +5,9 @@ hello.sbx 52
|
||||
Life.sbx 1411
|
||||
Snake.sbx 2175
|
||||
Keys.sbx 663
|
||||
Say.sbx 155
|
||||
notes.txt 21
|
||||
6 files
|
||||
7 files
|
||||
> load what?
|
||||
> no such file
|
||||
> not a program
|
||||
@@ -18,5 +19,5 @@ finished
|
||||
what should I call you? hello, Claude. that is all I do.
|
||||
finished
|
||||
> halted
|
||||
Execution halted after 14083 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
CosmOS
|
||||
> loaded, starting at 2000
|
||||
> nothing was said
|
||||
finished
|
||||
> it says: notes.txt
|
||||
finished
|
||||
> it says: a longer thing with spaces
|
||||
finished
|
||||
> halted
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
@@ -289,5 +289,5 @@ you ran into something
|
||||
finished
|
||||
>
|
||||
halted
|
||||
Execution halted after 1910693 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
ABCZ
|
||||
Execution halted after 21 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
O
|
||||
K
|
||||
!
|
||||
Execution halted after 19 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
04
|
||||
04
|
||||
06
|
||||
Execution halted after 114 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
00
|
||||
from the disk
|
||||
02
|
||||
Execution halted after 245 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -2,5 +2,5 @@ one
|
||||
two
|
||||
three
|
||||
done
|
||||
Execution halted after 126 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
O
|
||||
K
|
||||
Execution halted after 17 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Fault: 0xFE at Program Address 0x0002 is not an instruction.
|
||||
Execution halted after 2 cycles.
|
||||
Execution halted.
|
||||
[exit 1]
|
||||
|
||||
@@ -4,5 +4,5 @@ caught 01FC
|
||||
read ok
|
||||
lowered ok
|
||||
fenced 05
|
||||
Execution halted after 535 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Hello, World!
|
||||
Execution halted after 70 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Input Test: Will echo anything you put in.
|
||||
Hello SplitBit
|
||||
Execution halted after 406 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Input Test: Will echo anything you put in.
|
||||
Hello SplitBit
|
||||
Execution halted after 406 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
00 00 00 00 01
|
||||
1
|
||||
Execution halted after 4707 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ready
|
||||
trap
|
||||
device
|
||||
Execution halted after 105 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
OKC
|
||||
Execution halted after 19 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
loader
|
||||
loaded off a disk, with a string and a loop of its own
|
||||
Execution halted after 1098 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
before
|
||||
loaded
|
||||
back
|
||||
Execution halted after 132 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Fault: The device on port 16 interrupted at Program Address 0x0019, and hardware vector 16 has no handler installed.
|
||||
M
|
||||
S
|
||||
Execution halted after 16 cycles.
|
||||
Execution halted.
|
||||
[exit 1]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
0000 0032
|
||||
Execution halted after 1134 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
AAA
|
||||
Execution halted after 24 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
00 31
|
||||
Execution halted after 78 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -3,5 +3,5 @@ twenty
|
||||
sixty three
|
||||
automatic
|
||||
done
|
||||
Execution halted after 272 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Hello
|
||||
World
|
||||
H
|
||||
Execution halted after 79 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Hello, World!
|
||||
42 is the great answer.
|
||||
Execution halted after 295 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -9,5 +9,5 @@ Testing printByteHex...
|
||||
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
|
||||
|
||||
Testing complete!
|
||||
Execution halted after 71185 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Fault: The device on port 17 refused the access at Program Address 0x000A, and nothing is installed to deal with it.
|
||||
O
|
||||
Execution halted after 6 cycles.
|
||||
Execution halted.
|
||||
[exit 1]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
caught
|
||||
caught
|
||||
done
|
||||
Execution halted after 136 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -3,5 +3,5 @@ flags 01
|
||||
5A 5A
|
||||
refused
|
||||
refused
|
||||
Execution halted after 443 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
10 10 00
|
||||
FF 01 00
|
||||
05 00 00
|
||||
Execution halted after 440 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
here.txt 0002 already here
|
||||
doc.txt 0003 first draft
|
||||
doc.txt 0004 a second draft, which is longer than the first
|
||||
notes.txt 0004 a second draft, which is longer than the first
|
||||
doc.txt gone
|
||||
notes.txt gone
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
@@ -3,5 +3,5 @@ across.txt 0002 BC ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHI
|
||||
aName22CharactersLong! 0000 16 exactly twenty two!!!!
|
||||
empty.txt 0000 00
|
||||
absent.txt missing
|
||||
Execution halted after 19230 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -11,5 +11,5 @@ across.txt 700
|
||||
empty.txt 0
|
||||
aName22CharactersLong! 22
|
||||
files: 12
|
||||
Execution halted after 9498 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
here.txt 0002 already here
|
||||
first.txt 0003 written by SplitBit itself
|
||||
second.txt 0004 and a second one after it
|
||||
Execution halted after 5888 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
OK
|
||||
Execution halted after 14 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -5,5 +5,5 @@ reclaimed: FFFF
|
||||
still good: 33
|
||||
borrowed: 7 and 9
|
||||
back home: FFFF
|
||||
Execution halted after 1054 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -2,5 +2,5 @@ one
|
||||
two
|
||||
three
|
||||
AFTER
|
||||
Execution halted after 122 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Fault: Software vector 20, dispatched from Program Address 0x0008, has no handler installed.
|
||||
O
|
||||
Execution halted after 5 cycles.
|
||||
Execution halted.
|
||||
[exit 1]
|
||||
|
||||
@@ -4,5 +4,5 @@ same: yes no no no
|
||||
hex: 2000 00FF 00FF BEEF FFFF 0000
|
||||
0 is a fine way to begin a string
|
||||
no number here
|
||||
Execution halted after 2730 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Two pointers, no stack shenanigans.
|
||||
Execution halted after 395 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
OK!
|
||||
good
|
||||
AFTER
|
||||
Execution halted after 77 cycles.
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
load Edit.sbx
|
||||
run poem.txt
|
||||
a
|
||||
alpha
|
||||
beta
|
||||
gamma
|
||||
.
|
||||
i 2
|
||||
INSERTED
|
||||
.
|
||||
l
|
||||
c 1
|
||||
CHANGED
|
||||
d 4
|
||||
l
|
||||
w
|
||||
q
|
||||
run poem.txt
|
||||
l
|
||||
d 99
|
||||
q
|
||||
exit
|
||||
@@ -0,0 +1,9 @@
|
||||
dir
|
||||
rename one.txt first.txt
|
||||
dir
|
||||
delete first.txt
|
||||
dir
|
||||
delete first.txt
|
||||
rename two.txt
|
||||
rename two.txt two.txt
|
||||
exit
|
||||
@@ -0,0 +1,5 @@
|
||||
load Say.sbx
|
||||
run
|
||||
run notes.txt
|
||||
run a longer thing with spaces
|
||||
exit
|
||||
+32
-1
@@ -59,7 +59,7 @@ for i in 1 2 3 4 5 6 7 8; do "$TOOL" put "$DISKS/sbfs.img" "filler$i.txt" >/dev/
|
||||
# to the hardware, so something has to check that one still gives the machine back.
|
||||
#
|
||||
# notes.txt is there so that loading something that is not a program can be tried too.
|
||||
"$TOOL" format "$DISKS/cosmos.img" 32 1 >/dev/null
|
||||
"$TOOL" format "$DISKS/cosmos.img" 256 2 >/dev/null
|
||||
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \
|
||||
"$ROOT/Programs/CosmOS/Apps/greet.asm" -o "$WORK/greet.sbx" >/dev/null
|
||||
"$TOOL" put "$DISKS/cosmos.img" "$WORK/greet.sbx" >/dev/null
|
||||
@@ -86,6 +86,11 @@ for i in 1 2 3 4 5 6 7 8; do "$TOOL" put "$DISKS/sbfs.img" "filler$i.txt" >/dev/
|
||||
"$ROOT/Assembler" -I "$ROOT/Programs/Libraries" -I "$ROOT/Programs/CosmOS/Source" \
|
||||
"$ROOT/Programs/CosmOS/Apps/Keys.asm" -o "$WORK/Keys.sbx" >/dev/null
|
||||
"$TOOL" put "$DISKS/cosmos.img" "$WORK/Keys.sbx" >/dev/null
|
||||
# Say.sbx is the first program that can be told anything. Everything before it did the same
|
||||
# thing however it was started.
|
||||
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \
|
||||
"$ROOT/Programs/CosmOS/Apps/Say.asm" -o "$WORK/Say.sbx" >/dev/null
|
||||
"$TOOL" put "$DISKS/cosmos.img" "$WORK/Say.sbx" >/dev/null
|
||||
printf 'this is not a program' > notes.txt
|
||||
"$TOOL" put "$DISKS/cosmos.img" notes.txt >/dev/null
|
||||
|
||||
@@ -94,3 +99,29 @@ printf 'this is not a program' > notes.txt
|
||||
"$TOOL" format "$DISKS/write.img" 32 1 >/dev/null
|
||||
printf 'already here' > here.txt
|
||||
"$TOOL" put "$DISKS/write.img" here.txt >/dev/null
|
||||
|
||||
# A disk of its own for the editing test, which deletes and renames things and would
|
||||
# otherwise leave the writing test's disk looking nothing like the writing test expects.
|
||||
# It starts with one file on it so that a document written here has to be placed around
|
||||
# something, and so that the disk can be compared afterwards against one that never had
|
||||
# any of it: the metadata should come back exactly as it was.
|
||||
"$TOOL" format "$DISKS/edit.img" 32 1 >/dev/null
|
||||
"$TOOL" put "$DISKS/edit.img" here.txt >/dev/null
|
||||
|
||||
# A disk for the shell's delete and rename, which is its own because those change what is
|
||||
# on it. A fixture whose name has a directory in it is used as it stands rather than being
|
||||
# made fresh per test, so a test that writes to a shared one would quietly change what
|
||||
# every test after it sees.
|
||||
"$TOOL" format "$DISKS/files.img" 32 1 >/dev/null
|
||||
printf 'the first one' > one.txt
|
||||
printf 'the second one' > two.txt
|
||||
"$TOOL" put "$DISKS/files.img" one.txt >/dev/null
|
||||
"$TOOL" put "$DISKS/files.img" two.txt >/dev/null
|
||||
|
||||
# A disk for the editor, holding nothing but the editor. Its own, because the whole point
|
||||
# of it is that it writes: a document made on a shared fixture would turn up in the file
|
||||
# listing of every test that came after it.
|
||||
"$TOOL" format "$DISKS/editor.img" 256 2 >/dev/null
|
||||
"$ROOT/Assembler" -I "$ROOT/Programs/Libraries" -I "$ROOT/Programs/CosmOS/Source" \
|
||||
"$ROOT/Programs/CosmOS/Apps/Edit.asm" -o "$WORK/Edit.sbx" >/dev/null
|
||||
"$TOOL" put "$DISKS/editor.img" "$WORK/Edit.sbx" >/dev/null
|
||||
|
||||
@@ -77,6 +77,11 @@ sbfsWalkTest | testPrograms/sbfsWalkTest.asm | run | -
|
||||
# Writing a filesystem, then reading back what was written. The disk starts with a file
|
||||
# on it, so allocation has to find room rather than start at the beginning.
|
||||
sbfsWriteTest | testPrograms/sbfsWriteTest.asm | run | - | - | disks/write.img
|
||||
# Deleting, renaming, and saving over something already there, which is what any tool that
|
||||
# edits a document needs and what none of the tests above touch. The interesting line is
|
||||
# the third: saving a longer version moves the file, because files here are contiguous and
|
||||
# do not grow. The last two lines are a rename and a delete having actually happened.
|
||||
sbfsEditTest | testPrograms/sbfsEditTest.asm | run | - | - | disks/edit.img
|
||||
|
||||
# Loading a program off a disk and running it. Everything below this line existed before
|
||||
# the loader did; the only new part is the sixteen bytes on the front of a loadable
|
||||
@@ -238,6 +243,26 @@ cosmosSnake | CosmOS/Source/cosmos.asm | run | cosmosSna
|
||||
# console's slot at FE00 is zero again, and CosmOS's own disk handler further along is
|
||||
# untouched by a program having installed over the top of it.
|
||||
cosmosKeys | CosmOS/Source/cosmos.asm | run | cosmosKeys.in | - | disks/cosmos.img
|
||||
# The shell taking things off a disk and calling them something else, which is the last of
|
||||
# CosmOS's original four verbs to be built and the first time anything has changed a disk
|
||||
# from the shell. Its own image, because it changes what is on it: a fixture named with a
|
||||
# directory is used as it stands, so a test that writes to a shared one changes what every
|
||||
# test after it sees. The refusals are here too - deleting what is not there, renaming with
|
||||
# a name missing, and renaming something to a name already taken.
|
||||
cosmosFiles | CosmOS/Source/cosmos.asm | run | cosmosFiles.in | - | disks/files.img
|
||||
# A program being told what it is for. Nothing loaded before this could be told anything,
|
||||
# so every app did the same thing however it was started - which is fine for a demo and no
|
||||
# use at all to a tool that edits a named document. Run three times: with nothing, with a
|
||||
# name, and with several words, since what arrives is the rest of the line rather than a
|
||||
# list and it is the program's business what to make of it.
|
||||
cosmosSay | CosmOS/Source/cosmos.asm | run | cosmosSay.in | - | disks/cosmos.img
|
||||
# The editor, which is the first program on this machine that makes a file a person typed:
|
||||
# every byte on every other image here was put there by the host tool. It is run twice in
|
||||
# one session, and that is the test rather than a flourish - the second run reads back what
|
||||
# the first one wrote, so the whole path is checked at once: read a name from the argument,
|
||||
# split a file into lines, edit them, build a file back out of them, and save it over
|
||||
# something that was already there and is now a different size.
|
||||
cosmosEdit | CosmOS/Source/cosmos.asm | run | cosmosEdit.in | - | disks/editor.img
|
||||
# The programs CosmOS loads, checked on their own so that a failure here reads as "the app
|
||||
# does not assemble" rather than as a broken disk image.
|
||||
app-greet | CosmOS/Apps/greet.asm | assemble | - | -
|
||||
@@ -245,6 +270,8 @@ app-hello | CosmOS/Apps/hello.asm | assemble | -
|
||||
app-Life | CosmOS/Apps/Life.asm | assemble | - | -
|
||||
app-Snake | CosmOS/Apps/Snake.asm | assemble | - | -
|
||||
app-Keys | CosmOS/Apps/Keys.asm | assemble | - | -
|
||||
app-Say | CosmOS/Apps/Say.asm | assemble | - | -
|
||||
app-Edit | CosmOS/Apps/Edit.asm | assemble | - | -
|
||||
|
||||
# ---- Programs driven by console input ----
|
||||
inputTest | inputTest.asm | run | inputTest.in | -
|
||||
|
||||
@@ -84,6 +84,24 @@ trim() {
|
||||
printf '%s' "$v"
|
||||
}
|
||||
|
||||
# Takes the cycle count out of the emulator's last line, in place.
|
||||
#
|
||||
# HOW MANY CYCLES A PROGRAM TOOK IS NOT WHAT ANY OF THESE TESTS ARE ABOUT, and having it in
|
||||
# every recorded result made every one of them fragile in the same way: two instructions
|
||||
# added to CosmOS moved the count in six unrelated files at once, so a real difference
|
||||
# would have arrived in a crowd of meaningless ones and had to be picked out by hand.
|
||||
#
|
||||
# WHETHER a program stopped on its own or ran into its limit is kept, because that is
|
||||
# behaviour and several tests exist to check it. Only the number goes.
|
||||
#
|
||||
# Anything that genuinely wants to measure cycles should say so out loud in a test of its
|
||||
# own rather than every test carrying the measurement and nothing asserting anything about
|
||||
# it.
|
||||
settle() {
|
||||
sed -i -E 's/^Execution halted after [0-9]+ cycles\.$/Execution halted./;
|
||||
s/^Execution stopped after [0-9]+ cycles\. \(cycle limit reached\)$/Execution stopped. (cycle limit reached)/' "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
# check <name> <actual-file>
|
||||
local name="$1" actual="$2" golden="$EXPECTED/$1.out"
|
||||
@@ -205,6 +223,7 @@ while IFS='|' read -r name src mode stdin limit disk; do
|
||||
# supposed to exit non zero, and that should be just as pinned down as
|
||||
# what it printed.
|
||||
printf '[exit %d]\n' "$STATUS" >> "$OUT"
|
||||
settle "$OUT"
|
||||
check "$name" "$OUT"
|
||||
;;
|
||||
*)
|
||||
|
||||
Executable
+259
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env bash
|
||||
# Checks the things a recorded output cannot see.
|
||||
#
|
||||
# Every other test in this suite pipes standard input in and standard output to a file, and
|
||||
# compares what came out against what came out last time. That answers "what does this
|
||||
# program print", which is the right question almost always, and it is blind to two whole
|
||||
# classes of behaviour:
|
||||
#
|
||||
# WHEN something is printed. Piped output is fully buffered and flushed when the process
|
||||
# ends, so a prompt that appears before input is read and a prompt that appears an hour
|
||||
# later produce byte-identical files. A prompt printed after the answer it was asking for
|
||||
# is invisible to every other test here.
|
||||
#
|
||||
# WHAT HAPPENS TO THE TERMINAL. Key mode only touches a terminal when there is one, so
|
||||
# with input from a file there is nothing to put into another state and nothing to put
|
||||
# back. A machine that leaves the terminal without echo passes all 92 other tests.
|
||||
#
|
||||
# Both of those have gone wrong in this repository, and both were found by a person whose
|
||||
# terminal stopped working rather than by anything here. So this runs the emulator under a
|
||||
# pseudo-terminal, which is what makes those questions askable at all.
|
||||
#
|
||||
# Written by Anachronaut
|
||||
|
||||
set -u
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT" || exit 1
|
||||
|
||||
BUILD="$ROOT/Tests/build/terminal"
|
||||
mkdir -p "$BUILD"
|
||||
|
||||
[ -x "$ROOT/SplitBit" ] || { echo "The emulator is not built."; exit 1; }
|
||||
[ -x "$ROOT/Assembler" ] || { echo "The assembler is not built."; exit 1; }
|
||||
|
||||
# A program that asks for key mode and then waits for a key that never comes. Every check
|
||||
# below needs a machine that is sitting in key mode with the terminal in its hands.
|
||||
cat > "$BUILD/keywait.asm" <<'ASM'
|
||||
#Program
|
||||
start:
|
||||
INIA 0x01
|
||||
OUTA 0x02
|
||||
INA 0x00
|
||||
HALT
|
||||
ASM
|
||||
|
||||
# A program that prints something with no newline after it and then waits, which is the
|
||||
# shape of a prompt and the shape the buffering problem hides in.
|
||||
cat > "$BUILD/prompt.asm" <<'ASM'
|
||||
#Program
|
||||
start:
|
||||
INIA 0d62 ; '>'
|
||||
OUTA 0x00
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
INA 0x00
|
||||
HALT
|
||||
ASM
|
||||
|
||||
"$ROOT/Assembler" "$BUILD/keywait.asm" -o "$BUILD/keywait.bin" >/dev/null 2>&1 || {
|
||||
echo "Could not assemble the terminal test programs."; exit 1; }
|
||||
"$ROOT/Assembler" "$BUILD/prompt.asm" -o "$BUILD/prompt.bin" >/dev/null 2>&1 || {
|
||||
echo "Could not assemble the terminal test programs."; exit 1; }
|
||||
|
||||
python3 - "$ROOT" "$BUILD" <<'PY'
|
||||
import os
|
||||
import pty
|
||||
import select
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
|
||||
root, build = sys.argv[1], sys.argv[2]
|
||||
emulator = os.path.join(root, "SplitBit")
|
||||
|
||||
passed = 0
|
||||
problems = []
|
||||
|
||||
|
||||
def report(ok, what, detail=""):
|
||||
global passed
|
||||
if ok:
|
||||
passed += 1
|
||||
print(" [ok ] %s" % what)
|
||||
else:
|
||||
problems.append(what if not detail else "%s: %s" % (what, detail))
|
||||
print(" [FAIL] %s%s" % (what, (" - " + detail) if detail else ""))
|
||||
|
||||
|
||||
def underPty(script, seconds=10):
|
||||
"""Runs a shell script with a pseudo-terminal for its controlling terminal, and
|
||||
gives back everything the terminal saw."""
|
||||
pid, fd = pty.fork()
|
||||
if pid == 0:
|
||||
os.execv("/bin/bash", ["/bin/bash", "-c", script])
|
||||
seen = b""
|
||||
end = time.time() + seconds
|
||||
while time.time() < end:
|
||||
ready, _, _ = select.select([fd], [], [], 0.2)
|
||||
if ready:
|
||||
try:
|
||||
chunk = os.read(fd, 4096)
|
||||
except OSError:
|
||||
break
|
||||
if not chunk:
|
||||
break
|
||||
seen += chunk
|
||||
if os.waitpid(pid, os.WNOHANG)[0]:
|
||||
break
|
||||
try:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
try:
|
||||
os.waitpid(pid, 0)
|
||||
except ChildProcessError:
|
||||
pass
|
||||
os.close(fd)
|
||||
return seen
|
||||
|
||||
|
||||
# ---- A prompt is shown before the answer to it is asked for ----
|
||||
#
|
||||
# The machine writes "> " and then waits. Nothing more will ever be printed, so if the two
|
||||
# characters have not arrived after a second of waiting, they are sitting in a buffer and
|
||||
# the person at the terminal is looking at nothing and being asked to answer it.
|
||||
#
|
||||
# This is exactly the bug that getchar used to hide: reading through stdio flushed the line
|
||||
# buffered streams first, and reading with read() does not.
|
||||
pid, fd = pty.fork()
|
||||
if pid == 0:
|
||||
os.execv(emulator, [emulator, "--fast", os.path.join(build, "prompt.bin")])
|
||||
seen = b""
|
||||
end = time.time() + 1.5
|
||||
while time.time() < end:
|
||||
ready, _, _ = select.select([fd], [], [], 0.2)
|
||||
if ready:
|
||||
try:
|
||||
seen += os.read(fd, 1024)
|
||||
except OSError:
|
||||
break
|
||||
report(b">" in seen, "a prompt is shown before its answer is read",
|
||||
"" if b">" in seen else "nothing arrived in 1.5s, so it is stuck in a buffer")
|
||||
try:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
os.waitpid(pid, 0)
|
||||
except (ProcessLookupError, ChildProcessError):
|
||||
pass
|
||||
os.close(fd)
|
||||
|
||||
|
||||
# ---- A key arrives without Return ----
|
||||
#
|
||||
# Which is the whole of what key mode is for. In line mode the terminal holds what is typed
|
||||
# until Return, so a machine that failed to ask for key mode would wait here for ever.
|
||||
pid, fd = pty.fork()
|
||||
if pid == 0:
|
||||
os.execv(emulator, [emulator, "--fast", os.path.join(build, "keywait.bin")])
|
||||
time.sleep(0.5)
|
||||
os.write(fd, b"x") # No newline, on purpose.
|
||||
finished = False
|
||||
end = time.time() + 2
|
||||
while time.time() < end:
|
||||
if os.waitpid(pid, os.WNOHANG)[0]:
|
||||
finished = True
|
||||
break
|
||||
time.sleep(0.05)
|
||||
report(finished, "a single keystroke arrives without Return",
|
||||
"" if finished else "the machine was still waiting, so the key was held by the terminal")
|
||||
try:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
os.waitpid(pid, 0)
|
||||
except (ProcessLookupError, ChildProcessError):
|
||||
pass
|
||||
os.close(fd)
|
||||
|
||||
|
||||
# ---- The terminal is handed back however the machine dies ----
|
||||
#
|
||||
# atexit covers stopping on purpose and nothing else: it does not run when a process is
|
||||
# killed by a signal. SIGHUP is the one that matters most, because it is what arrives when
|
||||
# whatever launched the machine dies and takes the terminal with it - and a terminal left
|
||||
# in key mode has no echo and no line editing, which is a far worse failure than anything
|
||||
# the program was doing.
|
||||
for name in ("HUP", "INT", "QUIT", "ABRT", "SEGV", "TERM"):
|
||||
# Cleared first. Without this a run that dies before it can measure leaves the PREVIOUS
|
||||
# signal's files in place, and the check compares those and passes - which is how the
|
||||
# SIGQUIT case came to be reporting success while proving nothing at all.
|
||||
for leftover in ("before.txt", "after.txt"):
|
||||
try:
|
||||
os.remove(os.path.join(build, leftover))
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
script = (
|
||||
# No core files. Two of these signals dump core by default, and a test suite has no
|
||||
# business leaving those around every time it runs. It also keeps the measuring
|
||||
# shell alive through SIGQUIT, which otherwise takes it down before it can look.
|
||||
"ulimit -c 0\n"
|
||||
"stty -g > {b}/before.txt\n"
|
||||
"{e} --fast {b}/keywait.bin < /dev/tty &\n"
|
||||
"P=$!\n"
|
||||
"sleep 0.5\n"
|
||||
"kill -{s} $P 2>/dev/null\n"
|
||||
"wait $P 2>/dev/null\n"
|
||||
"sleep 0.3\n"
|
||||
"stty -g > {b}/after.txt\n"
|
||||
).format(b=build, e=emulator, s=name)
|
||||
# < /dev/tty is load bearing. A background job in a non-interactive shell gets its
|
||||
# standard input from /dev/null, so without it the machine never sees a terminal, never
|
||||
# enters key mode, and has nothing to fail to put back - and this check would pass
|
||||
# against a machine that restores nothing at all.
|
||||
underPty(script)
|
||||
try:
|
||||
before = open(os.path.join(build, "before.txt")).read().strip()
|
||||
after = open(os.path.join(build, "after.txt")).read().strip()
|
||||
except FileNotFoundError:
|
||||
# A failure, and for SIGQUIT this is the shape the failure takes: a machine that
|
||||
# does not handle it dies in a way that takes the measuring shell with it, so what
|
||||
# is reported is not "the terminal was left wrong" but "nothing got as far as
|
||||
# looking". Both mean the same thing here, which is that the signal is unhandled.
|
||||
report(False, "the terminal is restored after SIG%s" % name, "could not measure")
|
||||
continue
|
||||
report(before == after, "the terminal is restored after SIG%s" % name,
|
||||
"" if before == after else "left as %s, was %s" % (after[:24], before[:24]))
|
||||
|
||||
|
||||
# ---- Suspending is not dying ----
|
||||
#
|
||||
# Ctrl-Z has to hand the terminal back while the machine is stopped, because whoever gets it
|
||||
# next is entitled to find it as they left it, and take key mode again on resume, because
|
||||
# the machine has not finished with it.
|
||||
script = (
|
||||
"ulimit -c 0\n"
|
||||
"stty -g > {b}/t0.txt\n"
|
||||
"{e} --fast {b}/keywait.bin < /dev/tty &\n"
|
||||
"P=$!\n"
|
||||
"sleep 0.5\n"
|
||||
"kill -TSTP $P; sleep 0.4\n"
|
||||
"stty -g > {b}/t1.txt\n"
|
||||
"kill -CONT $P; sleep 0.4\n"
|
||||
"stty -g > {b}/t2.txt\n"
|
||||
"kill -TERM $P; sleep 0.2\n"
|
||||
).format(b=build, e=emulator)
|
||||
underPty(script)
|
||||
try:
|
||||
t0 = open(os.path.join(build, "t0.txt")).read().strip()
|
||||
t1 = open(os.path.join(build, "t1.txt")).read().strip()
|
||||
t2 = open(os.path.join(build, "t2.txt")).read().strip()
|
||||
report(t1 == t0, "the terminal is handed back while suspended")
|
||||
report(t2 != t0, "key mode is taken again on resume")
|
||||
except FileNotFoundError:
|
||||
report(False, "suspending and resuming", "could not measure")
|
||||
|
||||
print()
|
||||
if problems:
|
||||
print("The terminal does not survive everything it should:")
|
||||
for p in problems:
|
||||
print(" " + p)
|
||||
sys.exit(1)
|
||||
print("All %d terminal checks passed." % passed)
|
||||
PY
|
||||
Reference in New Issue
Block a user