about summary refs log blame commit diff stats
path: root/subx/apps/pack.subx
blob: 6ac03fc49515031a4b2fb911c7c88734d44521b4 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
                                                                              

                                                                          










                                                                            





                                                                                                                                                 

                                                      
                                      
                                                                           


                                                                                                                                                                                         

                                                                                                                                                                       
                                                                  







                                                                                                                                                                     
                                        


                                                                                                                                                                  
                               
































                                                                                                                                                                                      


                                                                                           



                                                   

                                                                              
                                                                    





                                                                                                           

                                                                                 
                                     
                            
                  
                            


                                                               
                                                              
                                        


                                                       
                                        
                            
                               
                                          
              
                               
                                   





                                                                                                                                                                       
               
               

               





                                                                                                                                                                         




                                                                                                                                                                       
                                                                                                                                                                 
              
                        





                                                                                                                                                                  
                         



                                                                                                                                                                     
                             

                                                                                                                                                                  


                                                                                                                                                                    
                                              
                      















                                                                                                                                                                     
                                 
                      
                           




                                                                                                                                                                     
            
                                 
                   
               

               
                             
                      
                                                                                                                                                                  
                
                                                                
                                      
                   
               
              
                                

                                                                                                                                                                  
                                                  
                               
                                                         
                
                            































                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     


                                                   
                                    








                                                                                                                                                                  
                               
                                               
                                   
                   
               
               



                                                                                                                                                                  
































                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     












                                                                                                                                                                       









                                                                                                                                                                   

                                         









                                                                                                                                                                      
              








                                                                                                                                                                      

                                 
                                  
                   
               

                                                                                                                                                                      
                                     

                                                                                                                                                                  
            










                                                                                                                                                                      
                                                                                                                                                                  
                         

                 
                 
                 




                                                                                                                                                                       

                                                 



                                                                                                                                                                       
                                        
                   
                                     



                                                                                                                                                                  
                                                 
                   
                                                   





                                                                                                                                                                  
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   


                                                    
              
                                
                      


                                                                                                                                                                  
                   

                                             
              
                           

                                                                                                                                                                  
                                             
                                         
                   
                                             



                                                                                                                                                                  
                                                        
                   


                                                                








                                                                                                                                                                       

                                                       



                                                                                                                                                                       
                                        
                   
                                     



                                                                                                                                                                  
                                                 
                   
                                                   





                                                                                                                                                                  
                                         
                   









                                                                                                                                                                  




                                                                                                                                                                  
                                         
                   

                                     



                                                                                                                                                                  
                                                                    
                   

                                             
              
                           

                                                                                                                                                                  
                                             
                                         
                   
                                             



                                                                                                                                                                  
                                                                      
                   


                                                                         
              
                                                






                                                                                                                                                                       

                                                         



                                                                                                                                                                       
                                        
                   
                                     



                                                                                                                                                                  
                                                 
                   
                                                   





                                                                                                                                                                  
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   


                                                    
              

















                                                                                                                                                                  

                                                                                                                                                                  
                                             
                                         
                   
                                             



                                                                                                                                                                  
                                                               
                   


                                                                    








                                                                                                                                                                       






































                                                                                                                                                                       


                 

                                            
                              




                                                                                                                                                                  
                                              
                   
                              




                                                                                                                                                                  
                                                
                   
                                














                                                                                                                                                                  
                               
                      
                         












                                                                                                                                                                     
                                 
                      
                           












































                                                                                                                                                                       
                                    
                                                            









































                                                                                                                                                                       
                                              
                   
                              




                                                                                                                                                                  
                                                        
                   
                                        




                                                                                                                                                                  
                                                        
                   
                                        




                                                                                                                                                                  
                                              
                   
                              




                                                                                                                                                                  
                                                
                   
                                



































                                                                                                                                                                     
                                 
                      
                           






























































                                                                                                                                                                       
                                                                                      
                 
                               
                  
                                      

                                                                                                         
                                                               
                                        
                 
                                                             
                                        
                 
                                               
                                    
                                                                                           

                                    




                                                                                                                                                                       
               
               
               



                                                                                                                                                                       
                      
                                  

                            
                              
                 
                            

                                                                                                                                                                     
                                     

                                                                                                                                                                        
                              
                 
                                   

                                                                                                                                                                     
                                 
                      
                           
                              
                 
                            

                                                                                                                                                                     
            
                   







                                                                                                                                                                     
                            
                                  

                            
                              
                 
                            

                                                                                                                                                                     









                                                                                                                                                                     






                                                                                                                                                                     
                        
                      

                            


                                                                                                                                                                     
                                 
                      
                           


                              

                                                                                                                                                                     
            
                     
                                         






                                                                                                                                                                  
                           
                               


                                                       




                                                                                                                                                                               
                                       
                                       













                                                                                                                                                                      




                                                                                                                                                                               
                                       
                                        












                                                                                                                                                                      
                                             
                                         





                                 
                                                                                                                                                                  
                                              
                               










                                                                                                                                                                      
                                      

                              
                   
                    





                                                                                                                                                                      
                                      
                    
                  

                                                                                                                                                                  
                         
                 
                 
                 



                                                                                                                                                                       
 





















                                                                                                                                                                       
                                                    






















                                                                                                                                                                  
                                             






                                                                                                                                                                  
















                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     



























































                                                                                                                                                                       
                                             



















                                                                                                                                                                       
 














































                                                                                                                                                                       
                                             









                                                                                                                                                                  
                                









                                                                                                                                                                       
                                
                                                            



















































                                                                                                                                                                       
                                                                    













                                                                                                                                                                       

                                                                                 



















































                                                                                                                                                                       
                                                           












                                                                                                                                                                       





























                                                                                                                                                                       
                                        






















                                                                                                                                                                  
                                                              










































                                                                                                                                                                       
                                              






















                                                                                                                                                                  
                                                                  


































































                                                                                                                                                                       

                               
                      
                         












                                                                                                                                                                     
                                 
                      
                           




                                                                                                                                                                     
            













                                                                                                                                                                       






































































                                                                                                                                                                       
                                 
                      
                           



















                                                                                                                                                                       


                                                                                     
                                      

                                                                                                                                                 

                                                                    
                                             
                                                                                                        

                                                                                             
                          

                                        
                                      

                                              
                                      

                                            
                                      
                




































                                                                                                                                                                       
                               

                                                                 
                                                                           





                                                                                                                                                                               
                                       

                                                             
                                                                         





                                                                                                                                                                               
                                        
                                                                   
                                  
                                  



                                                                                                                                                                      
                                     






                                                                                                                                                                  
                                                                                                                                                                      






                                                                                                                                                                     
                                                                                                                                                                      






                                                                                                                                                                     
                                                                                                                                                                      






                                                                                                                                                                     
                                                                                                                                                                      






                                                                                                                                                                     
                                                                                                                                                                      






                                                                                                                                                                     
                                                                                                                                                                      





                                                                                                                                                                     

                                                                                                                                                                  









                                                                                                                                                                       







                               

                           



                                                                     
                             















                                                                                           




                                                                                                                                                                       


















                                                                                                                                                                       
                  



















                                                                                                                                                                     
                              





                                                                                                                                                                               
                                                 

















                                                                                                                                                                      

















                                                                                                                                                                      
                               










                                                                                                                                                                  
                               










                                                                                                                                                                  
                               



































































                                                                                                                                                                               
                               










                                                                                                                                                                  
                               

                                                 
                                           




















































                                                                                                                                                                               
                  

                                                                                                                                                                  
                         



                 







                                                                                                                                                                       
                                                          
                               
                  
                                      
                                             

                                                        
                                                                       

                                                     
                                                                        

                                                                                          
                                                                       
                             











                                                                                                                                                                       










































                                                                                                                                                                        
                                 
                      
                           














































                                                                                                                                                                     
                                 
                      
                           










































                                                                                                                                                                               
                               


























                                                                                                                                                                       
                               


























                                                                                                                                                                       
                               


























                                                                                                                                                                       
                               




















                                                                                                                                                                       



                                                                                                                                                                                




                                                                                                                                                                                 












                                                                                                                                                                                 
                

                                                                                                                                                                  
                         





                 







                                                                                                                                                                       
                               

                                      
                                             

                                                        
                                                                        

                                                     
                                                                         

                                                     
                                                                         
                           






                                




                                                                                                                                                                       










































                                                                                                                                                                        
                                 
                      
                           














































                                                                                                                                                                     
                                 
                      
                           










































                                                                                                                                                                               
                               


























                                                                                                                                                                       
                               


























                                                                                                                                                                       
                               










































                                                                                                                                                                                 
              

                                                                                                                                                                  
                         





                 






                                                                                                                                                                       
                           
                               
                  



                                                        
                                    
                 
                                                
                                    
                 
                                               
                                    
                 




                                                                                                                                                                       


               
                                 


                                                                                                                                                                       







                                                                                                                                                                     
                                  

                            
                              
                 
                            

                                                                                                                                                                     
                                       

                                                                                                                                                                        




                                                                                                                                                                     
                                 
                      
                           
                              
                 
                            



                                                                                                                                                                     
                                 






                                                                                                                                                                     

                                  

                            
                              
                 
                            

                                                                                                                                                                     









                                                                                                                                                                     






                                                                                                                                                                     
                        
                      

                            


                                                                                                                                                                     
                                 
                      
                           


                              



                                                                                                                                                                     

                                         









                                                                                                                                                                  

                                                    







                                                                                                                                                                               
                                              
                                          





                                 
                                                                                                                                                                  
                                     
                               

                                                       
                              

                    
               

                                                                                                                                                                      
                        

                                                                                                                                                                  
           

                                    
                                                   






                                          
                                                                                                                                                                  
                                     
                               

                                                      
                              

                    
               

                                                                                                                                                                      
                        

                                                                                                                                                                  
           

                                    
                                             






                                         
                                                                                                                                                                  
                          
                               

                                               
                              

                    
               

                                                                                                                                                                      
                        

                                                                                                                                                                  
           
                 

                                                                                                                                                                  
                         


                 






                                                                                                                                                                       
                           
                               
                  



                                                        
                                    
                 
                                               
                                    
                 
                                              
                                    

                 



                                                                                                                                                                       


               
                                 


                                                                                                                                                                       






                                                                                                                                                                     
                      
                                  

                            
                              
                 
                            

                                                                                                                                                                     
                                       

                                                                                                                                                                        
                              
                 
                                   

                                                                                                                                                                     
                                 
                      
                           
                              
                 
                            



                                                                                                                                                                     
                                 






                                                                                                                                                                     

                                  

                            
                              
                 
                            

                                                                                                                                                                     









                                                                                                                                                                     






                                                                                                                                                                     
                        
                      

                            


                                                                                                                                                                     
                                 
                      
                           


                              



                                                                                                                                                                     

                                         









                                                                                                                                                                  
                                                    








                                                                                                                                                                               
                                             
                                         





                                 
                                                                                                                                                                  
                                     
                               

                                                     
                              

                    
               

                                                                                                                                                                      
                        

                                                                                                                                                                  
           

                                   
                                             






                                         
                                                                                                                                                                  
                                     
                               

                                                    
                              

                    
               

                                                                                                                                                                      
                        

                                                                                                                                                                  
           

                                   
                                            






                                        
                                                                                                                                                                  
                          
                               

                                              
                              

                    
               

                                                                                                                                                                      
                        

                                                                                                                                                                  
           
                

                                                                                                                                                                  
                         


                 




                                                                                                                                                                       





                                                                                                                                                                       





                                                                                                                                                                      
                                  



                                                                                                                                                                      
                                     





























                                                                                                                                                                       
                                                    























































































































































































                                                                                                                                                                       
                                 
                      
                           


























































































                                                                                                                                                                       
                                 
                      
                           





























































                                                                                                                                                                       
              
                                       

                                                                                                                                                                  

                                         
                   
                                             
              
                         
                      

















                                                                                                                                                                     
                                 
                      
                           














                                                                                                                                                                     




                                                                                                                                                                       

                                                                       



















                                                                                                                                                                       
                                                    






                                                                                                                                                                  
                                                          
                   
                                          












                                                                                                                                                                  
                  






                                                                                                                                                                  
















                                                                                                                                                                     
                                 
                      
                           






                                                                                                                                                                     
                   

                                                                   









                                                                                                                                                                       

                                                                          



























                                                                                                                                                                       
                                                                
                   
                                                












                                                                                                                                                                  
                  






                                                                                                                                                                  
















                                                                                                                                                                     
                                 
                      
                           






                                                                                                                                                                     
                   

                                                                      









                                                                                                                                                                       

                                                                          



























                                                                                                                                                                       
                                                                
                   
                                                





































                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     
                                                                                               
                   

                                                                      









                                                                                                                                                                       

                                                                                     



























                                                                                                                                                                       
                                                          
                   
                                          





































                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     
                                                                                         
                   

                                                                        









                                                                                                                                                                       

                                                                  



























                                                                                                                                                                       
                                                                
                   
                                                





































                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     
                                                                                            
                   

                                                                               









                                                                                                                                                                       

                                                                  



























                                                                                                                                                                       
                                                                
                   
                                                





































                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     
                                                                                            
                   

                                                                               









                                                                                                                                                                       

                                                      



























                                                                                                                                                                       
                                                               
                   
                                               





































                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     
                                                                                           
                   

                                                                  









                                                                                                                                                                       

                                                        


                                                                                                                                                                       

                                        
                   
                                     
              



















                                                                                                                                                                  
                                                                 
                   
                                                 


                                     

                                                                                                                                                                  
                                                                         
                   

                                             
              
                                       

                                                                                                                                                                  
























                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     
                                                                                             
                   

                                                                             




                                                                                                                                                                  




                                                                                                                                                                       

                                                           



                                                                                                                                                                       
                                        
                   
                                     



                                                                                                                                                                  
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    

                          




                                                                                                                                                                  
                                                         
                   
                                         
                                     



                                                                                                                                                                  
                                                                         
                   

                                             



                                                                                                                                                                  
                  
                                         
                   
                                             



                                                                                                                                                                  
















                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     
                                                                                     
                   

                                                                                   
                                      



                                                                                                                                                                  




                                                                                                                                                                       

                                                            



                                                                                                                                                                       
                                        
                   
                                     



                                                                                                                                                                  
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   


                                                    




                                                                                                                                                                  
                                                        
                   
                                        
                                     



                                                                                                                                                                  
                                                                         
                   

                                             



                                                                                                                                                                  
                  
                                         
                   
                                             



                                                                                                                                                                  
















                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     
                                                                                    
                   

                                                                                    
                                      



                                                                                                                                                                  

                                                                                                                                                                       


                 

                                                           



























                                                                                                                                                                       
                                                         
                   
                                         





































                                                                                                                                                                     
                                 
                      
                           





                                                                                                                                                                     
                                                                                     
                   

                                                                                   






                                                                                                                                                                       


                 






































































                                                                                                                                                                       
                                 
                      
                           


























































































                                                                                                                                                                       
                                 
                      
                           


























































































                                                                                                                                                                       
                                 
                      
                           


























































































                                                                                                                                                                       
                                 
                      
                           



















                                                                                                                                                                       






































































                                                                                                                                                                       
                                 
                      
                           


























































































                                                                                                                                                                       
                                 
                      
                           


























































































                                                                                                                                                                       
                                 
                      
                           



















                                                                                                                                                                       






































































                                                                                                                                                                       
                                 
                      
                           



















                                                                                                                                                                       






































































                                                                                                                                                                       
                                 
                      
                           



























































































                                                                                                                                                                       
                                 
                      
                           



























































































                                                                                                                                                                       
                                 
                      
                           












                                                                                                                                                                     



































                                                                                                                                                                             
                                                         



                                                                                                                                                                        
                             









                                                                                                                                                                                        

                                                       







                                                                                                                                                                              
                        






















































































































































































                                                                                                                                                                              






                                                                                                                                                                       

                                                                               

                                                                                           
                  
                                                            

                                                





                                                                                                                                                                       



               

                                                                                                                                                                            

                                                                                                                                                                            



                                                                                                                                                                       






















                                                                                                                                                                            
                                          







                                                                                                                                                                  
                               
                                                    
                                            








                                                                                                                                                                      
                               
                                                   








                                                                                                                                                                            

                                                                                                                                                                  
                         




























                                                                                                                                                                             
                                                                                                                                                                  

































                                                                                                                                                                             
                                                                                                                                                                  

































                                                                                                                                                                             
                                                                                                                                                                  

































                                                                                                                                                                             
                                                                                                                                                                  

































                                                                                                                                                                             
                                                                                                                                                                  








                                                                                                                                                                  




                                                                                                                                                                       
                                                                               

                                                          

                                                                                     












                                                                                                                                                                             
                                                                








                                                                                                                                                                     
                                                                  
                                  


                   
                                  

                                                                                                                                                                  
                     
                               
                                         
           







                                                                                                                                                                     







                                                                                                                                                                     

                            

                                                                               


                                                                              
              
                                        





                                                                                                                                                                  
                                   
























                                                                                                                                                                       
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    





                                                                                                                                                                  



                                                                                                                                                                       
                                                


                    
                                             



                                                                                                                                                                  
                                       
                   
                                             



                                                                                                                                                                  
                                                         


                                           
                                      














                                                                                                                                                                       
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    





                                                                                                                                                                  



                                                                                                                                                                       
                                                


                    
                                             



                                                                                                                                                                  
                                       
                   
                                             



                                                                                                                                                                  
                                                            

                                           
                           
                                      
              
                                      











                                                                                                                                                                       
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    









                                                                                                                                                                       
                                                


                    
                                             



                                                                                                                                                                  
                                       
                   
                                             




                                                                                                                                                                  
                                                            
                   

                                                       
                                      
              
                                      











                                                                                                                                                                       
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    









                                                                                                                                                                       
                                                


                    
                                             



                                                                                                                                                                  
                                       
                   
                                             



                                                                                                                                                                  
                                                         

                                             
                         
                                      
              
                                      











                                                                                                                                                                       
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    









                                                                                                                                                                       
                                                


                    
                                             



                                                                                                                                                                  
                                       
                   
                                             



                                                                                                                                                                  
                                                          

                                                           
                          
                                      
              
                                      






                                                                                                                                                                       




                                                                                                                                                                       
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    









                                                                                                                                                                       
                                                


                    
                                             



                                                                                                                                                                  
                                       
                   
                                             



                                                                                                                                                                  
















                                                                                                                                                                     
                                 
                      
                           






                                                                                                                                                                     

                                                                 

                                      








                                                                                                                                                                       












                                                                                                                                                                       
               














                                                                                                                                                                             
                               





                                                                                                                                                                               
                                  


                                                 
                                   


                                                 
                                   










                                                  
                 


















































































































































































                                                                                                                                                                       
                                                                                        
                                                                          


















                                                                                                                                                                             
                                                                                                                                                                            


















                                                                                                                                                                                                       
                   






                                 
                 








                                                                                                                                                                       
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    





                                                                                                                                                                  
                                                   


                       
                                             



                                                                                                                                                                  
                                       
                   
                                             



                                                                                                                                                                  
                                                              


                                                  

                                             









                                                                                                                                                                      
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    





                                                                                                                                                                  
                                                     


                         
                                             



                                                                                                                                                                  
                                       
                   
                                             



                                                                                                                                                                  
                                                            

                                                      
                           
                                      
              
                                      



                                                                                                                                                                  


                       
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    





                                                                                                                                                                  
                                                   


                       
                                             



                                                                                                                                                                  
                                       
                   
                                             



                                                                                                                                                                  
                                                  

                                                 
                           
                                      
              
                                      



                                                                                                                                                                  
 

                       
                                         
                   
                                      



                                                                                                                                                                  
                                                  
                   
                                                    





                                                                                                                                                                  
                                                 


                     
                                             



                                                                                                                                                                  
                                       
                   
                                             



                                                                                                                                                                  
                                                         

                                                 
                           
                                      
              
                                      




                                                                                                                                                                  































                                                                                                                                                                            

                                                                                                                                                                  







                                                                                                                                                                       




                             


                                      



                           




                                         
 






                                                 



                                  
                            
