#include "vm.h" #include "vm/asm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { Interrupt* data; size_t capacity; size_t front; size_t back; } InterruptQueue; void int_queue_construct(InterruptQueue* queue, size_t capacity) { *queue = (InterruptQueue) { .data = malloc(sizeof(Interrupt) * capacity), .capacity = capacity, .back = 0, .front = 0, }; } void int_queue_destroy(InterruptQueue* queue) { free(queue->data); } void int_queue_push(InterruptQueue* queue, Interrupt req) { size_t front = queue->front + 1; if (front >= queue->capacity) { front = 0; } if (front == queue->back) { fprintf(stderr, "error: queue overflow\n"); exit(1); } queue->data[queue->front] = req; queue->front = front; } size_t int_queue_size(const InterruptQueue* queue) { return queue->front >= queue->back ? queue->front - queue->back : (queue->capacity - queue->back) + queue->front; } Interrupt int_queue_pop(InterruptQueue* queue) { if (queue->back == queue->front) { fprintf(stderr, "error: queue underflow\n"); exit(1); } Interrupt val = queue->data[queue->back]; size_t back = queue->back + 1; if (back >= queue->capacity) { back = 0; } queue->back = back; return val; } #define ch_width 8 #define ch_height 8 static const int width_in_ch = 40; static const int height_in_ch = 12; static const int px_width = 4; static const int px_height = 8; static const int width_in_px = width_in_ch * ch_width * px_width; static const int height_in_px = height_in_ch * ch_height * px_height; typedef struct { IODevice io_device; SDL_Window* window; SDL_Renderer* renderer; SDL_Texture* buffer_texture; pthread_t render_thread; atomic_bool should_run; pthread_mutex_t mutex; pthread_cond_t interrupt_waiter; InterruptQueue int_queue; } SdlDevice; int sdldevice_construct(SdlDevice* device); void sdldevice_destroy(SdlDevice* device); void sdldevice_set_char( IODevice* device_device, uint16_t offset, uint8_t value); void sdldevice_wait_for_interrupt(IODevice* io_device); void* sdldevice_thread_entry(void* data); void sdldevice_poll_events(SdlDevice* device); Interrupt sdldevice_maybe_next_interrupt(IODevice* io_device); int sdldevice_construct(SdlDevice* device) { int res; res = SDL_Init(SDL_INIT_VIDEO); if (res != 0) { fprintf(stderr, "error: could not init sdl: %s\n", SDL_GetError()); return -1; } SDL_Window* window; SDL_Renderer* renderer; res = SDL_CreateWindowAndRenderer( width_in_px, height_in_px, 0, &window, &renderer); if (res != 0) { fprintf(stderr, "error: could not create window/renderer: %s\n", SDL_GetError()); return -1; } SDL_Texture* buffer_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, width_in_px, height_in_px); if (buffer_texture == NULL) { fprintf(stderr, "error: could not create buffer texture: %s\n", SDL_GetError()); return -1; } *device = (SdlDevice) { .io_device = (IODevice) { .self = device, .set_char = sdldevice_set_char, .wait_for_interrupt = sdldevice_wait_for_interrupt, .maybe_next_interrupt = sdldevice_maybe_next_interrupt, }, .window = window, .renderer = renderer, .buffer_texture = buffer_texture, .render_thread = (pthread_t) { 0 }, .should_run = true, .mutex = PTHREAD_MUTEX_INITIALIZER, .interrupt_waiter = PTHREAD_COND_INITIALIZER, .int_queue = {0}, }; SDL_RenderPresent(device->renderer); pthread_create( &device->render_thread, NULL, sdldevice_thread_entry, device); int_queue_construct(&device->int_queue, 128); return 0; } void sdldevice_destroy(SdlDevice* device) { device->should_run = false; pthread_join(device->render_thread, NULL); pthread_mutex_destroy(&device->mutex); pthread_cond_destroy(&device->interrupt_waiter); int_queue_destroy(&device->int_queue); SDL_DestroyTexture(device->buffer_texture); SDL_DestroyRenderer(device->renderer); SDL_DestroyWindow(device->window); SDL_Quit(); } extern const bool charset[][ch_height][ch_width]; void sdldevice_set_char(IODevice* io_device, uint16_t offset, uint8_t value) { printf("value = %d '%c', offset = %d\n", value, value, offset); if (value < 'A' || value > 'C') return; SdlDevice* device = io_device->self; pthread_mutex_lock(&device->mutex); SDL_Color* buffer; int pitch; int res = SDL_LockTexture( device->buffer_texture, NULL, (void**)&buffer, &pitch); if (res != 0) { fprintf(stderr, "error: could not lock texture: %s\n", SDL_GetError()); pthread_mutex_unlock(&device->mutex); return; } for (int ch_y = 0; ch_y < ch_height; ++ch_y) { for (int ch_x = 0; ch_x < ch_width; ++ch_x) { bool ch = charset[value][ch_y][ch_x]; for (int px_y = 0; px_y < px_height; ++px_y) { for (int px_x = 0; px_x < px_width; ++px_x) { int x = (offset % width_in_ch * ch_width + ch_x) * px_width + px_x; int y = (offset / width_in_ch * ch_height + ch_y) * px_height + px_y; buffer[y * width_in_px + x] = ch ? (SDL_Color) { 0xff, 0xff, 0xff, 0xff } : (SDL_Color) { 0x00, 0x00, 0x00, 0xff }; } } } } SDL_UnlockTexture(device->buffer_texture); SDL_RenderCopy(device->renderer, device->buffer_texture, NULL, NULL); SDL_RenderPresent(device->renderer); pthread_mutex_unlock(&device->mutex); } void sdldevice_wait_for_interrupt(IODevice* io_device) { SdlDevice* device = io_device->self; pthread_mutex_lock(&device->mutex); // printf("vm: waiting for interrupt...\n"); pthread_cond_wait(&device->interrupt_waiter, &device->mutex); // printf("vm: got interrupt!\n"); pthread_mutex_unlock(&device->mutex); } void* sdldevice_thread_entry(void* data) { SdlDevice* device = (SdlDevice*)data; while (device->should_run) { sdldevice_poll_events(device); if (!device->should_run) break; SDL_Delay(6); } return NULL; } static inline bool is_exit_event(SDL_Event* event) { return event->type == SDL_QUIT || (event->type == SDL_KEYDOWN && event->key.keysym.scancode == SDL_SCANCODE_ESCAPE && event->key.keysym.mod & KMOD_CTRL); } void sdldevice_poll_events(SdlDevice* device) { pthread_mutex_lock(&device->mutex); bool should_notify = false; SDL_Event event; while (SDL_PollEvent(&event)) { if (is_exit_event(&event)) { device->should_run = false; int_queue_push(&device->int_queue, (Interrupt) { .type = InterruptType_Shutdown, }); should_notify = true; break; } else if (event.type == SDL_KEYDOWN) { int_queue_push(&device->int_queue, (Interrupt) { .type = InterruptType_KeyEvent, .keycode = (uint16_t)event.key.keysym.scancode, }); should_notify = true; } } pthread_mutex_unlock(&device->mutex); if (should_notify) { // printf("sdldevice: interrupt occured!\n"); pthread_cond_signal(&device->interrupt_waiter); } } Interrupt sdldevice_maybe_next_interrupt(IODevice* io_device) { SdlDevice* device = io_device->self; pthread_mutex_lock(&device->mutex); Interrupt val = { .type = InterruptType_None, }; if (int_queue_size(&device->int_queue) > 0) { val = int_queue_pop(&device->int_queue); } pthread_mutex_unlock(&device->mutex); return val; } typedef struct { Drive drive; uint64_t* mem; } MemDrive; void memdrive_construct(MemDrive* drive, uint64_t* mem, size_t mem_size); void memdrive_drive_read(Drive* in_drive, uint8_t* block, uint16_t i); void memdrive_drive_write(Drive* in_drive, const uint8_t* block, uint16_t i); void memdrive_construct(MemDrive* drive, uint64_t* mem, size_t mem_size) { const uint16_t block_size = 512; *drive = (MemDrive) { .drive = (Drive) { .self = drive, .block_size = block_size, .block_amount = (uint16_t)(mem_size / block_size), .read = memdrive_drive_read, .write = memdrive_drive_write, }, .mem = mem, }; } void memdrive_drive_read(Drive* in_drive, uint8_t* block, uint16_t i) { MemDrive* drive = in_drive->self; memcpy(block, &drive->mem[i * drive->drive.block_size], drive->drive.block_size); } void memdrive_drive_write(Drive* in_drive, const uint8_t* block, uint16_t i) { MemDrive* drive = in_drive->self; memcpy(&drive->mem[i * drive->drive.block_size], block, drive->drive.block_size); } __attribute__((unused)) static inline void dump_program(uint16_t* program) { for (size_t rip = 20; rip < 60; ++rip) { uint16_t val = program[rip]; printf("[%02lx] = %02x %02x %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c " "%s\n", rip * 2, val >> 8, val & 0xff, fmt_binary(val >> 8), fmt_binary(val & 0xff), op_str(val & 0x3f)); } } int main(void) { int res; int main_loop = 1; int interrupt_table = 2; int keyboard_interrupt = 3; #define L(LABEL) s_label(LABEL) Line program_asm[] = { // clang-format off s_nop(), // set video character display flag s_or_i(Rfl, Rfl, 1 << Fl_Vcd), // print ABC s_mov8_mi_i(0x0c00 + 0, 'A'), s_mov8_mi_i(0x0c00 + 1, 'B'), s_mov8_mi_i(0x0c00 + 2, 'C'), // setup stack s_mov16_r_i(Rbp, 2048), // rsp points *at* the top element s_mov16_r_i(Rsp, 2048 - 2), // load interrupt table s_lit_l(interrupt_table), // set interrupt flag s_or_i(Rfl, Rfl, 1 << Fl_Int), L(main_loop), s_hlt(), s_jmp_l(main_loop), s_nop(), L(interrupt_table), // size s_data_i(1), s_data_l(keyboard_interrupt), s_nop(), L(keyboard_interrupt), s_nop(), s_nop(), // clear interrupt flag s_and_i(Rfl, Rfl, (uint16_t)~(1 << Fl_Int)), // setup stack frame s_push_r(Rbp), s_mov16_r_r(Rbp, Rsp), // conserve registers s_push_r(R0), // read keyboard port s_in_i(R0, 0), s_add_i(R0, R0, 'A' - 4), s_mov8_mi_r(0x0c00 + 4, R0), // tear down frame s_pop_r(R0), s_mov16_r_r(Rsp, Rbp), s_pop_r(Rbp), // set interrupt flag s_or_i(Rfl, Rfl, 1 << Fl_Int), // return from interrupt s_iret(), // clang-format on }; size_t program_asm_size = sizeof(program_asm) / sizeof(program_asm[0]); uint16_t* program = calloc(512, sizeof(uint16_t)); assemble_to_binary(program, program_asm, program_asm_size); dump_program(program); /*return 0;*/ MemDrive drive; memdrive_construct(&drive, (uint64_t*)program, 512); SdlDevice io_device; res = sdldevice_construct(&io_device); if (res != 0) { exit(1); } vm_start(&drive.drive, &io_device.io_device); sdldevice_destroy(&io_device); } const char* __asan_default_options(void) { return "detect_leaks=0"; } #define _ false, #define t true, const bool charset[][ch_height][ch_width] = { ['A'] = { { _ _ _ _ _ _ _ _ }, { _ _ _ t t _ _ _ }, { _ t t t t t t _ }, { _ t t _ _ t t _ }, { _ t t _ _ t t _ }, { _ t t t t t t _ }, { _ t t _ _ t t _ }, { _ t t _ _ t t _ }, }, ['B'] = { { _ _ _ _ _ _ _ _ }, { _ t t t t _ _ _ }, { _ t t t t t t _ }, { _ t t _ _ t t _ }, { _ t t t t t _ _ }, { _ t t _ _ t t _ }, { _ t t t t t t _ }, { _ t t t t t _ _ }, }, ['C'] = { { _ _ _ _ _ _ _ _ }, { _ _ t t t t _ _ }, { _ t t t t t t _ }, { _ t t _ _ _ _ _ }, { _ t t _ _ _ _ _ }, { _ t t _ _ _ _ _ }, { _ t t t t t t _ }, { _ _ t t t t _ _ }, }, };