2 puan yazan GN⁺ 2025-01-27 | 1 yorum | WhatsApp'ta paylaş
  • Mac Classic II, ROM’daki aralık dışı atlama hatası yüzünden hatalı bir adrese yazmanın eşiğine gelmişti; ancak gerçek Motorola MC68030 CPU, belgelenmemiş bir komutu çalıştırıp A1 yazmacını değiştirerek çökmeyi önledi
  • Bu sorun, MAME’in Classic II emülasyonunda 32 bit adresleme açıldığında Sad Mac ve Chimes of Death’in görünmesiyle ortaya çıktı; 24 bit adreslemede ise normal açılıyormuş gibi görünüyordu
  • ROM’daki InstallSoundIntHandler yolu, Classic II’nin BoxFlag 17 değerini 16 öğeli bir dallanma tablosuna uygulayıp 0x40A43B94 adresine atlıyordu; bu konum aslında bir MOVEA.L komutunun ortasıydı
  • İlgili bayt dizisi 0C EC 08 A9 00 04, CAS D1,D2,$0004(A4) komutuna benziyordu; ancak ayrılmış bitleri ayarlanmış geçersiz bir CAS ailesi komutuydu ve gerçek 68030’da A1’i FFFF8FBA değerinden 40A4BBB2 değerine değiştiriyordu
  • Gerçek bir Classic II’ye 3 özel ROM takılarak yapılan doğrulamada, bu komut NOP ile kaldırıldığında donanımın da Sad Mac ile başarısız olduğu görüldü; MAME ise doğru davranış anlaşılana kadar bir ROM yamasıyla açılışı dolanarak sağladı

MAME’de Ortaya Çıkan Classic II Açılış Hatası

  • MAME arcade oyun emülatörü olarak iyi bilinse de, 68000 tabanlı Mac modellerinin emülasyonunu da destekliyor
  • MAME’de command+power tuş kombinasyonuyla hata ayıklayıcıyı çağırma işlevi düzeltilirken Classic II’de garip bir davranış fark edildi
    • Classic II’de fiziksel bir interrupt düğmesi ve klavye, fare vb. aygıtları işleyen “Egret” 68HC05 mikrodenetleyicisi bulunuyor
    • MacsBug’da command+power tuş kombinasyonunu etkinleştiren komutu gönderen kod var
  • Classic II, 24 bit adresleme ile normal açılıyordu; ancak MacsBug’ın yüklenmesi için gereken 32 bit adresleme açıldığında Sad Mac ve Chimes of Death ortaya çıkıyordu
  • Apple Tech Info Library belgesine göre Sad Mac kodu 0000000F bir istisnayı, 00000001 ise veri yolu hatasını ifade ediyor
    • 68k Mac’lerde veri yolu hatası genellikle var olmayan bir genişletme kartına erişim gibi hatalı bir adres erişimi anlamına gelir

ROM İzleme: Ses Interrupt’ı Başlatma Yolu

  • MAME hata ayıklayıcısında veri yolu hatası işleyicisi adresi 0x40A026F0 için kesme noktası koyulup, ROM sembol haritasıyla çağrı yolu izlendi
    • Bu adres ROM haritasında GenExcps olarak gösteriliyor
    • Ortak hata işleme rutini ToDeepShit adıyla gösteriliyor
  • Veri yolu hatasına yol açan komut, 0x40A43B9C adresindeki şu koddu
move.b #$90, ($1c00,A1)
  • Bu kod, ROM haritasında InstallSoundIntHandler olarak gösterilen rutinin içindeydi ve Classic II’nin ses interrupt’ı başlatmasıyla ilgiliydi
  • Classic II, yapı olarak özgün Macintosh Classic’ten çok Macintosh LC serisine daha yakındır ve LC’nin V8 çipine benzer EAGLE gate array’ini kullanır
    • ROM kodunda V8SndIntPatch1 gibi adların görünmesinin nedeni budur
    • EAGLE, Apple Sound Chip’in (ASC) küçültülmüş bir sürümüne karşılık gelen işlevler içerir

Hatalı Dallanma Tablosu ve A1 Yazmacı Sorunu

  • V8SndIntPatch1, Gestalt trap’in gestaltHardwareAttr seçicisini, yani 'hdwr' değerini kullanarak donanım özelliklerini kontrol eder
    • gestaltHasASC biti yoksa geri döner
    • Classic II’de gestaltHasASC ayarlanmıştır
  • Sonrasında kod, RAM’deki 0xCB3 adresinden BoxFlag değerini okuyup D0’a koyar
    • Classic II’nin BoxFlag değeri 0x11, yani 17’dir
  • Sorunlu dallanma kodu D0’ı ikiyle çarptıktan sonra PC’ye göreli atlama ofseti olarak kullanır
