Preemptive multitasking works as well! Since the task switcher is tied to the screen refresh IRQ, it's kind of jittery and laggy, so it's best to treat it like a cooperative multitasking system and swi $80 regularly.

Multitasking is working now - the graphics on the left and right are actually being produced by two separate programs!

Currently, this is cooperative multitasking (the programs are doing `swi $80` after each iteration), but all I have to do to make it preemptive is add a `jsr multitask` to the kernel's vblank ISR.

ok so turns out the two problems with multitasking were:

  • i did a 'store long' instead of 'store byte' to the address that enabled multitasking, so the byte there was still $00 because Canid is big-endian
  • the stack pointer, stack base, and stack top were being set to $00000000 for new processes, which made Canid freak out

Program loading works! It's loading to a fixed location, but it still works, at least.

I can probably use the vblank ISR (the thing that draws & increments the counter in the bottom-right) to switch out processes in order to do preemptive multitasking. Sure, it'd be slow and hacky, but it'd work.

crap blog posts take a long time to write

i'm absolutely dreading making the presentation

File operations are complete (for now)!

What's implemented:

  • Opening a file (fd_open)
  • Closing a file (fd_close)
  • Reading from a file (fd_read)
  • Moving head position (fd_seek)
  • Getting file info (fd_stat)

What's not implemented:

  • Writing to a file (fd_write)
  • Creating a new file (fd_touch)
  • Listing directories (fd_readdir)
  • Support for files in directories

Not only that, but fd_read will automatically find and move to the next data block until it fills the buffer you requested, rather than returning early. It should also do this when it hits CRFS's 48K limit and needs to traverse to the next file header, but I haven't tested that yet (and I probably won't, since I don't need files over 48K anytime soon).

i really should have made my assembler accept ld reg, *(reg) as an alternate form of ld reg, *(base, reg) where base = 0

90% of my errors are just "oops i forgot to add the base address of 0 to this instruction"

fd_open and fd_stat work now
(first breakpoint is after fd_open, second is after fd_stat)

didn't work on the OS as much as i hoped to today, but it's almost able to open a file

This looks pretty much exactly the same as the previous screenshot, but it's actually the kernel's error handler! (The reason it says "breakpoint" is because I'm using the breakpoint handler code to do a register dump.)

Okay I fixed the breakpoint handler, turns out the entire problem was that the program counter was misaligned...

wait... is it jumping to the previous instruction after an `rti`???

I spoke too soon about IRQs apparently, my breakpoint handler isn't working for some unknown reason...



My assembler sets lgdata on every instruction where the data value is the address of a label, even if that address is <= 0x7f.

When lgdata is set, Canid ignores the low byte of the instruction (where the 8-bit data field would be stored), and reads the data field from the next 4 bytes instead.

I can use that ignored byte to mark instructions that need their addresses translated!!

Welp, looks like there's another bug in the assembler :/

Basically, when the lgdata flag was set (which expands the instruction's data field to 32 bits), I forgot to serialize values > 0x7FFFFFFF as unsigned, so struct would throw an error because they were outside the range of a signed int.

I didn't really test interrupt handlers before, but they seem to be working fine. The only problem is that the vblank IRQ causes the system to lock up if I enable debug stepping. But that's okay, since I can just use swi $00 to trigger a breakpoint.

Show older

A Mastodon instance for users who like the study of programming languages, formal semantics, types, or linguistics.