# Read a text file of SubX instructions from stdin, and convert it into a list
# of whitespace-separated ascii hex bytes on stdout. Label definitions and
# uses are left untouched.
#
# To run (from the subx/ directory):
#   $ ./subx translate *.subx apps/pack.subx -o apps/pack
#   $ echo '05/add-to-EAX 0x20/imm32'  |./subx run apps/pack
# Expected output:
#   05 20 00 00 00  # 05/add-to-EAX 0x20/imm32
# The original instruction gets included as a comment at the end of each
# converted line.
#
# There's zero error-checking. For now we assume the input program is valid.
# We'll continue to rely on the C++ version for error messages.

== code
#   instruction                     effective address                                                   register    displacement    immediate
# . op          subop               mod             rm32          base        index         scale       r32
# . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes

Entry:  # run tests if necessary, convert stdin if not

    # for debugging: run a single test
#?     e8/call test-emit-non-number-with-all-hex-digits-and-metadata/disp32
#?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
#?     eb/jump  $main:end/disp8

    # . prolog
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # - if argc > 1 and argv[1] == "test", then return run_tests()
    # . argc > 1
    81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
    7e/jump-if-lesser-or-equal  $run-main/disp8
    # . argv[1] == "test"
    # . . push args
    68/push  "test"/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  kernel-string-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . check result
    3d/compare-EAX-and  1/imm32
    75/jump-if-not-equal  $run-main/disp8
    # . run-tests()
    e8/call  run-tests/disp32
    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
    eb/jump  $main:end/disp8
$run-main:
    # - otherwise convert stdin
    # var ed/EAX : exit-descriptor
    81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
    89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
    # configure ed to really exit()
    # . ed->target = 0
    c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
    # return convert(Stdin, 1/stdout, 2/stderr, ed)
    # . . push args
    50/push-EAX/ed
    68/push  Stderr/imm32
    68/push  Stdout/imm32
    68/push  Stdin/imm32
    # . . call
    e8/call  convert/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # . syscall(exit, 0)
    bb/copy-to-EBX  0/imm32
$main:end:
    b8/copy-to-EAX  1/imm32/exit
    cd/syscall  0x80/imm8

# - big picture
# We'll operate on each line/instruction in isolation. That way we only need to
# allocate memory for converting a single instruction.
#
# To pack an entire file, convert every segment in it
# To convert a code segment, convert every instruction (line) until the next segment header
# To convert a non-data segment, convert every word until the next segment header
#
# primary state: line
#   stream of 512 bytes; abort if it ever overflows