add.w d0,d0
jmp loc_40A43B72(pc,d0.w)
  • Her BRA.S komutu 2 bayt olduğundan BoxFlag değerini ikiyle çarpıp dallanma tablosu indeksi olarak kullanan bir yapıydı
  • Gerçek tabloda yalnızca BoxFlag 0–15 için 16 girdi vardı; Classic II’nin 17 değeri aralığın dışındaydı
    • Hesaplanan atlama hedefi 0x40A43B72 + 0x22 = 0x40A43B94 idi
    • Bu konum amaçlanan bir komut başlangıcı değil, bir MOVEA.L komutunun ortasıydı
  • Normalde MOVEA.L komutunun A1’e doğru adresi yüklemesi gerekiyordu; ancak komutun ortasına atlandığı için A1, önceki göreli atlama hesaplamasında kullanılan 0xFFFF8FBA değerini korudu
  • Ardından move.b #$90, ($1c00,A1) çalıştırıldığında yazma adresi 0xFFFF8FBA + 0x1C00 = 0xFFFFABBA oluyor ve Classic II için hatalı bir adrese dönüşüyordu

Belgelenmemiş 68030 Davranışı

  • Aralık dışı atlamanın ulaştığı bayt dizisi şöyleydi
0C EC 08 A9 00 04
  • IDA ve GNU objdump bu konumdaki komutu düzgün şekilde disassemble edemedi
  • Macintosh IIci’de MacsBug içinde aynı bayt dizisi çalıştırıldığında CAS.W D1,D2,$0004(A4) gibi gösterildi; ancak çalıştırma sonrası A1 değeri değişti
    • A1, 0xFFFF8FBA değerinden RAM adresi gibi görünen bir değere dönüştü
    • Yeni A1 değerinin özgün A1, A7 ve program sayacına bağlı olarak değiştiği gözlemlendi
  • Motorola M68000 Family Programmer’s Reference Manual temel alındığında ilk word CAS komutuna benziyor; ancak ikinci word’de 0 olması gereken 3 bit 1 olarak ayarlanmış durumda
  • Bu nedenle bu komut, belgelenmiş geçerli bir CAS değil; ayrılmış bitleri açık olan hatalı bir komut
  • Gerçek 68030 bu komutu illegal instruction istisnası olarak ele almıyor; A4+4 üzerinde read-modify-write veri yolu çevrimi gerçekleştiriyor
    • A4 hatalı bir adrese ayarlanırsa MacsBug, A4+4 üzerinde read-modify-write sırasında veri yolu hatası gösteriyor
    • Normal bir CAS olsaydı değiştirmemesi gereken A1 de değişiyor

Gerçek Classic II Donanımında Doğrulama

  • Varsayımı doğrulamak için 1991’de üretilmiş bir Classic II satın alınıp onarıldıktan sonra ROM, programlanabilir SST29EE010 EEPROM ile değiştirildi
    • Eski Mac’lerdeki yüzeye montaj kapasitörler aşındırıcı sızıntıya yol açabildiğinden, güç vermeden önce sökülüp onarıldı
    • CRT ve analog kart yerine logic board ayrı olarak çalıştırıldı ve Raspberry Pi Pico tabanlı VGA dönüştürücüyle görüntü kontrol edildi
  • A1 değerini ekranda gösteren 68030 assembly kodu ROM’daki boş alana yerleştirildi ve üç özel ROM üretildi
    • Custom ROM 1: MAME’de Sad Mac’e yol açan 0x40A43B9C adresindeki MOVE.B komutunu A1 gösterme koduyla değiştirdi
    • Custom ROM 2: Aralık dışı atlama hedefi olan 0x40A43B94 adresindeki CAS benzeri komutu A1 gösterme koduyla değiştirdi
    • Custom ROM 3: 0x40A43B94 adresindeki CAS benzeri komutu NOP ile değiştirdi
  • Custom ROM 1, ilgili kodun gerçek donanımda da çalıştığını ve MAME’de çökülen noktadaki A1’in 0x40A4BBB2 olduğunu gösterdi
    • Bu değer bir ROM adresi olduğundan yazma hedefi olarak uygun değil; ancak yazma denense bile veri yolu hatasına yol açmıyor
  • Custom ROM 2, aralık dışı atlamanın hemen öncesinde A1’in MAME’de görülen değerle aynı, yani 0xFFFF8FBA olduğunu gösterdi
    • Aralık dışı atlama gerçekten gerçekleşiyor
    • Ardından CAS benzeri komutun A1’i değiştirdiği varsayımıyla örtüşüyor
  • Custom ROM 3, CAS benzeri komut kaldırıldığında gerçek Classic II’nin de Sad Mac ile başarısız olduğunu gösterdi
    • Bu testte donanım 24 bit modda da aynı Sad Mac’i gösterdi
    • MAME’in 24 bit modda hatalı yazma için veri yolu hatası üretmemesi, gerçek donanıma göre daha hoşgörülü bir davranıştı

