Advertisement Jump to content
  • Advertisement
  • entries
    222
  • comments
    606
  • views
    591686

About this blog

Z80 and C#-related shenanigans - now with added electronics.

Entries in this blog

 

STM8S-Discovery review and tutorial

STMicroelectronics recently released the STM8S-Discovery, an exceedingly cheap (RRP $7) evaluation kit for one of their 8-bit microcontrollers. It features the microcontroller itself (an STM8S105C6), running at up to 16MHz and offering 32KB of program memory, 2KB of RAM and 1KB of data EEPROM. This microcontroller has a solid set of on-board peripherals, including four timers (one advanced, one general-purpose, and one basic), SPI, I2C, UART, and ADC - so there are plenty of connectivity options. The device offers up to 38 general-purpose I/O pins.


The evaluation board is pretty feature-packed, and includes an integrated ST-LINK for programming and debugging over USB. The circuit board has been designed so that you can simply snap off the ST-LINK part if you'd like to use the microcontroller on its own. Standard 0.1" pin headers are provided to permit you to connect the board to other components, and a small wrapping area is also present. A jumper can be used to select 5V or 3.3V operation.

A green LED and a touch sensitive key are built into the evaluation board; the device comes pre-programmed with a demo program that flashes this LED at different rates when you touch the key.

All in all, a decent piece of kit - but what really sets it apart is its price. ST's recommended price is $7; I bought mine for GBP4.25 from Farnell. All you need to do is provide a USB A to USB B cable and download the various development tools (Windows only at the time of writing), datasheets and libraries.

First impressions

I really wasn't expecting much for GBP4.25, but am very impressed with the hardware. It's solidly built and comes attractively packaged, with clear instructions on the back of the box on how to run the demo program (just plug it in to a USB port!) and where to go next for development tools.

Unfortunately, what appears to be lacking at the moment - not surprisingly for a new product - is guidance for absolute beginners with ST's microcontrollers. Hopefully that will improve as more people get hold of these new kits, as their incredibly low price and self-contained nature makes them ideal for beginners (no need to build up a collection of parts or buy a programmer to get started, just plug and play). I personally found the documentation quite baffling, and judging by a thread on Hack a Day I wasn't the only one.

Disclaimer

I'm not an expert with ST's microcontrollers, but I have at least managed to get something running on the microcontroller after a few frustrating hours spent with the current documentation. The following code may not be the best, but it is simple and it should work (if I've missed anything out or not been clear enough anywhere, please let me know so I can fix it). It doesn't go into any great detail; my assumption is that if you can get some code you've written yourself running on your evaluation board and understand how to use the basic peripherals offered by the microcontroller to work with the board's LED and touch key you should be ready enough to dive into the official documentation and sample code yourself!

Getting started

If you visit the STM8S-Discovery page, you will see options to download various pieces of software and development tools. You'll need to download the following:
A C compiler (I'm currently using Cosmic's, but it's worth installing the Raisonance compiler too). The ST MCU Toolset, which includes the ST Visual Develop IDE. The application development package (this contains examples and relevant libraries to access the various peripherals of the chip). The STM8S105C6 datasheet (invaluable device specifications/information). The STM8S microcontroller family reference manual (a general overview of the STMS8 family, including documentation on the various on-board peripherals). The STM8S-Discovery evaluation board user manual (information specific to the evaluation board, including circuit diagrams and other specifications). There is a document that accompanies the development package, Developing and debugging your STM8S-DISCOVERY application code, which contains instructions on setting up a project - confusingly, these directions don't appear to apply to Raisonance's tool chain, and rely on copying and pasting files from the demo programs. You will need to register the compilers to be able to use them, and Cosmic's registration process is handled by a human so may take some time for you to receive your licence key.

Debugging one of the sample programs

One way to verify that everything is set up correctly and to try out the development tools is to build one of the sample programs included as part of the application development package. In this zip file you will find a directory named STM8S-Discovery_dev; extract this somewhere sensible. Run ST Visual Develop, and select File->Open Workspace. Open the file STM8S-Discovery_dev\Project\Discover\STVD\Cosmic\Discover.stw - this is the project that came pre-loaded onto the evaluation board. If you click Build->Build it should crunch away and after a few seconds should report that there were no errors.

We'll now need to set up the IDE to use your evaluation board's ST-LINK as its debugging instrument. Click Debug instrument->Target Settings and select Swim ST-Link in the dropdown. You can leave the other settings as they are; click OK to close the dialog.


If you now click Debug->Start Debugging (or the blue "D" in the toolbar) the IDE should program the evaluation board and then enter the debugger in "Break" mode. Press Continue (F5) to start running the program; at this point you should be able to use the touch sensitive key on the board to change the rate at which the green LED flashes. When you're done, click Debug->Stop Debugging (or the red cross button in the toolbar) to stop debugging.


The debugger shouldn't be especially surprising to anyone who has used a graphical debugger (e.g. Visual Studio) before. Try going to line 148 in main.c - BlinkSpeed++; - and selecting Edit->Insert/Remove Breakpoint. Start debugging as before, and you'll notice that when you touch the key this time the debugger breaks on that line. Click Debug->Continue and the program will continue. Marvellous - all pretty intuitive thus far.

Creating your own project

Creating your own project is a rather more involved process, as there's quite a lot you need to set up first. Hopefully this step-by-step guide should help!

Firstly, click File->New Workspace, and select Create Workspace and Project from the dialog that appears. Now, we need to store our workspace (analagous to a solution in Visual Studio) somewhere; create a new directory for the workspace that is preferably not inside Program Files and give the workspace a name. I'm going to go for "Blinkenlight" as my workspace name. Create a new project with the same name as the solution name. It should default to the same directory; keep this as it is. Select STM8 Cosmic as the toolchain. Select STM8S105C6 as the microcontroller. With that done, you should have a shiny new workspace and project containing two files - main.c, containing the entry point for your application, and stm8_interrupt_vector.c, used to associate interrupt requests with interrupt service routines - more on those later. If you build the project and try to debug it you'll note that it does precisely nothing of use. Let's make it do something useful!

Adding the standard firmware library

To access the various peripherals of the microcontroller, ST have provided an extensive standard firmware library. You will need to download this from the STM8S documents and files page; it's the zip archive named STM8S firmware library. Open the zip archive, and copy the FWLib\library folder to your own project folder. You may wish to rename the library folder FWLib, so you should end up with the two folders Project\FWLib\inc and Project\FWLib\src.

There is a folder named project in the firmware library download - copy stm8s_conf.h from this folder into the root of your project folder.

Now, return to ST Visual Develop. Use the workspace panel to the left to create two new folders in your project - Source Files\FWLib and Include Files\FWLib. Add all of the files from FWLib\inc to Include Files\FWLib and stm8s_conf.h to the root of Include Files. You should now have something that looks like this:


You will also need to inform the firmware library that you are using an STM8S105 microcontroller as opposed to the default STM8S208. Here are two ways of doing this - pick whichever seems easiest to you.
Open stm8s.h in Project\FWLib\inc, comment out #define STM8S208 and uncomment #define STM8S105 near the top of the file. Pass the definition directly to the compiler by clicking Project->Settings, selecting the C Compiler tab and typing STM8S105 into the Preprocessor Definitions field. You will need to do this twice; once for the Debug configuration and once for Release (select the configuration with the drop-down box in the top left).
Illuminating the LED using GPIO

The cathode of the evaluation board's LED is connected to PD0 on the microcontroller - that is, pin 0 of port D. By driving this pin low we could therefore illuminate the LED. If you consult the documentation for the standard firmware library - it's the stm8s_fwlib_um.chm file in the zip archive - you can see a number of helper functions dedicated to GPIO, or "general-purpose I/O". To use the GPIO functions, we need to do two things:
Add FWLib\src\stm8s_gpio.c to the project under Source Files\FWLib. Enable inclusion of the relevant GPIO header files, achieved by uncommenting #define _GPIO (1) in stms8_conf.h Both steps will need to be carried out whenever you want to use a new peripheral (e.g. a timer or the UART). Once that's done, you can modify main.c to read as follows:

#include "stm8s.h"

int main(void) {

// Reset ("de-initialise") GPIO port D.
GPIO_DeInit(GPIOD);

// Initialise pin 0 of port D by setting it as:
// - an output pin,
// - using a push-pull driver,
// - at a low logic level (0V), and
// - 10MHz.
GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);

// Infinite loop.
for(;;);
}

There are more comments than code there - using the firmware library makes life rather easy, once it's set up! If you start debugging that, you'll note that the LED does indeed light up. It's not much, but it's a sign of life.

Flashing the LED using a delay loop

The GPIO library provides a handy GPIO_WriteReverse() function, which inverts the state of a pin. By toggling PD0, we can make the LED flash. To slow this flashing down at a rate we can see, a delay loop is introduced that delays for 50,000 nops, plus overhead of the for loop structure, between calls to GPIO_WriteReverse().

#include "stm8s.h"

int main(void) {

// Reset ("de-initialise") GPIO port D.
GPIO_DeInit(GPIOD);

// Initialise pin 0 of port D.
GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);

// Infinite loop.
for(;;) {

// Delay for a short while.
u16 d;
for (d = 0; d 50000; ++d) {
// Without a nop() in here, the entire loop would be optimised away!
nop();
}

// Invert the LED pin's state to flash it.
GPIO_WriteReverse(GPIOD, GPIO_PIN_0);
}
}

The reason for the nop() is that without it, the compiler optimises away the entire for loop as it does nothing useful.

Well, that's a bit more dynamic, but surely there's a better way to do this than a hard-coded delay loop?

Timers

Timers - of which the STM8S has four with varying capabilities - are an extremely versatile peripheral. They are typically based around a counter, which counts up or down, and various events can be triggered when this counter reaches particular values. We'll start here by using the TIM3 peripheral.

To use TIM3 we need to perform the following steps, as we did previously for GPIO:
Add FWLib\src\stm8s_tim3.c to the project under Source Files\FWLib. Uncomment #define _TIM3 (1) in stms8_conf.h
By default, the microcontroller uses its internal 16MHz RC oscillator ("HSI", or high-speed internal) divided by eight as a clock source. This results in a base timer frequency of 2MHz. When configuring a timer, you can specify a prescaler to further divide the clock frequency - if you were to divide the 2MHz clock frequency by 2000, the timer would count up once every millisecond.

TIM3 restricts the prescaler to powers of two between 1 and 32,768, so we'll use a prescaler of 2048 (that's close enough for jazz). You also need to specify a timer period; this is the value up to which the timer will count before resetting itself. With a period of 999, the timer will run from 0 to 999 before resetting - approximately one thousand milliseconds, or one second, from start to finish.

// Reset ("de-initialise") TIM3.
TIM3_DeInit();

// Set TIM3 to use a prescaler of 2048 and have a period of 999.
TIM3_TimeBaseInit(TIM3_PRESCALER_2048, 999);

// Enable TIM3.
TIM3_Cmd(ENABLE);

Using the standard firmware library allows for easy timer configuration. Now that we have a timer running, we can query its counter value to flash the LED - if it's in the range 0~499, switch the LED on; if it's in the range 500~999, switch the LED off. Building on the ealier source code, here's a program that does just that:

#include "stm8s.h"

int main(void) {

// Reset ("de-initialise") GPIO port D.
GPIO_DeInit(GPIOD);
// Initialise pin 0 of port D.
GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);

// Reset ("de-initialise") TIM3.
TIM3_DeInit();
// Set TIM3 to use a prescaler of 2048 and have a period of 999.
TIM3_TimeBaseInit(TIM3_PRESCALER_2048, 999);
// Enable TIM3.
TIM3_Cmd(ENABLE);

// Infinite loop.
for(;;) {
if (TIM3_GetCounter() 500) {
// Output a low on the LED pin to illuminate it.
GPIO_WriteLow(GPIOD, GPIO_PIN_0);
} else {
// Output a high on the LED pin to switch it off.
GPIO_WriteHigh(GPIOD, GPIO_PIN_0);
}
}
}


Pulse-width modulation for flashing

One of the many features of these timers is the ability to generate pulse-width modulation - PWM - output on dedicated pins. When this feature is enabled, the timer will set the output pin to one logic level when it starts or restarts and to another when it reaches a used-defined threshold. This is effectively what we're doing in our current program, just manually - far better if the timer could do it for us automatically!

The green LED is connected to PD0, which also acts as TIM3_CH2, or TIM3's channel 2. We can remove most of the code from our previous program, including the GPIO code, leaving us with the following:

#include "stm8s.h"

int main(void) {

// Reset ("de-initialise") TIM3.
TIM3_DeInit();

// Set TIM3 to use a prescaler of 2048 and have a period of 999.
TIM3_TimeBaseInit(TIM3_PRESCALER_2048, 999);

// Initialise output channel 2 of TIM3, by setting:
// - PWM1 mode (starts activated, deactivates when capture compare value is hit),
// - output is enabled,
// - capture compare value of 500, and
// - an active signal is low (0V).
TIM3_OC2Init(TIM3_OCMODE_PWM1, TIM3_OUTPUTSTATE_ENABLE, 500, TIM3_OCPOLARITY_LOW);

// Enable TIM3.
TIM3_Cmd(ENABLE);

// Infinite loop.
for(;;);
}

In PWM1 mode, the output starts in the activated state. As we have specified that TIM3_OCPOLARITY_LOW is an activated state, this means that the output will start at a logic low (the LED will be illuminated). When the capture compare value (500) is reached, the output will switch to the deactivated state (logic high) and the LED will switch off. If you run this program as before you will see that the LED does indeed flash on and off automatically.

Pulse-width modulation to change brightness

As the LED is on for approximately 500ms and off for approximately 500ms it is on half of the time. On average, therefore, it is at half its possible brightness. If you modify the 500 in the TIM3_OC2Init function call to 250 and run the program again you will see that on for a quarter of the possible time, and at 750 it is on for three quarters of the possible time. By increasing the rate at which the LED flashes so that it appears to be continuously lit we can control its apparent brightness by adjusting the relative amount of time it is switched on in comparison to the time it is switched off.

We can increase the rate at which the LED flashes by reducing the prescaler of TIM3. Try changing the TIM3_TimeBaseInit call to use TIM3_PRESCALER_16 instead of TIM3_PRESCALER_2048, and change the capture compare value in the TIM3_OC2Init call to 100. When you run your program, the LED on the board will appear to be dimly lit. If you pick up the board and very carefully shake it from side to side you should be able to see that the LED is flashing from the dotted trace it leaves in the air. If you drop the prescaler all the way down to TIM3_PRESCALER_1 you will find that you have to shake the board much faster, but take care not to damage anything!

The brightness of the LED can be modified at runtime by changing the value of the capture compare register with the TIM3_SetCompare2 function.

#include "stm8s.h"

// Short delay loop.
void delay(void) {
u16 d;
for (d = 0; d 150; ++d) {
nop();
}
}

int main(void) {

// Reset ("de-initialise") TIM3.
TIM3_DeInit();

// Set TIM3 to use a prescaler of 1 and have a period of 999.
TIM3_TimeBaseInit(TIM3_PRESCALER_1, 999);

// Initialise output channel 2 of TIM3.
TIM3_OC2Init(TIM3_OCMODE_PWM1, TIM3_OUTPUTSTATE_ENABLE, 0, TIM3_OCPOLARITY_LOW);

// Enable TIM3.
TIM3_Cmd(ENABLE);

// Infinite loop.
for(;;) {

u16 brightness;

// Set the brightness from 0 to 999 in a loop (fade up).
for (brightness = 0; brightness 1000; ++brightness) {

// Set the brightness of the LED by modifying the capture compare register
// for TIM3's channel 2.
TIM3_SetCompare2(brightness);

// Delay a short while.
delay();
}

// Set the brightness from 1000 to 1 in a loop (fade down).
for (brightness = 1000; brightness > 0; --brightness) {

// Set the brightness of the LED.
TIM3_SetCompare2(brightness);

// Delay a short while.
delay();
}
}
}

