try to do a little emulator from scratch under Python

Comments, problems, suggestions about Oric emulators (Euphoric, Mess, Amoric, etc...) it's the right place to ask. And don't hesitate to give your tips and tricks that help using these emulations in the best possible way on your favorite operating system.
User avatar
goyo
Pilot Officer
Posts: 65
Joined: Sat Jan 12, 2019 10:16 am

try to do a little emulator from scratch under Python

Post by goyo »

Hi,

I try to do a little emulator with the py65emu module https://github.com/docmarionum1/py65emu

In the output console of vscode I reach to this point but I dont know how to do with the IRQ routine. if I have to use a 'thread' for this, if the irq address must be 0x244 ? ( c.interruptAddress = 0x244 ) Are there any other thread necessary ?



ORIC Ext.Basic 1.22fr █
1983 Tangerine,2001 █


1308 bytes free █

Ready █




















Is there one IRQ routine on Oric ? ( timer + keyboard ) ?

my code :

Code: Select all

from py65emu.cpu import CPU
from py65emu.mmu import MMU
import tkinter as tk
from tkinter import font
import os
import threading
import time
import atexit
import sys
import keyboard # ajouter ce module externe : py -m pip install keyboard
import pygame

c = None
m = None
cyclecount = 0

memorybigstep = 10240 #10240

current_keypressed = None

# Fonction pour afficher du texte
def afficher_texte(fenetre, texte, couleur, position):
    texte_surface = police.render(texte, True, couleur)
    fenetre.blit(texte_surface, position)


def key():
    global window
    global current_keypressesd
    event = keyboard.read_event()

    while event!= keyboard is not keyboard.is_pressed('esc'):
        if event.event_type == keyboard.KEY_DOWN:
            if len(event.name) == 1:
                v= ord(event.name)
                #print (f"key={v}")
            
                m.write(0x2df,v)
            
        #=current_keypressed    

def display():  
   
    global m
    adr=0xbb80 # start screen address 1rst line
    #while runprogram:
        
    
    i=0
    page="" 
    col=0
    for i in range(adr,adr+(40*27)):
        r = m.read(i)
        if r <32:
            r=32
        #print(r)
        col+=1
        if col> 39:
            col=0
            page=page+"█\n\r"
        page=page + chr(r)
    
    os.system('cls')    
    print(page)

# init Pygame
pygame.init()

largeur_fenetre, hauteur_fenetre = 800, 600
fenetre = pygame.display.set_mode((largeur_fenetre, hauteur_fenetre))
pygame.display.set_caption("Balle qui rebondit")


# Définir les couleurs
noir = (0, 0, 0)
blanc = (255, 255, 255)
rouge = (255, 0, 0)

# Chemin vers le fichier de police Consolas.ttf
chemin_police = r'C:\Windows\Fonts\consola.ttf'


# for pygame :  Initialiser la police
police = pygame.font.Font(chemin_police, 12)  # Police par défaut, taille 74
# end of init pygame


f = open(os.getcwd()+"\\"+"Bas122fr.rom", "rb")
#f = open(os.getcwd()+"\\"+"Basic11b.rom", "rb")

m = MMU([
        (0x00, 0xbfff), # Create RAM with 48k
        (0xc000, 0xffff, True, f) # Create ROM starting at 0xc000 with your program.
])


    
# Create the CPU with the MMU and the starting program counter address
# You can also optionally pass in a value for stack_page, which defaults
# to 1, meaning the stack will be from 0x100-0x1ff.  As far as I know this
# is true for all 6502s, but for instance in the 6507 used by the Atari
# 2600 it is in the zero page, stack_page=0.
c = CPU(m,0xf88f)

#c.interruptAddress = 0x244
#cpu->pc = (cpu->read( cpu, 0xfffd )<<8) | cpu->read( cpu, 0xfffc );

c.r.s=0x100 #0100-01FF	Stack
# Do this to execute one instruction
#c.step()
# entrance addresse
c.r.pc = 0xf88f



thread_key = threading.Thread(target=key)
thread_key.start()

# c.interruptAddress = 0x244 .