MAME ve Emülasyonda Kalan Sorun

  • Keşfedilen davranış, belgelenmemiş bir MC68030 komutunun read-modify-write veri yolu çevrimi gerçekleştirdiği ve A1 yazmacını bile değiştirdiği bir örnek
  • Classic II ROM’undaki hata bu davranış sayesinde gizli kaldı ve gerçek cihaz normal şekilde çalıştı
  • Aynı kod parçası daha yeni Macintosh IIvx ROM’unda da bulundu; ancak o ROM’da atlama tablosu boyutu artırılmıştı ve Classic II durumu doğrudan RTS’ye atlıyordu
  • Bu davranış nedeniyle şu anda %100 kusursuz bir Motorola MC68030 emülatörü veya kopya uygulaması bulunmama olasılığı yüksek görünüyor
    • Bu komutu çalıştırıp A1 sonucunu kontrol ederek fiziksel 68030 ile emülatörü ayırt eden küçük bir kod yazılabilir
  • MAME, doğru komut davranışı ortaya çıkana kadar Classic II ROM hatasını yamalayarak açılışı mümkün kıldı
  • Ayrı olarak command+power tuş kombinasyonu gerçek Classic II’de MacsBug kurulduğunda çalışıyor; ancak MAME’de henüz çalışmıyor