# conceptual hierarchy within a line:
#   line = words separated by ' ', maybe followed by comment starting with '#'
#   word = datum until '/', then 0 or more metadata separated by '/'
#
# we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives:
#   next-token(stream, delim char) -> slice (start, end pointers)
#   next-token-from-slice(start, end, delim char) -> slice
#   slice-equal?(slice, string)

convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
    # pseudocode:
    #   var line = new-stream(512, 1)
    #   var in-code? = false
    #   while true
    #     clear-stream(line)
    #     read-line(in, line)
    #     if (line->write == 0) break             # end of file
    #     var word-slice = next-word(line)
    #     if slice-empty?(word-slice)             # whitespace
    #       write-stream-data(out, line)
    #     else if (slice-equal?(word-slice, "=="))
    #       word-slice = next-word(line)
    #       in-code? = slice-equal?(word-slice, "code")
    #       write-stream-data(out, line)
    #     else if (in-code?)
    #       rewind-stream(line)
    #       convert-instruction(line, out)
    #     else
    #       rewind-stream(line)
    #       convert-data(line, out)
    #   flush(out)
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    53/push-EBX
    # var line/ECX : (address stream byte) = stream(512)
    81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
    68/push  0x200/imm32/length
    68/push  0/imm32/read
    68/push  0/imm32/write
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # var word-slice/EDX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/curr
    89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
    # var in-code?/EBX = false
    31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
$convert:loop:
    # clear-stream(line)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # read-line(in, line)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  read-line/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$convert:check0:
    # if (line->write == 0) break
    81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
    0f 84/jump-if-equal  $convert:break/disp32
#?     # dump line {{{
#?     # . write(2/stderr, "LL: ")
#?     # . . push args
#?     68/push  "LL: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, line)
#?     # . . push args
#?     51/push-ECX
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # next-word(line, word-slice)
    # . . push args
    52/push-EDX
    51/push-ECX
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$convert:check1:
    # if (slice-empty?(word-slice)) write-stream-data(out, line)
    # . EAX = slice-empty?(word-slice)
    # . . push args
    52/push-EDX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) write-stream-data(out, line)
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $convert:pass-through/disp32
$convert:check2:
#?     # dump word-slice {{{
#?     # . write(2/stderr, "AA: ")
#?     # . . push args
#?     68/push  "AA: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . clear-stream(Stderr+4)
#?     # . . push args
#?     b8/copy-to-EAX  Stderr/imm32
#?     05/add-to-EAX  4/imm32
#?     50/push-EAX
#?     # . . call
#?     e8/call  clear-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write-slice(Stderr, word-slice)
#?     # . . push args
#?     52/push-EDX
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  write-slice/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . flush(Stderr)
#?     # . . push args
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  flush/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # if (slice-equal?(word-slice, "=="))
    #   word-slice = next-word(line)
    #   in-code? = slice-equal?(word-slice, "code")
    #   write-stream-data(out, line)
    # . EAX = slice-equal?(word-slice, "==")
    # . . push args
    68/push  "=="/imm32
    52/push-EDX
    # . . call
    e8/call  slice-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto check3
    3d/compare-EAX-and  0/imm32
    0f 84/jump-if-equal  $convert:check3/disp32
    # . next-word(line, word-slice)
    # . . push args
    52/push-EDX
    51/push-ECX
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump segment name {{{
#?     # . write(2/stderr, "AA: ")
#?     # . . push args
#?     68/push  "AA: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . clear-stream(Stderr+4)
#?     # . . push args
#?     b8/copy-to-EAX  Stderr/imm32
#?     05/add-to-EAX  4/imm32
#?     50/push-EAX
#?     # . . call
#?     e8/call  clear-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write-slice(Stderr, word-slice)
#?     # . . push args
#?     52/push-EDX
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  write-slice/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . flush(Stderr)
#?     # . . push args
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  flush/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . in-code? = slice-equal?(word-slice, "code")
    # . . push args
    68/push  "code"/imm32
    52/push-EDX
    # . . call
    e8/call  slice-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . . in-code? = EAX
    89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
    # . goto pass-through
    eb/jump  $convert:pass-through/disp8
$convert:check3:
    # else rewind-stream(line)
    # . rewind-stream(line)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  rewind-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # if (in-code? != 0) convert-instruction(line, out)
    81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
    74/jump-if-equal  $convert:data/disp8
$convert:code:
    # . convert-instruction(line, out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    51/push-ECX
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . loop
    e9/jump  $convert:loop/disp32
$convert:data:
    # else convert-data(line, out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    51/push-ECX
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . loop
    e9/jump  $convert:loop/disp32
$convert:pass-through:
    # write-stream-data(out, line)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-stream-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . loop
    e9/jump  $convert:loop/disp32
$convert:break:
    # flush(out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
$convert:end:
    # . reclaim locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
    # . restore registers
    5b/pop-to-EBX
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-passes-empty-lines-through:
    # if a line is empty, pass it along unchanged
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-input-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-input-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # write nothing to input
    # convert(_test-input-buffered-file, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-buffered-file/imm32
    # . . call
    e8/call  convert/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that the line just passed through
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "", msg)
    # . . push args
    68/push  "F - test-convert-passes-empty-lines-through"/imm32
    68/push  ""/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-passes-lines-with-just-whitespace-through:
    # if a line is empty, pass it along unchanged
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-input-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-input-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "    ")
    # . . push args
    68/push  "    "/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert(_test-input-buffered-file, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-buffered-file/imm32
    # . . call
    e8/call  convert/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that the line just passed through
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
    # . . push args
    68/push  "F - test-convert-passes-with-just-whitespace-through"/imm32
    68/push  "    "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-passes-segment-headers-through:
    # if a line starts with '==', pass it along unchanged
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-input-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-input-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "== abcd")
    # . . push args
    68/push  "== abcd"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert(_test-input-buffered-file, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-buffered-file/imm32
    # . . call
    e8/call  convert/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that the line just passed through
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "== abcd", msg)
    # . . push args
    68/push  "F - test-convert-passes-segment-headers-through"/imm32
    68/push  "== abcd"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-in-data-segment:
    # correctly process lines in the data segment
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-input-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-input-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    #   == code
    #   == data
    #   3 4/imm32
    # . write(_test-input-stream, "== code")
    # . . push args
    68/push  "== code\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "== data\n")
    # . . push args
    68/push  "== data\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "3 4/imm32\n")
    # . . push args
    68/push  "3 4/imm32\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert(_test-input-buffered-file, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-buffered-file/imm32
    # . . call
    e8/call  convert/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
#?     # debug print {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
    # . . push args
    68/push  "F - test-convert-in-data-segment/0"/imm32
    68/push  "== code"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
    # . . push args
    68/push  "F - test-convert-in-data-segment/1"/imm32
    68/push  "== data"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
    # . . push args
    68/push  "F - test-convert-in-data-segment/2"/imm32
    68/push  "03 04 00 00 00 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-code-and-data-segments:
    # correctly process lines in both code and data segments
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-input-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-input-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    #   == code
    #   e8/call 20/disp32
    #   68/push 0x20/imm8
    #   == data
    #   3 4/imm32
    # . write(_test-input-stream, "== code\n")
    # . . push args
    68/push  "== code\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "e8/call 20/disp32\n")
    # . . push args
    68/push  "e8/call 20/disp32\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "68/push 0x20/imm8\n")
    # . . push args
    68/push  "68/push 0x20/imm8\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "== data\n")
    # . . push args
    68/push  "== data\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write(_test-input-stream, "3 4/imm32\n")
    # . . push args
    68/push  "3 4/imm32\n"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert(_test-input-buffered-file, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-buffered-file/imm32
    # . . call
    e8/call  convert/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    #   == code
    #   e8 20 00 00 00  # e8/call 20/disp32
    #   68 20  # 68/push 0x20/imm8
    #   == data
    #   03 04 00 00 00
#?     # debug print {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
    # . . push args
    68/push  "F - test-convert-code-and-data-segments/0"/imm32
    68/push  "== code"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
    # . . push args
    68/push  "F - test-convert-code-and-data-segments/1"/imm32
    68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
    # . . push args
    68/push  "F - test-convert-code-and-data-segments/2"/imm32
    68/push  "68 20  # 68/push 0x20/imm8"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
    # . . push args
    68/push  "F - test-convert-code-and-data-segments/3"/imm32
    68/push  "== data"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
    # . . push args
    68/push  "F - test-convert-code-and-data-segments/4"/imm32
    68/push  "03 04 00 00 00 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-next-stream-line-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

convert-data:  # line : (address stream byte), out : (address buffered-file) -> <void>
    # pseudocode:
    #   var word-slice = {0, 0}
    #   while true
    #     word-slice = next-word(line)
    #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
    #       break  # skip emitting some whitespace
    #     if slice-starts-with?(word-slice, "#")      # comment
    #       write-slice(out, word-slice)
    #       break
    #     if slice-ends-with?(word-slice, ":")        # label
    #       write-stream-data(out, line)
    #       break
    #     if has-metadata?(word-slice, "imm32")
    #       emit(out, word-slice, 4)
    #     # disp32 is not permitted in data segments, and anything else is only a byte long
    #     else
    #       emit(out, word-slice, 1)
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    # var word-slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
#?     # dump line {{{
#?     # . write(2/stderr, "LL: ")
#?     # . . push args
#?     68/push  "LL: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # write-stream(2/stderr, line)
#?     # . . push args
#?     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
$convert-data:loop:
    # next-word(line, word-slice)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump word-slice {{{
#?     # . write(2/stderr, "AA: ")
#?     # . . push args
#?     68/push  "AA: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . clear-stream(Stderr+4)
#?     # . . push args
#?     b8/copy-to-EAX  Stderr/imm32
#?     05/add-to-EAX  4/imm32
#?     50/push-EAX
#?     # . . call
#?     e8/call  clear-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write-slice(Stderr, word-slice)
#?     # . . push args
#?     51/push-ECX
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  write-slice/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . flush(Stderr)
#?     # . . push args
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  flush/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
$convert-data:check0:
    # if (slice-empty?(word-slice)) break
    # . EAX = slice-empty?(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) break
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $convert-data:break/disp32
$convert-data:check-for-comment:
    # if (slice-starts-with?(word-slice, "#"))
    # . start/EDX = word-slice->start
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    # . if (EAX != '#') goto next check
    3d/compare-EAX-and  0x23/imm32/hash
    75/jump-if-not-equal  $convert-data:check-for-label/disp8
$convert-data:comment:
    # write-slice(out, word-slice)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # break
    75/jump-if-not-equal  $convert-data:break/disp8
$convert-data:check-for-label:
    # if (slice-ends-with?(word-slice, ":"))
    # . end/EDX = word-slice->end
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
    # . c/EAX = *(end-1)
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
    # . if (EAX != ':') goto next check
    3d/compare-EAX-and  0x3a/imm32/colon
    75/jump-if-not-equal  $convert-data:check-for-imm32/disp8
$convert-data:label:
    # write-stream-data(out, line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-stream-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # break
    75/jump-if-not-equal  $convert-data:break/disp8
$convert-data:check-for-imm32:
    # if (has-metadata?(word-slice, "imm32"))
    # . EAX = has-metadata?(ECX, "imm32")
    # . . push args
    68/push  "imm32"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) process as a single byte
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $convert-data:single-byte/disp8
$convert-data:imm32:
    # emit(out, word-slice, 4)
    # . . push args
    68/push  4/imm32
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    e9/jump  $convert-data:loop/disp32
$convert-data:single-byte:
    # emit(out, word-slice, 1)
    # . . push args
    68/push  1/imm32
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    e9/jump  $convert-data:loop/disp32
$convert-data:break:
$convert-data:end:
    # . reclaim locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . restore registers
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-data-passes-comments-through:
    # if a line starts with '#', pass it along unchanged
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "# abcd")
    # . . push args
    68/push  "# abcd"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-data(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that the line just passed through
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "# abcd", msg)
    # . . push args
    68/push  "F - test-convert-data-passes-comments-through"/imm32
    68/push  "# abcd"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-data-passes-labels-through:
    # if the first word ends with ':', pass along the entire line unchanged
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "ab: # cd")
    # . . push args
    68/push  "ab: # cd"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-data(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that the line just passed through
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
    # . . push args
    68/push  "F - test-convert-data-passes-labels-through"/imm32
    68/push  "ab: # cd"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-data-passes-names-through:
    # If a word is a valid name, just emit it unchanged.
    # Later phases will deal with it.
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "abcd/imm32")
    # . . push args
    68/push  "abcd/imm32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-data(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that the line just passed through
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "abcd/imm32", msg)
    # . . push args
    68/push  "F - test-convert-data-passes-names-through"/imm32
    68/push  "abcd/imm32 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-data-handles-imm32:
    # If a word has the /imm32 metadata, emit it in 4 bytes.
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "30/imm32")
    # . . push args
    68/push  "30/imm32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-data(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that 4 bytes were written
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "30 00 00 00 ", msg)
    # . . push args
    68/push  "F - test-convert-data-handles-imm32"/imm32
    68/push  "30 00 00 00 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-data-handles-single-byte:
    # Any metadata but /imm32 will emit a single byte.
    # Data segments can't have /disp32, and SubX doesn't support 16-bit operands.
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "30/imm16")
    # . . push args
    68/push  "30/imm16"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-data(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that a single byte was written (imm16 is not a valid operand type)
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "30 ", msg)
    # . . push args
    68/push  "F - test-convert-data-handles-single-byte"/imm32
    68/push  "30 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-data-multiple-bytes:
    # Multiple single-byte words in input stream get processed one by one.
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "1 2")
    # . . push args
    68/push  "1 2"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-data(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "01 02 ", msg)
    # . . push args
    68/push  "F - test-convert-data-multiple-bytes"/imm32
    68/push  "01 02 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-data-byte-then-name:
    # Single-byte word followed by valid name get processed one by one.
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "30 abcd/o")
    # . . push args
    68/push  "30 abcd/o"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-data(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "30 abcd/o ", msg)
    # . . push args
    68/push  "F - test-convert-data-byte-then-name"/imm32
    68/push  "30 abcd/o "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-data-multiple-words:
    # Multiple words in input stream get processed one by one.
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
    # . . push args
    68/push  "30 abcd/o 42e1/imm32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-data(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 ", msg)
    # . . push args
    68/push  "F - test-convert-data-multiple-words"/imm32
    68/push  "30 abcd/o e1 42 00 00 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-data-trailing-comment:
    # Trailing comments in data segment get appropriately ignored.
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "30/imm32 # comment")
    # . . push args
    68/push  "30/imm32 # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-data(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
    # . . push args
    68/push  "F - test-convert-data-trailing-comment"/imm32
    68/push  "30 00 00 00 # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# pack an instruction, following the C++ version
