CODING ★ Anatomy of an RSX (1/2) ★

Rsx - Anatomy Of An Rsx - Part 1 (Amstrad Action)Rsx - Anatomy Of An Rsx - Part 2 (Amstrad Action)
★ Ce texte vous est présenté dans sa version originale ★ 
 ★ This text is presented to you in its original version ★ 
 ★ Este texto se presenta en su versión original ★ 
 ★ Dieser Text wird in seiner Originalfassung präsentiert ★ 

Resident System Extensions could be the best thing to happen to your CPC since banked RAM (whatever that is). DAVID HOLMES explains why...

One of the most powerful features of the Amstrad CPC is its ability to develop Resident System Extensions (RSXs), machine code routines which can be installed in the memory and then used and handled as BASIC commands.

It is possible to write simple routines which add to or modify the existing commands supplied in the BASIC interpreter or to create sophisticated systems which can be accessed easily from within a BASIC program.

Quite professional results can be obtained with very simple code which can then be developed as the programmer's expertise increases.

The major advantages of the RSX over CALLing machine code routines are the error checking on the command name done by BASIC and the increased readability of programs using RSXs.

An error in a CALL statement will generally result in a crash. A typing error in a properly developed RSX will either give an 'Unknown Command'error or an error message delivered by the routine itself. Adequate error trapping is an important feature of any program but is especially worthwhile with machine code!



The command names are chosen by the programmer and usually reflect the function of the routine. This makes listings much easier to follow than if there are many CALLs to memory addresses. The disadvantages of RSXs are that for very short routines the additional code needed to handle the command name may be longer than the actual working program. It is also more difficult to allow the code to be relocatable although there are various ways around this.