1 yorum

 
GN⁺ 2025-01-27
Hacker News yorumları
  • Bulunan MC68030 davranışı, CPU tasarımcısının kasıtlı olarak yaptığı “gerçek” bir komuttan ziyade, geçersiz bir komut için CPU’nun iç mantığının don’t-care girdiler almış hâlde tesadüfen çalışmasının sonucu olabilir.
    Normalde CPU’nun geçersiz komutu algılayıp bir exception üretmesi gerekir, ama bazı durumlarda öyle olmuyor gibi görünüyor.
    https://www.nxp.com/docs/en/reference-manual/MC68030UM.pdf belgesinin 8-9. sayfalarında geçersiz komutların “ilk word”ün bit örüntüsüne göre tanımlandığı yazıyor.
    Bu komut 3 word’den oluşuyor; ilk word normal, garip bitler ise ikinci word’de. Bu yüzden 68030’un ikinci word’ü doğrulamadan CAS komutu uygulama mantığıyla devam etmiş olması oldukça olası.

    • Üniversitedeki bilgisayar mimarisi dersinde öğrendiğimiz sahte CPU, komut kodunu birkaç bit alanına ayırıyordu; belirli bir bitin toplayıcı gibi ilkel bir işlemi açması türünden.
      “Geçerli” komut kodları yalnızca belgelenmeye değecek kadar kullanışlı kombinasyonlardı; “geçersiz” kombinasyonlar ise işe yaramaz ya da anlamsız kombinasyonlardı.
      Bu açıdan bakınca geçersiz/gizemli komut kodları şaşırtıcı bir istisna değil, neredeyse kaçınılmaz.
    • 68k uzmanı değilim ama ilk word yalnızca komut kodu gibi görünüyor; ikinci word ise CAS’ta kullanılacak D register’ını seçmek için kullanılıyor gibi.
      Normalde belgelerde anlamsız görünen 0 bitlerinin tamamen yok sayılmasını beklersiniz.
      Ama 68030’da bu bitlerin 0 olması gerekiyor olabilir; 6502’nin geçersiz komut kodlarında olduğu gibi, ilgili bit açıldığında başka bir komuta ait hardwired mantık etkinleşiyor olabilir.
  • CAS her zaman baş ağrıtan bir şeydi; emülasyon hata raporlarını muhtemelen en çok alan komut da buydu.
    Eski King of Fighters’ta da SBCD komutunun carry flag’ini “yanlış” kontrol eden bir hata vardı; bunu round zamanlayıcısını azaltmak ve round’u bitirmek için kullanıyordu.
    Belgelenmemiş davranış olsa bile, binary-coded decimal işlemlerinde aritmetik durum flag’lerini emüle etmezseniz KOF’un round zamanlayıcısı 00’dan 99’a dönüp duruyor ve hiç bitmiyor.
    SNK gerçekten 68000 çipinin tanrılarıydı.

    • Retro arcade ve 68k hayranı olarak, SNK’nin 68K’yı sınırlarına kadar nasıl kullandığı hakkında daha fazlasını duymak isterim.
  • Gerçekten uzun bir yolculuk.
    Bugünlerde yazar kadar derin bir tavşan deliğine inecek sabrım yok, ama bir şeyi bildiğini sanmak ile gerçekten bilmek arasındaki farktan gelen başarma hissine fazlasıyla katılıyorum.

    • Yazar olarak, böyle bir şey keşfettiğimde oldukça büyük bir derinlemesine analize kapılma eğilimim var.
      Çok zaman alsa da sonuna kadar gidince gerçekten karşılığını veriyor.
  • 30 yıldan fazla zaman geçmiş olmasına rağmen, o kadar küçük ekran çözünürlüklerinde Mac debugger UI’ının ne kadar etkili olabildiğine hep şaşırıyorum.
    Gerçekten ustalık hissi veriyor.

  • Neredeyse her CPU’da belgelenmemiş komutlar vardır ve 68k da istisna değil.
    Sadece o dönemde yeterince ilgiye ve düşük seviye bilgiye sahip kişilerin çoğu x86/PC’ye odaklanmıştı; x86/PC’nin Apple tarafına kıyasla çok daha açık ve kararlı bir mimari olduğu söylenebilir.
    8088 ve 8086 mikrokodu birkaç yıl önce disassemble edildi ve epey incelendi; transistor seviyesinde simülasyon denemeleri de yapıldığını biliyorum.
    x86 komut kodu alanının yapısı da şu belgelerle ayrıntılı biçimde araştırıldı:
    http://ref.x86asm.net/geek.html
    https://gist.github.com/seanjensengrey/f971c20d05d4d0efc0781...
    Bu komutun tam olarak ne yaptığı hâlâ bilinmiyor.
    Sınırlı testlerde A1 sonucunun özgün A1 değerine, A7 değerine ve program counter’a göre değiştiği görülmüş gibi, ama emin değilim.
    Birisi çeşitli register değerleri ve bellek içerikleri deneyen bir program yazıp kesin davranışı çıkarırsa, emülasyon da daha doğru hâle gelebilir.
    Şimdilik, birileri bunun kurcalamaya değer olduğuna karar verene kadar MAME, Classic II’nin boot edebilmesi için ROM’daki bu hatayı patch’liyor.
    Kişisel olarak doğru emülasyon için bunun yeterince aydınlatılmaya değer olduğunu düşünüyorum.
    68k’ya aşina değilim ama komutun bitleri ipucu veriyor. Varsayım şu: ikinci word’deki 5:3 bitleri başka bir mode alanı gibi görünüyor; mode 000 ile Dn register’ını seçmek yerine 101 yeniden (d16, An)’ı seçiyor ve 001 içeren Dc alanı A1 olarak yorumlanıyor gibi.

    • Küçük bir düzeltme ama 68k elbette Apple’ın mimarisi değildi.
      68030 o dönem oldukça iyi bir genel amaçlı CPU’ydu ve Amiga 3000, Atari Falcon, Sun 3, NeXT Cube gibi sistemlerde de kullanıldı.
    • Mac Classic’in anlamlı olduğu dönemde PC, ev bilişiminde henüz o kadar büyük bir şey değildi; x86/PC tarafında bu tür şeyleri kurcalayan çok kişi de yoktu.
      Demoscene hâlâ 8-bit ev bilgisayarlarına tutunuyordu; 16-bit ev sistemleri kullananlar ise Atari ve Amiga’ya odaklanmıştı.
      Evlerde PC ve x86’nın gerçekten yükselişe geçmesi, VGA ve ses kartları standart PC yapılandırmasına girdikten sonra oldu.
  • Çocukken içinde 68000 işlemci olan bir Amiga 2000’im vardı.
    68020 ya da 68030 haberlerini duyduğumda gerçekten heyecanlanmıştım; daha sonra RISC mimarileri için de öyle oldu.
    Amiga’ya robotik bir sesle “Hello, how are you?” gibi cümleler söylettiğimde arkadaşlarım Ay’a gidip gelmiş gibi şaşırırdı.
    40 yıl bile geçmeden LLM’lerle bilgisayarlarla doğal dille konuşacağımızı; Python, VS Code ve LLM’leri araç olarak kullanıp neredeyse istediğimiz her şeyi otomatikleştireceğimizi hayal bile edemezdim.
    Gerçekten çılgın bir çağ.

  • 68000 ailesini özlüyorum.
    Gerçekten harika çiplerdi.

    • 68000 assembly’sini özlüyorum.
      İyi mühendisliğin her zaman olması gerektiği gibi açık ve mantıklıydı; şöyle bir göz atınca bile ne olup bittiğini hemen anlayabiliyordunuz.
      Buna karşılık x86 assembly çoğunlukla kötü bir uykusuz gecenin sonunda çıkmış zekice hilelerden oluşan saf bir karmaşa gibi görünüyor.
      0 elde etmek için bir register’dan kendisini çıkarmak gerçekten fazla.
  • Bunun belirli sistemlerde çalışmasını engellemeye yönelik bir kopya koruması olup olmadığı, yoksa tüm 68030’larda görülen bir durum mu olduğu merak konusu

    • Yazının içeriğine bakılırsa bunun kopya koruması olmadığı kanaatindeyim
      Burada söz konusu olan jump table’da ilgili makineye ait giriş eksikti; muhtemelen ROM üzerinde çalışanlar yeni girişi eklemeyi tamamen unuttu ve tamamen tesadüfen, tablo girişi olmadan da çalıştı
    • Apple o dönemde planlı eskitmeyi zaten azar azar deniyordu
  • Günümüz Mac’lerinde böyle bir şeyin artık mümkün görünmediği söylenebilir
    Bugünkü Apple teknik dokümantasyonu berbat

    • Apple’ın bu düzeyde donanım dokümantasyonu sunmamasının nedeni, modern Mac’lerde buna denk bir genişletilebilirlik olmaması bence
      Eskisi gibi kullanıcının kart takacağı ve üçüncü taraf geliştiricilerin donanımla doğrudan etkileşime geçmesini gerektiren sistem veri yolunu bir konnektör üzerinden dışarı açmıyorlar
      Modern Mac’lerde kullanıcı alanı programlarının kullanabildiği USB ve Thunderbolt var
      Elbette bazı güncel macOS API’lerinin dokümantasyonunun çok yetersiz olduğunu inkâr etmiyorum
      Başlığında mavi gradyan bulunan ve “Apple documentation archive” yazan eski bir sayfaya ulaşırsanız, harika bir şeye denk geldiğinizi anlarsınız
    • Modern ortamda böyle bir hatanın yayımlanmış firmware veya yazılıma girme olasılığının çok daha düşük olduğunu düşünüyorum
      Adres alanı çok daha büyük ve adreslerin çoğu map edilmemiş olduğundan, çöp bir adresle bellek işlemi yapılırsa genelde başarısız olur; geçersiz komut da muhtemelen başarısız olur
      Sınır dışı bir indeksle jump table okuma hatasının kendisi modern yazılımlarda da ortaya çıkabilir, ancak buradaki gibi çalışmaya devam etmektense sürecin ölme olasılığı daha yüksek
      Bu arada WebAssembly, tek bir doğrusal adres alanına sahiptir ve tüm adresler okunup yazılabildiği için bu tür hatalar daha olasıdır
      Çöp adres aralık içindeyse hatalı okuma, yazma veya CAS yapıp yine de devam edebilir
      Ancak metindeki gibi geçersiz bir komut WASM modülünün yüklenememesine yol açacağından, Mac ROM’daki bu sorunla bire bir aynı değildir
  • 040/060’ın da bu “belgelenmemiş komutu” destekleyip desteklemediği merak konusu

    • 040’ta gerçekten D1’in işin içinde olduğu bir şey yapıyor gibi görünüyor
      A1’e hiç dokunmadı; daha fazla test etmedim ama sadece normal bir CAS olarak işliyor olabilir
      LC 475’te MacsBug içinde bu komutu ilk kez tek adım çalıştırdığımda sistem hatası verdi, ama sonrasında sorun olmadı
    • 060’lı bir Amiga’da kontrol ettiğimde, bu komut herhangi bir A register’ını değiştiriyor gibi görünmüyor
      Bu, tam olarak o komut word’ünü çok hızlıca test etmemin sonucu