diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 8efeb37..ea74410 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -633,6 +633,13 @@ returned to the prompt, and the program is recorded as having stopped rather tha A fault *below* where programs load is the system's own code, and there is nothing to go back to. That one says so and stops. +**The glyphs and the colours come back too.** A program is as free to redefine a letter as +any other tile, so one that did has left the shell unable to spell, and one that wrote its +own palette used to hand back green text on blue. Both were permanent until the video device +grew a character generator to ask. The shell asks for both whenever a program exits, before +restoring a saved screen, so a program that saved one still gets back what was actually on +it and a program that saved nothing at least leaves something readable. + **The screen is put back into a mode text can be seen in first**, and that is the part that matters most rather than the part that is prettiest. A program that faulted in bitmap mode left the console with no text rows at all, so it draws nothing - the message about what went diff --git a/Programs/CosmOS/Source/cosmos.asm b/Programs/CosmOS/Source/cosmos.asm index 5a89e55..9441bb4 100644 --- a/Programs/CosmOS/Source/cosmos.asm +++ b/Programs/CosmOS/Source/cosmos.asm @@ -1442,6 +1442,12 @@ faultScreen: ; have left every ink the same as every paper. Attribute one draws in palette entries 16 ; and 17, so those two are written and the rest of the program's colours are left alone - ; there is nothing to be gained here by taking away more than is needed. + ; The glyphs first, since a program is as free to redefine a letter as any other tile and + ; a message spelled in somebody's tile graphics is no message at all. It touches only the + ; console's own tiles, so a program's are left where they are. + INIA 0x01 + OUTA 0x39 + CALL screenBank INIA 0d4 OUTA 0xE3 @@ -3177,6 +3183,20 @@ screenSane: RSTA OUTA 0x37 OUTA 0x38 ; No fraction of a cell in either direction. + + ; ---- And glyphs and colours that can be read ---- + ; + ; A program may redefine any tile and any colour, and the font lives in tiles like anything + ; else - so a program that redefined a letter has left the shell unable to spell, and one + ; that wrote its own palette handed back green text on blue. Both used to be permanent: + ; there was nowhere to get the originals from, because the only copy was the one that had + ; been drawn over. The device has a character generator now and this is what it is for. + ; + ; BEFORE screenGive, like everything else here, so that a program which SAVED the screen + ; gets back what was actually on it rather than what a clean machine looks like. This is + ; the floor for the programs that saved nothing. + INIA 0x03 + OUTA 0x39 RET ; Video memory as bank 4, which is what makes it reachable at all. Bank 3 is the disk's; see diff --git a/Programs/Examples/tune.asm b/Programs/Examples/tune.asm index 59ddcec..fbd513a 100644 --- a/Programs/Examples/tune.asm +++ b/Programs/Examples/tune.asm @@ -37,6 +37,10 @@ #Program +#Include services.asm + +#Base 0x4000 + start: ; ---- The instrument ---- @@ -182,7 +186,7 @@ lastRing: CIF RSTA OUTA 0x35 - HALT + SWI osExit ; Sixty times a second, and it has nothing to do. WAIT only needs something to have happened, ; and this is the something. A handler still has to exist: an interrupt with nothing installed @@ -192,6 +196,8 @@ frame: #Data +#Base 0x2000 + ; ---- Notes and how long they last ---- ; ; Pairs: a MIDI note, then a count of frames. 60 is middle C and every 12 is an octave. A zero diff --git a/Source/Emulator/video.c b/Source/Emulator/video.c index d91e739..08f41f0 100644 --- a/Source/Emulator/video.c +++ b/Source/Emulator/video.c @@ -119,11 +119,15 @@ void videoTick(unsigned long now) { } void videoLoadFont(void) { - // One bit a pixel becomes one byte a pixel: index 1 where the font has a dot and 0 - // where it does not, which is what makes the two palette entries below mean ink and - // paper. Glyphs the font does not have are left blank rather than left as whatever was - // in tile memory. - memset(videoRAM + VIDEO_TILE_BASE, 0, (size_t)VIDEO_TILE_COUNT * VIDEO_TILE_BYTES); + // One bit a pixel becomes one byte a pixel: index 1 where the font has a dot and 0 where + // it does not, which is what makes the two palette entries of a scheme mean ink and + // paper. + // + // ONLY THE GLYPHS THE ROM HAS. Tile memory used to be cleared first, on the grounds that + // a glyph the font does not have should be blank rather than whatever was there - which + // was fine while this happened at reset and nothing else, and is wrong now that a program + // can ask for it. A program that defined a tile of its own above the font and then wanted + // its text back would have lost the tile to get it. for (int glyph = 0; glyph < CONSOLE_FONT_GLYPHS && glyph < VIDEO_TILE_COUNT; glyph++) { uint8_t *tile = videoRAM + VIDEO_TILE_BASE + glyph * VIDEO_TILE_BYTES; for (int y = 0; y < CONSOLE_FONT_BYTES; y++) { @@ -133,6 +137,9 @@ void videoLoadFont(void) { } } } +} + +void videoLoadPalette(void) { uint8_t *palette = videoRAM + VIDEO_PALETTE_BASE; for (int bank = 0; bank < 8; bank++) { // Colour on black, and then the same colour as paper with black ink, sixteen banks @@ -187,9 +194,11 @@ void videoReset(void) { frameWaiting = 0; frameInterrupts = 0; clearInterrupt(PORT_VIDEO); - // A machine wakes up able to show text. Everything here is ordinary video memory that a - // program may overwrite the moment it wants the screen for something else. + // A machine wakes up able to show text, and it does so by COPYING from the character + // generator into ordinary video memory - which a program may overwrite the moment it + // wants the screen for something else, and can ask back afterwards. videoLoadFont(); + videoLoadPalette(); } uint8_t *videoMemory(uint32_t *capacity) { @@ -235,6 +244,25 @@ uint8_t videoWrite(uint8_t value, uint8_t port) { case VIDEO_FINE_Y: fineY = (uint8_t)(value & VIDEO_FINE_MASK); break; + case VIDEO_COMMAND: + // ---- Asking the character generator for its contents ---- + // + // Written, and it happens at once - the same shape as the console's Command port + // and the controller's, rather than a bit in a register that otherwise holds + // state. There is nothing to read back: what a copy did is visible in the memory + // it copied into. + // + // IN BITMAP MODE THE TILES ARE THE PICTURE, so asking for the font there draws + // glyphs across the top of it. That is not a special case being ignored; it is + // what the memory means in that mode, and a caller that wants text has to be in + // a mode that has some. + if (value & VIDEO_COMMAND_FONT) { + videoLoadFont(); + } + if (value & VIDEO_COMMAND_PALETTE) { + videoLoadPalette(); + } + break; default: // Everything else is read only or not there yet. Writing does nothing rather // than refusing: a port block reserved for later should be quiet, not fatal. @@ -272,6 +300,10 @@ uint8_t videoRead(uint8_t port) { case VIDEO_SCROLL_COLUMN: return scrollColumn; case VIDEO_FINE_X: return fineX; case VIDEO_FINE_Y: return fineY; + case VIDEO_COMMAND: + // Write only, like the console's. A device that does something when told does + // not take instructions and hand out state through the same hole. + return 0; // Asked rather than assumed. A program that wants to know how wide the screen is // should be able to find out, the same way it asks the console what mode it is in. case VIDEO_COLUMNS: return (uint8_t)columnsFor(mode); diff --git a/Source/Emulator/video.h b/Source/Emulator/video.h index c2a8a0a..5c50709 100644 --- a/Source/Emulator/video.h +++ b/Source/Emulator/video.h @@ -124,6 +124,31 @@ int videoTextRows(void); #define VIDEO_FINE_X 0x37 #define VIDEO_FINE_Y 0x38 +// ---- The character generator, which is a chip and not a memory that remembers ---- +// +// The font and the sixteen colour schemes used to be WRITTEN INTO VIDEO RAM at reset, and +// that was the one piece of magic left in this device: RAM does not wake up with anything +// in it. It looked harmless until something needed the font BACK - a program that redefines +// a tile has overwritten a glyph, and there was nowhere to get it from, because the only +// copy was the one that had just been drawn over. +// +// So the device has a ROM, the way the machines this one is pretending to be really did, +// and the copy into RAM is a thing it DOES rather than a state it mysteriously starts in. +// Reset performs it, and a program can ask for it again. +// +// THE RAM IS STILL RAM. A program may overwrite every glyph and every colour, and should be +// able to: that is what makes a tile engine a tile engine rather than a text display. What +// has changed is that doing so is no longer a one way door. +// +// A SYSTEM THAT WANTS ITS OWN FONT still loads one over the top. This is the floor, not the +// policy - it is what makes a machine with no disk able to say so, and what lets a program +// with no system behind it put readable text on a screen. +#define VIDEO_COMMAND 0x39 +// Copy the 135 glyphs back into the tiles they live in, leaving every other tile alone. +#define VIDEO_COMMAND_FONT 0x01 +// Copy the sixteen ink and paper pairs back, leaving the rest of the palette alone. +#define VIDEO_COMMAND_PALETTE 0x02 + // Eight pixels to a cell, so three bits say where inside one the view begins. #define VIDEO_FINE_MASK 0x07 @@ -163,7 +188,16 @@ void videoReset(void); // // The font is expanded into tile memory at reset rather than stored expanded: 1,088 bytes // of one-bit rows against 16 kilobytes of tiles. +// Copies the character generator ROM into the tiles the console draws from, and the default +// schemes into the palette entries they live in. Reset does both; the Command port is how a +// program asks for either afterwards. +// +// NEITHER CLEARS WHAT IT DOES NOT OWN. The font writes glyphs 0 to 134 and stops, so a tile +// a program defined above them survives; the palette writes the two entries of each of the +// sixteen schemes and stops, so a program's own colours in between survive. Asking for the +// font back should not cost a program the tile it was drawing with. void videoLoadFont(void); +void videoLoadPalette(void); // ---- The cursor ---- // diff --git a/SplitBit Programming Manual.md b/SplitBit Programming Manual.md index 9fc2c4b..b4a33bb 100644 --- a/SplitBit Programming Manual.md +++ b/SplitBit Programming Manual.md @@ -735,6 +735,24 @@ The alternative was to let a write of 8 step the column and set the fine part to **None of the four does anything in bitmap mode**, which has no map to slide. +### The Character Generator: + +The font and the sixteen colour schemes come from a **ROM in the device**. Reset copies them into video memory, and Command port `0x39` copies them again on request: + +| Port | Register | +| --- | --- | +| 0x39 | Command. Bit 0 asks for the font back, bit 1 for the sixteen schemes. Write only. | + +**This used to be magic and now is not.** The glyphs were written into video RAM at reset and existed nowhere else, which looked harmless until something wanted the font *back*: RAM does not wake up with anything in it, and a program that redefined a glyph had destroyed the only copy there was. A machine with a character generator is what the machines this one is pretending to be actually had, and the copy into RAM is now a thing the device **does** rather than a state it mysteriously starts in. + +The RAM is still RAM. A program may overwrite every glyph and every colour and should be able to - that is what makes this a tile engine rather than a text display. What changed is that doing so is no longer a one way door. + +**Neither command clears what it does not own.** The font writes the 135 glyphs it has and stops, so a tile a program defined above them survives; the schemes write the two entries of each of the sixteen and stop, so a program's own colours in between survive. Asking for the font back must not cost a program the tile it was drawing with. + +In bitmap mode the tiles *are* the picture, so asking for the font there draws glyphs across the top of it. That is not a case being ignored: it is what the memory means in that mode. + +A system that wants a different font still loads one over the top. The ROM is the floor rather than the policy - it is what lets a machine with no disk say that it has no disk, and what lets a program with no system behind it put readable text on a screen. + ### Writing On The Screen: A console on a machine with a screen sends every byte to both, because a machine with a screen and a serial line is an ordinary machine and there is one console driving both. diff --git a/Tests/video.sh b/Tests/video.sh index b41e2ad..e5a42e4 100755 --- a/Tests/video.sh +++ b/Tests/video.sh @@ -73,6 +73,16 @@ poke() { $(( ($1 >> 8) & 0xFF )) $(( $1 & 0xFF )) $(( $2 & 0xFF )) } +# Many bytes from one address, using the Data port's own stepping rather than naming the +# address again for each. What a run of tile memory is for. +pokeRun() { + # pokeRun