Hi everyone. This is a blog for recording all my personal technical stuff. This time, I'm working on the creation of a fully-featured operating system named WishOS for computers in RedPower 2 (a mod for MineCraft). I will continually write small episodes to record my progress onto this project. Once finished, this would become a hacking tutorial for everyone.
To start my work, I have done some research for redpower computers. Basically these computers have a microcontroller derived from the 6502 (the one in Commodore 64!). Although it emulates nearly a fully functional hardware, it lacks good software support. The original bundled operating system, MineOS, has only a FORTH compiler and nothing else. Despite the poor functionality of FORTH, since the system has only a compiler, the code of a program cannot be viewed or changed once it's entered into the system. It has nothing for file systems, either.
So it is obvious I need start building a temporary toolchain for bootstrapping my final system. The temporary toolchain contains only a text editor and a compiler. I will use the text editor to edit system code, and the compiler to make the bootable floppy disk. The binary always starts from the 0 sector. The code will be placed at a fixed starting sector. This design eliminates the need for file systems.
In this episode, I'm going to write a FORTH program which builds a bootable floppy disk, which just prints the legendary "Hello World!" text when being booted.
Firstly we need to understand the way redpower computer boots. The BIOS code can be found at http://integratedredstone.wikispaces.com/RedPower+Control and I will not duplicate the words. The BIOS basically reads floppy disk sectors and put them into memory address at 0x0500.
When looking at the wiki page, you may have noticed the term "emulation mode". This is a mode used by the 65C816 processor (a 16-bit processor) to behave as the 8-bit processor 6502 for providing backwards compatibility. The RedPower CPU is actually a combination of 6502, 65C02 and 65C816 with custom modifications, so it comes with the emulation mode, too. The key point here is that emulation mode makes it possible that you can switch the registers and memory accesses between 8-bit and 16-bit.
Then I just start building the code. I need a basic buffer management utility to manage compiled code buffer.
Then I have to write instruction emitters for every instruction I'd like to use. To avoid useless work, I wrote the hello world program in assembly at this time point to check what instructions are needed.
Now I can define useful instruction emitters. I quickly recognized some instructions have more than one address modes (see http://www.obelisk.demon.co.uk/6502/addressing.html for explanation for each address mode), to distinguish between them, I added suffixes to instruction names.
The last one is a special suffix which can be appended to all above suffixes to indicate the 8-bit form of that instruction. (By default 16-bit forms of instructions are used.)
After all these preparations, it is now easy to program the instruction emitters. (Remember to run HEX to enter hexadecimal number mode.)
Lastly we define a helper function to move buffer pointer to a fixed position:
We are almost there. Let's assemble the final hello world code.
Our program is done. Try to "burn" it into the floppy disk:
Insert this disk to another computer and boots it, you will finally see "Hello World!" on the screen:
Enjoy our first bootable floppy!
Firstly we need to understand the way redpower computer boots. The BIOS code can be found at http://integratedredstone.wikispaces.com/RedPower+Control and I will not duplicate the words. The BIOS basically reads floppy disk sectors and put them into memory address at 0x0500.
When looking at the wiki page, you may have noticed the term "emulation mode". This is a mode used by the 65C816 processor (a 16-bit processor) to behave as the 8-bit processor 6502 for providing backwards compatibility. The RedPower CPU is actually a combination of 6502, 65C02 and 65C816 with custom modifications, so it comes with the emulation mode, too. The key point here is that emulation mode makes it possible that you can switch the registers and memory accesses between 8-bit and 16-bit.
Then I just start building the code. I need a basic buffer management utility to manage compiled code buffer.
VARIABLE buf 16384 ALLOT buf ! VARIABLE pos : CLR buf @ pos ! ; : WB pos @ ! pos @ 1 + pos ! ; : WD pos @ ! pos @ 2 + pos ! ;
Then I have to write instruction emitters for every instruction I'd like to use. To avoid useless work, I wrote the hello world program in assembly at this time point to check what instructions are needed.
0500 18 CLC ; clear carry 0501 FB XCE ; set native mode 0502 C2 30 REP #$30 ; use 16-bit registers 0504 A9 01 00 LDA #$1 ; default id of monitor 0507 EF 00 MMU #$0 ; set redbus 0509 A9 00 03 LDA #$300 050C EF 01 MMU #$1 ; set redbus memory mapping 050E EF 02 MMU #$2 ; enable redbus 0510 A9 01 00 LDA #$1 ; row 1 0513 8D 00 03 STA $300 0516 E2 30 SEP #$30 ; use 8-bit registers 0518 A2 00 LDX #$0 ; loop variable loop: 051A BD 40 05 LDA $540, X ; load current character 051D C9 00 CMP #$00 ; zero-terminated string 051F F0 07 BEQ final 0521 9D 10 03 STA $310, X ; save current character to screen buffer 0524 E8 INX 0525 4C 1A 05 JMP loop final: 0528 4C 28 05 JMP final ; dead loop 0540 48 65 6C "Hello World!", $00 6C 6F 20 57 6F 72 6C 64 21 00
Now I can define useful instruction emitters. I quickly recognized some instructions have more than one address modes (see http://www.obelisk.demon.co.uk/6502/addressing.html for explanation for each address mode), to distinguish between them, I added suffixes to instruction names.
Suffixes | Address Mode | Example |
---|---|---|
No suffixes | Direct absolute/relative address, or instructions having only one address mode | LDA $33 |
I | Immediate value | LDA #128 |
X | Absolute address + X | LDA $100, X |
Y | Absolute address + Y | LDA $100, Y |
ID | Indirect | JMP ($200) |
IX | Indexed indirect (X) | LDA ($100, X) |
IY | Indirect indexed (Y) | LDA ($100), Y |
Z | Zero page | LDA $FF |
ZX | Zero page + X | LDA $FF, X |
ZY | Zero page + Y | LDA $FF, Y |
8 | 8 bit form | See below |
The last one is a special suffix which can be appended to all above suffixes to indicate the 8-bit form of that instruction. (By default 16-bit forms of instructions are used.)
After all these preparations, it is now easy to program the instruction emitters. (Remember to run HEX to enter hexadecimal number mode.)
: CLC 18 WB ; : XCE FB WB ; : REP C2 WB WB ; : SEP E2 WB WB ; : MMU EF WB WB ; : LDAI A9 WB WD ; : STA 8D WB WD ; : LDXI8 A2 WB WB ; : LDAX BD WB WD ; : STAX 9D WB WD ; : CMPI8 C9 WB WB ; : BEQ F0 WB WB ; (Relative addresses are always 8 bit) : INX E8 WB ; : JMP 4C WB WD ;
Lastly we define a helper function to move buffer pointer to a fixed position:
: ORG 500 - buf @ + pos ! ;
We are almost there. Let's assemble the final hello world code.
CLR CLC XCE 30 REP 1 LDAI 0 MMU 300 LDAI 1 MMU 2 MMU 1 LDAI 300 STA 30 SEP 0 LDXI8 540 LDAX 0 CMPI8 7 BEQ 310 STAX INX 51A JMP 528 JMP 540 ORG 48 WB 65 WB 6C WB 6C WB 6F WB 20 WB 57 WB 6F WB 72 WB 6C WB 64 WB 21 WB 0 WB
Our program is done. Try to "burn" it into the floppy disk:
buf @ 0 DISKWS DISKNAME" Hello"
Insert this disk to another computer and boots it, you will finally see "Hello World!" on the screen:
Enjoy our first bootable floppy!