Page 4 of 5

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Thu Apr 09, 2015 8:04 pm
by lidnariq
Older machines like the 8086 and 80186 don't TRAP on invalid instructions, and will do something random instead. Usually NOP, but not always:
http://stackoverflow.com/questions/2811 ... el-8086-88

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Thu Apr 09, 2015 8:21 pm
by Joe
Espozo wrote:How do you do that though? I know this is probably basic stuff...
I, personally, would use section directives to force the assembler to output data in a certain way.

For example:

Code: Select all

section reset start=0x7fff0 vstart=0
    cli
    jmp ...
Espozo wrote:You mean like numbers that don't respond to anything?
Yes.
Espozo wrote:What are they even doing there?
Padding for the unused part of the ROM. The MAME debugger doesn't know what's code and what isn't, so it assumes everything is code.
Espozo wrote:How does the processor even handle something like that?
Like most x86-compatible CPUs, the V33 will invoke interrupt 6 when it encounters an invalid opcode. In this case, I doubt it ever tries to execute those invalid opcodes.

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Thu Apr 09, 2015 8:22 pm
by Joe
lidnariq wrote:Older machines like the 8086 and 80186 don't TRAP on invalid instructions,
The 80186 does.

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Thu Apr 09, 2015 9:06 pm
by Drew Sebastino
Well, I wrote this:

Code: Select all

CPU 80186

section reset start=0x7fff0 vstart=0
  cli
  jmp main_code

main_code:
  mov ax, 0xF800
  mov ds, ax
  mov byte [0x800], 0x1F

infinite_loop:
  jmp infinite_loop
and I assembled it, and ran it through the file splitter. I then made it into the rom and tried it, and then I changed the order of the files and tried it.

