Skip to content
Snippets Groups Projects
Commit 27105c9e authored by Thierno souleymane Bah's avatar Thierno souleymane Bah
Browse files

feat(swap disk implementation done and tested)

parent 12f5f1d5
No related branches found
No related tags found
No related merge requests found
File added
# $Id: Makefile 102 2009-11-03 13:14:39Z simon_duquennoy $
##############################################################################
ROOTDIR = ../x86-64/
LIBNAME = hardware
CC = gcc
CFLAGS = -Wall -ansi -pedantic
LIBDIR = $(ROOTDIR)
INCDIR = -I$(ROOTDIR)/include
LIBS = -L$(ROOTDIR)/lib -l$(LIBNAME)
###------------------------------
### Main targets
###------------------------------------------------------------
BINARIES= mmu_manager oracle
all: $(BINARIES)
###------------------------------
### Main rules
###------------------------------------------------------------
%.o: %.c
$(CC) $(CFLAGS) -c $< $(INCDIR)
%: %.o
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
Makefile.depends:
$(CC) -MM *.c $(INCDIR) > Makefile.depends
include Makefile.depends
mmu_manager: mmu_manager.o user_process.o matrix.o swap.o
oracle: oracle.o matrix.o
###------------------------------
### Misc.
###------------------------------------------------------------
.PHONY: clean realclean depend
clean:
$(RM) *.o $(BINARIES) Makefile.depends
realclean: clean
$(RM) vdiskA.bin vdiskB.bin
/* ------------------------------
$Id: hardware.h 114 2009-12-01 13:06:43Z simon_duquennoy $
------------------------------------------------------------
hardware.h
Interface de la bibliothèque de simulation du matériel.
*/
#ifndef _HARDWARE_H_
#define _HARDWARE_H_
/**
* CMD_
* commandes ATA-2
*/
#define CMD_SEEK 0x02
#define CMD_READ 0x04
#define CMD_WRITE 0x06
#define CMD_FORMAT 0x08
#define CMD_STATUS 0x12
#define CMD_DMASET 0x14
#define CMD_DSKINFO 0x16
#define CMD_MANUF 0xA2
#define CMD_DIAG 0xA4
/**
* Commandes de la MMU (registre MMU_CMD)
*/
#define MMU_PROCESS 0xCC /* Commande d'activation/désactivation de la MMU */
#define MMU_RESET 0xD5 /* Commande de réinitialisation de la MMU */
/**
* Physical and virtual memory for MMU
*/
extern void *physical_memory;
extern void *virtual_memory;
/**
* prototype des fonctions-interruptions.
* une interruption ne recoit aucun paramêtre "d'appel",
* une interruption ne retourne aucun resultat, mais
* sa terminaison restaure le contexte d'exécution du programme interrompu.
*/
typedef void (*func_irq)(void);
/**
* int init_hardware(const char *fileconfig);
* initialisation du matériel. Pas de "reinitialisation" possible.
* l'initialisation définit le matériel conformément aux spécifications
* fournies par le fichier dont le nom est "fileconfig".
* retourne 0 en cas de problème lors de l'initialisation.
*/
extern int init_hardware(const char *fileconfig);
/**
* IRQVECTOR
* donne la base d'un tableau de pointeur de fonction du type
* func_irq. la fonction IRQVECTOR[n]() est appelée lorsque
* l'interuption de niveau n est déclanchée par le matériel.
*/
#define IRQ_VECOTR_SIZE 256
extern func_irq *irq_vector; /* n'utilisez pas cette variable */
#define IRQVECTOR irq_vector /* préférez ce #define IRQVECTOR */
extern int SYSTICKDURATION; /* microseconde entre les SYSTICK */
/**
* MASTERBUFFER et SLAVEBUFFER
* Adresses des buffers de donnees des disques maitre et esclave
*/
/* n'utilisez pas ces variables*/
extern unsigned char ** HDA_masterbufferaddress, **HDB_masterbufferaddress;
/* préférez ces #define MASTERBUFFER et SLAVEBUFFER */
#define MASTERBUFFER (*HDA_masterbufferaddress)
#define SLAVEBUFFER (*HDB_masterbufferaddress)
/**
* BASEADDRESS_RAM
* variable associée à adresse de base de la mémoire globale
* de la machine. Cette mémoire est commune à tout les programmes
* qui utilisent la librairie sur la même machine.
*
*/
extern unsigned char *baseGlobalMem; /* n'utilisez pas cette variable */
#define BASEADDRESS_RAM baseGlobalMem /* préférez ce #define BASEADDRESS_RAM */
/**
* int _in(int port);
* lecture du contenu du registre matériel n° "port".
* retourne la valeur lue.
*/
int _in(int port);
/**
* void _out(int port, int value);
* ecriture de la valeur "value" dans le registre matériel n° "port".
*/
void _out(int port, int value);
/**
* void _sleep(int irq_level);
* Stoppe l'activité du microprocesseur jusqu'à l'occurence
* une interruption de niveau au moins égale à "irqLevel".
*/
void _sleep(int irq_level);
/**
* void _mask(int irqLevel);
* - cache au microprocesseur l'occurence d'interruptions
* de niveau inférieure à irqLevel.
* - 16ème bit à 0 : passage en mode protégé
* - 16ème bit à 1 : passage en mode user
*
*/
void _mask(int irq_level);
/**
* void _int(int irqLevel);
* - lance une interruption logicielle de niveau irqLevel
*
*/
void _int(int irqLevel);
#endif
# $Id: hardware.ini 114 2009-12-01 13:06:43Z simon_duquennoy $
#------------------------------------------------------------
#
# Hardware.cfg
# Fichier de configuration du simulateur de matériel
#
#
# Trace de Debug
#
# define DEBUG_SETUP 0x0001 /* trace hardware setup */
# define DEBUG_IT 0x0010 /* trace interruptions generation */
# define DEBUG_REG 0x0100 /* trace hardware register access */
# define DEBUG_WARNING 0x1000 /* trace hardware warning messages */
DEBUG = 0x0000
#
# Configuration des paramêtres de base du simulateur
#
SYSTICK = 1000 # delais entre deux ticks du simulateur (en micro-seconde)
#
# Configuration des matériels réseau
#
# configuration des ports COM (port serie de type RS232)
# > Port Serie n°1
SL1_ENABLE = 0 # SL1_ENABLE = 0 => simulation lien serie désactivée
SL1 = "chimay.lifl.fr" # Machine connecté à la sortie du lien serie n°1
SL1_COM = 1 # destinataire 1 = lien serie "com1", 2 = lien serie "com2"
SL1_NOISE = 500 # bruit de la ligne 0-999 (exprimé en /1000 d'erreur)
SL1_IRQ = 4 # niveau d'interruption de l'UART serie n°1
SL1_UARTSR = 0x3F8 # registre de status de l'UART serie n°1
SL1_UARTDATA = 0x3FA # registre d'entrée sortie de l'UART serie n°1
SL1_UDPPORT = 1500 # port UDP "réel" utiliser pour simulation serie n°1
# > Port Serie n°2
SL2_ENABLE = 0 # SL2_ENABLE = 0 => simulation lien serie désactivée
SL2 = "ldx2" # Machine connecté à la sortie du lien serie n°2
SL2_COM = 2 # destinataire 1 = lien serie "com1", 2 = lien serie "com2"
SL2_NOISE = 0 # bruit de la ligne 0-999 (exprimé en /1000 d'erreur)
SL2_IRQ = 5 # niveau d'interruption de l'UART serie n°2
SL2_UARTSR = 0x3FC # registre de status de l'UART serie n°2
SL2_UARTDATA = 0x3FE # registre d'entrée sortie de l'UART serie n°2
SL2_UDPPORT = 1501 # port UDP "réel" utiliser pour simulation serie n°2
# > Configuration de la carte Ethernet
ENABLE_ETHERNET = 0 # ENABLE_ETHERNET = 0 => simulation ethernet désactivée
Eth0_Link = 1 # num de cable de connexion de la carte Eth0
Eth0_DMASR = 0xE800 # registre de status de la carte Ethernet
Eth0_DMABASE = 0xE804 # adresse de base pour le vidage d'un paquet
Eth0_DMASIZE = 0xE808 # adresse limite pour le vidage d'un paquet
Eth0_IRQ = 9 # niveau d'interruption de la carte.
Eth0_MCADR = "225.0.0.1" # adresse multicast utilisée pour la simulation ethernet
Eth0_UDPPORT = 1502 # port UDP utilisé pour la simulation ethernet
#
# Configuration des disques durs
#
# > Disque dur IDE Maitre
ENABLE_HDA = 1 # ENABLE_HD = 0 => simulation du disque désactivée
HDA_FILENAME = "vdiskA.bin" # nom du fichier de stockage du disque simulé
HDA_CMDREG = 0x3F6 # registre de commande du disque maitre
HDA_DATAREGS = 0x110 # base des registres de données (r,r+1,r+2,...r+7)
HDA_IRQ = 14 # Interruption du disque
HDA_MAXCYLINDER = 16 # Nombre de pistes du disque maître
HDA_MAXSECTOR = 16 # Nombre de secteurs du disque maître
HDA_SECTORSIZE = 32 # Taille (en octet) d'un secteur du disque maître
HDA_STPS = 2 # nombre de SYSTICK pour changer de secteur
HDA_STPC = 1 # nombre de SYSTICK pour changer de piste
HDA_PON_DELAY = 30 # nombre de SYSTICK avant amorce du disque
HDA_POFF_DELAY = 30 # nombre de SYSTICK avant arret du disque
# > Disque dur IDE Esclave
ENABLE_HDB = 1 # ENABLE_HD = 0 => simulation du disque désactivée
HDB_FILENAME = "vdiskB.bin" # nom du fichier de stockage du disque simulé
HDB_CMDREG = 0x376 # registre de commande du disque esclave
HDB_DATAREGS = 0x170 # base des registres de données (r,r+1,r+2,...r+7)
HDB_IRQ = 15 # Niveau d'interruption du disque
HDB_MAXCYLINDER = 16 # Nombre de pistes du disque esclave
HDB_MAXSECTOR = 16 # Nombre de secteurs du disque esclave
HDB_SECTORSIZE = 512 # Taille (en octet) d'un secteur du disque esclave
HDB_STPS = 2 # nombre de SYSTICK pour changer de secteur
HDB_STPC = 3 # nombre de SYSTICK pour changer de piste
HDB_PON_DELAY = 30 # nombre de SYSTICK avant amorce du disque
HDB_POFF_DELAY = 30 # nombre de SYSTICK avant arret du disque
#
# Configuration de l'horologe interne
#
TIMER_CLOCK = 0xF0 # registre de lecture de la date courante (en ms)
TIMER_PARAM = 0xF4 # registre de configuration du TIMER
# bit 7 : RESET general (=1)
# bit 6 : Alarm ON = 1, Alarm OFF = 0
# bit 5 : Declanche la division Hz du Timer (=1)
# bit 4 \ Si le division Hz du timer est demandé :
# bit 3 / 00: 1 top d'alarme pour 1 tops d'horloge ,
# 01: 1 top d'alarme pour 8 tops d'horloge ,
# 10: 1 top d'alarme pour 64 tops d'horloge ,
# 11: 1 top d'alarme pour 512 tops d'horloge.
# bit 2 - R.F.U. -
# bit 1 \ Lecture d'un état interne de l'alarme
# bit 0 / 00: Alarme Courante,
# 01: Division Hz,
# 10: Ticks/Sec
# 11: niveau d'interruption de l'horloge
TIMER_ALARM = 0xF8 # registre de generation d'interruption
TIMER_IRQ = 2 # Niveau d'interruption de l'horologe
TIMER_TICKS = 1 # Nombre de SYSTICKS par tick d'horloge
#
# Configuration de la MMU
#
MMU_ENABLE = 1 # MMU_ENABLE = 0 => simulation de la MMU désactivée
MMU_IRQ = 13 # Niveau d'interruption de la MMU
MMU_CMD = 0x66 # Registre de commande de la MMU
MMU_FAULT_ADDR_LO = 0xCC # Registre contenant l'adresse mémoire ayant provoqué une faute (32 bits de poids faible)
MMU_FAULT_ADDR_HI = 0xCD # Registre contenant l'adresse mémoire ayant provoqué une faute (32 bits de poids fort)
TLB_ADD_ENTRY = 0xCE # Registre de commande d'ajout d'entrée dans la TLB
# attend une valeur de la forme :
# struct tlb_entry_s {
# unsigned unused: 8;
# unsigned virt_page: 12;
# unsigned phys_page: 8;
# unsigned access_type: 3;
# unsigned is_active: 1;
#};
# le nouveau mapping est ajouté dans la TLB,
# provoquant éventuellement l'écrasement d'une ancienne entrée
TLB_DEL_ENTRY = 0xDE # Registre de commande de suppression d'entrée dans la TLB
# seul phys_page est lu, et toutes les entrée
# correspondant à cette page physicique sont supprimées de la TLB
TLB_SIZE = 32 # Number of entries in the TLB
TLB_ENTRIES = 0x800 # Registre contenant les entrées de la TLB (32 bits par entrée)
# Accessible en lecture comme en écriture avec le même format que
# celui utilisé par TLB_ADD_ENTRY
/* ------------------------------
$Id: hw_config.h 105 2009-11-24 15:22:50Z simon_duquennoy $
------------------------------------------------------------
Fichier de configuration des acces au materiel
Philippe Marquet, march 2007
Code au niveau applicatif la description du materiel qui est fournie
par hardware.ini
*/
#ifndef _HW_CONFIG_H_
#define _HW_CONFIG_H_
#define HARDWARE_INI "hardware.ini"
#define MMU_IRQ 13
#define MMU_CMD 0x66
#define MMU_FAULT_ADDR 0xCD
#define TLB_ADD_ENTRY 0xCE
#define TLB_DEL_ENTRY 0xDE
#define TLB_SIZE 32
#define TLB_ENTRIES 0x800
#define PM_PAGES (1 << 8)
#define VM_PAGES (1 << 12)
#define PAGE_SIZE 4096
#define PM_SIZE (4096 * PM_PAGES)
#define VM_SIZE (4096 * VM_PAGES)
#endif
#include <stdio.h>
#include <stdlib.h>
#include "matrix.h"
static void log_state(int i, int n) {
if(n && i % n == n - 1) {
printf(".");
fflush(stdout);
}
}
void matrix_init(matrix *m) {
int i,j;
for(i = 0; i<MATRIX_SIZE; i++) {
for(j = 0; j<MATRIX_SIZE; j++) {
(*m)[i][j] = rand();
}
}
}
void matrix_add(matrix *dest, matrix *m1, matrix *m2) {
int i, j;
for(i = 0; i<MATRIX_SIZE; i++) {
log_state(i, MATRIX_SIZE / 20);
for(j = 0; j<MATRIX_SIZE; j++) {
(*dest)[i][j] = (*m1)[i][j] + (*m2)[i][j];
}
}
printf(" done\n");
}
void matrix_mult(matrix *dest, matrix *m1, matrix *m2) {
int i, j, k;
for(i = 0; i<MATRIX_SIZE; i++) {
log_state(i, MATRIX_SIZE / 20);
for(j = 0; j<MATRIX_SIZE; j++) {
int curr_val = 0;
for(k = 0; k<MATRIX_SIZE; k++) {
curr_val += (*m1)[i][k] * (*m2)[k][j];
}
(*dest)[i][j] = curr_val;
}
}
printf(" done\n");
}
unsigned matrix_checksum(matrix *m) {
int i, j;
unsigned int checksum = 0;
for(i = 0; i<MATRIX_SIZE; i++) {
for(j = 0; j<MATRIX_SIZE; j++) {
checksum += (*m)[i][j];
checksum += checksum >> 16;
checksum &= 0x0000ffff;
}
}
return checksum;
}
#ifndef _MATRIX_H_
#define _MATRIX_H_
#define MATRIX_SIZE 400
#define MATRIX_ADD 0
#define MATRIX_MUL 1
typedef unsigned int matrix[MATRIX_SIZE][MATRIX_SIZE];
extern void matrix_init(matrix *m);
extern void matrix_add(matrix *dest, matrix *m1, matrix *m2);
extern void matrix_mult(matrix *dest, matrix *m1, matrix *m2);
extern unsigned matrix_checksum(matrix *m);
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "hardware.h"
#include "hw_config.h"
#include "mmu_manager.h"
#include "swap.h"
#define PAGE_SWAPPED -2
#define EMPTY_VM_PAGE -1
#define EMPTY_PM_PAGE 0
int VM_MAPPING[VM_PAGES] = {EMPTY_PM_PAGE};
int PM_MAPPING[PM_PAGES] = {EMPTY_VM_PAGE};
int first_pm_free = 1;
extern void user_process();
void simple_swap_mmu_handler()
{
tlb_entry_t tlb;
int ppage = 1;
long int vaddr = ((long int)_in(MMU_FAULT_ADDR_HI) << 32) | (_in(MMU_FAULT_ADDR_LO) & 0xFFFFFFFF);
long int vpage = (vaddr >> 12) & 0xFFF;
if (vaddr < ((long int)virtual_memory) || vaddr > ((long int)virtual_memory) + VM_SIZE)
{
fprintf(stderr, "Adresse virtuelle incorrecte");
exit(EXIT_FAILURE);
}
/* Sauvegarder la vpage dans le fichier swap */
store_to_swap(vpage, ppage);
/* Rajouter l'entree dans la tlb */
tlb.s.tlb_vpage = vpage;
tlb.s.tlb_ppage = ppage;
tlb.s.tlb_rwx = 7;
tlb.s.tlb_used = 1;
/* Vider la MMU */
_out(MMU_CMD, MMU_RESET);
_out(TLB_ADD_ENTRY, tlb.i);
}
void swap_mmu_handler()
{
tlb_entry_t tlb;
long int vaddr = (((long int)_in(MMU_FAULT_ADDR_HI)) << 32) | (((long int)_in(MMU_FAULT_ADDR_LO)) & 0xFFFFFFFF);
int vpage = (vaddr >> 12) & 0xFFF;
if (vaddr < (long int)virtual_memory || vaddr > (long int)virtual_memory + VM_SIZE - 1)
{
fprintf(stderr, "Adresse virtuelle incorrecten \n");
exit(EXIT_FAILURE);
}
int ppage = VM_MAPPING[vpage];
/*
* Si la vpage n'a jamais eu de correspondance ou elle existe dans notre fichier de swap
* sinon, on recupere directement sa ppage coorespondante dans nos mapping.
*/
if (ppage == EMPTY_PM_PAGE || ppage == PAGE_SWAPPED)
{
/* round robin algorithm*/
if (!(++first_pm_free % PM_PAGES))
first_pm_free = 1;
ppage = first_pm_free;
/* old_vpage: la vpage qui pointait vers la ppage courante */
/* S'il n'y a aucune vpage qui pointait vers la ppage courante
* nous avons donc affaire à la première correspondance.
*/
int old_vpage = PM_MAPPING[ppage];
if (old_vpage != EMPTY_VM_PAGE)
{
store_to_swap(old_vpage, ppage);
VM_MAPPING[old_vpage] = PAGE_SWAPPED; /* On signifie qu'on l'a rajouté dans le swap*/
}
/* Si la vpage courant existe dans notre fichier swap,
* On le restore dans ce cas.
* */
if (VM_MAPPING[vpage] == PAGE_SWAPPED)
fetch_from_swap(vpage, ppage);
VM_MAPPING[vpage] = ppage;
PM_MAPPING[ppage] = vpage;
}
tlb.s.tlb_vpage = vpage;
tlb.s.tlb_ppage = ppage;
tlb.s.tlb_rwx = 7;
tlb.s.tlb_used = 1;
_out(TLB_DEL_ENTRY, tlb.i);
_out(TLB_ADD_ENTRY, tlb.i);
}
void master_process()
{
if (init_hardware(HARDWARE_INI) == 0)
fprintf(stderr, "Error in hardware initialization\n");
IRQVECTOR[MMU_IRQ] = swap_mmu_handler;
}
int main(int argc, char **argv)
{
master_process();
_mask(0x1001);
user_process();
return 0;
}
#if !defined(MI_KERNEL)
#define MI_KERNEL
#define MMU_ENABLE 1
#define MMU_IRQ 13
#define MMU_FAULT_ADDR_LO 0xCC
#define MMU_FAULT_ADDR_HI 0xCD
#define TLB_ADD_ENTRY 0xCE
#define MMU_CMD 0x66
#define HARDWARE_INI "hardware.ini"
struct tlb_entry_s
{
unsigned tlb_RFU : 8;
unsigned tlb_vpage : 12;
unsigned tlb_ppage : 8;
unsigned tlb_rwx : 3;
unsigned tlb_used : 1;
};
union tlb_entry_u
{
int i;
struct tlb_entry_s s;
};
typedef union tlb_entry_u tlb_entry_t;
void simple_swap_mmu_handler();
void swap_mmu_handler();
void init_master();
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "hw_config.h"
#include "matrix.h"
int main() {
int operation;
unsigned timestamp;
unsigned prev_checksum;
unsigned new_checksum;
matrix matrix1;
matrix matrix2;
matrix matrix3;
char line[1024];
while(!feof(stdin)) {
fgets(line, sizeof(line), stdin);
}
if(sscanf(line, "timestamp: 0x%x, operation: %d, checksum: 0x%x", &timestamp, &operation, &prev_checksum) == 0) {
fprintf(stderr, "[oracle] Invalid input\n");
exit(1);
}
srand(timestamp);
printf("[Starting oracle]\n");
printf("timestamp: 0x%04x, ", timestamp);
printf("operation: %d, ", operation);
printf("checksum: 0x%04x\n", prev_checksum);
/* init matrices */
printf("initializing matrices\n");
matrix_init(&matrix1);
matrix_init(&matrix2);
switch(operation) {
case 0: /* add matrices */
printf("adding VM matrices ");fflush(stdout);
matrix_add(&matrix3, &matrix1, &matrix2);
break;
case 1: /* multiply matrices */
printf("multiplying matrices ");fflush(stdout);
matrix_mult(&matrix3, &matrix1, &matrix2);
break;
default:
break;
}
new_checksum = matrix_checksum(&matrix3);
printf("new checksum: 0x%04x [%s]\n", new_checksum, new_checksum == prev_checksum ? "ok" : "ko");
return 0;
}
/* functions return non null value on error */
#include <stdio.h>
#include <stdint.h>
#include "hardware.h"
#include "hw_config.h"
static FILE *swap_file = NULL;
static int
init_swap(void)
{
swap_file = fopen(".swap_file", "w+"); /* w+: create, read, write*/
return swap_file == NULL;
}
int
store_to_swap(int vpage, int ppage)
{
if (swap_file == NULL)
if (init_swap())
return -2;
if (fseek(swap_file, vpage << 12, SEEK_SET) == -1)
return -1;
if (fwrite((void*)((ppage << 12) | (uintptr_t)physical_memory),
1, PAGE_SIZE, swap_file) == 0)
return -1;
return 0;
}
int
fetch_from_swap(int vpage, int ppage)
{
if (swap_file == NULL)
if (init_swap())
return -2;
if (fseek(swap_file, vpage << 12, SEEK_SET) == -1)
return -1;
if (fread((void*)((ppage << 12) | (uintptr_t)physical_memory),
1, PAGE_SIZE, swap_file) == 0)
return -1;
return 0;
}
#if !defined(SWAP)
#define SWAP
int store_to_swap(int, int);
int fetch_from_swap(int, int);
#endif // SWAP
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "hw_config.h"
#include "matrix.h"
extern void *virtual_memory;
#define MATRIX_OP MATRIX_ADD
/*#define MATRIX_OP MATRIX_MUL*/
void user_process() {
unsigned short timestamp = (unsigned)time(NULL);
matrix *matrix1 = (matrix*)virtual_memory;
matrix *matrix2 = ((matrix*)virtual_memory) + 1;
matrix *matrix3 = ((matrix*)virtual_memory) + 2;
srand(timestamp);
printf("[Starting user process]\n");
/* print some informations */
printf("matrices size: %dx%d\n", MATRIX_SIZE, MATRIX_SIZE);
printf("vm used: %5d pages\n", 3 * sizeof(matrix) / PAGE_SIZE);
printf("pm space: %5d pages\n", 1 << 8);
printf("vm space: %5d pages\n", 1 << 12);
/* init matrices */
printf("initializing matrices\n");
matrix_init(matrix1);
matrix_init(matrix2);
#if MATRIX_OP == MATRIX_ADD
/* add matrices */
printf("adding VM matrices ");fflush(stdout);
matrix_add(matrix3, matrix1, matrix2);
#elif MATRIX_OP == MATRIX_MUL
/* multiply matrices */
printf("multiplying matrices ");fflush(stdout);
matrix_mult(matrix3, matrix1, matrix2);
#endif
printf("timestamp: 0x%04x, ", timestamp);
printf("operation: %d, ", MATRIX_OP);
printf("checksum: 0x%04x\n", matrix_checksum(matrix3));
}
void show(){
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment