[PC C++] DirectX Help

Got questions? Got answers? Go here for both.

Moderator: MaxCoderz Staff

User avatar
thegamefreak0134
Extreme Poster
Posts: 455
Joined: Mon 23 Jan, 2006 10:09 pm
Location: In front of a Computer, coding
Contact:

Post by thegamefreak0134 »

If using palettised images (GIF, PNG, PCX, some BMP &c) you can get access to the palette through Bitmap.Palette. Be warned, however, that if you want to change the palette on the fly you need to copy the palette, edit it, then write it back - editing it directly doesn't work.
OK, so that't the palette covered. I assume that I would have to lock the bitmap (in the PCX case) as a byte type so it would return the pixels, which would be numbers pointing to colors in the palette? And if say I wanted to use 16 color pictures (you see them a lot in GBA programming) then each byte here would represent two pixels and I could bitmask + bitshift to access them individually? Am I just totally wrong here?

I will indeed make sure to get the new version. Fortunately, (1) My advisor trusts me, and (2) The school wouldn't know how to put restrictions on the computer if they wanted to. I personally installed the security measures on about half of the campus, so I know it's all worthless. (except the stupid filter. Grr...)

-gamefreak
I'm not mad, just a little crazy.

DarkNova - a little side project I run.
User avatar
benryves
Maxcoderz Staff
Posts: 3089
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Post by benryves »

Take a look at this for the various pixel formats: http://msdn2.microsoft.com/en-us/librar ... ormat.aspx

For a picture with a 256 colour palette, you'd lock the bitmap using Format8bppIndexed, and each byte would represent a palette index.

As for 16 colour palettes, there is a Format4bppIndexed. I'm not sure what this is, and it might be that it is two pixels per byte. I'd actually suggest wasting a little memory and using the 256 colour image and a whole byte for each pixel, rather than having to deal with shifting to fit things into a single byte.
User avatar
thegamefreak0134
Extreme Poster
Posts: 455
Joined: Mon 23 Jan, 2006 10:09 pm
Location: In front of a Computer, coding
Contact:

Post by thegamefreak0134 »

I will definately look, as my program will of course have to be able to determine what type it is on the fly.
As for 16 colour palettes, there is a Format4bppIndexed. I'm not sure what this is, and it might be that it is two pixels per byte.
Cool.
...I'd actually suggest wasting a little memory and using the 256 colour image and a whole byte for each pixel, rather than having to deal with shifting to fit things into a single byte.
I would, but the memory being wasted here would be GBA data (on which the cart size is limited severely on standerd carts), not PC data.

