Forums

Sega Master System / Mark III / Game Gear
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew

View topic - Sega Genesis 68K assembly programming

Reply to topic
Author Message
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Sega Genesis 68K assembly programming
Post 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
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jan 2017
  • Posts: 9
  • Location: Canada
Reply with quote
Post 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.
  View user's profile Send private message
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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). ?
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jan 2017
  • Posts: 9
  • Location: Canada
Reply with quote
Post Posted: Fri Feb 25, 2022 5:51 pm
siudym wrote
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?


Yep, that's why.
  View user's profile Send private message
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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?
  View user's profile Send private message Visit poster's website
  • Joined: 16 May 2002
  • Posts: 1356
  • Location: italy
Reply with quote
Post Posted: Sat Feb 26, 2022 11:11 am
siudym wrote
will "W" ONLY move bits on the word range?
Yes.
  View user's profile Send private message Visit poster's website
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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.
  View user's profile Send private message Visit poster's website
  • Joined: 16 May 2002
  • Posts: 1356
  • Location: italy
Reply with quote
Post 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.
  View user's profile Send private message Visit poster's website
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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.
  View user's profile Send private message Visit poster's website
  • Joined: 16 May 2002
  • Posts: 1356
  • Location: italy
Reply with quote
Post 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.
  View user's profile Send private message Visit poster's website
  • Joined: 04 Jul 2010
  • Posts: 542
  • Location: Angers, France
Reply with quote
Post Posted: Mon Feb 28, 2022 5:03 pm
There is also Plutiedev discord (MD specific)
  View user's profile Send private message
  • Joined: 09 Nov 2021
  • Posts: 12
Reply with quote
Post Posted: Mon Feb 28, 2022 6:48 pm
Is your name siudym on sega 16 to?I can leave a message for admin.
  View user's profile Send private message
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post Posted: Mon Feb 28, 2022 8:16 pm
Yes.
  View user's profile Send private message Visit poster's website
  • Joined: 28 Sep 1999
  • Posts: 1197
Reply with quote
Post 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.
  View user's profile Send private message Visit poster's website
  • Joined: 16 May 2002
  • Posts: 1356
  • Location: italy
Reply with quote
Post 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 😁
  View user's profile Send private message Visit poster's website
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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

  View user's profile Send private message Visit poster's website
  • Joined: 28 Sep 1999
  • Posts: 1197
Reply with quote
Post Posted: Thu Mar 03, 2022 5:51 am
Quote

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:


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.
  View user's profile Send private message Visit poster's website
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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
  View user's profile Send private message Visit poster's website
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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?
  View user's profile Send private message Visit poster's website
  • Joined: 28 Sep 1999
  • Posts: 1197
Reply with quote
Post Posted: Sat Mar 05, 2022 4:08 pm
siudym wrote
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?


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.
  View user's profile Send private message Visit poster's website
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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
  View user's profile Send private message Visit poster's website
  • Joined: 16 May 2002
  • Posts: 1356
  • Location: italy
Reply with quote
Post 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.
  View user's profile Send private message Visit poster's website
  • Joined: 28 Sep 1999
  • Posts: 1197
Reply with quote
Post 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.
  View user's profile Send private message Visit poster's website
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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?
  View user's profile Send private message Visit poster's website
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post 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



  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post 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).
  View user's profile Send private message Visit poster's website
  • Joined: 23 Sep 2021
  • Posts: 100
  • Location: Poland
Reply with quote
Post Posted: Thu Mar 17, 2022 4:20 pm
I registered there (a month ago) and unfortunately my account has not been confirmed until today.
  View user's profile Send private message Visit poster's website
Reply to topic



Back to the top of this page

Back to SMS Power!