Разработка и ромхакинг > Программирование
Ребенок изучает asm [SMD]
SegaMark:
Нужна помощь знатоков ассемблера, может кто нибудь сталкивался с таким или эта какая-то распространенная проблема. Вообщем вот такой код:
--- Код: ---movem.l d0-d2/a0-a1, -(a7)
movea.l #$200000, a1
add.l d0, d0
subq.l #$1, d1
move.b $1(a1, d0.l), d2 ;d0=423e
move.b d2, (a0)+
--- Конец кода ---
проблема в том что размер РОМа у меня 2mb и получается что после этой строки move.b $1(a1, d0.l), d2 данные берутся за приделами РОМ. Как решить эту проблему чтобы он брал данные откуда нужно?
Томахомэ:
--- Цитата: SegaMark от 06 Март 2026, 09:03:28 ---movem.l d0-d2/a0-a1, -(a7)
movea.l #$200000, a1
add.l d0, d0
subq.l #$1, d1
move.b $1(a1, d0.l), d2 ;d0=423e
move.b d2, (a0)+
--- Конец цитаты ---
Вроде в пределах первых 4 МБ адресуемого адресного пространства, если данная область заполнена не полностью, SMD заполненную зеркалит по нескольку раз.
Добавлено позже:
В этом я убедился, просматривая дебаггер MAME на позициях параллельно $0 и $100000 во время выполнения Dune II.
Добавлено позже:
Ровно такая же ситуация с $200000 и $300000.
Добавлено позже:
Процитированный тобой код - это, возможно, форма антипиратской защиты.
SeregaZ:
я не специалист :) но знаю одного типка... с именем Grok :lol: :lol: :lol:
шаг первый - просим добавить комментарии, чтобы самому понимать что там происходит:
--- Код: --- movem.l d0-d2/a0-a1, -(a7) ; Сохраняем регистры d0,d1,d2,a0,a1 в стек
; (push multiple registers) — стандартный пролог
movea.l #$200000, a1 ; a1 = $200000
; Очень часто это начало области VRAM в DMA-режиме
; либо начало таблицы тайлов / спрайтов в RAM
add.l d0, d0 ; d0 *= 2
; Самый частый случай: преобразование номера тайла
; в смещение в байтах (каждый тайл = 32 байта → ×32 = ←8+←5)
subq.l #$1, d1 ; d1 -= 1
; Теперь d1 содержит количество повторений - 1
; (удобно для цикла типа dbra)
move.b $1(a1, d0.l), d2 ; d2 = byte[ a1 + d0 + 1 ]
; Берём второй байт из 16-битного слова по адресу
; (a1 + d0) — скорее всего это старший байт тайла
; или атрибуты тайла (palette, flip, priority)
move.b d2, (a0)+ ; *a0++ = d2
; Записываем этот байт по текущему адресу a0
; и сразу увеличиваем указатель на следующий байт
; После этого кода обычно идёт что-то вроде:
; move.b (a1,d0.l), (a0)+ ; младший байт тайла
; addq.l #2, d0 ; или add.l #32,d0 — следующий тайл
; dbra d1, метка_цикла
--- Конец кода ---
описываем проблему, что вот вылазит за пределы бла бла бла...
--- Цитата ---Проблема, которую ты описываешь, очень типичная при работе с ROM > 1 МБ на Sega Genesis / Mega Drive.
Коротко: у тебя нет ошибки в коде, а есть особенность зеркалирования (mirroring) адресов в железе Genesis при размере ROM 2 МБ.
Почему происходит чтение "за пределами ROM"?
Когда ROM ровно 2 МБ, железо зеркалит его дважды в пространстве $000000–$3FFFFF:
$000000 – $1FFFFF → первая копия (нижние 2 МБ)
$200000 – $3FFFFF → вторая копия тех же самых 2 МБ
То есть адреса $200000 – $3FFFFF физически читают то же самое содержимое, что и $000000 – $1FFFFF.
Поэтому строка:
--- Код: ---movea.l #$200000, a1 ; a1 = $200000
...
move.b $1(a1, d0.l), d2 ; читаем байт с адреса $200001 + d0
--- Конец кода ---
совершенно корректна на реальном железе при 2 МБ ROM — она просто читает из второй половины зеркала.
Когда это действительно ломается?
Чаще всего проблема проявляется в одном из двух случаев:
Эмулятор не реализует зеркалирование 2 МБ ROM (очень многие старые / простые эмуляторы этого не делают)
У тебя ROM не 2 МБ, а меньше (например 1.5 МБ, 1 МБ, 768 КБ и т.д.), но код всё равно обращается к $200000+
Пример универсального варианта (работает при любом размере ROM):
--- Код: --- movem.l d0-d2/a0-a1, -(a7)
; Вариант 1 — всегда безопасно (если данные ≤ 1 МБ)
; movea.l #0, a1 ; или #$10000, #$8000 и т.п. — где реально лежат данные
; Вариант 2 — если точно знаешь, что 2 МБ и хочешь зеркало
movea.l #$200000, a1
add.l d0, d0 ; ×2 (если индекс тайлов в словах → смещение в байтах)
subq.l #1, d1 ; для dbra
.loop
move.b 1(a1, d0.l), d2 ; старший байт
move.b d2, (a0)+
; move.b 0(a1, d0.l), (a0)+ ; младший байт (если дальше в цикле)
addq.l #2, d0 ; или add.l #32,d0 — смотря что за данные
dbra d1, .loop
movem.l (a7)+, d0-d2/a0-a1
rts
--- Конец кода ---
--- Конец цитаты ---
код не знаю для какого ассемблера, да и под вопросом рабочий ли он мне его подсунул, ибо начинаешь влезать в эту тему с ИИ - он из раза в раз пишет ахинею и надо вникать. но вот видео редактор (успешно) и свой аудио драйвер (не очень) я делал именно с помощью ИИ, только что нельзя ему сразу весь код пихать - у него крыша едет. надо разбивать задачу на мелкие кирпичики, а после их самому компоновать в кучу.
SegaMark:
--- Цитата: Томахомэ от 06 Март 2026, 10:40:02 ---если данная область заполнена не полностью, SMD заполненную зеркалит по нескольку раз.
--- Конец цитаты ---
Спасибо за подсказку, да это действительно оказалось зеркалирование, но почему то данные берутся неправильные.
--- Цитата: Томахомэ от 06 Март 2026, 10:40:02 ---Процитированный тобой код - это, возможно, форма антипиратской защиты.
--- Конец цитаты ---
если это так, знаешь какой нибудь способ как ее можно победить
--- Цитата: SeregaZ от 06 Март 2026, 17:28:19 ---шаг первый - просим добавить комментарии, чтобы самому понимать что там происходит:
--- Конец цитаты ---
Он не правильно понял это не чтение тайлов, а чтение некоторых данных для игры. Насколько я понял в РОМ находиться некоторая таблица параметров и эта часть функции последовательно считывает их, но мне не удалось найти это место в РОМ потому что данные берутся не совсем последовательно а через байт.
--- Цитата: SeregaZ от 06 Март 2026, 17:28:19 ---код не знаю для какого ассемблера
--- Конец цитаты ---
Для нашего родного Motorola M68K :lol:
--- Цитата: SeregaZ от 06 Март 2026, 17:28:19 ---да и под вопросом рабочий ли он мне его подсунул
--- Конец цитаты ---
выглядит рабочим
Я заметил еще одну особенность есть модификация данного РОМ где изменен только заголовок, и все работает как надо, данные берутся откуда положено, но только на эмуляторе Bizhawk и Exodus(на остальных не проверял), а мне нужно записать его на картридж и на реальном железе он почему то не работает, видимо ему плевать на заголовок. Как можно исправить эту проблему?
Слева ориг ром. Справа модифицированный
SeregaZ:
а контрольную сумму поправил перед записью? может из-за неё не работает на железе?
Ti_:
--- Цитата: SegaMark от 06 Март 2026, 21:02:43 ---Слева ориг ром. Справа модифицированный
--- Конец цитаты ---
это картридж с sram.
SegaMark:
--- Цитата: Ti_ от 07 Март 2026, 07:59:59 ---это картридж с sram.
--- Конец цитаты ---
похоже что да https://data.spludlow.co.uk/mame/software/megadriv/nhl98.
То есть получается если игре нужен sram, то она его размещает сразу после ROM?
А как тогда заголовок решил эту проблему, там типа сказано что нужно использовать sram и прописаны все его параметры?
И каким самым простым способом можно это починить чтобы заработало на карике?
Добавлено позже:
--- Цитата: SeregaZ от 06 Март 2026, 21:23:05 ---а контрольную сумму поправил перед записью?
--- Конец цитаты ---
Нет. Не подскажешь как проще всего это сделать, никогда не сталкивался с проблемами в контрольной сумме.
SeregaZ:
вроде что-то там есть какая-то программа... не помню уже точные детали. набросал примерно по мотивам... правда у меня в коде зачем-то еще в заголовке размер всего файла указывался. но вроде как не сильно то он и нужен. поэтому тут только сама сумма правится, размер нет. но если надо это легко поправить будет.
но лучше поискать что-то более проверенное и точно рабочее :) я мог накосячить :blush:
SegaMark:
В итоге удалось решить, но так как мне нужно было срочно, я придумал самый прямой способ. Возможно кому то это поможет. Я взял экспортировал sram в эмуляторе и объединил его с ромом после этого все заработало.
Томахомэ:
Вот тут у нас - первичный лонч Double Point:
--- Код: ---000106: 46FC 2700 move #$2700, SR
00010A: 4FF9 0008 4000 lea $84000.l, A7
000110: 41F9 0008 0000 lea $80000.l, A0
000116: 303C 0FFF move.w #$fff, D0
00011A: 20FC 0000 0000 move.l #$0, (A0)+
000120: 51C8 FFF8 dbra D0, $11a
000124: 41F9 0003 F800 lea $3f800.l, A0
00012A: 43F9 0008 0000 lea $80000.l, A1
000130: 3E3C 01FF move.w #$1ff, D7
000134: 22D8 move.l (A0)+, (A1)+
000136: 51CF FFFC dbra D7, $134
00013A: 4EB9 0001 489A jsr $1489a.l
000140: 60FE bra $140
<...>
01489A: 303C 48E7 movem.l D2-D3/A2-A5, -(A7)
01489E: 207C 0000 B97C move.l #$b97c, A0
0148A4: 2408 move.l A0, D2
0148A6: 263C 0008 24EC move.l #$824ec, D3
0148AC: 4EB9 0000 B1C2 jsr $b1c2.l
0148B2: 4241 clr.w D1
0148B4: 207C 0008 24C6 movea.l #$824c6, A0
0148BA: 227C 0000 062A movea.l #$62a, A1
0148C0: 247C 0008 24B2 movea.l #$824b2, A2
0148C6: 267C 0000 063E movea.l #$63e, A3
0148CC: 287C 0008 24DA movea.l #$824da, A4
0148D2: 203C 0000 0652 move.l #$652, D0
0148D8: 20D9 move.l (A1)+, (A0)+
0148DA: 24DB move.l (A3)+, (A2)+
0148DC: 2A40 movea.l D0, A5
0148DE: 38D5 move.w (A5), (A4)+
0148E0: 5241 addq.w #1, D1
0148E2: 5480 addq.l #2, D0
0148E4: 0C41 0005 cmpi.w #$5, D1
0148E8: 65EE bcs $148d8
0148EA: 4EB9 0000 BA14 jsr $ba14.l
0148F0: 4240 clr.w D0
0148F2: 33C0 0008 24EA move.w D0, $824ea.l
0148F8: 33C0 0008 2498 move.w D0, $82498.l
0148FE: 33C0 0008 24F6 move.w D0, $824f6.l
014904: 4EB9 0000 DFD8 jsr $dfd8.l
01490A: 2443 movea.l D3, A2
01490C: 082A 0007 0001 btst #$7, ($1,A2)
014912: 6706 beq $1491a
014914: 4878 0003 pea $3.w
014918: 6022 bra $1493c
01491A: 2043 movea.l D3, A0
01491C: 0828 0004 0001 btst #$4, ($1,A0)
014922: 6706 beq $1492a
014924: 4878 0004 pea $4.w
014928: 6012 bra $1493c
01492A: 2043 movea.l D3, A0
01492C: 0828 0005 0001 btst #$5, ($1,A0)
014932: 6706 beq $1493a
014934: 4878 0005 pea $5.w
014938: 6002 bra $1493c
01493A: 42A7 clr.l -(A7)
01493C: 4EB9 0000 B97C jsr $b97c.l
014942: 588F addq.l #4, A7
014944: 4EB9 0000 0174 jsr $174.l
01494A: 60FE bra $1494a
--- Конец кода ---
А тут - обработчик кадрового прерывания:
--- Код: ---000142: 48E7 FFFE movem.l D0-D7/A0-A6, -(A7)
000146: 3039 0008 1E96 move.w $81e96.l, D0
00014C: 0C40 0000 cmpi.w #$0, D0
000150: 6700 0010 beq $162
000154: 4EB8 0180 jsr $180.w
000158: 4E71 nop
00015A: 33FC 0000 0008 1E96 move.w #$0, $81e96.l
000162: 4EB8 01C2 jsr $1c2.w
000166: 4E71 nop
000168: 4EB9 0001 4EB6 jsr $14eb6.l
00016E: 4CDF 7FFF movem.l (A7)+, D0-D7/A0-A6
000172: 4E73 rte
--- Конец кода ---
Дизассемблят Double Point я решил взять за основу limited access-source'ного движка бонусной активности для разных хоумбрюшных игр на Neo-Geo. Такой перенос повторяющихся действий с обработчика прерываний на основной код достаточно грамотен?
--- Код: --- ;movem.l D2-D3/A2-A5, -(A7)
;move.l #LocalModeSelect, D2
jsr cls_local
moveq #4, D1
movea.l #TemporaryLocalPlayerNames, A0
movea.l #LocalPlayerNames, A1
movea.l #TemporaryLocalHiScore, A2
movea.l #LocalHiScore, A3
movea.l #TemporaryLocalAvatars, A4
movea.l #LocalAvatars, A5
LocalHiScoreInit:
move.l (A1)+, (A0)+
move.l (A3)+, (A2)+
move.w (A5)+, (A4)+
dbra D1, LocalHiScoreInit
;jsr LocalSoundInit
clr.w $824ea.l
clr.w $82498.l
clr.w $824f6.l
clr.l -(A7)
jsr LocalModeSelect
addq.l #4, A7
VBlankReturnCheck:
cmp BIOS_FRAME_COUNTER, LocalFrameCounter
beq VBlankReturnCheck
movem.l D0-D7/A0-A6, -(A7)
move.w LocalPalChange, D0
cmpi.w #0, D0
beq LocalNewFrame
jsr LocalLoadPal
move.w #0, LocalPalChange
LocalNewFrame:
jsr LocalPlaySound
jsr BonusGameLoop
movem.l (A7)+, D0-D7/A0-A6
bra VBlankReturnCheck
--- Конец кода ---
Предваряя возникающие вопросы, спешу предупредить, что в последней цитате из кода сохранившие свою числовую форму переменные считайте, называются так условно (про то, что work RAM у Neo-Geo и железа Double Point назначен на разные области адресуемого пространства, я таки прекрасно в курсах), LocalFrameCounter должна прирасти в BonusGameLoop или одной из её дочерних подпрограмм (примерно как системный счётчик кадров в оригинальной Double Point), а код с jsr $dfd8.l по clr.l -(A7) пропал потому, что у Double Point он отвечал за считывание аппаратных (в то время как у Neo-Geo за сугубо игровые настройки отвечают программные, да и вообще сложность и пр. у бонусной игры должны плясать от аналогичных параметров материнской игры) DIPов и переход, в зависимости от включенных, в один из нескольких разных сервисных режимов (коих у мини-игры, понятно-дело, быть не может). Сообразуясь с той же логикой, под нож пущены и предварительная чистка ОЗУ.
Навигация
Перейти к полной версии