#
# zero error handling at the moment (continuing to rely on the C++ version for that):
#   missing fields are always 0-filled
#   bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 ModR/M byte. You get *no* ModR/M byte.
#   may pick up any of duplicate operands in an instruction
#   silently drop extraneous operands
#   unceremoniously abort on non-numeric operands except disp or imm
#   opcodes must be lowercase and zero padded
#   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
convert-instruction:  # line : (address stream byte), out : (address buffered-file) -> <void>
    # pseudocode:
    #   # some early exits
    #   var word-slice = next-word(line)
    #   if slice-empty?(word-slice)
    #     write-stream-data(out, line)
    #     return
    #   if slice-starts-with?(word-slice, "#")
    #     write-stream-data(out, line)
    #     return
    #   if slice-ends-with?(word-slice, ":")
    #     write-stream-data(out, line)
    #     return
    #   # really convert
    #   emit-opcodes(line, out)
    #   emit-modrm(line, out)
    #   emit-sib(line, out)
    #   emit-disp(line, out)
    #   emit-imm(line, out)
    #   emit-line-in-comment(line, out)
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    # var word-slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # next-word(line, word-slice)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$convert-instruction:check0:
    # if (slice-empty?(word-slice)) break
    # . EAX = slice-empty?(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) pass through
    3d/compare-EAX-and  0/imm32
    75/jump-if-not-equal  $convert-instruction:pass-through/disp8
$convert-instruction:check1:
    # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
    # . start/EDX = word-slice->start
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    # . if (EAX == '#') pass through
    3d/compare-EAX-and  0x23/imm32/hash
    74/jump-if-equal  $convert-instruction:pass-through/disp8
$convert-instruction:check2:
    # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
    # . end/EDX = word-slice->end
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
    # . c/EAX = *(end-1)
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
    # . if (EAX == ':') pass through
    3d/compare-EAX-and  0x3a/imm32/colon
    75/jump-if-not-equal  $convert-instruction:really-convert/disp8
$convert-instruction:pass-through:
    # write-stream-data(out, line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-stream-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # return
    eb/jump  $convert-instruction:end/disp8
$convert-instruction:really-convert:
    # emit-opcodes(line, out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  emit-opcodes/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # emit-modrm(line, out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  emit-modrm/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # emit-sib(line, out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  emit-sib/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # emit-disp(line, out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  emit-disp/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # emit-imm(line, out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  emit-imm/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # emit-line-in-comment(line, out)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  emit-line-in-comment/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$convert-instruction:end:
    # . restore locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . restore registers
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

emit-opcodes:  # line : (address stream byte), out : (address buffered-file) -> <void>
    # opcodes occupy 1-3 bytes:
    #   xx
    #   0f xx
    #   f2 xx
    #   f3 xx
    #   f2 0f xx
    #   f3 0f xx
    #
    # pseudocode:
    #   rewind-stream(line)
    #
    #   var op1 = next-word(line)
    #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
    #   op1 = next-token-from-slice(op1->start, op1->end, "/")
    #   write-slice(out, op1)
    #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3")
    #     return
    #
    #   var op2 = next-word(line)
    #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
    #   op2 = next-token-from-slice(op2->start, op2->end, "/")
    #   write-slice(out, op2)
    #   if slice-equal?(op1, "0f")
    #     return
    #   if !slice-equal?(op2, "0f")
    #     return
    #
    #   var op3 = next-word(line)
    #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
    #   op3 = next-token-from-slice(op3->start, op3->end, "/")
    #   write-slice(out, op3)
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    53/push-EBX
    # var op1/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # var op2/EDX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
    # rewind-stream(line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  rewind-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
$emit-opcodes:op1:
    # next-word(line, op1)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # if (slice-empty?(op1)) return
    # . EAX = slice-empty?(op1)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) return
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
    # if (slice-starts-with?(op1, "#")) return
    # . start/EBX = op1->start
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # copy *ECX to EBX
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
    # . if (EAX == '#') return
    3d/compare-EAX-and  0x23/imm32/hash
    0f 84/jump-if-equal  $emit-opcodes:end/disp32
    # op1 = next-token-from-slice(op1->start, op1->end, '/')
    # . . push args
    51/push-ECX
    68/push  0x2f/imm32/slash
    ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
    ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
    # . . call
    e8/call  next-token-from-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # write-slice(out, op1)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # write-buffered(out, " ")
    # . . push args
    68/push  " "/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # if (slice-equal?(op1, "0f")) goto op2
    # . EAX = slice-equal?(op1, "0f")
    # . . push args
    68/push  "0f"/imm32
    51/push-ECX
    # . . call
    e8/call  slice-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX != 0) goto op2
    3d/compare-EAX-and  0/imm32
    75/jump-if-not-equal  $emit-opcodes:op2/disp8
    # if (slice-equal?(op1, "f2")) goto op2
    # . EAX = slice-equal?(op1, "f2")
    # . . push args
    68/push  "f2"/imm32
    51/push-ECX
    # . . call
    e8/call  slice-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX != 0) goto op2
    3d/compare-EAX-and  0/imm32
    75/jump-if-not-equal  $emit-opcodes:op2/disp8
    # if (slice-equal?(op1, "f3")) goto op2
    # . EAX = slice-equal?(op1, "f3")
    # . . push args
    68/push  "f3"/imm32
    51/push-ECX
    # . . call
    e8/call  slice-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX != 0) goto op2
    3d/compare-EAX-and  0/imm32
    75/jump-if-not-equal  $emit-opcodes:op2/disp8
    # otherwise return
    e9/jump  $emit-opcodes:end/disp32
$emit-opcodes:op2:
    # next-word(line, op2)
    # . . push args
    52/push-EDX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # if (slice-empty?(op2)) return
    # . EAX = slice-empty?(op2)
    # . . push args
    52/push-EDX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) return
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
    # if (slice-starts-with?(op2, "#")) return
    # . start/EBX = op2->start
    8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
    # . if (EAX == '#') return
    3d/compare-EAX-and  0x23/imm32/hash
    0f 84/jump-if-equal  $emit-opcodes:end/disp32
    # op2 = next-token-from-slice(op2->start, op2->end, '/')
    # . . push args
    52/push-EDX
    68/push  0x2f/imm32/slash
    ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
    ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
    # . . call
    e8/call  next-token-from-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # write-slice(out, op2)
    # . . push args
    52/push-EDX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # write-buffered(out, " ")
    # . . push args
    68/push  " "/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # if (slice-equal?(op1, "0f")) return
    # . EAX = slice-equal?(op1, "0f")
    # . . push args
    68/push  "0f"/imm32
    51/push-ECX
    # . . call
    e8/call  slice-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX != 0) return
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
    # if (!slice-equal?(op2, "0f")) return
    # . EAX = slice-equal?(op2, "0f")
    # . . push args
    68/push  "0f"/imm32
    52/push-EDX
    # . . call
    e8/call  slice-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) return
    3d/compare-EAX-and  0/imm32
    0f 84/jump-if-equal  $emit-opcodes:end/disp32
$emit-opcodes:op3:
    # next-word(line, op3)  # reuse op2/EDX
    # . . push args
    52/push-EDX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # if (slice-empty?(op3)) return
    # . EAX = slice-empty?(op3)
    # . . push args
    52/push-EDX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) return
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
    # if (slice-starts-with?(op3, "#")) return
    # . start/EBX = op2->start
    8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
    # . if (EAX == '#') return
    3d/compare-EAX-and  0x23/imm32/hash
    0f 84/jump-if-equal  $emit-opcodes:end/disp32
    # op3 = next-token-from-slice(op3->start, op3->end, '/')
    # . . push args
    52/push-EDX
    68/push  0x2f/imm32/slash
    ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
    ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
    # . . call
    e8/call  next-token-from-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # write-slice(out, op3)
    # . . push args
    52/push-EDX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # write-buffered(out, " ")
    # . . push args
    68/push  " "/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$emit-opcodes:end:
    # . restore locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # . restore registers
    5b/pop-to-EBX
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

