Tuesday, March 27, 2018

Taking advantage of the new MC-10 ROM Part 2

You really don't have to do anything else software wise to speed up your code with the new ROM.  It speeds up everything around 5%.  However, there are some things you can do to make programs faster, most of which work with all MC-10 BASICs. 

Using ELSE will make some code a little faster, especially if you make the default condition the most often executed condition.

Example:
IF A=1 most.executed ELSE everything.else

You may have to reverse the logic:
IF A <= 1 least.executed ELSE most.executed

Becomes:
IF A > 1 most.executed ELSE least.executed

The reason for this is that the interpreter has to spend less time dealing with new lines, searching for lines, and less time searching for the end of a line.  The difference is small, but it adds up.
In this case, the > requires slightly less parsing than <= so that also makes the code slightly faster.


Things that will speed up all versions of MC-10 BASIC.

Declare your variables at the top of the program in order from most used to least used.  Just assign them to zero or whatever.  This places them in order in the variable table based on when they are created, and it will make the most use variables quicker to find when BASIC has to search through the table.  This is something common to all Microsoft BASICs I've looked at.  This can speed up some programs by several percent.

Remove all spaces and do not execute lines with REM statements.  This reduces the number of characters BASIC has to parse.  It is especially true of Microcolor BASIC.  Microsoft BASIC has a subroutine in RAM that searches for keyword tokens or other characters that it returns to the code that called it, and it skips spaces.  The MC-10 didn't have enough room in low RAM (the direct page) for all of the code, so it executes part of the code in RAM, then jumps to ROM to test what character was read.  If it's a token or other character it's looking for, it returns that.  If it's a space, it jumps back to the code in RAM to parse the next character.  So each space result in two two jumps and multiple tests of the character that aren't necessary if you simply delete the spaces.

The patch I posted that can be embedded in your program places the test for the most common item the parser finds (tokens) into RAM so it doesn't have to jump to the ROM as much, but spaces are still costly.  The patch does not work with MCX-BASIC, as that uses the addresses required for the patch.


Hardware changes.

The other thing you can do to take advantage of the new ROM is add a RAM expansion that places RAM immediately following the direct page.  This is hex address $100 on.  This allows the ROM to install the entire parse subroutine into RAM.  If no RAM exists at that address, it only installs the code to check for tokens like the patch.  The more of the subroutine that resides in RAM, the faster the interpreter runs.  Sadly, the Radio Shack RAM expansion does not support this.

To Err is Human, to Invert and Multiply is Divide... or how to take advantage of the optimized MC-10 ROM Part 1.

Supporting the 6803's hardware multiply in the floating point math library provided the single greatest speed improvement of any of the optimizations in the new MC-10 BASIC ROM if a program uses lots of multiplication.  That speeds up a lot of calculations, but math isn't just multiplication.  You also need to divide numbers a lot, and there is no hardware divide instruction on the 6803.

There is a partial solution, and it comes from a shortcut normally used when dividing fractions.
Invert and multiply.  But we aren't dealing with fractions, so how do you invert a number?  Microcolor BASIC represents all numbers with floating point numbers, so fractions are represented as floating point, and to invert a number, you divide 1 by the number.

Example:

Suppose you want to divide 94 by 144. 
Normally, the BASIC code would look something like this:
10 PRINT 94 / 144

But to invert and multiply, 94 can be represented as 94/1 and 144 can be represented as 144/1.  When you invert 144/1 you get 1/144.  When you multiply 94/1 by 1/144, you get 94/144.  Since 94/1 is still 94 that part of the code is the same as the original.
The code now looks like this:
10 C = 1 / 144
40 PRINT 94 * C

This lets us take advantage of the fast 6803 hardware multiply used in the optimized floating point library, but as I said, this is a partial solution.  When you invert 144/1, in order to create the floating point equivalent of 1/144, you are performing a divide.  So now you are performing a divide and a multiply to get the result instead of just a divide.  This is obviously slower, so it will not be faster in every case, including the one above, but that shows how it works.

Repeatedly dividing by a number is where we can take advantage of the faster multiply.  How much faster the code is will depend on how many divides there are.  This approach may be faster with as few as 2 divides given how slow the floating point divide is compared to the multiply, but you need to benchmark the code to be sure if there are a small number of divides.  If the divide is inside of a loop, the savings could be significant.
Example:

