Skip to content

Commit a9108de

Browse files
authored
Merge pull request #13 from jgarzik/updates
lang support Updates
2 parents 908ce27 + 6fca9cc commit a9108de

25 files changed

Lines changed: 2242 additions & 396 deletions

File tree

.github/workflows/TestingCI.yml

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,68 @@ jobs:
7373
& ./target/release/xbasic64.exe $_.FullName -o example.exe
7474
if ($LASTEXITCODE -ne 0) { throw "compile failed: $($_.Name)" }
7575
& ./example.exe | Out-Null
76-
if ($LASTEXITCODE -ne 0) { throw "run failed: $($_.Name)" }
76+
# Report the code, not just the failure. Windows says what went
77+
# wrong in it, and nothing else here can: 1 is the compiler's own
78+
# diagnosed abort, while 0xC0000005 (access violation), 0xC0000409
79+
# (stack buffer overrun) and 0xC0000374 (heap corruption) each name
80+
# a different defect, and none of the three can be reproduced on
81+
# the Linux job.
82+
if ($LASTEXITCODE -ne 0) {
83+
# `-f` with X8 renders a negative Int32 as its two's-complement
84+
# hex on its own. Masking first needed no cast and got one: in
85+
# PowerShell 0xFFFFFFFF is Int32 -1, so the -band was a no-op and
86+
# [uint32] then threw on the very value it was there to show.
87+
throw ("run failed: {0} (exit {1}, 0x{2:X8})" -f $_.Name, $LASTEXITCODE, $LASTEXITCODE)
88+
}
7789
}
90+
91+
# An example is a whole program, so a crash in one names the program and
92+
# not the statement. These are one statement each, and they are the only
93+
# coverage the console statements have anywhere: the test suite runs on
94+
# Linux, and CLS, LOCATE and COLOR are precisely the helpers whose two
95+
# implementations differ most.
96+
#
97+
# Every probe runs, and the step reports the whole table before failing,
98+
# so one CI run says which statements are broken rather than the first.
99+
- name: Probe each console statement
100+
if: always()
101+
shell: pwsh
102+
run: |
103+
$probes = [ordered]@{
104+
'CLS' = 'CLS'
105+
'CLS after PRINT' = "PRINT `"x`"`nCLS"
106+
'CLS twice' = "CLS`nCLS"
107+
'PRINT alone' = 'PRINT "x"'
108+
'COLOR' = 'COLOR 14, 1'
109+
'LOCATE' = 'LOCATE 2, 5'
110+
'LOCATE row only' = 'LOCATE 3'
111+
'POS' = 'PRINT POS(0)'
112+
'TIMER' = 'PRINT TIMER'
113+
'RANDOMIZE n' = 'RANDOMIZE 42'
114+
'RANDOMIZE TIMER' = 'RANDOMIZE TIMER'
115+
'RND' = 'PRINT RND'
116+
'RND(0)' = 'PRINT RND(0)'
117+
'BEEP' = 'BEEP'
118+
'DATE$' = 'PRINT DATE$'
119+
'TIME$' = 'PRINT TIME$'
120+
'FRE' = 'PRINT FRE(0)'
121+
'DEFINT' = "DEFINT A-Z`nX = 3`nPRINT X"
122+
'EQV' = 'PRINT 1 EQV 1'
123+
'ERASE' = "DIM A(3)`nERASE A"
124+
'SYSTEM' = 'SYSTEM'
125+
}
126+
$failed = @()
127+
foreach ($p in $probes.GetEnumerator()) {
128+
Set-Content -Path probe.bas -Value $p.Value
129+
& ./target/release/xbasic64.exe probe.bas -o probe.exe | Out-Null
130+
if ($LASTEXITCODE -ne 0) {
131+
Write-Host ("{0,-20} DID NOT COMPILE" -f $p.Key)
132+
$failed += $p.Key
133+
continue
134+
}
135+
& ./probe.exe | Out-Null
136+
$code = $LASTEXITCODE
137+
Write-Host ("{0,-20} exit {1} (0x{2:X8})" -f $p.Key, $code, $code)
138+
if ($code -ne 0) { $failed += $p.Key }
139+
}
140+
if ($failed.Count -gt 0) { throw "crashed: $($failed -join ', ')" }