emit-modrm:  # line : (address stream byte), out : (address buffered-file) -> <void>
    # pseudocode:
    #   rewind-stream(line)
    #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
    #   var word-slice = {0, 0}
    #   while true
    #     word-slice = next-word(line)
    #     if (slice-empty?(word-slice)) break
    #     if (slice-starts-with?(word-slice, "#")) break
    #     if (has-metadata?(word-slice, "mod"))
    #       mod = parse-hex-int(next-token-from-slice(word-slice, "/"))
    #       has-modrm? = true
    #     else if (has-metadata?(word-slice, "rm32"))
    #       rm32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
    #       has-modrm? = true
    #     else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop"))
    #       r32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
    #       has-modrm? = true
    #   if has-modrm?
    #     var modrm = mod & 0b11
    #     modrm <<= 2
    #     modrm |= r32 & 0b111
    #     modrm <<= 3
    #     modrm |= rm32 & 0b111
    #     emit-hex(out, modrm, 1)
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    53/push-EBX
    56/push-ESI
    57/push-EDI
    # var word-slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # var has-modrm?/EDX = false
    31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
    # var mod/EBX = 0
    31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
    # var rm32/ESI = 0
    31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
    # var r32/EDI = 0
    31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
    # rewind-stream(line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  rewind-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
$emit-modrm:loop:
#?     # dump line {{{
#?     # . write(2/stderr, "LL: ")
#?     # . . push args
#?     68/push  "LL: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, line)
#?     # . . push args
#?     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # next-word(line, word-slice)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump word-slice {{{
#?     # . write(2/stderr, "AA: ")
#?     # . . push args
#?     68/push  "AA: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . clear-stream(Stderr+4)
#?     # . . push args
#?     b8/copy-to-EAX  Stderr/imm32
#?     05/add-to-EAX  4/imm32
#?     50/push-EAX
#?     # . . call
#?     e8/call  clear-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write-slice(Stderr, word-slice)
#?     # . . push args
#?     51/push-ECX
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  write-slice/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . flush(Stderr)
#?     # . . push args
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  flush/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
$emit-modrm:check0:
    # if (slice-empty?(word-slice)) break
    # . EAX = slice-empty?(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) pass through
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $emit-modrm:break/disp32
$emit-modrm:check1:
    # if (slice-starts-with?(word-slice, "#")) break
    # . spill EDX
    52/push-EDX
    # . start/EDX = word-slice->start
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    # . restore EDX
    5a/pop-to-EDX
    # . if (EAX == '#') pass through
    3d/compare-EAX-and  0x23/imm32/hash
    0f 84/jump-if-equal  $emit-modrm:break/disp32
$emit-modrm:check-for-mod:
    # if (has-metadata?(word-slice, "mod"))
    # . EAX = has-metadata?(ECX, "mod")
    # . . push args
    68/push  "mod"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto next check
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-modrm:check-for-rm32/disp8
$emit-modrm:mod:
    # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    # . EAX = parse-datum-of-word(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  parse-datum-of-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . mod = EAX
    89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
    # has-modrm? = true
    ba/copy-to-EDX  1/imm32/true
    # continue
    e9/jump  $emit-modrm:loop/disp32
$emit-modrm:check-for-rm32:
    # if (has-metadata?(word-slice, "rm32"))
    # . EAX = has-metadata?(ECX, "rm32")
    # . . push args
    68/push  "rm32"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto next check
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-modrm:check-for-r32/disp8
$emit-modrm:rm32:
    # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    # . EAX = parse-datum-of-word(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  parse-datum-of-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . rm32 = EAX
    89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
    # has-modrm? = true
    ba/copy-to-EDX  1/imm32/true
    # continue
    e9/jump  $emit-modrm:loop/disp32
$emit-modrm:check-for-r32:
    # if (has-metadata?(word-slice, "r32"))
    # . EAX = has-metadata?(ECX, "r32")
    # . . push args
    68/push  "r32"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto next check
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-modrm:check-for-subop/disp8
$emit-modrm:r32:
    # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    # . EAX = parse-datum-of-word(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  parse-datum-of-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . r32 = EAX
    89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
    # has-modrm? = true
    ba/copy-to-EDX  1/imm32/true
    # continue
    e9/jump  $emit-modrm:loop/disp32
$emit-modrm:check-for-subop:
    # if (has-metadata?(word-slice, "subop"))
    # . EAX = has-metadata?(ECX, "subop")
    # . . push args
    68/push  "subop"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) loop
    3d/compare-EAX-and  0/imm32
    0f 84/jump-if-equal  $emit-modrm:loop/disp32
$emit-modrm:subop:
    # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    # . EAX = parse-datum-of-word(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  parse-datum-of-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . r32 = EAX
    89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
    # has-modrm? = true
    ba/copy-to-EDX  1/imm32/true
    # continue
    e9/jump  $emit-modrm:loop/disp32
$emit-modrm:break:
    # if (!has-modrm?) return
    81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
    74/jump-if-equal  $emit-modrm:end/disp8
$emit-modrm:calculate:
    # modrm/EBX = mod & 0b11
    81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
    # modrm <<= 2
    c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
    # modrm |= r32 & 0b111
    81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
    09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
    # modrm <<= 3
    c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
    # modrm |= rm32 & 0b111
    81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
    09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
$emit-modrm:emit:
    # emit-hex(out, modrm, 1)
    # . . push args
    68/push  1/imm32
    53/push-EBX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
$emit-modrm:end:
    # . restore locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . restore registers
    5f/pop-to-EDI
    5e/pop-to-ESI
    5b/pop-to-EBX
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

emit-sib:  # line : (address stream byte), out : (address buffered-file) -> <void>
    # pseudocode:
    #   var has-sib? = false, base = 0, index = 0, scale = 0
    #   var word-slice = {0, 0}
    #   while true
    #     word-slice = next-word(line)
    #     if (slice-empty?(word-slice)) break
    #     if (slice-starts-with?(word-slice, "#")) break
    #     if (has-metadata?(word-slice, "base")
    #       base = parse-hex-int(next-token-from-slice(word-slice, "/"))
    #       has-sib? = true
    #     else if (has-metadata?(word-slice, "index")
    #       index = parse-hex-int(next-token-from-slice(word-slice, "/"))
    #       has-sib? = true
    #     else if (has-metadata?(word-slice, "scale")
    #       scale = parse-hex-int(next-token-from-slice(word-slice, "/"))
    #       has-sib? = true
    #   if has-sib?
    #     var sib = scale & 0b11
    #     sib <<= 2
    #     sib |= index & 0b111
    #     sib <<= 3
    #     sib |= base & 0b111
    #     emit-hex(out, sib, 1)
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    53/push-EBX
    56/push-ESI
    57/push-EDI
    # var word-slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # var has-sib?/EDX = false
    31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
    # var scale/EBX = 0
    31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
    # var base/ESI = 0
    31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
    # var index/EDI = 0
    31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
    # rewind-stream(line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  rewind-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
$emit-sib:loop:
#?     # dump line {{{
#?     # . write(2/stderr, "LL: ")
#?     # . . push args
#?     68/push  "LL: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, line)
#?     # . . push args
#?     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # next-word(line, word-slice)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump word-slice {{{
#?     # . write(2/stderr, "AA: ")
#?     # . . push args
#?     68/push  "AA: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . clear-stream(Stderr+4)
#?     # . . push args
#?     b8/copy-to-EAX  Stderr/imm32
#?     05/add-to-EAX  4/imm32
#?     50/push-EAX
#?     # . . call
#?     e8/call  clear-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write-slice(Stderr, word-slice)
#?     # . . push args
#?     51/push-ECX
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  write-slice/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . flush(Stderr)
#?     # . . push args
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  flush/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
$emit-sib:check0:
    # if (slice-empty?(word-slice)) break
    # . EAX = slice-empty?(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) pass through
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $emit-sib:break/disp32
$emit-sib:check1:
    # if (slice-starts-with?(word-slice, "#")) break
    # . spill EDX
    52/push-EDX
    # . start/EDX = word-slice->start
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    # . restore EDX
    5a/pop-to-EDX
    # . if (EAX == '#') pass through
    3d/compare-EAX-and  0x23/imm32/hash
    0f 84/jump-if-equal  $emit-sib:break/disp32
$emit-sib:check-for-scale:
    # if (has-metadata?(word-slice, "scale"))
    # . EAX = has-metadata?(ECX, "scale")
    # . . push args
    68/push  "scale"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto next check
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-sib:check-for-base/disp8
$emit-sib:scale:
    # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    # . EAX = parse-datum-of-word(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  parse-datum-of-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . scale = EAX
    89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
    # has-sib? = true
    ba/copy-to-EDX  1/imm32/true
    # continue
    e9/jump  $emit-sib:loop/disp32
$emit-sib:check-for-base:
    # if (has-metadata?(word-slice, "base"))
    # . EAX = has-metadata?(ECX, "base")
    # . . push args
    68/push  "base"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto next check
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-sib:check-for-index/disp8
$emit-sib:base:
    # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    # . EAX = parse-datum-of-word(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  parse-datum-of-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . base = EAX
    89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
    # has-sib? = true
    ba/copy-to-EDX  1/imm32/true
    # continue
    e9/jump  $emit-sib:loop/disp32
$emit-sib:check-for-index:
    # if (has-metadata?(word-slice, "index"))
    # . EAX = has-metadata?(ECX, "index")
    # . . push args
    68/push  "index"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) loop
    3d/compare-EAX-and  0/imm32
    0f 84/jump-if-equal  $emit-sib:loop/disp32
$emit-sib:index:
    # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    # . EAX = parse-datum-of-word(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  parse-datum-of-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . index = EAX
    89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
    # has-sib? = true
    ba/copy-to-EDX  1/imm32/true
    # continue
    e9/jump  $emit-sib:loop/disp32
$emit-sib:break:
    # if (!has-sib?) return
    81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
    74/jump-if-equal  $emit-sib:end/disp8
$emit-sib:calculate:
    # sib/EBX = scale & 0b11
    81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
    # sib <<= 2
    c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
    # sib |= index & 0b111
    81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
    09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
    # sib <<= 3
    c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
    # sib |= base & 0b111
    81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
    09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
$emit-sib:emit:
    # emit-hex(out, sib, 1)
    # . . push args
    68/push  1/imm32
    53/push-EBX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
$emit-sib:end:
    # . restore locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . restore registers
    5f/pop-to-EDI
    5e/pop-to-ESI
    5b/pop-to-EBX
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

emit-disp:  # line : (address stream byte), out : (address buffered-file) -> <void>
    # pseudocode:
    #   rewind-stream(line)
    #   var word-slice = {0, 0}
    #   while true
    #     word-slice = next-word(line)
    #     if (slice-empty?(word-slice)) break
    #     if (slice-starts-with?(word-slice, "#")) break
    #     if has-metadata?(word-slice, "disp32")
    #       emit(out, word-slice, 4)
    #       break
    #     if has-metadata?(word-slice, "disp16")
    #       emit(out, word-slice, 2)
    #       break
    #     if has-metadata?(word-slice, "disp8")
    #       emit(out, word-slice, 1)
    #       break
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    # var word-slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # rewind-stream(line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  rewind-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump line {{{
#?     # . write(2/stderr, "LL: ")
#?     # . . push args
#?     68/push  "LL: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, line)
#?     # . . push args
#?     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
$emit-disp:loop:
    # next-word(line, word-slice)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump word-slice {{{
