rep movsb
увеличивает SI и DI, а также уменьшает CX .Это как memcpy, который берет свои dst, src по ссылке и обновляет их до конца скопированной области.
Так что вам нужно add di, 320-16
, а si
уже указывает на следующую строку спрайта(поскольку шаг строки соответствует ширине = 16).
Что касается сегментации, movsb
копирует с DS:SI
до ES:DI
, поэтому настройте ES: DI, чтобы указатьв видеопамяти правильная.
Соглашение о вызовах Turbo C требует / обеспечивает DF = 0 при входе / выходе функции (как обычные 32-разрядные соглашения о вызовах), поэтому вам не нужно cld
, чтобы убедиться, чтоmovsb
идет в правильном направлении (вперед, а не назад).(Если вы использовали std
где-то еще и не положили его обратно, исправьте его там, чтобы избежать нарушения соглашения о вызовах.)
Соглашение о вызовах в Turbo C также имеет AX / BC / CX / DX с закрытым вызовоми ES.( Спасибо @ MichaelPetch ).Если его встроенный asm похож на MSVC, компилятор сохранит / восстановит DI и SI для вас.Но, возможно, он не сохраняет и не восстанавливает DS для вас, поэтому @ MichaelPetch предлагает вам нужно будет нажать / выдвинуть DS, чтобы сохранить / восстановить его самостоятельно.Посмотрите на asm, сгенерированный компилятором, чтобы убедиться, что вы соблюдаете соглашение о вызовах.
Из вашего обновленного вопроса мы можем видеть, что ваши варианты сборки включают модель памяти = большой, которая превращает все указатели в дальние указателиЭто будет значительное замедление по сравнению с ручным выбором, какие указатели должны быть FAR, а другие только 16-битными.Но если у вас нет причин изучать 16-битную сегментацию в реальном режиме и все, что больше не актуально, продолжайте использовать это.(Вы можете выбрать модель памяти, где, по крайней мере, код может быть рядом, поэтому рядом с call / ret можно только выдвигать / выдвигать значение IP, а не CS.)
Вы можете поместить код вцикл, как это.
У меня есть смесь жесткого / ширины / высоты кодирования и загрузки его, как ваш вопрос, но если вы вычислите шаг строки в BX (320-ширина), у вас есть достаточно регистроввывести расчеты.Сама ветвь цикла уже обрабатывает также спрайты с переменным временем выполнения.
push ds
xor di,di // DI=0
//mov si,[sprite.data] /* source memory ram ???*/
lds si,[sprite.data] // with your build options, everything is a seg:off FAR pointer
lea ax, [si + 16*16] // end_src pointer
mov dx, [sprite.width]
shr dx, 1 // words to copy = bytes / 2
// if you can't assume even width, then just use movsb
// or optimize with rep movsb + a test of the low bit for one movsb
@loop: // do {
mov cx,dx /* words to copy */
rep movsw /* copy 16 bytes from ds:si to es:di */
add di, 320-16 /* starting column in next scanline in vram */
// add si, 0 // sprite row stride - width = 0
cmp si, ax
jb @loop // } while(src < endsrc);
pop ds
Обратите внимание на использование movsw
для копирования в 2-байтовых чанках.x86 до PPro действительно просто копировал 1 байт или 1 слово за раз, в зависимости от размера операнда.
PPro и более поздние версии имеют микрокод быстрых строк, который копирует большими кусками.Но это приводит к значительным накладным расходам при запуске, поэтому для 16-битного режима на современном x86 было бы лучше использовать, возможно, 4 целочисленных регистра DWORD (eax) или qword с x87 fild qword
/ fistp
или 16-байт с одним регистром XMM.
На реальных 8086 или 286 fild
/ fistp
будет ужасно медленным по сравнению с целочисленными копиями.С 16-битной шиной данных вы все равно можете копировать только 2 байта за раз, поэтому rep movsw
хорошо для реальных 286.
См. Также Какую настройку выполняет REP?
И Улучшенный REP MOVSB для memcpy для memcpy на современном x86 (хотя в основном и для больших копий.)
Также обратите внимание, что VRAM обычно не кэшируется или комбинируется при записи, поэтомуесли вы на самом деле оптимизируете процедуру копирования в VRAM, несколько узких хранилищ для одной и той же строки кэша отстой для UC, но неплохо для WC, на процессоре с кешем.