Unfortunatelly, none of them worked and this is what I got:
Result 1.png
Result 2.png

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Thu Apr 09, 2015 9:23 pm
by Joe
Espozo wrote:Well, I wrote this:
First, you need a separate section directive for the rest of your code. (Later, you'll probably need more section directives.) Second, you must use a far jump to reach the rest of your code, not a near jump. For example:

Code: Select all

section code vstart=0 align=16

main_code:
  mov ax, 0xF800
  mov ds, ax
  mov byte [0x800], 0x1F

infinite_loop:
  jmp infinite_loop

section reset start=0x7fff0 vstart=0

  cli
  jmp (section.code.start >> 4):main_code
Espozo wrote:Unfortunatelly, none of them worked and this is what I got:
It looks like one of the two files you're using is full of zeroes instead of containing the correct data.

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Fri Apr 10, 2015 2:14 pm
by Drew Sebastino
I actually tried you code, (with "CPU 80186" at the beginning) but the debugger just shows the instruction for the value 0 all the way through and obviously doesn't work. One strange thing I noticed is that each file is 256KB long, and it used to be 6 bytes with the code that showed something in the debugger. Is there something wrong with your code?

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Fri Apr 10, 2015 7:38 pm
by Drew Sebastino
You know, could someone actually just tell me what everything means here? I switched the order you had the code to where the bottom is now at the top like so:

Code: Select all

CPU 80186

section reset start=0x7fff0 vstart=0

  cli
  jmp (section.code.start >> 4):main_code

section code vstart=0 align=16

main_code:
  mov ax, 0xF800
  mov ds, ax
  mov byte [0x800], 0x1F

infinite_loop:
  jmp infinite_loop
and this is what I get in the debugger, which is at least better than having nothing like it used to:
Debugger.png
I'm guessing that it jumps to an area with no code in it instead of the correct area, because if I let it run, it fills up with "add [bw+ix],al".

This has proven to be far more complicated than I had originally anticipated...

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Sat Apr 11, 2015 1:09 am
by koitsu
Edit: Never mind. I just found the key piece of information: viewtopic.php?p=144842#p144842
MAME is using mnemonics from NEC instead of Intel, which is why they don't look like x86 instructions.
I Googled around and found this (PDF) for the NEC V30NX. I have to assume this CPU is extremely close to the V30/V33 used on this board, but I could easily be wrong.

Upon reviewing Chapter 3, I was greeted with official definitions for all registers (BW = general-purpose register, IX = index register), and Chapter 3.6 clearly denotes that there are BR and DI opcodes on the V30 CPU. The Appendix List has a chart of all the opcodes. BR = Branch, DI = I have not the slightest idea ("CPU control" is too vague). There doesn't appear to be a "clear" definition of what all the opcodes do.

Skimming more of the instruction set of that PDF shows me that there are many opcodes and registers that are "similar" to 8086/8088 but the official explanations of them all is missing (there's the Appendix but it really doesn't describe their functionality).

So I'm left thinking this:

DI = NEC V30 opcode for what Intel calls CLI
BR = NEC V30 opcode for what Intel calls JMP
IX = NEC V30 register for some particular Intel index register (there are multiples and I don't know which one)
BW = NEC V30 register for possibly Intel's BX register? Again not sure.

You basically get to learn the mapping between Intel and NEC V30 when looking at MAME's debugger -- unless someone know of a way to force the debugger to display Intel mnemonics.

Good luck with this -- this would drive me absolutely batshit crazy. "The code I write is technically working as intended, but what I see in real-time in the debugger isn't the syntax I use in the code itself".

Thus, you probably just need to execute the code/step through it. The MAME debugger documentation is abysmal, so I wish you luck there too. :-)

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Sat Apr 11, 2015 3:27 am
by Joe
Espozo wrote:One strange thing I noticed is that each file is 256KB long, and it used to be 6 bytes with the code that showed something in the debugger. Is there something wrong with your code?
No, that's intentional. The assembler can automatically put code in the right place in the ROM file, so that's what I did. You should be able to just split the output file from my example and load it with MAME. (Now that I look, I forgot to pad it to the correct size. It probably should have been exactly 512kiB total/256kiB each, but it's a few bytes short.)
Espozo wrote:You know, could someone actually just tell me what everything means here?
I'll do my best.
Espozo wrote:I switched the order you had the code to where the bottom is now at the top like so:
That probably broke it.

Anyway, time to explain things.

Code: Select all

section code vstart=0 align=16
The first section is named "code". Its origin (vstart) is 0. It is aligned to a 16-byte boundary, since x86 segments are 16-byte aligned. Its physical location in the output is unspecified, so YASM puts it after the previous section. Since I put this section first, it ends up at the beginning of the ROM.

Code: Select all

main_code:
---snip---
  jmp infinite_loop
This is the code you want to run immediately after reset. You could replace this with whatever you want, as long as you update the reset vector to point to the correct label.

Code: Select all

section reset start=0x7fff0 vstart=0
The second section is named "reset". It starts at 0x7FFF0 in the ROM file. Its origin is 0.

Code: Select all

  cli
  jmp (section.code.start >> 4):main_code
This disables most interrupts, then performs a far jump to the start of your code. The purpose of "section.code.start >> 4" is to calculate the segment that should be used to access the "code" section. Since the code section is at the beginning of the ROM right now, I could have just written jmp 0:main_code. At some point you'll be moving the code section to make room for the IVT, so I made sure it will automatically correct itself when that happens.
Espozo wrote:I'm guessing that it jumps to an area with no code in it instead of the correct area, because if I let it run, it fills up with "add [bw+ix],al".
You should probably step through it instead of letting it run. At least that way, you'll be able to see what it's doing right before it jumps off to nowhere. (Plus, if anything triggers a NMI, it'll jump off to nowhere anyway since you don't have a NMI handler yet.)
Espozo wrote:This has proven to be far more complicated than I had originally anticipated...
Don't worry. Once you've got the basic setup code functioning, it'll get easier.

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Sat Apr 11, 2015 6:27 am
by Drew Sebastino
koitsu wrote:Thus, you probably just need to execute the code/step through it. The MAME debugger documentation is abysmal, so I wish you luck there too. :-)
Well, I stepped through it, and this is where "br" brought me. The whole thing looks like that:
Wow..png
Joe wrote:That probably broke it.
That's the only way anything even showed up though. (The picture above was where the older code led.)

You know, when people talk about a reset vector, are they talking about the same thing on the SNES? Like the arcade board has a reset button on it? I originally thought the reset button just turned the system on and off, but games that normally can't save will often save high score and options in the options menu and whatnot.

By the way, I just posted something on some Mame forum about if there is a way to force it to display x86 instructions, so hopefully, I'll hear from them soon.

It still is kind of weird though, Joe, that what you wrote doesn't even display anything. I don't see anything wrong if you're describing it correctly. Is it because it needs to be exactly the right size or something like you kind of said?

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Sat Apr 11, 2015 7:28 am
by Joe
Espozo wrote:You know, when people talk about a reset vector, are they talking about the same thing on the SNES?
All CPUs have a reset vector. When a CPU is powered on or reset, it begins executing the reset vector.
Espozo wrote:I originally thought the reset button just turned the system on and off, but games that normally can't save will often save high score and options in the options menu and whatnot.
As long as RAM contents are maintained, the CPU can't tell the difference between power-on and reset. For example, some NES games without nonvolatile RAM can also keep high scores across a reset.
Espozo wrote:It still is kind of weird though, Joe, that what you wrote doesn't even display anything. I don't see anything wrong if you're describing it correctly. Is it because it needs to be exactly the right size or something like you kind of said?
It sounds like I'll need to check this out in the MAME debugger.

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Sat Apr 11, 2015 8:22 am
by Drew Sebastino
Well Joe, here you go. Best of luck! :)

