; yaglitch : Yet Another Glitch ; Copyright (C) 2018 Weber Yann ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 3 of the License, or ; any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . ; [bits 64] %include "sdl.asm" %include "utils.asm" %define STACK_SZ 256 %define MAX_FILE_SZ (16 * 17) ; 16 lines of 16 chars + newline chr %define NL "!" section .data op_ptrs: dq OP.t dq OP.put dq OP.drop dq OP.mul dq OP.div dq OP.add dq OP.sub dq OP.mod dq 0 ; syntax error dq OP.lshift dq OP.rshift dq OP.and dq OP.or dq OP.xor dq OP.not dq OP.dup dq OP.pick dq OP.swap dq OP.lt dq OP.gt dq OP.eq audiospec_wanted: dd 8000 ; freq dw AUDIO_U8 ; format db 1 ; channels db 0 ; silence dw 4096 ; samples dd 0 ; size dw 0 ; allign dq audio_cllbck dq 0 ; userdata def_str usage, "Usage : " def_str opts, {" FILE.glitch",0xA} def_str openerr, {'Error opening file : "', 0xA} def_str syntax_error, {"Syntax error", 0xA} def_str bigline_error, {"Line with more than 16 tokens !", 0xA} def_str nl_error, {"Character \n is not the last one", 0xA} def_str badop_error, {"Bad OP", 0xA} ; ; Old stuff for first oscillator ; val: db 0 dir: db 0 dot: db "." nl: db 0xA section .bss ; glitch name (1st line) : len 16 + \0 glitch_name: resb 17 ; program internal repr for stack machine ; each token uses 2 words : 1st for the callback address 2nd for ; optional value glitch_pgm: resq 16 * 16 * 2 ; stack machine ring buffer stack_buff: resd STACK_SZ ; top of stack pointer tosp: resq 1 ; stack machine rgister t: resd 1 ; audiospec returned from SDL audiospec_recv: resb 32 section .text global _start _start: ; checking args mov rcx, [rsp] cmp rcx, 2 jne exit.badarg mov rax, 0x2 ; sys_open mov rdi, [rsp+16] ; argv[1] xor rsi, rsi ; no flags xor rdx, rdx ; O_RDONLY syscall cmp rax, 0 jl exit.err_open push rax ; source fd mov rax, 0xc ; brk xor rdi, rdi syscall push rax ; heap start mov rdi, rax add rdi, MAX_FILE_SZ ; new heap end mov rax, 0xc ; brk syscall pop rsi ; heap start mov r14, rsi xor rax, rax ; sys_read mov rdi, [rsp] ; source_fd mov rdx, MAX_FILE_SZ syscall cmp rax, 0 jl exit.err_open mov r15, rax mov rax, 0x3 ; sys_close pop rdi ; source_fd syscall mov rbx, r15 ;read size mov rdi, r14 ; heap start add rdi, rbx ; heap end mov r15, rdi mov rax, 0xc ; brk syscall ; shrink heap to read size ; init program space mov rdi, glitch_pgm xor rax, rax stosq stosq mov rsi, r14 push rsi xor r13, r13 xor rbx, rbx parse: ; go trhough file , with rsi current ptr, r15 file stop ; r13 will contain updated lineno and rbx chr in current line ; rdi store the destination pointer ; parse glitch name mov rcx, 16 mov rdi, glitch_name .name_loop: inc rbx lodsb test al, al ; EOF jz .no_nl cmp al, 0xA je .trailing_nl cmp al, "!" ; EOL je .name_end cmp al, "_" je .chrname cmp al, "0" jl exit.syntax_error cmp al, "9" jle .chrname cmp al, "a" jl exit.syntax_error cmp al, "z" jg exit.syntax_error .chrname: stosb loop .name_loop jmp exit.bigline .name_end: inc r13 ; lineno xor rbx, rbx xor al, al stosb ; parsing tokens xor edx, edx ; 32bits numeric token value xor eax, eax xor r10, r10 ; numeric token flag (1 mean in numeric token) xor r11, r11 ; numeric token len mov rcx, r15 sub rcx, r14 mov rdi, glitch_pgm .parse_loop: inc rbx lodsb test al, al jz .no_nl cmp al, 0xA je .trailing_nl cmp al, "!" ; EOL je .next_line cmp al, "." ; token separator je .next_token cmp al, "0" jl exit.syntax_error cmp al, "9" jle .dec_token cmp al, "A" jl exit.syntax_error cmp al, "F" jle .hex_token jmp .op_match ; allowing loop near jump .end_op: stosq xor rax, rax stosq jmp .next_token .hex_token: sub al, "F" jmp .numeric_token .dec_token: sub al, "0" .numeric_token: cmp r11, 8 je exit.syntax_error ; Numeric constant OF inc r11 mov r10, 1 shl edx, 8 add edx, eax .next_token: ;inc rbx ; chrno loop .parse_loop .next_line: inc r13 xor rbx, rbx loop .parse_loop .trailing_nl: ; check that NL is the last chr, else syntax error cmp rsi, r15 jne exit.nl_not_last jmp .parse_end .op_match: ; allow loop .parse_loop near jump cmp al, "a" jl exit.syntax_error cmp al, "u" jg exit.syntax_error ; OP shortand matching ; checking for previous numeric token to write test r10, r10 jz .match_op push rax ; add previous numeric token mov rax, OP.numeric stosq xor rax, rax xor r11, r11 ; numeric token length raz xchg rax, rdx shl rax, 32 ; shl to allow reading as dword ? stosq pop rax .match_op: sub al, "a" shl rax, 3 ; mul by 8 (size of ptr) add rax, op_ptrs mov rax, [rax] test rax, rax jz exit.bad_op jmp .end_op .no_nl: ; TODO : print warning ; no NL at EOF .parse_end: ; clean heap mov rax, 0xc pop rdi syscall ; print glitch name mov rax, "Playing " push rax mov rax, 1 mov rdi, 1 mov rsi, rsp mov rdx, 8 syscall pop rax mov rdi, glitch_name call strlen mov rdx, rax mov rax, 1 mov rdi, 1 mov rsi, glitch_name syscall mov rax, `\n` push rax mov rax, 1 mov rdi, 1 mov rsi, rsp mov rdx, 1 syscall pop rax ; init stack machine runtime stack_init: mov rcx, STACK_SZ mov rdi, stack_buff xor rax, rax mov [t], eax .loop_buff_init: stosd loop .loop_buff_init mov al, 7 * 4 mov [tosp], al sdl_init: mov rdi, 0x0000FFFF call SDL_Init mov rdi, audiospec_wanted mov rsi, audiospec_recv call SDL_OpenAudio ;mov rdi, 256 ;mov rsi, 255 ;mov rdx, 32 ;;mov rcx, SDL_SWSURFACE ;xor rcx, rcx ;call SDL_SetVideoMode audio_start: ;start audio xor rdi, rdi call SDL_PauseAudio mov rdi, 2000 call SDL_Delay exit: call SDL_Quit xor rdi, rdi mov rax, 0x3c syscall .err_open: mov rax, 1 mov rdi, 2 mov rsi, openerr mov rdx, openerr_len - 1 syscall mov rdi, [rsp+16] push rdi call strlen mov rdx, rax mov rax, 1 mov rdi, 2 pop rsi syscall mov rax, 1 mov rdi, 2 mov rsi, openerr + openerr_len - 2 mov rdx, 2 syscall .badarg: mov rax, 1 mov rdi, 2 mov rsi, usage mov rdx, usage_len syscall mov rdi, [rsp+8] call strlen mov rdx, rax mov rax, 1 mov rdi, 2 mov rsi, [rsp+8] syscall mov rax, 1 mov rdi, 2 mov rsi, opts mov rdx, opts_len syscall mov rdi, 1 .exit_err: ; with rdi error code mov rax, 0x3c ; exit syscall ; expect : r13 lineno, rbx chr num in line ; TODO: real error message .nl_not_last: mov rsi, nl_error mov rsi, nl_error_len push qword 3 jmp exit.parse_error .syntax_error: mov rsi, syntax_error mov rdx, syntax_error_len push qword 2 jmp exit.parse_error .bigline: mov rsi, bigline_error mov rdx, bigline_error_len push qword 2 jmp exit.parse_error .bad_op: mov rsi, badop_error mov rdx, badop_error_len push qword 2 jmp exit.parse_error .parse_error: ; print error lineno & chrno push rsi ; source ptr push rdx push rbx ; chrno in line sub r14, rsi ; chr count push 14 mov rdi, "chr:0x" mov rsi, 6 call short_err pop rdi ; chr count mov rsi, 2 call print_hnum mov rdi, ",line:0x" mov rsi, 8 call short_err mov rdi, r13 mov rsi, 2 call print_hnum mov rdi, ",col:0x" mov rsi, 7 call short_err pop rdi mov rsi, 2 call print_hnum mov rdi, ` :\t` mov rsi, 3 call short_err pop rdx pop rsi mov rax, 1 mov rdi, 2 syscall pop rdi jmp exit.exit_err short_err: ; rdi is the message (less than 8 chr) ; rsi is message len push rdi mov rdx, rsi mov rsi, rsp mov rax, 1 mov rdi, 1 syscall pop rdi ret strlen: ; rdi containing str pointer ; rax will contain strlen and rdi is unchanged mov rsi, rdi xor rdx, rdx pushf cld .loop: inc rdx lodsb mov cl, al test al, al jnz .loop dec rdx mov rax, rdx popf ret print_hnum: ; rdi : number to print ; rsi : output fd pushf mov rax, rdi xor rcx, rcx push rcx ; using stack as buffer std lea rdi, [rsp + 8] .loop: test rax, rax jz .endloop inc rcx inc rcx push rax and al, 0x0F call .al2digit stosb mov rax, [rsp] shr al, 4 call .al2digit stosb pop rax shr rax, 8 jmp .loop .endloop: mov rax, 1 xchg rdi, rsi inc rsi ;mov rdi, rsi ;mov rsi, rsp mov rdx, rcx syscall pop rax popf ret .al2digit: cmp al, 9 jg .hex add al, "0" ret .hex: add al, "A" - 10 ret audio_cllbck: ; rdi -> *userdata ; rsi -> *stream ; rdx -> stream_len mov rcx, rdx mov rdi, rsi .loop: push rcx push rdi call run_glitch pop rdi stosb pop rcx inc dword [t] loop .loop ret run_glitch: ; Run the glitch_pgm ; return TOSP value in eax mov rsi, glitch_pgm .loop: lodsq test rax, rax jz .end_glitch push rax lodsq mov rdi, rax pop rax push rsi call rax pop rsi jmp .loop .end_glitch: xor rbx, rbx mov bl, [tosp] lea rdi, [stack_buff + rbx] mov eax, [rdi] ; DEBUG ;push rax ;xor rdi, rdi ;mov rdi, rax ;mov rsi, 1 ;call print_hnum ;mov rax, " " ;push rax ;mov rax, 1 ;mov rdi, 1 ;mov rsi, rsp ;mov rdx, 1 ;syscall ;pop rax ;pop rax ; /DEBUG ret OP: .numeric: ; rdi contain the number shr rdi, 32 ._push: ; push rdi (edi) on stack_buff mov eax, edi xor rbx, rbx mov bl, [tosp] add bl, 4 cmp bl, 8 * 4 jl .go_push mov bl, 0 .go_push: mov [tosp], bl lea rdi, [stack_buff+rbx] stosd ret .drop: ; drop just calls pop ._pop: ; pop eax from stack_buff xor rbx, rbx mov bl, [tosp] lea rsi, [stack_buff+rbx] xor rax, rax lodsd test bl, bl jz .pop_no_dec sub bl, 4 jmp .pop_end .pop_no_dec: mov bl, 7 * 4 .pop_end: mov [tosp], bl ret .t: ; push t on the stack mov edi, [t] call ._push ret .put: pushf cld xor rbx, rbx xor rax, rax mov bl, [tosp] lea rsi, [stack_buff+rbx] lodsd and eax, 0xFF inc eax mov edx, eax lodsd neg rdx lea rdi, [stack_buff + rdx] stosq call OP._pop popf ret .mul: call .prep_2arg mul ebx mov edi, eax call OP._push ret .div: call .prep_2arg test ebx, ebx jz .nodiv xor edx, edx div ebx jmp .divend .nodiv: xor eax, eax .divend: mov edi, eax call OP._push ret .add: call .prep_2arg add eax, ebx mov edi, eax call OP._push ret .sub: call .prep_2arg sub eax, ebx mov edi, eax call OP._push ret .mod: call .prep_2arg test ebx, ebx jz .nomod xor edx, edx div ebx jmp .endmod .nomod: xor edx, edx .endmod: mov edi, edx call OP._push ret .lshift: call OP._pop push rax call OP._pop pop rcx .lshift_loop: shl eax, 1 loop .lshift_loop mov edi, eax call OP._push ret .rshift: call OP._pop push rax call OP._pop pop rcx .rshift_loop: shr eax, 1 loop .rshift_loop mov edi, eax call OP._push ret .and: call .prep_2arg and eax, ebx mov edi, eax call OP._push ret .or: call .prep_2arg or eax, ebx mov edi, eax call OP._push ret .xor: call .prep_2arg xor eax, ebx mov edi, eax call OP._push ret .not: call OP._pop neg eax mov edi, eax call OP._push ret .dup: call OP._pop push rax mov edi, eax call OP._push pop rdi call OP._push ret .pick: call OP._pop inc eax and eax, 0xFF neg eax lea rsi, [stack_buff+eax] lodsd push rax call OP._pop pop rdi call OP._push ret .swap: call OP._pop push rax call OP._pop xchg rax, [rsp] mov edi, eax call OP._push pop rdi call OP._push ret .lt: call .prep_2arg xor rdi, rdi cmp eax, ebx jge .lt_false not rdi .lt_false: call OP._push ret .gt: call .prep_2arg xor rdi, rdi cmp eax, ebx jle .gt_false not rdi .gt_false: call OP._push ret .eq: call .prep_2arg xor rdi, rdi cmp eax, ebx jne .eq_false not rdi .eq_false: call OP._push ret .prep_2arg: ; utils that pop both arguments V1 in eax, V2 in ebx call OP._pop push rax call OP._pop pop rbx ret