#?     # . write(2/stderr, "AA: ")
#?     # . . push args
#?     68/push  "AA: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . clear-stream(Stderr+4)
#?     # . . push args
#?     b8/copy-to-EAX  Stderr/imm32
#?     05/add-to-EAX  4/imm32
#?     50/push-EAX
#?     # . . call
#?     e8/call  clear-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write-slice(Stderr, word-slice)
#?     # . . push args
#?     51/push-ECX
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  write-slice/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . flush(Stderr)
#?     # . . push args
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  flush/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
$emit-disp:check0:
    # if (slice-empty?(word-slice)) break
    # . EAX = slice-empty?(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) pass through
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $emit-disp:break/disp32
$emit-disp:check1:
    # if (slice-starts-with?(word-slice, "#")) break
    # . start/EDX = word-slice->start
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    # . if (EAX == '#') break
    3d/compare-EAX-and  0x23/imm32/hash
    0f 84/jump-if-equal  $emit-disp:break/disp32
$emit-disp:check-for-disp32:
    # if (has-metadata?(word-slice, "disp32"))
    # . EAX = has-metadata?(ECX, "disp32")
    # . . push args
    68/push  "disp32"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto next check
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-disp:check-for-disp16/disp8
$emit-disp:disp32:
    # emit(out, word-slice, 4)
    # . . push args
    68/push  4/imm32
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # break
    e9/jump  $emit-disp:break/disp32
$emit-disp:check-for-disp16:
    # else if (has-metadata?(word-slice, "disp16"))
    # . EAX = has-metadata?(ECX, "disp16")
    # . . push args
    68/push  "disp16"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto next check
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-disp:check-for-disp8/disp8
$emit-disp:disp16:
    # emit(out, word-slice, 2)
    # . . push args
    68/push  2/imm32
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # break
    e9/jump  $emit-disp:break/disp32
$emit-disp:check-for-disp8:
    # if (has-metadata?(word-slice, "disp8"))
    # . EAX = has-metadata?(ECX, "disp8")
    # . . push args
    68/push  "disp8"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) loop
    3d/compare-EAX-and  0/imm32
    0f 84/jump-if-equal  $emit-disp:loop/disp32
$emit-disp:disp8:
    # emit(out, word-slice, 1)
    # . . push args
    68/push  1/imm32
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # break
$emit-disp:break:
    # . restore locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . restore registers
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

emit-imm:  # line : (address stream byte), out : (address buffered-file) -> <void>
    # pseudocode:
    #   rewind-stream(line)
    #   var word-slice = {0, 0}
    #   while true
    #     word-slice = next-word(line)
    #     if (slice-empty?(word-slice)) break
    #     if (slice-starts-with?(word-slice, "#")) break
    #     if has-metadata?(word-slice, "imm32")
    #       emit(out, word-slice, 4)
    #       break
    #     if has-metadata?(word-slice, "imm16")
    #       emit(out, word-slice, 2)
    #       break
    #     if has-metadata?(word-slice, "imm8")
    #       emit(out, word-slice, 1)
    #       break
    #
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    # var word-slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # rewind-stream(line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  rewind-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump line {{{
#?     # . write(2/stderr, "LL: ")
#?     # . . push args
#?     68/push  "LL: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, line)
#?     # . . push args
#?     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
$emit-imm:loop:
    # next-word(line, word-slice)
    # . . push args
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # dump word-slice {{{
#?     # . write(2/stderr, "AA: ")
#?     # . . push args
#?     68/push  "AA: "/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . clear-stream(Stderr+4)
#?     # . . push args
#?     b8/copy-to-EAX  Stderr/imm32
#?     05/add-to-EAX  4/imm32
#?     50/push-EAX
#?     # . . call
#?     e8/call  clear-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write-slice(Stderr, word-slice)
#?     # . . push args
#?     51/push-ECX
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  write-slice/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . flush(Stderr)
#?     # . . push args
#?     68/push  Stderr/imm32
#?     # . . call
#?     e8/call  flush/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
$emit-imm:check0:
    # if (slice-empty?(word-slice)) break
    # . EAX = slice-empty?(word-slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) pass through
    3d/compare-EAX-and  0/imm32
    0f 85/jump-if-not-equal  $emit-imm:break/disp32
$emit-imm:check1:
    # if (slice-starts-with?(word-slice, "#")) break
    # . start/EDX = slice->start
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    # . c/EAX = *start
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    # . if (EAX == '#') break
    3d/compare-EAX-and  0x23/imm32/hash
    0f 84/jump-if-equal  $emit-imm:break/disp32
$emit-imm:check-for-imm32:
    # if (has-metadata?(word-slice, "imm32"))
    # . EAX = has-metadata?(ECX, "imm32")
    # . . push args
    68/push  "imm32"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto next check
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-imm:check-for-imm16/disp8
$emit-imm:imm32:
    # emit(out, word-slice, 4)
    # . . push args
    68/push  4/imm32
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # break
    e9/jump  $emit-imm:break/disp32
$emit-imm:check-for-imm16:
    # if (has-metadata?(word-slice, "imm16"))
    # . EAX = has-metadata?(ECX, "imm16")
    # . . push args
    68/push  "imm16"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) goto next check
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit-imm:check-for-imm8/disp8
$emit-imm:imm16:
    # emit(out, word-slice, 2)
    # . . push args
    68/push  2/imm32
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # break
    e9/jump  $emit-imm:break/disp32
$emit-imm:check-for-imm8:
    # if (has-metadata?(word-slice, "imm8"))
    # . EAX = has-metadata?(ECX, "imm8")
    # . . push args
    68/push  "imm8"/imm32
    51/push-ECX
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX == 0) loop
    3d/compare-EAX-and  0/imm32
    0f 84/jump-if-equal  $emit-imm:loop/disp32
$emit-imm:imm8:
    # emit(out, word-slice, 1)
    # . . push args
    68/push  1/imm32
    51/push-ECX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # break
$emit-imm:break:
    # . restore locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . restore registers
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

emit-line-in-comment:  # line : (address stream byte), out : (address buffered-file) -> <void>
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # write-buffered(out, " # ")
    # . . push args
    68/push  " # "/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # write-stream-data(out, line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    # . . call
    e8/call  write-stream-data/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$emit-line-in-comment:end:
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-passes-comments-through:
    # if a line starts with '#', pass it along unchanged
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "# abcd")
    # . . push args
    68/push  "# abcd"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that the line just passed through
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "# abcd", msg)
    # . . push args
    68/push  "F - test-convert-instruction-passes-comments-through"/imm32
    68/push  "# abcd"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-passes-labels-through:
    # if the first word ends with ':', pass along the entire line unchanged
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "ab: # cd")
    # . . push args
    68/push  "ab: # cd"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check that the line just passed through
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
    # . . push args
    68/push  "F - test-convert-instruction-passes-labels-through"/imm32
    68/push  "ab: # cd"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-single-opcode:
    # if the instruction consists of a single opcode, strip its metadata and pass it along
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "ab/cd # comment")
    # . . push args
    68/push  "ab/cd # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
    68/push  "ab  # ab/cd # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-0f-opcode:
    # if the instruction starts with 0f opcode, include a second opcode
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
    # . . push args
    68/push  "0f/m1 ab/m2 # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
    68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-f2-opcode:
    # if the instruction starts with f2 opcode, include a second opcode
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
    # . . push args
    68/push  "f2/m1 ab/m2 # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
    68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-f3-opcode:
    # if the instruction starts with f3 opcode, include a second opcode
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
    # . . push args
    68/push  "f3/m1 ab/m2 # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
    68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-f2-0f-opcode:
    # if the instruction starts with f2 0f opcode, include a second opcode
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
    # . . push args
    68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
    68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-f3-0f-opcode:
    # if the instruction starts with f3 0f opcode, include a second opcode
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
    # . . push args
    68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
    68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-unused-opcodes:
    # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
    # . . push args
    68/push  "ab/m1 cd/m2 # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
    68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-unused-second-opcodes:
    # if the second opcode isn't 0f, don't include further opcodes
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
    # . . push args
    68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
    68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-unused-second-opcodes-2:
    # if the second opcode isn't 0f, don't include further opcodes
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
    # . . push args
    68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
    68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-emits-modrm-byte:
    # pack mod, rm32 and r32 operands into ModR/M byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
    # . . push args
    68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
    # . . push args
    68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
    68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-emits-modrm-byte-from-subop:
    # pack mod, rm32 and subop operands into ModR/M byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
    # . . push args
    68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
    # . . push args
    68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
    68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-emits-modrm-byte-with-missing-mod:
    # pack rm32 and r32 operands into ModR/M byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
    # . . push args
    68/push  "8b/copy 0/rm32 1/r32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
    # . . push args
    68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
    68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-emits-modrm-byte-with-missing-rm32:
    # pack mod and r32 operands into ModR/M byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
    # . . push args
    68/push  "8b/copy 0/mod 1/r32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
    # . . push args
    68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
    68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-emits-modrm-byte-with-missing-r32:
    # pack mod and rm32 operands into ModR/M byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "8b/copy 0/mod 0/rm32")
    # . . push args
    68/push  "8b/copy 0/mod 0/rm32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "8b 00  # 8b/copy 0/mod 0/rm32", msg)
    # . . push args
    68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
    68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-emits-sib-byte:
    # pack base, index and scale operands into SIB byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale")
    # . . push args
    68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
    # . . push args
    68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
    68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-emits-sib-byte-with-missing-base:
    # pack index and scale operands into SIB byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
    # . . push args
    68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
    # . . push args
    68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
    68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-emits-sib-byte-with-missing-index:
    # pack base and scale operands into SIB byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
    # . . push args
    68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
    # . . push args
    68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
    68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-emits-sib-byte-with-missing-scale:
    # pack base and index operands into SIB byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
    # . . push args
    68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
    # . . push args
    68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
    68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-disp32-operand:
    # expand /disp32 operand into 4 bytes
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "e8/call 20/disp32")
    # . . push args
    68/push  "e8/call 20/disp32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
    68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-disp16-operand:
    # expand /disp16 operand into 2 bytes
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "e8/call 20/disp16")
    # . . push args
    68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
    68/push  "e8 20 00  # e8/call 20/disp16"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-disp8-operand:
    # expand /disp8 operand into 1 byte
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "eb/jump 20/disp8")
    # . . push args
    68/push  "eb/jump 20/disp8"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
    68/push  "eb 20  # eb/jump 20/disp8"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-disp8-name:
    # pass /disp8 name directly through
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "eb/jump xyz/disp8")
    # . . push args
    68/push  "eb/jump xyz/disp8"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
    68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-imm32-operand:
    # expand /imm32 operand into 4 bytes
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "68/push 0x20/imm32")
    # . . push args
    68/push  "68/push 0x20/imm32"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
    68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-imm16-operand:
    # expand /imm16 operand into 2 bytes
    # we don't have one of these at the moment, so this expands to an invalid instruction
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "68/push 0x20/imm16")
    # . . push args
    68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
    68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-convert-instruction-handles-imm8-operand:
    # expand /imm8 operand into 1 byte
    # we don't have one of these at the moment, so this expands to an invalid instruction
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-input-stream)
    # . . push args
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # initialize input
    # . write(_test-input-stream, "68/push 0x20/imm8")
    # . . push args
    68/push  "68/push 0x20/imm8"/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # convert-instruction(_test-input-stream, _test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    68/push  _test-input-stream/imm32
    # . . call
    e8/call  convert-instruction/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check output
    # . flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
    # . . push args
    68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
    68/push  "68 20  # 68/push 0x20/imm8"/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# (re)compute the bounds of the next word in the line