CLAUDE.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,20 @@ Integration tests organized by feature area:
7272
- **Runtime checks on by default**: `--unsafe` removes them
7373
- **Two runtimes in lockstep**: `runtime/sysv/` and `runtime/win64-native/` export
7474
the same `.globl` names; a new helper must be added to both
75+
- **Two assemblers, not just two runtimes**: `main.rs` assembles with GNU `as`
76+
on Linux and `clang -c` on Windows, so the Win64 tree is the one built by the
77+
assembler no test here runs. They disagree about `.equ len, . - label`: GNU
78+
`as` folds it to an immediate, and an assembler that cannot prove the symbol
79+
absolute assembles `mov reg, len` as a *load from that address* instead.
80+
`CLS` did that on every Windows run. Bracket data with an `_end` label and
81+
subtract at run time
7582
- **A runtime helper takes at most four arguments**: Win64 passes only four in
7683
registers, so `arg_reg(4)` panics there while System V accepts six. A helper
7784
that needs a fifth is invisible on Linux and breaks every Windows compile;
7885
fold an argument away instead (see `_rt_file_open_random`)
7986
- **Unsupported GW-BASIC names are refused, not ignored**: an unrecognised name
8087
is otherwise just a new variable, so `UNSUPPORTED` in `sema.rs` names the
81-
keywords this compiler does not provide and why. See [NONGOALS.md](NONGOALS.md)
88+
keywords this compiler does not provide and why
8289
- **String builtins returning a static buffer must copy**: two calls in one
8390
expression would otherwise alias, which is why `_rt_str`, `_rt_chr`, `_rt_hex`,
8491
`_rt_oct` and `_rt_mk` end in `_rt_strdup`

LANGREF.md

Lines changed: 104 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,24 @@ xbasic64 supports five data types, indicated by suffix characters:
144144

145145
### Default Type
146146

147-
**Unsuffixed numeric variables default to DOUBLE (`#`).**
147+
**Unsuffixed numeric variables default to DOUBLE (`#`)** unless a `DEF*`
148+
statement says otherwise.
149+
150+
### DEFINT, DEFLNG, DEFSNG, DEFDBL, DEFSTR
151+
152+
These set the default type for names beginning with the given letters, so a
153+
listing need not suffix every variable:
154+
155+
```basic
156+
DEFINT A-Z ' every unsuffixed name is an INTEGER
157+
DEFSTR S ' except those starting with S, which are strings
158+
DEFINT A, C-E ' single letters and ranges, comma separated
159+
```
160+
161+
A suffix always wins over the default, and the two spellings of one name are
162+
the same variable: after `DEFINT A`, `A` and `A%` share storage. The default
163+
applies to the whole program rather than from the statement onwards, which is
164+
where these are written in practice.
148165

149166
```basic
150167
X = 3.14159 ' X is Double
@@ -269,6 +286,8 @@ case-sensitive and a prefix sorts before the longer string (`"ab" < "abc"`).
269286
| `OR` | Bitwise/logical OR |
270287
| `XOR` | Bitwise/logical XOR |
271288
| `NOT` | Bitwise/logical NOT |
289+
| `EQV` | Bitwise equivalence |
290+
| `IMP` | Bitwise implication |
272291

273292
These operate bitwise on integers, allowing both logical tests and bit
274293
manipulation. Their operands are converted to integers first, and the result is
@@ -302,6 +321,8 @@ From highest to lowest:
302321
6. `NOT`
303322
7. `AND`
304323
8. `OR`, `XOR`
324+
9. `EQV`
325+
10. `IMP`
305326

306327
Because `^` binds tighter than unary negation, `-2 ^ 2` is `-(2 ^ 2)` = -4.
307328

@@ -663,6 +684,46 @@ Clear screen:
663684
CLS
664685
```
665686

687+
### BEEP, ERASE and SYSTEM
688+
689+
```basic
690+
BEEP ' Ring the terminal bell
691+
DIM Scores(10)
692+
ERASE Scores ' Release it, so it may be DIMed again
693+
DIM Scores(50)
694+
SYSTEM ' End the program, as END does
695+
```
696+
697+
`ERASE` is recognised only before a name, so `Erase` remains usable as a label
698+
or a variable elsewhere. Its argument must be an array that is DIMed somewhere
699+
-- a name that is not one is a mistake, not a statement that does nothing.
700+
701+
### LOCATE and COLOR
702+
703+
Console control, written as ANSI escape sequences:
704+
705+
```basic
706+
CLS
707+
LOCATE 5, 10 ' Row 5, column 10; both count from 1
708+
LOCATE 3 ' Row only; the column is left alone
709+
LOCATE , 20 ' Column only
710+
COLOR 14, 1 ' Bright yellow on blue
711+
COLOR 7 ' Foreground only
712+
PRINT "positioned"
713+
```
714+
715+
Colours are GW-BASIC's 0-15, where 8-15 are the bright half. GW-BASIC's third
716+
`COLOR` argument sets the border, which a terminal has no equivalent for, and is
717+
refused rather than ignored. `LOCATE` with no row asks for row 1, since the row
718+
is not tracked the way the column is.
719+
720+
`POS(0)` gives the column the next character will be written to, counting from 1:
721+
722+
```basic
723+
PRINT "abc";
724+
PRINT POS(0) ' 4
725+
```
726+
666727
### SWAP
667728

668729
Exchange two values of the same type, including array elements:
@@ -847,23 +908,41 @@ STOP ' Terminate (historically for debugging)
847908
| `EXP(x)` | e raised to power x |
848909
| `LOG(x)` | Natural logarithm |
849910
| `RND` | Random number 0 ≤ r < 1 |
911+
| `FRE(x)` | Free memory; a large constant here |
850912

851913
**Numeric output:** `PRINT` writes the shortest decimal that reads back as the
852914
same value, so a `DOUBLE` shows its full precision (`PRINT 1 / 3` gives
853915
`0.3333333333333333`) and a `SINGLE` shows only the ~7 digits it carries.
854916