The above program fades the LED up from the minimum brightness to the maximum brightness then back down again in an infinite loop. It also reintroduces our old enemy, the delay loop, which leads us neatly on to the next subject - interrupts.

Interrupts

Interrupts provide a way to respond to events in a way that doesn't require that we constantly check (poll) the event source. One such event is a timer overflowing - we can use this event to update the brightness of the LED every millisecond without having to poll a timer's counter manually or use delay loops.

We'll use TIM1 to generate the interrupt; it provides a few additional features that are not present on the other timers, but we'll need to keep TIM2 and TIM4 free for later. As before, you'll need to add stm8s_tim1.c to your project and uncomment #define _TIM1 (1) in stm8s_conf.h.

We'll start by adding some skeleton interrupt handler code and reference it in the interrupt vector table to ensure that it is called when the timer updates itself. Firstly, add the two following files to your project:

stm8s_it.c
#include "stm8s.h"
#include "stm8s_it.h"

void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void) {
// TODO: Implement TIM1 update interrupt handler.
}

stm8s_it.h
#ifndef __STM8S_IT_H
#define __STM8S_IT_H

@far @interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void);

#endif

These stm8s_it files contain the interrupt request handlers. Interrupt handler functions are called via the interrupt vector table, which is defined in the stm8_interrupt_vector.c file that was automatically generated when you created the project. Open this file and add #include "stm8s_it.h" to the top of it so that it can see your interrupt handler functions. According to the STM8S105xx datasheet the TIM1 update/overflow interrupt is mapped to IRQ 11, so scroll down the table of interrupt vectors and change NonHandledInterrupt on the line marked irq11 (some lines omitted for clarity):

struct interrupt_vector const _vectab[] = {
{0x82, (interrupt_handler_t)_stext}, /* reset */
{0x82, NonHandledInterrupt}, /* trap */
{0x82, NonHandledInterrupt}, /* irq0 */
/* [...] */
{0x82, NonHandledInterrupt}, /* irq10 */
{0x82, (interrupt_handler_t)TIM1_UPD_OVF_TRG_BRK_IRQHandler}, /* irq11 */
{0x82, NonHandledInterrupt}, /* irq12 */
/* [...] */
{0x82, NonHandledInterrupt}, /* irq29 */
};

Now we have that in place we can start writing the interrupt handler code. Internally, interrupts are signalled by setting a flag in a control register, which the microcontroller periodically checks. If you do not clear this flag the microcontroller will call your interrupt handler again as soon as you return from the function, so you must remember to do so - this is done with the TIM1_ClearITPendingBit(TIM1_IT_UPDATE) function. Using a variable to store the current brightness "direction" (positive to get brighter; negative to get dimmer) the LED brightness could be adjusted every time the timer overflowed using the following code:

stm8s_it.c
#include "stm8s.h"
#include "stm8s_it.h"

s16 brightness_direction = +1; // Start by getting brighter.

void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void) {

// Get the current brightness.
u16 current_brightness = TIM3_GetCapture2();

// Check whether we've hit the maximum/minimum brightness yet.
if (brightness_direction > 0) {
// We're currently getting brighter.
if (current_brightness == 1000) {
// We're already at the maximum brightness; start getting darker.
brightness_direction = -1;
}
} else {
// We're currently getting dimmer.
if (current_brightness == 0) {
// We're already at the minimum brightness; start getting brighter.
brightness_direction = +1;
}
}

// Update the brightness of the LED according to the brightness "direction".
TIM3_SetCompare2(current_brightness + brightness_direction);

// Clear the interrupt pending bit for TIM1.
TIM1_ClearITPendingBit(TIM1_IT_UPDATE);
}

We also need to configure TIM1 to generate interrupts. This can be done with the TIM1_ITConfig function, in addition to the existing code used to configure TIM3:

main.c
#include "stm8s.h"

int main(void) {

// Reset ("de-initialise") TIM3.
TIM3_DeInit();
// Set TIM3 to use a prescaler of 1 and have a period of 999.
TIM3_TimeBaseInit(TIM3_PRESCALER_1, 999);
// Initialise output channel 2 of TIM3.
TIM3_OC2Init(TIM3_OCMODE_PWM1, TIM3_OUTPUTSTATE_ENABLE, 0, TIM3_OCPOLARITY_LOW);
// Enable TIM3.
TIM3_Cmd(ENABLE);

// Reset ("de-initialise") TIM1.
TIM1_DeInit();
// Set TIM1 to:
// - use an exact prescaler of 1000,
// - to count up,
// - to have a period of 1, and
// - to have a repetition counter of 0.
TIM1_TimeBaseInit(1000, TIM1_COUNTERMODE_UP, 1, 0);
// Set TIM1 to generate interrupts every time the counter overflows (every ms).
TIM1_ITConfig(TIM1_IT_UPDATE, ENABLE);
// Enable TIM1.
TIM1_Cmd(ENABLE);

// Enable interrupts (no, really).
enableInterrupts();

// Infinite loop.
for(;;);
}


Interrupts are globally disabled by default, hence the need to call enableInterrupts(). If you run this program, you should find that the LED fades in and out as before, but without the need for hacky delay loops. As you can see, TIM1 takes a few additional parameters to its TIM1_TimeBaseInit function; you aren't limited to powers of two for its prescaler, it can count in a number of different ways and you can specify a "repetition count" that will only update the timer registers after a given number of cycles of the counter (in this case, we've disabled that feature).

Touch key input

As well as an LED for output, the evaluation board provides a touch key for input. This requires considerably more computing power to handle than a conventional push switch, but is considerably more interesting! Thankfully, ST have provided a royalty-free library to handle touch sensing keys, sliders and wheels with their microcontrollers which we can use:
Visit the documents and files page again to download the STM8S Touch Sensing Library; it is packaged as an installer, which should be run. Go to the installation directory and copy Libraries\STM8_TouchSensing_Driver to your own project folder as you did for FWLib previously. Move Inc\STM8_TSL_RC_Configuration_TOADAPT.h to the root of your project folder and rename it STM8_TSL_RC_Configuration_TOADAPT.h (remove "_TOADAPT"). When you have copied the files, switch back to your project and follow these steps:
Create a folder Touch Sensing Library under Source Files and add all of the files in STM8_TouchSensing_Driver\Src apart from STM8_TSL_RC_MultiChannelKey.c to it. Create a folder Touch Sensing Library under Include Files and add all of the files in STM8_TouchSensing_Driver\Inc to it. Add STM8_TSL_RC_Configuration.h to the root of Include Files. Due to some functions needing to be aligned to even memory addresses, you will need to modify your linker settings. In the IDE, click Project->Settings and switch to the Linker tab. Set the Category dropdown to Input, expand the Code, Constants section and add a section named .TSL_IO_ALCODE with its options set to -r2. You will need to do this to both Debug and Release configurations.


Now we need to go and configure STM8_TSL_RC_Configuration.h for our particular hardware. Open this file, and make the following amendments:
TIMACQ will need to be changed to TIM2 as we're using TIM3 to drive our LED. TIMACQ_CNTR_ADD needs to be changed to 0x530A to match the change to TIM2. The touch key is attached to GPIO port C so LOADREF_PORT_ADDR needs to be changed to GPIOC_BaseAddress. The load reference is connected to pin PC2, so we need to change LOADREF_BIT to 0x04 (1 We only have one key, so change SCKEY_P1_KEY_COUNT to 1. The touch key input is connected to PC1, so leave SCKEY_P1_PORT_ADDR at GPIOC_BaseAddress and SCKEY_P1_A at 0x02 (1SCKEY_P1_B to SCKEY_P1_H) to 0. As we don't have any keys on a second port, set SCKEY_P2_KEY_COUNT, SCKEY_P2_PORT_ADDR and SCKEY_P2_A--SCKEY_P2_H to 0. As we don't have any multi-channel keys, set NUMBER_OF_MULTI_CHANNEL_KEYS to 0. The touch key electrodes are connected to PC1 and PC2, so set GPIOC_ELECTRODES_MASK to 0x0A (0b00001010). Set all of the other electrode masks to 0. Whew, quite a lot of work there! Now we've set that up, we can get programming. Try building your project; it should take a bit longer than before, but not emit any errors if you've set things up correctly!

The first thing we need to change in our program is to switch to running at 16MHz, a requirement of the touch sensing library. To do this, we need to use the CLK peripheral library; add stm8s_clk.c to Source Files\FWLib as before, and uncomment #define _CLK (1) in stm8s_conf.h. Now add CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1) to the start of your main() function to set the internal high-speed oscillator prescaler to 1 (it defaults to eight). If you now build and run your project you will notice that the LED fades up and down much faster - eight times faster, in fact. Change the TIM1 prescaler to 8000 to revert to the old speed:

#include "stm8s.h"

int main(void) {

// Set the internal high-speed oscillator to 1 to run at 16/1=16MHz.
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);

// Reset ("de-initialise") TIM3.
TIM3_DeInit();
// Set TIM3 to use a prescaler of 1 and have a period of 999.
TIM3_TimeBaseInit(TIM3_PRESCALER_1, 999);
// Initialise output channel 2 of TIM3.
TIM3_OC2Init(TIM3_OCMODE_PWM1, TIM3_OUTPUTSTATE_ENABLE, 0, TIM3_OCPOLARITY_LOW);
// Enable TIM3.
TIM3_Cmd(ENABLE);

// Reset ("de-initialise") TIM1.
TIM1_DeInit();
// Set TIM1 to use a prescaler of 8000 and to have a period of 1.
TIM1_TimeBaseInit(8000, TIM1_COUNTERMODE_UP, 1, 0);
// Set TIM1 to generate interrupts every time the counter overflows (every ms).
TIM1_ITConfig(TIM1_IT_UPDATE, ENABLE);
// Enable TIM1.
TIM1_Cmd(ENABLE);

// Enable interrupts.
enableInterrupts();

// Infinite loop.
for(;;);
}

Now we've got that organised, we can go ahead with using the touch sensing library. Start by adding #include "STM8_TSL_RC_API.h" to the top of main.c. We need to initialise the library and the touch key in our main function; add the following lines after the CLK_HSIPrescalerConfig call:

// Initialise the touch sensing library.
TSL_Init();
// Initialise the key (we only have one key).
sSCKeyInfo[0].Setting.b.IMPLEMENTED = 1; // It's implemented...
sSCKeyInfo[0].Setting.b.ENABLED = 1; // ...and enabled.

The touch sensing library makes use of a timer interrupt. We've set TIMTICK to TIM4, so need to attach the TIM4 update/overflow interrupt (IRQ 23) to TSL_Timer_ISR. Open stm8_interrupt_vector.c, add #include "STM8_TSL_RC_API.h" to the top of it, then modify the vector marked irq23:

#include "stm8s_it.h"
#include "STM8_TSL_RC_API.h"

/* [...] */

struct interrupt_vector const _vectab[] = {
{0x82, (interrupt_handler_t)_stext}, /* reset */
{0x82, NonHandledInterrupt}, /* trap */
{0x82, NonHandledInterrupt}, /* irq0 */
/* [...] */
{0x82, NonHandledInterrupt}, /* irq22 */
{0x82, (interrupt_handler_t)TSL_Timer_ISR}, /* irq23 */
{0x82, NonHandledInterrupt}, /* irq24 */
/* [...] */
{0x82, NonHandledInterrupt}, /* irq29 */
};

Some lines are, as before, omitted for clarity.

The infinite loop at the end of the program will need to be modified to call the TSL_Action function to update the touch sensing library's internal state machine, then check the state of the touch sensing library to see if there's any input to be processed:

// Infinite loop.
for(;;) {
// Update the touch sensing library's state machine.
TSL_Action();
// Check to see if something has happened, and that we're in the idle state before handling it.
if ((TSL_GlobalSetting.b.CHANGED) && (TSLState == TSL_IDLE_STATE)) {
// Clear the "something has changed" flag.
TSL_GlobalSetting.b.CHANGED = 0;
// Has our key been pressed/detected?
if (sSCKeyInfo[0].Setting.b.DETECTED) {
nop(); //
}
}
}

Set a breakpoint on the nop() line, then build and run the program. The LED will fade up and down as before, but if all has gone to plan touching the key should break execution on the nop() line. A slightly more useful program is shown below, modifying the current LED fading code to only fade out and using the touch key to set the LED to its maximum brightness when tapped.

main.c
#include "stm8s.h"
#include "STM8_TSL_RC_API.h"

int main(void) {

// Set the internal high-speed oscillator to 1 to run at 16/1=16MHz.
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);

// Initialise the touch sensing library.
TSL_Init();
// Initialise the key (we only have one key).
sSCKeyInfo[0].Setting.b.IMPLEMENTED = 1; // It's implemented...
sSCKeyInfo[0].Setting.b.ENABLED = 1; // ...and enabled.

// Reset ("de-initialise") TIM3.
TIM3_DeInit();
// Set TIM3 to use a prescaler of 1 and have a period of 999.
TIM3_TimeBaseInit(TIM3_PRESCALER_1, 999);
// Initialise output channel 2 of TIM3.
TIM3_OC2Init(TIM3_OCMODE_PWM1, TIM3_OUTPUTSTATE_ENABLE, 0, TIM3_OCPOLARITY_LOW);
// Enable TIM3.
TIM3_Cmd(ENABLE);

// Reset ("de-initialise") TIM1.
TIM1_DeInit();
// Set TIM1 to use a prescaler of 8000 and to have a period of 1.
TIM1_TimeBaseInit(8000, TIM1_COUNTERMODE_UP, 1, 0);
// Set TIM1 to generate interrupts every time the counter overflows (every ms).
TIM1_ITConfig(TIM1_IT_UPDATE, ENABLE);
// Enable TIM1.
TIM1_Cmd(ENABLE);

// Enable interrupts.
enableInterrupts();

// Infinite loop.
for(;;) {
// Update the touch sensing library's state machine.
TSL_Action();
// Check to see if something has happened, and that we're in the idle state before handling it.
if ((TSL_GlobalSetting.b.CHANGED) && (TSLState == TSL_IDLE_STATE)) {
// Clear the "something has changed" flag.
TSL_GlobalSetting.b.CHANGED = 0;
// Has our key been pressed/detected?
if (sSCKeyInfo[0].Setting.b.DETECTED) {
TIM3_SetCompare2(1000);
}
}
}
}

stm8s_it.c
#include "stm8s.h"
#include "stm8s_it.h"

void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void) {

// Get the current brightness.
u16 current_brightness = TIM3_GetCapture2();

// If it's brighter than zero, dim it by one unit.
if (current_brightness > 0) {
TIM3_SetCompare2(current_brightness - 1);
}

// Clear the interrupt pending bit for TIM1.
TIM1_ClearITPendingBit(TIM1_IT_UPDATE);
}


Conclusion

Now that you've got this far, you should be able to delve into the documentation and samples provided by ST to find out more about this platform. If ST can keep the price as low as they currently recommend, then this is an extremely attractive platform for hobbyists, especially beginners as you don't need any additional tools barring a USB A to USB B cable - I have deliberately avoided interfacing with external components, for that reason. The hardware is extremely capable, low price or not, so I'm sure we'll see many interesting projects created with this board as a starting point!

Further Reading
Mr Foo has written a useful tutorial on mixing C and assembly. m_kisacanin has written a handy beginner's GPIO primer.

benryves

benryves

 

Repairing a PlayStation controller to USB adaptor

I recently purchased an inexpensive PlayStation controller USB adaptor for my PC. Several reviews confirmed that it was compatible with the controller's analogue joysticks so I thought it would be what I was after. Life is rarely that easy with cheap electronics, unfortunately!



When it arrived I plugged it in and Windows installed the appropriate HID drivers for it automatically, but as much as I waggled the joysticks on a connected DualShock 2 controller the axis preview in Control Panel remained resolutely in the zero position. PlayStation controllers have an "Analog" button that can be pressed to toggle between digital and analogue modes, but any attempts to press this resulted in the "Analog" light briefly flashing before immediately switching off again.