Original code:
10 FOR I = 1 TO 1000
20 PRINT I / 7
30 NEXT I

Becomes:
0  C = 1 / 7
10 FOR I = 1 TO 1000
20 PRINT I * C
30 NEXT I

Monday, March 26, 2018

What good is a new MC-10 ROM? (reply to CoCo Crew Podcasts)

My replacement for Microcolor BASIC has had a some mentions on the CoCo Crew Podcast in recent episodes.  This nay answer a couple questions they had.

Why did I start this?  The project arose for a couple reasons, and no it's not nostalgia, I did't own an MC-10 until a few years ago.  I wanted to enter the retro challenge (why I started this blog), and I had claimed on a forum that the MC-10 is more powerful than people give it credit for CPU wise... or something like that.  Someone mocked me for that.  Challenge accepted!  Plus this sort of thing is fun for me, I created a commented disassembly of the Amiga exec back in the 90s. 

Why would you want it?  If you want to run BASIC games on the MC-10, or you want to program the MC-10 in BASIC, programs will run faster.  It also adds the ELSE statement which makes it easier to port programs from other machines including from CoCo Color BASIC. Since a lot of programs never appeared on the MC-10, you can be the first to port them.

Okay, so there's a new ROM, how can we use it?
1)  The most obvious is that you can use it in an emulator.  This is how I test the ROM.  Just tell the emulator to load the ROM image instead of the original.  This is also the best way to write BASIC programs for the MC-10.  If you don't have to type them in on that little keyboard things become a lot easier.
2) The MC-10 hardware was designed so that external hardware can disable the internal CPU address decoding, including the ROM.  You can put the new ROM onto an expansion board that includes a socket for one, or replace internal ROM with RAM where supported to load the ROM image into RAM.
3) You can replace the system ROM inside the machine.  Most MC-10s have the ROM soldered to the motherboard, but there are a few (like mine) that have the ROM socketed.  You can desolder the ROM and install a socket if it doesn't already have one, then plug in a new ROM. 
You could have the ROM desoldered for you at CoCo Fest. 
At the same time, you could also have the CPU socketed for a future 6303 upgrade.  The 6303 upgrade requires a new ROM.  Getting the shielding removed to access the ROM and CPU also makes it easier to upgrade the internal RAM to take advantage of the highest resolution 6847 graphics modes.

FWIW, the optimizations I've made are mostly machine or Motorola specific.  You can't speed up the 6502 or Z80 versions of Microsoft BASIC the same way.  Microcolor BASIC on the MC-10, and Color BASIC on the CoCo weren't optimized much over the 6800 version they seem to be derived from.

Why the MC-10 ROM and not the CoCo ROM?   There's less software to break, and the MC-10 needs some love.

Thursday, March 15, 2018

MAME MC-10 HD6303 support is on hold

It seems the MAME crew is rewriting a lot of stuff again and it makes no sense to fix any code that is just going to to broken by the rewrite.  The design of the system is still a bit C oriented anyway.  The HD6303 object should be able to inherit from the MC6801/3 object which should be able to inherit from the 6800 object... and then just overload things that have changed.  The 6800/6801/6303 stuff hasn't quite made that transition yet.  The built in I/O and RAM isn't even in the 6801/6303 objects, it part of the code for each machine.

I'll hack together a version of MAME using the timing table of the 6303 in place of the 6803 so I can perform some benchmarks and make a video or two, but until I see what changes are being made to MAME, there isn't much point in doing anything more.  

Thursday, March 8, 2018

MAME MC-10 support update part 2