# return empty string on reaching end of file
next-word:  # line : (address stream byte), out : (address slice)
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    56/push-ESI
    57/push-EDI
    # ESI = line
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    # EDI = out
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
    # skip-chars-matching(line, ' ')
    # . . push args
    68/push  0x20/imm32/space
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  skip-chars-matching/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$next-word:check0:
    # if (line->read >= line->write) clear out and return
    # . EAX = line->read
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
    # . if (EAX < line->write) goto next check
    3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
    7c/jump-if-lesser  $next-word:check-for-comment/disp8
    # . return out = {0, 0}
    c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
    c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
    eb/jump  $next-word:end/disp8
$next-word:check-for-comment:
    # out->start = &line->data[line->read]
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
    89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
    # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
    # . EAX = line->data[line->read]
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
    # . compare
    3d/compare-EAX-and  0x23/imm32/pound
    75/jump-if-not-equal  $next-word:regular-word/disp8
$next-word:comment:
    # . out->end = &line->data[line->write]
    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
    # . line->read = line->write
    89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
    # . return
    eb/jump  $next-word:end/disp8
$next-word:regular-word:
    # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  skip-chars-not-matching-whitespace/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # out->end = &line->data[line->read]
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
$next-word:end:
    # . restore registers
    5f/pop-to-EDI
    5e/pop-to-ESI
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-next-word:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # write(_test-stream, "  ab")
    # . . push args
    68/push  "  ab"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # next-word(_test-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-stream/imm32
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
    # . check-ints-equal(slice->start - _test-stream, 14, msg)
    # . . push args
    68/push  "F - test-next-word: start"/imm32
    68/push  0xe/imm32
    # . . push slice->start - _test-stream
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # check-ints-equal(slice->end - _test-stream->data, 4, msg)
    # . check-ints-equal(slice->end - _test-stream, 16, msg)
    # . . push args
    68/push  "F - test-next-word: end"/imm32
    68/push  0x10/imm32
    # . . push slice->end - _test-stream
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-next-word-returns-whole-comment:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # write(_test-stream, "  # a")
    # . . push args
    68/push  "  # a"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # next-word(_test-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-stream/imm32
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
    # . check-ints-equal(slice->start - _test-stream, 14, msg)
    # . . push args
    68/push  "F - test-next-word-returns-whole-comment: start"/imm32
    68/push  0xe/imm32
    # . . push slice->start - _test-stream
    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # check-ints-equal(slice->end - _test-stream->data, 5, msg)
    # . check-ints-equal(slice->end - _test-stream, 17, msg)
    # . . push args
    68/push  "F - test-next-word-returns-whole-comment: end"/imm32
    68/push  0x11/imm32
    # . . push slice->end - _test-stream
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-next-word-returns-empty-string-on-eof:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # write nothing to _test-stream
    # next-word(_test-stream, slice)
    # . . push args
    51/push-ECX
    68/push  _test-stream/imm32
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(slice->end - slice->start, 0, msg)
    # . . push args
    68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
    68/push  0/imm32
    # . . push slice->end - slice->start
    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

has-metadata?:  # word : (address slice), s : (address string) -> EAX : boolean
    # pseudocode:
    #   var twig : &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
    #   curr = twig->end
    #   while true
    #     twig = next-token-from-slice(curr, word->end, '/')
    #     if (twig.empty()) break
    #     if (slice-equal?(twig, s)) return true
    #     curr = twig->end
    #   return false
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    51/push-ECX
    52/push-EDX
    56/push-ESI
    57/push-EDI
    # ESI = word
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    # EDX = word->end
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
    # var twig/EDI : (address slice) = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
    # next-token-from-slice(word->start, word->end, '/', twig)
    # . . push args
    57/push-EDI
    68/push  0x2f/imm32/slash
    52/push-EDX
    ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
    # . . call
    e8/call  next-token-from-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # curr/ECX = twig->end
    8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
$has-metadata?:loop:
    # next-token-from-slice(curr, word->end, '/', twig)
    # . . push args
    57/push-EDI
    68/push  0x2f/imm32/slash
    52/push-EDX
    51/push-ECX
    # . . call
    e8/call  next-token-from-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # if (slice-empty?(twig)) return false
    # . EAX = slice-empty?(twig)
    # . . push args
    57/push-EDI
    # . . call
    e8/call  slice-empty?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0) return false
    3d/compare-EAX-and  0/imm32
    75/jump-if-not-equal  $has-metadata?:false/disp8
    # if (slice-equal?(twig, s)) return true
    # . EAX = slice-equal?(twig, s)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    57/push-EDI
    # . . call
    e8/call  slice-equal?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . if (EAX != 0) return true
    3d/compare-EAX-and  0/imm32
    75/jump-if-not-equal  $has-metadata?:true/disp8
    # curr = twig->end
    8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
    eb/jump  $has-metadata?:loop/disp8
$has-metadata?:true:
    b8/copy-to-EAX  1/imm32/true
    eb/jump  $has-metadata?:end/disp8
$has-metadata?:false:
    b8/copy-to-EAX  0/imm32/false
$has-metadata?:end:
    # . reclaim locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . restore registers
    5f/pop-to-EDI
    5e/pop-to-ESI
    5a/pop-to-EDX
    59/pop-to-ECX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-has-metadata-true:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # (EAX..ECX) = "ab/c"
    b8/copy-to-EAX  "ab/c"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
    05/add-to-EAX  4/imm32
    # var in/ESI : (address slice) = {EAX, ECX}
    51/push-ECX
    50/push-EAX
    89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    # EAX = has-metadata?(ESI, "c")
    # . . push args
    68/push  "c"/imm32
    56/push-ESI
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(EAX, 1, msg)
    # . . push args
    68/push  "F - test-has-metadata-true"/imm32
    68/push  1/imm32/true
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-has-metadata-false:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # (EAX..ECX) = "ab/c"
    b8/copy-to-EAX  "ab/c"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
    05/add-to-EAX  4/imm32
    # var in/ESI : (address slice) = {EAX, ECX}
    51/push-ECX
    50/push-EAX
    89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    # EAX = has-metadata?(ESI, "c")
    # . . push args
    68/push  "d"/imm32
    56/push-ESI
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(EAX, 0, msg)
    # . . push args
    68/push  "F - test-has-metadata-false"/imm32
    68/push  0/imm32/false
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-has-metadata-ignore-name:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # (EAX..ECX) = "a/b"
    b8/copy-to-EAX  "a/b"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
    05/add-to-EAX  4/imm32
    # var in/ESI : (address slice) = {EAX, ECX}
    51/push-ECX
    50/push-EAX
    89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    # EAX = has-metadata?(ESI, "a")
    # . . push args
    68/push  "a"/imm32
    56/push-ESI
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(EAX, 0, msg)
    # . . push args
    68/push  "F - test-has-metadata-ignore-name"/imm32
    68/push  0/imm32/false
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-has-metadata-multiple-true:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # (EAX..ECX) = "a/b/c"
    b8/copy-to-EAX  "a/b/c"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
    05/add-to-EAX  4/imm32
    # var in/ESI : (address slice) = {EAX, ECX}
    51/push-ECX
    50/push-EAX
    89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    # EAX = has-metadata?(ESI, "c")
    # . . push args
    68/push  "c"/imm32
    56/push-ESI
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(EAX, 1, msg)
    # . . push args
    68/push  "F - test-has-metadata-multiple-true"/imm32
    68/push  1/imm32/true
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-has-metadata-multiple-false:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # (EAX..ECX) = "a/b/c"
    b8/copy-to-EAX  "a/b/c"/imm32
    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   4/disp8         .                 # copy EAX+ECX+4 to ECX
    05/add-to-EAX  4/imm32
    # var in/ESI : (address slice) = {EAX, ECX}
    51/push-ECX
    50/push-EAX
    89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    # EAX = has-metadata?(ESI, "d")
    # . . push args
    68/push  "d"/imm32
    56/push-ESI
    # . . call
    e8/call  has-metadata?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # check-ints-equal(EAX, 0, msg)
    # . . push args
    68/push  "F - test-has-metadata-multiple-false"/imm32
    68/push  0/imm32/false
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# If datum of 'word' is not a valid name, it must be a hex int. Parse and print
# it in 'width' bytes of hex, least significant first.
# Otherwise just print the entire word including metadata.
# Always print a trailing space.
emit:  # out : (address buffered-file), word : (address slice), width : int -> <void>
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    56/push-ESI
    57/push-EDI
    # ESI = word
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
    # var name/EDI : (address slice) = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
    # datum = next-token-from-slice(word->start, word->end, '/')
    # . . push args
    57/push-EDI
    68/push  0x2f/imm32/slash
    ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
    ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
    # . . call
    e8/call  next-token-from-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # if (is-valid-name?(datum)) write-slice(out, word) and return
    # . EAX = is-valid-name?(name)
    # . . push args
    57/push-EDI
    # . . call
    e8/call  is-valid-name?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . if (EAX != 0)
    3d/compare-EAX-and  0/imm32
    74/jump-if-equal  $emit:hex-int/disp8
$emit:name:
    # . write-slice(out, word)
    # . . push args
    56/push-ESI
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  write-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . write-buffered(out, " ")
    # . . push args
    68/push  " "/imm32
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . return
    eb/jump  $emit:end/disp8
    # otherwise emit-hex(out, parse-hex-int(datum), width)
    #   (Weird shit can happen here if the datum of 'word' isn't either a valid
    #   name or a hex number, but we're only going to be passing in real legal
    #   programs. We just want to make sure that valid names aren't treated as
    #   (valid) hex numbers.)
$emit:hex-int:
    # . value/EAX = parse-hex-int(datum)
    # . . push args
    57/push-EDI
    # . . call
    e8/call  parse-hex-int/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . emit-hex(out, value, width)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
    50/push-EAX
    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
