|
ForumsSega Master System / Mark III / Game GearSG-1000 / SC-3000 / SF-7000 / OMV |
Home - Forums - Games - Scans - Maps - Cheats - Credits Music - Videos - Development - Hacks - Translations - Homebrew |
Author | Message |
---|---|
|
Sega Genesis 68K assembly programming
Posted: Thu Feb 24, 2022 9:49 pm
|
Although this is not a forum strictly about Sega Genesis - but maybe someone here has knowledge about ASM 68K. ?
I am learning 68000 assembler and due to the fact that I am just starting my adventure with it, I have many not entirely clear things related to this language. I am using ASM68K. I wrote code to copy 512 bytes from WRAM to VRAM: SetVRAMWrite 0xF000 ; copy 512 bytes from FF0000-FF01FF (WRAM) to F000-F1FF (VRAM) - 128xL=512 LEA 0x00FF0000,A0 ; MOVE.L #$00FF0000,A0 MOVE.B #128-1,D0 LOOP: MOVE.L (A0)+,$00C00000 ; vdp_data ; MOVE.L (A0)+,($00C00000) DBF D0,LOOP ; DBF/DBRA ;----------------------------------------- SetVRAMWrite: macro addr move.l #(vdp_cmd_vram_write)|((\addr)&$3FFF)<<16|(\addr)>>14, vdp_control endm vdp_control equ 0x00C00004 vdp_data equ 0x00C00000 vdp_cmd_vram_write equ 0x40000000 ;----------------------------------------- The code copies 512 bytes correctly, but my question is related to the D0 register in this case - changing to D1 causes a bad code execution which copies 1536 bytes to VRAM (384xL=1536) - what is the reason? From what I read, the Dn register can be any counter. Interestingly, using a different register, eg D4,5,6,7 copies some strange values to VRAM and freezes the code. Changing to D2 works like D0 (everything is correct), D3 works like D1, and D4 + writes strange values to VRAM |
|
|
Posted: Thu Feb 24, 2022 10:13 pm |
The DBcc instructions use the lower word of the register as the counter, and you're only initializing the lower byte. So, change
MOVE.B #128-1,D0
into MOVE.W #128-1,D0
and then it should work in any register. |
|
|
Posted: Fri Feb 25, 2022 10:38 am |
That's right, thanks a lot. Now any Dn register works - I wonder why exactly D0, D2 was ok? Probably they just had their higher bits completely reset, while D1, D3 + just had some remnants of data from previous calculations there?
D1 copied 3x more data, so the value # $ 02 was possible in its next byte (MSB), which resulted in 1536 bytes being copied. On the other hand, the further registers, eg D4 +, had some large values which made a huge loop resetting the entire WRAM (suspending the code). ? |
|
|
Posted: Fri Feb 25, 2022 5:51 pm |
Yep, that's why. |
|
|
Posted: Sat Feb 26, 2022 9:33 am |
I have one more thing that I am not sure about because I have not read this example anywhere:
For example, in register D0 I have the value: # $ FFFF0123 Now performs a right shift using LSR.W D0 Will only the lower 16-bits of D0 be shifted and the higher 16-bits completely omitted from this operation? Mainly I wonder if, for example, in this case bit16 will not jump into place bit15 when I execute LSR.W - will "W" ONLY move bits on the word range? |
|
|
Posted: Sat Feb 26, 2022 11:11 am |
Yes. |
|
|
Posted: Mon Feb 28, 2022 12:12 pm |
I noticed that the BCC command works a bit differently compared to the SMS (or I am doing something wrong) :
For example, I have a collision of two sprites in Z80 (SMS) of 16x16 pix size, additionally the value # 15 is added so that the collision is when the walls of both objects touch each other. The same code for 68K causes the collision to be performed when the walls of both objects overlap each other with 2 pixels - that is, completely different. Why is this the case with BCC ? (using BHI executes the code correctly). LD A,(SPRITE_A_X) ADD A,15 LD B,A LD A,(SPRITE_B_X) CP B JR NC,SPRITE_B_vs_SPRITE_A_Coll_no LD A,(SPRITE_B_X) ADD A,15 LD B,A LD A,(SPRITE_A_X) CP B JR NC,SPRITE_B_vs_SPRITE_A_Coll_no LD A,(SPRITE_A_Y) ADD A,15 LD B,A LD A,(SPRITE_B_Y) CP B JR NC,SPRITE_B_vs_SPRITE_A_Coll_no LD A,(SPRITE_B_Y) ADD A,15 LD B,A LD A,(SPRITE_A_Y) CP B JR NC,SPRITE_B_vs_SPRITE_A_Coll_no ; a collision will occur when the sides of both objects touch each other SPRITE_B_vs_SPRITE_A_Coll_no: MOVE.W (SPRITE_B_X),D0 ADDI.W #15,D0 MOVE.W (SPRITE_A_X),D1 CMP.W D0,D1 BCC.S SPRITE_B_vs_SPRITE_A_Coll_no MOVE.W (SPRITE_A_X),D0 ADDI.W #15,D0 MOVE.W (SPRITE_B_X),D1 CMP.W D0,D1 BCC.S SPRITE_B_vs_SPRITE_A_Coll_no MOVE.W (SPRITE_B_Y),D0 ADDI.W #15,D0 MOVE.W (SPRITE_A_Y),D1 CMP.W D0,D1 BCC.S SPRITE_B_vs_SPRITE_A_Coll_no MOVE.W (SPRITE_A_Y),D0 ADDI.W #15,D0 MOVE.W (SPRITE_B_Y),D1 CMP.W D0,D1 BCC.S SPRITE_B_vs_SPRITE_A_Coll_no ; a collision will take place when the sides of both objects overlap by 2pix (instead of when they touch each other) SPRITE_B_vs_SPRITE_A_Coll_no: Of course, there are also differences in the coordinates of objects on the screen, because in SMS the sprite moves in the 1-byte range (000-255), while Genesis on 2-byte (128-439 - when we have a 256x224 screen). I also tried to subtract # 128 before loading the entries into the registers so that the range was in 1-byte (000-255), but then CMP.B D0, D1 and BCC work exactly as before. |
|
|
Posted: Mon Feb 28, 2022 12:37 pm |
I don't want to discourage you, so please read my post in a positive way: if you plan to have more technical questions about Mega Drive / Genesis programming, you're way more likely to receive valuable answers on the Gendev forum. | |
|
Posted: Mon Feb 28, 2022 3:30 pm |
I started a topic here, because both on the Sega-16 forum and on the one you mention (SpritesMind), I CANNOT register - in both cases I have been waiting for administrative confirmation for many days. | |
|
Posted: Mon Feb 28, 2022 5:01 pm |
That sucks, I couldn't know that. I wish I could help you more, but my knowledge of the 68000 is extremely limited. | |
|
Posted: Mon Feb 28, 2022 5:03 pm |
There is also Plutiedev discord (MD specific) | |
|
Posted: Mon Feb 28, 2022 6:48 pm |
Is your name siudym on sega 16 to?I can leave a message for admin. | |
|
Posted: Mon Feb 28, 2022 8:16 pm |
Yes. | |
|
Posted: Tue Mar 01, 2022 4:18 am |
68K has a lot of branch conditions to use compared to Z80. See page 10 here, it's helpful:
http://wpage.unina.it/rcanonic/didattica/ce1/docs/68000.pdf Depending if you are doing signed or unsigned comparisons there's different branch conditions to use. That doc explains the differences between BHI (>) and BCC (>=), etc. |
|
|
Posted: Tue Mar 01, 2022 6:41 am |
You know the internet is complete when someone from the other side of the world pulls a document from an Italian university 😁 | |
|
Posted: Wed Mar 02, 2022 9:52 pm |
No changes so far with account activation.
It's hard for me to understand these addressing modes at the moment. I have such an example - a byte table in ROM, I would like to read it using indexing - I don't know if you can use Dn as index registers? MOVE.W TABLE,A0 MOVE.W A0,D1 ; D1 = 0088 MOVE.W TABLE+2,A0 MOVE.W A0,D1 ; D1 = 0099 MOVE.W TABLE+2(PC),D1 ; D1 = 0099 TABLE: DC.W 0x0088,0x0099,0x00AA,0x00BB,0x00CC,0x00DD For example, by adding certain values to TABLE, this can be read, but I would like to replace it with a register such as D0: MOVE.W #$0002,D0 ; can I add the contents of D0 to TABLE as index? MOVE.W TABLE,A0 MOVE.W A0,D1 |
|
|
Posted: Thu Mar 03, 2022 5:51 am |
To use a data register as an index you need to use this addressing mode: "(disp8,An,Xn)"
where disp8 is 8-bit signed displacement An is base address register, or PC Xn is an index register that can be any of Dn or An. If you don't specify a displacement it is zero. For example: lea table, a0
move.w #index, d0 move.w (a0, d0.w), d1 ; read from memory location An[Xn+disp8] In this example An=table, Xn=D0.w (lower 16 bits of register) and disp8=0, thus it is reading from memory address table+d0. The assembler knows you really meant: move.w 0(a0, d0.w), d1
Note that the index register can be 16 bits (d0.w here) or 32 bits (d0.l) It just depends how big the table you are addressing is. If it's less than 64K you can use .w, if it's bigger you can use .l But for a table of words you'll will want to multiply the index by 2. The 68000 can't read a word from an odd offset: lea table, a0
lsl.w #1, d0 ; convert index in range 0000-00ff to 0000-01fe move.w (a0, d0.w), d1 ; read from memory location (table+d0) The signed 8-bit displacement is more important when using PC-relative mode as the assembler puts the distance to the table in the displacement. So the table has to be within -128/+127 bytes of the code that uses it. This puts some constraints on where the table is and how big it can be. move.w #index, d0
lsl.w #1, d0 move.w table(pc, d0.w), d1 The assembler understands that by specifying the label "table" as a displacement, what you really want to store is the 8-bit signed delta between the current value of the program counter and the absolute address of the table. Typically you'd do the PC-relative access close to a return or branch instruction so the table could immediately follow it. This helps keep the displacement within range: ; convert value in d0 (range 0-F) to ASCII character '0'-'9', 'A'-'F' to_ascii: andi.b #0x0F, d0 move.b hextable(pc, d0.w), d0 rts hextable: dc.b "0123456789ABCDEF" Here hextable is close to the instruction that references it. If you had a lot of code before the rts eventually the assembler would complain that the displacement was too big. |
|
|
Posted: Thu Mar 03, 2022 6:36 am |
Thanks, that will clear things up a bit.
I did something wrong before, because now the code works: MOVE.W #$0002,D0 LEA Table,A6 ADD.W D0,A6 MOVE.B (A6),D1 ; D1 = $22 Table: DC.B 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77 MOVE.W #$0006,D4 LEA TABLE,A0 ADD.W D4,A0 MOVE.W (A0),D4 ; D4 = 00BB TABLE: dc.w 0x0088,0x0099,0x00AA,0x00BB,0x00CC,0x00DD |
|
|
Posted: Fri Mar 04, 2022 1:04 pm |
I often see stack pointer (in tutotials) set to 0x00FFE000 - does it have any important meaning? Is there any difference when I set higher values, e.g. to 0x00FFF000? | |
|
Posted: Sat Mar 05, 2022 4:08 pm |
The Genesis has 64K work RAM at 0xFF0000-0xFFFFFF and you can place the stack pointer anywhere you want within that range. Since the stack grows downward it's usually placed near the top. You can also set the stack pointer to zero as the first word pushed goes to SP-2 or 0xFFFFFE. Some games leave a little spare RAM at the top like 128 bytes to store settings that will persist after a reset such as high score tables and have the initialization code zero out all RAM except for that top part. The stack pointer could be set to 0xFFFFxx in this case. Then the startup code can check for a warm or cold boot (typically by checking the state of the TH pin direction from the I/O chip as Sega recommends, or by looking for a distinct pattern in the spare RAM area) to determine if the contents are valid or not. That way settings can survive a reset. |
|
|
Posted: Wed Mar 09, 2022 9:57 pm |
Thanks for the info.
I've read a lot about Genesis programming - very interesting architecture. I still have a lot of problems to deal with Yamaha YM2612 programming - are there any libraries like PSGLib but for this YM chip? So far I have only found the ECHO SOUND ENGINE. My first demo, unfortunately I can't test it on real hardware: https://dl.dropboxusercontent.com/s/gwvn2v0gud4lh95/platform_demo_genesis.bin |
|
|
Posted: Wed Mar 09, 2022 10:18 pm |
There is a number of tools which target the so-called SMPS engine, which is the one used by the main Sonic games, though I'm not sure how easy it would be to port the SMPS engine to a homebrew project. The official GEMS development kit has been made available, too, and it might be easier to implement, too bad that it tends to sound like crap (think Sonic Spinball). Not sure if there's anything beyond those. | |
|
Posted: Thu Mar 10, 2022 1:32 am |
I know people make really good Genesis music with the DefleMask tracker (deflemask.com), it can write out VGM files but seems to have a 'Save ROM' option too. I wonder if that means it has a real 68000 replay routine in the ROM or if it's just an embedded VGM player.
Regardless though it doesn't handle what you'd need for a game with sound effects, but I wonder if it couldn't be the basis of a sound engine or at least a way to start experimenting with in-game audio before moving to something of your own design. |
|
|
Posted: Thu Mar 10, 2022 10:48 am |
Does the processor automatically secure the processor flags status (SR/ CCR) before executing an interrupt, eg VBlank, or do it have to be done manually? | |
|
Posted: Thu Mar 17, 2022 12:54 pm |
At first I couldn't understand how ECHO SOUND ENGINE works, but in the end it worked and it works beautifully :)
https://dl.dropboxusercontent.com/s/00kvqwc8orm1eyk/platform_demo_genesis_music.... I have questions related to sending the SAT buffer from WRAM to SAT VRAM - I set the buffer size to 512 bytes (256 words) which is the range for 64 sprites. I send the buffer without using DMA, in the CPU loop in every frame. The ROM running on the console works, also on emulators - so even without DMA, sending the buffer to VRAM outside the VBLANK is done without problems - the only question is whether you can do that, are there any restrictions? I tried to send using DMA, but I am doing something wrong and here I would like to advise you on how to do it correctly. SATBuff Send Code to SAT VRAM Directly, No DMA: (simple_platformer_genesis_DIRECT_SAT.bin) ;------------------------------ Forever: BSR WaitVSync ; synchronize speed BSR UpdateSAT ; game mechanics BRA Forever ;------------------------------ WaitVSync: MOVE.L (VarVSync),D0 ; Read value from VarVSync into D0 WaitVSync_: MOVE.L (VarVSync),D1 ; Read value from VarVSync into D1 CMP.L D0,D1 ; Compare D0 and D1 BEQ WaitVsync_ ; If result is 0 the value has not been changed ; so jump back to 1 RTS ;------------------------------ INT_VInterrupt: ; Vertical interrupt - run once per frame (50hz in PAL, 60hz in NTSC) ADD.L #1,(VarVSync) RTE ;------------------------------ UpdateSAT: ; ### Runs on real hardware too, sends SATBuff directly to SAT VRAM ### SetVRAMWrite 0xF000 ; copy 128 long-words to SAT (512 bytes = 64 sprites) LEA 0x00FF0000,A0 MOVE.W #128-1,D0 UpdateSAT_Loop: MOVE.L (A0)+,$00C00000 ; vdp_data DBF D0,UpdateSAT_Loop ; DBF/DBRA RTS DMA version code, working bad: (simple_platformer_genesis_DMA_SAT.bin) ;------------------------------ UpdateSAT_: ; ### It works incorrectly, not on every emu and probably it will not work on hardware ### ; MOVE.W #$8174,($C00004) ; VDP Register #1, Enable DMA MOVE.W #$0100,D0 ; Word's to Send MOVE.L #$70000003,D2 ; VRAM destination address LEA $00FF0000,A0 ; source address JSR DoDMAtoVRAM ; MOVE.W #$8164,($C00004) ; VDP Register #1, Disable DMA RTS ;------------------------------ DoDMAtoVRAM: MOVE.L #$C00004,A1 ; D0 = WORD's to transfer MOVE.L #$94009300,D1 ; D2 = VRAM address OR.B D0,D1 ; A0 = Source address LSR.W #8,D0 SWAP D0 CLR.W D0 OR.L D0,D1 MOVE.L D1,(A1) MOVE.L A0,D0 LSR.L #1,D0 MOVE.L #$97009500,D1 OR.B D0,D1 LSR.W #8,D0 OR.W #$9600,D0 MOVE.W D0,(A1) ; MID SWAP D0 SWAP D1 AND.B #$3F,D0 OR.B D0,D1 MOVE.L D1,(A1) ; LOW, HIGH OR.B #$80,D2 MOVE.L D2,(A1) RTS |
|
|
Posted: Thu Mar 17, 2022 3:19 pm |
I think you may get better advice and responses on another forum, I think https://gendev.spritesmind.net/forum/ is the best place these days (but I am not very familiar with it). | |
|
Posted: Thu Mar 17, 2022 4:20 pm |
I registered there (a month ago) and unfortunately my account has not been confirmed until today. | |