Disassembly of Commodore LCD kernal

Note: main 'documentation' and description (including I/O memory layout, additional information, details, etc) page can be found here.

List of added comments

Click here to return to the disassembly list.

8000At offset 4: this bytes tells the number of Kbytes to be checked by the ROM checksum routine. I don't know the purpose of the other bytes though.
8008Every ROM images begins with this "identification" string. This one is also used to compare with the searched ones by the ROM scanning routine.
8015Every ROMs contain a "directory" with the "applications" can be found (I guess). Besides the names, I know only maybe three bytes of the six (prefixed every name): the size of that entry, zero for no more entries. Two other bytes (last two bytes - a word - before the name itself) may encode the entry point of the given software. I don't know the purpose of other bytes.
8038It seems to be the entry point of "MONITOR.MON".
804CIt seems to be the entry point of "COMMAND.CMD".
8277Interesting, though I don't know the purpose of the given ZP locations. It seems, $FD00, $FD80, $FE00, $FE80 are used some kind of MMU purpose, based on value CMP'd with constants which suggests memory is devided into parts (high byte only): $00-$3F, $40-$7F, $80-$BF, $C0-$F7, $F8-$FF.
82CBThe 'ScanROMs' routine uses this table to write values to $FF00 to check for ROMs.
82D3This routine seems to scan ROMs, searching for the "Commodore LCD" string. This is done by using register at $FF00 which seems to tell the memory mapping at CPU address $4000. So I think $FF00 tells what is mapped to $4000.
82F3$4004 is the paged-in ROM, where the id string would be ($FF00 controls what can you see from $4000), it's compared with the kernal's image's id string ("Commodore LCD").
8307Hmm, it seems to be a bug for me, it should be 'pla', otherwise X is messed up to be used to address byte on the stack.
832FPush Y onto the stack. Write "ROMSUM ...." text, then take the value from the stack, "covert" into an ASCII number (ORA), and print it, followed by the " INSTALLED" text.
8367Creates checksum on ROMs. Input: A = value of $FF00 reg to start at X = number of Kbytes to check Output: X/A = 16 bit checksum (simple addition, X is the high byte)
84FAThis seems to be the "shutdown" function or part of it: "state" should be saved (which is checked on next reset to see it was a clean shutdown) and then it used /POWEROFF line to actually switch the power off (the RAM is still powered at least on CLCD!)
852ARelease /POWERON signal, machine will switch off. Run the endless BRA if it needs some cycle to happen or some kind of odd problem makes it impossible to power off actually.
8534************************************* Start of the real RESET routine after MMU set up. *************************************
8535As soon as possible set /POWERON signal to low (low-active signal) configure DDR bit as well.
8653Funny :)
86A0Set MEMBOT vector to $0FFF
86A9D9/DA shows here the tested amount of RAM to be found OK, starts from zero
86AEThis seems to test the zero page memory.
86C2Test rest of the RAM, using the kernal window to page in the testable area.
8763Inits VIAs, ACIA and possible other stuffs with JSRing routines.
942BJumps (by a jump table) to the right routine to dump the "virtual 1541" directory listing tail/head or the content itself, IMHO.
9437This (and other part later) fragment seems to be the usual directory listing produced by an 1541 and compatible drives. The text says "virtual 1541" thus it seems CLCD handles something (possible programs in ROM and/or RAM disk) as it would be a "real" 1541 drive for the user.
9457This dumps the directory listing head, with the "virtual 1541" text.
9488This dumps the directory listing tail, with the "block used" text.
94A8Afaik this dumps a row of the directory listing, about a file, you can even see the "PRG", "SEQ" stuffs appended.
AF25This op puts the chr into the video RAM.
AFCBThis fragments prints spaces (ie; clear) on screen.
B0D5Just guessing: this table is a byte then a jump address. Maybe it's some kind of terminal escape sequence table or such, at least pointed routines are often touch cursor position zp locs, etc.
B228This routine is called by RESET routine, with carry set. It seems it's the only part where locations $FF80 - $FF83 are written. $FF80-$FF83 is the write-only registers of the LCD controller. It's called first with carry set from $87B5, then called second with carry clear from $B204
B39AMy guess: these tables help to decode the keyboard matrix read into actual characters. More tables, because of shifted, etc state.
B5E4It seems, CLCD's kbd is read through VIA's SR ... Interesting. AFAIK, port B of VIA is mainly used by IEC bus, but bit 0 for example seems to trigger (0->1) the keyboard "controller" to provide bits through serial transfer in SR-in.
BB7FMaybe typo? :) Or it's only my English ... "SAVEING" ...
BC96This routine seems to be used to send out a byte on the IEC bus.
BE1AReset VIA1 port-B bit#4.
BE23Set VIA1 port-B bit#4.
BE2CReset VIA1 port-B bit#5.
BE35Set VIA1 port-B bit#5.
BE3EWaiting for change on port B of VIA1. The high two bits are inverted, and bit 7 moved to carry. Maybe used for CLK/DAT sense on IEC bus?
BF4FUpdates time-of-day (TOD) clock. Should be called at 60Hz frequency.
BF7Ethis TOD stuff was easy, but I have no idea what the rest is ...
BFE4Waits for multiple of 1/60 seconds. Interrupt must be enabled, since it used TOD's 1/60 val. Input: X = number of 1/60 seconds.
C352This quite odd routine reads (4 bit) data from the RTC chip. Odd, because port A of VIA#2 is configured as _output_ otherwise ... $40 is for AW (address write) signal for the RTC. Input: Y = RTC register number Output: A = read value
C669It seems the following routines will be copied from $0338 to the RAM and used from there. Guessed purpose: the ROM itself is not always paged in, so we need them to be in RAM. Note about the "dummy writes", those (maybe ...) used to set/reset flip-flops to switch on/off mapping of various parts of the memories, but dunno what exactly :( ---------------------------------------------------- My best guess so far: dummy writes to ... * $FA00: enables lower parts of KERNAL to be "seen" * $FA80: disables the above but enable ROM mapped from $4000 to be seen * $FB00: disables all mapped, but the "high area" "High area" is the end of the KERNAL & some I/O registers from at $FA00 (or probably from $F800?) and needs to be always (?) seen. ---------------------------------------------------- This will be $0338 in RAM. It's even used by BASIC for example, the guessed purpose: allow to use RAM for BASIC even at an area where there is BASIC ROM paged in (from $4000) during its execution. $033C will be the RAM zp loc of LDA (zp),Y op.
C672This will be $0341 in RAM. $0345 will be the RAM zp loc of STA (zp),Y op. This routine is also used by BASIC. It seems ZP loc of STA is modified in RAM.
C67BThis will be $034A in RAM. $034E will be the RAM zp loc of LDA (zp),Y op.
C684This will be $0353 in RAM. $0357 will be the RAM zp loc of LDA (zp),Y op.
C68DThis will be $035C in RAM. $0360 will be the RAM zp loc of STA (zp),Y op.
C6A1This copies the routines from $C669 into the RAM from $338.
CAC3Prints a hex word given at ZP locs and then a space. Input: $CC = high byte, $CB = low byte
CAC7Prints a hex word and then a space. Input: X = high byte, A = low byte
CAD8Byte as hex print function, prints byte in A as hex number. X is saved to $39D and loaded back then.
CAE8Byte to hex converter Input: A = byte Output: A = high nibble hex ASCII digit, X = low nibble hex ASCII digit
CAF2Nibble to hex converter Input: A = byte (low nibble is used only) Output: A = hex ASCII digit
CC1DPrints PC as hex word, and registers?
CE12Addressing mode characters for the monitor/(dis)assembler?
F700This is the character set. It contains 6 bytes for each characters, and the bitmap is "rotated", ie the on screen the resolution is 6*8, not 8*6. Character set area is from $F700 to $F9FF, for 128 characters (codes > 128 would mean inverse text probably?). The area from the point of view of the CPU also contains the VIA registers, it seems.
FA07The actual RESET routine, pointed by the RESET hardware vector. Notice the usage $FA00, seems to be a dummy write (no actual LDA before it, etc). Maybe it's just for enabling the lower part of the KERNAL to be mapped, so we can jump there, or something like that.
FA0EThe IRQ routine, pointed by the IRQ hardware vector. Note about the usage of $FC00 and $FA80 locations, seems to be dummy write, as with the RESET routine, but different addresses ...
FA31Default IRQ handler, where IRQ RAM vector ($314) points to by default.
FA7EAn interesting example for addresses like $FA80 are write only registers, but on read, normal ROM content is read as opcodes, as $FA80 here is inside and opcode itself.
FA8DContains $FA87 by default.
FA90Default values of "RAM vectors" copied to $314 into the RAM. The "missing" vector in the gap seems to be "monitor" entry (according to C128's ROM) but points to RTS in CLCD. The the last two vectors are unknown, not exists on C128.
FB51This stuff prints (zero terminated) string after the JSR to the screen (by using the return address from the stack). The multiple entry points seems to be about the fact that "kernal messages control byte" should be checked or not, and such ...
FB92Code from here clearly shows many examples for the need to "dummy write" some "MMU registers" - $FA00 - (maybe only a flip-flop) before jumping to lower address in the KERNAL ROM. Usually there is even an operation like that after the call - $FA80. My guess: the top of the kernal is always (?) mapped into the CPU address space, but lower addresses are not; so you need to "page in" first. However I don't know _exactly_ what happens with $FA00/$FA80 (set/reset a flip-flop, but what memory region is affected then exactly).
FDFCSCREEN. Fetch number of screen rows and columns. On CLCD the screen's resolution is 80*16 chars.
FE01PLOT. Save or restore cursor position. Input: Carry: 0 = Restore from input, 1 = Save to output; X = Cursor column (if Carry = 0); Y = Cursor row (if Carry = 0). Output: X = Cursor column (if Carry = 1); Y = Cursor row (if Carry = 1). Used registers: X, Y.
FE0C?? Might be IOBASE. Fetch CIA #1 base address. Input: - Output: X/Y = CIA #1 base address . Used registers: X, Y. Though CLCD contains VIA, not CIA, but fair enough :) $F800 seems to be OK, as this is addr of VIA-1, indeed. It also helped me to be sure that F800 is really start of the VIA regs.
FE11Seems to be an unused area.
FF3FPower off with saving the state.
FF81------------------------------------------------------------------------------ Begin of the table of the kernal vectors (well, compared with "standard KERNAL entries" on Commodore 64, I can just guess if there is not so much difference on the CLCD) ------------------------------------------------------------------------------
FFA2The following entry (three bytes) would be "SETTMO. Unknown. (Set serial bus timeout.)" according to the C64 KERNAL, however on CLCD it seems to be unused.
FFB4??TALK. Send TALK command to serial bus. Input: A = Device number.
FFB7??READST. Fetch status of current input/output device, value of ST variable. (For RS232, status is cleared.) Output: A = Device status.
FFBA??SETLFS. Set file parameters. Input: A = Logical number; X = Device number; Y = Secondary address.
FFBDSETNAM. Set file name parameters. Input: A = File name length; X/Y = Pointer to file name.
FFC0"OPEN". Must call SETLFS and SETNAM beforehands. RAMVEC_OPEN points to $FCE7 in RAM by default.
FFD5??LOAD. Load or verify file. (Must call SETLFS and SETNAM beforehands.) Input: A: 0 = Load, 1-255 = Verify; X/Y = Load address (if secondary address = 0). Output: Carry: 0 = No errors, 1 = Error; A = KERNAL error code (if Carry = 1); X/Y = Address of last byte loaded/verified (if Carry = 0). Used registers: A, X, Y. Real address: $F49E.
FFD8??SAVE. Save file. (Must call SETLFS and SETNAM beforehands.) Input: A = Address of zero page register holding start address of memory area to save; X/Y = End address of memory area plus 1. Output: Carry: 0 = No errors, 1 = Error; A = KERNAL error code (if Carry = 1). Used registers: A, X, Y. Real address: $F5DD.
FFDB??SETTIM. Set Time of Day, at memory address $0390-$0392. Input: A/X/Y = New TOD value. Output: – Used registers: – Real address: $F6E4.
FFDE??RDTIM. read Time of Day, at memory address $0390-$0392. Input: – Output: A/X/Y = Current TOD value. Used registers: A, X, Y.
FFE1??STOP. Query Stop key indicator, at memory address $0091; if pressed, call CLRCHN and clear keyboard buffer. Input: – Output: Zero: 0 = Not pressed, 1 = Pressed; Carry: 1 = Pressed. Used registers: A, X. Vector in RAM ($328) seems to point to $FDC2
FFE4GETIN. Read byte from default input. (If not keyboard, must call OPEN and CHKIN beforehands.) Input: – Output: A = Byte read. Used registers: A, X, Y.
FFEA??Might be UDTIM. Update Time of Day, at memory address $0390-$0392, and Stop key indicator
FFEDSCREEN. Fetch number of screen rows and columns.
FFF0PLOT. Save or restore cursor position. Input: Carry: 0 = Restore from input, 1 = Save to output; X = Cursor column (if Carry = 0); Y = Cursor row (if Carry = 0). Output: X = Cursor column (if Carry = 1); Y = Cursor row (if Carry = 1). Used registers: X, Y.
FFF3?? Might be IOBASE. Fetch CIA #1 base address. Input: - Output: X/Y = CIA #1 base address . Used registers: X, Y.
FFF6Four unused bytes, this is the same as with C64.
FFFAThe 65xx hardware vectors (NMI, RESET, IRQ).
FFFCThis is the RESET vector.