• Question concerning Disk II emulation / timing

    From Jens Gaulke@21:1/5 to All on Fri Mar 24 15:21:50 2023
    Hi there,

    I tried to do Disk II emulation and implemented all softswitches and the functions as far as I understood them from Jim Sather. The only disk image start starts for me is "Apple Galaxian". A standard DOS 3.3 System Master reads some tracks and then
    crashes with an illegal opcode. What am I missing? Do I have some timing problems in my emulation? Did I not correctly implement the softswitch functions?

    I attached my code (I hope the formatting will not be corrupted) and would be really glad to get an answer ...

    import random
    import pygame
    from dataclasses import dataclass
    import threading
    import math
    import re

    @dataclass
    class DRIVE_STATE:
    filename: str
    half_track: int
    prev_half_track: int
    write_mode: bool
    current_phase: int
    disk_has_changes: bool
    motor_running: bool
    track_start: list
    track_nbits: list
    track_location: int
    is_write_protected: bool
    disk_data: bytearray
    status: str


    firmware = [0xA2,0x20,0xA0,0x00,0xA2,0x03,0x86,0x3C,0x8A,0x0A,0x24,0x3C,0xF0,0x10,0x05,0x3C,
    0x49,0xFF,0x29,0x7E,0xB0,0x08,0x4A,0xD0,0xFB,0x98,0x9D,0x56,0x03,0xC8,0xE8,0x10,
    0xE5,0x20,0x58,0xFF,0xBA,0xBD,0x00,0x01,0x0A,0x0A,0x0A,0x0A,0x85,0x2B,0xAA,0xBD,
    0x8E,0xC0,0xBD,0x8C,0xC0,0xBD,0x8A,0xC0,0xBD,0x89,0xC0,0xA0,0x50,0xBD,0x80,0xC0,
    0x98,0x29,0x03,0x0A,0x05,0x2B,0xAA,0xBD,0x81,0xC0,0xA9,0x56,0xa9,0x00,0xea,0x88,
    0x10,0xEB,0x85,0x26,0x85,0x3D,0x85,0x41,0xA9,0x08,0x85,0x27,0x18,0x08,0xBD,0x8C,
    0xC0,0x10,0xFB,0x49,0xD5,0xD0,0xF7,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0xAA,0xD0,0xF3,
    0xEA,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0x96,0xF0,0x09,0x28,0x90,0xDF,0x49,0xAD,0xF0,
    0x25,0xD0,0xD9,0xA0,0x03,0x85,0x40,0xBD,0x8C,0xC0,0x10,0xFB,0x2A,0x85,0x3C,0xBD,
    0x8C,0xC0,0x10,0xFB,0x25,0x3C,0x88,0xD0,0xEC,0x28,0xC5,0x3D,0xD0,0xBE,0xA5,0x40,
    0xC5,0x41,0xD0,0xB8,0xB0,0xB7,0xA0,0x56,0x84,0x3C,0xBC,0x8C,0xC0,0x10,0xFB,0x59,
    0xD6,0x02,0xA4,0x3C,0x88,0x99,0x00,0x03,0xD0,0xEE,0x84,0x3C,0xBC,0x8C,0xC0,0x10,
    0xFB,0x59,0xD6,0x02,0xA4,0x3C,0x91,0x26,0xC8,0xD0,0xEF,0xBC,0x8C,0xC0,0x10,0xFB,
    0x59,0xD6,0x02,0xD0,0x87,0xA0,0x00,0xA2,0x56,0xCA,0x30,0xFB,0xB1,0x26,0x5E,0x00,
    0x03,0x2A,0x5E,0x00,0x03,0x2A,0x91,0x26,0xC8,0xD0,0xEE,0xE6,0x27,0xE6,0x3D,0xA5,
    0x3D,0xCD,0x00,0x08,0xA6,0x2B,0x90,0xDB,0x4C,0x01,0x08,0x00,0x00,0x00,0x00,0x00]


    DEFAULT_VOLUME = 254
    NUM_DRIVES = 2
    DOS_NUM_SECTORS = 16
    DOS_NUM_TRACKS = 35
    DOS_TRACK_BYTES = 256 * DOS_NUM_SECTORS
    RAW_TRACK_BYTES = 0x1A00
    STANDARD_2IMG_HEADER_SIZE = 64
    STANDARD_PRODOS_BLOCKS = 280

    # diskdata = [0,1][0,35][1a00]

    pickbit = [128, 64, 32, 16, 8, 4, 2, 1]
    clearbit = [0b01111111, 0b10111111, 0b11011111, 0b11101111, 0b11110111, 0b11111011, 0b1111101, 0b11111110]


    class FloppyDisk:

    def __init__(self, myApple, slot) -> None:
    self.drive = 0
    self.is_motor_on = False
    self.is_write_protected = [0,1]
    #self.disk_data = [[0 for i in range(2*DOS_NUM_TRACKS)], [0 for i in range(2*DOS_NUM_TRACKS)]]
    self.curr_phys_track = 0
    self.curr_nibble = 0

    self.drive_curr_phys_track = [0,0]
    self.real_track = [0 for i in range(RAW_TRACK_BYTES+1)]

    self.latch_data = 0
    self.write_mode = False
    self.load_mode = False
    self.drive_spin = False

    self.gcr_nibble_pos = 0
    self.gcr_nibbles = []

    pygame.mixer.pre_init(frequency=44100, size=-16, channels=1, allowedchanges=0)
    pygame.init()
    pygame.mixer.init(frequency=44100, size=-16, channels=1, allowedchanges=0)
    self.motor_sound = pygame.mixer.Sound(f'./src_audio_driveMotor.mp3')
    self.track_off_end = pygame.mixer.Sound(f'./src_audio_driveTrackOffEnd.mp3')
    self.track_seek = pygame.mixer.Sound(f'./src_audio_driveTrackSeek.mp3')

    self.setup_firmware(myApple, slot)

    self.drive_state = [DRIVE_STATE('', 0, 0, False, 0, False, False, [None]*80, [None]*80, 0, False, self.load_wozdisk(), ""),
    DRIVE_STATE('', 0, 0, False, 0, False, False, [None]*80, [None]*80, 0, False, self.load_wozdisk(), "")]

    self.reset_drives()

    self.disk_data = [self.load_wozdisk(), self.load_wozdisk()]
    #arraypart = self.disk_data[0][0:12]
    #print (' '.join('{:02x}'.format(x) for x in arraypart))

    self.init_diskstates(0,'Dung Beetles.woz')
    self.init_diskstates(1,'DOS_3.3_System_Master.woz')

    self.SLOT = slot << 4
    self.DRVPH0_OFF = 0xC080 + self.SLOT
    self.DRVPH0_ON = self.DRVPH0_OFF + 1
    self.DRVPH1_OFF = 0xC082 + self.SLOT
    self.DRVPH1_ON = self.DRVPH1_OFF + 1
    self.DRVPH2_OFF = 0xC084 + self.SLOT
    self.DRVPH2_ON = self.DRVPH2_OFF + 1
    self.DRVPH3_OFF = 0xC086 + self.SLOT
    self.DRVPH3_ON = self.DRVPH3_OFF + 1
    self.DRVMOTOR_OFF = 0xC088 + self.SLOT
    self.DRVMOTOR_ON = self.DRVMOTOR_OFF + 1
    self.DRVSEL_0 = 0xC08A + self.SLOT
    self.DRVSEL_1 = self.DRVSEL_0 + 1
    self.DRVDATA_SHIFT = 0xC08C + self.SLOT
    self.DRVDATA_LOAD = self.DRVDATA_SHIFT + 1
    self.DRVREAD = 0xC08E + self.SLOT
    self.DRVWRITE = 0xC08F + self.SLOT
    self.DRVPH0_isSet = False
    self.DRVPH1_isSet = False
    self.DRVPH2_isSet = False
    self.DRVPH3_isSet = False
    self.DRVMOTOR_isSet = False
    self.DRVDATA_isSet = False
    self.DRVREAD_isSet = False


    self.current_drive = 0
    self.prev_cycle = 0
    self.data_register = 0
    self.sm = None

    def setup_firmware(self, myApple, slot):
    for i in range(0x100):
    myApple.memory.ram.write_byte(0xC000 + (slot << 8) + i, firmware[i])



    def load_wozdisk(self):
    filename = 'DOS_3.3_System_Master.woz'
    #filename = 'Dung Beetles.woz'
    #filename = 'Apple DOS 3.3 August 1980.woz'
    #filename = 'Choplifter.dsk'
    #filename = 'Apple_Galaxian.woz'
    diskdata = []
    with open(filename, "rb") as f:
    diskdata = bytearray(f.read())
    return diskdata


    def reset_drives(self):
    #self.stop_motor(0)
    #self.stop_motor(1)
    self.drive_state[0].half_track = 68
    self.drive_state[1].half_track = 68
    self.drive_state[0].prev_half_track = 68
    self.drive_state[1].prev_half_track = 68


    def is_dsk(self, filename):
    isdsk = filename.endswith(".dsk") or filename.endswith(".do")
    ispo = filename.endswith(".po")
    return isdsk | ispo

    def decode_woz2(self, drivestate, filename):
    disk_data = drivestate.disk_data
    # Überprüfen, ob die Diskette den richtigen Header hat
    woz2 = [0x57, 0x4F, 0x5A, 0x32, 0xFF, 0x0A, 0x0D, 0x0A]
    found = True
    for i in range (len(woz2)):
    if woz2[i] != disk_data[i]:
    found = False
    if not found:
    return
    if disk_data[22] == 1:
    drivestate.is_write_protected = True
    else:
    drivestate.is_write_protected = False
    # checksum überprüfung fehlt noch
    # WOZ 2
    for htrack in range(80):
    tmap_index = disk_data[88 + htrack*2]
    if tmap_index < 255:
    tmap_offset = 256 + 8*tmap_index
    trk = disk_data[tmap_offset:tmap_offset + 8]
    drivestate.track_start[htrack] = 512 * (trk[0] + (trk[1] << 8))
    drivestate.track_nbits[htrack] = trk[4] + (trk[5] << 8) + (trk[6] << 16) + (trk[7] << 24)
    else:
    drivestate.track_start[htrack] = 0
    drivestate.track_nbits[htrack] = 51200
    return True


    def decode_woz1(self, drivestate, filename):
    disk_data = drivestate.disk_data
    # Überprüfen, ob die Diskette den richtigen Header hat
    woz1 = [0x57, 0x4F, 0x5A, 0x31, 0xFF, 0x0A, 0x0D, 0x0A]
    found = True
    for i in range (len(woz1)):
    if woz1[i] != disk_data[i]:
    found = False
    if not found:
    return
    if disk_data[22] == 1:
    drivestate.is_write_protected = True
    else:
    drivestate.is_write_protected = False
    # checksum überprüfung fehlt noch
    # WOZ 1
    for htrack in range(80):
    tmap_index = disk_data[88 + htrack*2]
    if tmap_index < 255:
    drivestate.track_start[htrack] = 256 + tmap_index * 6656
    trk = disk_data[(drivestate.track_start[htrack]+6646):(drivestate.track_start[htrack]+6656)]
    drivestate.track_nbits[htrack] = trk[2] + (trk[3] << 8)
    else:
    drivestate.track_start[htrack] = 0
    drivestate.track_nbits[htrack] = 51200
    return True


    def decode_dsk(self, drivestate, filename):
    pass

    def decode_diskdata(self, drivenum, filename):
    self.drive_state[drivenum].filename = filename
    if self.is_dsk(filename):
    print("dsk image")
    self.decode_dsk(self.drive_state[drivenum], filename)
    if self.decode_woz2(self.drive_state[drivenum], filename):
    print("woz2 image")
    if self.decode_woz1(self.drive_state[drivenum], filename):
    print("woz1 image")


    def init_diskstates(self, drivenum, filename):
    self.decode_diskdata(drivenum, filename)


    def set_switches(self, address):
    if address == self.DRVPH0_OFF:
    self.DRVPH0_isSet = False
    if address == self.DRVPH0_ON:
    self.DRVPH0_isSet = True
    if address == self.DRVPH1_OFF:
    self.DRVPH1_isSet = False
    if address == self.DRVPH1_ON:
    self.DRVPH1_isSet = True
    if address == self.DRVPH2_OFF:
    self.DRVPH2_isSet = False
    if address == self.DRVPH2_ON:
    self.DRVPH2_isSet = True
    if address == self.DRVPH3_OFF:
    self.DRVPH3_isSet = False
    if address == self.DRVPH3_ON:
    self.DRVPH3_isSet = True
    if address == self.DRVMOTOR_OFF:
    self.DRVMOTOR_isSet = False
    if address == self.DRVMOTOR_ON:
    self.DRVMOTOR_isSet = True
    if address == self.DRVDATA_SHIFT:
    self.DRVDATA_isSet = False
    if address == self.DRVDATA_LOAD:
    self.DRVDATA_isSet = True
    if address == self.DRVREAD:
    self.DRVREAD_isSet = False
    if address == self.DRVWRITE:
    self.DRVREAD_isSet = True


    def softswitches(self, address, value, cycle):
    self.set_switches(address)
    disk_drive = self.drive_state[self.current_drive]
    delta = cycle - self.prev_cycle
    result = 0
    # Phasenmotoren
    a = address - self.DRVPH0_OFF
    phases = [self.DRVPH0_OFF, self.DRVPH1_OFF, self.DRVPH2_OFF, self.DRVPH3_OFF]
    phases_isset = [self.DRVPH0_isSet, self.DRVPH1_isSet, self.DRVPH2_isSet, self.DRVPH3_isSet]
    if a >= 0 and a <= 7:
    ascend = phases_isset[(disk_drive.current_phase + 1) % 4]
    descend = phases_isset[(disk_drive.current_phase + 3) % 4]
    # Abfrage, ob der phasenmotor aus ist. wie? Wo wird der softswitch gesetzt?
    if not phases_isset[disk_drive.current_phase]:
    if disk_drive.motor_running:
    if ascend:
    #if ascend == address:
    self.move_head(disk_drive,1)
    disk_drive.current_phase = (disk_drive.current_phase + 1) % 4
    #print("ascending")
    elif descend:
    #elif descend == address:
    self.move_head(disk_drive,-1)
    disk_drive.current_phase = (disk_drive.current_phase + 3) % 4
    #print("descending")
    # 0xC088
    elif address == self.DRVMOTOR_OFF:
    #print("Stop Motor:",self.current_drive)
    self.stop_motor(disk_drive, self.current_drive)
    return result
    # 0xC089
    elif address == self.DRVMOTOR_ON:
    #print("Start Motor:",self.current_drive)
    self.start_motor(disk_drive, self.current_drive)
    return result
    # 0xC08A && 0xC08B
    elif address == self.DRVSEL_1 or address == self.DRVSEL_0:
    self.current_drive = 0 if address == self.DRVSEL_0 else 1
    if self.drive_state[1 - self.current_drive].motor_running:
    self.drive_state[1 - self.current_drive].motor_running = False
    self.drive_state[self.current_drive].motor_running = True
    return result
    # 0xC08C SHIFT
    elif address == self.DRVDATA_SHIFT:
    if disk_drive.motor_running and not disk_drive.write_mode:
    return self.get_next_byte(disk_drive)
    # 0xC08D LOAD/READ
    elif address == self.DRVDATA_LOAD:
    if disk_drive.motor_running:
    if disk_drive.write_mode:
    self.do_write_byte(delta, disk_drive)
    self.prev_cycle = cycle
    if value >= 0:
    self.data_register = value
    # 0xC08E READ
    elif address == self.DRVREAD:
    if disk_drive.motor_running and disk_drive.write_mode:
    self.do_write_byte(delta,disk_drive)
    self.prev_cycle = cycle
    disk_drive.write_mode = False
    # if self.DRVDATA.isset:
    # result = disk_drive.iswriteprotected ? 0xff : 0
    result = 255
    # 0xC08F WRITE
    elif address == self.DRVWRITE:
    disk_drive.write_mode = True
    self.prev_cycle = cycle
    if value >= 0:
    self.data_register = value

    return result



    def debug_data(self, address):
    pass

    def move_head(self, disk_drive, direction):
    if disk_drive.track_start[disk_drive.half_track] > 0:
    disk_drive.prev_half_track = disk_drive.half_track
    disk_drive.half_track += direction
    if disk_drive.half_track < 0 or disk_drive.half_track > 68:
    # sound: drive.trackend
    if disk_drive.half_track < 0:
    disk_drive.half_track = 0
    if disk_drive.half_track > 68:
    disk_drive.half_track = 68
    else:
    # sound: drive.trackseek
    pass

    disk_drive.status = str(disk_drive.half_track // 2)

    if disk_drive.track_start[disk_drive.half_track] > 0 and disk_drive.prev_half_track != disk_drive.half_track:
    disk_drive.track_location = math.floor(disk_drive.track_location * (disk_drive.track_nbits[disk_drive.half_track] / disk_drive.track_nbits[disk_drive.prev_half_track]))
    if disk_drive.track_location > 3:
    disk_drive.track_location -= 4

    #print("Drive track:", disk_drive.half_track //2)


    def do_write_byte(self, delta):
    print("do write byte")
    return

    def do_nothing(self):
    for i in range(10):
    i=i+1
    #print("NOP")
    pass

    def get_next_bit(self, disk_drive):
    disk_drive.track_location = disk_drive.track_location % disk_drive.track_nbits[disk_drive.half_track]
    bit = 0
    if disk_drive.track_start[disk_drive.half_track] > 0:
    file_offset = disk_drive.track_start[disk_drive.half_track] + (disk_drive.track_location >> 3)
    byte = disk_drive.disk_data[file_offset]
    b = disk_drive.track_location & 7
    bit = (byte & pickbit[b]) >> (7-b)
    else:
    bit = 1
    disk_drive.track_location += 1
    return bit


    def get_next_byte(self, disk_drive):
    if len(disk_drive.disk_data) == 0 or disk_drive.half_track % 2 != 0:
    print("No Disk")
    return 0
    result = 0
    if self.data_register == 0:
    while(self.get_next_bit(disk_drive) == 0):
    self.do_nothing()
    self.data_register = 0x40
    for i in range(5,-1,-1):
    # print("data register:", i)
    self.data_register |= self.get_next_bit(disk_drive) << i
    else:
    bit = self.get_next_bit(disk_drive)
    self.data_register = (self.data_register << 1) | bit
    result = self.data_register
    if (self.data_register > 127):
    self.data_register = 0
    print("get next byte:", hex(result), ' ', disk_drive.track_location,' ', disk_drive.half_track)
    return result


    def start_motor(self, disk_drive, current_drive):
    disk_drive.motor_running = True
    if self.sm:
    self.sm.cancel()
    pygame.mixer.Channel((current_drive+1)*2-1).play(self.motor_sound)


    def stop_motor(self, disk_drive, current_drive):
    self.sm = threading.Timer(1,self.stopit,[disk_drive, current_drive])
    self.sm.start()


    def stopit(self, disk_drive, current_drive):
    disk_drive.motor_running = False
    pygame.mixer.Channel((current_drive+1)*2-1).stop()

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael AppleWin Debugger Dev@21:1/5 to Jens Gaulke on Sat Mar 25 06:44:01 2023
    On Friday, March 24, 2023 at 3:21:51 PM UTC-7, Jens Gaulke wrote:
    I tried to do Disk II emulation and implemented all softswitches and the functions as far as I understood them from Jim Sather. The only disk image start starts for me is "Apple Galaxian". A standard DOS 3.3 System Master reads some tracks and then
    crashes with an illegal opcode. What am I missing?
    Many nights of poring over boot traces. /s

    Seriously, the "secret sauce" is nothing more then hard work of logging to tell where things are going off the rails.
    i.e.
    When you boot DOS 3.3, what are the PC & regs when it crashes? What stage of DOS doe the PC reach? Stage 0? Stage 1? Stage 2?

    Do I have some timing problems in my emulation? Did I not correctly implement the softswitch functions?

    The easiest way to debug this would probably be to use a process of elimination as you verify the softswitches one-one by one.

    0. Write a small test program to select drive 0, wait, select drive 1, wait to rule our DRVSEL_1 and DRVSEL_2. Yes, this is trivial. Yes, you need to eliminate this.

    1. I would then rule out the MOTORON, MOTOROFF. Yes this is again trivial, and yes you need to eliminate this.

    2 I would test the four PHASE0..PHASE3 softswitches by writing a small assembly program to move the track outwards 40 tracks and then inwards 40 tracks logging the track number. That leaves four softswitches to test SHIFT, LOAD, READ, WRITE. You don't
    need to worry about WRITE for now, so this leaves SHIFT, LOAD, and READ.

    3. Prepare a "raw disk" so that each of 35 tracks contains nothing but FF filler and only ONE disk nibble of an unique track marker. Modify the utility from step (1) to read the raw 4&4 disk nibble, and step the 35 tracks. This SO question may be
    helpful as Nick was kind enough to provide some nibble counting code: https://retrocomputing.stackexchange.com/questions/503/absolute-maximum-number-of-nibbles-on-an-apple-ii-floppy-disk-track/664#664

    4. Get .do/.dsk working first before .woz. I don't see your 6&2 decode logic? Have you verified this is correct?

    5. You may want to use a "simpler" program to trace. I'm extremely biased so something like Fantavision since I have already written up the boot-tracing :-) but any program/game where you already have boot-tracing notes (say from 4AM) would work.
    https://github.com/Michaelangel007/apple2_fantavision_reloaded#ye-olde-boot-tracing

    6. I already mentioned the DOS 3.3 System Master above.

    7. Use another emulator to debug your emulator. i.e In AppleWin see the disk logging defines, LOG_DISK_PHASES, LOG_DISK_NIBBLES_SPIN, LOG_DISK_NIBBLES_READ, etc. in source/Disk.cpp.

    There are also some interesting comments in DataLatchReadWriteWOZ().

    I attached my code (I hope the formatting will not be corrupted)
    Unfortunately, Python uses the retarded decision of using whitespace for code blocks which sadly means indentation will usually be completely lost due to HTML defaulting to sequences of white space being collapsed. Do you have a git repository where it
    is easier to read that python code?

    Pro-Tip: (Almost) no one wants to read a dump of code, but we can give you pointers if you share more details of what you have tried and where exactly things have stopped working.

    Good luck! Keep us posted as you trace through the issue.

    Michael

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jens Gaulke@21:1/5 to Michael AppleWin Debugger Dev on Wed Mar 29 08:15:16 2023
    On Saturday, March 25, 2023 at 2:44:03 PM UTC+1, Michael AppleWin Debugger Dev wrote:
    On Friday, March 24, 2023 at 3:21:51 PM UTC-7, Jens Gaulke wrote:
    I tried to do Disk II emulation and implemented all softswitches and the functions as far as I understood them from Jim Sather. The only disk image start starts for me is "Apple Galaxian". A standard DOS 3.3 System Master reads some tracks and then
    crashes with an illegal opcode. What am I missing?
    Many nights of poring over boot traces. /s

    Seriously, the "secret sauce" is nothing more then hard work of logging to tell where things are going off the rails.
    i.e.
    When you boot DOS 3.3, what are the PC & regs when it crashes? What stage of DOS doe the PC reach? Stage 0? Stage 1? Stage 2?
    Do I have some timing problems in my emulation? Did I not correctly implement the softswitch functions?
    The easiest way to debug this would probably be to use a process of elimination as you verify the softswitches one-one by one.

    0. Write a small test program to select drive 0, wait, select drive 1, wait to rule our DRVSEL_1 and DRVSEL_2. Yes, this is trivial. Yes, you need to eliminate this.

    1. I would then rule out the MOTORON, MOTOROFF. Yes this is again trivial, and yes you need to eliminate this.

    2 I would test the four PHASE0..PHASE3 softswitches by writing a small assembly program to move the track outwards 40 tracks and then inwards 40 tracks logging the track number. That leaves four softswitches to test SHIFT, LOAD, READ, WRITE. You don't
    need to worry about WRITE for now, so this leaves SHIFT, LOAD, and READ.

    3. Prepare a "raw disk" so that each of 35 tracks contains nothing but FF filler and only ONE disk nibble of an unique track marker. Modify the utility from step (1) to read the raw 4&4 disk nibble, and step the 35 tracks. This SO question may be
    helpful as Nick was kind enough to provide some nibble counting code:
    https://retrocomputing.stackexchange.com/questions/503/absolute-maximum-number-of-nibbles-on-an-apple-ii-floppy-disk-track/664#664

    4. Get .do/.dsk working first before .woz. I don't see your 6&2 decode logic? Have you verified this is correct?

    5. You may want to use a "simpler" program to trace. I'm extremely biased so something like Fantavision since I have already written up the boot-tracing :-) but any program/game where you already have boot-tracing notes (say from 4AM) would work.
    https://github.com/Michaelangel007/apple2_fantavision_reloaded#ye-olde-boot-tracing

    6. I already mentioned the DOS 3.3 System Master above.

    7. Use another emulator to debug your emulator. i.e In AppleWin see the disk logging defines, LOG_DISK_PHASES, LOG_DISK_NIBBLES_SPIN, LOG_DISK_NIBBLES_READ, etc. in source/Disk.cpp.

    There are also some interesting comments in DataLatchReadWriteWOZ().
    I attached my code (I hope the formatting will not be corrupted)
    Unfortunately, Python uses the retarded decision of using whitespace for code blocks which sadly means indentation will usually be completely lost due to HTML defaulting to sequences of white space being collapsed. Do you have a git repository where it
    is easier to read that python code?

    Pro-Tip: (Almost) no one wants to read a dump of code, but we can give you pointers if you share more details of what you have tried and where exactly things have stopped working.

    Good luck! Keep us posted as you trace through the issue.

    Michael


    Hi all,

    I found a very stupid error. To do some testing, I had implemented a Routine for changing values in the ROM.
    The call to this Routine was still active, and so while booting up DOS, DOS tried to access ROM addresses and changed contents of the ROM. This caused the illegal Instruction I talked about. I disabled the writing to the ROM and the dsk booted up. So I
    learned, that DOS tries to access the 16k RAM Card.

    On top of that I remembered that french_postcards needed a RAM expansion to boot up properly and I implemented the 16k language Card. The 16k are working well and some dsk are booting up properly.

    At the moment , I try to unify the woz and dsk routines to make handling easier.

    There are just two things that bother me. Booting up karateka does not switch to graphics mode correctly though I Implemented all the softswitches and they work correctly When testing via AppleSoft. Second topic: booting a Disk is slooooow. I have no
    idea how to Speed that up at the Moment.

    That's how it is going at the moment. Thank you all for your support!

    Cheers, Jens

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jens Gaulke@21:1/5 to Michael AppleWin Debugger Dev on Wed Mar 29 08:09:48 2023
    On Saturday, March 25, 2023 at 2:44:03 PM UTC+1, Michael AppleWin Debugger Dev wrote:
    On Friday, March 24, 2023 at 3:21:51 PM UTC-7, Jens Gaulke wrote:
    I tried to do Disk II emulation and implemented all softswitches and the functions as far as I understood them from Jim Sather. The only disk image start starts for me is "Apple Galaxian". A standard DOS 3.3 System Master reads some tracks and then
    crashes with an illegal opcode. What am I missing?
    Many nights of poring over boot traces. /s

    Seriously, the "secret sauce" is nothing more then hard work of logging to tell where things are going off the rails.
    i.e.
    When you boot DOS 3.3, what are the PC & regs when it crashes? What stage of DOS doe the PC reach? Stage 0? Stage 1? Stage 2?
    Do I have some timing problems in my emulation? Did I not correctly implement the softswitch functions?
    The easiest way to debug this would probably be to use a process of elimination as you verify the softswitches one-one by one.

    0. Write a small test program to select drive 0, wait, select drive 1, wait to rule our DRVSEL_1 and DRVSEL_2. Yes, this is trivial. Yes, you need to eliminate this.

    1. I would then rule out the MOTORON, MOTOROFF. Yes this is again trivial, and yes you need to eliminate this.

    2 I would test the four PHASE0..PHASE3 softswitches by writing a small assembly program to move the track outwards 40 tracks and then inwards 40 tracks logging the track number. That leaves four softswitches to test SHIFT, LOAD, READ, WRITE. You don't
    need to worry about WRITE for now, so this leaves SHIFT, LOAD, and READ.

    3. Prepare a "raw disk" so that each of 35 tracks contains nothing but FF filler and only ONE disk nibble of an unique track marker. Modify the utility from step (1) to read the raw 4&4 disk nibble, and step the 35 tracks. This SO question may be
    helpful as Nick was kind enough to provide some nibble counting code:
    https://retrocomputing.stackexchange.com/questions/503/absolute-maximum-number-of-nibbles-on-an-apple-ii-floppy-disk-track/664#664

    4. Get .do/.dsk working first before .woz. I don't see your 6&2 decode logic? Have you verified this is correct?

    5. You may want to use a "simpler" program to trace. I'm extremely biased so something like Fantavision since I have already written up the boot-tracing :-) but any program/game where you already have boot-tracing notes (say from 4AM) would work.
    https://github.com/Michaelangel007/apple2_fantavision_reloaded#ye-olde-boot-tracing

    6. I already mentioned the DOS 3.3 System Master above.

    7. Use another emulator to debug your emulator. i.e In AppleWin see the disk logging defines, LOG_DISK_PHASES, LOG_DISK_NIBBLES_SPIN, LOG_DISK_NIBBLES_READ, etc. in source/Disk.cpp.

    There are also some interesting comments in DataLatchReadWriteWOZ().
    I attached my code (I hope the formatting will not be corrupted)
    Unfortunately, Python uses the retarded decision of using whitespace for code blocks which sadly means indentation will usually be completely lost due to HTML defaulting to sequences of white space being collapsed. Do you have a git repository where it
    is easier to read that python code?

    Pro-Tip: (Almost) no one wants to read a dump of code, but we can give you pointers if you share more details of what you have tried and where exactly things have stopped working.

    Good luck! Keep us posted as you trace through the issue.

    Michael

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From D Finnigan@21:1/5 to Jens Gaulke on Wed Mar 29 20:11:29 2023
    Jens Gaulke wrote:

    To do some testing, I had implemented a
    Routine for changing values in the ROM.
    The call to this Routine was still active, and so while booting up DOS,
    DOS
    tried to access ROM addresses and changed contents of the ROM. This caused the illegal Instruction I talked about.

    Back in the old days, Apple Computer used to always guarantee that the
    contents of ROM would never change during execution of your program.
    Probably this is the reason why.

    ;-)

    --
    ]DF$
    The New Apple II User's Guide:
    https://macgui.com/newa2guide/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael AppleWin Debugger Dev@21:1/5 to Jens Gaulke on Thu Mar 30 11:25:57 2023
    On Wednesday, March 29, 2023 at 8:15:18 AM UTC-7, Jens Gaulke wrote:
    I found a very stupid error. To do some testing, I had implemented a Routine for changing values in the ROM.
    The call to this Routine was still active, and so while booting up DOS, DOS tried to access ROM addresses and changed contents of the ROM. This caused the illegal Instruction I talked about. I disabled the writing to the ROM and the dsk booted up. So I
    learned, that DOS tries to access the 16k RAM Card.

    Yes, sadly DOS 3.3 rudely futzes with the language card at 3FCB:

    3FCB:AD 81 C0 022 LDA $C081 ; magic number If only ROMIN = $C081 for Language Card (LC)
    3FCE:AD 81 C0 023 LDA $C081 ; magic number 1st read = read ROM, 2nd read = write enable LC Bank 2
    3FD1:A9 00 024 LDA #0
    3FD3:8D 00 E0 025 STA $E000

    On top of that I remembered that french_postcards needed a RAM expansion to boot up properly and I implemented the 16k language Card. The 16k are working well and some dsk are booting up properly.

    Awesome to hear!

    There are just two things that bother me. Booting up karateka does not switch to graphics mode correctly though I Implemented all the softswitches and they work correctly

    Hmm, I just took a quick look at the Karateka and it is hitting these softswitches (low byte stored $1020 .. $1028):

    1000:A0 00 LDY #$00
    1015:BC 20 10 LDY $1020,X
    1018:99 00 C0 STA KEYBOARD,Y
    101B:F0 0D BEQ $102A
    101D:E8 INX
    101E:D0 F5 BNE $1015
    1020:5F B_1020 db #$5F
    1021:57 B_1021 db #$57
    1022:52 B_1022 db #$52
    1023:54 B_1023 db #$54
    1024:50 B_1024 db #$50
    1025:0C B_1025 db #$0C
    1026:08 B_1026 db #$08
    1027:04 B_1027 db #$04
    1028:02 B_1028 db #$02
    1029:00 B_1029 db #$00
    102A:A6 2B LDX BAS2H

    i.e.
    C05F
    C057
    C052
    C054
    C050
    C00C
    C008
    C004
    C002

    Might be a place to start?

    When testing via AppleSoft. Second topic: booting a Disk is slooooow. I have no idea how to Speed that up at the Moment.

    In AppleWin check the variable: bool m_enhanceDisk

    It looks like we omit the spinning up time in Disk2InterfaceCard::ReadWrite(): i.e.
    if (!m_enhanceDisk && pDrive->m_spinning)

    That's how it is going at the moment. Thank you all for your support!

    Thanks for the update!

    Michael

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From peter.ferrie@gmail.com@21:1/5 to All on Sat Apr 1 12:37:09 2023
    DOS turns off the drive on exit, and skips spin-up if the parameters are the same on the next call and the disk appears to be still spinning. Maybe that's causing the read failure?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From I am Rob@21:1/5 to All on Sun Apr 2 17:29:25 2023
    There are just two things that bother me. Booting up karateka does not switch to graphics mode correctly though I Implemented all the softswitches and they work correctly When testing via AppleSoft. Second topic:

    Might be the order in which the soft switches are accessed. Sweet16 has the same problem. $C050 has to be accessed before any other softswitch.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Steve Nickolas@21:1/5 to I am Rob on Mon Apr 3 00:41:23 2023
    On Sun, 2 Apr 2023, I am Rob wrote:

    There are just two things that bother me. Booting up karateka does not
    switch to graphics mode correctly though I Implemented all the
    softswitches and they work correctly When testing via AppleSoft. Second
    topic:

    Might be the order in which the soft switches are accessed. Sweet16 has
    the same problem. $C050 has to be accessed before any other softswitch.

    For what it's worth, I frequently hit them from the bottom up, C057, C054, C052, C050.

    I just looked at one of my file cracks: it says that it relies on the
    firmware to hit C052 and C054 (in no defined order, but fwiw, it's the FE84-FE89-FE93-FB2F dance you see some variation of in a lot of software)
    and then it hits C057, and only some time later hits C050.

    So you're going to need to be prepared for the softswitches to be hit in
    any arbitrary order.

    -uso.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jens Gaulke@21:1/5 to All on Mon Apr 3 13:31:21 2023
    I also got the speed problem with the floppy drive under control.

    first i restructured the gameloop and then realized that i was providing each sector "on demand" in the DSK routine. but since some sectors are read multiple times, i was wasting clock cycles here. I then converted the DSK file completely, stored it in
    an array and access the array on demand.

    Stupid me

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jens Gaulke@21:1/5 to All on Mon Apr 3 13:23:55 2023
    Hi all,

    the errors in the graphical representation were due to the fact that I overestimated the performance of Python. I refreshed the screen completely every 20 ms, as I know it from game programming. The setting of all pixels including the calculation of the
    color values blew up these 20ms. I now use a surfarray3d and set the respective pixel when it is set in RAM. Then I flash every 20ms the content of the surfarray3d on the screen. for this I use a surfarray3d for hires1 and one for hires2. Depending on
    the softswitch I flash the corresponding surfarray3d. This works surprisingly well. What I don't like so much is that my object-oriented structure has suffered a bit.

    That sometimes softswitches are set wildly and I have to take this into account in the graphics output, I have noticed in the meantime. I try to map this behavior by my new graphics routines.

    Now that Karateka runs graphically, I took care of the joystick implementation. In most cases this works as well.

    Actually I just wanted to resurrect my old Apple II, but here one more function and there one more function... that's really fun.

    Thank you all for your input and the exchange, I really appreciate that!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)