$emit:end:
    # . reclaim locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . restore registers
    5f/pop-to-EDI
    5e/pop-to-ESI
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-number:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = "30"
    68/push  _test-slice-three-zero-end/imm32/end
    68/push  _test-slice-three-zero/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit(_test-output-buffered-file, slice, 1)
    # . . push args
    68/push  1/imm32
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream, "30 ", msg)
    # . . push args
    68/push  "F - test-emit-number/1"/imm32
    68/push  "30 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-negative-number:
    # test support for sign-extending negative numbers
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = "-2"
    68/push  _test-slice-negative-two-end/imm32/end
    68/push  _test-slice-negative-two/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit(_test-output-buffered-file, slice, 2)
    # . . push args
    68/push  2/imm32
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream, "fe ff ", msg)
    # . . push args
    68/push  "F - test-emit-number/1"/imm32
    68/push  "fe ff "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-number-with-metadata:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = "-2/foo"
    68/push  _test-slice-negative-two-metadata-end/imm32/end
    68/push  _test-slice-negative-two/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit(_test-output-buffered-file, slice, 2)
    # . . push args
    68/push  2/imm32
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # the '/foo' will have no impact on the output
    # check-stream-equal(_test-output-stream, "fe ff ", msg)
    # . . push args
    68/push  "F - test-emit-number-with-metadata"/imm32
    68/push  "fe ff "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-non-number:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = "xyz"
    68/push  _test-slice-non-number-word-end/imm32/end
    68/push  _test-slice-non-number-word/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit(_test-output-buffered-file, slice, 2)
    # . . push args
    68/push  2/imm32
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream, "xyz", msg)
    # . . push args
    68/push  "F - test-emit-non-number"/imm32
    68/push  "xyz "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-non-number-with-metadata:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = "xyz/"
    68/push  _test-slice-non-number-word-metadata-end/imm32/end
    68/push  _test-slice-non-number-word/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit(_test-output-buffered-file, slice, 2)
    # . . push args
    68/push  2/imm32
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream, "xyz/", msg)
    # . . push args
    68/push  "F - test-emit-non-number-with-metadata"/imm32
    68/push  "xyz/ "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-non-number-with-all-hex-digits-and-metadata:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # var slice/ECX = "abcd/xyz"
    68/push  _test-slice-hexlike-non-number-word-metadata-end/imm32/end
    68/push  _test-slice-hexlike-non-number-word/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # emit(_test-output-buffered-file, slice, 2)
    # . . push args
    68/push  2/imm32
    51/push-ECX
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
#?     # dump output {{{
#?     # . write(2/stderr, "^")
#?     # . . push args
#?     68/push  "^"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write-stream(2/stderr, _test-output-stream)
#?     # . . push args
#?     68/push  _test-output-stream/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write-stream/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # . write(2/stderr, "$\n")
#?     # . . push args
#?     68/push  "$\n"/imm32
#?     68/push  2/imm32/stderr
#?     # . . call
#?     e8/call  write/disp32
#?     # . . discard args
#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
#?     # }}}
    # check-stream-equal(_test-output-stream, "abcd/xyz")
    # . . push args
    68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
    68/push  "abcd/xyz "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# conditions for 'valid' names that are not at risk of looking like hex numbers
# keep in sync with the rules in labels.cc
#: - if it starts with a digit, it's treated as a number. If it can't be
#:   parsed as hex it will raise an error.
#: - if it starts with '-' it's treated as a number.
#: - if it starts with '0x' it's treated as a number. (redundant)
#: - if it's two characters long, it can't be a name. Either it's a hex
#:   byte, or it raises an error.
is-valid-name?:  # in : (address slice) -> EAX : boolean
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    51/push-ECX
    56/push-ESI
    # ESI = in
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    # start/ECX = in->start
    8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
    # end/EAX = in->end
    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
$is-valid-name?:check0:
    # if (start >= end) return false
    39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # compare ECX with EAX
    7d/jump-if-greater-or-equal  $is-valid-name?:false/disp8
$is-valid-name?:check1:
    # EAX -= ECX
    29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
    # if (EAX == 2) return false
    3d/compare-EAX-and  2/imm32
    74/jump-if-equal  $is-valid-name?:false/disp8
$is-valid-name?:check2:
    # c/EAX = *ECX
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
    # if (c == "-") return false
    3d/compare-EAX-and  2d/imm32/-
    74/jump-if-equal  $is-valid-name?:false/disp8
$is-valid-name?:check3a:
    # if (c < "0") return true
    3d/compare-EAX-with  30/imm32/0
    7c/jump-if-lesser  $is-valid-name?:true/disp8
$is-valid-name?:check3b:
    # if (c > "9") return true
    3d/compare-EAX-with  39/imm32/9
    7f/jump-if-greater  $is-valid-name?:true/disp8
$is-valid-name?:false:
    # return false
    b8/copy-to-EAX  0/imm32/false
    eb/jump  $is-valid-name?:end/disp8
$is-valid-name?:true:
    # return true
    b8/copy-to-EAX  1/imm32/true
$is-valid-name?:end:
    # . restore registers
    5e/pop-to-ESI
    59/pop-to-ECX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-is-valid-name-digit-prefix:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # var slice/ECX = "34"
    68/push  _test-slice-hex-int-end/imm32
    68/push  _test-slice-hex-int/imm32
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # EAX = is-valid-name?(slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  is-valid-name?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(EAX, 0, msg)
    # . . push args
    68/push  "F - test-is-valid-name-digit-prefix"/imm32
    68/push  0/imm32/false
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-is-valid-name-negative-prefix:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # var slice/ECX = "-0x34"
    68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
    68/push  _test-slice-hex-int-with-0x-prefix-negative/imm32
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # EAX = is-valid-name?(slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  is-valid-name?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(EAX, 0, msg)
    # . . push args
    68/push  "F - test-is-valid-name-negative-prefix"/imm32
    68/push  0/imm32/false
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-is-valid-name-0x-prefix:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # var slice/ECX = "0x34"
    68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
    68/push  _test-slice-hex-int-with-0x-prefix/imm32
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # EAX = is-valid-name?(slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  is-valid-name?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(EAX, 0, msg)
    # . . push args
    68/push  "F - test-is-valid-name-0x-prefix"/imm32
    68/push  0/imm32/false
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-is-valid-name-starts-with-pre-digit:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # var slice/ECX = "/03"
    68/push  _test-slice-with-slash-prefix-end/imm32
    68/push  _test-slice-with-slash-prefix/imm32
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # EAX = is-valid-name?(slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  is-valid-name?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(EAX, 1, msg)
    # . . push args
    68/push  "F - test-is-valid-name-starts-with-pre-digit"/imm32
    68/push  1/imm32/true
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-is-valid-name-starts-with-post-digit:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # var slice/ECX = "q34"
    68/push  _test-slice-char-and-digits-end/imm32
    68/push  _test-slice-char-and-digits/imm32
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # EAX = is-valid-name?(slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  is-valid-name?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(EAX, 1, msg)
    # . . push args
    68/push  "F - test-is-valid-name-starts-with-post-digit"/imm32
    68/push  1/imm32/true
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-is-valid-name-starts-with-digit:
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # var slice/ECX = "0x34"
    68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
    68/push  _test-slice-hex-int-with-0x-prefix/imm32
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # EAX = is-valid-name?(slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  is-valid-name?/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(EAX, 0, msg)
    # . . push args
    68/push  "F - test-is-valid-name-starts-with-digit"/imm32
    68/push  0/imm32/false
    50/push-EAX
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

# print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
emit-hex:  # out : (address buffered-file), n : int, width : int -> <void>
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    50/push-EAX
    51/push-ECX
    52/push-EDX
    53/push-EBX
    57/push-EDI
    # EDI = out
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
    # EBX = n
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
    # EDX = width
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
    # var curr/ECX = 0
    31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
$emit-hex:loop:
    # if (curr >= width) break
    39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
    7d/jump-if-greater-or-equal  $emit-hex:end/disp8
    # print-byte(out, EBX)
    # . . push args
    53/push-EBX
    57/push-EDI
    # . . call
    e8/call  print-byte/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # write-byte(out, ' ')
    # . . push args
    68/push  0x20/imm32/space
    57/push-EDI
    # . . call
    e8/call  write-byte/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # EBX = EBX >> 8
    c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/EBX    .           .             .           .           .               8/imm8            # shift EBX right by 8 bits, while padding zeroes
$emit-hex:continue:
    # ++curr
    41/increment-ECX
    eb/jump  $emit-hex:loop/disp8
$emit-hex:end:
    # . restore registers
    5f/pop-to-EDI
    5b/pop-to-EBX
    5a/pop-to-EDX
    59/pop-to-ECX
    58/pop-to-EAX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

test-emit-hex-single-byte:
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # emit-hex(_test-output-buffered-file, 0xab, 1)
    # . . push args
    68/push  1/imm32
    68/push  0xab/imm32
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-ints-equal(*_test-output-stream->data, 'ab ', msg)
    # . . push args
    68/push  "F - test-emit-hex-single-byte"/imm32
    68/push  0x206261/imm32
    # . . push *_test-output-stream->data
    b8/copy-to-EAX  _test-output-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . end
    c3/return

test-emit-hex-multiple-byte:
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # emit-hex(_test-output-buffered-file, 0x1234, 2)
    # . . push args
    68/push  2/imm32
    68/push  0x1234/imm32
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream, "34 12 ", msg)
    # . . push args
    68/push  "F - test-emit-hex-multiple-byte/1"/imm32
    68/push  "34 12 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . end
    c3/return

test-emit-hex-zero-pad:
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # emit-hex(_test-output-buffered-file, 0xab, 2)
    # . . push args
    68/push  2/imm32
    68/push  0xab/imm32
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check(_test-output-stream->data == 'ab 00 ')
    # . . push args
    68/push  "F - test-emit-hex-zero-pad/1"/imm32
    68/push  "ab 00 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . end
    c3/return

test-emit-hex-negative:
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # . clear-stream(_test-output-buffered-file+4)
    # . . push args
    b8/copy-to-EAX  _test-output-buffered-file/imm32
    05/add-to-EAX  4/imm32
    50/push-EAX
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # emit-hex(_test-output-buffered-file, -1, 2)
    # . . push args
    68/push  2/imm32
    68/push  -1/imm32
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    # check-stream-equal(_test-output-stream == "ff ff ")
    # . . push args
    68/push  "F - test-emit-hex-negative/1"/imm32
    68/push  "ff ff "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    # . end
    c3/return

# shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/'))
parse-datum-of-word:  # word : (address slice) -> value/EAX
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    51/push-ECX
    56/push-ESI
    # ESI = word
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    # var slice/ECX = {0, 0}
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    # slice = next-token-from-slice(word->start, word->end, '/')
    # . . push args
    51/push-ECX
    68/push  0x2f/imm32/slash
    ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
    ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
    # . . call
    e8/call  next-token-from-slice/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    # value/EAX = parse-hex-int(slice)
    # . . push args
    51/push-ECX
    # . . call
    e8/call  parse-hex-int/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
$parse-datum-of-word:end:
    # . reclaim locals
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    # . restore registers
    5e/pop-to-ESI
    59/pop-to-ECX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

== data

_test-slice-negative-two:
    2d/- 32/2
_test-slice-negative-two-end:
    2f/slash 66/f 6f/o 6f/o
_test-slice-negative-two-metadata-end:

_test-slice-three-zero:
    33/3 30/0
_test-slice-three-zero-end:

_test-slice-non-number-word:
    78/x 79/y 7a/z
_test-slice-non-number-word-end:
    2f/slash
_test-slice-non-number-word-metadata-end:

_test-slice-hexlike-non-number-word:
    61/a 62/b 63/c 64/d
_test-slice-hexlike-non-number-word-end:
    2f/slash
    78/x 79/y 7a/z
_test-slice-hexlike-non-number-word-metadata-end:

_test-slice-with-slash-prefix:
  2f/slash 30/0 33/3
_test-slice-with-slash-prefix-end:

# . . vim:nowrap:textwidth=0