I had figured that I should make a simple demo of some kind because everyone else seems to be doing it and because I'm in a tough spot trying to make my game. I was going to do it on the SNES, but I don't feel like it, as I already have well beyond what I'm trying to do made. Anyway, this code is trying to make a ball bounce around like in 45 degree angles like pong, which I eventually want to turn this into. Nothing has been defined, and I don't have anything hooked up to any hardware registers, and I think you have a good understanding of what hardware I want this to run on... Vram is the biggest pain in the a** to me, and I thought I'd do it on something that doesn't use vram, so I don't have to upload any tiles or anything.
Here it is: (It isn't hooked up to a starting vector or anything, but that's easy to do. Also, this is meant for the 80186 if that is useful.)
Code:
start:
mov XDirection, 0x0001
mov YDirection, 0x0001
mov XPosition, 0x0000
mov YPosition, 0x0000
x_movement__start:
mov DX, #BallXVelocity
x_movement_loop:
jz y_movement_loop
add XPosition, XDirection
mov AX, #(ScreenWidth+BallDiameter) ;you write it like that, right?
cmp XPosition, AX
ja x_out_of_bounds
dec DX
jmp x_movement_loop
x_out_of_bounds:
xor XDirection, 0xFFFF
add XPosition, XDirection
dec DX
jmp x_movement_loop
y_movement__start:
mov DX, #BallYVelocity
y_movement_loop:
jz next_frame
add YPosition, YDirection
mov AX, #(ScreenHieght+BallDiameter) ;you write it like that, right?
cmp YPosition, AX
ja y_out_of_bounds
dec DX
jmp y_movement_loop
y_out_of_bounds:
xor YDirection, 0xFFFF
add YPosition, YDirection
dec DX
jmp y_movement_loop
next_frame:
int ;is this like wai?
jmp x_movement__start
Espozo wrote:
Code:
mov DX, #BallXVelocity
[...]
x_movement_loop:
jz y_movement_loop
[...]
dec DX
jmp x_movement_loop
Consider using
LOOP; it operates on CX instead of DX, and it's faster (mostly because it's smaller) on the early x86 machines. (It's equivalent to
dec cx / jnz somewhere. On 486 though pentium 3 machines it's slower than its equivalent, and I have absolutely no idea about anything made in the past decade. )
I'm not certain why you'd need to use # to specify some numeric constants and not other ones ... that seems weird to me.
Have you been testing it against an assembler? Not to see if it works, just to avoid syntax errors...
Quote:
xor XDirection, 0xFFFF
Consider using
NOTAlso, most of the time I've seen a more RISC-y data flow: load values into registers, do tests and bounds and changes, write back to registers, and not so much
opc mem, immForever ago (BBS era) I found this document:
http://www.intel-assembler.it/portale/5 ... -guide.asp describing all the instructions up through the 486, which was invaluable when I was doing more x86 asm.
lidnariq wrote:
Consider using LOOP
lidnariq wrote:
Consider using NOT
Grr... Too many instructions!
(The complete opposite of MIPS...)
lidnariq wrote:
Also, most of the time I've seen a more RISC-y data flow
I don't want to try anything too RISC-y yet because I'm not very good at x86 assembly right now.
Is this any better?
Code:
start:
mov XDirection, 0x0001
mov YDirection, 0x0001
mov XPosition, 0x0000
mov YPosition, 0x0000
x_movement__start:
mov CX, #BallXVelocity
x_movement_loop:
add XPosition, XDirection
mov AX, #(ScreenWidth+BallDiameter) ;you write it like that, right?
cmp XPosition, AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not XDirection
add XPosition, XDirection
loop x_movement_loop
y_movement__start:
mov CX, #BallYVelocity
y_movement_loop:
add YPosition, YDirection
mov AX, #(ScreenHieght+BallDiameter) ;you write it like that, right?
cmp YPosition, AX
ja y_out_of_bounds
loop x_movement_loop
jmp next_frame
y_out_of_bounds:
not YDirection
add YPosition, YDirection
loop x_movement_loop
next_frame:
int ;is this like wai?
jmp x_movement__start
Anyway though, did you hear of the instruction GAME yet? You just write GAME and it does everything for you.
Espozo wrote:
Anyway though, did you hear of the instruction GAME yet? You just write GAME and it does everything for you.
The 65C02 derivative in the TurboGrafx-16 is so cool it has SMB3 in one instruction. But then the 68000 has an instruction for making LINK, and the MVP instruction in the Super NES's 65816 might be good for
baseball. Like Bad Piggies? ARM has SWINE. Perhaps the "adult" games for Atari 2600 sucked because you need the 6809 in a CoCo to have SEX. And with a 68000, you can TAS anything.
I've got dozens of 'em.
Espozo wrote:
Is this any better?
Looks reasonable, if non-idiomatic. I'd probably move things around a little, more like:
Code:
x_movement_loop:
add XPosition, XDirection
mov AX, #(ScreenWidth+BallDiameter) ;you write it like that, right?
cmp XPosition, AX
ja x_out_of_bounds
loop x_movement_loop
; moving the error case out of the way allows us to fall through here rather than explicitly jumping
y_movement__start:
[...]
next_frame:
int ;is this like wai?
jmp x_movement__start
x_out_of_bounds:
not XDirection
add XPosition, XDirection
loop x_movement_loop
Somehow I didn't notice the question about INT..
INT is approximately equivalent to BRK. (It's more useful, though, because it actually does something with its argument.)
HLT is closest to WAI, but ... for some reason I never saw it used.
Quote:
DAS: Decimal Adjust AL after Subtraction
Attachment:
DAS.png [ 43.91 KiB | Viewed 2311 times ]
Dun dun dunnn!
tepples wrote:
SEX
(I'm really mature as you can tell.)
I'm surprised you didn't say anything about this:
Quote:
I don't want to try anything too RISC-y yet because I'm not very good at x86 assembly right now.
Get it?
This is a dumb question, but the DMA controller isn't actually on the 65816, is it? I just wonder if the 80186 has any sort of thing, but I don't think the DMA controller is actually tied to the processor, considering you have to write to registers to get it working, like the multiplication/division units. Is the NEC V33 like a casing like the 5A22? Is there any alternative to DMA?
It'd be best to look at the datasheet.... not that I can find one for the bare V33... Here's the V33A:
http://www.datasheetarchive.com/U10032E-datasheet.htmlIn the original PC, the 8086 did not provide DMA, so they added an external 8237 IC to handle that.
Wikipedia claims that the V33 has some very nice features, but no DMA controller. It looks like NEC didn't integrate the DMA controller until the V53.
This doesn't mean that the arcade board doesn't have one—but if it does it would be an external component instead.
lidnariq wrote:
This doesn't mean that the arcade board doesn't have one—but if it does it would be an external component instead.
It doesn't appear to have one, unfortunately, but luckily, there's no vram to worry about, so all you'd need to worry about is the sprite table and cgram. Does the 80186 support 32 bit loads and stores like the 68000? That would help. Also, do all systems need to be in vblank to update oam and cgram, or does it vary?
No, the 8088 through the 80286 are genuine 16 bit processors, so no 32-bit moves. It looks like the Vxx series were always 16-bit.
Anyway, you do have access to the REP MOVSW and REP STOSW instructions, which might help.
lidnariq wrote:
Anyway, you do have access to the REP MOVSW and REP STOSW instructions, which might help.
Is that like for mass transfers of data? I read something and I think it said it will copy from the address stored in DS all the way to ES but I don't understand how it would be able to hold something that big. I guess oam and cgram updates can be done whenever if nothing about turning the screen on or off was ever done. Is there even really a vblank on the Irem M92, or is everything done while the screen is rendering? I imagine that would simplify things.
MOVSW means "copy the word from DS:SI to ES:DI, then add two to SI and DI". STOSW means "write the word in AX to ES:DI, then add two to DI"
REP MOVSW or REP STOSW means "do the same, but then decrement CX, and if CX isn't 0, do it again"
There's also the MOVSB and STOSB variants, which work on bytes instead. There's also also OUTSW, which writes to the alternate I/O bus instead. (from DS:SI to [DX])
So it's like move a word from an address that is being indexed, and than increment the index?
Sounds a lot like LDI / LDIR on the Z80.
Espozo wrote:
So it's like move a word from an address that is being indexed, and than increment the index?
The important bit is the REP prefix, which lets you just tell the CPU "copy all this data from here to there". It's similar to other machines' DMA, but there's no trigger, just "move all this data in this manner now".
And, yes, exactly what Dwedit said. Which makes sense when you consider the x86's origins with the 8080 (even though LDI and LDIR are Z80 additions)
Similiarly, x86 REP INSB ↔ Z80 INIR/INDR; x86 REP OUTSB ↔ Z80 OTIR/OTDR; x86 REP CMPSB ↔ Z80 CPIR/CPDR. The major difference is that the x86 specifies "increment" or "decrement" based on the D(irection) flag in the flags register, conveniently toggled by using the CLD/STD instructions.
Confusingly, x86 has a LODSB instruction which differs from the Z80 LDI instruction—LODSB just loads from DS:SI to AL, which is a little confusing to use with the REP prefix.
In yasm, how do you define stuff like this, to where addresses are automatically calculated?
Code:
SpriteCount: .res 2
MetaspriteCount: .res 2
Can you also still have it to where it isn't automatically calculated, like If you wanted to have a "Sprite Table" be at a certain spot that corresponds with sprite ram?
Here, check out the manual. In particular, look at the sections on
resb and
equ.
So nothing is really different? If I where to change how I was supposed to write that, it would be like this?
Code:
SpriteCount: .resb 2
MetaspriteCount: .resb 2
I don't get what they where trying to say about the equal thing. I just wanted to have it to where I wanted to call certain hardware registers by names, so I could say "SpriteRam" instead of "$XXXX". I guess I could just say "SpriteRam = $XXXX", but is there a way to make it to where the resb thing won't even overwrite a hardware register?
Espozo wrote:
I just wanted to have it to where I wanted to call certain hardware registers by names, so I could say "SpriteRam" instead of "$XXXX".
SpriteRam equ 0xXXXXEspozo wrote:
is there a way to make it to where the resb thing won't even overwrite a hardware register?
You can divide things up using different sections, and set them up so that the assembler will produce an error if your .bss section ever grows too large.
Joe wrote:
SpriteRam equ 0xXXXX
Wait, I'm stupid, I could have just written "SpriteRam = XXXX" couldn't I?
This is kind of random, but do you know an easy way to make a zero filled file that's 256KB long? I want this for the chrrom graphics.
I still don't understand what "REP MOVSB" does. It copies the values of the addresses from DS to ES, and then how do you say where it is supposed to store it at? Is rep's operand the address?
Espozo wrote:
I still don't understand what "REP MOVSB" does. It copies the values of the addresses from DS to ES, and then how do you say where it is supposed to store it at? Is rep's operand the address?
I think it means read from DS:SI, write to ES:DI, increment both, until CX runs out. It's like MVN on another processor family, except it doesn't need the bank numbers inline because ES exists.
Espozo wrote:
I still don't understand what "REP MOVSB" does. It copies the values of the addresses from DS to ES, and then how do you say where it is supposed to store it at? Is rep's operand the address?
REP is a prefix. It modifies the behavior of the following string move instruction: it will be executed CX times, or 0 times if CX is 0. It is not valid for string scan or string compare instructions; those use
REPE/REPZ/REPNE/REPNZ instead. It is not valid for non-string instructions.
MOVSB and
MOVSW are string move instructions. They load the byte or word at DS:SI and store it at ES:DI, then increment or decrement SI and DI based on the direction flag and operand size. They're most useful with a
REP prefix, but occasionally you might find optimizations that involve using
MOVSB or
MOVSW without a
REP prefix. Segment override prefixes affect the source (CS:SI, ES:SI, or SS:SI instead of DS:SI) but do not change the destination (always ES:DI).
REP MOVSB copies a block of data, one byte at a time. All of its operands are implicit. CX determines how many bytes will be copied. DS:SI is the address of the first byte to be read, and ES:DI is the address of the first byte to be written. The direction flag determines if SI and DI count up or down during the copy operation.
It's basically
memmove() in assembly.
So you're telling me REP modifies the CX register, and CX is how many bytes it will copy? You shouldn't mess with SI or DI, should you, because they are being moved by the cpu? Or wait, does REP clear out SI and DI and you just have to manually MOV a number into register CX? If I just wanted to copy 32 words, or 16 bits from 0x0000 to 0x1000, how would you write that?
Espozo wrote:
So you're telling me REP modifies the CX register, and CX is how many bytes it will copy?
Yes.
Quote:
You shouldn't mess with SI or DI, should you, because they are being moved by the cpu?
The CPU is halted during the copy. Are you familiar with how the MVN opcode on the 65816 works?
Espozo wrote:
So you're telling me REP modifies the CX register, and CX is how many bytes it will copy?
That is correct. Set CX to the number of bytes you want to copy, and
REP MOVSB will copy that many bytes. At the end of the copy operation, CX will be 0.
Espozo wrote:
You shouldn't mess with SI or DI, should you, because they are being moved by the cpu?
It's not a DMA operation;
REP MOVSB is a single-instruction loop. The only way to mess with SI and DI while the CPU is using them is with a hardware interrupt that occurs during that loop, but you probably want hardware interrupts that return all registers unmodified.
Espozo wrote:
Or wait, does REP clear out SI and DI and you just have to manually MOV a number into register CX?
REP only affects CX.
Espozo wrote:
If I just wanted to copy 32 words, or 16 bits from 0x0000 to 0x1000, how would you write that?
I would probably end up with something like this:
Code:
mov cx, 32
xor si, si
mov di, 0x1000
mov es, si
cld
rep es movsw
Do note that combining
REP with another prefix will behave incorrectly on some revisions of 8088 and 8086 CPUs, so you must avoid doing that in any code that might run on one of those.
In real code, you'll probably keep the direction flag cleared by default, so you won't need
CLD.
Joe wrote:
Espozo wrote:
Or wait, does REP clear out SI and DI and you just have to manually MOV a number into register CX?
REP only affects CX.
but MOVSx affects SI and DI, so when both are used together...
More precisely, if the direction flag is clear, REP MOVSB will SI ← SI+CX, DI ← DI+CX, CX←0; REP MOVSW will SI ← SI+2*CX, DI ← DI+2*CX, CX←0.
lidnariq wrote:
More precisely, if the direction flag is clear, REP MOVSB will SI ← SI+CX, DI ← DI+CX, CX←0; REP MOVSW will SI ← SI+2*CX, DI ← DI+2*CX, CX←0.
Is MOVSW any quicker than MOVSB?
Any way, tell me if I'm getting this:
mov cx, 32: Just saying the transfer size.
xor si, si: Should make SI zero, which is the address we are loading from. Why not just move 0 into SI?
mov di, 0x1000: The address in which we are storing the data.
mov es, si
cld
rep es movsw: What? What do we need the rep for if we already filled out cx with the mov? Could you please explain this part?
From what I understand, I would have thought this would have been fine:
Code:
mov cx, 32
mov si, 0x0000
mov di, 0x1000
cld
rep movsw
tepples wrote:
The CPU is halted during the copy. Are you familiar with how the MVN opcode on the 65816 works?
Nope. I've never head of it before.
Anyway, this is what I have so far. Did I set up the sprite correctly in the main_code section Joe?
Code:
cpu 80186
section code vstart=0 align=16
;========================================================================
;Define Stuff:
XDirection: .resb 2
YDirection: .resb 2
XPosition: .resb 2
YPosition: .resb 2
SpriteRam equ 0xF800
SpriteRamControl equ 0xF900
ScreenWidth = 320
ScreenHieght = 240
BallDiameter = 8
BallVelocity = 2
;========================================================================
main_code:
mov XDirection, 0x0001
mov YDirection, 0x0001
mov XPosition, 0x0000
mov YPosition, 0x0000
mov DX, #SpriteRam
mov DS, DX
mov [0x002], 0001h
mov DX, #SpriteRamControl
mov DS, DX
mov [0x000], 0001h
mov [0x004], 0008h
mov [0x008], 0001h
x_movement__start:
mov CX, #BallXVelocity
x_movement_loop:
add XPosition, XDirection
mov AX, #(ScreenWidth+BallDiameter) ;you write it like that, right?
cmp XPosition, AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not XDirection
add XPosition, XDirection
loop x_movement_loop
y_movement__start:
mov CX, #BallYVelocity
y_movement_loop:
add YPosition, YDirection
mov AX, #(ScreenHieght+BallDiameter) ;you write it like that, right?
cmp YPosition, AX
ja y_out_of_bounds
loop x_movement_loop
jmp next_frame
y_out_of_bounds:
not YDirection
add YPosition, YDirection
loop x_movement_loop
next_frame:
mov AX, XPosition
and AX, 0000000000011111b
mov BX, YPosition
and BX, 0000000000011111b
mov DX, #SpriteRam
mov DS, DX
and [0x006], 1111111111100000b
ora [0x006], AX
and [0x000], 1111111111100000b
ora [0x000], BX
hlt
jmp x_movement__start
;========================================================================
section reset start=0x7fff0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
MOVSW should be twice as fast as MOVSB. The V33 datasheet says 6cy/count for movsb; 6cy/count for movsw IFF both SI and DI were even, +2 for each of SI and DI that are odd (so 6/8/10cy)
Espozo wrote:
xor si, si: Should make SI zero, which is the address we are loading from. Why not just move 0 into SI?
Using XOR reg,self is smaller and therefore faster on oldest x86 machines. BUT they fixed that oversight in the V33, apparently. (Both XOR reg,reg and MOV reg,imm are 2 clocks according to the datasheet)
Quote:
mov es, si
I don't know the arcade machine's memory layout, but reading memory from 0:0 strikes me as exceptionally unlikely. If nothing else, the INT instruction uses the first 1KiB for an array of 256 pointers...
Quote:
rep es movsw: What? What do we need the rep for if we already filled out cx with the mov? Could you please explain this part?
The "rep movs_" instruction is the one that actually does the bulk move. Everything else is just configuring that. Without the rep, it doesn't
repeat, and only one unit (byte or word, depending) is transfered.
Quote:
From what I understand, I would have thought this would have been fine:
[...]
You're only specifying 16 bits of each of the two 20-bit pointers here; that COULD be ok if DS and ES were already configured, but without already knowing that...
lidnariq wrote:
The "rep movs_" instruction is the one that actually does the bulk move. Everything else is just configuring that. Without the rep, it doesn't repeat, and only one unit (byte or word, depending) is transfered.
What's up with the ES though?
lidnariq wrote:
You're only specifying 16 bits of each of the two 20-bit pointers here; that COULD be ok if DS and ES were already configured, but without already knowing that...
Oh, so that's what it means by DS:[DI]. In my case, they could have actually been zero filled. I swear though, 20 bit addressing is the devil.
lidnariq wrote:
I don't know the arcade machine's memory layout, but reading memory from 0:0 strikes me as exceptionally unlikely. If nothing else, the INT instruction uses the first 1KiB for an array of 256 pointers...
It was just something random I made up. CGram actually starts at 0xF8800, and I just wanted to see how to upload a whole palette. I'm just so glad there isn't any vram. I just thought of something: If you have a table somewhere and you wanted to use the 20 bit address, how would you go about this? It couldn't be one thing because of how 20 bit addressing works. Hell, sorry, but could someone actually just make a quick thing that transfers 16 words from "Palette" to 0xF8800? In x86 assembly, registers have the "0x" in front of it, and for immediates, it's either h for hexadecimal or b for binary after it?
Espozo wrote:
lidnariq wrote:
The "rep movs_" instruction is the one that actually does the bulk move. Everything else is just configuring that. Without the rep, it doesn't repeat, and only one unit (byte or word, depending) is transfered.
What's up with the ES though?
Just a guess, but I think the ES prefix sets the source bank to DS instead of ES.
Quote:
CGram actually starts at 0xF8800 [...] If you have a table somewhere and you wanted to use the 20 bit address, how would you go about this?
Load 0xF880 into ES, and then CGRAM starts at address 0x0000 within ES. The rule is final address = bank register value * 0x10 + address. So if you divide the starting address of an array by 0x10 (or, equivalently, shift it right by 4 or drop the least significant nibble), you have the value to load into a bank register to access that array starting at 0x0000.
I don't remember much x86 after a decade and a half, but try this and see what it does in the debugger:
Code:
mov DX, (src_ptr >> 4)
mov DS, DX
mov SI, (src_ptr & 0x0F)
mov DX, (CGRAM >> 4)
mov ES, DX
mov DI, (CGRAM & 0x0F)
mov CX, sizeof_CGRAM / 2
cld
rep movsw
Espozo wrote:
What's up with the ES [in "rep es movsw"] though?
Normally, movsw moves a 16-bit word from DS:[SI] to ES:[DI]. You can instead specify that it should move
from a different location, by prefixing the instruction with one of "cs", "es", or "ss". These "segment overrides" can be used with almost any instruction, to change the default segment (almost always DS) to one of the other three.
For the other string instructions that only have one pointer (almost all the rest), the segment override affects the only pointer. (So with STOS it would allow you to override ES). However, MOVS can't take two overrides: it always moves to ES:[DI].
For
non-string instructions, the segment override is usually specified next to the register, e.g.
mov es:[bx], ax, but
mov [bx]:ax implicitly uses ds.
Quote:
If you have a table somewhere and you wanted to use the 20 bit address, how would you go about this? It couldn't be one thing because of how 20 bit addressing works. Hell, sorry, but could someone actually just make a quick thing that transfers 16 words from "Palette" to 0xF8800?
I'd probably do something like:
Code:
push ds
lds si, Palette
mov ax, 0xF880
mov es, ax
mov cx, 16
xor di, di
rep movsw
pop ds
Quote:
In x86 assembly, registers have the "0x" in front of it, and for immediates, it's either h for hexadecimal or b for binary after it?
That somewhat depends on the assembler... Most of them seem to accept both 0xHEX and 0HEXh syntax.
Espozo wrote:
Is MOVSW any quicker than MOVSB?
Usually. It depends on the CPU.
Espozo wrote:
Why not just move 0 into SI?
Using
XOR or
SUB produces code that is smaller and, on many CPUs, faster. In this case, it's a matter of personal preference: the differences in size and speed are usually not impressive, and I didn't need to worry about the flags. (Both of those instructions affect flags;
MOV does not.)
Espozo wrote:
What do we need the rep for if we already filled out cx with the mov? Could you please explain this part?
MOVSW by itself only moves one word. The
REP prefix causes
MOVSW to execute multiple times, based on the value in CX. If you don't set CX, you don't know how many times the
REP prefix will cause
MOVSW to be executed.
Espozo wrote:
From what I understand, I would have thought this would have been fine:
You didn't specify that DS and ES were already the correct values, so I assumed they were unknown.
Espozo wrote:
Did I set up the sprite correctly in the main_code section Joe?
I'm not sure why you're asking me...
Espozo wrote:
What's up with the ES though?
The
ES prefix causes the source address to be ES:SI instead of DS:SI, which means I can skip setting DS.
Espozo wrote:
I swear though, 20 bit addressing is the devil.
Yep.
Espozo wrote:
If you have a table somewhere and you wanted to use the 20 bit address, how would you go about this?
If you put a label named "table" inside a section named "data", you'll load
section.data.start >> 4 into a segment register and use the label as an offset relative to that segment register. It might look like this:
Code:
mov si, (section.data.start >> 4)
mov ds, si
mov si, table
rep movsw
Espozo wrote:
Hell, sorry, but could someone actually just make a quick thing that transfers 16 words from "Palette" to 0xF8800?
Code:
section code vstart=0 align=16
upload_palette:
mov si, (section.data.start >> 4)
mov ds, si
mov si, 0xF880
mov es, si
mov si, .palette
xor di, di
mov cx, 16
cld
rep movsw
;ret or whatever
section data vstart=0 align=16
.palette:
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
section code
; more code
section data
; more data
You'll probably want to change it a bit (perhaps save modified registers to the stack?), but the basic idea is there.
Espozo wrote:
In x86 assembly, registers have the "0x" in front of it, and for immediates, it's either h for hexadecimal or b for binary after it?
There is no difference between the "0x" prefix and the "h" suffix. They both mean the number is hexadecimal. The assembler doesn't care which you use, nor does it care if the number will be used as an address, immediate, or anything else. These two instructions will generate exactly the same code:
Code:
mov [0x89AB], 0xCDEF
mov [89ABh], 0CDEFh
Joe wrote:
I'm not sure why you're asking me...
Well, I meant on the weird sprite control thing for the arcade board, but I guess you wouldn't remember that.
Joe wrote:
You didn't specify that DS and ES were already the correct values, so I assumed they were unknown.
They weren't, I just didn't know about how it used 20 bit addressing so forget what I wrote because I didn't get it.
Joe wrote:
The ES prefix causes the source address to be ES:SI instead of DS:SI, which means I can skip setting DS.
But it isn't useful if one of the addresses isn't 0?
Quote:
Quote:
I swear though, 20 bit addressing is the devil.
Yep.
They must have been smoking something pretty strong when they thought that was a good idea...
Quote:
(section.data.start >> 4)
Isn't that relative to the start of the data area, and not the palette table? I realize the palette table is a t the beginning, but if I add more data, I'll have to manually calculate the addresses. Could you actually explain what stuff like ">> 4" means?
Espozo wrote:
I swear though, 20 bit addressing is the devil.
[...]
They must have been smoking something pretty strong when they thought that was a good idea...
It was originally an expansion plan... they knew they needed more than 16 bit of address space, so they had to add another word, and the overlap is actually kinda useful for making code that can be executed at different locations. So they were going to slowly reduce the overlap as time went on ... unfortunately everyone just baked the assumption of a 12-bit overlap into their code ... oops.
Actually, it turns out that the V33 contains an entirely different but funny facility to address up to 16 MiB, which
hopefully the relevant arcade board doesn't use...
Quote:
Could you actually explain what stuff like ">> 4" means?
">>4" is a C-language expression that means "bit shift right by 4 bits".
Espozo wrote:
Joe wrote:
The ES prefix causes the source address to be ES:SI instead of DS:SI, which means I can skip setting DS.
But it isn't useful if one of the addresses isn't 0?
If the source and destination are both in the same segment, DS contains the wrong value, and I don't want or need to set DS, then I'll just set ES and use the
ES prefix.
If the source and destination are both in the same segment, and DS contains the right value, I have to set ES anyway (DS and ES will have the same value) so the
ES prefix is just a waste of space.
If the source and destination are in different segments, obviously I do not want to use the same segment for both source and destination. (However, the
CS and
SS prefixes may still be useful in this situation, if either of those segments are correct for the source address.)
Espozo wrote:
They must have been smoking something pretty strong when they thought that was a good idea...
It was a quick hack to provide a competitive successor to their earlier CPUs (8008, 8080, 8085) while all of the real effort was put into their
revolutionary new CPU.
Things didn't quite go the way Intel was expecting.
Espozo wrote:
Quote:
(section.data.start >> 4)
Isn't that relative to the start of the data area, and not the palette table? I realize the palette table is a t the beginning, but if I add more data, I'll have to manually calculate the addresses. Could you actually explain what stuff like ">> 4" means?
section.data.start is the linear address of the section named "data". The
>> 4 part divides it by 16, to get a value that's useful as a segment. I put that value into DS, so I can access the symbol
.palette using DS.
It works because of the way I've defined the "data" section:
vstart=0 align=16 causes the assembler to align it to a 16-byte boundary (so it always starts at an address that can be used for a segment) and start calculating offsets from 0 (so symbols in that section will all be relative to the start of the section).
Joe wrote:
If the source and destination are both in the same segment, DS contains the wrong value, and I don't want or need to set DS, then I'll just set ES and use the ES prefix.
What's a "segment"?
Joe wrote:
It was a quick hack to provide a competitive successor to their earlier CPUs (8008, 8080, 8085) while all of the real effort was put into their revolutionary new CPU.Things didn't quite go the way Intel was expecting.
5-10x slower than the 68000? And they thought that this was going to be their "revolutionary" new CPU? Imagine how it would look like on a technical spec sheet... It would be like blast processing x 5-10.
Joe wrote:
section.data.start is the linear address of the section named "data". The >> 4 part divides it by 16, to get a value that's useful as a segment. I put that value into DS, so I can access the symbol .palette using DS.It works because of the way I've defined the "data" section: vstart=0 align=16 causes the assembler to align it to a 16-byte boundary (so it always starts at an address that can be used for a segment) and start calculating offsets from 0 (so symbols in that section will all be relative to the start of the section).
Wait, so a segment is a 16 bit of the 20 bit area of memory, so you can address 16 total segments? I get what you mean about addressing the segment instead of the palette table now.
Espozo wrote:
What's a "segment"?
A 64 KiB window, selected by CS, DS, ES, or SS. Alternatively, the "upper half" of the 20 bit address.
Quote:
Wait, so a segment is a 16 bit of the 20 bit area of memory, so you can address 16 total segments?
No, a segment is 16 bits, so you can address 65536 of them. Or, you only have four segment registers, so you can address four of them.
It's just that both F880:0 and F000:8800 address the same location in memory.
lidnariq wrote:
No, a segment is 16 bits, so you can address 65536 of them. Or, you only have four segment registers, so you can address four of them.It's just that both F880:0 and F000:8800 address the same location in memory.
I would have thought that each segment would have contained 65536 bytes, the 16 low bits, and there would be 16 of these, which would be the 4 high bits for selecting segments. I'm not exactly what relation 20 bit addressing has with the segments then.
The 20 bits of address are redundantly split into two 16-bit words, the "segment" and "offset".
The equation that relates the 20-bit address to the segment and offset is:
LinearAddress = (Offset + (Segment × 16))
So, the segment "0" contains the 64 KiB of memory starting at 0x00000...
The segment "2" contains the 64 KiB of memory starting at 0x00020...
Yes, those overlap!
The segment "0x8000" contains the 64 KiB of memory starting at 0x80000... and so on.
Although peripherals are often aligned to an address where the bottom 16 bits are all 0 (For example, the old IBM PC MDA card places its video memory at 0xB0000 through 0xB7FFF, or, segment 0xB000 and offsets 0 though 0x7FFF.) they aren't necessarily—for example, on the arcade board here, palette memory starts at 0xF8800.
You mean that the segments overlap, but with 20 bit addressing, there is not enough granularity to have overlap? Just tell me this: there are 1048576 bytes total that you can address, and with 20 bit addressing, you can not address segments to where they overlap, correct?
Espozo wrote:
You mean that the segments overlap, but with 20 bit addressing, there is not enough granularity to have overlap?
What do you mean when you say "granularity" here?
Quote:
you can not address segments to where they overlap, correct?
I ... don't know what you mean, so I'll try to guess:
DS, ES, SS, and CS can have the same value. In this case, all four segment registers refer to the same (overlapping) 64 KiB section of memory
The segments "0" and "1" have 65536-16=65520 bytes of overlap: those from (0:0010h through 0:FFFFh) = (1:0000h though 1:FFEFh) = (linear 0x00010 through 0x0FFFF)
But no two different numbers
completely overlap.
Espozo wrote:
Joe wrote:
It was a quick hack to provide a competitive successor to their earlier CPUs (8008, 8080, 8085) while all of the real effort was put into their revolutionary new CPU.Things didn't quite go the way Intel was expecting.
5-10x slower than the 68000? And they thought that this was going to be their "revolutionary" new CPU? Imagine how it would look like on a technical spec sheet... It would be like blast processing x 5-10.
I think Joe meant it the other way, x86 was the stopgap processor while they were making the one they intended to truly take off.
This "revolutionary new processor" was supposed to be the
i432, designed for efficient execution of programs written in Ada. It flopped, just like Itanium would years later. The
i960 did a bit better, seeing wide use in embedded systems and Sega's Model 2 arcade platform. The i960 team went on to build the P6 core used in the Pentium Pro, II, and III.
lidnariq wrote:
What do you mean when you say "granularity" here?
Never mind.
lidnariq wrote:
I ... don't know what you mean, so I'll try to guess:
Never mind again.
I'm saying that with the whole 20 bit addressing, you can either have it like this with the nibbles, where 0 equals nothing and F equals some non zero value and nothing will overlap like this:
Segment: F000
Offset: FFFF
Like this:
Segment: FF00
Offset: 0FFF
Like this:
Segment:FFF0
Offset: 00FF
And finally like this:
Segment:FFFF
Offset: 000F
There as 1048576 bytes total though, right? I think I'm going to use option #1 because it makes the most sense in my head.
Oh, sure. In my experience, it's nicest (when possible) to set the segment such that the first thing I care about is at an offset of 0. An example I can think of is the M92 palette RAM, where I would find it conceptually simpler to use a segment of F880 (instead of F000 and start the offset at 8800)
[Referring to a digression that was split to this topic]What tepples?
Anyway, I tried to run my code through yasm and look what happened...
Quote:
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
C:\Users\mrdre_000>cd C:\Users\mrdre_000\Desktop\Irem M92 and 107 Dev
C:\Users\mrdre_000\Desktop\Irem M92 and 107 Dev>yasm test.asm
test.asm:11: error: instruction expected after label
test.asm:11: warning: no non-local label before `.resb'
test.asm:12: error: instruction expected after label
test.asm:13: error: instruction expected after label
test.asm:14: error: instruction expected after label
test.asm:35: warning: ignoring unrecognized character `#'
test.asm:44: error: undefined symbol `main_code.palette' (first use)
test.asm:44: error: (Each undefined symbol is reported only once.)
test.asm:56: warning: ignoring unrecognized character `#'
test.asm:60: warning: ignoring unrecognized character `#'
test.asm:64: error: undefined symbol `y_movement_start' (first use)
test.asm:73: warning: ignoring unrecognized character `#'
test.asm:77: warning: ignoring unrecognized character `#'
test.asm:95: warning: ignoring unrecognized character `#'
test.asm:99: error: instruction expected after label
test.asm:101: error: instruction expected after label
C:\Users\mrdre_000\Desktop\Irem M92 and 107 Dev>
Holy crap...
Well, if anyone wants to help me fix this... (Didn't someone say that xor and moving an immediate take the same amount of cycles in the v33? How good is one megahert here vs on the 65816, like the 65816 vs the 68000?)
Code:
;========================================================================
;Section Code
;========================================================================
cpu 80186
section code vstart=0 align=16
;========================================================================
;Define Stuff:
XDirection .resb 2
YDirection .resb 2
XPosition .resb 2
YPosition .resb 2
SpriteRam equ 0xF800
SpriteRamControl equ 0xF900
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
;========================================================================
main_code:
mov XDirection, 0x0001
mov YDirection, 0x0001
mov XPosition, 0x0000
mov YPosition, 0x0000
mov DX, SpriteRam
mov DS, DX
mov [0x002], 0001h
mov DX, #SpriteRamControl
mov DS, DX
mov [0x000], 0001h
mov [0x004], 0008h
mov [0x008], 0001h
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, .palette
mov DI, 0xF880
mov ES, DI
xor DI, DI
mov CX, 16
cld
rep movsw
x_movement__start:
mov CX, #BallVelocity
x_movement_loop:
add XPosition, XDirection
mov AX, #(ScreenWidth+BallDiameter) ;you write it like that, right?
cmp XPosition, AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not XDirection
add XPosition, XDirection
loop x_movement_loop
y_movement__start:
mov CX, #BallVelocity
y_movement_loop:
add YPosition, YDirection
mov AX, #(ScreenHieght+BallDiameter) ;you write it like that, right?
cmp YPosition, AX
ja y_out_of_bounds
loop x_movement_loop
jmp next_frame
y_out_of_bounds:
not YDirection
add YPosition, YDirection
loop x_movement_loop
next_frame:
mov AX, XPosition
and AX, 0000000000011111b
mov BX, YPosition
and BX, 0000000000011111b
mov DX, #SpriteRam
mov DS, DX
and [0x006], 1111111111100000b
ora [0x006], AX
and [0x000], 1111111111100000b
ora [0x000], BX
hlt
jmp x_movement__start
;========================================================================
section reset start=0x7fff0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
.palette:
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
Espozo wrote:
"thread locker"
That's certainly what will happen if we continue our digression into parallels between Spanish and x86 assembly. (Besides, that's not even how you use that instruction: the second operand can only be memory, not an immediate.)
Anyway, when trying to figure out why the assembler is upset at you, start with the first error message. In this case, it says there's a syntax error on line 11.
Code:
XDirection .resb 2
In this case, the issue is that
.resb is being interpreted as a local label. You want to use
resb instead (or perhaps
resw). Fixing that elsewhere in your program will remove several other warnings. However, it doesn't fix the fact that you're trying to reserve space for variables that belong in RAM inside your code segment, which is ROM! You'll need to create a new segment for uninitialized memory, something like this:
segment bss start=[physical address of RAM] vstart=0 nobits align=16By the way, you can switch segments as many times as you'd like in your source file. If you switch to a segment that's already been defined, the assembler will see it as a continuation of that segment. That way, you can keep code and data that belong to a single function close together in your source file, even if they end up far apart in the ROM.
Segments end up in the order you define them, so you need to define segments that belong in lower addresses before segments that belong in higher addresses. I recommend defining all of the segments near the beginning of your source file. (It's possible to override this behavior, but this way is easier.)
Well, I did what you said about the ram segment, but I still got a ton of errors. Here's the code:
Code:
;========================================================================
;Section Code
;========================================================================
section code vstart=0 align=16
;========================================================================
main_code:
mov XDirection, 0x0001
mov YDirection, 0x0001
mov XPosition, 0x0000
mov YPosition, 0x0000
mov DX, SpriteRam
mov DS, DX
mov [0x002], 0001h
mov DX, #SpriteRamControl
mov DS, DX
mov [0x000], 0001h
mov [0x004], 0008h
mov [0x008], 0001h
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, .palette
mov DI, 0xF880
mov ES, DI
xor DI, DI
mov CX, 16
cld
rep movsw
x_movement__start:
mov CX, #BallVelocity
x_movement_loop:
add XPosition, XDirection
mov AX, #(ScreenWidth+BallDiameter) ;you write it like that, right?
cmp XPosition, AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not XDirection
add XPosition, XDirection
loop x_movement_loop
y_movement__start:
mov CX, #BallVelocity
y_movement_loop:
add YPosition, YDirection
mov AX, #(ScreenHieght+BallDiameter) ;you write it like that, right?
cmp YPosition, AX
ja y_out_of_bounds
loop x_movement_loop
jmp next_frame
y_out_of_bounds:
not YDirection
add YPosition, YDirection
loop x_movement_loop
next_frame:
mov AX, XPosition
and AX, 0000000000011111b
mov BX, YPosition
and BX, 0000000000011111b
mov DX, #SpriteRam
mov DS, DX
and [0x006], 1111111111100000b
or [0x006], AX
and [0x000], 1111111111100000b
or [0x000], BX
hlt
jmp x_movement__start
;========================================================================
section reset start=0x7FFF0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
.palette:
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
;========================================================================
;Section RAM
;========================================================================
cpu 80186
segment bss start=0xE0000 vstart=0 nobits align=16
;========================================================================
;Define Stuff:
XDirection resb 2
YDirection resb 2
XPosition resb 2
YPosition resb 2
SpriteRam equ 0xF800
SpriteRamControl equ 0xF900
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
And here's what the command prompt said:
Command Prompt wrote:
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
C:\Users\mrdre_000>cd C:\Users\mrdre_000\Desktop\Irem M92 and 107 Dev
C:\Users\mrdre_000\Desktop\Irem M92 and 107 Dev>yasm test.asm
test.asm:20: warning: ignoring unrecognized character `#'
test.asm:29: error: undefined symbol `main_code.palette' (first use)
test.asm:29: error: (Each undefined symbol is reported only once.)
test.asm:41: warning: ignoring unrecognized character `#'
test.asm:45: warning: ignoring unrecognized character `#'
test.asm:49: error: undefined symbol `y_movement_start' (first use)
test.asm:58: warning: ignoring unrecognized character `#'
test.asm:62: warning: ignoring unrecognized character `#'
test.asm:80: warning: ignoring unrecognized character `#'
C:\Users\mrdre_000\Desktop\Irem M92 and 107 Dev>
How the heck do you say the address of something in ram, instead of what number that address holds, like if you had something like "InvincibilityCounter", and you wanted to load the number of where it is instead of how much time is in the counter. It doesn't seem to accept "#" or "0x" or "h"...
Espozo wrote:
How the heck do you say the address of something in ram, instead of what number that address holds, like if you had something like "InvincibilityCounter", and you wanted to load the number of where it is instead of how much time is in the counter.
mov AX, [Address] ; stores the value pointed to by Address in AX
mov AX, Address ; stores Address in AX
lea AX, [Address] ; stores Address in AX
LEA is mostly useful for doing address precalculations in one swoop:
lea AX, [BX+SI+Address] ; stores BX+SI+Address in AX
Cut me a break...
Command Prompt wrote:
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
C:\Users\mrdre_000>cd C:\Users\mrdre_000\Desktop\Irem M92 and 107 Dev
C:\Users\mrdre_000\Desktop\Irem M92 and 107 Dev>yasm test.asm
test.asm:11: error: invalid combination of opcode and operands
test.asm:12: error: invalid combination of opcode and operands
test.asm:13: error: invalid combination of opcode and operands
test.asm:14: error: invalid combination of opcode and operands
test.asm:18: error: invalid size for operand 1
test.asm:22: error: invalid size for operand 1
test.asm:23: error: invalid size for operand 1
test.asm:24: error: invalid size for operand 1
test.asm:44: error: invalid combination of opcode and operands
test.asm:46: error: invalid combination of opcode and operands
test.asm:52: error: invalid combination of opcode and operands
test.asm:53: error: invalid combination of opcode and operands
test.asm:61: error: invalid combination of opcode and operands
test.asm:63: error: invalid combination of opcode and operands
test.asm:69: error: invalid combination of opcode and operands
test.asm:70: error: invalid combination of opcode and operands
test.asm:83: error: invalid size for operand 1
test.asm:85: error: invalid size for operand 1
C:\Users\mrdre_000\Desktop\Irem M92 and 107 Dev>
Code:
;========================================================================
;Section Code
;========================================================================
section code vstart=0 align=16
;========================================================================
main_code:
mov XDirection, 0x0001
mov YDirection, 0x0001
mov XPosition, 0x0000
mov YPosition, 0x0000
mov DX, SpriteRam
mov DS, DX
mov [0x002], 0001h
mov DX, [SpriteRamControl]
mov DS, DX
mov [0x0000], 001h
mov [0x0004], 008h
mov [0x0008], 001h
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, palette
mov DI, 0xF880
mov ES, DI
xor DI, DI
mov CX, 16
cld
rep movsw
x_movement__start:
mov CX, [BallVelocity]
x_movement_loop:
add XPosition, XDirection
mov AX, [ScreenWidth+BallDiameter]
cmp XPosition, AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not XDirection
add XPosition, XDirection
loop x_movement_loop
y_movement_start:
mov CX, [BallVelocity]
y_movement_loop:
add YPosition, YDirection
mov AX, [ScreenHieght+BallDiameter]
cmp YPosition, AX
ja y_out_of_bounds
loop x_movement_loop
jmp next_frame
y_out_of_bounds:
not YDirection
add YPosition, YDirection
loop x_movement_loop
next_frame:
mov AX, XPosition
and AX, 0000000000011111b
mov BX, YPosition
and BX, 0000000000011111b
mov DX, [SpriteRam]
mov DS, DX
and [0x006], 1111111111100000b
or [0x006], AX
and [0x000], 1111111111100000b
or [0x000], BX
hlt
jmp x_movement__start
;========================================================================
section reset start=0x7FFF0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
palette:
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
;========================================================================
;Section RAM
;========================================================================
cpu 80186
segment bss start=0xE0000 vstart=0 nobits align=16
;========================================================================
;Define Stuff:
XDirection resb 2
YDirection resb 2
XPosition resb 2
YPosition resb 2
SpriteRam equ 0xF800
SpriteRamControl equ 0xF900
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
Quote:
test.asm:18: error: invalid size for operand 1 ( = mov [0x002], 0001h )
You need to add "byte" or "word" before the [] so that it knows how big the data being moved is.
Espozo wrote:
test.asm:11: error: invalid combination of opcode and operands ( = mov XDirection, 0x0001 )
You also need to use [XDirection], because you're writing to the memory at that location.
You probably also need to initialize DS first.
You also can't do
opc [mem], [mem], as you seem to be trying to do with "add XPosition, XDirection"
Well, I fixed all the problems and it
sort of works. It uploads the palette which is just about right before the sprite business, but nothing actually shows up on the screen. I don't know if it's a problem setting up the sprites, or if it's that something else is just not right with the code. The CHR Rom is filled out, so I don't know.
Here's the new code: (Setting up sprites is right at the beginning.)
Code:
;========================================================================
;Section Code
;========================================================================
section code vstart=0 align=16
;========================================================================
main_code:
mov word [XDirection], 0x0001
mov word [YDirection], 0x0001
mov word [XPosition], 0x0000
mov word [YPosition], 0x0000
mov DX, [SpriteRam]
mov DS, DX
mov word [0x002], 0001h
mov word DX, [SpriteRamControl]
mov DS, DX
mov word [0x000], 0001h
mov word [0x004], 0008h
mov word [0x008], 0001h
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, palette
mov DI, 0xF880
mov ES, DI
xor DI, DI
mov CX, 16
cld
rep movsw
x_movement__start:
mov CX, [BallVelocity]
x_movement_loop:
mov AX, XDirection
add [XPosition], AX
mov AX, [ScreenWidth+BallDiameter]
cmp [XPosition], AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not word [XDirection]
mov AX, XDirection
add [XPosition], AX
loop x_movement_loop
y_movement_start:
mov CX, [BallVelocity]
y_movement_loop:
mov AX, YDirection
add [YPosition], AX
mov AX, [ScreenHieght+BallDiameter]
cmp [YPosition], AX
ja y_out_of_bounds
loop x_movement_loop
jmp next_frame
y_out_of_bounds:
not word [YDirection]
mov AX, YDirection
add [YPosition], AX
loop x_movement_loop
next_frame:
mov AX, XPosition
and AX, 0000000000011111b
mov BX, YPosition
and BX, 0000000000011111b
mov DX, [SpriteRam]
mov DS, DX
and word [0x006], 1111111111100000b
or [0x006], AX
and word [0x000], 1111111111100000b
or [0x000], BX
hlt
jmp x_movement__start
;========================================================================
section reset start=0x7FFF0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
palette:
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
;========================================================================
;Section RAM
;========================================================================
cpu 80186
segment bss start=0xE0000 vstart=0 nobits align=16
;========================================================================
;Define Stuff:
XDirection resb 2
YDirection resb 2
XPosition resb 2
YPosition resb 2
SpriteRam equ 0xF800
SpriteRamControl equ 0xF900
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
Here's the rom and some stuff saying how to set it up:
Attachment:
Irem M92 Test Help Package.zip [1.05 MiB]
Downloaded 56 times
The very first instruction after the reset vector shows one problem:
Code:
mov word [XDirection], 0x0001
All memory accesses are relative to a segment register. In this case, you are writing to memory relative to DS. You never set DS, so who knows what you're actually writing.
Speaking of segments, your section definitions are in the wrong order. Sections that belong in ROM must go between the IVT and reset sections.
I did mention earlier that you can switch between sections any time you want. Here's what that looks like:
Code:
;===================================================================
;Section Definitions
;===================================================================
section ivt start=0 ; you will need this later
section code vstart=0 align=16
section data vstart=0 align=16
section reset start=0x7FFF0 vstart=0
section bss start=0xE0000 vstart=0 nobits align=16
;===================================================================
;Reset Vector
;===================================================================
section reset
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
section code
main_code:
mov ax, (section.bss.start >> 4)
mov ds, ax
mov word [XDirection], 0x0001
; ...and so on
You can then switch sections using
section code,
section data,
section bss, and the names of any other sections you come up with later. You can even do this in the middle of code; for example...
Code:
section code
upload_palette:
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, .palette
mov DI, 0xF880
mov ES, DI
xor DI, DI
mov CX, 16
cld
rep movsw
; execution goes from here...
section data
.palette:
dw 0x6542, 0x7320, 0x7275, 0x2065, 0x6f74, 0x6420, 0x6972, 0x6b6e
dw 0x7920, 0x756f, 0x2072, 0x764f, 0x6c61, 0x6974, 0x656e, 0x0021
section code
; ...to here!
mov ax, (section.bss.start >> 4)
mov ds, ax
x_movement__start:
mov CX, [BallVelocity]
; ...and so on
It's not actually skipping anything. The assembler will rearrange things when it generates the ROM so that there's nothing between the
REP MOVSW and
MOV AX, (section.bss.start >> 4) instructions.
Damn it!
Now why won't it work?
Code:
section ivt start=0 ; you will need this later
;========================================================================
;Section Code
;========================================================================
section code vstart=0 align=16
;========================================================================
main_code:
mov word AX, 0x0000
mov word DX, AX
mov word [XDirection], 0x0001
mov word [YDirection], 0x0001
mov word [XPosition], 0x0000
mov word [YPosition], 0x0000
mov DX, [SpriteRam]
mov DS, DX
mov word [0x002], 0001h
mov word AX, 0x0000
mov word DX, AX
mov word DX, [SpriteRamControl]
mov DS, DX
mov word [0x000], 0001h
mov word [0x004], 0008h
mov word [0x008], 0001h
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, palette
mov DI, 0xF880
mov ES, DI
xor DI, DI
mov CX, 16
cld
rep movsw
mov word AX, 0x0000
mov word DX, AX
x_movement__start:
mov CX, [BallVelocity]
x_movement_loop:
mov AX, XDirection
add [XPosition], AX
mov AX, [ScreenWidth+BallDiameter]
cmp [XPosition], AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not word [XDirection]
mov AX, XDirection
add [XPosition], AX
loop x_movement_loop
y_movement_start:
mov CX, [BallVelocity]
y_movement_loop:
mov AX, YDirection
add [YPosition], AX
mov AX, [ScreenHieght+BallDiameter]
cmp [YPosition], AX
ja y_out_of_bounds
loop x_movement_loop
jmp next_frame
y_out_of_bounds:
not word [YDirection]
mov AX, YDirection
add [YPosition], AX
loop x_movement_loop
next_frame:
mov AX, XPosition
and AX, 0000000000011111b
mov BX, YPosition
and BX, 0000000000011111b
mov DX, [SpriteRam]
mov DS, DX
and word [0x006], 1111111111100000b
or [0x006], AX
and word [0x000], 1111111111100000b
or [0x000], BX
hlt
jmp x_movement__start
;========================================================================
;Section Reset
;========================================================================
section reset start=0x7FFF0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
palette:
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
;========================================================================
;Section RAM
;========================================================================
cpu 80186
segment bss start=0xE0000 vstart=0 nobits align=16
;========================================================================
;Define Stuff:
XDirection resb 2
YDirection resb 2
XPosition resb 2
YPosition resb 2
SpriteRam equ 0xF800
SpriteRamControl equ 0xF900
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
Espozo wrote:
mov word AX, 0x0000
mov word DX, AX
... That is definitely not what you want.
DS should definitely not be 0, that's ROM, not RAM.
You don't need "word" if it's not a memory write.... although yasm accepts it.
And the typo of "DS" as "DX".
Your sections are still in the wrong order. The "data" section needs to be before the "reset" section or it won't work.
It looks like you're trying to set DS, but you don't know what to set it to.
Code:
mov word AX, 0x0000
mov word DX, AX
Since you're trying to access variables inside the bss section, you need something like this.
Code:
mov ax, (section.bss.start >> 4)
mov ds, ax
There are also a few places where you do something like this.
Code:
mov DX, [SpriteRam]
mov DS, DX
That's loading a value from memory, but SpriteRam is a constant, which you should be loading as an immediate value.
Code:
mov ax, SpriteRam
mov ds, ax
Great, now it doesn't work at all after I moved stuff around. It worked fine immediately before. Also, isn't what you said about "[]" contradictory to what lidnariq said? I know I'm wasting page after page, but I don't care. Here it is, again... (I came up with a way to solve my 20 bit addressing problem, and I'm pretty sure that that isn't the problem because it worked even after I did that.)
Code:
;========================================================================
;Section Code
;========================================================================
cpu 80186
section code vstart=0 align=16
main_code:
mov DX, [WorkRamStart]
mov DS, DX
mov word [XDirection], 0x0001
mov word [YDirection], 0x0001
mov word [XPosition], 0x0000
mov word [YPosition], 0x0000
mov DX, [VideoHardwareRamStart]
mov DS, DX
mov word [SpriteRam+0x0002], 0001h
mov DX, [WorkRamStart]
mov DS, DX
mov DX, [VideoHardwareRamStart]
mov DS, DX
mov word [SpriteControlRam+0x0000], 0001h
mov word [SpriteControlRam+0x0004], 0008h
mov word [SpriteControlRam+0x0008], 0001h
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, palette
mov DI, [VideoHardwareRamStart]
mov ES, DI
mov DI, [PaletteRam]
mov CX, 16
cld
rep movsw
mov DX, [WorkRamStart]
mov DS, DX
x_movement__start:
mov CX, [BallVelocity]
x_movement_loop:
mov AX, XDirection
add [XPosition], AX
mov AX, [ScreenWidth+BallDiameter]
cmp [XPosition], AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not word [XDirection]
mov AX, XDirection
add [XPosition], AX
loop x_movement_loop
y_movement_start:
mov CX, [BallVelocity]
y_movement_loop:
mov AX, YDirection
add [YPosition], AX
mov AX, [ScreenHieght+BallDiameter]
cmp [YPosition], AX
ja y_out_of_bounds
loop x_movement_loop
jmp next_frame
y_out_of_bounds:
not word [YDirection]
mov AX, YDirection
add [YPosition], AX
loop x_movement_loop
next_frame:
mov AX, XPosition
and AX, 0000000000011111b
mov BX, YPosition
and BX, 0000000000011111b
mov DX, [VideoHardwareRamStart]
mov DS, DX
and word [SpriteRam+0x0006], 1111111111100000b
or [SpriteRam+0x0006], AX
and word [SpriteRam+0x0000], 1111111111100000b
or [SpriteRam+0x0000], BX
hlt
jmp x_movement__start
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
palette:
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
;========================================================================
;Section Reset
;========================================================================
section reset start=0x7FFF0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section RAM
;========================================================================
segment bss start=0xE0000 vstart=0 nobits align=16
;Define Stuff:
XDirection resb 2
YDirection resb 2
XPosition resb 2
YPosition resb 2
WorkRamStart equ 0xE000
VideoHardwareRamStart equ 0xF000
SpriteRam equ 0x8000
PaletteRam equ 0x8800
SpriteControlRam equ 0x9000
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
(Could somebody just fix this for me...
)
Espozo wrote:
Also, isn't what you said about "[]" contradictory to what lidnariq said?
Nah. Foo is a number. [Foo] means "treat Foo as an address and read from/write to that address".
So when you
mov dx, [WorkRamStart], you're loading the value at DS:[WorkRamStart], not the constant number "WorkRamStart"
Anyway, other thought: Do you need to, and did you enable the NMI source / unmask and enable IRQs? HLT won't ever finish otherwise.
For the umpteenth time, I think this is alright. I fixed it accoriding to what you said about "[]".
Code:
;========================================================================
;Section Code
;========================================================================
cpu 80186
section code vstart=0 align=16
main_code:
mov DX, WorkRamStart
mov DS, DX
mov word [XDirection], 0x0001
mov word [YDirection], 0x0001
mov word [XPosition], 0x0000
mov word [YPosition], 0x0000
mov DX, VideoHardwareRamStart
mov DS, DX
mov word [SpriteRam+0x0002], 0001h
mov DX, WorkRamStart
mov DS, DX
mov DX, VideoHardwareRamStart
mov DS, DX
mov word [SpriteControlRam+0x0000], 0001h
mov word [SpriteControlRam+0x0004], 0008h
mov word [SpriteControlRam+0x0008], 0001h
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, palette
mov DI, VideoHardwareRamStart
mov ES, DI
mov DI, PaletteRam
mov CX, 16
cld
rep movsw
mov DX, WorkRamStart
mov DS, DX
x_movement__start:
mov CX, BallVelocity
x_movement_loop:
mov AX, [XDirection]
add [XPosition], AX
mov AX, ScreenWidth+BallDiameter
cmp [XPosition], AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not word [XDirection]
mov AX, [XDirection]
add [XPosition], AX
loop x_movement_loop
y_movement__start:
mov CX, BallVelocity
y_movement_loop:
mov AX, [YDirection]
add [YPosition], AX
mov AX, ScreenHeight+BallDiameter
cmp [YPosition], AX
ja y_out_of_bounds
loop y_movement_loop
jmp next_frame
y_out_of_bounds:
not word [YDirection]
mov AX, [YDirection]
add [YPosition], AX
loop y_movement_loop
next_frame:
mov AX, [XPosition]
and AX, 0000000000011111b
mov BX, [YPosition]
and BX, 0000000000011111b
mov DX, VideoHardwareRamStart
mov DS, DX
and word [SpriteRam+0x0006], 1111111111100000b
or [SpriteRam+0x0006], AX
and word [SpriteRam+0x0000], 1111111111100000b
or [SpriteRam+0x0000], BX
hlt
jmp x_movement__start
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
palette:
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
;========================================================================
;Section Reset
;========================================================================
section reset start=0x7FFF0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section RAM
;========================================================================
segment bss start=0xE0000 vstart=0 nobits align=16
;Define Stuff:
XDirection resb 2
YDirection resb 2
XPosition resb 2
YPosition resb 2
WorkRamStart equ 0xE000
VideoHardwareRamStart equ 0xF000
SpriteRam equ 0x8000
PaletteRam equ 0x8800
SpriteControlRam equ 0x9000
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
lidnariq wrote:
Anyway, other thought: Do you need to, and did you enable the NMI source / unmask and enable IRQs? HLT won't ever finish otherwise.
What? You mean I need to set up a vblank thing? Is it like whenever the frame ends, it jumps to some part of memory, like how it starts at 0x7FFF0 for reset and power on? Oh yeah, I remember. I need the equivalent of "RTI" at the reset vector, right?
What he's saying is that HLT will resume when any interrupt fires, but if all interrupts are disabled (all of them) then the CPU will have nothing to wake it up.
...also wait, what is the HLT for? Pretty sure a loop here makes more sense...
Espozo wrote:
What? You mean I need to set up a vblank thing?
Just like with the NES and SNES, there is a vblank interrupt and you probably need to use it for something. There are other interrupts as well, but vblank is probably the first one you'll want to set up.
In order to use interrupts, you need to at set up a stack and fill the relevant parts of the IVT. Remember when I showed you code with "section ivt" in it? That's why you need that section.
Espozo wrote:
Is it like whenever the frame ends, it jumps to some part of memory, like how it starts at 0x7FFF0 for reset and power on?
The x86 architecture has very flexible interrupt handling, with 256 interrupts that can be triggered either by software or hardware. Several of the first 32 interrupts will also be triggered automatically by the CPU to handle different circumstances; for example, interrupt 0 will occur if you try to divide by zero, and interrupt 2 is triggered by NMI. The CPU-triggered functions of the first 32 interrupts are standardized, but not every CPU has all of them. You'll have to check the manual for the CPU you're using (V33, I think?) to see which ones your CPU might trigger.
(Here's a bit of a history lesson: when IBM designed the PC, they used some of those first 32 interrupts for hardware and software interrupts, because the 8086 hadn't assigned them to any functions yet. Of course, Intel assigned them on later CPUs, which caused conflicts.)
Espozo wrote:
Oh yeah, I remember. I need the equivalent of "RTI" at the reset vector, right?
That's right. The instruction you're looking for is
IRET.
Should this suffice? Also, the SNES can only write to oam and cgram and stuff during vblank, but it seems the M92 can do both of those outside of vblank, and there is no vram, so what is it really useful for? Could you actually just do general processing here?
Code:
;========================================================================
;Section IVT
;========================================================================
cpu 80186
section ivt start=0 ; you will need this later
iret
;========================================================================
;Section Code
;========================================================================
The IVT is an array of 256 far pointers (i.e. 32 bits consisting of segment and offset), not code.
I know that R-Type ran on the M72 instead, but the first 1024 bytes show that:
Code:
$ hd -n 1024 RTYPE_CPU.BIN
00000000 fc 00 40 00 fc 00 40 00 fc 00 40 00 fc 00 40 00 |..@...@...@...@.|
*
00000080 fe 00 40 00 fa 00 40 00 ee 02 40 00 fc 00 40 00 |..@...@...@...@.|
00000090 fc 00 40 00 fc 00 40 00 fc 00 40 00 fc 00 40 00 |..@...@...@...@.|
*
00000400
which says that, for every single interrupt
except 32, 33, and 34 (i.e. 0x20, 0x21, and 0x22), the CPU should jump to 0040:00FC. (For 0x20 through 0x22 it should instead jump to 0040:00FE, 0040:00FA, and 0040:02EE instead)
(I'm just using R-Type because I picked up DotEmu's port in one of the Humble Bundles, and looking inside includes massaged versions of the original arcade data)
Espozo wrote:
Also, the SNES can only write to oam and cgram and stuff during vblank, but it seems the M92 can do both of those outside of vblank, and there is no vram, so what is it really useful for?
Timing. Having an interrupt that fires once per frame helps you keep your game loop in sync with the screen.
Also, even if you can write to sprite and palette memory during active display, you probably shouldn't do so lightly because you can glitch out the picture that way (I'm assuming the writes take effect immediately rather than being hung up until the end of the frame). Much safer to change things around during vertical retrace when it definitely isn't going to cause tearing and artifacting. The same goes for stuff like scroll values that you generally want to apply to the whole screen.
What nidnariq? Are you saying that there is a place in memory that you can fill out that says where the interrupt will jump to? If so, do you know where it is on the M92? If there are 32 interrupts that the CPU can trigger, how do you make it to where it uses different interrupts?
93143 wrote:
Also, even if you can write to sprite and palette memory during active display, you probably shouldn't do so lightly because you can glitch out the picture that way (I'm assuming the writes take effect immediately rather than being hung up until the end of the frame).
You could actually abuse this for scan line effects, right? The problem is that I imagine it would jump all over the place based on how much processing is being done.
93143 wrote:
Also, even if you can write to sprite and palette memory during active display, you probably shouldn't do so lightly because you can glitch out the picture that way (I'm assuming the writes take effect immediately rather than being hung up until the end of the frame). Much safer to change things around during vertical retrace when it definitely isn't going to cause tearing and artifacting. The same goes for stuff like scroll values that you generally want to apply to the whole screen.
That and if it's anything like the Sega Genesis, transfers to OAM and tilemaps are likely to have fewer wait states during vblank. The Genesis VDP allows writing during draw time but stalls the CPU if at least four writes are pending.
Espozo wrote:
What lidnariq? Are you saying that there is a place in memory that you can fill out that says where the interrupt will jump to? If so, do you know where it is on the M92? If there are 32 interrupts that the CPU can trigger, how do you make it to where it uses different interrupts?
That is exactly what I'm (and Joe) is saying. That's what the
IDT is. It's an x86 thing, so it's true for any x86 machine running in "real" mode.
(The 8086, the 80186, and all of NEC's processors can only run in "real" mode)(When in real mode) it's in the same place as on the M72, which is in segment 0, offsets 0-0x3FF.
The nasm/yasm documentation says
Quote:
To declare a far pointer to a data item in a data segment, you must code
dw symbol, seg symbol
So you'll have some large number of lines in that section covering at least the first 48 entries (since the first 32 can be generated by the CPU, and the next 16 seem likely to be generated by the hardware that Irem added to the board.
Could it look something like this?
Code:
;========================================================================
;Section IVT
;========================================================================
cpu 80186
section ivt start=0 ; you will need this later
idt:
dw (section.code.start >> 4), interupt_1
;========================================================================
;Section Code (Pointers)
;========================================================================
section code start=0x00400 vstart=0 align=16
interupt_1:
iret
;========================================================================
;Section Code (Main Code)
;========================================================================
main_code:
mov DX, WorkRamStart
mov DS, DX
Again though, when you use hlt, how do you say what interrupt you want to use? What happens if there's slowdown and it never gets there? Where will it go?
HLT will wait until any interrupt fires, doesn't matter which one. It's literally "halt the processor until something happens".
If you need to get some particular interrupt to fire you need to program the rest of the hardware accordingly and make the relevant interrupt handler cope with it.
How?
Read MAME's M92 implementation?
It looks like drivers/m92.c mentions:
Code:
#define M92_IRQ_0 ((m_irq_vectorbase+0)/4) /* VBL interrupt */
#define M92_IRQ_1 ((m_irq_vectorbase+4)/4) /* Sprite buffer complete interrupt */
#define M92_IRQ_2 ((m_irq_vectorbase+8)/4) /* Raster interrupt */
#define M92_IRQ_3 ((m_irq_vectorbase+12)/4) /* Sound cpu interrupt */
and later on m_irq_vectorbase is either 0x80 or 0x20 depending on the physical board:
Code:
DRIVER_INIT_MEMBER(m92_state,m92)
{
[…]
m_irq_vectorbase = 0x80;
}
/* different vector base */
DRIVER_INIT_MEMBER(m92_state,m92_alt)
{
[…]
m_irq_vectorbase = 0x20;
}
lethalth … 0x20;
m92_bank … 0x80;
majtitl2 … 0x80;
ppan … 0x80;
Which should be interrupts 8 through 11 (at locations 0x20 through 0x2F) or interrupts 0x20 through 0x23 (at locations 0x80 through 0x8F).
Interestingly, the V33A's datasheet says that interrupts 8 through 15 are explicitly reserved, but ... apparently that didn't stop Irem!
The CPU-generated interrupts on the V33A are (as with the 80186), int 0 = divide error, int 1 = brk, int 2 = /nmi, int 3,4,5 = by instruction, int 6 = undefined instruction, int 7 = no coprocessor, int 0x10 = coprocessor error.
As to how to enable them? Well, read the source :/
lidnariq wrote:
Interestingly, the V33A's datasheet says that interrupts 8 through 15 are explicitly reserved, but ... apparently that didn't stop Irem!
They're reserved for use on other x86 CPUs, which means it's perfectly safe to use them if you aren't planning on running the same software on a different CPU. (That's probably the same logic IBM used when they designed the PC...)
Espozo wrote:
interupt_1
That's interrupt 0.
IBM's logic was most likely "we have 64KB to fit in the vectors, BIOS data, OS and program" (the original PCs only had 64KB of RAM, not counting video memory). Although it's also true the original PC was made during an era that computers were never (or rarely) forwards compatible...
(off-topic) Except that that they didn't even use the ≈unused vectors in the bottom-most 1 KiB for anything... and the
BDA took another 256 bytes at 0x00400...
Joe wrote:
That's interrupt 0.
Oops...
Well, I fixed it, and since there are only 48 interrupts instead of 256, I guess I can have the code start at 0x000C0 instead of 0x00400.
Code:
;========================================================================
;Section IVT
;========================================================================
cpu 80186
section ivt start=0 ; you will need this later
idt:
dw (section.code.start >> 4), interupt_0
;========================================================================
;Section Code (Pointers)
;========================================================================
section code start=0x000C0 vstart=0 align=16
interupt_0:
iret
;========================================================================
;Section Code (Main Code)
;========================================================================
main_code:
mov DX, WorkRamStart
mov DS, DX
lidnariq wrote:
As to how to enable them? Well, read the source :/
I was hoping it wouldn't come to that.
I guess there should be a register that you write to for a general vblank?
Espozo wrote:
Code:
section code start=0x000C0 vstart=0 align=16
You don't have to specify
start for that section; yasm will put it after the previous section automatically.
Espozo wrote:
I guess there should be a register that you write to for a general vblank?
Possibly. I took a quick look at it but didn't see any way to disable the vblank interrupt, so it might be enabled all the time. If that's the case, the only thing you'd need to do to start receiving vblank interrupts is execute the
STI instruction. (Make sure your IVT has entries for the other three hardware interrupts, too; otherwise, you'll crash as soon as you receive your first non-vblank interrupt.)
Espozo wrote:
dw (section.code.start >> 4), interupt_0
Offset first, then segment.
It looks like nothing ever generate interrupts above 0x23, so you only need the first 36 entries (not even 48)... just use something like
Code:
dw diverr_handler, (section.code.start >> 4)
dw brk_handler, (section.code.start >> 4)
dw nmi_handler, (section.code.start >> 4)
dw int3_handler, (section.code.start >> 4)
dw into_handler, (section.code.start >> 4)
dw bound_handler, (section.code.start >> 4)
dw undefinst_handler, (section.code.start >> 4)
dw nocoprocessor_handler, (section.code.start >> 4)
dw vbl_handler, (section.code.start >> 4)
dw sprbuf_handler, (section.code.start >> 4)
dw raster_handler, (section.code.start >> 4)
dw sound_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw coprocessorerror_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw vbl_handler, (section.code.start >> 4)
dw sprbuf_handler, (section.code.start >> 4)
dw raster_handler, (section.code.start >> 4)
dw sound_handler, (section.code.start >> 4)
or use some other names you like better.
I guess this should be good? I'm also guessing that "vbl_handler" is for vblank? This sounds familiar so I'm wondering what it means, but nmi mean? Also, Do I see that the M92 can support a coprocessor? I would have though that would have been an odd design choice for an arcade machine, seeing that with Sega at least, their solution is to just throw their money to make a new one every year or two even when it isn't necessarily needed.
Code:
;========================================================================
;Section IVT
;========================================================================
cpu 80186
section ivt start=0 ; you will need this later
idt:
dw diverr_handler, (section.code.start >> 4)
dw brk_handler, (section.code.start >> 4)
dw nmi_handler, (section.code.start >> 4)
dw int3_handler, (section.code.start >> 4)
dw into_handler, (section.code.start >> 4)
dw bound_handler, (section.code.start >> 4)
dw undefinst_handler, (section.code.start >> 4)
dw nocoprocessor_handler, (section.code.start >> 4)
dw vbl_handler, (section.code.start >> 4)
dw sprbuf_handler, (section.code.start >> 4)
dw raster_handler, (section.code.start >> 4)
dw sound_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw coprocessorerror_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw vbl_handler, (section.code.start >> 4)
dw sprbuf_handler, (section.code.start >> 4)
dw raster_handler, (section.code.start >> 4)
dw sound_handler, (section.code.start >> 4)
;========================================================================
;Section Code (Pointers)
;========================================================================
section code vstart=0 align=16
diverr_handler:
iret
brk_handler:
iret
nmi_handler:
iret
int3_handler:
iret
into_handler:
iret
bound_handler:
iret
undefinst_handler:
iret
nocoprocessor_handler:
iret
vbl_handler:
iret
sprbuf_handler:
iret
raster_handler:
iret
sound_handler:
iret
def_handler:
iret
coprocessorerror_handler:
iret
;========================================================================
;Section Code (Main Code)
;========================================================================
main_code:
mov DX, WorkRamStart
mov DS, DX
mov word [XDirection], 0x0001
mov word [YDirection], 0x0001
mov word [XPosition], 0x0000
mov word [YPosition], 0x0000
mov DX, VideoHardwareRamStart
mov DS, DX
mov word [SpriteRam+0x0002], 0001h
mov DX, WorkRamStart
mov DS, DX
mov DX, VideoHardwareRamStart
mov DS, DX
mov word [SpriteControlRam+0x0000], 0001h
mov word [SpriteControlRam+0x0004], 0008h
mov word [SpriteControlRam+0x0008], 0001h
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, palette
mov DI, VideoHardwareRamStart
mov ES, DI
mov DI, PaletteRam
mov CX, 16
cld
rep movsw
mov DX, WorkRamStart
mov DS, DX
sti ;Enable Interupts
x_movement_start:
mov CX, BallVelocity
x_movement_loop:
mov AX, [XDirection]
add [XPosition], AX
mov AX, ScreenWidth+BallDiameter
cmp [XPosition], AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not word [XDirection]
mov AX, [XDirection]
add [XPosition], AX
loop x_movement_loop
y_movement_start:
mov CX, BallVelocity
y_movement_loop:
mov AX, [YDirection]
add [YPosition], AX
mov AX, ScreenHeight+BallDiameter
cmp [YPosition], AX
ja y_out_of_bounds
loop y_movement_loop
jmp next_frame
y_out_of_bounds:
not word [YDirection]
mov AX, [YDirection]
add [YPosition], AX
loop y_movement_loop
next_frame:
mov AX, [XPosition]
and AX, 0000000000011111b
mov BX, [YPosition]
and BX, 0000000000011111b
mov DX, VideoHardwareRamStart
mov DS, DX
and word [SpriteRam+0x0006], 1111111111100000b
or [SpriteRam+0x0006], AX
and word [SpriteRam+0x0000], 1111111111100000b
or [SpriteRam+0x0000], BX
hlt
jmp x_movement_start
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
palette:
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
dw 0x0123, 0x4567, 0x89AB, 0xCDEF
dw 0xFEDC, 0xBA98, 0x7654, 0x3210
;========================================================================
;Section Reset
;========================================================================
section reset start=0x7FFF0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section RAM
;========================================================================
segment bss start=0xE0000 vstart=0 nobits align=16
;Define Stuff:
XDirection resb 2
YDirection resb 2
XPosition resb 2
YPosition resb 2
WorkRamStart equ 0xE000
VideoHardwareRamStart equ 0xF000
SpriteRam equ 0x8000
PaletteRam equ 0x8800
SpriteControlRam equ 0x9000
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
The "coprocessor" here would be the standard x87 FPU unit, for doing floating-point math.
I have no idea whether the M92 actually contains one... it looks like the V33A's matched coprocessor was the µPD72291, and it was only available in PGA, and I don't see anything in a PGA package on the pictures I can find of M92 PCBs. So ... probably not.
The reason for the "no coprocessor" handler is you can slowly emulate the floating point math using the CPU, rather than just not running at all.
The top part should be good though, right? I don't want anything to happen during vblank.
Well, I assembled it and I still don't have a sprite onscreen, but I noticed the background color was different and it turns out that whatever it was displaying beforehand did not match palette table, so at least I'm one step closer. I also changed the palette table into a simple red gradient and it also worked. I wonder what the problem is...
I can't find something that's clearly a sprite enable, but I found the enables for the three tiled layers at I/O bus addresses 0x98-0x9F
(look at src/mame/*/m92.c)
Hello?
Does anyone know how to enable sprites?
I promise you you are one of the first ten people to ever get anywhere at all in making homebrew for this specific arcade board.
I'd try doing something with the background layers first, then come back to the sprites question.
One of this first ten? I'm probably going to be one of the
only ten! It's not like it's a bad platform either. 3 BG layers, 256 64x64 sprites and 2048 colors with 15bit color depth sounds awesome to me, and the main cpu isn't MIPS or ARM or something else god awful. If anything, this board seems like a better version of the CPS 1. Irem never gets any attention.
I wonder if I'll ever be able to convince my mother to let me get one of these...
Espozo wrote:
Hello?
Does anyone know how to enable sprites?
Start a DMA operation to copy the sprites from sprite RAM to the graphics chip's internal sprite buffer. The relevant code starts at line 64 in
this file. Since it's a write16 function, the offsets are in words, not bytes. (You'll also need the address map on line 354 of
this file.)
Espozo wrote:
and the main cpu isn't MIPS or ARM or something else god awful
Yeah, it's x86, which is worse!
Espozo wrote:
I wonder if I'll ever be able to convince my mother to let me get one of these...
If you do, you'll have to reverse-engineer the interrupt controller (and possibly other hardware functions as well). MAME's M92 driver isn't very accurate.
Joe wrote:
Yeah, it's x86, which is worse!
Oh please, early x86 was alright. The 8-bit CPUs were all way worse.
Joe wrote:
Yeah, it's x86, which is worse!
It was okay before it got to this...
Quote:
Instruction: VFMADDSUBPD xmm0, xmm1, xmm2, xmm3
Opcode: C4E3 WvvvvL01 5D /r /is4
Meaning: Fused Multiply-Alternating Add/Subtract of Packed Double-Precision Floating-Point Values
How do you even read any of the mame code? On the first link you showed me, could you explain what each means? For offset 0, it says sprite list size, but then it says negative which I have no clue what they mean by negative except that maybe 0xFFFF means 0 sprites. I don't know what sprite control does, and it seems that I just need to write any value to sprite dma and then something should appear.
(I still don't know why anyone likes C. There are like a billion different semicolons and commas and parenthesis and it just ends up looking like somebody just pressed random keys.)
Espozo wrote:
(I still don't know why anyone likes C. There are like a billion different semicolons and commas and parenthesis and it just ends up looking like somebody just pressed random keys.)
If C is already too full of punctuation for you, definitely don't learn LISP. Imagine if they solved
every problem with parentheses instead...
Though, honestly, the cosmetic details of syntax are probably one of the least important parts of a programming language. Like, if C had no semicolons, and forced every statement to be on its own line, like Python, it wouldn't make much of a difference to me. It's real advantages and disadvantages lie elsewhere.
Espozo wrote:
How do you even read any of the mame code?
MAME tends to be pretty easy to read, as far as C++ code goes. (Obviously it depends on the driver, since they aren't all written to the same quality standards...)
Espozo wrote:
On the first link you showed me, could you explain what each means? For offset 0, it says sprite list size, but then it says negative which I have no clue what they mean by negative except that maybe 0xFFFF means 0 sprites. I don't know what sprite control does, and it seems that I just need to write any value to sprite dma and then something should appear.
The comments are just hints to make reading the code easier. You still have to read the code to understand what's happening.
If the value you write to offset 2 has a low byte of 8, the value you wrote to offset 0 is subtracted from 256 and used as the number of sprites (or, if you wrote 0, no sprites will be displayed). Otherwise, all 256 sprites are displayed.
Any value you write to offset 4 will start the sprite DMA.
Espozo wrote:
I still don't know why anyone likes C.
It's a lot easier to write working C code than working assembly code, and you don't have to completely start over to port it to another CPU architecture.
Espozo wrote:
There are like a billion different semicolons and commas and parenthesis and it just ends up looking like somebody just pressed random keys.
You could say the same about assembly, but with mnemonics instead of punctuation.
Espozo wrote:
I still don't know why anyone likes C.
C (at least, ANSI C) can pretty much be compiled onto anything. What isn't there to like about portability?
nicklausw wrote:
Espozo wrote:
I still don't know why anyone likes C.
C (at least, ANSI C) can pretty much be compiled onto anything. What isn't there to like about portability?
The lack of time and space efficiency of the output of well-known freely distributed C compilers for certain 8- and 16-bit targets, perhaps. The integer promotion rules of C itself assume at least a 16-bit machine, making life hard for 6502 and Z80 users. In addition, before GNU, people were expected to pay for compilers, and
"We don’t support 16-bit machines in GNU."What are good freely available C compilers for 8086/8088, 80186, and 80286?
Open Watcom C still has old x86 targets like that.
I used to use it in the '90s. It was a great compiler then (better than DJGPP was at the time). I expect it's still pretty good.
I used to have Pacific C at some point, which also supports real mode x86 (and has a rather easy way to embed assembly in the code, useful for hardware accesses). The problem is that it's a DOS compiler, although if you can tolerate working from DOSBox... :v
The problem here is more finding a suitable x86 compiler that doesn't assume DOS or 16-bit Windows (this is relevant for the object format, since the only headerless format is limited to 64KB and toolchains enforce this limit).
There's bcc/
dev86, formerly part of the
ELKS project, which seems to be a modified 6809 compiler. But it sounds like it's still stuck in what Turbo C++ called the "compact" (64KiB code / 1MiB data) memory model.... Also, apparently the rightsholders to Borland's Turbo compilers have released a
few (very) old compilers.
Well, why doesn't this work? I just made it to where it would display all the sprites but it still doesn't display any.
Code:
;========================================================================
;Section IVT
;========================================================================
cpu 80186
section ivt start=0 ; you will need this later
idt:
dw diverr_handler, (section.code.start >> 4)
dw brk_handler, (section.code.start >> 4)
dw nmi_handler, (section.code.start >> 4)
dw int3_handler, (section.code.start >> 4)
dw into_handler, (section.code.start >> 4)
dw bound_handler, (section.code.start >> 4)
dw undefinst_handler, (section.code.start >> 4)
dw nocoprocessor_handler, (section.code.start >> 4)
dw vbl_handler, (section.code.start >> 4)
dw sprbuf_handler, (section.code.start >> 4)
dw raster_handler, (section.code.start >> 4)
dw sound_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw coprocessorerror_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw vbl_handler, (section.code.start >> 4)
dw sprbuf_handler, (section.code.start >> 4)
dw raster_handler, (section.code.start >> 4)
dw sound_handler, (section.code.start >> 4)
;========================================================================
;Section Code (Pointers)
;========================================================================
section code vstart=0 align=16
diverr_handler:
iret
brk_handler:
iret
nmi_handler:
iret
int3_handler:
iret
into_handler:
iret
bound_handler:
iret
undefinst_handler:
iret
nocoprocessor_handler:
iret
vbl_handler:
iret
sprbuf_handler:
iret
raster_handler:
iret
sound_handler:
iret
def_handler:
iret
coprocessorerror_handler:
iret
;========================================================================
;Section Code (Main Code)
;========================================================================
main_code:
mov DX, WorkRamStart
mov DS, DX
mov word [XDirection], 0x0001
mov word [YDirection], 0x0001
mov word [XPosition], 0x0000
mov word [YPosition], 0x0000
mov DX, VideoHardwareRamStart
mov DS, DX
mov word [SpriteRam+0x0002], 0x0001
mov word [SpriteControlRam+0x0008], 0x0001
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, palette
mov DI, VideoHardwareRamStart
mov ES, DI
mov DI, PaletteRam
mov CX, 16
cld
rep movsw
mov DX, WorkRamStart
mov DS, DX
sti ;Enable Interupts
x_movement_start:
mov CX, BallVelocity
x_movement_loop:
mov AX, [XDirection]
add [XPosition], AX
mov AX, ScreenWidth+BallDiameter
cmp [XPosition], AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not word [XDirection]
mov AX, [XDirection]
add [XPosition], AX
loop x_movement_loop
y_movement_start:
mov CX, BallVelocity
y_movement_loop:
mov AX, [YDirection]
add [YPosition], AX
mov AX, ScreenHieght+BallDiameter
cmp [YPosition], AX
ja y_out_of_bounds
loop y_movement_loop
jmp next_frame
y_out_of_bounds:
not word [YDirection]
mov AX, [YDirection]
add [YPosition], AX
loop y_movement_loop
next_frame:
mov AX, [XPosition]
and AX, 0000000000011111b
mov BX, [YPosition]
and BX, 0000000000011111b
mov DX, VideoHardwareRamStart
mov DS, DX
and word [SpriteRam+0x0006], 1111111111100000b
or [SpriteRam+0x0006], AX
and word [SpriteRam+0x0000], 1111111111100000b
or [SpriteRam+0x0000], BX
hlt
jmp x_movement_start
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
palette:
dw 0x0000, 0x1111, 0x2222, 0x3333
dw 0x4444, 0x5555, 0x6666, 0x7777
dw 0x8888, 0x9999, 0xAAAA, 0xBBBB
dw 0xCCCC, 0xDDDD, 0xEEEE, 0xFFFF
;========================================================================
;Section Reset
;========================================================================
section reset start=0x7FFF0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section RAM
;========================================================================
segment bss start=0xE0000 vstart=0 nobits align=16
;Define Stuff:
XDirection resb 2
YDirection resb 2
XPosition resb 2
YPosition resb 2
WorkRamStart equ 0xE000
VideoHardwareRamStart equ 0xF000
SpriteRam equ 0x8000
PaletteRam equ 0x8800
SpriteControlRam equ 0x9000
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
Sik wrote:
it's a DOS compiler, although if you can tolerate working from DOSBox... :v
So long as DOSBox can be scripted from a makefile on the outside, it should be fine.
Quote:
The problem here is more finding a suitable x86 compiler that doesn't assume DOS or 16-bit Windows (this is relevant for the object format, since the only headerless format is limited to 64KB and toolchains enforce this limit).
Is it really that hard to resolve the relocations in an
MZ format executable to produce a flat binary after linking it?
tepples wrote:
The lack of time and space efficiency of the output of well-known freely distributed C compilers for certain 8- and 16-bit targets, perhaps.
Which is partially why I don't use C for making games on older consoles. At least, not for now.
Is assembly even really that hard anyway? People always talk about compatibility, but as far as I see, most all things use x86 processors now, aside for cellphones that use ARM processors, but a lot of software on those isn't also on PC or BS4 or whatever.
Espozo wrote:
Is assembly even really that hard anyway?
Yes, it is. Movement away from assembly has permitted huge, huge gains in the scope of what we can do in games today. Portability is hardly the only reason, for many it is a rather unimportant factor.
Talking about portability for M92 homebrew is kind of dumb (・~・) Although asm on modern hardware is indeed a bad idea, not only opcodes are a mess, but there's so much to take into account during optimization that any even remotely decent compiler will beat anything you can do by at least 100x while keeping your original source code way more readable.
Now as I said, old school 16-bit x86 assembly is not anywhere as painful as later processors, and memory here is limited, so it's not that bad of an idea in this specific case.
Sik wrote:
Talking about portability for M92 homebrew is kind of dumb (・~・)
I think the idea is that you write platform-independent game logic (level format decoding, physics, enemy behavior, and music sequence interpretation) in C, then you make specific graphics and sound output modules for each platform so that you can put out the same game on Genesis, Neo Geo, CPS, M92, GBA, PC, etc. It appears to have worked for Koei's multi-platform military simulators.
Sik wrote:
Although asm on modern hardware is indeed a bad idea, not only opcodes are a mess, but there's so much to take into account during optimization that any even remotely decent compiler will beat anything you can do by at least 100x while keeping your original source code way more readable.
Yeah, now that I think about it, I don't want to look through a 1 billion opcode list every time I want to do something just to make sure there is an instruction for it so I don't have to do multiple simpler ones because it would be slower. I still think that people trying to use C on the NES can get a grip. You know though, one thing I've always wondered is how memory is managed on modern systems In how a program doesn't load a part of ram that is already being used, like if two different programs where both loading from 0x8000, one of them would carry a messed up value.
tepples wrote:
Sik wrote:
Talking about portability for M92 homebrew is kind of dumb (・~・)
I think the idea is that you write platform-independent game logic (level format decoding, physics, enemy behavior, and music sequence interpretation) in C, then you make specific graphics and sound output modules for each platform so that you can put out the same game on Genesis, Neo Geo, CPS, M92, GBA, PC, etc. It appears to have worked for Koei's multi-platform military simulators.
I don't see anyone porting any potential new M92 games... Unless it's to the M107.
Espozo wrote:
You know though, one thing I've always wondered is how memory is managed on modern systems In how a program doesn't load a part of ram that is already being used, like if two different programs where both loading from 0x8000, one of them would carry a messed up value.
On a modern PC, different programs run in different banks. This is thanks to the MMU, which is like an MMC but offers even finer-grained bank switching.
MS-DOS programs came in two formats: COM and MZ. COM programs are "position-independent", meaning they're fed a starting segment, and all memory accesses are relative to that. MZ programs are "relocatable", meaning they're patched during loading to start at a given segment by adding a constant value to each 16-bit word in the program that is marked in the executable's header as a segment number.
Espozo wrote:
You know though, one thing I've always wondered is how memory is managed on modern systems In how a program doesn't load a part of ram that is already being used, like if two different programs where both loading from 0x8000, one of them would carry a messed up value.
There's a number of ways to do this. The hardware might support some sort of paging table that remaps virtual RAM pages to real memory locations, and switch to a different table for each program. If you don't have a hardware feature like that, there are lots of alternatives. You could copy out RAM to some other location when you switch between programs. On the NES, you could have a banked WRAM region and let each program have a bank. You can also create an executable format with linking information that lets the operating system patch the executable when loaded to use a dynamically allocated section of RAM. etc.
Edit: I guess tepples was answering at the same time.
So, every program has the "relocatable" code in software, right? Couldn't you make a program to where it just loads 0x00000 and it loads there, no relocatable stuff or anything?
(Still though, back to the main subject, does anyone know what's wrong?)
Espozo wrote:
So, every program has the "relocatable" code in software, right? Couldn't you make a program to where it just loads 0x00000 and it loads there, no relocatable stuff or anything
On some implementations, maybe, though this is obvious a security problem. (Lots of multitasking implementations were very insecure, though.) With an
MMU, the program doesn't have to be "relocatable". The hardware relocates it automatically, and the program can operate as if it was at a fixed address. By the same token, the program shouldn't have permission to access other programs' memory pages. Only the operating system should be allowed to do that.
Well, the problem I was originally going at is "you need something that lets you inject your own header" (in particular, you'll want the vector table with the correct addresses)
rainwarrior wrote:
Espozo wrote:
So, every program has the "relocatable" code in software, right? Couldn't you make a program to where it just loads 0x00000 and it loads there, no relocatable stuff or anything
On some implementations, maybe, though this is obvious a security problem. (Lots of multitasking implementations were very insecure, though.) With an
MMU, the program doesn't have to be "relocatable". The hardware relocates it automatically, and the program can operate as if it was at a fixed address. By the same token, the program shouldn't have permission to access other programs' memory pages. Only the operating system should be allowed to do that.
So is it actually impossible to have it to where a program always loads from 0x08000 or something? I'm not sure what use this would have though. Wait, how do you access hardware registers then?
Espozo wrote:
Wait, how do you access hardware registers then?
That's usually the job of the operating system (e.g. drivers). Most hardware resources (CPU core, RAM, the mouse, the screen, a printer, etc.) are a thing that only one program can use at a time. They need a manager (like the OS) to work this out.
Alright, now back to the topic...
Hello?
Did you ever do anything with the BGs lidnariq? I still don't have a clue as to why I can't display sprites.
Wait, does the sprite DMA maybe have to happen during vblank? I don't have a clue, I just really don't know what I'm doing wrong. Maybe this should be simple enough to find out with the Mame debugger... Actually, scratch that. I forgot that it was displaying the wrong mnemonics and it would probably be easier to just mess around with stuff. Would the Mame source code give any information on this? (Has anyone even looked at my sprite code yet?)
Well, I tried to put the part where it DMAs the sprite data in vblank, and still nothing happened. Well, actually, I looked at palette ram in the palette ram viewer in Mame, and even though I only transferred 16 colors, for some reason, It transferred 3 more, which where black (well, I'm not actually sure if it transferred black or not considering it was black to begin with) a low brightness red (1-2) and a slightly less low brightness red (2-4) in that order. I have no clue how that happened, and I got rid of the code in vblank, and I noticed that it was transferring 2 blacks instead. (Like I said, I'm not sure if it's transferring the first one because the first color in a color palette in Mame is normally black). I have no clue what's going on.
Code:
;========================================================================
;Section IVT
;========================================================================
cpu 80186
section ivt start=0 ; you will need this later
idt:
dw diverr_handler, (section.code.start >> 4)
dw brk_handler, (section.code.start >> 4)
dw nmi_handler, (section.code.start >> 4)
dw int3_handler, (section.code.start >> 4)
dw into_handler, (section.code.start >> 4)
dw bound_handler, (section.code.start >> 4)
dw undefinst_handler, (section.code.start >> 4)
dw nocoprocessor_handler, (section.code.start >> 4)
dw vbl_handler, (section.code.start >> 4)
dw sprbuf_handler, (section.code.start >> 4)
dw raster_handler, (section.code.start >> 4)
dw sound_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw coprocessorerror_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw def_handler, (section.code.start >> 4)
dw vbl_handler, (section.code.start >> 4)
dw sprbuf_handler, (section.code.start >> 4)
dw raster_handler, (section.code.start >> 4)
dw sound_handler, (section.code.start >> 4)
;========================================================================
;Section Code (Pointers)
;========================================================================
section code vstart=0 align=16
diverr_handler:
iret
brk_handler:
iret
nmi_handler:
iret
int3_handler:
iret
into_handler:
iret
bound_handler:
iret
undefinst_handler:
iret
nocoprocessor_handler:
iret
vbl_handler:
mov DX, VideoHardwareRamStart
mov DS, DX
mov word [SpriteControlRam+0x0008], 0x0001
mov DX, WorkRamStart
mov DS, DX
iret
sprbuf_handler:
iret
raster_handler:
iret
sound_handler:
iret
def_handler:
iret
coprocessorerror_handler:
iret
;========================================================================
;Section Code (Main Code)
;========================================================================
main_code:
mov DX, WorkRamStart
mov DS, DX
mov word [XDirection], 0x0001
mov word [YDirection], 0x0001
mov word [XPosition], 0x0000
mov word [YPosition], 0x0000
mov DX, VideoHardwareRamStart
mov DS, DX
mov word [SpriteRam+0x0002], 0x0001
;Palette Upload
mov SI, (section.data.start >> 4)
mov DS, SI
mov SI, palette
mov DI, VideoHardwareRamStart
mov ES, DI
mov DI, PaletteRam
mov CX, 16
cld
rep movsw
mov DX, WorkRamStart
mov DS, DX
sti ;Enable Interupts
x_movement_start:
mov CX, BallVelocity
x_movement_loop:
mov AX, [XDirection]
add [XPosition], AX
mov AX, ScreenWidth+BallDiameter
cmp [XPosition], AX
ja x_out_of_bounds
loop x_movement_loop
jmp y_movement_start
x_out_of_bounds:
not word [XDirection]
mov AX, [XDirection]
add [XPosition], AX
loop x_movement_loop
y_movement_start:
mov CX, BallVelocity
y_movement_loop:
mov AX, [YDirection]
add [YPosition], AX
mov AX, ScreenHieght+BallDiameter
cmp [YPosition], AX
ja y_out_of_bounds
loop y_movement_loop
jmp prepare_for_next_frame
y_out_of_bounds:
not word [YDirection]
mov AX, [YDirection]
add [YPosition], AX
loop y_movement_loop
prepare_for_next_frame:
mov AX, [XPosition]
and AX, 0000000000011111b
mov BX, [YPosition]
and BX, 0000000000011111b
mov DX, VideoHardwareRamStart
mov DS, DX
and word [SpriteRam+0x0006], 1111111111100000b
or [SpriteRam+0x0006], AX
and word [SpriteRam+0x0000], 1111111111100000b
or [SpriteRam+0x0000], BX
hlt
jmp x_movement_start
;========================================================================
;Section Data
;========================================================================
section data vstart=0 align=16
palette:
dw 0x0000, 0x1111, 0x2222, 0x3333
dw 0x4444, 0x5555, 0x6666, 0x7777
dw 0x8888, 0x9999, 0xAAAA, 0xBBBB
dw 0xCCCC, 0xDDDD, 0xEEEE, 0xFFFF
;========================================================================
;Section Reset
;========================================================================
section reset start=0x7FFF0 vstart=0
cli
jmp (section.code.start >> 4):main_code
times 16-($-$$) db 0
;========================================================================
;Section RAM
;========================================================================
segment bss start=0xE0000 vstart=0 nobits align=16
;Define Stuff:
XDirection resb 2
YDirection resb 2
XPosition resb 2
YPosition resb 2
WorkRamStart equ 0xE000
VideoHardwareRamStart equ 0xF000
SpriteRam equ 0x8000
PaletteRam equ 0x8800
SpriteControlRam equ 0x9000
ScreenWidth equ 320
ScreenHieght equ 240
BallDiameter equ 8
BallVelocity equ 2
Well, for the one person who's interested, thanks to lidnariq'a magical powers, I got BGs to work!
One strange thing about the BGs on the M92 is that they start 2 pixels more to the right relative to the lower number BG, which is why you see a repeated pattern of 3 pixels when the tile only had one in the bottom right corner.
Attachment:
BG Starting Offset.png [ 3.23 KiB | Viewed 2489 times ]
It just occured to me that you never initialize SS or SP, so when the interrupt fires, it pushes the return value to nowhere, and when it tries to return, it reads random values from ROM.
I'm (unsurprisingly) taking a break right now from this, but I just want to acknowledge that I heard you. What do SS and SP do though? (One thing I'm really not a fan about x86 is that there are so many registers, but only a handful of them (AX, BX, CX, and DX) actually appear useful... I don't remember the 65816 having that much garbage, and the amount of useful registers is about the same.
SP is equivalent to the 65816's S register—it's used by push, pop(=pull), call, return, and so on.
SS ("Stack Segment") is like CS ("Code Segment"), DS ("Data Segment"), or ES ("Extra Segment"). But for the stack. (SP and BP are by default paired to SS)
Basically: SP is the stack pointer, SS is its segment.
May I ask you what assembler you're using? I've been interested in learning x86 for a while now. (And I'm also too lazy to look through 8 pages)
I'm using yasm. Do I have myself a fellow Irem M92 buddy?
You do now! I got interested when I learned RCT was written in x86 asm though...
Sogona wrote:
RCT
What is "RCT"?
I use nasm when I feel like doing x86 assembly, if anybody wants to know about other assemblers. Just make sure to read the manual regarding the current mode (although I think the default is 16-bit code starting at 0000h, good enough for real mode).
I think I remember not using nasm because of the BS compiling thing it wanted me to do.
Espozo wrote:
Sogona wrote:
RCT
What is "RCT"?
Some
theme park management simulator game.
He got interested in development for an arcade machine by a roller coaster video game?
Probably by the fact that the roller coaster video game was written entirely in assembly language, not something more modern like Pascal.
I'm just wondering why he'd pick this over a PC, but I suppose the answer is the same as if you asked anyone why they would rather develop games for a video game console vs. a PC, seeing that this is practically a video game console.
Espozo wrote:
He got interested in development for an arcade machine by a roller coaster video game?
Have you ever played it? It's amazing. If you like SimCity I guarantee you'll enjoy it
Espozo wrote:
He got interested in development for an arcade machine by a roller coaster video game?
It got me interested in x86 in general. But after seeing all the games on the m92 I'm interested in it as well.
I forgot that x86 was an entire family of languages instead of just one. Strange to think that the same instruction set Windows was written in is also on arcade machines.
Do you need any help trying to get started, or information about the M92? I'm sure you could read through the pages if you're that interested. I've taken a little break, but I'll get back to it at some point. I have a dumb school summer project that I've been working on, and that's taken up a lot of my time. I'd also grown frustrated with not being able to display sprites, although I did get BGs to display if you saw that. Have you given up on NES, or are you going to do both, like me with the SNES and the Irem M92.
Probably the most popular game on the Irem M92 that I know is Ninja Baseball Batman, although In the Hunt is another big one.
Espozo wrote:
Do you need any help trying to get started, or information about the M92? I'm sure you could read through the pages if you're that interested. I've taken a little break, but I'll get back to it at some point. I have a dumb school summer project that I've been working on, and that's taken up a lot of my time. I'd also grown frustrated with not being able to display sprites, although I did get BGs to display if you saw that. Have you given up on NES, or are you going to do both, like me with the SNES and the Irem M92.
Probably the most popular game on the Irem M92 that I know is Ninja Baseball Batman, although In the Hunt is another big one.
Idk it'll probably be a little side project. If you have any information on it I'd love to see it.
Sogona wrote:
Idk it'll probably be a little side project. If you have any information on it I'd love to see it.
Nothing really outside of what's here. I don't know C++, and unfortunately, that's what the documentation for this is in, as it's the Mame source code. I have a little thing I just threw together, and I can tell you about it tomorrow when it's not 11:00 PM.
Here's something decent that I've found (I think some relating to sprites are wrong on it though, like how $F9008 "Clears Sprite Ram")
http://patpend.net/technical/arcade/m92.htmlHere's just about all you'll need, and I'll tell you what to do tomorrow:
Attachment:
Irem M92 Development.zip [1.95 MiB]
Downloaded 65 times
Espozo wrote:
I think I remember not using nasm because of the BS compiling thing it wanted me to do.
What? o_o I never had any problems like that (just make sure to pass -f bin to ensure it'll build a raw binary instead of something that needs a linker)
and now on a semi-completely unrelated note, seeing this:
Espozo wrote:
Code:
start:
mov XDirection, 0x0001
mov YDirection, 0x0001
mov XPosition, 0x0000
mov YPosition, 0x0000
reminded me of something I've been wondering for a while.
why does 0x denote a hexadecimal value? or is it used to denote a hexadecimal address?
Answers to
this question attempt to explain why 0x was chosen.