Thinking it may be a driver issue I tried to install the drivers from the mini CD that had been included with the adaptor. My PC could not read the disc (it appeared to be scratched, and was not very well protected in postage) so I hunted around online until I found a package that worked using the device's USB ID (VID_0810&PID_0001). This enabled the controller's rumble/vibration feature, but I still couldn't get analogue input to work. Thinking that if one driver package could add vibration support, another might add analogue support I contacted the Amazon seller to ask them if they could send me a copy of the correct drivers - they instead chose to send me a whole other unit in the post.

In the meantime, I experimented with another controller plugged into the adaptor. I was surprised to find that with two controllers plugged in at once I could enable analogue mode on one of the controllers. This made me think there could be a power issue - the second controller increased the capacitance across the power supply, which would make it more resilient to voltage spikes and reduce ripple that could be causing the controller to reset out of analogue mode. This was further confirmed by plugging the adaptor with a single controller into a powered USB hub - in this scenario the controller would only leave analogue mode when vibrating. I checked the power supply pins on the controller ports and was very surprised to see that there was apparently nothing connected to pin 5, which is supposed to deliver +5V to the controllers. At this point I decided to dismantle the adaptor to see what was going on.



On the inside of the adaptor I could see that several components had been omitted. This could be to blame on cost-cutting measures (e.g. the LEDs D1 and D2 which are purely cosmetic) but the removal of D3 puzzled me the most - this diode is connected between USB VCC and the controller port pin 5, and is presumably responsible for providing power to the connected controller. I put this down to an oversight at the factory, and soldered a 1N4001 rectifier diode in the marked place.



The above image shows a close-up of the place the missing diode should appear - D3 is indicated by a silk-screened diode symbol. Unsurprisingly the 1N4001 silicon diode has far superior characteristics to the silk-screen diode it replaced.



With the diode in place both controller ports started working flawlessly, even allowing me to use a wireless Guitar Hero controller receiver (though not the whammy bar - Guitar Hero controllers lack the "Analog" button to manually enable the analogue mode and instead rely on the PlayStation to enable it via software). Whilst I had the soldering iron out I thought I should add the missing LEDs, once again using the existing markings to establish the correct polarity:



If the markings are unclear, the anode (+) is always to the left when viewing the bottom of the circuit board when the other markings are upright.



As the enclosure is blue and I seem to remember some fuss being made of the PlayStation 2's blue LED when it first came out I opted to use two blue LEDs with 1K5 resistors. I do not have any surface-mount resistors but through-hole ones fit quite easily though they can be a little fiddly to solder down.

When the replacement adaptor arrived in the post I was surprised to see that (once again) the diode D3 was missing and it demonstrated the same problems as the other one I'd fixed. I find it unlikely that the same mistake could be made twice, so this seems to be a genuine cost-cutting measure. Microcontroller I/O pins often have an internal protection diode between them and the positive power supply, which is how I assume the circuit works at all when the controllers are left unpowered - a small amount of current flows from the I/O (data) pins to the positive rail via these protection diodes, which is just enough to let the controller work in digital mode but once they draw more current (e.g. when sampling analogue inputs or driving the vibration motors) the voltage droops far enough for the controller to reset and leave analogue mode.



With these fixes in place I now have two working PlayStation USB adaptors for the price of one (and two 1N4001 diodes). I'm still rather perplexed by why there's such a blatent flaw in the hardware, but it is at least an easy fix which is why I've written it up. In summary: if your cheap PlayStation to USB adaptor ("Twin USB Vibration Gamepad", "Twin USB Joystick") is not working correctly, unscrew it and see if D3 is missing. If it is, solder a 1N4001 or similar diode between the two holes left for that purpose.

benryves

benryves

 

A useful Z80 computer in a project box

Work continues on the Z80 computer. The two final modifications to the box itself are the holes for the status LEDs and the power switch.


The green LED indicates power and the orange one disk activity. Unfortunately, the project box is fairly scratched on the outside (one scratch on the front is my own fault, but the sides and back were fairly scuffed and scratched when I bought it). If anyone has any tips for polishing scratches out of ABS I'd be glad to hear them; the usual household polishing abrasives (such as toothpaste) remove most of the light scuffs and result in a lovely mirror finish, but don't do anything to the deeper scratches. I'll probably invest in the finest grade wet-and-dry sandpaper I can find and have a go with that followed with a Brasso polish, and if that doesn't help (or makes it worse) just sand the whole thing down and paint it.


The circuit board inside the case needs to be attached to the case-mounted components somehow. In simpler projects I've resorted to soldering these connectors directly to the board, but this can make maintenance a problem (to remove the circuit board one would have to cut and resolder the wires). For this project I've left pin header strips on the board. The external connectors have leads soldered to them terminated with pin headers cut to size using some wire cutters and a rotary tool to polish them off; these headers are pictured above.


The main circuit board can then be easily installed or removed from the case as required. The small circuit board for the video display controller is connected to the main circuit board in the same way.


A Z80 computer can't live up to its name without some sort of a Z80 inside it, so I thought that that was the most obvious part to add next. Computers also generally need access to memory so I decided to add the 128KB SRAM chip at the same time. The Z80 communicates with the memory over an eight-bit data bus, a sixteen-bit address bus (to indicate which address in memory it is reading from or writing to) and a number of control lines (to indicate whether the current operation is a memory read or a memory write, for example). This provides a fairly tedious amount of soldering work; each pin on the memory needs to be connected to the corresponding pin on the Z80. To aid in the construction I stuck masking tape to the bottom of the perfboard around the outline of where the two chips would go and wrote the pin numbers onto the tape, shown in the photograph above.


I put the two chips close together so I could put all of the bus wires on the inside of the IC holders rather than going around the outside. This saves a bit of space and avoids having to route the wires around the chip holders which gets a little untidy. The above photograph shows all of the wires in place before the chip holders were soldered in. Adding those in should be a quick and easy job, at least...


Well, you'd have thought so, but somehow I managed to solder in the 32-pin SRAM socket the wrong way around. Each socket has a notch to help you align the chip using its corresponding notch. As you can see in the above photo the notch points right when it should point left like all of the other sockets. It wouldn't affect the operation of the circuit (as long as the SRAM chip was inserted with the notch to the left) but it looks untidy and I may as well do the job properly.


On the positive side I suppose I got to practice my desoldering skills.


The computer design uses an AVR microcontroller to manage the I/O devices (such as the keyboard, video display controller and SD card) and to load the OS into the Z80's memory on reset. To achieve this the Z80 and the AVR need to be connected together. The above photograph shows some new wires between the AVR (bottom left) and Z80 (bottom middle) to connect the Z80's data bus to the AVR's PORTA and a number of other wires to connect the Z80's control lines to several other I/O pins on the AVR. A number of pull-up resistors have been added to control lines on the Z80 so that when nothing is driving the control bus they rise high (the de-asserted state). If left disconnected ("floating") the other components connected to the control bus may think these lines had gone low (asserted) and treat that as a read or write operation, corrupting data.


The AVR also needs to be connected to the Z80's address bus. This would take another sixteen pins if driven directly by the AVR; sixteen pins that aren't available to me! I am therefore using two MCP23S08 eight-bit I/O expanders, pictured above, to drive the address bus from the AVR. These are controlled over the SPI bus, which only takes up three pins on the AVR (these pins are shared with other SPI peripherals, such as the SD card) plus a single chip select pin that is unique to the I/O expanders. Four pins is better than sixteen, at any rate.


I keep mentioning chips even though the sockets are quite clearly empty in the above photographs. As I was approaching a useful computer circuit at this point I plugged all of the chips into their sockets to test the connections. As there was no SD card, real-time clock or keyboard I had to modify the boot loader on the AVR quite considerably; I started with a test program that wrote random data to blocks of memory then read them back to verify that they had written correctly. Once I had verified that the AVR was able to access memory correctly I reprogrammed it to copy a small Z80 program to memory and then let the Z80 take over. This Z80 program repeatedly output the string 'Z80' to the console output port. With everything plugged in I switched on the computer and saw the screen fill with Z80Z80Z80... so I was pretty certain that I'd wired everything up correctly!


At this point I could start reintroducing the various peripherals to the computer. A DS1307 is used as a real-time clock. This clock needs to keep running when the computer is switched off, so I've added a 3V battery connector to the computer to keep it ticking.


As the computer uses a 512MB SD card for storage, I have added a pin socket strip to the board to plug in the SD card slot I scavenged from a card reader. The card is connected to the SPI bus along with the I/O expanders used to drive the Z80 address bus. SD cards run at 3.3V rather than the 5V that nearly everything else on the board uses so I've used a series of voltage dividers to drop the voltage on each input pin from 5V to around 3V (the resistor values I have don't allow me to get to 3.3V; 3V is the closest I can manage without going over 3.3V). The video display controller board also runs on 3.3V so I do at least have a suitable voltage supply for the card!


The final part of the computer that was on the breadboard prototype but not yet in the final build was the keyboard connector. This is simply a four pin header on the board that is connected to the PS/2 port screwed to the case. However, when I tried to use the computer, the keyboard didn't appear to work. Pressing Num Lock, Caps Lock or Scroll Lock would toggle the associated LED and hitting Ctrl+Alt+Del would reboot the computer but no other key worked. This implied that the AVR was handling the keyboard correctly but the Z80 wasn't receiving any notification of key presses. A bit of digging identified the problem; I'd forgotten to connect the Z80's interrupt pin to the AVR! When a key is pressed the AVR triggers an interrupt to let the Z80 know that a key is available. By soldering a wire between the two chips it started working as intended.


The computer is now up to the same standard as it was when assembled on the breadboard, but is much more practical to work on. I hope to add a serial and parallel port to the computer soon, and would like to mount an LCD into the lid of the project box, but for the time being I am happy that I have managed to get this far.


One of the advantages of running CP/M on the computer rather than my own operating system is the availability of existing software. The above photograph shows the computer running VEDIT, which is an excellent visual text editor.




With the hardware in a decent configuration I can start writing my own software. I think the first CP/M program I'll write is a graphical analogue clock, as this is the sort of program that can be left running for long periods as a way to check the stability of the computer.

benryves

benryves

 

Building a VGA line blanker and 3D glasses driver

Assembling a circuit on breadboard is a good way to experiment with electronics, but the result is not something you could really use - it's bulky, fragile and awkward to set up. It's far nicer to solder the components of the circuit together to form a more permanent device and put it in a enclosure to make it robust. This is not something I'm especially good at, but something I thought I'd try with the VGA line blanker and LCD shutter glasses controller I've been experimenting with recently.


In the past I've struggled along with a hand drill and the nail file on a Swiss Army knife, but have more recently acquired a high-speed rotary tool and an assortment of attachments which make things much easier. I took some photos when building this project, which I've documented below; I'm not sure my techniques are very efficient, but I do get there in the end. I'd be very glad to hear any advice anyone has!


I started with a plain project box. Having planned roughly where I was going to put the VGA ports and DC power socket, I covered one side of the box in masking tape and drew on where I was going to put the holes.


To cut straight-edged holes, such as those required for a D-subminiature connector, I drill a hole in each corner and use a small cylindrical burr to cut between the holes. This leaves a very rough edge, but is a good start.


I then widen the hole using a large cylindrical burr and a needle file until the part I'm attempting to mount fits snugly.


When I had both VGA connectors in place, I marked and drilled the holes for the jack posts that the VGA leads will screw into. Neither hole is especially neatly cut, but the D-subminiature connector overlaps the hole sufficiently to hide any shoddy workmanship.


The last part of the back is the DC power socket. As I don't have a drill bit large enough to cut the hole on its own, I drill it as large as I can then widen it using the cylindrical burrs mentioned before. With all of the holes cut, I inserted the components to see how they look and identified one problem - I'd underestimated how fat the connectors on the end of VGA leads are. Fortunately, I have a slim VGA cable that fits, but a regular sized one does not - in future I'll need to remember to put the VGA connectors further apart!


With that mistake fresh in my mind, I thought I'd move onto something a bit more difficult to get wrong - the 3.5mm stereo jack on the front of the box to plug the glasses into. This is just another round hole, cut in the same way as the DC power socket.


The two control switches on the top of the box require much larger holes. These were cut in the same way as before - a small hole is gradually widened by using a cylindrical burr. This is a very tedious job, not helped by having to keep stopping to clean the melted plastic that adheres to the burr.


Finally, the switches were installed. I was originally going to use latching push buttons, but had previously used those nice round rocker switches as the power switch on the AVR TV Game project so opted to use them instead.


The final bit of physical work was to cut some stripboard down to size to fit inside the enclosure. These were cut by first scoring along the tracks where the cut was to be made, then snapping the board over the edge of a table. This results in a clean break, but to ensure a snug fit the boards were tidied up with a sanding drum. The lid (or, in my case, base) of the enclosure has a raised edge that fits inside the box, so the sanding drum was also used to remove two of the corners of the stripboard pieces to allow the base to fit.


The next stage was to move onto the electronics, and I started with the circuit board that was to host the video amplifier IC, voltage regulator and Schmitt trigger on vsync/hsync. The video amplifier is attached to a TSSOP14 adaptor that has a D-shaped pin configuration, with two rows of four pins and two rows of three pins. Having cut through the tracks in the stripboard to mount the amplifier, I needed to find some suitable pin sockets.


As I don't have any pin sockets with just three pins in them (only two, four and eight) I cut two eight-way pin sockets in two with a pair of wire cutters then tidied up the ragged edges with a sanding drum and needle file.


With the pin sockets soldered in place you can see the D shape I mentioned above. I don't generally plan stripboard circuits very thoroughly, preferring to start by placing large components in approximately the right location with respect to where the external connectors are and how they need to relate to other components. Once those are in place I add smaller components (such as discrete resistors or capacitors) before finishing by adding the wire links to connect all of the parts together. This does lead to situations where I wish that I'd placed a component one hole along to give myself more space or to avoid having to insert so many wire links, but it generally works.


With the video amplifier in position, I added the resistors that are required on its inputs and outputs. To keep the circuit reasonably compact I cut through stripboard tracks between the holes using a conical HSS burr with a small tip - this is an especially useful tool when you need to deal with double-row pin sockets


I then added the support circuitry for the voltage regulator (smoothing capacitors and a rectifier diode to protect the circuit if the polarity of the power supply is incorrect) and a socket for the Schmitt trigger IC. I find the easiest way to keep components in place on any sort of through-hole board is to tape them down firmly with masking tape before soldering - bending the legs out makes the parts much harder to remove if you make a mistake. Blu-Tack is easier to use but has a habit of melting when soldering and leaving an unpleasant blue residue on your circuit, so I'd advise against it! To make this part of the circuit slightly more future-proof a pair of jumpers are used to connect the sync lines (vsync and hsync) from the VGA input and VGA output together. These could be removed if I decided to change the logic board to override these signals - for example, as part of a sync-doubler, which injects a vsync pulse half way down the screen.


I finally added the bulkiest components; the 5V regulator and the pin header to connect the upper and lower boards together. Soldering pin headers to the underside of a board is a fiddly job, but is required in this instance to connect the bottom of the upper board to the top of the lower board.


With the upper board completed it was time to put it into the enclosure and solder the VGA connectors and DC power socket to it. This is the part I least enjoy.


I started by soldering some stranded wire to the VGA connectors. Most of the wires are the same length, as they are required to carry signals to and from the circuit, but some wires are shorter and only connected to one of the VGA connectors. These are the white, yellow, orange and brown wires in the above photo, and these are attached to pins used to exchange information between the PC and the monitor (e.g. supported resolutions and refresh rates). As we're not interested in these, they're connected straight through from one connector to the other.


I inserted the VGA connector with these identification pins into the top hole, passed the shorter identification wires through the other and soldered them to the second VGA connector. This leaves the red, green, blue, vsync, hsync and ground pins loose inside, ready to be connected to the upper circuit board.