The RSX command is prefixed by the 'I' character (Shift This tells the BASIC interpreter that it must search for an external command using the firmware routine KL_FIND_COMMAND at &bcd4. If the command specified is found the named routine is entered. If it is not found the error message 'Unknown Command'is given and the BASIC program stops.

Listing 1 is a BASIC loader for the assembler program in listing 2. This is a fairly simple screen fading program which is worth examining in some detail as it illustrates some of the features of the system.

The command to be set up is:


where pen is the pen number to use in filling the screen and rate is a number between 0 and 255 which determines the rate and pattern of screen filling.

The first section deals with the setting up of an RSX. The new command or commands must be 'logged on' to the system using the firmware call KL_LOG_EXT at &bcdl. This call requires that the BC register pair be loaded with the address of the RSX command table (which will be discussed below) and that the HL register pair be loaded with the address of a four byte workspace which the system uses to keep track of what is happening.

Both these addresses must be in the central 32K of RAM so that they may be accessed if either or both the lower or upper ROMs are enabled. Usually RSX machine code is stored just above HIMEM and it is sensible to define these addresses within the main body of the code rather than scatter them around the memory without good reason.

The command table is primarily a list of jumps to direct the system to the section of code to be executed when an external command is issued. It is possible to have many different commands all logged on with a single call to &bcdl, although in this example there is only one. The first item in the command table must however be the definition of the address of the name table, (defw name_table)

The name table defines the actual command names as used from BASIC. The name must be in upper case, avoiding spaces and commas. The last character of each name must have bit 7 set. This is achieved by adding &80 to the last character. The ' |' prefix does not form part of the name. As described above this is used by BASIC to detect that the command is not to be found in the BASIC ROM.

The order of names in the name table must correspond to the order of the jump instructions in the command table if multiple commands are being created. It is not necessary for the name in the command table to be the same as the name in the name table, although for simplicity this is often a good idea! The name table is finished with a zero byte.


  org &200

If the above instructions are followed it is very easy to convert machine code routines to RSXs.

The functional code of the |FADE command can now be examined.

The first instruction is cp 2. |FADE requires two parameters - pen and rate. When an RSX command (or a CALL) is issued followed by parameters separated by commas, the number of parameters is contained in the A register.

This provides a useful error trap. If the wrong number of parameters is passed the routine can be intercepted.

Cp 2 subtracts two from the contents of the A register (without changing the A register) and the result affects the zero and carry flags in the F register. If A contains two the result will be zero and the zero flag will be set. If A contains any other number the zero flag will not be set and the next instruction - ret nz -will return to BASIC without having any other effect.

It is possible to jump to a more sophisticated error routine e.g. print a message or sound  a bleep before returning to BASIC.

The next instruction - Id a,(ix+2) - illustrates how to access the parameters passed from BASIC. The parameters are stored in a data area pointed to by the IX register in the following way. The parameters are passed as two byte integers. They are stored in normal low byte/high byte notation in a block of memory starting from the address contained in the IX register.

The low byte of the last parameter in the list may be found at the address contained in IX and the high byte is pointed to by IX+1. The second last parameter is pointed to by IX+2 and IX+3 and so on. If there are n parameters the address of the yth parameter is found by the formula:

Low byte of address

= contents of ix +(n-y)*2 High byte of address

= contents of ix +(n-y)*2 +1

In this case IX+2 points to the low byte of the first parameter. This refers to the pen number and will normally be in the range 0 to 15. Only the low byte is needed for a number of this size. If a number greater than 255 was passed the high byte would be found at IX+3. The program ignores this so there is no need for an error trap at this point. If necessary, however, the value passed could be tested and errors could be detected and acted upon.

In this particular situation it will be seen that there is no need to worry about errors in the low byte as the next instruction copes with any illegal pen numbers.

The way the pen number is used to place a coloured pixel on the screen is not simple. It depends on screen mode and the ink to whichthe particular pen is set.

A discussion of this is not necessary here. Sufficient to say that the firmware call SCR_INK_ENCODE at &bc2c takes the value in the A register, masks out any unwanted bits and converts it to the range 0-1, 0-3 or 0-15 for screen modes 2, 1, or 0 respectively. It then returns in the A register an encoded ink value which can be poked directly into the screen memory area to display a pixel plotted in the current ink colour of the relevant pen. This value is then loaded into the D register for later use using the Id d,a instruction. The next two instructions set the E register to zero and set the HL register pair to &c000 which is normally the start of the screen memory area.

At this point nothing has happened to the screen. The DE and HL registers have been initialised with the values required at the start of the routine. The next step is to load the address pointed to by HL, i.e. &c000, with the value in D, i.e. the encoded ink. This plots a screen pixel with the pen number passed from BASIC. This will be at the top left comer of the screen if the screen has not been scrolled but may be anywhere if scrolling has occurred. It should be clear from the above discussion that Id b,(ix+0) loads B with the low byte of the second parameter which is the rate. As before it does not matter if a value greater than 255 is passed as the high byte is ignored and only the low byte is used.

The next section labelled .next uses the automated instruction djnz. This acts on the B register and allows it to be used as a loop counter. At each pass through the loop djnz next reduces B by one and jumps back to .next until B reaches zero. Every time the loop is executed HL is increased by one. This has the effect as the loop cycles of increasing HL by the value in B. It should be clear that when the loop has been completed for the first time, HL contains (&C000+the contents of IX+0).

The next section tests if HL still points to part of the screen memory and acts accordingly. Firstly A is loaded with zero and then compared with H. H contains the high byte of the memory address currently being pointed to by HL. If this address is part of the screen H will contain a value between &c0 and &ff. If H is incremented by one from a value of &ff it will become zero, As the maximum increment in HL possible is 255, the value of the H register cannot increase by more than one at each circuit of the loop.

This allows the testing to be performed in the following way:

Cp h subtracts the value in H from the value in A without affecting either register and sets the zero and carry flags appropriately. As long as H is greater than A this operation will set the carry flag. The jr nc test will fail and then program jumps back to .plot with HL incremented by the number passed from BASIC in the rate parameter. If H equals zero there will not be a carry and the program jumps to .row. It would be equally valid to test the zero flag with a jr z,row instruction.

It is important to note that following the increment in HL its value is tested before the contents of the D register are poked into it. If the test was made afterwards there would be a risk of crashing the system by poking the D register contents into the system memory from &0000 upwards.

The final section .row when reached for the first time increments the E register from zero to one. HL is reset to &c000 and L is loaded with the value in E. This leaves HL containing &c001. The A register is loaded with E to allow it to be compared with the contents of IX+0 (the rate parameter).

If A (and therefore E) is not equal to (IX+0) then the program loops back to .plot and runs through the screen memory again. This time the addresses loaded with the encoded pen number in D are stepped up by one from the previous circuit of the loop.

Every time that .row is reached E is incremented until it reaches the value passed by the rate parameter. When this occurs the main loop at .plot has been performed 'rate' times and the whole screen has been filled with the pen required. The comparison gives a zero result and the routine returns to BASIC.

To install the RSX command the code can be assembled at the required location or can be loaded by a BASIC loader, e.g. Listing 1. It may also be loaded as a binary file at the address to which it was assembled.

Once the code is loaded into place the routine must be CALLed to log on the command. It is important to only CALL the code once as further CALLs may corrupt the workspace with unreliable results. Other RSXs with different workspaces may be added as desired however.

The effect of the |FADE command varies with the rate parameter. It is best to experiment with different values to find those which give the desired appearance. |FADE,0,9 is useful for clearing text.

This program could be developed further if required. For example the program as it stands assumes that the screen memory is from &c00Q to &ffff. If the memory being used is the &4000 to &7fff block it will not work as expected. Additional code could be added to detect which area is in use with a call to the firmware routine SCR GET LOCATION at &bc0b. This call returns the high byte of the start of screen memory in the A register. This could then be used to initialise the screen start positions. Further testing of HL at the end of the screen memory block would also be required.

Next time: David Holmes tackles the tricky task of RSX relocation. Don't miss it!


CPCrulez[Content Management System] v8.7-desktop/cache
Page créée en 200 millisecondes et consultée 1002 fois

L'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent  du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko.