Load a program that has no data
A five instruction program that writes one port and exits has no Data Segment at all, and the loader stopped the machine dead on it. It asked the memory controller to move a segment of no bytes, and a length of zero asks for the whole 64K - which is the machine's rule, and a reasonable one, since two bytes cannot say 65536 and a transfer of nothing is not usually what anybody meant. It is exactly what was meant here. 64K did not fit, the controller refused, and the load stopped half done. ON A TERMINAL THAT PRINTS A FAULT WITH AN ADDRESS. Behind a window it is a frozen picture and no reason at all, which is how it was found and is a separate problem from this one. The header says how long each segment is, so the loader knows before it asks. Both bytes are already in hand, so the test costs one OR. Nothing is lost by skipping the transfer: a blit leaves the controller's addresses past whatever it touched, and a blit of nothing would have left them where they already are, which is where the vectors are read from next. Guarded for the code segment too. A program with no code is equally assemblable and would have stopped in exactly the same place. Mode.sbx is the fix's test and a program worth having on its own: forty columns or eighty, whichever the screen is not in, which is what a person wanting Snake drawn twice the size actually needs. Ten instructions and no data, deliberately - it prints its two digits a register at a time rather than from a string, so it stays the smallest shape a loadable program can take. Nothing else on that disk had ever been that shape, which is why nothing had ever tried it. Reported by the user, who wrote the program. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
373454ec00
commit
81e544eb3d
@@ -0,0 +1,50 @@
|
||||
; Forty columns or eighty, whichever the screen is not in.
|
||||
;
|
||||
; A machine wakes up in the forty column mode and CosmOS asks for eighty, because that is
|
||||
; what its own output was written for. A game is the other way round: Snake on a forty column
|
||||
; screen is the same board drawn twice the size, which is what a person sitting in front of
|
||||
; it actually wants.
|
||||
;
|
||||
; ---- The smallest program this system can load ----
|
||||
;
|
||||
; Ten instructions and NO DATA AT ALL, which is not a curiosity: it is the shape that found
|
||||
; a bug in the loader. Every program written for CosmOS until this one had something in its
|
||||
; Data Segment, so the loader had never been asked to move a segment of no bytes - and a
|
||||
; length of zero asks the memory controller for the whole 64K, which does not fit, which it
|
||||
; refused, which stopped the machine in the middle of loading. On a terminal that printed a
|
||||
; fault. Behind a window it looked exactly like a hang.
|
||||
;
|
||||
; So this is kept dataless on purpose. The two digits it prints are put in a register one at
|
||||
; a time rather than being a string, which is the only reason it can say anything at all.
|
||||
;
|
||||
; Written by Anachronaut
|
||||
|
||||
#Include services.asm
|
||||
|
||||
#Program
|
||||
|
||||
#Base 0x4000
|
||||
|
||||
start:
|
||||
INA 0x31 ; Which mode the screen is in now.
|
||||
BRA modeWide ; Nought is the forty column one, so go the other way.
|
||||
|
||||
; Anything else becomes forty, and that deliberately includes bitmap mode. A program that
|
||||
; left the screen with no text on it left nowhere to print, so coming back to a mode that
|
||||
; has characters in it is more use than refusing.
|
||||
RSTA
|
||||
OUTA 0x31
|
||||
INIA 0x34 ; '4'
|
||||
BRI modeSay
|
||||
modeWide:
|
||||
INIA 0x01
|
||||
OUTA 0x31
|
||||
INIA 0x38 ; '8'
|
||||
modeSay:
|
||||
OUTA 0x00
|
||||
INIA 0x30 ; '0'
|
||||
OUTA 0x00
|
||||
INIA 0x0A
|
||||
OUTA 0x00
|
||||
RSTA
|
||||
SWI osExit
|
||||
@@ -572,6 +572,8 @@ from every assembly file in it. Several are old programs written for the bare ma
|
||||
| Compare | Compares two files a block at a time, stopping at their real tails rather than comparing unused bytes in the final disk blocks. |
|
||||
| Wander | Goes to the directory it is given and reads a file there by a bare name. The only thing that moves the machine from inside a program, and so the only thing that can check the shell puts the working directory back afterwards. |
|
||||
| More | A forward-only pager. Space advances a screen, Return one line, and q stops. |
|
||||
| Press | Says what the console handed it, in hexadecimal and by name. It reads a line and then keys, because the keys that are not characters are dropped in line mode and delivered in key mode, and both halves of that rule want showing. |
|
||||
| Mode | Forty columns or eighty, whichever the screen is not in. Ten instructions and no data at all, which is the point of it: it is the smallest shape a loadable program can take, and the loader used to stop the machine dead on one. |
|
||||
|
||||
### The Monitor:
|
||||
|
||||
|
||||
@@ -1548,11 +1548,29 @@ loadVersionKnown:
|
||||
LDA.0
|
||||
OUTA 0xE6
|
||||
INCD.0
|
||||
LDA.0
|
||||
OUTA 0xE7
|
||||
LDB.0
|
||||
OUTB 0xE7
|
||||
|
||||
; ---- Nothing to move is not the same as everything to move ----
|
||||
;
|
||||
; A LENGTH OF ZERO ASKS THE CONTROLLER FOR THE WHOLE 64K, which is the machine's rule and
|
||||
; a sensible one for a length somebody typed: two bytes cannot say 65536, and a transfer
|
||||
; of no bytes is not usually what anybody meant. It is exactly what is meant here. A
|
||||
; segment can genuinely be empty - a five instruction program that only writes to a port
|
||||
; has no data at all - and this asked to move 65536 bytes into a bank that has not got
|
||||
; them, so the controller refused and the machine stopped in the middle of loading.
|
||||
;
|
||||
; The header says how long the segment is, so the loader knows before it asks. It has
|
||||
; both bytes of the length in hand here, so testing them costs one instruction.
|
||||
;
|
||||
; NOTHING IS LOST BY SKIPPING IT. A blit leaves the controller's addresses past whatever
|
||||
; it touched, and a blit of nothing would have left them exactly where they are.
|
||||
OR
|
||||
BRQ loadNoCode
|
||||
|
||||
INIA 0x01
|
||||
OUTA 0xE8 ; Blit.
|
||||
loadNoCode:
|
||||
|
||||
; Then the data. A blit leaves its addresses past whatever it touched, so the source is
|
||||
; already sitting on the first byte of the data and only the destination changes.
|
||||
@@ -1571,11 +1589,16 @@ loadVersionKnown:
|
||||
LDA.0
|
||||
OUTA 0xE6
|
||||
INCD.0
|
||||
LDA.0
|
||||
OUTA 0xE7
|
||||
LDB.0
|
||||
OUTB 0xE7
|
||||
|
||||
; And the same for the data, which is the segment that is actually empty in practice.
|
||||
OR
|
||||
BRQ loadNoData
|
||||
|
||||
INIA 0x01
|
||||
OUTA 0xE8 ; Blit.
|
||||
loadNoData:
|
||||
|
||||
; ---- The vectors it brought ----
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user