Jump to content
IGNORED

Multicolor mode - the mode everybody wants


Asmusr

Recommended Posts

12 hours ago, SteveB said:

I tried 4 and 8 MOVs ... 52ms vs. 50ms. 

 

Taking into account the 20ms vor the CALL LINK and 5ms for the handling of one parameter, it is 25 vs. 27 vs. 35 without unrolling... and faster than my theoretical speed from above !?! Is the 0,333uS not valid for the 99/4a or do I have a fundamental missunderstanding of the tables in the data sheet?

 

The timing in the datasheet is not terribly intuitive.

 

You tried to estimate "MOV R1,*R2+" (But, that isn't the instruction you are using? You want (at best) "MOV *R1+,*R2" (where R1 is source address, and R2 is VDPWD), no? Let's do that one.

 

In BF's test, Classic99 says this instruction takes 40 cycles. We can break it down. He's running in 8-bit RAM and I'm assuming that registers are in 16-bit RAM.

 

MOVB is a base of 14 cycles and 4 memory accesses. We're in byte mode, so *R1+ adds 6 cycles and 2 memory accesses, while *R2 adds 4 cycles and 1 more memory access. Break it down step by step.

 

MOVB base: 14 cycles
Memory wait states: read instruction (8-bit, 4 cycles), read source register (16-bit, 0 cycles), read destination register (16-bit, 0 cycles), write destination address (8-bit, 4 cycles)
MOVB -> 22 cycles

*R1+ base: 6 cycles
Memory wait states: read source address (8-bit, 4 cycles), write register (16-bit, 0 cycles)
*R1+ -> 10 cycles

*R2 base: 4 cycles
Memory wait states: read destination address (8-bit, 4 cycles) (the write is covered above)
*R2 -> 8 cycles

Total = 22+10+8 = 40 cycles.

It's tricky because the way they word it makes it hard to think about all the steps involved. It's easier ("easier") to think about it linearly than to try and sort the tables (then you can just use the tables to make sure you accounted for all the memory accesses):


Read instruction (4 cycles)
Process MOVB (14 cycles, includes basic register access)
Read source register (0 cycles)

Read source address (4 cycles)
Increment source register (6 cycles (not strictly correct, 2 of these cycles are memory access, but keep it simple referencing the table))
Write source register (0 cycles)

Read destination register (0 cycles)
Calculate address (4 cycles)

Read destination address (4 cycles - read before write step)

Write destination address (4 cycles)

 

I set the colors based on whether it's part of the MOVB, the *R1+, or the *R2. All memory cycles should be accounted for.

 

It's easy to get confused because the table mixes processing time and memory accesses - it's always 2 cycles for a 9900 memory access. But this value is rolled into the table so you have to just ignore it if you want to use those numbers to calculate timing.


You have the clock speed right, 0.3333 uS per cycle. So this one instruction takes 13.333 microseconds. If you have 1536 of them in a row, that's 20.48 milliseconds - just over one frame at 60hz. Your measurement of 25ms with a loop is thus pretty good. Of course, you can boost it more by putting the loop in scratchpad, but it saves just 4 cycles per byte, plus the fractional overhead of the loop instructions if you unrolled.

 

 


 

 

 

  • Like 4
  • Thanks 1
Link to comment
Share on other sites

1 hour ago, Tursi said:

It's easy to get confused because the table mixes processing time and memory accesses - it's always 2 cycles for a 9900 memory access. But this value is rolled into the table so you have to just ignore it if you want to use those numbers to calculate timing.

You have to understand that the memory accesses are listed to give you a figure to multiply any wait states with. The TI 99/4A is pretty terrible there, with four additional wait state for each 16-bit memory access. And they are all 16 bits, even if you really only want to access half of them.

Well, except for accessing memory on the 16-bit bus, where there are no wait states. For a standard console that's only the ROM chips with the main operating system and the scratch pad RAM at >8300. RAM expansion with 16-bit access hs to be mounted inside the console.

But with "normal" programming practice, you can disregard wait states for the register access, since you normally put them in scratch pad RAM. This makes it more complicated to compute the execution time for this particular computer, since them memory accesses are so different.

  • Like 1
Link to comment
Share on other sites

On 1/3/2023 at 8:44 PM, Asmusr said:

I combine pixels from two rows at the same time:

mov  *r4+,r1                    ; Even pixel
sla  r1,4                       ; Shift to high nybble
soc  *r5+,r1                    ; Odd pixel
movb r1,*r15                    ; Write to VDP
movb *r6,*r15                   ; Write low byte to VDP

Edit: r6 contains the address of r1 low byte. r15 contains VDPWD.

I didn't do the r6 trick (yet), using SWPB between both MOVB, but moving from MOVB to MOV brought the time down to 83ms (from 120), replacing @VDPWD with R15 further to 78ms. 

 

I thought I had some clean and efficient code. Discounting the 20 ms for the CALL LINK, we came from 100ms to 58ms. You are the King of Speed!

 

Heck! Now I wanna know it ... it can't be that hard to do the R6 trick as a final optimization... (10min go by...)

 

To get the address of the WP I have STWP. R1 is the second register and I want the second byte of the word... As R6 is my row-counter, I use R2.

 

       STWP R2             Get Workspace Address
       AI R2,3               R1 starts in byte 2, LSB in byte 3
 

and in the loop:

 

       MOVB R1,*R8             Write "character hi" to VRAM
       MOVB *R2,*R8           Write "character lo" to VRAM
 

Et voilá ...  76ms ... not a big leap, but "Everything counts in small amounts..." to say it in a reference to Depeche Mode.

 

I can't stop here ... I unrolled the loop to four inlines of the same code... I didn't expect a big improvement as there is much more code in the loop than in the CLEAR procedure. I had to run the speed-test twice to be sure. 70ms! 

 

Again, disregarding the slow CALL LINK, we came from 100ms to 50ms. Unbelievable! Half the time.

 

I am so happy I asked your advice! Thank you all who replied!

 

Steve

 

  • Like 2
Link to comment
Share on other sites

4 minutes ago, SteveB said:

I didn't do the r6 trick (yet), using SWPB between both MOVB, but moving from MOVB to MOV brought the time down to 83ms (from 120), replacing @VDPWD with R15 further to 78ms. 

 

I thought I had some clean and efficient code. Discounting the 20 ms for the CALL LINK, we came from 100ms to 58ms. You are the King of Speed!

 

Heck! Now I wanna know it ... it can't be that hard to do the R6 trick as a final optimization... (10min go by...)

 

To get the address of the WP I have STWP. R1 is the second register and I want the second byte of the word... As R6 is my row-counter, I use R2.

 

       STWP R2             Get Workspace Address
       AI R2,3               R1 starts in byte 2, LSB in byte 3
 

and in the loop:

 

       MOVB R1,*R8             Write "character hi" to VRAM
       MOVB *R2,*R8           Write "character lo" to VRAM
 

Et voilá ...  76ms ... not a big leap, but "Everything counts in small amounts..." to say it in a reference to Depeche Mode.

 

I can't stop here ... I unrolled the loop to four inlines of the same code... I didn't expect a big improvement as there is much more code in the loop than in the CLEAR procedure. I had to run the speed-test twice to be sure. 70ms! 

 

Again, disregarding the slow CALL LINK, we came from 100ms to 50ms. Unbelievable! Half the time.

 

I am so happy I asked your advice! Thank you all who replied!

 

Steve

 

There is no doubt, @Asmusr and several others are "gods in this world".  🙂

  • Like 1
Link to comment
Share on other sites

For the TMS 9900 in general: If you can do it in less instructions, even if you have to use slow instructions, do so. It's almost always better than using more simpler instructions.

For the TI 99/4A in specific (due to the memory architecture): Keep the register file in scratch pad RAM and use registers if there's any way at all, instead of accessing memory. But only if you do the access several times. Otherwise, obey rule one and don't spend several instructions to get an address into a register, if you can just access it directly.

  • Like 4
Link to comment
Share on other sites

Doubling down on apersson850's comment. Fewer instructions is /almost always/ faster on the 99/4A. Even the expensive multiply with setup often wins out over more than one shift with an add. ;) 

 

@SteveB - why are you using STWP? Don't you use your own workspace pointer, and so know the address in advance?

 

  • Like 2
Link to comment
Share on other sites

2 hours ago, Tursi said:

@SteveB - why are you using STWP? Don't you use your own workspace pointer, and so know the address in advance?

I want to create an Extended Basic Library like XB256, so I need to keep the Scratchpad RAM intact. And as I started assembly programming only three weeks ago, I do not feel up to the job to decide, which addresses I can safely overwrite without risking the corruption on the GPL interpreter etc. 

 

I haven't seen the advantage of an own WP when leaving R11 to R15 alone and the WP is in 16 bit Scratchpad RAM. 

 

Things would be easier as in @matthew180 fine assembly manual to take over the console completely with LIMI 0, but I don't want to go down this one-way street. I think of a library to use Multicolor in XB and later with the XB compiler. 

 

3 hours ago, Tursi said:

Even the expensive multiply with setup often wins out over more than one shift with an add.

I also considered to replace the multiplation by 48 with "X <<5 plus  X<<4", but MPY with 52 cycles is hard to beat...

  • Like 2
Link to comment
Share on other sites

4 hours ago, SteveB said:

I want to create an Extended Basic Library like XB256, so I need to keep the Scratchpad RAM intact. And as I started assembly programming only three weeks ago, I do not feel up to the job to decide, which addresses I can safely overwrite without risking the corruption on the GPL interpreter etc. 

 

I haven't seen the advantage of an own WP when leaving R11 to R15 alone and the WP is in 16 bit Scratchpad RAM. 

 

Things would be easier as in @matthew180 fine assembly manual to take over the console completely with LIMI 0, but I don't want to go down this one-way street. I think of a library to use Multicolor in XB and later with the XB compiler. 

 

I also considered to replace the multiplation by 48 with "X <<5 plus  X<<4", but MPY with 52 cycles is hard to beat...

Documentation says that >8300 is free even in BASIC, but I guess it depends on whether it's worth the setup to switch it. But the workspace should be fixed with CALL LINK too...

 

I was under the impression that under a CALL LINK, the workspace is in 8 bit RAM (specifically the low memory expansion)... but maybe that's not true.

 

 

Link to comment
Share on other sites

1 hour ago, apersson850 said:

With TI BASIC and Editor/Assembler, yes.

With Extended BASIC, no. It's GPLWS at >83E0.

CALL LINK in XB or Basic both put the value in FAC (>834A) and it is a byte value for a number or string value if a string.

CALL LINK("PGMNAM",65) in XB or Basic both put an 1 byte number in FAC   

  • Like 1
Link to comment
Share on other sites

Here is an effort to combine multicolor mode with XB. This is very simplistic - there is really only one routine that prints a single pixel on the screen. The demo uses PEEKV to get the character patterns and uses that to put the letters on the screen one character at a time. Very slow and obviously there should be an improved assembly routine that does this.

Use it this way:

CALL LOAD("DSK1.MULTICOLOR.OBJ")

The subroutines:

CALL LINK("MCON")      turns on the interrupt routine for multicolor mode. You must do this before the program starts.

CALL LINK("MC")     selects the multicolor mode

CALL LINK("G32")   selects the 32 column graphics mode

CALL LINK("PIXEL",row,column,color)   puts a pixel on the screen at row and column

CALL LINK("MAGNIF",size)              need this because CALL MAGNIFY resets to the graphics mode

CALL LINK("CLRMC",color)              sets all the pixels and the screen background to color.

 

This shows that it can be done. I will not  am not particularly motivated to do any more work on this. Feel free to add whatever routines you want to this.

As with TML, T40XB, and T80XB, you can break the program, inspect variables, and then CON to continue. You can switch from G32 to multicolor mode and the screens for both are preserved.

MULTICOLOR.GIF.73e1f73812101899590c90c839c8fda6.GIF

 

MULTICOLOR.obj

MULTICOLOR.TXT

MULTIDEMO2

 

 

Edited by senior_falcon
  • Like 11
Link to comment
Share on other sites

Hi @senior_falcon,

 

I've been studying yout code for about an hour and experimented with it just as long ... I get some understanding, but some questions as well. 

 

What I understood:

  • The MCON starts the monitor to check the stack for changes from the Garbage Collection and restores them to >1600
  • The program runs fine without MCON, until a GC happens. MCON needs to be started before the XB program in command mode
  • CALL MAGNIFY overwrites VDP Register 1 ... Your screenshot changes the multiple times the MAGNIF, your code does not (also differs in upper/lower case)

 

What I don't understand (yet)

  • How to handle MCON in a game ... I can't ask the user to "first do CALL LINK("MCON")" before run ? How to build cartridges? Activate it and then use your MAKECART?
  • BOTSTK in >8324 "VDP value stack base pointer" gets update to the base >1600 ... what about the Top of Stack >8372 ? Why not needed? Not changed by GC?
  • You define two workspaces, WKSP for your routines, MONWS for your interrupts. Both in 8 bit CPU memory? Why don't you stay at least for your routines in the XB/GPL workspace in 16 bit? 
  • Your code returns to XB with "B @>006A", not via R11. Why? I didn't find it in the "TI Ex BASIC assembler guide", but instead a "B @>0070" (aka NEXT) in another tutorial example, completing my confusion. I found a reference to GPL Status Reset in TI Intern though.
  • You use the Screen Image Table to >0C00 to keep your normal graphics screen intact? And keep Sprite Pattern Table untouched, so it uses the usual characterset?

I couldn't get my library GC-safe .. may I incorporate your interrupt routine with the due credit?

 

Thank you

Steve

 

Link to comment
Share on other sites

I have the opinion when you are trying to get something new done and using the wrong tool it gets worse over time.

Like using a hammer to fix a toaster.

 

RXB has a command CALL EXE(address) that uses the GPL Registers to EXEcute a Assembly program address.

CALL EXE(29656) is the COMPCT in ROMs 1 is garbage collection routine >73D8 it expects to run from GPL Registers and R7 is the return register to save R11

But as it is only in ROM 1 you have to turn on ROM 1 in case it was left in ROM 2 instead, so you have to do a CALL LOAD(24576,0) to turn on ROM 1

CALL LOAD(24576,0) is >6000 for ROM 1 as when you write to that address it sets ROM 1 on, or if you wanted ROM 2 you would CALL LOAD(29578,0)

So this is the lines to turn on ROM 1 and do a garbage collection in RXB:

CALL LOAD(24576,0) :: CALL EXE(29656)

 

I assume RXB is not your answer but maybe this info might help.

Link to comment
Share on other sites

4 hours ago, RXB said:

I have the opinion when you are trying to get something new done and using the wrong tool it gets worse over time.

Like using a hammer to fix a toaster.

Hi Rich,

 

I am actually progressing well and learn a lot. I am sorry if I annoy anyone with my questions.

 

As for the selection of my tool... I want to build an universal library. I want to learn TMS9900 Assembler. I wanto to be able to compile my Multicolor Mode programs and build a cartridge one day.

 

I lost my "fear" of assembly when I wanted to load my "dictionary" of a word game in low memory and learned, that RXB can do so directly, but when I want to make a cartridge, I have to make an Object-File. This is done with an Assembler. By the I only did exactly what Harry wrote in the manual for his compiler. Now I created the first assembly program, though it only consisted of BYTE commands. Then I learned to use AORG. Then I used labels to build an index to PEEK to the 3 letter words, 4 letter words, ...  before long I got more and more interested in TMS assembly language. My WordSearch game might be the most ignored new game of 2022, but still got me on the assembler track.

 

So yes, I could perhaps use RXB to do something in Multicolor Mode, perhaps write a whole game, but neither learn assembly nor make any game-cartridges.

 

Still I test my library against XB 1.1, XB 2.9 G.E.M. and RXB 2022D to make it an universal tool. 

 

  • Like 1
Link to comment
Share on other sites

30 minutes ago, SteveB said:

Hi Rich,

 

I am actually progressing well and learn a lot. I am sorry if I annoy anyone with my questions.

 

As for the selection of my tool... I want to build an universal library. I want to learn TMS9900 Assembler. I wanto to be able to compile my Multicolor Mode programs and build a cartridge one day.

 

I lost my "fear" of assembly when I wanted to load my "dictionary" of a word game in low memory and learned, that RXB can do so directly, but when I want to make a cartridge, I have to make an Object-File. This is done with an Assembler. By the I only did exactly what Harry wrote in the manual for his compiler. Now I created the first assembly program, though it only consisted of BYTE commands. Then I learned to use AORG. Then I used labels to build an index to PEEK to the 3 letter words, 4 letter words, ...  before long I got more and more interested in TMS assembly language. My WordSearch game might be the most ignored new game of 2022, but still got me on the assembler track.

 

So yes, I could perhaps use RXB to do something in Multicolor Mode, perhaps write a whole game, but neither learn assembly nor make any game-cartridges.

 

Still I test my library against XB 1.1, XB 2.9 G.E.M. and RXB 2022D to make it an universal tool. 

 

Naw never annoyed ask away.

 

Cool that I found I had messed up RXB 2021 on to RXB 2022G with a change to some VDP pointers for changing upper 24K memory and flags in XB.

Other than a few TI Basic programs and a oddball XB program most do not see those errors I created.

 

Greg found a TI Basic program that has drove me to new level of looking at every single byte of code byte by byte.

Today found a fix for a ton of those VDP pointers and should fix 2021 to RXB 2023 soon to be released.

Oddly the fix was using TI Basic RND routine that opened up 10 bytes of VDP space not used.

(It was for setting up Floating Point RANDOMIZE for XB RND)

As RXB ditched the XB RND for the TI Basic RND routine instead, it is faster and saves a ton of space too. 

 

Tursi keep saying you are the answer to many of my problems I have had.

Glad to work with you on it.

 

 

 

  • Like 1
Link to comment
Share on other sites

On 1/8/2023 at 12:25 PM, SteveB said:

Hi @senior_falcon,

 

I've been studying yout code for about an hour and experimented with it just as long ... I get some understanding, but some questions as well. 

 

What I understood:

  • The MCON starts the monitor to check the stack for changes from the Garbage Collection and restores them to >1600
  • The program runs fine without MCON, until a GC happens. MCON needs to be started before the XB program in command mode
  • CALL MAGNIFY overwrites VDP Register 1 ... Your screenshot changes the multiple times the MAGNIF, your code does not (also differs in upper/lower case)

 

What I don't understand (yet)

  • How to handle MCON in a game ... I can't ask the user to "first do CALL LINK("MCON")" before run ? How to build cartridges? Activate it and then use your MAKECART?
  • BOTSTK in >8324 "VDP value stack base pointer" gets update to the base >1600 ... what about the Top of Stack >8372 ? Why not needed? Not changed by GC?
  • You define two workspaces, WKSP for your routines, MONWS for your interrupts. Both in 8 bit CPU memory? Why don't you stay at least for your routines in the XB/GPL workspace in 16 bit? 
  • Your code returns to XB with "B @>006A", not via R11. Why? I didn't find it in the "TI Ex BASIC assembler guide", but instead a "B @>0070" (aka NEXT) in another tutorial example, completing my confusion. I found a reference to GPL Status Reset in TI Intern though.
  • You use the Screen Image Table to >0C00 to keep your normal graphics screen intact? And keep Sprite Pattern Table untouched, so it uses the usual characterset?

I couldn't get my library GC-safe .. may I incorporate your interrupt routine with the due credit?

 

Thank you

Steve

 

The MCON starts the monitor to check the stack for changes from the Garbage Collection and restores them to >1600. The program runs fine without MCON, until a GC happens. MCON needs to be started before the XB program in command mode

As you say, MCON starts up the monitor that sets the top of the stack to >1600 and keeps memory below that from being overwritten. If you never used strings you would probably be OK without calling MCON. But strings use stack space. Try this change to the demo program:

200  A$="X"::GOTO 200

Because XB wants the top of the stack to be at >0968 it will overwrite the multicolor screen and it is pretty dramatic when it gets to it. It doesn't seem to crash XB, but it does trash the multicolor display.

 

Your screenshot changes the multiple times the MAGNIF, your code does not (also differs in upper/lower case)

Turns out the demo program I uploaded was not the latest version. You can add these lines to make it be the same as the program in the video:

   205 FOR J=1 TO 4
   206 CALL LINK("MAGNIF",J)
   207 FOR I=1 TO 200 :: NEXT I
   208 NEXT J
   210 GOTO 205

The font is copied from the XB character patterns. The demo uses XB 2.9. Standard XB, XB 2.9, and RXB use different fonts, so if you use those the demo will look different.

 

How to handle MCON in a game ... I can't ask the user to "first do CALL LINK("MCON")" before run ?  There are at least 2 ways to do this:

10 CALL INIT :: CALL LOAD("DSK2.MULTICOLOR.OBJ"):: CALL LINK("MCON"):: RUN 50     This will load the assembly routines and then run the program again starting at line 50.

Once all is tested and working, you can embed MULTICOLOR.OBJ in a loader such as SYSTEX or my KWIKLOAD. This will quickly copy the routines to low memory, and you can add an instruction to the last line to load and run your program.

 

How to build cartridges? Activate it and then use your MAKECART? 

Do you want to make a cartridge containing the XB program? In theory this could be done by using 5 groms for XB, 2 - 8K banks for XB and then enough additional banks to contain the XB program which would be copied to expansion memory. Ideally you would pick a point in the XB program where everything is initialized, screens generated, graphics defined, etc. and take a picture of that. This would require 6 additional 8K banks. Sounds like a good project.... For someone else.

Or do you want to compile the program and make a cartridge from that. That can be done, but the interrupt routine complicates matters a bit. The assembly code needs a bit of adjustment for this to work, but I did it for TML, XB256, T40XB and T80XB. Get the assembly package together first and running well in XB, then I will help you with that.

 

what about the Top of Stack >8372 ? Why not needed? Not changed by GC?

Not a problem. That has nothing to do with the VDP stack.

 

You define two workspaces, WKSP for your routines, MONWS for your interrupts. Both in 8 bit CPU memory? Why don't you stay at least for your routines in the XB/GPL workspace in 16 bit? 

Mainly because it is a big pain in the buttocks. You pretty much can only use the gplws at >83E0. R0 to R10 should be safe to use, but if you use CFI then R0 to R5 are trashed. With some creativity you should be able to use GPLWS. Remember that XB is super slow and there is so much overhead just doing a CALL LINK that a small slowdown in your assembly routines will be hard to notice.

 

Your code returns to XB with "B @>006A"

That is the same as B @>007A, but also it resets the condition bit which you are supposed to do when returning to XB. B *R11 may also work, but if you want to compile this then you must use B @>006A or B @>0070.

 

You use the Screen Image Table to >0C00 to keep your normal graphics screen intact? And keep Sprite Pattern Table untouched, so it uses the usual characterset?

Yes and yes. The demo doesn't show it, but you can switch between multicolor and graphics mode with CALL LINK("MC") and CALL LINK("G32"). All the screen information is preserved.

 

may I incorporate your interrupt routine with the due credit?

Of course, that is why I posted it.

Edited by senior_falcon
  • Like 3
  • Thanks 1
Link to comment
Share on other sites

4 hours ago, senior_falcon said:

I have a feeling that would crash XB.

Correct.

 

Sorry yea normal XB will error out with  Syntax Error

You can only get away with this using ROM 1 or ROM 2 first 4K section in XB as both are the same.

 

 

 

Link to comment
Share on other sites

This compiles just fine. It is past my bed time, so I will have the details tomorrow. Meanwhile, here is a teaser video. MCOLORC is the assembly support routines, adapted for compiled code. (Very simple to do this) MDEMO2C-X is the compiled program. Note that this is running at normal speed, not CPU overdrive.

MULTICOLORC.GIF.a9f73782a63339738388a5d8cb19e179.GIF

Edited by senior_falcon
  • Like 9
Link to comment
Share on other sites

Here is a "final" package from me. I hope this leads to a more finished and polished version. I have made a few little adjustments and added 2 new call links. Now, when you turn on the interrupt with CALL LINK("MCON") the screen background is light green instead of cyan. This way you know the interrupt is active.

XBMulticolorMode.zip

In the zipped folder are the following files:

MULTICOLOR.TXT

MULTICOLOR.OBJ

MCDEMO2        the XB version of the demo. Assuming you are using DSK2, then CALL LOAD("MULTICOLOR.OBJ") then CALL LINK("MCON") then RUN "DSK2.MCDEMO2"

For the compiler:

MC4COMP        MULTICOLOR.OBJ converted for compatibility with compiler. More on how to do this below.

MCDEMO2C      MC4COMP with CALL INIT added and RUN "DSK2.MCDEMO2L-X"    When you run this, it first loads the assembly support to low memory, then runs the compiled version of MCDEMO2

MCDEMO2L-X   the compiled version of MCDEMO2

MCDEMO2L-E    EA5 files

MCDEMO2L-F        "

 

The CALL LINKs:

   CALL LINK("MCON")     turns on the interrupt routine
   CALL LINK("MCOFF")    turns off the interrupt routine
   CALL LINK("MCINIT")    needed in compiled program if you are running from XB and want to break the program, then run it again. It sets a flag so MULTICOLOR knows to initialize the screen at startup.
   CALL LINK("MC")          selects the multicolor mode
   CALL LINK("G32")         selects the usual XB graphics mode
   CALL LINK("CLRMC",color)   sets the multicolor screen to a uniform color and sets the screen background
   CALL LINK("PIXEL",row,column,color)    row from 1 to 48, column from 1 to 64, color from 1 to 16
   CALL LINK("MAGNIF",size)     size from 1 to 4

 

Using assembly support routines with compiled code is described in the manual. Here is a quick recap:

To convert MULTICOLOR.OBJ to work with the compiler:

CALL INIT

CALL LOAD("DSK2.MULTICOLOR.OBJ")

CALL LOAD("DSK1.FIXAL")          FIXAL is part of the XB Game Developer's Package

CALL LINK("X")

SAVE DSK2.MC4COMP            The assembly support routines are embedded in MC4COMP. When you run it they are quickly loaded into low memory.

 

To convert MCDEMO2 to work with the compiler:

OLD DSK2.MCDEMO2

CALL LOAD("DSK1.UC2LC")      UC2LC is part of the XB Game Developer's Package

CALL LINK("X")                       converts all the CALL LINKS to lower case

SAVE DSK2.MCDEMO2L           save it with L appended

 

Compile MCDEMO2L normally. When you get to the loader, press Y for "using assembly support"

You will have to enter DSK2.MC4COMP when asked for the assembly support file

Once loaded you can save as EA5 and also as an XB loader.

If you want to run this from XB, then:

OLD DSK2.MC4COMP

To line 10, add CALL INIT as the first instruction, and RUN "DSK2.MCDEMO2L-X" as the last instruction. (Look at MCDEMO2C if this is not clear)

SAVE DSK2.MCDEMO2C    

The program is in 2 parts. MCDEMO2C loads the assembly routines, then runs the compiled program MCDEMO2L-X.

 

 

 

  • Like 12
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...