Skip to main content
21 events
when toggle format what by license comment
Jun 5, 2022 at 18:47 comment added cjs Sure, at 21 cycles per byte, on a .75 MHz machine you could copy only 350 or so bytes without running the risk of losing a character at 9600 bps. But who was sending input that would cause a copy at 9600 bps? (Program loads were done in line order, and you needed to copy only for inserts. And a 9600 bps program load might overrun the tokenizer, anyway; I've seen it happen at 19.2k on faster machines.) Who even had the capability for 9600 bps input at the time anyway?
Jun 5, 2022 at 18:29 comment added supercat @cjs: Disabling data for the entire duration of a copy operation seems dodgy. I wonder if code might have been faster and safer if it used the B register to count up to 256 iterations at once, and after each group of 256 bytes restored the stack, enabled interrupts, and checked whether it was necessary to process any more groups?
Jun 5, 2022 at 18:22 comment added cjs @supercat In Altair 680 BASIC it's done using the SP, though it attempts to avoid trashing it. See my disassembly here
Oct 31, 2019 at 18:40 history edited Maury Markowitz CC BY-SA 4.0
added 69 characters in body
Oct 29, 2019 at 13:02 history edited Maury Markowitz CC BY-SA 4.0
added 139 characters in body
Jul 29, 2019 at 14:05 comment added supercat @MauryMarkowitz: The fact that the sound of the whine changes noticeably with each power of two reached by the loop index suggests that the time required isn't trivial. In Commodore Basic (and I think other MS derivatives) the loop step and limits are computed once at the start of the loop and kept on the stack, so they don't need to get processed from the source each time. Certainly for many purposes, having a "fast case" to handle parsing of numbers up to 65535 would be helpful as well.
Jul 29, 2019 at 12:32 comment added Maury Markowitz So to strings... I'm not sure I understand your proposal. What is the non-zero byte at the front, a character or a flag/token?
Jul 29, 2019 at 12:28 comment added Maury Markowitz A big advantage to that approach is that you can pick and choose which ops you want the int package to have. So maybe just + and - and a handful of functions. Leave out things line SIN and you'll automatical failover lines with that code to the FP side at edit-time. And then you parse any constants into your FP format and leave them in that form in memory. IIF everything is int, call your FP-to-INT on the constants and save a few bytes and some copying time.
Jul 29, 2019 at 12:23 comment added Maury Markowitz @supercat - the 16-bit issue is one of the first I looked at, but in MS-derived versions, parsing the line takes longer than running it (so I've been told) so the actual savings is not huge. However, if you pre-tokenize, like Atari BASIC or Basic09, then the parsing goes ~= 0 and an int math package makes sense. You can even decide at edit-time - if everything on the RHS of the = is int and the ops have int versions, replace their tokens with the int equivalents and the = with "assign int to FP var", an invisible INT().
Jul 27, 2019 at 17:15 comment added supercat ...to be worthwhile.
Jul 27, 2019 at 17:15 comment added supercat @MauryMarkowitz: Newer strings would be stored lower in the heap, and since newer strings are more likely to be abandoned, the size of the region that could be processed in each pass would tend to increase significantly with succeeding passes. An alternative approach which would be slightly less storage-efficient would be to precede every string with a "spare" byte, not used for storing the length; this would have the advantage of allowing the GC to count up in advance the total size of all strings, which would allow it to stop if future GC passes wouldn't free up enough space...
Jul 27, 2019 at 17:07 comment added supercat ...that is not entirely within the first N bytes. All objects which were entirely within the bottom N bytes could have their references updated in that same pass. At the end of the pass, the GC could move the object that wasn't entirely within that bottom region and update references to it. This algorithm could then be repeated until a reasonable amount of space had been freed up, and once that was done all objects could be moved en masse back against the top of the used portion of the heap, and all references updated by adding a suitable offset to their addresses.
Jul 27, 2019 at 17:03 comment added supercat @MauryMarkowitz: A design improvement which could have a really big payoff for garbage collection would be storing string literals as a "string literal token" followed by a length byte, and having every string on the heap be a minimum of three bytes, the first of which is non-zero. If GC was triggered when heap storage was down to some non-zero N, then the GC could in one pass relocate every object which is entirely within the bottom N bytes of the heap into the empty space, replacing it with a zero byte and the new address, and identify the lowest object...
Jul 27, 2019 at 16:46 comment added supercat @MauryMarkowitz: I think the biggest "easy win" would be to modify the floating-point code so that if the exponent bytes of all operands were zero, it would use integer math for values up to 65,535, and otherwise any values with zero exponents would get converted to floating-point and processed normally. On my original C64 back in the day, I remember noticing that "FOR I=1 to 10000:NEXT" would produce a whine which decreased in pitch each time I passed a power of two, which implies that the code was spending a lot of time on normalization and denormalization.
Jul 27, 2019 at 12:10 comment added Maury Markowitz Hey Kurt, as I continued working on it I found it converging on QuickBASIC from a conceptual standpoint. Basically the two key concepts that any reasonably fast BASIC needs is to pre-parse the line so that doesn’t happen at runtime, and some smarts when dealing with numeric formats. But a lot of time is spend in line number lookups, for/next and doing really basic math like +1 in a loop. But I’d be happy to send you what I have although it’s based on an understanding of Atari basic.
Jul 26, 2019 at 3:26 comment added cjs @MauryMarkowitz Do you have this collection of ideas documented anywhere that's publicly available, such as a text file in a GitHub repo or something like that?
May 29, 2018 at 16:02 comment added Maury Markowitz @supercat - I have never seen the original 6800 source, only the 6502 port. Your musings on strings are VERY interesting to me. I am not an assembly programmer, but I have collected ideas wide and far that seem to present the possibility of major performance improvements to the 6502 code. I'm adding yours!
May 18, 2018 at 20:43 comment added supercat Do you know how the Altair 6800 BASIC did its memory-copy loops? The 6800 isn't amenable to doing anything very fast; I'd guess a self-modifying loop: lda extended / sta 0,x / inx / inc lsb_of_extended / inc b / bne loop" would be best, but wouldn't be at all surprised if the BASIC does something like ldx src / lda 0,x / inx / stx src / ldx dest / sta 0,x / inx / stx dest`. I really don't see any good way to do things without using self-modifying code or trashing SP.
May 18, 2018 at 18:50 comment added supercat ...of free storage would grow significantly after each GC pass, making the process closer to O(NlgN) than O(N*N). A key point, though, would be that as the GC relocated each string it would be replaced with a relocation marker containing a zero byte and the new address. If the first byte of each string is the length, and zero-byte strings are represented as null pointers, this works out nicely. If strings aren't prefixed, however, this works out less well.
May 18, 2018 at 18:49 comment added supercat I'm not actively working on any BASIC interpreters, though I've toyed with some conceptual designs. The garbage-collector design leaves perhaps the most room for improvement. If strings were preceded by length (or any other byte which is guaranteed non-zero) and took up a minimum of three bytes, then on each GC pass where one had N bytes of contiguous storage available, one could relocate into that region all strings that were within an N-byte region elsewhere and observe the address of the string (if any) which crosses out of it. In most usage cases, the amount...
May 18, 2018 at 18:29 history answered Maury Markowitz CC BY-SA 4.0