--- title: "DATAGEN" id: 70998 type: "computer_media" slug: "datagen" url: "http://localhost/computer_media/datagen/" markdown_url: "http://localhost/computer_media/datagen.md" published_at: "2026-08-25T13:57:10+00:00" modified_at: "2026-08-25T14:07:22+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2022/04/20230809-041332.jpg" excerpt: "A self-removing utility that interactively builds a DATA statement by writing bytes directly into the BASIC program area — no editor needed." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/DATAGEN%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" media_type_tags: "Utility" --- # DATAGEN DATAGEN is a utility that generates a DATA statement in memory by directly POKEing bytes into the BASIC program area. It prompts the user for the desired number of bytes and a target line number, then calculates available space within the existing REM-filled program structure to house the new DATA line. The program navigates the BASIC line-length bytes stored in the program area to locate a suitable address, writes the line number, length, and DATA keyword token byte (228) directly, and fills in quoted string entries as the user inputs them one at a time. After entry is complete, it can optionally delete itself while leaving the generated DATA line intact, making it useful as a self-removing code-generation tool. *** ### Program Structure The program is divided into two distinct regions. Lines `1` through `493` consist entirely of empty `REM` statements, which serve as a large contiguous block of pre-allocated program space. This space is reserved so that a new DATA line can be constructed inside it at runtime. The active logic begins at line `9972` and runs through `9999`. ### Purpose of the REM Block The 493 consecutive empty `REM` lines are not decorative — they are the raw material. The BASIC interpreter stores each line as a header (line number, 2 bytes; length, 2 bytes) followed by a token byte and a newline. By scanning the program area with `PEEK`, the code locates the first REM-region address that provides enough contiguous space to hold `n` bytes of DATA content. The REM lines thus act as a pool of reserved memory that the utility carves a DATA line out of. ### Key Variables | Variable | Purpose | | --- | --- | | `n` | Number of bytes requested for the DATA statement | | `l` | Target BASIC line number for the new DATA line (must be < 9997) | | `prog` | Start address of the BASIC program, read from system variables at addresses 23635–23636 | | `adr` | Crawling pointer used to walk the BASIC line-length chain | | `ad` | Final address marking the end of the available space block | | `bytes` | Actual byte count available for the DATA line content | | `d$` | Each string entry typed by the user | | `z` | Loop counter for POKEing individual characters | ### Memory Navigation Technique Lines `9978`–`9982` implement a walk through the BASIC program’s line-length chain. The system variable pair at addresses 23635–23636 gives the start of the BASIC program. From there, each line’s length is stored as a little-endian 16-bit word at offsets +2 and +3 from the line start (offset +3 is the low byte and +4 the high byte as used here, accounting for the 2-byte line number preceding the length). The loop at line `9980` advances `adr` by `4 + PEEK(adr+3) + 256*PEEK(adr+4)` until it finds a position where `prog+5+n < adr`, meaning the preceding block is large enough to hold the new DATA line. ### Direct POKE Construction of a BASIC Line Once the target address is found, the program constructs a valid BASIC line entirely through POKE operations: 1. Line `9983`: POKEs the high and low bytes of the new line number (`l`) into `prog` and `prog+1`. 2. Line `9984`: Calculates the line body length (`bytes = ad - prog - 3`) and writes it as a little-endian word to `prog+2` and `prog+3`. 3. Line `9985`: POKEs token byte `228` (the `DATA` keyword) at `prog+4`. 4. Lines `9988`–`9993`: Each user-entered string is surrounded by quote characters (ASCII 34) and followed by a comma (ASCII 44), written byte-by-byte with POKE. ### User Interaction Loop Lines `9988`–`9993` form the data-entry loop. Each iteration opens a quoted string slot (POKE 34), reads `d$` via `INPUT`, checks whether the entry fits in the remaining space, POKEs each character individually with a `FOR`/`NEXT` loop, then writes closing quote (34) and comma (44). Entering `end` or `END` exits the loop. A BEEP pair signals each prompt. ### Self-Deletion Mechanism After the DATA line is built, line `9995` prompts the user to press D to delete the DATAGEN program itself. Lines `9997` and `9998` use a pair of bare numeric arguments — a known technique where a line beginning with just a number causes the interpreter to delete the line with that number — to remove lines `9996` and `9997` from the program. Meanwhile, the new DATA line at its user-chosen line number (less than 9997) remains intact, effectively leaving only the DATA statement behind. ### Notable Idioms and Techniques - Reading PROG from system variables: `PEEK 23635 + 256 * PEEK 23636` — standard Spectrum system variable access. - `POKE 23561,200` and `POKE 23609,100` at line `9976` adjust the editor margin and RAMTOP-related settings to give INPUT more display room. - The boundary check `IF NOT (l<9997)` at line `9977` ensures the new line number does not collide with the utility’s own line range. - Line `9994` fills unused space at the end of the allocated block with spaces (ASCII 32) to sanitize leftover REM data. - The fit check at line `9990` — `adr + LEN d$ + 4 > ad` — prevents overwriting memory beyond the reserved block. ### Bugs and Anomalies The line-length byte-order used in lines `9980` and `9984` treats offset +3 as the low byte and +4 as the high byte of the length word, which is consistent with the TS2068 BASIC line format (line number high, line number low, length low, length high — noting the 1-based offset from the line start as the pointer is decremented by 1 at line `9979`). The arithmetic is internally consistent though non-obvious without careful tracing. The final DATA line will contain a trailing comma after the last entry because the comma is written unconditionally in line `9992` before the loop checks for `end`; this trailing comma after the last quoted value could cause a syntax issue depending on how the DATA line is later consumed. ## Source Code ``` 1 REM 2 REM 3 REM 4 REM 5 REM 6 REM 7 REM 8 REM 9 REM 10 REM 11 REM 12 REM 13 REM 14 REM 15 REM 16 REM 17 REM 18 REM 19 REM 20 REM 21 REM 22 REM 23 REM 24 REM 25 REM 26 REM 27 REM 28 REM 29 REM 30 REM 31 REM 32 REM 33 REM 34 REM 35 REM 36 REM 37 REM 38 REM 39 REM 40 REM 41 REM 42 REM 43 REM 44 REM 45 REM 46 REM 47 REM 48 REM 49 REM 50 REM 51 REM 52 REM 53 REM 54 REM 55 REM 56 REM 57 REM 58 REM 59 REM 60 REM 61 REM 62 REM 63 REM 64 REM 65 REM 66 REM 67 REM 68 REM 69 REM 70 REM 71 REM 72 REM 73 REM 74 REM 75 REM 76 REM 77 REM 78 REM 79 REM 80 REM 81 REM 82 REM 83 REM 84 REM 85 REM 86 REM 87 REM 88 REM 89 REM 90 REM 91 REM 92 REM 93 REM 94 REM 95 REM 96 REM 97 REM 98 REM 99 REM 100 REM 101 REM 102 REM 103 REM 104 REM 105 REM 106 REM 107 REM 108 REM 109 REM 110 REM 111 REM 112 REM 113 REM 114 REM 115 REM 116 REM 117 REM 118 REM 119 REM 120 REM 121 REM 122 REM 123 REM 124 REM 125 REM 126 REM 127 REM 128 REM 129 REM 130 REM 131 REM 132 REM 133 REM 134 REM 135 REM 136 REM 137 REM 138 REM 139 REM 140 REM 141 REM 142 REM 143 REM 144 REM 145 REM 146 REM 147 REM 148 REM 149 REM 150 REM 151 REM 152 REM 153 REM 154 REM 155 REM 156 REM 157 REM 158 REM 159 REM 160 REM 161 REM 162 REM 163 REM 164 REM 165 REM 166 REM 167 REM 168 REM 169 REM 170 REM 171 REM 172 REM 173 REM 174 REM 175 REM 176 REM 177 REM 178 REM 179 REM 180 REM 181 REM 182 REM 183 REM 184 REM 185 REM 186 REM 187 REM 188 REM 189 REM 190 REM 191 REM 192 REM 193 REM 194 REM 195 REM 196 REM 197 REM 198 REM 199 REM 200 REM 201 REM 202 REM 203 REM 204 REM 205 REM 206 REM 207 REM 208 REM 209 REM 210 REM 211 REM 212 REM 213 REM 214 REM 215 REM 216 REM 217 REM 218 REM 219 REM 220 REM 221 REM 222 REM 223 REM 224 REM 225 REM 226 REM 227 REM 228 REM 229 REM 230 REM 231 REM 232 REM 233 REM 234 REM 235 REM 236 REM 237 REM 238 REM 239 REM 240 REM 241 REM 242 REM 243 REM 244 REM 245 REM 246 REM 247 REM 248 REM 249 REM 250 REM 251 REM 252 REM 253 REM 254 REM 255 REM 256 REM 257 REM 258 REM 259 REM 260 REM 261 REM 262 REM 263 REM 264 REM 265 REM 266 REM 267 REM 268 REM 269 REM 270 REM 271 REM 272 REM 273 REM 274 REM 275 REM 276 REM 277 REM 278 REM 279 REM 280 REM 281 REM 282 REM 283 REM 284 REM 285 REM 286 REM 287 REM 288 REM 289 REM 290 REM 291 REM 292 REM 293 REM 294 REM 295 REM 296 REM 297 REM 298 REM 299 REM 300 REM 301 REM 302 REM 303 REM 304 REM 305 REM 306 REM 307 REM 308 REM 309 REM 310 REM 311 REM 312 REM 313 REM 314 REM 315 REM 316 REM 317 REM 318 REM 319 REM 320 REM 321 REM 322 REM 323 REM 324 REM 325 REM 326 REM 327 REM 328 REM 329 REM 330 REM 331 REM 332 REM 333 REM 334 REM 335 REM 336 REM 337 REM 338 REM 339 REM 340 REM 341 REM 342 REM 343 REM 344 REM 345 REM 346 REM 347 REM 348 REM 349 REM 350 REM 351 REM 352 REM 353 REM 354 REM 355 REM 356 REM 357 REM 358 REM 359 REM 360 REM 361 REM 362 REM 363 REM 364 REM 365 REM 366 REM 367 REM 368 REM 369 REM 370 REM 371 REM 372 REM 373 REM 374 REM 375 REM 376 REM 377 REM 378 REM 379 REM 380 REM 381 REM 382 REM 383 REM 384 REM 385 REM 386 REM 387 REM 388 REM 389 REM 390 REM 391 REM 392 REM 393 REM 394 REM 395 REM 396 REM 397 REM 398 REM 399 REM 400 REM 401 REM 402 REM 403 REM 404 REM 405 REM 406 REM 407 REM 408 REM 409 REM 410 REM 411 REM 412 REM 413 REM 414 REM 415 REM 416 REM 417 REM 418 REM 419 REM 420 REM 421 REM 422 REM 423 REM 424 REM 425 REM 426 REM 427 REM 428 REM 429 REM 430 REM 431 REM 432 REM 433 REM 434 REM 435 REM 436 REM 437 REM 438 REM 439 REM 440 REM 441 REM 442 REM 443 REM 444 REM 445 REM 446 REM 447 REM 448 REM 449 REM 450 REM 451 REM 452 REM 453 REM 454 REM 455 REM 456 REM 457 REM 458 REM 459 REM 460 REM 461 REM 462 REM 463 REM 464 REM 465 REM 466 REM 467 REM 468 REM 469 REM 470 REM 471 REM 472 REM 473 REM 474 REM 475 REM 476 REM 477 REM 478 REM 479 REM 480 REM 481 REM 482 REM 483 REM 484 REM 485 REM 486 REM 487 REM 488 REM 489 REM 490 REM 491 REM 492 REM 493 REM 9972 REM FIRST:IF 4K is not enough spacethen the longest DATA line you need then EDIT 10 & change its line no. till you have enoughSECOND: MERGE this program 9974 REM GO TO 9999 TO SAVE all this THEN you can LOAD this or RUN LINE 9976 9976 CLS :POKE 23561,200:POKE 23609,100:INPUT "How many bytes do you need in the DATA statement? ";n 9977 INPUT "What will be the DATA statement LINE number (less then 9997) ";l:IF NOT (l<9997) THEN GO TO 9977 9978 PRINT AT 10,10;"Please wait":LET prog= PEEK 23635+256* PEEK 23636 9979 LET adr=prog-1 9980 LET adr=adr+4+ PEEK (adr+3)+256* PEEK (adr+4) 9981 IF prog+5+nad THEN CLS :PRINT AT 5,0;"""";d$;""" does not fit in this"'"DATA list.":GO TO 9994 9991 FOR z=1 TO LEN d$:LET adr=adr+1:POKE adr, CODE d$(z):NEXT z 9992 LET adr=adr+1:POKE adr,34:LET adr=adr+1:POKE adr,44 9993 GO TO 9988 9994 PRINT AT 10,10;"Please Wait":FOR z=adr-1 TO ad-1:POKE z,32:NEXT z 9995 BEEP .5,5:BEEP .5,-5:BEEP .5,5:BEEP .5,-5:PRINT AT 15,0;"Depress Letter D to DELETE this program. The DATA list will notbe Deleted." 9996 IF INKEY$ <>"d" AND INKEY$ <>"D" THEN GO TO 9996 9997 2,9996:POKE prog, INT (l/256):POKE prog+1,l-256* INT (l/256):CLS 9998 9997, 9999 SAVE "DATAGEN" LINE 9976 ```