Mind if I join in as a assembly and MSX dev noob? I'm experimenting with some basic assembly (loops, conditionals) and MSX (the keyboard in this case). So I started experimenting with waiting for a keypress:
keys EQU 0xFBE5 // NEWKEY memory area waitforkey ld b,11 // 11 rows total to scan ld hl, keys .loop ld a,(hl) // read the bits from the keyboard cp 255 jp nz,.keypressed // keypressed inc hl // next part of keyboard to scan djnz .loop jp waitforkey .keypressed: ret
I'm trying to initialize screen 2 en define one tile, but something seems wrong. I'm only changing VDP R0 and R1 to change to screen 2, and that works. According to most docs the addresses for the charachter table, name table, and color table should be 0000H, 1800H and 2000H, but there not (according to openmsx debugger). Should I set R2 - R6 myself to get those addresses or am I missing something else?
Yes, if you want to change the screen mode by writing VDP registers, then you have to do everything yourself. Which includes setting the correct table addresses and initialising VRAM when needed.
Not sure what your question is, but here are some tips:
keys EQU 0xFBE5 // NEWKEY memory area waitforkey ld b,11 ld hl, keys .loop ld a,(hl) inc a // INC A can be used instead of CP 255 if you do not further process the value in A ret nz // instead of jumping to a RET, do a conditional RET inc l // use the (faster) INC L instead of INC HL: the LSB never overflows (0xFBE5 + 11 = 0xFBF0) djnz .loop halt // the NEWKEY area is updated in the bios ISR, so give the z80 a break jr waitforkey // I'd use JR here instead of JP when speed is not an issue, as its opcode is shorter
MOA: halt here is really not much more than a waste of a byte. The z80 can't actually take a 'break' and there is no timing advantage.
However, as the comment says, this table is updated in the BIOS ISR, so interrupts must be turned on for this routine to work.
Oh, I had no questions about the waiting for the keypress. Just something I posted for others (newbies like me) to see. But thanks for the tips, I had not discovered "ret nz" and "halt" yet
MOA: halt here is really not much more than a waste of a byte. The z80 can't actually take a 'break' and there is no timing advantage.
However, as the comment says, this table is updated in the BIOS ISR, so interrupts must be turned on for this routine to work.
I guess you are right. Modern processors would enter some low power state to save power consumption when a halt is encountered. If a z80 doesn't do that, just keep polling that buffer and save a byte
Ok. Here is the collision detection routine I made (and use) for my little project. It is based on the collision detection routine of Daniel Bienvenu (so real credits are for him).
Perhaps it is usefull for other beginners (or fun for the experts to laugh at )
; ============================= ; collision ; ; checks colliosn between 2 sprites. ; ; IX contains the npc sprite ; IY contains the mc/bullet/weap sprite ; ; result is returned in the C flag ; ; ; ============================= collision: ld a,(IX+_NPC_y) ; y pos + 32 + offset add a,32 ; add 32 because the sprite can be outsid the top of the screen add a,(IX+_NPC_yoffset) ld e,a ld d,0 ; result in de ld a,(IY+_MC_y) add a,32 add a,(IY+_MC_yoffset) ld l,a ld h,0 ; result in hl ld b,(IX+_NPC_ysize) or a ; reset C flag for sbc sbc hl,de ; substract hl with de jr nc, 1f ; HL >= DE ld b,(IY+_MC_ysize) add hl,de ; restore sbc action before swapping ex de,hl or a sbc hl,de 1: ld a,l ; holds the y axis difference between collisions windows cp b ; inside the collision window? jr nc, endcollision ; no collision possible end the check ld a,(IX+_NPC_x) ; x pos + 32 + offset add a,32 ; add 32 because the sprite can be outsid the top of the screen add a,(IX+_NPC_xoffset) ld e,a ld d,0 ; result in de ld a,(IY+_MC_x) add a,32 add a,(IY+_MC_xoffset) ld l,a ld h,0 ; result in hl ld b,(IX+_NPC_xsize) or a ; reset C flag for sbc sbc hl,de ; substract hl with de jr nc, 2f ; HL >= DE ld b,(IY+_MC_xsize) add hl,de ; restore sbc action before swapping ex de,hl or a sbc hl,de 2: ld a,l ; holds the x axis difference between collisions windows cp b endcollision: ; carry holds the result -> C = collide, NC = nocollide. ret
That looks rather complicated for what it needs to do. I optimised the hell out of the collision checker for Universe: Unknown since it had to do a lot of them and found that for an 8 bit check you actually only need a few instructions. It takes much more cpu to actually get the data from memory.
; ; Collision ; ; Check for collision between objects ; ; IN: IX - first object record ; IY - second object record ; OUT: Carry set = collision ; Collision ld b,(IX+loc_x) add (IX+offset_x) ld c,(IX+size_x) ld d,(IY+loc_x) add (IY+offset_x) ld e,(IY+size_x) call CalcCollision ret nc ld b,(IX+loc_y) add (IX+offset_y) ld c,(IX+size_y) ld d,(IY+loc_y) add (IY+offset_y) ld e,(IY+size_y) ; run through to calccollision ; ; CalcCollision ; ; 1D collision check ; ; IN: B = Location object 1 ; C = Size object 1 ; D = Location object 2 ; E = Size object 2 ; OUT: Carry set = collision ; CalcCollision: ld a,d sub b jr c,.switch ; x1>x2 sub c ret .switch: neg sub e ret
Note: This is strictly 8 bit. So you'll need to fiddle with it a bit in order to support EC.
You can even remove the "ld a,d" by using a for input directly. Of course, it's much more efficient to evaluate the storage of data to make retrieval quicker.
Erh, are U sure this should work? "add (IX+offset_y)" has no effect as 'a' is not used....