Thanks a bunch. I think I have all I need for now. Thanks a million. (If I ever get that much, it's really yours! :lol: )

-thegamefreak0134
I'm not mad, just a little crazy.

DarkNova - a little side project I run.
User avatar
thegamefreak0134
Extreme Poster
Posts: 455
Joined: Mon 23 Jan, 2006 10:09 pm
Location: In front of a Computer, coding
Contact:

Post by thegamefreak0134 »

Well, get ready to laugh.

I fixed up the older emulator (a CHIP-8 emulator, BTW) and used the new graphics methods, which should have been much faster. I even got rid of the whole timer thing in favor of the while loop. The catch? Well, it still runs the same speed. Almost exactly. Apparently, I can't code worth crap. :roll:

I have the darn thing donig an If check for all 35 (more like 16 at this point) different instructions. I realise this i a horrible way to do things, but I still figured it would be slightly fast. (I'm used to a calculator, remember?) Not so. You can easily see everything re-drawing, which is not good.

My guess is that there is a better way. In the instructions, I can eparate them into groups by the first nibble of the first byte. (all instructions are 2 bytes long.) In doing so, I could easily cut the checks down to more like 4 or 5 checks than 35. However, I'm running into another issue.

How do you do pointer arrays in VB.NET? For that matter, how do you even do pointers? I want to have something to the effect of jump to function (x) where x is the first nibble of the instruction. Unfortunately, the only thing I can find is functions that differ by argument list, which is not at all what I need. I really want to create an array of functions. Is there a way to do this in VB? It would help me tremendously...

Thanks again.

-thegamefreak

PS: Can't open PCX. Apparently Microsoft didn't have enough money from the company that made it when VB7 came out. I'll have to do it the old fashioned way and read the file in by hand. Darn.
I'm not mad, just a little crazy.

DarkNova - a little side project I run.
User avatar
benryves
Maxcoderz Staff
Posts: 3089
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Post by benryves »

Pointer arrays? No such thing, really, in managed languages. You can't use unmanaged pointers at all in VB, and it's really a last-resort thing in C#.

When you say you check each instruction, I hope you don't mean like this:

Code: Select all

If Instruction = &HFF Then
    ' Do something
ElseIf Instruction = &HFE Then
    ' Do something else (&c)
EndIf
If so, no wonder it is slow (it needs to trickle through nearly every single conditional to get to the ones near the bottom) :D Use a Select Case block instead (the BASIC "switch" equivalent), as the compiler can optimise this (within reason) to a jump table (much, much faster!).

One bottleneck is the locking and unlocking of the image. Make sure you don't call this more than is required. :)

You could also always use the managed equivalent of "jumping into functions" which is to use delegates (often used for callbacks and so on) if you really must. Try the Select Case method first, though.
User avatar
thegamefreak0134
Extreme Poster
Posts: 455
Joined: Mon 23 Jan, 2006 10:09 pm
Location: In front of a Computer, coding
Contact:

Post by thegamefreak0134 »

Well of all things... Maybe I'm not as comfortable in VB as I thought. I don't even know how to do that. (You don't have to tell me, I'm sure it's really simple.) I've done all of my programming out of business related VB books, which basically tell you how to do business type math. (Thay think the purpose of all computers is to add columns of numbers together for you.) As such, speed is never mentioned. I will definately do a makeover of the code now that I know this.

As for the unlocking-locking, I do this every time a sprite is drawn, which is how the actual hardware would have it done. When I do my GB emu, unless I include it as a debug option to see every pixel get drawn, I will simply draw every Vblank, as would look proper.


You could also always use the managed equivalent of "jumping into functions" which is to use delegates (often used for callbacks and so on) if you really must. Try the Select Case method first, though.
I assume this is just another way this can be done? And I also assume that it would be slightly slower? I don't particularly need to use a pointer array, it just would have worked well.
I'm not mad, just a little crazy.

DarkNova - a little side project I run.
User avatar
thegamefreak0134
Extreme Poster
Posts: 455
Joined: Mon 23 Jan, 2006 10:09 pm
Location: In front of a Computer, coding
Contact:

Post by thegamefreak0134 »

Alright, I'm having a really tiny math error somewhere that's throwing the entire emulator into a wierdness-like state. Maybe you can help me out here. I've posted a link to my source here:

http://thegamefreak0134.googlepages.com/CHALLENGE8.zip

I promise you I've serched this code over and over and over again, to no avail. I think the problem lies in the subtract routine (8XY6). The docs I have state that it should set vF (Vary(15) in my code) to 1 if the subtract requires a borrow, and 0 if not. However, I find that by reversing that (having it set vF to 1 if a borrow is not needed) more is right. I also have docs that say it should simply be set to the borrow, whatever that means.

For an example of what this is doing, run the pong program (included with my source.) Let it sit there, pressing no buttons. When it gets to about the 4th volley (the first time it hits your paddle) notice that it goes straight rather than at an angle like it should. This type of error occurs very few times, so I can't exactly pinpoint it.

The program is in debug mode, meaning that all of the registers are being displayed. You can see the value of I (pointer) the current instruection, and the delay timer under those. As a plus, you can pause and then use the step forward command (F2 I believe) to go one instruction at a time. If anyone can see my error, I will try to find a way to send them a cake. Seriously.

-thegamefreak

*EDIT* Another one this is throwing out of whack is the Invaders game, which is completely unplayable due to it. Apparently, either the timer is not working (Which I'm pretty sure it is) or it's not reading it right due to this bug. Grr...
I'm not mad, just a little crazy.

DarkNova - a little side project I run.
User avatar
benryves
Maxcoderz Staff
Posts: 3089
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Post by benryves »

I'll have a look at it over the weekend. I'm familiar with CHIP-8 already, which is always useful ;)

For starters, 8XY6 is shift right, not subtract. 8XY7 is subtract - did you mean that?

In any case, the flags register should be set if the carry (borrow) IS needed.
User avatar
thegamefreak0134
Extreme Poster
Posts: 455
Joined: Mon 23 Jan, 2006 10:09 pm
Location: In front of a Computer, coding
Contact:

Post by thegamefreak0134 »

Yes, I meant that. (Wasn't actually looking at it when I typed.)

If that's true, it means there's a serious error elsewhere. This is not good. Thanks for having a look at it for me. Yes, my source is quite long and probably a bit un-tidy, but it works (thus far) and I'm proud of that. Note that I'm using bigger-than-needed variable sizes so I can actually do the checks I need to do.

-thegamefreak

PS: Just saw this... What are "MaxCoins"?
I'm not mad, just a little crazy.

DarkNova - a little side project I run.
threefingeredguy
Calc King
Posts: 2195
Joined: Sun 27 Mar, 2005 4:06 am
Location: sleeping
Contact:

Post by threefingeredguy »

New mod (look in the announcements section).
Image
User avatar
benryves
Maxcoderz Staff
Posts: 3089
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Post by benryves »

http://benryves.com/bin/CHALLENGE8.zip

Not too heavily tested, seems to run most CHIP-8 games fine though.

You were for the most part 99% of the way there, but that 1% often completely screws everything up when it comes to emulation.

OK, I hope you don't take this the wrong way - people seem to take constructive criticism badly in these parts - but here are some points I thought I'd raise.

First up are .NET/VB programming issues in general that stuck out as "the wrong way to do things":

Use of Visual Basic 6 file operations works, yes, but I suggest you use the .NET classes for file operations instead. They are far cleaner (code-wise), and can be used in any other .NET language.

Here's a little example:

Code: Select all

Imports System.IO ' Stick this at the absolute top of your source file

' We need to create a new BinaryReader object.
' The BinaryReader works on a stream (for example: network stream, memory stream, file stream).
' That's why I also have to create a new FileStream as well of the file I want to read.

Dim BR As New BinaryReader(New FileStream("file.bin", FileMode.Open))

For i As Integer = 0 To BR.BaseStream.Length - 1
    BR.ReadByte() ' Read a byte
Next

BR.Close()

' There are lots of .NET classes that deal with streams, so being familiar
' with them is useful.
One problem with VB.NET is that it allows people to keep on writing VB6 code. ;)

Another place this is visible is in use of the "Hex" function.
You'll notice that any object has a method "ToString()" - you can override this when creating your own classes. If you want to convert a number to 2-character upper-case hexadecimal, for example, use Variable.ToString("X2"). Variable.ToString("X4") is, unsurprisingly, the 4-character equivalent.
MSDN has a lot of examples on common formatting strings as well as how do define your own.

Never use End. Use Me.Close() if you want it to close a form - End forces the application to suddenly terminate, which can lead to unwanted results.

For loops exist. Using counter variables and while loops is... weird.

Code: Select all

' Print the numbers 10->20
For Counter As Integer = 10 To 20
    Console.WriteLine(Counter)
Next Counter

' Print the numbers 5,4,3,2,1,0
For Counter As Integer = 5 To 0 Step -1
    Console.WriteLine(Counter)
Next Counter
This one isn't so important apart from a stylistic approach:

Code: Select all

x = x operator y
...can normally be collapsed to...
x operator= y

For example:

x = x + 1 -> x += 1
y = y - x -> y -= x
z = z >> 1 -> z >>= 1
I find it makes code a bit more readable. Plus, less typing is good. :D

Oh, and keep variables within the scope they are required in. A lot of your variables are defined in the class itself, not within the method that uses them.

On to more CHIP8 related comments :)

An easy issue to spot is the key handling. Think of it in terms of the keypad - you have 16 keys, each of which is either up or down.
You seem to be trying to represent this with a single value; it would be far better to represent this as an array of booleans.

First up, set the form's KeyPreview property to True. This means that any keypresses in any control on that form raises the form's keypress events as well (otherwise, if you had a TextBox selected, the form wouldn't see the key press info as the text box would swallow it for itself).

Now, we need to map key codes (from your PC) to key numbers (of the CHIP8 machine). I'll use a hash table for this purpose.
A hash table maps a key to a value. In .NET 1, you have a Hashtable object that can be used for this (and it maps an object key to an object value). In .NET 2, which introduces generics, you can have a strongly-bound hash table (called a Dictionary) that maps one object of a specific type to another object of a specific type.
As this is a .NET 1 project, I'll stick to the .NET Hashtable.

Code: Select all

' Add these to the top of your file.
' It saves having to type them in all the time :)

Imports System.Windows.Forms
Imports System.Collections

Dim KeyMap As New Hashtable ' This will be our key map

' Somewhere in our form's load event handler

' Default key mapping

KeyMap.Add(Keys.D0, &H0)
KeyMap.Add(Keys.D1, &H1)
KeyMap.Add(Keys.D2, &H2)
KeyMap.Add(Keys.D3, &H3)
KeyMap.Add(Keys.D4, &H4)
KeyMap.Add(Keys.D5, &H5)
KeyMap.Add(Keys.D6, &H6)
KeyMap.Add(Keys.D7, &H7)
KeyMap.Add(Keys.D8, &H8)
KeyMap.Add(Keys.D9, &H9)

KeyMap.Add(Keys.NumPad0, &H0)
KeyMap.Add(Keys.NumPad1, &H1)
KeyMap.Add(Keys.NumPad2, &H2)
KeyMap.Add(Keys.NumPad3, &H3)
KeyMap.Add(Keys.NumPad4, &H4)
KeyMap.Add(Keys.NumPad5, &H5)
KeyMap.Add(Keys.NumPad6, &H6)
KeyMap.Add(Keys.NumPad7, &H7)
KeyMap.Add(Keys.NumPad8, &H8)
KeyMap.Add(Keys.NumPad9, &H9)

KeyMap.Add(Keys.A, &HA)
KeyMap.Add(Keys.B, &HB)
KeyMap.Add(Keys.C, &HC)
KeyMap.Add(Keys.D, &HD)
KeyMap.Add(Keys.E, &HE)
KeyMap.Add(Keys.F, &HF)

' This maps a KEY to a VALUE.
' If I was to then do something like this:
' Dim O as Object = KeyMap(Keys.NumberPad4)
' O would be the integer 4.
' Note that if a key was NOT in the Hashtable, O would be Nothing.

' As you can see, multiple keys can correspond to the same value.
' The reverse is NOT true - each key is unique.

' This will be our array of key up/down flags:

Dim KeyGrid(16) As Boolean

' Here is a function we call when a key up/down message is posted.
' We pass the key code and a boolean to specify whether it was being
' pushed down or released.

Private Sub KeyStateChanged(ByVal keyChanged As Keys, ByVal isDown As Boolean)
    ' Get the actual key code
    Dim RealKeyCode As Object = KeyMap(keyChanged)

    ' Was it a recognised key?
    If RealKeyCode = Nothing Then Return

    ' So, we know what it is. Set the value...
    KeyGrid(RealKeyCode) = isDown
End Sub

' Here are the two event handlers for key handling.

Private Sub EmulatorKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles MyBase.KeyDown
    KeyStateChanged(e.KeyCode, True)
End Sub

Private Sub EmulatorKeyUp(ByVal sender As Object, ByVal e As KeyEventArgs) Handles MyBase.KeyUp
    KeyStateChanged(e.KeyCode, False)
End Sub
I hope you can follow that. This has a number of advantages over hard-coded comparisons - only one of everything, for starters! It also has the advantge over your old code of handling multiple keypresses correctly (holding 1 and A at the same time, for example).

Of course, this means some of the emulator code needs to be changed - instructions skpr k, skup k and key vr.

skpr k and skup k are easy enough:

Code: Select all

If (Instruction And &HF0FF) = &HE0A1 Then ' skup k EXA1
    If Not KeyGrid(Vary(Instruction1 And &HF)) Then
        ProgramCounter = ProgramCounter + 2
    End If
    Valid = True
End If

If (Instruction And &HF0FF) = &HE09E Then ' skpr k EX9E
    If KeyGrid(Vary(Instruction1 And &HF)) Then
        ProgramCounter = ProgramCounter + 2
    End If
    Valid = True
End If
Here's one for key vr:

Code: Select all

If (Instruction And &HF0FF) = &HF00A Then ' key vr FX0A
    Dim FoundAKey As Boolean = False ' Flag to show we've hit a key
    ' Check each key in turn
    For i As Integer = 0 To 15
        If KeyGrid(i) Then ' Is it pressed?
            Vary(Instruction1 And &HF) = i
            FoundAKey = True
            Exit For
        End If
    Next
    ' Did we find any keys?
    If Not FoundAKey Then
        ' No key was pressed!
        ProgramCounter -= 2 ' Jump backwards 2
    End If
    Valid = True
End If
Note the way I jump the program counter backwards two bytes to cause a loop until a key is pressed. This is a better approach than looping (in VB) until a key is pressed, as it lets the rest of your application do whatever it needs to (it doesn't block up the app).

Exceptions are really useful. In the olden days, functions returned result codes determining whether there was an error or not. This relies on programmers checking the return codes, and as we all know programmers are lazy sods (well, I am, at any rate :P).
What we now do is to throw exceptions. An exception happens when something goes wrong, and code stops there. What you need to do is to wrap a block of code that might throw an exception in a Try..Catch block. Here's an example:

Code: Select all

' Here's our function:

 Function Divide(ByVal a As Integer, ByVal b As Integer) As Integer
     If b = 0 Then
         Throw New Exception("You can't divide by zero, you burk!")
     Else
         Return a / b
    End If
End Function

Dim x As Integer = Divide(10, 0) ' Try and do something naughty
If you were to run this, your program would crash and the IDE would suddenly highlight the "Throw New Exception" line telling you that the exception was unhandled, along with our friendly (cough cough) message.

What you can do is make this more friendly by doing this:

Code: Select all

' Assume the function Divide still exists

Dim x As Integer

Try

    ' Try to do this
    x = Divide(10, 0) 

Catch ex As Exception

    ' If an exception is thrown, this block of code "catches" it in ex.
    ' We can then recover nicely.
    MessageBox.Show(Me, "Something went wrong:" & vbCrLf & ex.Message, "Whoops", MessageBoxButtons.OK, MessageBoxIcon.Error)

End Try
Where am I going with this, you might ask? Well, the thing here is that your big instruction Select..Case block is the ideal place for exceptions! Setting a flag is a bit of a pain, and clutters up your code - like their name suggests, exceptions are exceptional cases that should only be used if needed.

Basically, you could do something like this:

Code: Select all

Select Case (Instruction And &HF000) >> 12
    Case 0
        DoSomething()
    Case 1
        DoSomethingElse()
    Case 2
        DoSomethingAltogetherDifferent()
    Case Else
        ' Oh dear, if we get here it means we
        ' haven't been caught by any of the above blocks!
        Throw New Exception("Instruction " + Instruction.ToString("X4") + " not recognised!")
End Select
Top tip: Click Debug->Exceptions, select Common Language Runtime Exceptions, select "When the exception is thrown"->"Break into the debugger". This allows you to see where the exception was thrown from. (Normally you want it on 'continue', so your Try..Catch block catches it).

You wrote your own stack implementation - .NET provides you with a stack. :)

Code: Select all

Dim ProgramStack As New Stack

ProgramStack.Push(50)
ProgramStack.Push(1000)
Dim X As Integer = ProgramStack.Pop() ' X = 1000
Dim Y As Integer = ProgramStack.Pop() ' Y = 50
Next, the mysterious GoFlag. What does this mean? It's just a byte, with no real significance. Sometimes it is 0, sometimes 1, sometimes 2.
What you need is an enumeration! This allows you to attach meaningful names to values like the GoFlag. In this case:

Code: Select all

Enum RunningFlag
    Stopped
    Running
    Paused
End Enum

Dim GoFlag As RunningFlag
You can now set GoFlag to meaningful values, like RunningFlag.Paused.

Decouple timing of the sound/time timers and the "CPU" ticks.
For easiness, I just stuck a timer on the form with an interval of 17 (1000/60=16.7), which isn't too accurate but should be sufficient.

The main loop itself should really look like this:

Code: Select all

Dim LastFrame As DateTime = DateTime.Now ' LastFrame = time of the last frame
        While Me.CloseMe = False

            Dim ThisFrame As DateTime = DateTime.Now ' ThisFrame = time of this frame

            Dim TimePassed As TimeSpan = ThisFrame.Subtract(LastFrame) ' Time passed = how long has passed since last frame

            If GoFlag = RunningFlag.Running Then ' Are we running?
                Try ' Try to execute
                    For i As Integer = 1 To TimePassed.TotalSeconds * 800 ' 800 instructions per second
                        ProcessInstruction2()
                    Next i
                Catch ex As Exception
                    GoFlag = RunningFlag.Paused ' It buggered up, so pause
                    MessageBox.Show(Me, "Error: " + ex.Message, "Whoops", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End Try
            End If

            UpdateV() ' Update variables
            DrawScreen() ' Draw screen
            Application.DoEvents() ' Flush window messages

            LastFrame = ThisFrame ' Set the time of the last frame to the time of this frame
        End While
Basically, we have two times. One is the time of the LAST frame, one is the time of the CURRENT frame. We can work out how long has passed between the two frames by subtracting one from the other.
In this case, I opt for 800 instructions per second (ideally this would be a user-configurable variable).

Back to instructions - I recoded all of the 8??? ones again from scratch (they're horribly confusing).

shl vr would have loaded &H80 into the flags register, not &H01 (think about it).

IIf is a useful construct in VB. It allows you to assign one of two different values to something, depending on whether a condition evaluates to true or false. For example:

Code: Select all

Dim X As Integer = IIf(True, 123, 456) ' X = 123
Dim Y As Integer = IIf(False, 123, 456) ' Y = 456
I rewrote the sprite function to allow for sprite wrapping (note that some games, eg BLITZ, do not work with wrapping (it is BLITZ that has the bug, NOT the emulator).

Also, I load the file to an array, copying it to &H200. I don't bother adding/subtracting &H200 - it's not needed. All addresses are "proper".

You were missing instruction B??? completely.

I used the .NET random number generator for CXKK :)

I also retyped most of the FX?? instructions with shorter versions.

Most of the "problems" are a case of doing things the VB6 way rather than doing them the shiny new .NET way. I suggest using the .NET functions and classes rather than VB built-in functions. The problem with VB is that it was designed to be backwards compatible with VB6 (hence some of the ghastly legacy controls VB has), which means you can get away with doing things in the VB6 way.

Another VB problem is that it is NOT strict with types whatsover! For example,

Code: Select all

Dim Something As Integer = 1

If Something Then
' How is this valid code? :(
End If
Try and help people along and use the correct data types for things (Boolean, of course, in the above example).

Please ask if I've said anything confusing or if you need any further info. Again, don't take this the wrong way, I'm just pointing out things that should make life easier in future. :)
User avatar
benryves
Maxcoderz Staff
Posts: 3089
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Post by benryves »

OK, feeling charitable here - here's a better version of the sprite function:

Code: Select all

Vary(15) = 0 ' Clear the sprite collision flag

Dim SpriteX As Integer = Vary(RegX) ' Sprite X coord
Dim SpriteY As Integer = Vary(RegY) ' Sprite Y coord
Dim SpriteHeight As Integer = Instruction2 And &HF ' Height

For DrawY As Integer = 0 To SpriteHeight - 1 ' For each height...

    If DrawY + SpriteY < 32 Or SpriteWrappingY Then ' Is it off screen?

        Dim SpriteLine As Byte = FileByteCode(Index + DrawY) ' Line of sprite data

        For DrawX As Integer = SpriteX To SpriteX + 7 ' For each pixel in the line...

            If DrawX < 64 Or SpriteWrappingX Then ' Are we still on the screen?

                Dim SettingPixel As Boolean = (SpriteLine And &H80) <> 0 ' Is it set?
                SpriteLine <<= 1 ' Shift the pixel off the line

                If SettingPixel Then ' Flip pixels, if need be

                    Dim PixelPointer As Integer = (DrawX And 63) + ((DrawY + SpriteY) And 31) * 64

                    Dim ExistingPixel = ScreenBuffer(PixelPointer)
                    If ExistingPixel = 0 Then
                        ExistingPixel = &HFFFFFF
                    Else
                        ExistingPixel = &H0
                        Vary(15) = 1 ' Sprite collision!
                    End If

                    ScreenBuffer(PixelPointer) = ExistingPixel

                End If

            End If

        Next

    End If

Next
Add two Boolean variables - SpriteWrappingX and SpriteWrappingY. They should both be True, but this breaks certain buggy games (Blitz, VBrix). Setting both to False disables sprite wrapping (inaccurate emulation) but it allows those games to operate.

Note that VBrix also demonstrates another 'bug' - sometimes the ball is invisible. This appears to be because, as the ball is flickered on/off, the interpreter only updates the frame when the ball is flickered 'off'. A simple (but bad) workaround is to put a call to DrawScreen() at the end of the the sprite function, but this degrades performance.

Ideally, you should emulate a screen which averages itself over each frame - so a pixel that was half on, half off over the period of the frame would appear as 50% grey (think of how a calc emulator emulates the latency of the LCD).
User avatar
benryves
Maxcoderz Staff
Posts: 3089
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Post by benryves »

Sorry for the triple post, but here's something else that might be useful... properties.
Most of the instructions are of the form ?XY?, where X and Y point to CHIP-8 registers. It would be cool if we could create two 'virtual' registers, X and Y, that automatically pointed to the correct real registers based on the current instruction.
You can do this through properties - methods that operate like variables.
For example;

Code: Select all

Dim Instruction As UShort ' Current instruction

Dim Vary(15) As Byte ' Registers

Private Property RegisterX() As Byte
    Get
        Return Vary((Instruction And &H0F00) >> 8)
    End Get
    Set (ByVal Value As Byte)
        Vary((Instruction And &H0F00) >> 8) = Value
    End Set
End Property

Private Property RegisterY() As Byte
    Get
        Return Vary((Instruction And &H00F0) >> 4)
    End Get
    Set (ByVal Value As Byte)
        Vary((Instruction And &H00F0) >> 4) = Value
    End Set
End Property

' Now, for example, you can just do (in your fetch-execute method):
RegisterX += RegisterY
(NB: Code is from memory, the IDE will insert the correct code blocks for you when you declare the property).
User avatar
thegamefreak0134
Extreme Poster
Posts: 455
Joined: Mon 23 Jan, 2006 10:09 pm
Location: In front of a Computer, coding
Contact:

Post by thegamefreak0134 »

Three words. W. O. W.

I had no idea I was so... in-experienced. I basically migrated from vb6 to vb.net this year, so if there is an old way to do things I like it very much. Wrong I know, but alas I'm still rather new.

I will make sure to implement probably all of these nice features, as they seem so handy to use. (Oh, and BTW I think my version of the sprite routine did do sprite wrapping, I designed it that way. The abscence was the ability to turn it off...)

My main question is this. What did you do to fix the error? I knew perfectly well that I was missing instructions, I had been implementing them on the fly as the programs I ran required them. I appreciate the cleaner code and handy features, as they are all very nice things that my "business" books would never have taught me. However, that still leaves the problem of the math... I had a feeling that the problem was somewhere in the 8??? registers, so I can see you re-wrote them and that concerns me. Do you think you could tell me (besides the fact that they are confusing, I agree) what didn't work about them?

Thanks a million on this. I will go back and re-write my code based on the info you have given me. I stress again, this will make life in the GB emu world so much easier when I know this!

-thegamefreak
I'm not mad, just a little crazy.

DarkNova - a little side project I run.
User avatar
benryves
Maxcoderz Staff
Posts: 3089
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Post by benryves »

Not to worry - the disadvantage of VB.NET is that it doesn't really push you into using the .NET framework and it's classes - but once you feel comfortable with it, it really is the advantage of .NET programming.

As for where the bug lies - apart from the more obvious << setting the flag to &H80, not &H00, I couldn't really see anything blatently wrong - so I opted to just rewrite blocks of code until it worked. There was nothing blatently wrong.

It sounds like you should find a new VB reference, though ;) [MSDN is very good for finding out about the .NET classes and has a good VB language reference - it should have been installed alongside the IDE].
Post Reply