The DC power socket also needs to be connected to the circuit board, but at only two wires that's a much simpler job.


All of the loose leads are soldered onto the circuit board and stripboard is slotted into place inside the enclosure. The wires could be shorter, but that would have made soldering them a bit harder.


The lower circuit board will host the main logic for the project - it receives the vsync and hsync signals, and uses these to control whether the video signal should be blanked or not, and which shutter on the glasses should be closed and which should be open. It also contains the oscillator that generates the AC voltage that drives the glasses. I arranged the three logic ICs roughly next to eachother according to their layout on the breadboard version of the circuit and cut the stripboard tracks as appropriate.


I started by adding the sockets for the ICs and pin header to connect this circuit board to the video amplifier one, then added the discrete components. As before, I taped the components down before soldering them in place to make the task easier. Being able to copy the circuit directly from the breadboard version also made the task much easier.


The last step for this part of the project was, as before, adding the wire links. Rather than run long wires around ICs I found it more practical to solder a few wires onto the underside of the stripboard.


The two circuit boards needed to be connected together somehow. Without the facilities to make a proper ribbon cable, I just soldered some lengths of stranded wire (rather messily) between two pin sockets. As I'm not outputting anything to vsync or hsync (I'm feeding the input sync signals straight back to the output via the jumpers previously discussed), I didn't need to connect anything to these pins - hence the apparently missing wires in the photos.


The cable to connect the two boards together needed to be bent to fit - it's getting snug, but everything's in there without having to be forced, which is a good sign.


The next job was to attach the 3.5mm stereo jack that the LCD shutter glasses are plugged into. This is pushed through the hole in the enclosure from the inside and screwed on from the outside, so it can be soldered directly to the circuit board without having to thread it through the hole first. The small red "washer" is a length of enamelled wire that has been bent around the thread of the jack socket and is used as a spacer - without it, quite a lot of the thread protrudes from the front of the box, looking rather untidy.


Last of all are the two control switches. These are soldered to the track side of the stripboard like the stereo jack, but must be snapped through their holes in the enclosure first, which is why they were left until last. Everything is slotted into place, the base of the enclosure is screwed on, and the project is pretty much complete.


The VGA cables don't fit especially well - the D-subminiature sockets are a bit too close to eachother. If I use a thin VGA extension cable and wiggle the leads I can just about get both to screw in.


The demonstration pattern from some previous ramblings of mine is quite useful for testing 3D glasses, and by holding the left eye of the shutter glasses to the screen you can see that only the "L" part of the image is let through.

benryves

benryves

 

Booting CP/M 3 from an SD card

Up to this point I have been running CP/M 2.2 on the Z80 computer. CP/M 3 adds a number of useful features, including the following: Support for more than 64KB RAM via banked memory.
Standardised access to real-time clock for file date and time stamping. Improved text entry on the command-line when using the memory-banked version, such as the ability to move the cursor when editing and recall the previously entered line. Support for disks with physical sectors larger than the default record size of 128 bytes. Switching to a banked memory system would require some new hardware in the form of a memory management unit so I have stuck with the simpler non-banked system for the time being. Support for physical disk sectors larger than 128 bytes is more interesting (SD cards use 512 byte "blocks") and real-time clocks are always useful so I have started working on updating to CP/M 3.


Z80 computer with new SD card slot (bottom left) and real-time clock (top right)
CP/M consists of three main pieces of software: A BIOS which exposes a small number of routines to perform primitive, hardware-specific operations (e.g. output a character to the console, read a raw sector from a disk, check if a key has been pressed). The BDOS which provides the main API for transient programs (e.g. read a complete line of input from the console, create a file, read a record from a file). The CCP, or console command processor, which provides the main user interface for loading and running other programs or performing some basic tasks via its built-in commands. This would be analogous to COMMAND.COM on DOS. When working with CP/M 2.2 I had source files for these three pieces of software, so I just needed to implement the 17 BIOS functions, reassemble the three files to fixed addresses in memory and load them to these fixed addresses using the AVR when booting the computer. These three files were stored in the lower 8KB of the flash memory chip and were not accessible from within CP/M itself.

CP/M 3 proved to be a bit more of a challenge, as it is loaded slightly differently. The CCP is stored as a regular file named CCP.COM on the floppy disk you're booting from, so only the BIOS and BDOS need to be loaded from their hiding place at the start of the boot disk. These two pieces of software are merged into a single file named CPM3.SYS by a CP/M utility named GENCPM. To get this utility to work I needed to provide GENCPM with a hardware-specific BIOS3.SPR file that implemented the 31 BIOS routines. Fortunately, a file named BIOSKRNL.ASM is provided that implements most of the boilerplate code involved with writing a BIOS (you still have to provide the hardware-specific routines yourself, but your task is made much easier by following the template) so I just needed to recompile that for a non-banked system and link it with my handful of hardware-specific routines.


Ideally, CPM3.SYS would be stored on the regular file system with CCP.COM and the hidden boot loader would load CPM3.SYS for you. CP/M 3 does provide a small boot loader for this purpose (aptly named CPMLDR) which employs a cut-down BDOS and BIOS to load CPM3.SYS from the file system into memory for you. I haven't been able to get it to work, though, so I currently parse and load CPM3.SYS using some C code on the AVR. This works well enough for the time being, as can be seen in the above output generated by the computer when testing the real-time clock.


The time and date is maintained by a DS1307, an inexpensive eight-pin real-time clock and calendar chip that is shown in the middle of the above photograph. It is accessed over the I2C bus using a protocol that is natively supported by the AVR hardware. It uses binary-coded decimal to represent dates and times, which corresponds nicely to the time format used by CP/M; however, CP/M represents dates as a 16-bit integer counting the number of days since the 31st December 1977. I have used the algorithms on this website to convert dates to and from this format and the individual components.

The only downside of the DS1307 is that it only stores a two-digit year number, not the four digits one would hope for. This means that the century is discarded when setting the real-time clock, allowing for you to set a date that is then retrieved differently (truncated to the range 1930..2029). I haven't thought of a suitable solution to this problem just yet. I could use the AVR to act as the real-time clock, but I would then lose the advantage of the DS1307's battery backup that kicks in when the main power supply is removed.

The state of the DS1307 is effectively random at power-up. One of the first things the computer does when booting is to read the current date and time and check that all fields are within range. If not it resets them to midnight on the 1st January 1978 and displays a message to indicate that it has done so.


The SD card has been a bit of a headache to get working and though it currently only supports reading, not writing, it should hopefully be a useful addition to the computer. Rather than the previous arrangement of series rectifier diodes to drop the supply voltage and zener diodes to protect the inputs I'm using a dedicated 3.3V regulator to power the card and resistor voltage dividers to drop the 5V logic signals to around 3V (the closest I could get to 3.3V with the resistors I had to hand). I'm using the disk image from the old 512KB flash chip and treating the card as having 128 byte sectors so the arrangement is no more capable than before and in some cases quite a lot slower (reading a 128 byte record now entails reading a whole 512 byte block from the card then returning the desired 128 byte range within that block) but it seems to be as reliable as it used to be at least. SD cards append a CRC16 checksum when transferring data blocks so I can hopefully detect errors more easily and their on-board flash memory controller should perform wear-levelling, prolonging the life of the card.

To write the disk image to the card I used HxD which makes the job as easy as copy and paste. One problem I did have is that it displayed an "Access denied" error when attempting to write data, which I assume to be because something in Windows was using the card at the same time as HxD. I knocked together a short program for the AVR that wrote junk to the first block of the card, the result being that Windows no longer recognised the card's file system and HxD managed to write the data to the disk with no further problems.


Sockets for regular SD cards seem to be relatively expensive for what they are, but the above SD card reader cost a pound (what else?) from Poundland. A bit of work with a soldering iron and some desoldering tools yielded some useful components:


The crystal is unmarked and I'm hardly short of LEDs but the USB A connector could be a good way to reduce the size of a project that plugs into a USB port (USB B connectors are rather bulky) and the SD card slot works brilliantly for my needs here. There are cheaper and nastier ways to add an SD card slot to your project, but something like this feels more robust and has the advantage of reporting the state of the card's write protection switch.

benryves

benryves

 

Laserstrike for Zen X-Fi 2

It's been a long time since I posted about any of my projects for the simple reason that I haven't had any real time to work on them this year. Work commitments have not been particularly kind to my free time and there has been no progress on my 3D engine for TI calculators or any new electronics projects.

I did, however, replace my ailing Zen Xtra digital audio player with a Zen X-Fi 2 earlier in the year. The X-Fi 2 supports simple application development in Lua, a language I had no experience with, so I spent a few days in April knocking together a game as a learning project. I've always been fond of Kevin Ng's Laserstrike and it seemed a good fit for a device with a touch screen.

[media] [/media]

I used the smaller levels from Badga's Laser Mayhem as it let me use larger tiles, otherwise it would be tricky to tap the correct block on the X-Fi 2's 3" screen.

Having not used Lua before the code is far from brilliant (for some reason I chose to represent the level as a string rather than an array, by way of example) but it works well enough and has occasionally kept me occupied on bus and train journeys. Rather than let the game stagnate on my hard disk drive I added a final bit of polish and have released it on my website. If you'd like to try the game but do not own an X-Fi 2 (which would be almost everyone reading this) you can play it in the Zen X-Fi 2 Application Development Kit (extract the game to C:\Creative\ZEN X-Fi2\Applications) but be warned that the simulator is a little buggy (it doesn't detect touch input in the 16 rows and columns at the top and left edges of the screen for starters).

Fingers crossed I can get more time for what I enjoy doing in 2012. I have plenty of fun ideas, but little time to put them into practice!

benryves

benryves

 

Controlling a PG320240H-P9 with a dsPIC33FJ128GP802

In a previous entry I mentioned that I had purchased a PG320240H-P9 graphical LCD. This is a 320x240 white-on-blue pixel display, and it does not have an on-board controller or RAM. To display something on it you need to constantly refresh it with picture data; in this instance, sending four pixels at a time, starting from the top left and working from left to right, top to bottom -- a bit like the scanning pattern of a CRT monitor.


Connecting a circuit to the LCD is made slightly more tricky by its use of a 16-pin 1mm flexible flat cable. To get around this I soldered together an adaptor using a suitable FCC connector, pin strip, piece of stripboard and a fairly excessive quantity of hot melt adhesive. Even more tricky was the lack of a suitable datasheet for the LCD. After some digging I located this one for the PG320240WRM-HNNIS1 -- it's slightly different, but contains timing diagrams and specifications that seem to work with the LCD I bought. One thing I still haven't worked out is the contrast adjustment; a 5K variable resistor between 0V and the relevant pin seems to have had the best results thus far. A helpful webpage, Graphical LCD controller for ST8024+ST8016 based displays, has a plain English description of how to drive the LCD, though as far as I'm aware the M pin should have its logic level toggled every frame, giving you a "glass" frequency of half of the refresh rate, not 200Hz-400Hz. The lack of a proper datasheet makes these things a little complicated!


My first attempt to drive the LCD involved an ATmega644P, a microcontroller with 64KB of flash ROM and 4KB of RAM. The above photo shows it displaying a picture of a cat, which was stored in ROM and output using the following code:

#include
#include
#include

#define LCD_FLM (6)
#define LCD_M (5)
#define LCD_C1 (4)
#define LCD_C2 (3)
#define LCD_D_OFF (2)

#define LCD_CONTROL_PORT (PORTC)
#define LCD_CONTROL_PIN (PINC)
#define LCD_CONTROL_DDR (DDRC)

#define LCD_DATA_PORT (PORTA)
#define LCD_DATA_PIN (PINA)
#define LCD_DATA_DDR (DDRA)

#include "cat.h"

int main(void) {

// Make control pins outputs.
LCD_CONTROL_DDR |= _BV(LCD_FLM) | _BV(LCD_M) | _BV(LCD_C1) | _BV(LCD_C2) | _BV(LCD_D_OFF);

// Make data pins outputs.
LCD_DATA_DDR |= 0b1111;

// Enable the LCD.
LCD_CONTROL_PORT |= _BV(LCD_D_OFF);

for(;;) {

// Toggle the M pin to provide the LCD AC voltage.
LCD_CONTROL_PIN |= _BV(LCD_M);

const uint8_t* picture_ptr = cat_picture;

// Scan 240 rows in the image.
for (uint8_t row = 0; row 240; ++row) {

// Begin the line.
LCD_CONTROL_PIN |= _BV(LCD_C1);
LCD_CONTROL_PIN |= _BV(LCD_C1);

if (row 2) LCD_CONTROL_PIN |= _BV(LCD_FLM);

// Send 40 eight-bit words.
for (uint8_t column = 0; column 40; ++column) {
LCD_DATA_PORT = pgm_read_byte(picture_ptr) >> 4;
LCD_CONTROL_PIN |= _BV(LCD_C2);
LCD_CONTROL_PIN |= _BV(LCD_C2);
LCD_DATA_PORT = pgm_read_byte(picture_ptr);
LCD_CONTROL_PIN |= _BV(LCD_C2);
LCD_CONTROL_PIN |= _BV(LCD_C2);
++picture_ptr;
}
}
}

}

A 320x240 display has 76,800 pixels, and if you store each pixel as a single bit (so eight pixels per byte) you need 9600 bytes to store a complete frame, which clearly won't fit in the 4KB offered by the ATmega644P. Rather than upgrade to an AVR with more memory, I jumped to the dsPIC33FJ128GP802, a 16-bit microcontroller with 16KB of RAM. As well as quadrupling the RAM from the ATmega644P it also doubles the program memory (128KB from 64KB) and speed (40 MIPS from 20 MIPS). When working with AVRs I'd been using a slow home-made serial programmer, and rather than continue with this sorry state of affairs (lack of debugging capabilities is never fun, especially when it takes over a minute to program the microcontroller) I treated myself to a PICkit 3 Debug Express.


The above photo shows the LCD connected to the microcontroller as well as the PICkit 3. The dsPIC33FJ128GP802 requires a voltage supply from 3.0V to 3.6V, not the 5V I am used to, so to power it I have put two IN4001 rectifier diodes in series with the 5V regulator output. Each diode incurs a voltage drop of 0.7V, producing 3.6V for the rest of the circuit. The LCD is powered from the main 5V supply, but it seems happy with the 3.6V logic "high" from the dsPIC.

The LCD is connected to the dsPIC as follows:
FLM to RB15 M to RB14 C1 to RB13 C2 to RB12 /D_OFF to RB11 D0~D3 to RA0~RA3 A 10K resistor is included between /D_OFF and ground. This is very important, as it holds the /D_OFF line low when RB11 is floating (e.g. during reset), forcing the display off -- if the display is powered, but is not being actively refreshed, the LCD panel can become overloaded and damaged.

I have knocked together a simple demo that shows a few different graphics on the LCD. The LCD is constantly refreshed by an interrupt service routine that runs in the background, leaving some CPU time to the user program. As there is only enough RAM for a single frame buffer, animation has to be quite simple to avoid flickering, but I've still managed to include my favourite spinning cube.

">
Click to view the demo video on YouTube
The project can be downloaded here. I'm still getting to grips with the dsPIC series; the code is likely to be pretty awful, and I still have a problem where the dsPIC resets itself every couple of minutes (I'm not really sure if this is a software or hardware issue). Still, it's a start, and I hope that I can use this LCD as the display for my Z80 computer project.

Update: Having seen this post, the chap who originally suggested that I investigate the dsPIC33FJ128GP802 sent me an email with some advice, chiefly about my poor power supply, missing decoupling capacitors and use of an electrolytic capacitor on the VCAP pin. I have since replaced the two rectifier diode affair with a proper 3.3V regulator for the power supply, added a decoupling capacitor across AVDD/AVSS and moved the decoupling capacitor between VDD/VSS closer to the microcontroller. I have also ordered some tantalum capacitors to replace the electrolytic one. A bit of debugging found that the watchdog timer is responsible for the spurious resets; I have disabled it in the code for the time being, which has stopped the resets.