855-
**RND behavior:**
917+
**RND behavior:** the argument selects between three behaviours.
918+
856919
```basic
857920
X = RND ' Next random number
858-
X = RND(0) ' Same as RND
859-
X = RND(-1) ' Reseed with system time (implementation-defined)
921+
X = RND(1) ' Next random number; any positive value does this
922+
X = RND(0) ' The previous number again
923+
X = RND(-7) ' Reseed from -7, then return the next number
924+
```
925+
926+
The generator starts from a fixed seed, so a program that never reseeds replays
927+
the same numbers on every run -- which is useful while debugging and wrong for a
928+
game. `RANDOMIZE` is how a program chooses:
929+
930+
```basic
931+
RANDOMIZE ' Seed from the clock: a different run every time
932+
RANDOMIZE TIMER ' The same thing, written out
933+
RANDOMIZE 42 ' A fixed seed: the same run every time
860934
```
861935

936+
GW-BASIC's bare `RANDOMIZE` asks the operator for a seed. A compiled program has
937+
nobody to ask, so it takes the clock.
938+
862939
### String Functions
863940

864941
| Function | Description |
865942
|-----------------------|------------------------------------------------|
866943
| `LEN(s$)` | Length of string |
944+
| `DATE$` | Current date, as `MM-DD-YYYY` |
945+
| `TIME$` | Current time, as `HH:MM:SS` |
867946
| `LEFT$(s$, n)` | Leftmost n characters |
868947
| `RIGHT$(s$, n)` | Rightmost n characters |
869948
| `MID$(s$, start, len)`| Substring (1-based index) |
@@ -1283,20 +1362,30 @@ Each of these is practical on both Linux and Windows and simply has not been
12831362
written. Programs using them are refused today.
12841363

12851364
- **Error trapping** -- `ON ERROR GOTO`, `RESUME`, `RESUME NEXT`, `ERR`, `ERL`, `ERROR`
1286-
- **Console control** -- `LOCATE`, `COLOR`, `WIDTH`, `CSRLIN`, `POS`, `VIEW PRINT`, `INKEY$`, `BEEP`, `SLEEP`
1287-
- **Date and time** -- `DATE$`, `TIME$`
1365+
- **Console control** -- `WIDTH`, `CSRLIN`, `VIEW PRINT`, `INKEY$`, `BEEP`, `SLEEP`
12881366
- **Operating system** -- `SHELL`, `ENVIRON$`, `KILL`, `NAME`, `FILES`, `CHDIR`, `MKDIR`, `RMDIR`
1289-
- **Odds and ends** -- `RANDOMIZE`, `ERASE`, `INPUT$`, `FRE`, `SHARED`, `STATIC`
1290-
- **`DEFINT` and friends** -- `DEFINT`, `DEFLNG`, `DEFSNG`, `DEFDBL`, `DEFSTR`; use a
1291-
type suffix or `DIM ... AS`
1367+
- **Odds and ends** -- `INPUT$`, `SHARED`, `STATIC`
1368+
1369+
### Out of scope
1370+
1371+
**Graphics** -- `SCREEN`, `PSET`, `PRESET`, `LINE` in its graphics form,
1372+
`CIRCLE`, `DRAW`, `PAINT`, `POINT`, `VIEW`, `WINDOW`, `PALETTE`, `PMAP`. These
1373+
need a display this compiler does not provide.
1374+
1375+
**The 8086's machine** -- `PEEK`, `POKE`, `DEF SEG`, `VARPTR`, `VARPTR$`, `USR`,
1376+
`INP`, `OUT`, `WAIT`, `BLOAD`, `BSAVE`. There is no fixed address worth naming
1377+
in a 64-bit hosted program: the video buffer is not memory, addresses are
1378+
randomised, and the pages are protected. A `POKE` that appeared to work would be
1379+
the worst outcome available, so these are refused rather than emulated.
1380+
1381+
**Commands to the interpreter** -- `RUN`, `LIST`, `LOAD`, `SAVE`, `MERGE`,
1382+
`NEW`, `EDIT`, `RENUM`, `AUTO`, `CONT`, `DELETE`, `TRON`, `TROFF`, `CLEAR`.
1383+
These operate on program text that a compiled program no longer has.
12921384

1293-
### Never
1385+
**Program chaining** -- `CHAIN` and `COMMON` need separate compilation.
12941386

1295-
Graphics, sound, joysticks, light pens, direct memory access, port I/O, the
1296-
line printer, and the interpreter's own commands (`RUN`, `LIST`, `CHAIN`, ...)
1297-
are permanent non-goals: they describe a machine and a way of working that a
1298-
compiled 64-bit program does not have. **[NONGOALS.md](NONGOALS.md)** gives the
1299-
full list and the reasoning.
1387+
Everything else GW-BASIC provides that is missing here is listed above as not
1388+
yet implemented. Each refused name says which of the two it is.
13001389

13011390
### Structural
13021391

0 commit comments

Comments
 (0)