James Stanley

The kilo editor

Sat 3 April 2021
Tagged: software, cpu

Thanks to the excellent Build Your Own Text Editor tutorial, SCAMP/os now has an editor. It's pretty bare-bones, but perfect for what I need.

The tutorial walks you through implementing the kilo editor, so-named because it's less than 1000 lines of C. Ported to SLANG (and with syntax highlighting not supported), it currently totals 811 lines. I skipped the syntax highlighting partly because of performance considerations (SCAMP is a pretty slow CPU), but mostly because I felt that colours would ruin the aesthetic.

Here's a demo of the editor:

And here's the source of my version: https://github.com/jes/scamp-cpu/blob/master/sys/kilo.sl.

Using kilo is basically like using nano but with a lot fewer features.

Implementation details

Kilo stores each line as a C string on the heap (realloc()'d as needed), with another dynamically-resizing array storing a pointer to each line. This is good because it is easy, but it does mean there is sometimes a large block copy when a string/array needs to be moved. It seems fine enough in practice though, even on (emulated) SCAMP.

The "real" kilo actually stores 2 copies of each line: one is the contents of the line as they appear in the file, and the other is as they appear on the screen. The principle difference is that tab characters in the file are rendered as a series of spaces on the screen. You might be tempted to skip this, but it results in weird behaviour when the screen scrolls horizontally. Perhaps you could work around that by snapping the horizontal scroll to the tab stops? Maybe that is worth trying.

To save on memory usage, SCAMP kilo only stores 1 copy of each line and generates the display version only when needed. You might think this is a premature optimisation, but consider that the source for the compiler is 27K characters. Even with no extra overhead in kilo, storing 2 copies of that would be 54K, and with the kernel taking up 14K, we've already ran out of address space. (As it happens, kilo currently can't open the compiler source anyway, because other inefficiencies push the total over 64K - I eventually want to fix this).

The real kilo redraws the screen after every keypress, which is totally fine on modern computers but is definitely not going to fly on SCAMP: e.g. it would take more than 2 seconds to do a full screen redraw at 9600 baud. For the SCAMP version I keep track of which lines have changed since they were last drawn, and only redraw those that have changed.

Compared to ZDE 1.6

On the RC2014 I use the ZDE 1.6 editor. It is more feature-complete than kilo, but quite weird, and not well-suited to modern keyboards. The backspace key does what the delete key should do, and to get a backspace you need to type Ctrl-H. Sometimes it writes ^I at the end of a line for no apparent reason (I don't think it's anything to do with tab characters). The key shortcuts are confusing and hard to learn, because they don't match any other software I've ever used.

While kilo is clearly inferior to ZDE in terms of features, it is much easier to use, mainly because the lack of available operations means typing the wrong keys is unlikely to make it run off in a direction you didn't want.

To my untrained fingers, ZDE sometimes feels a bit like trying to use vi with caps lock stuck on. Or like trying to drive a car but the steering wheel operates the throttle, the gear lever operates the brakes, and you steer by winding the windows up and down. It's mostly fine as long as you go slowly and carefully, but if you slip into your usual flow it all comes crashing down.

OK, that's a bit unfair. ZDE is definitely the easiest editor I've found for CP/M. Maybe I should port kilo to CP/M.


Apart from working on the editor, I've made a bit more progress on the hardware. I have now got a UART wired up on a breadboard, and communication is working in both directions:

From left to right, we have a 1.8432 MHz quartz crystal and a resistor and a couple of capacitors, forming the oscillator from which the baud rate clock is derived. Then there's the UART itself, this is a 16450, which is equivalent to an 8250 but with support for higher baud rates. Then the little black button is the reset button, and finally there's an Arduino Nano.

The Arduino is there to:

The fastest I can bit-bang a square wave from C on the Arduino is about 80 kHz, and SCAMP still seems to work correctly at this speed, which is a good sign. I'll be disappointed if I can't get another factor of 10 on that, and delighted if I can get another two factors of 10.

I'm still umming and ahhing about how I'm going to generate the "WR" pulse without the Arduino.

"WR" is an input to the UART that tells it we want to write to it. The problem is that the UART does not have a clock input, and it seems to take input from the bus at the falling edge of "WR". The closest signal to "WR" that SCAMP generates is "DI" which says we want an IO device to take input at the rising clock edge. Simply connecting "DI" to "WR" doesn't work because "DI" doesn't have a falling edge until the next clock cycle starts. At the start of the next cycle, "DI" and the data on the bus both disappear at the same time, which leads to the UART sometimes (most of the time) not reading the correct input. We need a way to make the "WR" signal have a falling edge when the clock edge rises, rather than when "DI" falls.

I think this shows what I mean:

(drawn with WaveDrom)

I want to generate a signal that looks like "WR (wanted)". A potential solution would be to create "DI & !CLK", but it has the risk of a small glitch at the next falling clock edge while "DI" is still high. Past that, I'm not sure. Maybe I have to come up with something based around some flip-flops? I think it would be fine to pulse "WR" when given a rising edge of the clock, rather than holding it high while the clock is low. Maybe that would be easier. Something like the 74121-based single-stepping circuit in my RC2014 front panel.

I have found working on the electronics quite difficult. On several occasions I have reached a working configuration, made a small change, determined that it did not work, reverted the change, and found that the formerly-working configuration no longer worked, with no way to know whether I have permanently broken something, failed to properly restore the previous configuration, or the previous configuration only worked by accident for some unrelated reason. Electronics really needs better development tooling.

I have splashed out on a DSO (I went for a "Hanmatek DOS1102", which is cheap-ish but well-reviewed and seems fine). Unfortunately I haven't found it to be a great deal of use. For example, if I connect it to the clock signal, the CPU stops working and I have no idea why. The clock signal still looks fine to me:

But evidently the magic pixies know they're being observed and they don't like it. It seems like you can't ever use a DSO to debug anything because the mere presence of the DSO changes the behaviour of the system you're connecting it to. In hardware, every bug is a heisenbug.

If you like my blog, please consider subscribing to the RSS feed or the mailing list: