Эмуляция является многоаспектной областью. Вот основные идеи и функциональные компоненты. Я собираюсь разломать его на кусочки и затем заполнить детали через редактирования. Многие вещи, которые я собираюсь описать, потребуют знания внутренних работ процессоров - знание блока необходимо. Если я немного слишком неопределенен на определенных вещах, задайте вопросы, таким образом, я могу продолжить улучшать этот ответ.
Эмуляция работает путем обработки поведения процессора и отдельных компонентов. Вы создаете каждую отдельную часть системы и затем соединяетесь, части во многом как провода делают в аппаратных средствах.
существует три способа обработать эмуляцию процессора:
Со всеми этими путями, у Вас есть та же полная цель: выполните часть кода, чтобы изменить состояние процессора и взаимодействовать с 'аппаратными средствами'. Состояние процессора является скоплением регистров процессора, обработчиков прерываний, и т.д. для данной цели процессора. Для этих 6502 у Вас было бы много 8-разрядных целых чисел, представляющих регистры: A
, X
, Y
, P
, и S
; у Вас также было бы 16-разрядное PC
регистр.
С интерпретацией, Вы запускаете в IP
(указатель команд - также названный PC
, счетчик команд) и читаете инструкцию из памяти. Ваш код анализирует эту инструкцию и использует эту информацию для изменения состояния процессора, как определено процессором. Базовая проблема с интерпретацией состоит в том, что это очень медленно; каждый раз, когда Вы обрабатываете данную инструкцию, необходимо декодировать ее и выполнить необходимую операцию.
С динамической перекомпиляцией, Вы выполняете итерации по коду во многом как интерпретация, но вместо того, чтобы просто выполнить коды операций, Вы создаете список операций. Как только Вы достигаете команды перехода, Вы составляете этот список операций к машинному коду для Вашей серверной платформы, тогда Вы кэшируете этот скомпилированный код и выполняете его. Тогда при ударе данной группы инструкции снова только необходимо выполнить код от кэша. (BTW, большинство людей на самом деле не составляет список инструкций, но компилирует их в машинный код на лету - это делает более трудным оптимизировать, но это вне объема этого ответа, если достаточному количеству людей не интересно)
Со статической перекомпиляцией, Вы делаете то же как в динамической перекомпиляции, но Вы следуете за ответвлениями. Вы заканчиваете тем, что создали блок кода, который представляет весь код в программе, которая может тогда быть выполнена без дальнейшей интерференции. Это было бы большим механизмом если бы не следующие проблемы:
, Они объединяются для создания статической перекомпиляции абсолютно неосуществимой в 99% случаев. Для получения дополнительной информации Michael Steil провел некоторое большое исследование в статическую перекомпиляцию - лучшее, которое я видел.
другая сторона к эмуляции процессора является путем, которым Вы взаимодействуете с аппаратными средствами. Это действительно имеет две стороны:
платформы Certain - особенно более старые консоли как NES, SNES, и т.д. - требуют, чтобы Ваш эмулятор имел строгую синхронизацию, чтобы быть абсолютно совместимым. С NES у Вас есть PPU (пиксельный блок обработки), который требует, чтобы ЦП поместил пиксели в свою память в точные моменты. При использовании интерпретации можно легко считать циклы и эмулировать надлежащую синхронизацию; с динамической/статичной перекомпиляцией вещами является/lot/более сложное.
Прерывания являются основным механизмом, который ЦП передает с аппаратными средствами. Обычно Ваши аппаратные компоненты скажут ЦП, что прерывает его заботы о. Это довольно просто - когда Ваш код бросает данное прерывание, Вы смотрите на таблицу обработчика прерываний и называете соответствующий обратный вызов.
существует две стороны к эмуляции данного устройства:
Берут случай жесткого диска. Функциональность эмулирована путем создания внешней памяти, считайте/пишите/форматируйте стандартные программы и т.д. Эта часть обычно очень проста.
фактический интерфейс устройства немного более сложен. Это обычно - некоторая комбинация регистров с отображенной памятью (например, части памяти, которую устройство наблюдает за изменениями, чтобы сделать передачу сигналов), и прерывания. Для жесткого диска у Вас может быть область с отображенной памятью, куда Вы помещаете команды чтения, записи, и т.д., затем считывают эти данные назад.
я вдавался бы в большее количество подробностей, но существует миллион путей, которыми можно пойти с ними. Если у Вас есть какие-либо конкретные вопросы здесь, не стесняйтесь спрашивать, и я добавлю информацию
я думаю, что дал довольно хорошее введение здесь, но существует тонна из дополнительных областей. Я более, чем рад помочь с любыми вопросами; я был очень неопределенен в большей части из этого просто из-за огромной сложности.
Это был хорошо более чем год, с тех пор как этот ответ был отправлен, и со всем вниманием это добиралось, я полагал, что пора обновить некоторые вещи.
, Возможно, самая захватывающая вещь в эмуляции прямо сейчас libcpu, запущенный вышеупомянутым Michael Steil. Это - библиотека, предназначенная для поддержки большого количества ядер процессора, которые используют LLVM для перекомпиляции (статичный и динамичный!). Это имеет огромный потенциал, и я думаю, что это сделает большие вещи для эмуляции.
на документы страуса эму также обратили мое внимание, который содержит большой репозиторий документации по системе, которая очень полезна для целей эмуляции. Я не провел много времени там, но похоже, что у них есть много больших ресурсов.
я рад, что это сообщение было полезно, и я надеюсь, что могу выйти своей задницы и закончить мою книгу по предмету к концу года в следующем году / ранний в следующем году.
Переопределение NSCell's -titleRectForBounds:
должно сделать это - это метод, отвечающий за указание ячейке, где рисовать текст:
- (NSRect)titleRectForBounds:(NSRect)theRect {
NSRect titleFrame = [super titleRectForBounds:theRect];
NSSize titleSize = [[self attributedStringValue] size];
titleFrame.origin.y = theRect.origin.y + (theRect.size.height - titleSize.height) / 2.0;
return titleFrame;
}
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
NSRect titleRect = [self titleRectForBounds:cellFrame];
[[self attributedStringValue] drawInRect:titleRect];
}
К вашему сведению, это работает хорошо, хотя мне не удалось заставить его оставаться в центре при редактировании ячейки ... Иногда у меня есть ячейки с большим количеством текста, и этот код может привести к в них они смещены, если высота текста больше, чем высота ячейки, в которой он пытается центрировать его по вертикали. Вот мой модифицированный метод:
- (NSRect)titleRectForBounds:(NSRect)theRect
{
NSRect titleFrame = [super titleRectForBounds:theRect];
NSSize titleSize = [[self attributedStringValue] size];
// test to see if the text height is bigger then the cell, if it is,
// don't try to center it or it will be pushed up out of the cell!
if ( titleSize.height < theRect.size.height ) {
titleFrame.origin.y = theRect.origin.y + (theRect.size.height - titleSize.height) / 2.0;
}
return titleFrame;
}
Любой, кто пытается это сделать, используя метод Мэтта Болла drawInteriorWithFrame: inView:
, больше не будет рисовать фон, если вы настроили его рисование в ячейке. Чтобы решить эту проблему, добавьте что-нибудь вроде
[[NSColor lightGrayColor] set];
NSRectFill(cellFrame);
в начало вашего метода drawInteriorWithFrame: inView:
.