benryves

benryves

 

Fun with IThumbnailProvider

Note: I have been informed that the code below no longer works in Windows 7 due to changes in the way IThumbnailProvider operates. It is recommended that you use unmanaged code instead of the managed solution presented below.


I have started releasing Cogwheel binaries on its project page, so if you'd like a look at the project but can't be bothered to check out and build the source yourself you can now give it a whirl.

One of the newer additions is a savestate mechanism; this is a very lazy bit of code on my behalf as all it does currently is serialise the entire emulator to a file using the BinaryFormatter. This resulted in savestates weighing in at about 6MB; by marking certain private fields (such as look-up tables in the Z80 emulator) as [NonSerialized] it was down to 2MB. To squash it down to the current ~250KB size the savestate is compressed using the zip file classes I've written to handle loading ROMs from zips.

Whilst this is going to change soon (I'm currently working this on an simple INI file serialiser, so the savestate files will be compatible with later releases of the software) I decided to experiment with the idea of dumping extra data into the savestate - namely, a screenshot.


The screenshot is simply saved as Screenshot.png in the root of the savestate's zip archive. Creating a thumbnailer is extremely easy under Vista, and as the thumbnailer runs out-of-process you can use .NET code! Here's a quick and dirty run-down of how to make them if you decide to write one yourself.


Setting up the project

Create a new class library project in Visual Studio, then go switch to its project properties editor. On the Application tab, set Target Framework to something sensible (I currently try and keep everything at .NET 2.0 level), then click on the Assembly Information button and tick the Make assembly COM-Visible box.

Finally, move to the Signing tab, and tick the box marked Sign the assembly. From the drop-down box, pick New, which will create a new key file and add it to the project (this is required later for COM registration).

Add the COM interface wrappers

This is a simple copy and paste job! Just bung this in a source file somewhere:

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Thumbnailer {

///
/// Defines the format of a bitmap returned by an .
///
public enum WTS_ALPHATYPE {
///
/// The bitmap is an unknown format. The Shell tries nonetheless to detect whether the image has an alpha channel.
///
WTSAT_UNKNOWN = 0,
///
/// The bitmap is an RGB image without alpha. The alpha channel is invalid and the Shell ignores it.
///
WTSAT_RGB = 1,
///
/// The bitmap is an ARGB image with a valid alpha channel.
///
WTSAT_ARGB = 2,
}

///
/// Exposes a method for getting a thumbnail image.
///
[ComVisible(true), Guid("e357fccd-a995-4576-b01f-234630154e96"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IThumbnailProvider {
///
/// Retrieves a thumbnail image and alpha type.
///
/// The maximum thumbnail size, in pixels. The Shell draws the returned bitmap at this size or smaller. The returned bitmap should fit into a square of width and height , though it does not need to be a square image. The Shell scales the bitmap to render at lower sizes. For example, if the image has a 6:4 aspect ratio, then the returned bitmap should also have a 6:4 aspect ratio.
/// When this method returns, contains a pointer to the thumbnail image handle. The image must be a device-independent bitmap (DIB) section and 32 bits per pixel. The Shell scales down the bitmap if its width or height is larger than the size specified by cx. The Shell always respects the aspect ratio and never scales a bitmap larger than its original size.
/// Specifies the format of the output bitmap.
void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType);
}

///
/// Provides a method used to initialize a handler, such as a property handler, thumbnail provider, or preview handler, with a file stream.
///
[ComVisible(true), Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IInitializeWithStream {
///
/// Initializes a handler with a file stream.
///
/// Pointer to an interface that represents the file stream source.
/// Indicates the access mode for .
void Initialize(IStream stream, int grfMode);
}

}

(You may wish to set the namespace to something more appropriate). As you can see, most of that source file is documentation.

Create your thumbnailer class

First thing you'll need to do here is to generate a GUID for your thumbnailer; this is so that when you register your thumbnailer Windows will know which COM object to create an instance of which it can then call to generate a thumbnail (the GUID of your thumbnailer is attached to the extension of the file via standard file associations - more on that later).

Your thumbnailer class should implement two interfaces; IThumbnailProvider (obviously!) and IInitializeWithStream. Here's a skeleton class for the thumbnailer:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Thumbnailer {

[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
[ProgId("YourApp.ThumbnailProvider"), Guid("YOUR-GUID-IN-HERE")]
public class ThumbnailProvider : IThumbnailProvider, IInitializeWithStream {

#region IInitializeWithStream

private IStream BaseStream { get; set; }

public void Initialize(IStream stream, int grfMode) {
this.BaseStream = stream;
}

#endregion

#region IThumbnailProvider

public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType) {

hBitmap = IntPtr.Zero;
bitmapType = WTS_ALPHATYPE.WTSAT_UNKNOWN;

try {

// Thumbnailer code in here...

} catch { } // A dirty cop-out.

}

#endregion
}
}

You will probably want to set the ProgId to something meaningful, and make sure you set the GUID to the one you just generated.

What will happen is that Windows will first initialise your object by calling IInitializeWithStream.Initialize(), passing in an IStream. The above implementation stores the IStream in a member property for future reference.

Windows will then call IThumbnailProvider.GetThumbnail(). cx is the maximum size of the thumbnail (width and height) you should return; Windows will scale your thumbnail down if you return one that is too large. Do not scale your thumbnail up to match this value; it is perfectly valid to return one that is smaller than the requested value. Also; do not scale your thumbnail up to a square: you should return it at the same aspect ratio of the source image.

For the moment, and for the sake of testing, here's a snippet that will create a bright red thumbnail using GDI+:

using (var Thumbnail = new Bitmap(cx, cx)) {
using (var G = Graphics.FromImage(Thumbnail)) {
G.Clear(Color.Red);
}
hBitmap = Thumbnail.GetHbitmap();
}


Registration

If you compile your class library at this point you should end up with a single DLL. You need to register this DLL using the command-line tool RegAsm.exe that comes with the .NET framework.

Open an elevated command prompt (you need admin rights for this bit) and set the working directory to the output directory of your DLL. Now, invoke the following command:
%windir%\Microsoft.NET\Framework\v2.0.50727\RegAsm /codebase YourThumbnailer.dllThat's half of the battle; the last bit boils down to conventional file associations.

Run the registry editor, and open the HKEY_CLASSES_ROOT key. You will see a list of keys representing file extensions; find one (or create a new one) to match the extension that you wish to attach your thumbnailer to. Under that create a new key named shellex, and under that create another key named {e357fccd-a995-4576-b01f-234630154e96}. Set its (Default) value to {YOUR-GUID-IN-HERE} - yes, the GUID you created earlier. That should look something like this:
HKEY_CLASSES_ROOT.yourextensionshellex{e357fccd-a995-4576-b01f-234630154e96} = {YOUR-GUID-IN-HERE}
That's it! [smile] You may need to log out then in again (and/or reboot and/or just kill all Explorer instances and restart them) for Explorer to catch on if nothing seems to be working.

A final note: IStream to Stream

The only final hitch is that IStream is not the same as our beloved .NET Stream. I use the following snippet to dump all of the contents of an IStream into an array of bytes (which can then be converted to a stream using new MemoryStream(byte[]) if need be).

private byte[] GetStreamContents() {

if (this.BaseStream == null) return null;

System.Runtime.InteropServices.ComTypes.STATSTG statData;
this.BaseStream.Stat(out statData, 1);

byte[] Result = new byte[statData.cbSize];

IntPtr P = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(UInt64)));
try {
this.BaseStream.Read(Result, Result.Length, P);
} finally {
Marshal.FreeCoTaskMem(P);
}
return Result;
}

This, naturally, is not a good idea if you're thumbnailing very large files, as it dumps the entire thing into memory!

For more information, take a look at MSDN: Thumbnail Providers, which includes useful information (including how to change the overlay icon in the bottom-right of your thumbnails or the adornments).

The COM wrappers and GetStreamContents() snippet are based on this uberdemo article.




Finally, another screenshot; you can now load IPS patch files directly into Cogwheel using the advanced ROM load dialog - which can be useful for translations.

benryves

benryves

 

Combining a Z80 and an ATmega644P to boot CP/M

I've been working on a new Z80 computer over the last few days. I would say that I had been working on the existing Z80 computer were it not for the fact that this a completely new design.

The previous computer had two 32KB RAM chips to provide a total of 64KB RAM. To run a user program you need to get it into RAM somehow, so I also included a 128KB ROM chip which occupied the lower 16KB of the Z80's address space to provide the fixed operating system that could be used to load programs. By adding memory banking hardware I could select one of eight 16KB pages of ROM. The next 16KB was one of two banks of RAM from one RAM chip, and the final 32KB was mapped directly to the other RAM chip.


This is all fairly complicated, and not very flexible. Programs written for CP/M tend to be loaded into memory starting at address $0100, which is impossible with my old design as that section of memory is taken up by ROM.

Giving another device access to the buses

The Z80 accesses memory and other hardware devices using three buses; an eight-bit data bus which shuttles bytes of data between the various chips, a sixteen-bit address bus which addresses a location in memory or a particular I/O device, and a control bus which contains numerous lines that specify the type of operation (for example, if /MREQ and /WR go low together it indicates that a byte is being written to memory, or if /IORQ and /RD go low together it indicates that a byte is being read from an I/O device).

There is also a pin named /BUSREQ that can be used to request access to these buses. The Z80 will periodically check this pin and if it is held low it will put the data, address and control buses into a high-impedance state and drive /BUSACK low to acknowledge this. This effectively removes the Z80 from the circuit, and another device can now drive the buses.


Click to toggle labels
This is the feature which I have based the new design around -- the current prototype is pictured above. It features a Z80 and 128KB of SRAM (only 64KB is currently addressable) on the upper board. On the lower board is an ATmega644P microcontroller, which is used to start the computer.

When the circuit is reset, the ATmega644P requests access to the buses from the Z80. When access has been granted, it proceeds to copy the CP/M BIOS from the 512KB flash memory IC to a specific location in RAM (currently $F200). It then writes the Z80 jump instruction jp $F200 to the start of memory, returns control of the buses to the Z80 and pulses its /RESET pin. The CP/M BIOS then runs directly on the Z80.

As the ATmega644P doesn't have enough pins to drive all of the buses directly, I've added sixteen GPIO pins by using two MCP23S08 8-bit I/O expander chips. These are used to drive or sample the Z80 address bus; the data and control buses are driven or sampled directly by the GPIO ports on the ATmega644P.

Using a slow to respond microcontroller for I/O

The Z80 is most useful if it can talk to the outside world somehow, which is usually achieved by reading from or writing to I/O devices. In my previous design I built these out of latches and lots of glue logic. As I've added a powerful microcontroller to the computer which features a number of useful on-board peripherals, it would seem sensible to use that instead.

One problem with this idea is that the Z80 expects to read or write to an I/O device in a mere four clock cycles. The AVR has a delay between an interrupt occurring (such as a pin state changing) and executing interrupt service routine of at least five clock cycles. Even though the AVR is running at twice the clock speed of the Z80 this still doesn't provide much time to sample the address bus and perform some useful action before returning a value to the Z80. Fortunately, the Z80 has another useful pin, /WAIT, specifically to address this concern. By pulling this pin low the Z80 can be stalled, allowing the I/O device plenty of time to respond. I have included a 7474 D-type flip-flop as an SR latch to control the /WAIT pin. When the Z80's /IORQ pin goes low the flip-flop is reset, which pulls the /WAIT pin low. When the AVR notices that the /IORQ line has gone low it samples the address bus, performs the requisite task then sets the flip-flop, which drives the /WAIT pin high again and the Z80 continues executing the program.

The 7474 is a dual D-type flip-flop, so I have used the second flip-flop to halve the AVR's 20MHz clock signal to provide the 10MHz clock for the Z80.

CP/M interacts with the host computer by calling numbered BIOS functions. I have implemented a number of these BIOS functions by outputting a value to a port number that matches the BIOS function number. For example, CONOUT is function number four and is used to send the character in register C to the console.

CONOUT:
ld a,c
out (4),a
ret

The AVR detects a write to port 4 and sends the incoming byte to one of its UARTs. I have connected this UART to a simple transistor inverter (pictured in the top right of the above photograph) and plugged the output from that into one of my PC's serial ports, so by running a terminal emulator I can see the output of CP/M on the screen. I have implemented only a handful of other functions (WBOOT outputs a value to port 1 to indicate that I should load the BDOS and CCP into RAM from the flash memory and READ can be used to copy 128 byte floppy disk sectors from flash memory to Z80 RAM) so the results are not exactly impressive:

As I haven't implemented console input yet there's no way to type at the prompt, but that it gets that far is encouraging.

I haven't implemented writing to the flash memory due to a mistake I made when reading its datasheet. When writing to flash memory the value you write is ANDed with the data that's already there (you can only set a 1 bit to a 0 bit, but not vice-versa) - this is referred to as programming. If you want to write a 1 bit you have to erase the memory before writing to it (this is unsurprisingly referred to as erasing). Flash memory can be split into pages (small regions, in this case 256 bytes) and sectors (large regions, in this case 64KB). You can often program any number of bytes (up to a page at a time, aligned to page boundaries) but can only erase in larger blocks -- pages, sectors, or the entire memory (bulk erase). I thought that the flash memory ICs I bought supported page erasing, but they only support sector erasing. CP/M transfers data between floppy disks and RAM in 128 byte floppy disk sectors, so to write an updated sector I would need to read 64KB from the flash memory, update a 128 byte region within it, erase an entire flash sector, then program the 64KB back to it. This would be very slow and quickly wear out the flash memory, so I am looking for some replacement flash memory ICs which do support page erase.


To copy the system files and a sample disk image to the flash memory I cobbled together the above parallel port programmer which is driven by an application cobbled together in C#. It's rather slow but gets the job done -- unlike my AVR programmer. After finally managing to get CP/M to boot in a satisfactory manner I made a few tweaks to the AVR program and hit the "Build and Program" button in the editor. The code built, but rather than program the AVR my computer switched off. No error message, not even a blue screen, just a sudden and surprising power down. Since then I've only managed to talk to the AVR once; every other time has resulted in either a power down or blue screen. I had hoped to add some keyboard handling routines to the project to at least be able to interact with CP/M, but after fiddling around for an hour and a half without managing to get anything working again I gave up. I wish I knew why it suddenly stopped working, after hours of reliable service -- maybe it's a hint that it's time to buy a proper USB debugger rather than the cheap and cheerful home-made serial port programmer I've been using!


