Monitor: a line assembler
a <address>, then instructions until a line that is just a dot. The syntax is the assembler's own: a selector rides on the mnemonic as LDA.0 or LDD.0.1, and leaving one off means Data Pointer 0 exactly as it does in a source file, so nothing learned at the monitor has to be unlearned when writing a program. Case is folded, since the assembler does not care either. Numbers are hexadecimal and bare. A source file writes 0x2000 or 0d16 because it has both and must say which; a monitor has one and says so once, in the manual, rather than on every line. It reads the same table the disassembler does, searched the other way round, which is the point of it being a table rather than two lists: what a writes, d reads back, and neither can drift from the other or from the assembler both were generated from. Instruction lengths come from the shared shape table too, so the cursor cannot get out of step with what was written. THE WHOLE LINE IS UNDERSTOOD BEFORE ANYTHING IS WRITTEN. Emitting the opcode first and discovering a missing operand afterwards leaves half an instruction in memory, which the next line usually covers up and the last line of a session does not. Written that way first and fixed. What cannot be written is a label, and that is the whole difference between this and the assembler proper: a label is a promise to fill an address in later, and later is what a line at a time does not have. The recorded test now types in a complete program - a string poked into Data Memory, instructions assembled into Program Memory, and the result run - and includes a lower case mnemonic, both selector forms, an instruction that does not exist and one missing its value, so the refusals sit beside the successes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c23adb2836
commit
3b800a69e8
@@ -99,6 +99,7 @@ prompt rather than at the shell.
|
||||
| -- | -- |
|
||||
| `x [address]` | Display 64 bytes as hexadecimal and as characters. |
|
||||
| `d [address]` | Disassemble eight instructions. |
|
||||
| `a <address>` | Assemble instructions into memory until a line containing only a dot. |
|
||||
| `s <address> <byte>...` | Write bytes into the bank being examined, including Program Memory. |
|
||||
| `b <program\|data\|bank>` | Select a memory space or a registered bank number. |
|
||||
| `g <address>` | Begin execution at an address. |
|
||||
@@ -250,7 +251,7 @@ than merely checking its filesystem code against itself.
|
||||
|
||||
CosmOS is early software for an experimental computer. It runs one application at a
|
||||
time, has no privilege levels or process isolation, does not relocate applications, and
|
||||
does not yet provide a native assembler or linker. Its present purpose is to make
|
||||
provides a line assembler but not yet a native assembler for source files, nor a linker. Its present purpose is to make
|
||||
SplitBit usable from inside the machine: inspect it, manage persistent files, load
|
||||
programs, provide common services, and return reliably to a command prompt.
|
||||
|
||||
|
||||
@@ -179,6 +179,11 @@ promptSay:
|
||||
CALL textSame
|
||||
BRQ doGo
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 AsmName2
|
||||
CALL textSame
|
||||
BRQ doAssemble
|
||||
|
||||
promptUnknown:
|
||||
; Nothing matched. Saying which word was not understood is worth the four instructions:
|
||||
; it tells somebody who mistyped what they actually typed.
|
||||
@@ -1848,6 +1853,385 @@ findFound:
|
||||
ADD
|
||||
RET
|
||||
|
||||
|
||||
; ---- Assembling a line at a time ----
|
||||
;
|
||||
; a <address>, then instructions until a line that is just a dot. The syntax is the
|
||||
; assembler's - a selector rides on the mnemonic as LDA.0 or LDD.0.1, and leaving one off
|
||||
; means Data Pointer 0, exactly as it does in a source file - so nothing learned here has to
|
||||
; be unlearned when writing a real program.
|
||||
;
|
||||
; NUMBERS ARE HEXADECIMAL AND BARE. A source file writes 0x2000 or 0d16 because it has both
|
||||
; and must say which; a monitor has only one and says so once, in the manual, rather than on
|
||||
; every line. It is the same reason x and d take bare addresses.
|
||||
;
|
||||
; What cannot be written here is a label, and that is the whole difference between this and
|
||||
; the assembler proper: a label is a promise to fill an address in later, and later is what
|
||||
; a line at a time does not have.
|
||||
|
||||
doAssemble:
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
CALL textHexWord
|
||||
BNQ assembleWhat
|
||||
SETD.0 TextValue
|
||||
SETD.1 DumpAt
|
||||
CALL sbfsCopyWord ; Two bytes from DP0 to DP1, which sbfs already has.
|
||||
|
||||
assembleLine:
|
||||
SETD.0 DumpAt
|
||||
CALL printWordHex
|
||||
SETD.0 AsmPrompt
|
||||
CALL printString
|
||||
|
||||
SETD.0 AsmLine
|
||||
INIB 0d40
|
||||
CALL readLine
|
||||
|
||||
INA 0x01
|
||||
INIB 0x02 ; ENDED, so there is nothing more to assemble.
|
||||
AND
|
||||
BNQ prompt
|
||||
|
||||
SETD.0 AsmLine
|
||||
LDA.0
|
||||
BRA assembleLine ; An empty line is somebody thinking.
|
||||
|
||||
SETD.0 AsmLine
|
||||
SETD.1 DotText
|
||||
CALL textSame
|
||||
BRQ prompt
|
||||
|
||||
SETD.0 AsmLine
|
||||
CALL textSplit ; The mnemonic, and whatever follows it.
|
||||
CALL assembleOne
|
||||
BRI assembleLine
|
||||
|
||||
assembleWhat:
|
||||
SETD.0 AsmUsage
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI prompt
|
||||
|
||||
; The mnemonic is in CommandLine's place - AsmLine - and TextRest is what followed it.
|
||||
; Puts the bytes down and steps the cursor past them.
|
||||
assembleOne:
|
||||
CALL takeMnemonic
|
||||
CALL findByName
|
||||
BNQ assembleUnknown
|
||||
|
||||
; WHATEVER IT NEEDS IS READ BEFORE ANYTHING IS WRITTEN. Emitting the opcode first and
|
||||
; discovering the missing value afterwards leaves half an instruction in memory, which the
|
||||
; next line usually covers up and the last line of a session does not.
|
||||
SETD.0 AsmShape
|
||||
LDA.0
|
||||
BRA assemblePut ; 0, nothing to read.
|
||||
DECA
|
||||
BRA assembleWantAddress ; 1
|
||||
DECA
|
||||
BRA assembleWantByte ; 2
|
||||
DECA
|
||||
BRA assemblePut ; 3, a selector and nothing else.
|
||||
DECA
|
||||
BRA assembleWantByte ; 4
|
||||
DECA
|
||||
BRA assembleWantAddress ; 5
|
||||
BRI assemblePut ; 6, two selectors.
|
||||
|
||||
assembleWantByte:
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
CALL textHexWord
|
||||
BNQ assembleNeedsValue
|
||||
BRI assemblePut
|
||||
|
||||
assembleWantAddress:
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
CALL textHexWord
|
||||
BNQ assembleNeedsValue
|
||||
|
||||
assemblePut:
|
||||
; Point the controller at the cursor. Every byte written steps it on by itself.
|
||||
SETD.0 DumpBank
|
||||
LDA.0
|
||||
OUTA 0xE3
|
||||
SETD.0 DumpAt
|
||||
LDA.0
|
||||
OUTA 0xE4
|
||||
INCD.0
|
||||
LDA.0
|
||||
OUTA 0xE5
|
||||
|
||||
SETD.0 AsmOpcode
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
|
||||
; What follows depends only on the shape, exactly as it does when reading one back.
|
||||
SETD.0 AsmShape
|
||||
LDA.0
|
||||
BRA assembleDone ; 0
|
||||
DECA
|
||||
BRA assembleAddress ; 1
|
||||
DECA
|
||||
BRA assembleByte ; 2
|
||||
DECA
|
||||
BRA assembleSelector ; 3
|
||||
DECA
|
||||
BRA assembleSelByte ; 4
|
||||
DECA
|
||||
BRA assembleSelAddress ; 5
|
||||
|
||||
SETD.0 AsmSelOne
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
SETD.0 AsmSelTwo
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
BRI assembleDone
|
||||
|
||||
assembleSelector:
|
||||
SETD.0 AsmSelOne
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
BRI assembleDone
|
||||
|
||||
assembleSelByte:
|
||||
SETD.0 AsmSelOne
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
BRI assembleByte
|
||||
|
||||
assembleSelAddress:
|
||||
SETD.0 AsmSelOne
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
BRI assembleAddress
|
||||
|
||||
assembleByte:
|
||||
SETD.0 TextValue
|
||||
INCD.0
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
BRI assembleDone
|
||||
|
||||
assembleAddress:
|
||||
SETD.0 TextValue
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
INCD.0
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
|
||||
assembleDone:
|
||||
; Past what was just written. The length is the shape's, out of the same table the
|
||||
; disassembler reads, so the two can never disagree about how much room one takes.
|
||||
SETD.0 ShapeLength
|
||||
SETD.1 AsmShape
|
||||
LDB.1
|
||||
assembleStep:
|
||||
BRB assembleStepped
|
||||
INCD.0
|
||||
DECB
|
||||
BRI assembleStep
|
||||
assembleStepped:
|
||||
LDA.0
|
||||
SETD.0 DumpAt
|
||||
CALL stepCursor
|
||||
RET
|
||||
|
||||
assembleUnknown:
|
||||
SETD.0 NoSuchOp
|
||||
CALL printString
|
||||
CALL newLine
|
||||
RET
|
||||
|
||||
assembleNeedsValue:
|
||||
SETD.0 NeedsValue
|
||||
CALL printString
|
||||
CALL newLine
|
||||
RET
|
||||
|
||||
; DP0 is a two byte address and A is how far to move it on.
|
||||
stepCursor:
|
||||
DPUP.0 0d01
|
||||
LDB.0
|
||||
CCF
|
||||
ADD
|
||||
STQ.0
|
||||
DPDN.0 0d01
|
||||
LDA.0
|
||||
RSTB
|
||||
ADD
|
||||
STQ.0
|
||||
RET
|
||||
|
||||
; The typed mnemonic into four padded characters and up to two selectors, which is the shape
|
||||
; the table holds. Folded to upper case, because the assembler does not care about the case
|
||||
; of a mnemonic and neither should this.
|
||||
;
|
||||
; A selector that was not typed is Data Pointer 0, exactly as an omitted one means in a
|
||||
; source file. That is worth matching rather than demanding: half the instruction set names
|
||||
; a pointer, and most code only ever uses the first.
|
||||
takeMnemonic:
|
||||
RSTA
|
||||
SETD.1 AsmSelOne
|
||||
STA.1
|
||||
SETD.1 AsmSelTwo
|
||||
STA.1
|
||||
INIA 0d4
|
||||
SETD.1 AsmLeft
|
||||
STA.1
|
||||
|
||||
SETD.0 AsmLine
|
||||
SETD.1 AsmName
|
||||
|
||||
takeMnemonicChar:
|
||||
SETD.2 AsmLeft
|
||||
LDA.2
|
||||
BRA takeMnemonicPad ; Four is as long as a mnemonic gets.
|
||||
LDA.0
|
||||
BRA takeMnemonicPad ; The word ended.
|
||||
INIB 0d46 ; A dot, so the selectors start here.
|
||||
XOR
|
||||
BRQ takeMnemonicPad
|
||||
|
||||
; Nothing below compares against A, so it survives all of this and is stored at the end.
|
||||
INIB 0d97 ; a
|
||||
CCF
|
||||
SUB
|
||||
BRC takeMnemonicPut ; It borrowed, so this is below 'a'.
|
||||
INIB 0d123 ; One past z.
|
||||
CCF
|
||||
SUB
|
||||
BNC takeMnemonicPut ; No borrow, so it is 'z' or later.
|
||||
INIB 0x20
|
||||
CCF
|
||||
SUB
|
||||
MVQA ; Upper case, which is how the table holds them.
|
||||
|
||||
takeMnemonicPut:
|
||||
STA.1
|
||||
INCD.0
|
||||
INCD.1
|
||||
SETD.2 AsmLeft
|
||||
LDA.2
|
||||
DECA
|
||||
STA.2
|
||||
BRI takeMnemonicChar
|
||||
|
||||
takeMnemonicPad:
|
||||
SETD.2 AsmLeft
|
||||
LDA.2
|
||||
BRA takeMnemonicSelectors
|
||||
INIA 0x20
|
||||
STA.1
|
||||
INCD.1
|
||||
SETD.2 AsmLeft
|
||||
LDA.2
|
||||
DECA
|
||||
STA.2
|
||||
BRI takeMnemonicPad
|
||||
|
||||
takeMnemonicSelectors:
|
||||
RSTA
|
||||
STA.1 ; The four are a string now, like the ones in the table.
|
||||
|
||||
LDA.0
|
||||
INIB 0d46
|
||||
XOR
|
||||
BNQ takeMnemonicEnd ; No dot, so both selectors stay at nought.
|
||||
INCD.0
|
||||
LDA.0
|
||||
INIB 0d48
|
||||
CCF
|
||||
SUB
|
||||
MVQA
|
||||
SETD.1 AsmSelOne
|
||||
STA.1
|
||||
|
||||
INCD.0
|
||||
LDA.0
|
||||
INIB 0d46
|
||||
XOR
|
||||
BNQ takeMnemonicEnd
|
||||
INCD.0
|
||||
LDA.0
|
||||
INIB 0d48
|
||||
CCF
|
||||
SUB
|
||||
MVQA
|
||||
SETD.1 AsmSelTwo
|
||||
STA.1
|
||||
|
||||
takeMnemonicEnd:
|
||||
RET
|
||||
|
||||
; Looks the four characters up. AsmOpcode and AsmShape are filled in and Q is zero, or Q is
|
||||
; not zero and there is no such instruction.
|
||||
;
|
||||
; The same table the disassembler reads, searched the other way round. That is the point of
|
||||
; it being a table rather than two lists: what can be written can be read back, and what can
|
||||
; be read back can be written, and neither can drift from the other.
|
||||
findByName:
|
||||
SETD.3 Instructions
|
||||
SETD.0 InstructionCount
|
||||
LDA.0
|
||||
SETD.1 AsmLeft
|
||||
STA.1
|
||||
|
||||
findByNameStep:
|
||||
PSHD.3
|
||||
POPD.0
|
||||
DPUP.0 0d02
|
||||
SETD.2 AsmName
|
||||
INIA 0d4
|
||||
SETD.1 AsmCount
|
||||
STA.1
|
||||
|
||||
findByNameChar:
|
||||
LDA.0
|
||||
LDB.2
|
||||
XOR
|
||||
BNQ findByNameNext
|
||||
INCD.0
|
||||
INCD.2
|
||||
SETD.1 AsmCount
|
||||
LDA.1
|
||||
DECA
|
||||
STA.1
|
||||
BNA findByNameChar
|
||||
|
||||
PSHD.3
|
||||
POPD.0
|
||||
LDA.0
|
||||
SETD.1 AsmOpcode
|
||||
STA.1
|
||||
PSHD.3
|
||||
POPD.0
|
||||
INCD.0
|
||||
LDA.0
|
||||
SETD.1 AsmShape
|
||||
STA.1
|
||||
RSTA
|
||||
RSTB
|
||||
CCF
|
||||
ADD
|
||||
RET
|
||||
|
||||
findByNameNext:
|
||||
DPUP.3 0d07
|
||||
SETD.1 AsmLeft
|
||||
LDA.1
|
||||
DECA
|
||||
STA.1
|
||||
BNA findByNameStep
|
||||
RSTA
|
||||
INIB 0d1
|
||||
CCF
|
||||
ADD
|
||||
RET
|
||||
|
||||
; Is there such a bank? Q is zero if there is not.
|
||||
;
|
||||
; ASKING THE CONTROLLER FOR A BANK THAT IS NOT THERE IS REFUSED, and a refusal nobody
|
||||
@@ -2008,7 +2392,7 @@ UnknownText:
|
||||
MonitorName:
|
||||
"monitor"
|
||||
MonitorHelp:
|
||||
"x examine, d disassemble, s set, b bank, g go, exit leaves"
|
||||
"x examine, d disassemble, a assemble, s set, b bank, g go, exit leaves"
|
||||
ExamineName:
|
||||
"x"
|
||||
DisName:
|
||||
@@ -2029,6 +2413,18 @@ ReadOnlyText:
|
||||
"that bank will not be written"
|
||||
GoUsage:
|
||||
"g <address>"
|
||||
AsmName2:
|
||||
"a"
|
||||
AsmPrompt:
|
||||
": "
|
||||
DotText:
|
||||
"."
|
||||
AsmUsage:
|
||||
"a <address>, then instructions, then a dot"
|
||||
NoSuchOp:
|
||||
"no such instruction"
|
||||
NeedsValue:
|
||||
"that one needs a value after it"
|
||||
DirName:
|
||||
"dir"
|
||||
LoadName:
|
||||
@@ -2102,6 +2498,22 @@ BankWas:
|
||||
0x00
|
||||
BankFlags:
|
||||
0x00
|
||||
AsmSelOne:
|
||||
0x00
|
||||
AsmSelTwo:
|
||||
0x00
|
||||
AsmLeft:
|
||||
0x00
|
||||
AsmCount:
|
||||
0x00
|
||||
AsmOpcode:
|
||||
0x00
|
||||
AsmShape:
|
||||
0x00
|
||||
AsmName:
|
||||
#Reserve 0d5
|
||||
AsmLine:
|
||||
#Reserve 0d41
|
||||
ShowAsCode:
|
||||
0x00
|
||||
DumpRecord:
|
||||
|
||||
@@ -719,10 +719,32 @@ bank 01
|
||||
| --- | --- |
|
||||
| `x [addr]` | Sixty-four bytes, as hex and as characters |
|
||||
| `d [addr]` | Eight instructions, disassembled |
|
||||
| `a addr` | Assemble instructions, until a line that is just a dot |
|
||||
| `s addr b b …` | Put those bytes there |
|
||||
| `b program\|data\|n` | Which bank to look at |
|
||||
| `g addr` | Go there |
|
||||
|
||||
`a` writes the assembler's own syntax: a selector rides on the mnemonic as `LDA.0` or `LDD.0.1`, and leaving one off means Data Pointer 0 exactly as it does in a source file, so nothing learned at the monitor has to be unlearned when writing a program. Case does not matter, and the whole line is refused before anything is written, so a mistyped instruction leaves no half of itself behind.
|
||||
|
||||
```
|
||||
* b data
|
||||
* s 8100 68 65 6C 6C 6F 2C 20 74 79 70 65 64 0A 00
|
||||
* b program
|
||||
* a 8200
|
||||
8200: SETD.0 8100
|
||||
8204: SWI 10
|
||||
8206: SWI 12
|
||||
8208: .
|
||||
* g 8200
|
||||
hello, typed
|
||||
```
|
||||
|
||||
A program and its data, both entered by hand, calling a system service and returning to the prompt they were written at. Note the two banks: instructions go into Program Memory and the string into Data Memory, because that is what a Harvard machine means and the monitor will not guess for you.
|
||||
|
||||
**Numbers here are hexadecimal and bare.** A source file writes `0x2000` or `0d16` because it has both and must say which; the monitor has one and says so once.
|
||||
|
||||
**What cannot be written is a label**, and that is the whole difference between this and the assembler proper. A label is a promise to fill an address in later, and later is what a line at a time does not have. It is also why the same instruction table serves both directions here: what `a` writes, `d` reads back, and neither can drift from the other or from the assembler they were generated from.
|
||||
|
||||
`x` and `d` share one cursor and each leaves it past what it showed, so without an address either carries on — reading through memory is one letter at a time, and you can switch between bytes and instructions without retyping where you are. `s` deliberately does not move it.
|
||||
|
||||
Everything else here does something; the monitor looks at what the others did. It shows memory as hex and as characters, disassembles it, writes bytes into it, and jumps to an address — all through the memory controller, which is the only thing that can reach Program Memory.
|
||||
|
||||
@@ -3,11 +3,11 @@ CosmOS
|
||||
> two stops, and what the registers were at each
|
||||
break at 200E
|
||||
A 11 B 22 Q 00 status 00
|
||||
DP0 1030 DP1 05EF DP2 039A DP3 2000 SP FFFF
|
||||
DP0 1030 DP1 0661 DP2 039A DP3 2000 SP FFFF
|
||||
press a key
|
||||
break at 2023
|
||||
A 44 B 55 Q 00 status 00
|
||||
DP0 1000 DP1 05EF DP2 039A DP3 2000 SP FFF5
|
||||
DP0 1000 DP1 0661 DP2 039A DP3 2000 SP FFF5
|
||||
press a key
|
||||
carried on to the end
|
||||
finished
|
||||
|
||||
@@ -8,7 +8,7 @@ finished
|
||||
cd
|
||||
the console has been handed back
|
||||
finished
|
||||
> > x examine, d disassemble, s set, b bank, g go, exit leaves
|
||||
> > x examine, d disassemble, a assemble, s set, b bank, g go, exit leaves
|
||||
* bank 00
|
||||
* FE00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
FE10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
CosmOS
|
||||
> x examine, d disassemble, s set, b bank, g go, exit leaves
|
||||
> x examine, d disassemble, a assemble, s set, b bank, g go, exit leaves
|
||||
* b <program|data|number>
|
||||
* there is no such bank
|
||||
* loaded, starting at 2000
|
||||
@@ -46,6 +46,20 @@ finished
|
||||
800A 00 ADD
|
||||
800B 00 ADD
|
||||
800C 00 ADD
|
||||
* bank 01
|
||||
* * bank 00
|
||||
* 8200: 8204: 8206: 8208: 820B: no such instruction
|
||||
820B: that one needs a value after it
|
||||
820B: 820D: * 8200 47 00 81 00 SETD.0 8100
|
||||
8204 18 10 SWI 10
|
||||
8206 42 02 LDA.2
|
||||
8208 4A 00 01 LDD.0.1
|
||||
820B 18 12 SWI 12
|
||||
820D 00 ADD
|
||||
820E 00 ADD
|
||||
820F 00 ADD
|
||||
* hello, typed
|
||||
finished
|
||||
* > greet.sbx 210
|
||||
hello.sbx 52
|
||||
Life.sbx 1411
|
||||
|
||||
@@ -15,6 +15,20 @@ s 8000 26 48 D1 00 26 0A D1 00 18 12
|
||||
d 8000
|
||||
g 8000
|
||||
d 8000
|
||||
b data
|
||||
s 8100 68 65 6C 6C 6F 2C 20 74 79 70 65 64 0A 00
|
||||
b program
|
||||
a 8200
|
||||
SETD.0 8100
|
||||
SWI 10
|
||||
lda.2
|
||||
LDD.0.1
|
||||
frobnicate
|
||||
INIA
|
||||
SWI 12
|
||||
.
|
||||
d 8200
|
||||
g 8200
|
||||
exit
|
||||
dir
|
||||
exit
|
||||
|
||||
@@ -225,6 +225,12 @@ cosmosRun | CosmOS/Source/cosmos.asm | run | cosmosRun
|
||||
# It also asks to write into bank 2, the controller's own table, which is published read
|
||||
# only. That used to stop the machine, and the recorded output of this test contained the
|
||||
# crash without anybody noticing, which is what blessing a result without reading it buys.
|
||||
#
|
||||
# It ends with a whole program typed in: a string poked into Data Memory, instructions
|
||||
# assembled into Program Memory, and the result run - which prints something no assembler
|
||||
# ever saw. The mnemonics deliberately include a lower case one, both selector forms, an
|
||||
# instruction that does not exist and one missing its value, so that the refusals are
|
||||
# recorded next to the successes and a failed line is shown writing nothing.
|
||||
cosmosMonitor | CosmOS/Source/cosmos.asm | run | cosmosMonitor.in | - | disks/cosmos.img
|
||||
# The original hello.asm, brought over as an application. It is not much of a program,
|
||||
# but it is the one that talks to the hardware directly: it writes to port 0x00 instead
|
||||
|
||||
Reference in New Issue
Block a user