#while cyclecount<921600:
for j in range(0,9):
    for i in range(0,memorybigstep):
        if (c.r.pc==0xfa3c):
            pass
        try:
            c.step()
        except:
            pass
            
        #print(c.r.pc,a)
        
        cyclecount+=1    

        '''
        for evenement in pygame.event.get():
            if evenement.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            # Mettre à jour l'affichage

         # Afficher le texte
        afficher_texte(fenetre, "Bonjour Pygame!", rouge, (50, 50))
        pygame.display.flip()
        # Limiter la vitesse de la boucle
        pygame.time.Clock().tick(60)
        '''
    current_opcode = c.nextByte() 
    display()
    #print(c.cc)
while True:
    c.step()
    #m.blocks[0]['memory'][0x02f]=65
    display()
#display(oric)
User avatar
Chema
Game master
Posts: 3082
Joined: Tue Jan 17, 2006 10:55 am
Location: Gijón, SPAIN
Contact:

Re: try to do a little emulator from scratch under Python

Post by Chema »

I am not sure what you mean. The 6502 processor has 3 interrupt vectors at addresses fffa, fffc and fffe. In the Oric they lie in the ROM and point to the service routines that scan the keyboard, flash the cursor and so on. The addresses at page 2 are a way the BASIC provides to include your own routines (by setting thenadress of your own routines there).

But surely you know the details.

So I don't know why you need a separate thread. If you emulate the CPU whenever an IRQ is detected, it will work as expected. Maybe you are trying to emulate the VIA, which includes timers that triggers the IRQ interrupts or handle the tape and external devices.

Could you be more specific for those of us who don't know about py65emu?
User avatar
Dbug
Site Admin
Posts: 4748
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Re: try to do a little emulator from scratch under Python

Post by Dbug »

Regarding the thread, the 6502 is a single core CPU, when it serves and IRQ the main work is halted during the time spent in the IRQ, and when the RTI is reached, the context is restored and the CPU continues the main code, so I don't see how a thread would help.

And as Chema said, the page 2 address is just to make it possible for users to define their own IRQ, the core IRQ is called from the ROM vector.
ThomH
Flying Officer
Posts: 240
Joined: Thu Oct 13, 2016 9:55 pm

Re: try to do a little emulator from scratch under Python

Post by ThomH »

Jumping on board a little after the fact:

In real hardware an interrupt occurs when the 6502 realises that it's being asked to perform one; at that point it does the CPU-defined behaviour of fetching a vector from the ROM and jumping to it; the Oric's ROM then vectors again through RAM for the convenience of other software.

So what that means is that you'd expect a 6502 emulator to have some sort of call to set whether an interrupt is currently being requested, and then itself to do everything that follows from there.

However, scanning that emulator's CPU implementation I didn't see any such call. Instead it looks like:
  • the `Registers` class down to line 56 is what the name says, and an internal implementation detail, being storage for the 6502 registers plus some manipulation functions;
  • ignoring `__init__`, the topmost two CPU functions are `reset` and `step`, which seem to be the totality of the public API. `execute` is defined but not implemented, which might be a clue as to completeness;
  • `nextByte` at 109 down to `stackPopWord` at 132 are convenience memory accessors, and similarly the BCD and two's complement functions and the convenience getter for interrupt vectors seem to be for internal use;
  • that rolls into code down to line 240 that implements the various addressing modes;
  • lines 242 to 614 establish a dispatch table for opcodes; and
  • 614 all the way until the end of the file at 1005 implement operations.
So there's just no obvious means at any point to indicate to the CPU that an interrupt is requested.

Given that that the emulator isn't intended to be bus or cycle accurate, what you'd probably want to do is add a setter for current interrupt request status and check for the pair of interrupt requested + I flag not set in `step`, doing a modified version of `BRK` if appropriate.

You then need something that generates interrupts, of course. It's the VIA for IRQs and the reset button for NMIs; the former is possibly the bigger task since it looks like that emulator not only functions only in whole instructions but also doesn't indicate their length? You might well have to do some handwaving on how you convert that into an amount of time, e.g. call each instruction three cycles — regardless of what it was — and just accept the artifice of the situation.
Post Reply