The ROM performs the memory test, copies setup code to RAM (that isn't there), returns and jumps into never never land because the stack pointer was never set due to loading it from page zero.
Turns out the HD6301/3 support in MAME is incomplete, it doesn't include the built in hardware and RAM. 
There were several oversights in my code prior to last night's work, so it would have had problems anyway, but seriously?  Someone couldn't take the time to inherit this from the 6801 emulation?
It's already there.  I'll look at fixing this in the next day or two.

One of the problems with MAME is that they use a lot of macros to "simplify" supporting new systems, but it has the side effect of keeping developers that aren't familiar with them from seeing what is going on, or more appropriately, what is going wrong.  Once I got around that hurdle, it was pretty easy to finish the HD6803 support in the MC-10 code.  I still have to fix an issue with the Alice support this caused, but I'll cross that bridge when I get to it.  Perhaps the most difficult part will be getting the MAME devs to accept these changes. 

Saturday, March 3, 2018

MAME MC-10 support update

I just spent several hours setting up a build environment for the MAME/MESS emulator.
It took a couple times to get it working properly.  Not sure what went wrong with the first install. 
Building it took 3 hours on my Intel i7 quad core laptop.  One thing MAME isn't, is small and I need a faster hard drive.

Adding HD6303 support for the MC-10 took about 2 hours.  Half of that was finding examples to make the changes, and the rest of the time was needed to figure out what was barfing in the build.  I cut and pasted from the 6309 CoCo3h definition and didn't update the tag on the line that replaces the cpu.  Du-Oh!

A real MC-10 with a 6303 will require a different ROM to fix timing differences, to add the extra 6303 interrupt vector, etc... but that shouldn't be a big deal.  Some testing will before I can submit the changes to the MAME project.  I won't be able to start that until tomorrow night.

MAME may not properly support 6803/6303 internal direct page RAM... but I haven't looked through all of the code yet.

Friday, March 2, 2018

Testing 1... 2... 3...

Testing some code formatting so it doesn't look ugly anymore.




Using http://hilite.me to format the html.
; Simple speed up for MC-10 Microcolor BASIC
; (C) 2018  James Diffendaffer
; May be freely redistributed
; Date:  2/28/2018
; code to patch the CHRGET function on the direct page 
; chrget is used to parse through the BASIC code and gets called a lot.
; by checking the most frequent case in RAM, it saves a 3 clock cycle jmp
; to the remainder of the function in ROM.  

; definitions for the TASM cross assembler needed for 6803 syntax
.MSFIRST           ; Most Significant byte first

#define EQU     .EQU
#define ORG     .ORG
#define RMB     .BLOCK
#define FCB     .BYTE
#define FCC     .TEXT
#define FDB     .WORD
#define END  .END
#define FCS  .TEXT

#define equ     .EQU
#define org     .ORG
#define rmb     .BLOCK
#define fcb     .BYTE
#define fcc     .TEXT
#define fdb     .WORD
#define end  .END
#define fcs  .TEXT

;start of code
 org  $434B   ;1st address after a REM on the first line of codeof the program.
       ;this is constant on startup in Microcolor BASIC

 pshx     ;preserve register contents
 psha
 pshb
 ldaa $F6
 cmpa #$7E    ; is it a jmp instruction?
 bne  exit   ; if not we exit
 
 ldd  $F7    ; grab the current address JMP calls
 addd #$0105   ; hopefully this will skip the compare & branch now on the direct page
 subd #$0101   ; - to avoid putting a zero in the code. 
 std  $FC    ; save it at the end of the new code

; ldx  #PATCH   ; get the address of our patch code minus 1 (to avoid using LDD 0,X)
 ;pc relative version of ldx to make code relocatable
 bsr  pcRel+1   ; push PC (avoiding zero byte)
pcRel:
 nop      ; origin for PC-relative indexing
 pulx     ; X = pcRel
 
 ldd  PATCH-pcRel,X    ; get the firs two bytes
 std  $F6    ; save them
 ldd  PATCH-pcRel+2,x    ; get the next two
 std  $F8    ; etc...
 ldd   PATCH-pcRel+4,x
 std  $FA
exit:
 pulb     ; restore registers
 pula
 pulx

 rts      ; return to BASIC

; contains the patch
PATCH:
; FCB $F0     ; dummy byte so LDD doesn't have to use LDD 0,X
; org $00F6    ; the address the patch is meant to run at.  Not really needed due to relative branch.
 cmpa      #':'    ; set Z flag if statement separator
 bcs       AA    ; perform more tests if not
 rts       ; return if >= ':' 
AA jmp $E1CC     ; jump to the parser back end.  The address can be dropped 
        ; - because we copy the current one plus 4 now

 end