One equally cheap but useful addition to my tools is the above 5V power supply (yes, it's just a 7805 regulator in a box). Every project I have built needs a 5V supply from somewhere, which usually comes from a 7.5V wall wart power supply unit regulated to 5V with a 7805. This takes up valuable breadboard space and the weight of the cable from the power supply tends to drag the breadboard around the smooth surface of my desk, so having a dedicated box with an on-off switch, indicator LED, reverse voltage protection and an easy way to connect to the circuit via 2mm sockets is very handy indeed.

I now need to find a way to program AVRs without my PC switching itself off before I can make any more progress on the project...

benryves

benryves

 

Mounting circuit boards and rear panel connectors

One of the fun things about working with electronics is that you can end up with a physical product at the end of your hard work. To this end I have started moving my Z80 computer from its current breadboard to a more permanent enclosure.


Large project boxes can be quite expensive (around GBP40, it seems), but the one I picked out was a slightly more reasonable GBP7. It's not the prettiest enclosure I've seen but it should be large enough to house the computer and provide space on the lid for the LCD and on the rear surface for a collection of connectors (as you'd expect to find on the rear of any computer).



The first challenge was how I intended to mount the circuit board within the box. The perfboard I will use for the main computer circuit doesn't fit the marked mounting posts on the bottom of the project box; it's too narrow and too deep. What the photo doesn't show very well is that the perfboard is not able to lie flat in the box due to the curve at the rear of the box. To raise the board above the bottom of the box I decided to use four PCB spacers, which required two new holes to be drilled into the perfboard away from its corners.


I decided that the video display controller, which resides on its own board, should be mounted on the main circuit board using PCB spacers too.


This required four more holes to be drilled into the main circuit board. I tried to align the small video display board so that its 16-way pin socket for connection to the LCD was as close to the horizontal centre as possible.


The base of the project box needed to have four holes drilled into it to support the main circuit board. Once the two nearest the front edge had been drilled, I screwed the circuit board to the back of the project box to mark the position for the other two holes to ensure that they lined up exactly with the holes drilled in the circuit board.


Screws come through the bottom of the project box to hold the main circuit board in place. Some sticky foam feet are provided with the project box which will raise it off the surface it is resting on to prevent these four screws from leaving scratches! Due to the curve at the back of the box the circuit board is only a few millimetres above its surface, which is why I reversed the screws holding the video display board to leave the long threaded ends pointing upwards.


As working on the enclosure is a fairly noisy activity I switched my attention to the electronics for a brief spell. The first part of the circuit I assembled was the power supply; this just uses a pair of voltage regulators to provide 5V and 3.3V from an external power supply (I use a cheap wall wart affair rated at 7.5V DC).


I decided that the next part to tackle would be the oscillator. This uses a 20MHz crystal and a 74LS04 according to the design on z80.info to generate a 20MHz clock signal which will be further divided by two to produce a 10MHz clock signal for the Z80. I had some real problems with this design; it would run at 20MHz until I attached a load to it, at which point it would generate a fairly random-looking signal or stop oscillating entirely. I experimented with a few different capacitors and found that if I remove the 120pF capacitor and replace it with a 33pF capacitor on the other end of the crystal it works reliably. I'm not entirely sure why this is, but it's the design I've been using for a while with the computer on a breadboard so I'm happy to keep it this way for the time being.


I added a D flip-flop to divide the 20MHz clock to 10MHz and then added the ATmega644P microcontroller to the board. This has a jumper next to its clock input allowing for the selection of either 20MHz or 10MHz operation; a pin header to the left of this jumper allows for it to be programmed in-circuit.


With those new parts in place I reinstated the video display board to check that everything still fit. My main concern now was how far the connectors screwed into the rear of the case would intrude and whether there'd be any problems with them getting in the way of the circuit boards.


I sketched a design of how I saw the connectors would fit on the back of the case and then copied the layout to some masking tape stuck to the case. The computer naturally needs a power supply and keyboard input, and the video display board accounts for the VGA connector and an RCA connector for composite video (which I neglected to mark). I also hope to include a serial port and a parallel port in the final design (though neither are currently supported by the software) so left space for those two connectors.


The 6-way mini-DIN connector for the keyboard is the deepest one to contend with so I decided to start with it. I cut the hole in the case by drilling a small hole in the plastic which I then enlarged with a burr tool to the correct shape and size.


Fortunately it looks like there's plenty of room in the case for connectors!


The next few connectors confirm this. I really do not enjoy cutting the holes for D-sub connectors (such as the one for the serial port); they don't have much of a metal lip to hide a botched hole, so I have to cut very slowly and very carefully, taking a very long time to slowly enlarge each hole until the connector fits. I'm therefore not really sure why I decided to have three D-sub connectors in this computer design; maybe I'm just a glutton for punishment.


Finally, the rear of the case is completed. I will leave the masking tape on there as scratch protection until I have finished the front of the case (this will be significantly simpler -- just a power switch, power LED and disk activity LED). Once that is done I can resume working on the electronics!

benryves

benryves

 

VGA output for the dsPIC33 VDC

I have spent quite a while working on different projects that generate PAL video signals in software. This may seem a bit odd if you consider the fact that I don't own a TV, so tend to rely on a video capture card or VGA box to see the output of these projects on a computer monitor -- something I do have a fair number of.

This reliance on another piece of technology between my project and the display device is not something I'm too keen on, so have spent some time adding native 640x480 60Hz VGA output to my dsPIC33 video display controller.


Another advantage of using a VGA monitor directly is that individual pixels are shown very crisply, unlike my video capture card or VGA box which tend to blur the image horizontally. This is shown in the zoomed in part of the above photo.

Generating a video signal for a VGA monitor is easier than generating a composite video signal for a PAL TV, as there are distinct pins for the image data, horizontal sync and vertical sync. One problem I did have, however, is with the length of the vertical sync pulse. I started with a very brief pulse (the same duration as the horizontal sync pulse) which worked fine with my old analogue CRT monitors but didn't work at all with my modern LCD monitor. The documentation I was using for timing information indicated that there were "two scanlines" for vertical sync so I extended the pulse to last for those two frames, which worked on the LCD but didn't on the CRTs. My final compromise has been to assert the vertical sync pin for the duration of a single scanline, which seems to work on all of my monitors.


When connected to a TV two microcontroller pins are used to drive a single load (composite input). When connected to a VGA monitor, however, a single microcontroller pin is used to drive three loads (red, green and blue inputs). I thought it prudent to check the datasheet for the dsPIC before connecting this increased load to the output pin where I was surprised to discover that the maximum source or sink current for each output pin is a measly 4mA -- not even enough to drive an LED! I have added a buffer to each video output pin to protect the dsPIC -- any buffer capable of sourcing up to 30mA or so should be sufficient (I'm using a 74F125, which can be seen in the bottom right of the above photo). I had previously been occasionally using the video output pins as inputs to check if there is a load on the output or not (such a load would indicate whether a TV or VGA monitor is plugged in or not) but I can no longer do this with the external buffer IC so have had to revise the circuit somewhat. Updated source code featuring the new VGA output code and an accompanying schematic are available for those who are interested!

benryves

benryves

 

A larger level, moving sectors and failed optimisation attempts

I've made a few attempts to boost the performance of the 3D engine for the TI-83+ I'm working on with little success. I had previously failed to get any improvement by adding bounding boxes around each BSP node (the idea being that if a node falls outside the view you can discard it and, by extension, all of its children) but the act of transforming the bounding box to determine whether it was inside or outside the view was more CPU intensive than blindly handling the nodes whether they were inside the view or not.

A simpler test, I reckoned, would be to use bounding circles. These only have one point to transform, and determining whether they are in the view is one comparison to ensure that they're in front of the camera followed by one multiplication (by the constant ?2) and two more comparisons to determine whether they are to the left or right of the camera's view; far simpler than a bounding box!

The bounding circles did cut down the number of BSP nodes that were handled each frame but the additional checks made the engine slightly slower in general than it had been before. In some circumstances it was slightly faster, but not enough to make a noticeable difference. The additional data per BSP node added over 900 bytes to the level data, too, so the attempted optimisation had to go.


????????

????????

????????

The newly added rooms to the demo level

One tweak that did boost performance noticeably was to cache the projected X coordinate of each vertex. All vertices in the map have at least two walls connected to them and so are projected to the screen at least twice if within the view. I already had a table that was used to indicate whether a vertex had been transformed around the camera or not that frame so it was easy enough to add the X coordinate of the projected vertex to that table, adding around a 15% boost to the framerate.

Points are projected to the screen by dividing their X (left/right) or Z (up/down) component by their Y (depth) component. Division is slower than multiplication so I tried to calculate the reciprocal of the depth for the vertex then perform all subsequent projection operations by multiplying the X or Z component by this reciprocal. Unfortunately, this resulted in a lack of precision owing to my use of 16-bit fixed-point numbers (walls "wobbled" as you moved the camera) and performance was about the same as it had been before, so I rolled back the changes.

The block of screenshots in the above text shows a new region that has been added to the demo level, and the image below is a map of that level -- fans of DOOM may notice that it's based on a small portion of E2M7 (The Spawning Vats).



Map of the level
This level now uses every one of the 256 walls that are available, so is probably a good indication of the maximum size of a single level (and at 6,626 bytes it's certainly rather taxing on the limited amount of memory in a TI-83+ calculator).

This is, however, the maximum size of a single level. It does not take long to load and unload levels, so it would be quite possible to construct a continuous level that appears larger by unloading the current one and loading a different one when the user moves to a particular region. This could be implemented in an obvious manner (such as the player stepping into a teleporter) or transparently (by moving the player into an identical copy of the room he left to hide the transition). The latter option also introduces the option of level geometry that would otherwise be impossible in a 2D-based engine, such as rooms above rooms. Special effects could also be tried, such as an infinite corridor that warps you back to the beginning when you reach its end.


However this feature is implemented, there would need to be some way to trigger the action. The above animated screenshot demonstrates the current trigger system which is used to set a sector in motion. A sector, in this instance, is a region with a particular floor height and ceiling height. Each wall indicates which sector is in front of it and which sector is behind it. Convex sub-sectors contain sets of walls and also indicate which sector they are part of, and are attached to the leaves of the BSP tree. Given a point, you can quickly find out which convex sub-sector it is in by walking the BSP tree. When you have found the convex sub-sector you can then look up its sector. This is currently used to set the player's height, as the sector tells you the floor height.

If you keep track of the player's sector each frame you can tell when they have moved from one sector to another. This then fires an event, reporting which sector the player used to be in and which they are in now. In the above screenshot, the platform is set to descend whenever the sector surrounding it is entered from any sector other than the platform itself (this is to stop it from automatically descending when the player walks off the top of the raised platform). It is also set to rise whenever the platform's own sector is entered. This produces a simple lift; doors are handled in a similar fashion elsewhere in the level.

If you'd like to try this demo on your calculator, you can download the binaries for the TI-83 and TI-83+ in Nostromo.zip. As ever, please back up any important files on your calculator before running the demo; it may well clear your RAM. For those without calculators, an animated screenshot is available.

benryves

benryves

 

8-bit Raycasting Quake Skies and Animated Textures

All of this Quake and XNA 3D stuff has given me a few ideas for calculator (TI-83) 3D.

One of my problems with calculator 3D apps is that I have never managed to even get a raycaster working. Raycasters aren't exactly very tricky things to write.

So, to help me, I wrote a raycaster in C#, limiting myself to the constraints of the calculator engine - 96x64 display, 256 whole angles in a full revolution, 16x16 map, that sort of thing. This was easy as I had floating-point maths to fall back on.



With that done, I went and ripped out all of the floating-point code and replaced it with fixed-point integer arithmetic; I'm using 16-bit values, 8 bits for the whole part and 8 bits for the fractional part.

From here, I just rewrote all of my C# code in Z80 assembly, chucking in debugging code all the way through so that I could watch the state of values and compare them with the results from my C# code.



The result is rather slow, but on the plus side the code is clean and simple. [smile] The screen is cropped for three reasons: it's faster to only render 64 columns (naturally), you get some space to put a HUD and - most importantly - it limits the FOV to 90?, as the classic fisheye distortion becomes a more obvious problem above this.



I sneaked a look at the source code of Gemini, an advanced raycaster featuring textured walls, objects and doors. It is much, much faster than my engine, even though it does a lot more!

It appears that the basic raycasting algorithm is pretty much identical to the one I use, but gets away with 8-bit fixed point values. 8-bit operations can be done significantly faster than 16-bit ones on the Z80, especially multiplications and divisions (which need to be implemented in software). You can also keep track of more variables in registers, and restricting the number of memory reads and writes can shave off some precious cycles.

Some ideas that I've had for the raycaster, that I'd like to try and implement:

Variable height floors and ceilings. Each block in the world is given a floor and ceiling height. When the ray intersects the boundary, the camera height is subtracted from these values, they are divided by the length of the ray (for projection) and the visible section of the wall is drawn. Two counters would keep track of the upper and lower values currently drawn to to keep track of the last block's extent (for occlusion) and floor/ceiling colours could be filled between blocks. No texturing: wall faces and floors/ceilings would be assigned dithered shades of grey. I think this, combined with lighting effects (flickering, shading), would look better than monochrome texture mapping - and would be faster! Ray-transforming blocks. For example, you could have two 16x16 maps with a tunnel: the tunnel would contain a special block that would, when hit, tell the raycaster to start scanning through a different level. This could be used to stitch together large worlds from small maps (16x16 is a good value as it lets you reduce level pointers to 8-bit values). Adjusting floors and ceilings for lifts or crushing ceilings.



As far as the Quake project, I've made a little progress. I've added skybox support for Quake 2:



Quake 2's skyboxes are simply made up of six textures (top, bottom, front, back, left, right). Quake doesn't use a skybox. Firstly, you have two parts of the texture - one half is the sky background, and the other half is a cloud overlay (both layers scroll at different speeds). Secondly, it is warped in a rather interesting fashion - rather like a squashed sphere, reflected in the horizon:



For the moment, I'm just using the Quake 2 box plus a simple pixel shader to mix the two halves of the sky texture.



I daresay something could be worked out to simulate the warping.



The above is from GLQuake, which doesn't really look very convincing at all.



I've reimplemented the texture animation system in the new BSP renderer, including support for Quake 2's animation system (which is much simpler than Quake 1's - rather than have magic texture names, all textures contain the name of the next frame in their animation cycle).

benryves

benryves

 

A primitive 3D engine for the TI-83+

As you may have guessed from the number of spinning cubes in my projects, I am quite fond of primitive 3D. As you may also have guessed from the number of TI-83+ calculator projects I have undertaken, I'm also quite fond of programming on low-end machines. I have never really successfully put 3D and the TI-83+ together, though.


One way to build a 3D world in software is raycasting (e.g. Wolfenstein 3D). This typically results in blocky worlds where all walls are at 90? angles to each other. There are several games using raycasting engines on the TI-83+ already; they are much faster and better-looking than my sorry attempt pictured above.


Another method is to use true 3D geometry (e.g. Quake). Many years ago I attempted to work on something that looked a little like Quake. I built this on the Matt3D engine, which supported basic 8-bit coordinates and lines, but not solid objects. The result was even less useful than the above raycaster!


Another method somewhere between the two is a "2.5D" engine, where level geometry is defined between points in 2D space but projected in 3D (e.g. DOOM). This allows for walls that are not at 90? angles to each other, whilst simplifying the rendering procedure significantly. I spent some weeks working on such an engine a few years ago yet never managed to get any further than the above screenshot. As you can probably tell from the fact that you can see the walls through each other I never found a good way to handle occlusion, and the project ended up stagnating.

????

????
Looking for a quick weekend project I thought back to the work I'd done with the DOOM and Quake engines. These engines use a BSP tree structure to sort the level geometry for rendering. I reckoned that if simplified a little a similar tree structure could be used to render a 3D world on the TI-83+ calculator. The four screenshots above show that this technique is indeed quite successful. My implementation could certainly do with a lot of work but I think the theory is at least sound.


I decided that one way to make this project a bit more fun was to set myself a challenge; to design a level that I would, ultimately, be able to walk around in. This level is shown above, and contains a number of walls that are not parallel to the X or Y axis and a pillar. I have split the world into eight convex "sectors" (labelled 0 to 7) with a dotted line between them to show where the BSP tree is partitioned. All of the partitions are either horizontal or vertical to speed up tree traversal; the TI-83+'s Z80 CPU does not support floating point arithmetic, let alone multiplication or division, so being able to decide which side of a partition you're on quickly is very useful.


Rather than dive straight into Z80 assembly programming I knocked together a quick prototype in C#. This allows for quick and easy debugging; the blocks of colour allow me to quickly identify walls and the application title bar contains the order in which the sectors have been rendered. These can then be checked against the version running on the TI-83+ in case there are problems.


With the C# version running satisfactorily I started converting it to Z80 assembly. The above screenshot shows the first step; transforming the level's vertices around the camera. Clicking on the screenshots will take you to an animated version; as some of them are quite large I have linked to them rather than embedding them directly.


The next step was to traverse the BSP tree. The numbers across the top of the screen indicate the order in which to render the sectors, from back to front -- however, due to a simple bug, they are actually listed from front to back. This was fortunately very easy to fix.


Walls are connected between the vertices, so I quickly threw something together to display all of the walls on the screen. The walls will have to be clipped against the camera's view (or discarded entirely if they are outside the view) so being able to see them is a great debugging aid!


We are only interested in drawing walls that are in front of the camera, so the first bit of clipping code deals with clipping the walls against Y=0.

????????
The above screenshots show the final three stages of clipping to the camera's view, defined by Y>0, Y>+X and Y>-X. The first screenshot shows culling of any wall that does not satisfy this in any way; walls that are completely outside the view are discarded. The second screenshot shows walls being clipped against Y=+X, and the third finally adds clipping against Y=-X. The lack of hardware floating-point arithmetic makes the code fairly slow and ugly but it does seem to be working relatively well.


We are only really interested in dealing with walls that are facing the camera; we don't want to draw the back of walls. To work out which we want to keep and which we want to ignore, we project the wall to the screen and check whether its projected start vertex appears to the left or the right of its end vertex.

?????
A simple perspective projection is performed to turn this clipped 2D world into what appears to be a 3D one. The X coordinate of each vertex is divided by its Y to get the X coordinate on-screen and the height of the wall is divided by the vertex Y to get the Y coordinate on-screen. The left screenshot shows the top and bottom of wall edges; the right screenshot adds lines between the floor and the ceiling to produce a more convincing "wireframe" view of the world.


The final step is to make the world appear solid, by hiding walls that are far away behind walls that are closer to us. Traversing the BSP tree gives us the order in which to draw the walls, so all that is required is to draw solid quadrilaterals for each wall rather than the lines around its outside. A fast clipped quadrilateral filler would take me some time to write so I cheated by drawing a solid white rectangle the width of the wall and the height of the entire screen before drawing the wall outlines. As the camera is half-way up each wall and all of the walls are the same height there are no cases where a foreground wall only partially covers a background one so this trick works for the time being.

I'm glad I achieved my goal of walking around the 3D world I'd sketched in pencil at the start of the weekend but I'm not sure where I'll be able to take the project now. Turning it into a useful 3D engine for a game would certainly require a lot of work. The level and its BSP tree were generated by hand, which would not lend itself well to anything but the simplest of levels. However, the lack of variation in wall heights produces fairly dull levels in any case; DOOM-style levels would be something to strive for, but I'm not sure how well the calculator would be able to cope with them. I'm also unsure how well the engine would scale; this very primitive version only achieves around 12 FPS on a 6MHz TI-83+. It's certainly given me something interesting to think about!

If you would like to try the program on your calculator, please download Nostromo.8xp. It requires an Ion-compatible shell to run. It is very primitive, likely to be quite buggy (you may encounter rendering bugs when very close to walls due to integer overflow in the clipping and projection code) and may well crash your calculator; please back up any important files before running it. Use the arrow keys to move around, Trace and Graph to strafe and Clear to quit.

benryves

benryves

 

Collision detection makes the world feel solid

One of the larger problems with the 3D engine for the TI-83+ calculator series I have been working on is that it's possible to move the camera through walls. This doesn't make the world feel especially solid, so I've started working on some collision detection routines.

Work commitments have left me with little time to spend on this project over the last couple of weeks so progress has been very slow, but I've got a basic collision detection system mostly working.



Click to view an animated GIF of the collision detection routines in action.
I spend most of the above screenshot running into walls. The code seems to work relatively well and quite quickly, though it's far from perfect. The still image shows the new settings screen, which is hopefully a little easier to use than remembering which keys do what. It also has the advantage of displaying the state of the current settings.

The walls are stored as line segments between two 2D vertices, and the collision detection has to ensure that the player does not get too close to any of these walls. The technique I have used starts by calculating the closest point on the line to the player.


The above image shows a wall (the solid line segment) and three possible player positions (the heavy dots). The arrows point to the closest point on the wall's line. The closest point on the line to the top player position is past the end of the line segment, so it is ignored. The other two closest points lie on the line segment, so these are checked in more detail.


The distance between the closest point on the line and the player position is then calculated and compared to a threshold value (the radius of the player). The above image highlights the out-of-bounds region in tan. The lower player position is outside this region so is ignored, but the upper player position is inside it and needs to be corrected.


The correction is quite straightforward. We know the closest point on the wall to the player. The angle of the wall's normal is stored in the level file, so we can easily calculate a vector from that to push the player a fixed distance away from the wall.

In addition to the above 2D checks, a very simple height check is performed for "upper and lower"-type walls. These are walls with a central hole so you can pass over or under them, and are used to connect sectors with varying floor and ceiling heights. The top of the player's head is used to check the ceiling height. Rather than use the height of the player's feet to check the floor height their knee height is used. This is to allow the player to climb low walls (such as the edges of steps).

When I first implemented these collision detection techniques I checked every wall in the map. This halved the framerate in places, and as the framerate is not particularly high in the first place I needed to find a way to reduce the number of tests. Taking further inspiration from DOOM I implemented a "blockmap". This breaks the map down into square blocks and each block contains a list of which walls pass through it. To perform collision detection I look up which block the player is in and from that I can retrieve a reduced list of which walls they may end up walking into. The original implementation had to check well over a hundred walls for each movement; the blockmap reduces this to 26 in the worst case scenario for the current level design.

Sadly, this additional blockmap enlarged the size of the map quite a bit, so I've attempted to reduce it a little. For simplicity and performance most structures referred to other structures by pointer (for example a sub sector contained a list of pointers to walls and each wall contained pointers to a front and back sector). I've changed most of these to now refer to other structures by index, which shaved a few hundred bytes off the map at the cost of a few hundred clock cycles. Overall performance still isn't great, though I haven't found it noticeably slower than the previous demos.

I added very primitive physics for moving the player up and down relative to the floor to complement the collision detection. This retrieves the floor height from the sector directly under the centre of the player and compares it to the current player height. If the new floor height is higher than the old floor height then the player's foot height is set to a point half way between the two; this smoothes the animation slightly when climbing up stairs (rather than just snapping to the new floor height). When moving from a higher floor to a lower floor the player's downward speed is increased to roughly simulate gravity.

A demo for the TI-83+ series and TI-83 can be found in Nostromo.zip. As always, this is a piece of software in development and there may be calculator-crashing bugs, so please back up any important files before running it.

benryves

benryves

 

XNA Quake

After pratting around with DOOM with MDX, it's logical to move up to Quake with XNA. After all, the levels are "true" 3D, and there's a healthy amount of new textures (including bump and gloss maps) so that it would make creating an advanced engine easier. My problem is always finding resources.

On the subject of resources, I'm using a system whereby the various classes for game resources (such as the Level or Model class) can implement ResourceLoader.ILoadable, so I can do things like this:

ResourceLoader = new ResourceLoader(@"C:\Program Files\Quake\ID1");

Palette DefaultPalette = ResourceLoader.Load("gfx/palette.lmp");
Level SomeLevel = ResourceLoader.Load("maps/start.bsp");



This way the ResourceLoader can handle dragging data out of multiple pack (.pak) files or loading override files.

First things first - loading levels. This is nice and easy (unlike DOOM) - the .bsp files contain a list of vertices and a list of edges (each edge contains a start and end vertex index). For testing purposes, I used the Graphics class to draw out each level in 2D:



Next up, I create a point list and render those in 3D:



...and now the edges as a line list:



That seems to be on its side. Quake uses (x, y) as a 2D floor plan and z for height, so I need to swap z and y (and negate y whilst we're at it).



Quake uses faces for solid walls, which are simply convex polygons made up from lists of the aforementioned edges (and may have more than three sides). I'm creating a simple triangle list, and applying some fog to make the world look 3D (there's no lighting):



Texturing would make a welcome addition. Quake uses a rather novel texture coordinate system (at least, I've never seen anything quite like it) whereby each face references a TextureInfo structure, which contains the index of the texture itself, a pair of vectors indicating the horizontal and vertical axis (for aligning the texture against the surface of the face) and an offset in texels (so the texture can be aligned to the edges of the face). Fortunately, the BSP documentation I have indicates how to use this information to calculate the texture offset per-vertex, and by dividing by the texture's width and height I can get "conventional" texture coordinates.



The real Quake textures would be nice, I suppose. As I break up the faces into triangle indices, I group them by texture index. The final triangle list is made up of groups of triangles by texture, so that (for example) triangles 0 to 10 will be texture 0, triangles 11 to 14 will be texture 1, triangle 15 will be texture 2 and so on. I also maintain a list of which triangles use which texture, so to render I simply set the texture, draw a range of triangles from the single list, then move on to the next texture.





It's all pretty brute-force stuff at the moment, but it's still very fast on modern hardware. Next up will be lightmaps. [smile]

benryves

benryves

 

Superprobe

I have recently been working on building my own Superprobe. This is a cheap and simple tool based around a single PIC 16F870, a four-digit display and a handful of other parts. Hardware details and software can be found on the Superprobe section of the Mondo Technology website.


As the name suggests, at its simplest the Superprobe can be used as a logic probe, displaying an L, H or - if it touches a point in the circuit that is in a low, high or floating state. What makes it so "super" is that by using the two input buttons you can switch it to a different mode. The supplied software provides seventeen different modes, including a logic pulser, frequency counter, voltmeter, capacitance meter, signal generator and serial ASCII data output.


Measuring a 10uF capacitor.
Having such a wide range of functions for such a modest part count made this a very attractive project to build. Unfortunately, I couldn't find a 16F870 so used the pin-compatible 16F876A instead; porting the code from the older microcontroller mainly involved changing the list p=16f870 directive. I did notice that the probe didn't seem to save its settings when powering down as it should, so I copied the EEPROM reading and writing code from the 16F876A datasheet into the source to replace the existing code which seemed to fix it.


The insides of the Superprobe.
As I couldn't find a suitable low drop-out 5V regulator I opted to use a conventional L7805 regulator. This means that the input voltage has to be at least two volts higher than the output; I normally power circuits from a 7.5V or 9V supply anyway so this isn't too much of a problem. Finding a suitable battery to go inside the case was more of a challenge; there's insufficient room for the typical 9V PP3, sadly. A bit of hunting for "7.5V battery" led me to a suitable battery with a variety of names and a rather high price. Aided by a ruler and the dimensions on the above website it seems that the A175 is exactly the same size as five LR44/AG13 cells stacked on top of eachother (coincidence? I think not). A reputable high street shop noted for the quality of its goods sold a card of forty button cells (including ten AG13 cells), so five of those and a bit of masking tape provided me with a passable imitation. Sparing no expense, the battery holder is constructed from paper clips.

The 16F876A has more program space and SRAM than the 16F870, making developing software in C more viable. Not all of of the original software's features were especially useful to me, and I was likely to want to add new modes myself in the future, so set about reimplementing the functions that I did find handy in C. The result is quite a bit easier to modify; for example, the above photograph demonstrating the measurement of a capacitor shows a value with an SI prefix (10.1u for a 10uF capacitor). All one needs to do to display such a number on the display is call display_print_float(10.1e-6f); - the code does the rest for you. Sadly, this does inflate the size of the code significantly and my current version of the code only squeezes 11 functions into a much larger chip (compared to the 17 on the original).


Measuring a 2.2K? resistor.
One of the new modes is a resistance meter. This works by pulling the probe tip high using a known resistance (5K?, 10K? or 100K?) and combining this with the resistor to be measured between the probe tip and ground to form a voltage divider. The output of the voltage divider is measured, and from that the resistance of the resistor being tested can be determined. The ability to use multiplication, division and floating point arithmetic makes this easy to program in C; much more so than it would have been in assembly, at least!

">
View Superprobe video demonstration on YouTube.
I have recorded a video demonstrating the Superprobe. The code for my variation on the theme can be downloaded here, and can be compiled with the free ("lite") edition of the HI-TECH C compiler.

benryves

benryves

 

USB remote control receiver for PowerDVD

I enjoy watching films and mainly do so sitting at my desktop PC. This has taught me that cheap office chairs are not the most comfortable things to sit on for extended periods of time, especially when the next room contains a comfortable bean bag and a good place to stick a screen. A gap between the two rooms allows me to pass cables from one to the other, and after purchasing a 10m DVI-D cable and a USB extension lead on eBay I had both picture and sound sorted out (I use a USB sound "card"). This left me with one final problem: how to control the PC through a wall.

One possibility would be to extend the lead on my keyboard, but its media buttons light up (bothersome in a darkened room) and some of the keyboard shortcuts in PowerDVD (such as Ctrl+P for the popup menu when watching Blu-ray discs) are tricky to hit in the dark. Given my fondness for infra-red remote controls building a remote control receiver would seem like both an interesting and useful way to spend a weekend.


Rather than build something that relied on some Windows software to translate received remote control signals into keystrokes I decided to use the free V-USB library to construct something that showed up in Windows as a standard USB keyboard. One of the sample V-USB projects is a USB keyboard, which made getting started much easier! The above photograph shows the initial prototype, based around an ATmega168. The tall three-legged component sticking up out of the board is a TSOP2438, which is an infra-red receiver and demodulator. This is tuned to the 38kHz carrier employed by most remote controls and outputs a logic low or logic high depending on the presence or absence of such a signal. The ATmega168 is programmed to time the incoming signal and passes this timing information to a collection of routines that attempt to decode it. I have currently two decoders, one for the NEC protocol and another for SIRCS -- information about some common protocols can be found on this website.

The choice of these two protocols is down to the remote controls I have around me. The one that offered me the most useful buttons was the PlayStation 2 DVD remote control (SIRCS), though this is missing some useful controls, such as volume and the red, green, yellow and blue buttons. To remedy this I went and bought a cheap universal remote control from Clas Ohlson. After hunting through several of the modes I settled on the Clas Ohlson DVD one (0815) as most of the buttons work in this mode (the only unshifted one that doesn't is the record button, and I can live without it). In this mode the remote control uses the NEC protocol.


To turn the receiver into something more conveniently sized I decided to switch from the 28-pin ATmega168 to the 14-pin ATtiny84, shown in the above photograph. The compiled program was already small enough to fit into the reduced memory, and the only modification I had to make was to amend two timing routines to share the same timer peripheral as the ATtiny84 only has two timers, not the three I'd been using on the ATmega168.

I also opted to add a switch to the design. One problem with supporting both Blu-ray and DVD is that the way you navigate menus is quite different between the two; Blu-ray discs use a simple popup menu (Ctrl+P) which appears on top of the film, whereas DVDs seem to offer a number of different menu commands -- the two most common ones being "Title menu" (no shortcut) and "Root menu" (J). PowerDVD also lets you choose from a list of DVD menus in a context menu with one shortcut (L). I set the button on the receiver to switch between "Blu-ray" and "DVD" modes; in Blu-ray mode, the menu button sends Ctrl+P and in DVD mode the menu button sends L.


I bought an enclosure that is, in retrospect, a little too small. The above photograph shows the receiver assembled on stripboard with a fairly cramped layout. Fortunately there was sufficient room to include pin headers on the board, which will allow me to plug in a programmer to modify the software should I need to in the future. The LED on the front serves as simple user feedback -- it flashes whenever it receives a valid command and sends a keystroke back to the PC. When the mode is toggled between Blu-ray and DVD menus it flashes to indicate the new mode -- a long flash followed by a single short one for Blu-ray, a long flash followed by two short ones for DVD.


Overall, I'm quite happy with the way it turned out. It works well enough for my needs, though as those needs only extend as far as PowerDVD and a particular remote control it's rather basic and much more could be done with the hardware. I have uploaded the source code and a schematic for the project to my website as it currently stands for those who are interested.



benryves

benryves

 

Z80 computer with a primitive I/O board

A computer needs some way of interacting with the outside world via input and output devices. It's about time, then, that the Z80 computer project acquires a section dedicated to I/O.


The Z80 differentiates between memory and I/O devices, though both share the data bus and the address bus. You can control I/O devices using the in (input) and out (output) instructions. When you input or output you must specify a device address and a value or target register. For example,

in a,($20) ; Read a value from device $20 and store it in A.
ld a,123
out ($40),a ; Output 123 to device $40.


When you write to a device, the following happens:
The address bus is set to the address of the device to output to. The data bus is set to the value to be written to the device. The /IORQ and /WR pins on the Z80 go low. The device processes the written data. The /IORQ and /WR pins on the Z80 go high.
Reading from the device is very similar:
The address bus is set to the address of the device to read from. The /IORQ and /RD pins on the Z80 go low. The device puts the value to read onto the data bus. The Z80 reads the value on the data bus. The /IORQ and /RD pins on the Z80 go high.
(Accessing memory is a similar procedure, except with the /IORQ pin replaced by the /MREQ pin).

Interfacing I/O devices to a Z80 CPU should be rather straightforwards, then. I am using a 74HCT138N 3-to-8 line inverting decoder to handle the address bus input and /IORQ signal. This IC has three address inputs and 8 outputs. If the address input is %000, output 0 is low and all the other pins are high; if the address input is %001 output 1 is low and all the others are high; if the input is %010 output 2 is low and all the others are high (and so on and so forth). /IORQ is connected to another input on the chip, /E1, which causes all of the pins to go high when it is high regardless of the address input.

What does this mean in practice? Well, most devices have a "chip enable" or "chip select" input pin. When this input is active the device performs its function, but when the input is not active the device is deactivated and doesn't respond to any other inputs or output anything. By connecting each output of the 3-to-8 decoder to a particular device's chip enable pin I can ensure that each device is only activated when its address is specified on the address bus and the /IORQ pin on the Z80 is low.

I have connected the Z80's A5-A7 to A0-A2 on the 3-to-8 decoder. This means that the first device has a base address of $00, the second $20, the third $40 and so on at increments of $20. This might sound a little odd, but has a reason. Some devices, such as the LCD, have sub-addresses of their own. In the case of the LCD, it has a pin that specifies whether you're dealing with an instruction (such as a command to switch the display on or off or read the LCD status) or some data (which forms part of the picture on the LCD). By attaching this pin directly to the Z80's A0 and the LCD's chip select pin to output 1 from 3-to-8 decoder you end up with an LCD instruction port at $20 and an LCD data port at $21.

An LCD is all well and good, but we'll need to take input from the user. To accomplish this, I'm going to supply two PS/2 ports and implement the AT protocol (as used by PS/2 keyboards and pointing devices) in software. Each device only requires two open-collector data lines (data and clock), so a single I/O device that provides eight I/O lines would be useful.

The design I'm going for uses two 74AC373 octal transparent latches. When the latch enable input pin is held high whatever value is on the input passes through to the corresponding output. When the latch enable pin goes low, the last value that was latched at the input is still output. These particular latches also have an output enable pin that can be used to disable the outputs and let them "float" (ie, other devices can then drive that particular connection high or low as required). In this instance, one latch has its output enable pin activated so that it always outputs the last value written to it and has its latch enable pin connected to the Z80's /WR pin. The other latch has its latch enable pin activated so that it always outputs the values at its input and has its output enable pin connected to the Z80's /RD pin.


Click for legible schematic
The transistors on each output are used to provide open-collector outputs. When the base of the transistor is held low, the transistor is "switched off" and its output floats, and so can be driven by external circuitry. When the base of the transistor is held high, it switches on and effectively connects the output to ground. A pull-up resistor ensures that the pin has a high signal when not connected to anything. This arrangement is useful as each pin can be driven low by either device and so works as an input or an output (for a real-world example, an AT keyboard usually outputs a clock signal on one line to the host when sending data, but if the host pulls the clock line low it can inhibit communication and the keyboard buffers the data to send instead).


Click to toggle labels
Rather than build the circuit on breadboard, I went straight to stripboard. The above photo shows an incomplete version of the output board. Only one PS/2 port is wired up at all! The pin header to the left is to connect the LCD to. The coloured wires at the extreme left connect this I/O board to the rest of the computer.


Click to toggle labels
I have modified the Z80 board I was using last time to add support for RAM. The 3-to-8 decoder in the bottom right is used to partition the address space into two 32KB regions. The lower 32KB is mapped to ROM, and the upper 32KB is mapped to RAM. This wastes 75% of the ROM chip (it's a 128KB chip) but without a more complex memory management unit this will have to do for the moment. The most significant bit of the address bus, A15, is fed into the 3-to-8 decoder along with the /MREQ pin.

The test software is a Z80 program that displays an animation on the LCD using 20 frames (1KB per frame) stored in ROM.


Click for video (943KB WMV)
The Z80 is still not breaking MHz speeds, but there are problems here. I have not interfaced the LCD correctly, as its timing patterns for reading and writing data are quite different to those used by the Z80. Bizarrely, holding the E pin on the LCD permanently high appears to work 99% of the time, even though the datasheet indicates that it should be used to clock data in or out. The result is glitches in the data sent to the LCD, usually on the left hand side (the left hand side of the display has a propensity to believe it's been sent the "switch off" command). I'm not sure I'll be able to remedy this situation. Judging by the datasheet it looks like the LCD does its stuff when the E pin goes from a low to high state (the Z80 does everything when /IORQ goes low), so maybe simply inverting /IORQ and pumping it into E will do the trick.

benryves

benryves

 

Enlarging the world

There have been very few changes to the features of Nostromo recently. I have tried a number of ways to optimise the performance and whilst the handful of micro-optimisations I have made have boosted the frame rate a little none of the higher-level optimisations have done much. I did try, for example, storing a bounding box around each BSP node and ignoring it (and all its children) should this bounding box fall outside the field of view; the additional code to check the bounding box ended up halving the framerate rather than improving it.

????????

????????
I have, however, enlarged the level quite considerably. A staircase connects the central room with the pit to a rather strangely-shaped arrangement of walls (again copied from E2M7). The room with a pit continues to cause issues; looking across it towards the room with the small central staircase forces the engine to step through a very large number of convex sub-sectors and check many walls. This drops the frame rate down to about 3 FPS on a TI-83+. However, this is specific to that room; the newly-added rooms have not noticeably affected the frame rate in other parts of the level.

Another minor improvement is that the engine now supports different sprites. I'm not too good at drawing them, as you can probably tell from the above screenshots, but at least the code is there to support them.

You can download a TI-83 and TI-83+ binary to try the demo on your calculator (please back up any important files first). Alternatively, here is an animated screenshot.

benryves

benryves

 

SG-1000

I've added some support for the SG-1000, Sega's first? home video game console.



The Master System's VDP is a modified TMS9918, and so most Master System games run in its extended 'Mode 4' setting. That was the only video mode, therefore, that I'd emulated in any form.

For some reason, the older computer games are, the more charm they seem to have to me (maybe because the first games I played would have been on a BBC Micro, which certainly looked a lot more primitive than the Master System games I've been attempting to emulate thus far). I dug out TI's TMS9918 documentation - the differences are quite significant! Tiles are monochrome (though you can pick which foreground and background colour is in use - to some extent), the palette is fixed to 16 pre-defined colours (one of which being 'transparent') and sprite sizes and collisions are handled differently. Various features found in the Master System's VDP (scrolling, line-based interrupts) also appear to be missing from the 'vanilla' TMS9918, but I'm not sure whether or not they make an appearance in the SMS variation of the VDP or not, along with the original TMS9918 limitations.


Flipper

At any rate, the emulator now has a 'SG-1000' mode. The only differences at the moment are that the TMS9918 palette is used and line interrupts are disabled, so you can still (for example) use mode 4 on it.



From first to last: Drol, Choplifter, Hustle Chummy, Championship LodeRunner, Space Invaders, H.E.R.O., Elevator Action, and Zaxxon.

All but one of the SG-1000 games I had ran - and that was The Castle. According to meka.nam, this has an extra 8KB of onboard RAM. Whilst doing some research into the SG-1000 and the TMS9918, I found a forum post by Maxim stating "The Castle runs nicely on my RAM cart :)". Enter the new ROM mapper option to complement Standard, Codemasters and Korean - it's the RAM mapper, which simply represents the entire Z80 address range with a 64KB RAM.



That seems to have done the trick!

I mentioned that the palette was different in the SMS VDP and the original TMS9918 - here's an example (SMS on the left, SG-1000 on the right):



I'm assuming this is the result of truncating the TMS9918 palette to the SMS 6-bit palette without needing to implement two palette modes on the new VDP. Another comparison is between two versions of Wonder Boy - SMS on the left again, SG-1000 on the right:


benryves

benryves

 

ColecoVision and TMS9918 Multicolor

One notable gap in my TMS9918 (video) emulation was its "Multicolor" mode. This mode broke the screen down into 4x4 pixel squares, resulting in a 64x48 grid. Each cell could be assigned a unique colour, giving you a crude bitmapped video mode.

No Master System or SG-1000 software used this mode to my knowledge, which reduced the likelihood of it being supported at all (if I could't test it, how could I emulate it?) I was tipped off that some ColecoVision software made use of it, so set about emulating the ColecoVision.


The ColecoVision hardware is very similar to the SG-1000 in terms of what is inside the case - a Z80 CPU, TMS9918 video and SN76489 sound. The memory map (as in, which address ranges map to which memory devices) is different, as is the I/O map (as in, the I/O ports that the various hardware components are connected to). To handle this case, the emulator now has a Family field, which can be set to Sega or ColecoVision. This controls which of the different mappings it uses for memory and hardware I/O.

Another difference is the presence of a BIOS ROM. The Sega Master System and Sega Game Gear consoles had the option of BIOS ROMs, but all these did was very basic initialisation and header/checksum checking. The ColecoVision, however, has an 8KB BIOS ROM that offers a lot of functionality to the programmer, so this must be present to run most ColecoVision games.


The controllers are also quite different. As well as the typical eight-direction joystick and two fire buttons, it added a 12-key keypad (0-9, * and #). This many keys is making my InputManager class look thoroughly idiotic, so that will certainly need a rewrite.

Apart from that, it's pretty simple. RAM is 1KB instead of 8KB; the sound generator uses the standard 15-bit wide shift register (instead of the 16-bit wide one used in the SMS); the video display processor interrupt output is connected to NMI rather than INT.

With those differences applied, it's easy to add the few lines of code to emulate the Multicolor video mode.


Smurf Paint 'n' Play Workshop
ColecoVision emulation has not been tested at all thoroughly, so chances are it doesn't work very well; Multicolor emulation has been tested with precisely one game!

You can download the latest build of Cogwheel from its website, featuring the new ColecoVision emulation.

benryves

benryves

 

New Z80 emulator

One chap I cannot thank enough is CoBB for all his hard work in the Z80 field.

I've rewritten the Z80 emulation from scratch; this time it uses an expanded switch block (the 'manual' way) to decode instructions. Rather than write every combination of instructions out by hand, the code making up the switch blocks up is generated by another program, reading instruction information from a table copied from an Excel spreadsheet.

At the cost of a significantly larger assembly (from 40KB to 140KB) I now get a 100% speed increase (from ~50MHz to 100MHz).

I still can't pass the port of ZEXALL I'm testing with (and the same instructions too - not bad for a 100% rewrite to end up with exactly the same bugs), but after comparing some of my offending tables against CoBB's ones I've isolated some of the hiccoughs. The only instruction group test I fail is, naturally, the one that takes the longest to execute - getting a hardware, or indeed emulator, comparison takes well over an hour.

Anyhow, rewriting the Z80 emulation seems to have been the right thing to do. As you might have guessed from the picture at the top of this entry, Sonic now runs.


My VDP (Video Display Processor) emulation is still rather rough-and-ready (I've really been concentrating on the Z80 bit) and the second column of background tiles is not updated correctly, so I apologise in advance for the distortion! It only appears in SMS mode (the display is cropped in Game Gear mode).


The fill colour (in the left column here) is incorrect in most games too.


I rather preferred Sonic 2, but maybe that's because you can pick up dropped rings and I'm rather lousy at it otherwise.


I accused the previous Wonder Boy III shot of not reading the start button. Somehow I also failed to notice the missing sprites (clouds and main part of the castle), which was part of the main problem. It now appears to play well.


Support for zoomed sprites seems to be missing in some emulators (at least, the versions of Dega and Pastorama I have to hand), but I use them so implemented them to let my programs work when testing - it's nice to see a commerical game use them too!


Gunstar Heroes runs, but flickers and jumps (not the visible sprite 'flicker' in those screenshots) - some bug in my CPU interrupt handling or VDP interrupt generation.


Psycho Fox runs insanely fast - even faster than The Flash as seen to the right - making an already difficult game to control virtually impossible. I'm really not sure what could be causing that, but the enemy sprites sometimes flicker up and back down quickly, so it could be one of the remaining CPU bugs.


Fantasy Zone demonstrates both the blanking colour bug (that left column should be green) and the distorted second column bug, but plays well. I tried to get a better screenshot of Maze Walker, but not handling 3D glasses makes looking at the screen a rather unpleasant experience (it flickers the left and right eye views in quick succession; the 3D glasses had two LCD shutters that opened and closed in sequence with the images on-screen).

I might add a Dega-esque red/green anaglyph filter, but I find those rather unpleasant to look at so might provide a stereo pair view.

In any case, the most important SMS game now runs -


I shall refrain from using the Terry Pratchett quote (just this once).


Some games look like the above, which makes me happy - it's the ones that do nothing at all that worry me. What with the CPU bugs, dodgy emulation of the mapper, missing (important!) hardware ports and hackish VDP, it's surprising anything runs. It's getting better.

EDIT: How are there so many spelling errors? Fantasy Start as opposed to Fantasy Zone, Video Display Hardware as the expansion of the VDP acronym... I should not write these so late at night.

benryves

benryves

 

JavaScript Raycaster

A silly project from over the weekend: JavaScript raycaster.

I couldn't work out how to handle keypresses in Firefox, and Firefox is too slow and flickery to make it worthwhile. IE runs it fine but at less than 1FPS, meaning that more often than not you end up walking through the walls. Opera is about the only browser it works happily in (~12FPS). Ah well.

benryves

benryves

 

Remapped IO.DLL

A common problem with software that uses the parallel port is that is hard-coded to use particular port addresses, such as 0x378 for LPT1. This is all well and good on older machines that have integrated or ISA ports that can be assigned this base address, but newer machines with PCI cards don't get any choice over which port address range is assigned to the parallel port.


Programming an AM29F010B with a PCI parallel port.
The supporting software for the Willem programmer has this problem, and so far I've been using an old XP laptop with a flaky WiFi adapter to program chips. This has been rather painful, understandably!

Fortunately, in Windows NT Microsoft forbade us from accessing hardware ports directly when in user-mode. This probably doesn't sound very fortunate, but the result is that various libraries for accessing the I/O ports using an embedded kernel-mode driver sprang up. Whilst this still isn't perfect (you need to run the calling app with Administrator privileges) it does mean that apps that access the parallel port will have one of these libraries kicking around as a DLL in their installation directory. The Willem programmer software is no exception; it uses IO.DLL.

The trick, then, is to simply replace the IO.DLL bundled with the app with a custom one that performs the same task, but redirects port writes in the LPT1 range (0x378~0x37F) to a user-specified base address (in my case, 0xCCD8). I couldn't get IO.DLL itself to work from within a DLL, and using it meant that you'd have to also rename the existing IO.DLL to something else, so I used Inpout32.dll instead to access the ports from the new DLL.

IO.DLL provides many helper functions, Inpout32.dll only offers two - Inp32() and Out32(). Fortunately, the Willem software doesn't seem to use any of the helper functions, and only uses PortIn() and PortOut(). (It does also use IsDriverInstalled(), but I've hard coded that to always return -1). The sample C++ interface code doesn't check the return value of GetProcAddress(), so the software still initialises, but will crash with an access violation if it tries to use any of the unsupported functions.

With some help from ibutsu to get around the C function name mangling problem (resolved by adding a simple .def file), you can simply extract three files into the Willem software directory (new io.dll, inpout32.dll for port access and io.ini for the user-specified port address) and the software appears to work fine! (I haven't tested all chip modes, as I only have one type of flash memory chip and an I2C serial EEPROM, but they work).

Download DLLs and source or documentation.

benryves

benryves

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!