(Mod edit: ROM removal)

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Sat Apr 11, 2015 9:11 am
by Joe
It's probably a bad idea to post copyrighted ROMs.

So! As it turns out, you are correct: MAME doesn't load your replacement ROMs if they are too small.

With that in mind, I have made a slight correction:

Code: Select all

cpu 80186

section code vstart=0 align=16

main_code:
  mov ax, 0xF800
  mov ds, ax
  mov byte [0x800], 0x1F

infinite_loop:
  jmp infinite_loop

section reset start=0x7fff0 vstart=0

  cli
  jmp (section.code.start >> 4):main_code
  
  times 16-($-$$) db 0
After padding the reset section to 16 bytes long, the entire file is exactly 512kiB, which is enough to make MAME load it as expected.
gunforc2.png

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Sat Apr 11, 2015 9:38 am
by Drew Sebastino
Joe wrote:It's probably a bad idea to post copyrighted ROMs.
Oops... :oops: (That's what banned me from the last website I was on...)
I'll get rid of it pronto! (Although I doubt Irem is going to come back from the grave to get me. :roll:
Joe wrote:
gunforc2.png
Is that is what I think it is? :shock:

Re: Is Anyone Willing to Explain This C++ Code?

Posted: Sat Apr 11, 2015 12:19 pm
by koitsu
Oh and one thing, Espozo: cli might confuse you because its behaviour is opposite on x86 compared to 65xxx:

65xxx = sei = disables interrupts (i of P becomes 1), cli = enables interrupts (i of P becomes 0)
x86 = sti = enables interrupts (i of FLAGS becomes 1), cli = disables interrupts (i of FLAGS becomes 0)

65xxx = P = 8-bit CPU flag/status register
x86 = FLAGS = 16-bit CPU flag/status register (later extended to 32-bits (EFLAGS) and 64-bits (RFLAGS))

And as Joe more or less said -- yes, every CPU has some form of vectors (reset, interrupt, etc.). You can stop reading here if you don't wanna get hung up on interrupt vectors and the like (i.e. I recommend you stop reading here, haha ;-) ).

x86's interrupt vectors are crazy -- way more advanced than 65xxx because there's so many different hardware interrupts: there's the capability for 256 independent interrupt vectors on the x86 through the IDT (which is just a dedicated 1024-byte area of memory in RAM (0x0000 to 0x03FF) entirely for 32-bit vectors address). The table there on Wikipedia should give you some idea of the kinds of (hardware) interrupts the x86 handles. Bytes 0-3 = division by zero handler, bytes 4-7 = debugger handler, bytes 8-11 = NMI handler, etc..

NMI will probably be the one that looks familiar to you from the SNES ("hey I know that / have heard of that one!") -- except with one huge difference: NMI on PC isn't tied to VBlank (often called Vsync on PC). Note that I said PC and not x86! -- it's completely possible at the hardware level to tie VBlank to NMI on the x86 (maybe arcade boards do this? Not sure!), but on PC architecture this is not how they did it. Instead, on PC architecture (during MS-DOS days, etc. -- things are different now with video cards today), waiting for Vsync on PC required that you sit in a loop waiting for bit 3 of I/O register 0x3DA (one of the VGA status registers) to become 0. See the bottom of this page (C code ahead, but it's easy to understand, kinda. They have two loops there: the first spins the CPU waiting for bit 3 to become 1 (waiting for VBlank to finish in case you're already in VBlank), the second spins the CPU waiting for bit 3 to become 0 (waiting for VBlank to start so you can begin doing your graphics updates/palette changes)).

P.S. -- Feeling big nostalgia for PC after writing this. It's all stuff that isn't used like how it was back in the early 90s -- now with more advanced OSes, 32-bit/64-bit stuff, crazy hardware, etc. lots of stuff is much more complex. But the IDT is still there today, workin' as designed.